Admin redirects for HPOS URLs (#35463)
* Redirect COT/HPOS admin requests to the corresponding CPT screen, if COT is not authoritative. * Tidy handling of query parameters. * Linting fixes.
This commit is contained in:
parent
c561d7941d
commit
925432aebe
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
When custom order tables are not authoritative, admin UI requests will be redirected to the matching legacy order screen as appropriate.
|
|
@ -6,6 +6,7 @@
|
|||
* @version 2.5.0
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\Orders\COTRedirectionController;
|
||||
use Automattic\WooCommerce\Internal\Admin\Orders\PageController as Custom_Orders_PageController;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
|
@ -316,6 +317,8 @@ class WC_Admin_Menus {
|
|||
if ( wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ) {
|
||||
$this->orders_page_controller = new Custom_Orders_PageController();
|
||||
$this->orders_page_controller->setup();
|
||||
} else {
|
||||
wc_get_container()->get( COTRedirectionController::class )->setup();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Internal\Admin\Orders;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
|
||||
|
||||
/**
|
||||
* When Custom Order Tables are not the default order store (ie, posts are authoritative), we should take care of
|
||||
* redirecting requests for the order editor and order admin list table to the equivalent posts-table screens.
|
||||
*
|
||||
* If the redirect logic is problematic, it can be unhooked using code like the following example:
|
||||
*
|
||||
* remove_action(
|
||||
* 'admin_page_access_denied',
|
||||
* array( wc_get_container()->get( COTRedirectionController::class ), 'handle_hpos_admin_requests' )
|
||||
* );
|
||||
*/
|
||||
class COTRedirectionController {
|
||||
use AccessiblePrivateMethods;
|
||||
|
||||
/**
|
||||
* Add hooks needed to perform our magic.
|
||||
*/
|
||||
public function setup(): void {
|
||||
// Only take action in cases where access to the admin screen would otherwise be denied.
|
||||
self::add_action( 'admin_page_access_denied', array( $this, 'handle_hpos_admin_requests' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for denied admin requests and, if they appear to relate to HPOS admin screens, potentially
|
||||
* redirect the user to the equivalent CPT-driven screens.
|
||||
*
|
||||
* @param array|null $query_params The query parameters to use when determining the redirect. If not provided, the $_GET superglobal will be used.
|
||||
*/
|
||||
private function handle_hpos_admin_requests( $query_params = null ) {
|
||||
$query_params = is_array( $query_params ) ? $query_params : $_GET;
|
||||
|
||||
if ( ! isset( $query_params['page'] ) || 'wc-orders' !== $query_params['page'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$params = wp_unslash( $query_params );
|
||||
$action = $params['action'] ?? '';
|
||||
unset( $params['page'] );
|
||||
|
||||
if ( 'edit' === $action && isset( $params['id'] ) ) {
|
||||
$params['post'] = $params['id'];
|
||||
unset( $params['id'] );
|
||||
$new_url = add_query_arg( $params, get_admin_url( null, 'post.php' ) );
|
||||
} elseif ( 'new' === $action ) {
|
||||
unset( $params['action'] );
|
||||
$params['post_type'] = 'shop_order';
|
||||
$new_url = add_query_arg( $params, get_admin_url( null, 'post-new.php' ) );
|
||||
} else {
|
||||
// If nonce parameters are present and valid, rebuild them for the CPT admin list table.
|
||||
if ( isset( $params['_wpnonce'] ) && check_admin_referer( 'bulk-orders' ) ) {
|
||||
$params['_wp_http_referer'] = get_admin_url( null, 'edit.php?post_type=shop_order' );
|
||||
$params['_wpnonce'] = wp_create_nonce( 'bulk-posts' );
|
||||
}
|
||||
|
||||
// If an `order` array parameter is present, rename as `post`.
|
||||
if ( isset( $params['order'] ) && is_array( $params['order'] ) ) {
|
||||
$params['post'] = $params['order'];
|
||||
unset( $params['order'] );
|
||||
}
|
||||
|
||||
$params['post_type'] = 'shop_order';
|
||||
$new_url = add_query_arg( $params, get_admin_url( null, 'edit.php' ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $new_url ) && wp_safe_redirect( $new_url, 301 ) ) {
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\Orders\COTRedirectionController;
|
||||
use Automattic\WooCommerce\Internal\Admin\Orders\Edit;
|
||||
use Automattic\WooCommerce\Internal\Admin\Orders\ListTable;
|
||||
use Automattic\WooCommerce\Internal\Admin\Orders\PageController;
|
||||
|
@ -21,6 +22,7 @@ class OrderAdminServiceProvider extends AbstractServiceProvider {
|
|||
* @var string[]
|
||||
*/
|
||||
protected $provides = array(
|
||||
COTRedirectionController::class,
|
||||
PageController::class,
|
||||
Edit::class,
|
||||
ListTable::class,
|
||||
|
@ -32,6 +34,7 @@ class OrderAdminServiceProvider extends AbstractServiceProvider {
|
|||
* @return void
|
||||
*/
|
||||
public function register() {
|
||||
$this->share( COTRedirectionController::class );
|
||||
$this->share( PageController::class );
|
||||
$this->share( Edit::class )->addArgument( PageController::class );
|
||||
$this->share( ListTable::class )->addArgument( PageController::class );
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
<?php
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\Orders\COTRedirectionController;
|
||||
|
||||
/**
|
||||
* Describes our redirection logic covering HPOS admin screens when Custom Order Tables are not authoritative.
|
||||
*/
|
||||
class COTRedirectionControllerTest extends WC_Unit_Test_Case {
|
||||
/**
|
||||
* @var COTRedirectionController
|
||||
*/
|
||||
private $sut;
|
||||
|
||||
/**
|
||||
* Holds the URL of the last attempted redirect.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $redirected_to = '';
|
||||
|
||||
/**
|
||||
* Setup our SUT and start listening for redirects.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->sut = new COTRedirectionController();
|
||||
$this->sut->setup();
|
||||
$this->redirected_to = '';
|
||||
|
||||
add_filter( 'wp_redirect', array( $this, 'watch_and_anull_redirects' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove our redirect listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function tearDown(): void {
|
||||
parent::tearDown();
|
||||
remove_filter( 'wp_redirect', array( $this, 'watch_and_anull_redirects' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the attempted redirect location, and stops the redirect from taking place.
|
||||
*
|
||||
* @param string $url Redirect location.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function watch_and_anull_redirects( string $url ) {
|
||||
$this->redirected_to = $url;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies the URL of the last attempted redirect, then resets ready for the next test.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_redirect_attempt(): string {
|
||||
$return = $this->redirected_to;
|
||||
$this->redirected_to = '';
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that redirects only occur in relation to HPOS admin screen requests.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_redirects_only_impact_hpos_admin_requests() {
|
||||
$this->sut->handle_hpos_admin_requests( array( 'page' => 'wc-orders' ) );
|
||||
$this->assertNotEmpty( $this->get_redirect_attempt(), 'A redirect was attempted in relation to an HPOS admin request.' );
|
||||
|
||||
$this->sut->handle_hpos_admin_requests( array( 'page' => 'foo' ) );
|
||||
$this->assertEmpty( $this->get_redirect_attempt(), 'A redirect was not attempted in relation to a non-HPOS admin request.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test order editor redirects work (in relation to creating new orders).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_redirects_to_the_new_order_screen(): void {
|
||||
$this->sut->handle_hpos_admin_requests(
|
||||
array(
|
||||
'action' => 'new',
|
||||
'page' => 'wc-orders',
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'/wp-admin/post-new.php?post_type=shop_order',
|
||||
$this->get_redirect_attempt(),
|
||||
'Attempts to access the new order page (HPOS) are successfully redirected to the new order page (CPT).'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test order editor redirects work (in relation to existing orders).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_redirects_to_the_order_editor_screen(): void {
|
||||
$this->sut->handle_hpos_admin_requests(
|
||||
array(
|
||||
'action' => 'edit',
|
||||
'id' => 12345,
|
||||
'page' => 'wc-orders',
|
||||
)
|
||||
);
|
||||
|
||||
$redirect_url = $this->get_redirect_attempt();
|
||||
$redirect_base = wp_parse_url( $redirect_url, PHP_URL_PATH );
|
||||
parse_str( wp_parse_url( $redirect_url, PHP_URL_QUERY ), $redirect_query );
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'/post.php',
|
||||
$redirect_base,
|
||||
'Confirm order editor redirects go to the expected WordPress admin controller.'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'12345',
|
||||
$redirect_query['post'],
|
||||
'Confirm order editor redirects maintain the correct order ID.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests order list table redirects work.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_redirects_to_the_order_admin_list_screen(): void {
|
||||
$this->sut->handle_hpos_admin_requests(
|
||||
array(
|
||||
'arbitrary' => '3pd-integration',
|
||||
'order' => array(
|
||||
123,
|
||||
456,
|
||||
),
|
||||
'page' => 'wc-orders',
|
||||
)
|
||||
);
|
||||
|
||||
$redirect_url = $this->get_redirect_attempt();
|
||||
$redirect_base = wp_parse_url( $redirect_url, PHP_URL_PATH );
|
||||
parse_str( wp_parse_url( $redirect_url, PHP_URL_QUERY ), $redirect_query );
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'/edit.php',
|
||||
$redirect_base,
|
||||
'Confirm order list table redirects go to the expected WordPress admin controller.'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'123',
|
||||
'456',
|
||||
),
|
||||
$redirect_query['post'],
|
||||
'Confirm order list table redirects maintain a list of order IDs for bulk action requests (if one was passed).'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'shop_order',
|
||||
$redirect_query['post_type'],
|
||||
'Confirm order list table redirects reference the correct custom post type.'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'3pd-integration',
|
||||
$redirect_query['arbitrary'],
|
||||
'Confirm that arbitrary query parameters are also passed across via order list table redirects.'
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue