From 1f42f3f17bf356789b1543feeae36a006cc95335 Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Tue, 23 May 2023 16:03:15 -0700 Subject: [PATCH 1/6] Add missing post_type value to WP_Screen for HPOS order screens --- .../woocommerce/src/Internal/Admin/Orders/PageController.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/woocommerce/src/Internal/Admin/Orders/PageController.php b/plugins/woocommerce/src/Internal/Admin/Orders/PageController.php index b710a4e2456..191e85a1293 100644 --- a/plugins/woocommerce/src/Internal/Admin/Orders/PageController.php +++ b/plugins/woocommerce/src/Internal/Admin/Orders/PageController.php @@ -152,6 +152,9 @@ class PageController { * Perform initialization for the current action. */ private function handle_load_page_action() { + $screen = get_current_screen(); + $screen->post_type = $this->order_type; + if ( method_exists( $this, 'setup_action_' . $this->current_action ) ) { $this->{"setup_action_{$this->current_action}"}(); } From 498c08592a45858925b897340359b8109c9cb2a0 Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Tue, 23 May 2023 16:07:22 -0700 Subject: [PATCH 2/6] Add method is_order_screen to HPOS PageController class --- .../Internal/Admin/Orders/PageController.php | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/plugins/woocommerce/src/Internal/Admin/Orders/PageController.php b/plugins/woocommerce/src/Internal/Admin/Orders/PageController.php index 191e85a1293..bc34017ed90 100644 --- a/plugins/woocommerce/src/Internal/Admin/Orders/PageController.php +++ b/plugins/woocommerce/src/Internal/Admin/Orders/PageController.php @@ -428,4 +428,89 @@ class PageController { return admin_url( 'admin.php?page=wc-orders' . ( 'shop_order' === $order_type ? '' : '--' . $order_type ) ); } + /** + * Helper method to check if the current admin screen is related to orders. + * + * @param string $type Optional. The order type to check for. Default shop_order. + * @param string $action Optional. The purpose of the screen to check for. 'list', 'edit', or 'new'. + * Leave empty to check for any order screen. + * + * @return bool + */ + public function is_order_screen( $type = 'shop_order', $action = '' ) : bool { + if ( ! did_action( 'current_screen' ) ) { + wc_doing_it_wrong( + __METHOD__, + sprintf( + // translators: %s is the name of a function. + esc_html__( '%s must be called after the current_screen action.', 'woocommerce' ), + esc_html( __METHOD__ ) + ), + 'x.x.x' + ); + + return false; + } + + $valid_types = wc_get_order_types( 'view-order' ); + if ( ! in_array( $type, $valid_types, true ) ) { + wc_doing_it_wrong( + __METHOD__, + sprintf( + // translators: %s is the name of an order type. + esc_html__( '%s is not a valid order type.', 'woocommerce' ), + esc_html( $type ) + ), + 'x.x.x' + ); + + return false; + } + + if ( wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ) { + if ( $action ) { + switch ( $action ) { + case 'edit': + $is_action = 'edit_order' === $this->current_action; + break; + case 'list': + $is_action = 'list_orders' === $this->current_action; + break; + case 'new': + $is_action = 'new_order' === $this->current_action; + break; + default: + $is_action = false; + break; + } + } + + $type_match = $type === $this->order_type; + $action_match = ! $action || $is_action; + } else { + $screen = get_current_screen(); + + if ( $action ) { + switch ( $action ) { + case 'edit': + $screen_match = 'post' === $screen->base && filter_input( INPUT_GET, 'post', FILTER_VALIDATE_INT ); + break; + case 'list': + $screen_match = 'edit' === $screen->base; + break; + case 'new': + $screen_match = 'post' === $screen->base && 'add' === $screen->action; + break; + default: + $screen_match = false; + break; + } + } + + $type_match = $type === $screen->post_type; + $action_match = ! $action || $screen_match; + } + + return $type_match && $action_match; + } } From 83f3dae4296dc8f573c34478be51c3d86f0c6898 Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Tue, 23 May 2023 16:07:39 -0700 Subject: [PATCH 3/6] Use container class to retrieve orders page controller This ensures that the instance of the page controller class that is used to set up the HPOS order admin screens is accessible and has all its properties set correctly for use with the new is_order_screen method. --- plugins/woocommerce/includes/admin/class-wc-admin-menus.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-menus.php b/plugins/woocommerce/includes/admin/class-wc-admin-menus.php index 800de06ee8a..b48199dede0 100644 --- a/plugins/woocommerce/includes/admin/class-wc-admin-menus.php +++ b/plugins/woocommerce/includes/admin/class-wc-admin-menus.php @@ -319,7 +319,7 @@ class WC_Admin_Menus { */ public function orders_menu(): void { 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 = wc_get_container()->get( Custom_Orders_PageController::class ); $this->orders_page_controller->setup(); } else { wc_get_container()->get( COTRedirectionController::class )->setup(); From 1e40699ed270750b44803edd489c8ff4579cdc0a Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Tue, 23 May 2023 16:10:32 -0700 Subject: [PATCH 4/6] Add OrderUtil methods to check for specific order admin screens --- .../woocommerce/src/Utilities/OrderUtil.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/plugins/woocommerce/src/Utilities/OrderUtil.php b/plugins/woocommerce/src/Utilities/OrderUtil.php index 21775789852..cbc8ae51f7a 100644 --- a/plugins/woocommerce/src/Utilities/OrderUtil.php +++ b/plugins/woocommerce/src/Utilities/OrderUtil.php @@ -135,6 +135,39 @@ final class OrderUtil { return wc_get_container()->get( PageController::class )->get_new_page_url(); } + /** + * Check if the current admin screen is an order list table. + * + * @param string $order_type Optional. The order type to check for. Default shop_order. + * + * @return bool + */ + public static function is_order_list_table_screen( $order_type = 'shop_order' ) : bool { + return wc_get_container()->get( PageController::class )->is_order_screen( $order_type, 'list' ); + } + + /** + * Check if the current admin screen is for editing an order. + * + * @param string $order_type Optional. The order type to check for. Default shop_order. + * + * @return bool + */ + public static function is_order_edit_screen( $order_type = 'shop_order' ) : bool { + return wc_get_container()->get( PageController::class )->is_order_screen( $order_type, 'edit' ); + } + + /** + * Check if the current admin screen is adding a new order. + * + * @param string $order_type Optional. The order type to check for. Default shop_order. + * + * @return bool + */ + public static function is_new_order_screen( $order_type = 'shop_order' ) : bool { + return wc_get_container()->get( PageController::class )->is_order_screen( $order_type, 'new' ); + } + /** * Get the name of the database table that's currently in use for orders. * From ed2013d93321d086a28cf5de49ef3ed677b32cc3 Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Tue, 23 May 2023 16:12:38 -0700 Subject: [PATCH 5/6] Add changelog file --- plugins/woocommerce/changelog/fix-37484-hpos-screen-props | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/fix-37484-hpos-screen-props diff --git a/plugins/woocommerce/changelog/fix-37484-hpos-screen-props b/plugins/woocommerce/changelog/fix-37484-hpos-screen-props new file mode 100644 index 00000000000..4e2891222b2 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-37484-hpos-screen-props @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add properties and methods for detecting order admin screens more easily. From d6eab3615c06d3ef2dc6e84783180ea9d1f7abc0 Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Wed, 24 May 2023 16:22:41 -0700 Subject: [PATCH 6/6] Add unit tests --- .../Admin/Orders/PageControllerTest.php | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 plugins/woocommerce/tests/php/src/Internal/Admin/Orders/PageControllerTest.php diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/Orders/PageControllerTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/Orders/PageControllerTest.php new file mode 100644 index 00000000000..45f71d1a192 --- /dev/null +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/Orders/PageControllerTest.php @@ -0,0 +1,197 @@ +setup_cot(); + $this->toggle_cot( false ); + + $this->user_admin = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $this->user_admin ); + + global $mock_filter_input; + $mock_filter_input = false; + } + + /** + * Tear down after each test. + * + * @return void + */ + public function tearDown(): void { + $this->clean_up_cot_setup(); + parent::tearDown(); + } + + /** + * @testDox Basic order screen detection works. + */ + public function test_is_order_screen_any() { + set_current_screen(); + + $controller = new PageController(); + $screen = get_current_screen(); + + $screen->post_type = 'shop_order'; + $this->assertTrue( $controller->is_order_screen() ); + + $screen->post_type = 'post'; + $this->assertFalse( $controller->is_order_screen() ); + + $this->toggle_cot( true ); + global $pagenow, $plugin_page; + + $controller = new PageController(); + $pagenow = 'admin.php'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $plugin_page = 'wc-orders'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $controller->setup(); + $this->assertTrue( $controller->is_order_screen() ); + + $controller = new PageController(); + $pagenow = 'edit.php'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $controller->setup(); + $this->assertFalse( $controller->is_order_screen() ); + } + + /** + * @testDox Order list table screen detection works. + */ + public function test_is_order_screen_list() { + set_current_screen(); + + $controller = new PageController(); + $screen = get_current_screen(); + + $screen->post_type = 'shop_order'; + $screen->base = 'edit'; + $this->assertTrue( $controller->is_order_screen( 'shop_order', 'list' ) ); + + $screen->base = 'post'; + $this->assertFalse( $controller->is_order_screen( 'shop_order', 'list' ) ); + + $this->toggle_cot( true ); + global $pagenow, $plugin_page; + + $controller = new PageController(); + $pagenow = 'admin.php'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $plugin_page = 'wc-orders'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $_GET['action'] = ''; + $controller->setup(); + $this->assertTrue( $controller->is_order_screen( 'shop_order', 'list' ) ); + + $controller = new PageController(); + $_GET['action'] = 'edit'; + $controller->setup(); + $this->assertFalse( $controller->is_order_screen( 'shop_order', 'list' ) ); + } + + /** + * @testDox Edit Order screen detection works. + */ + public function test_is_order_screen_edit() { + global $mock_filter_input, $mock_return; + $mock_filter_input = true; + set_current_screen(); + + $controller = new PageController(); + $screen = get_current_screen(); + + $screen->post_type = 'shop_order'; + $screen->base = 'post'; + $mock_return = 123; + $this->assertTrue( $controller->is_order_screen( 'shop_order', 'edit' ) ); + + $mock_filter_input = false; + $this->assertFalse( $controller->is_order_screen( 'shop_order', 'edit' ) ); + + $this->toggle_cot( true ); + global $pagenow, $plugin_page; + + $controller = new PageController(); + $pagenow = 'admin.php'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $plugin_page = 'wc-orders'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $_GET['action'] = 'edit'; + $controller->setup(); + $this->assertTrue( $controller->is_order_screen( 'shop_order', 'edit' ) ); + + $controller = new PageController(); + $_GET['action'] = 'new'; + $controller->setup(); + $this->assertFalse( $controller->is_order_screen( 'shop_order', 'edit' ) ); + } + + /** + * @testDox Add New Order screen detection works. + */ + public function test_is_order_screen_new() { + set_current_screen(); + + $controller = new PageController(); + $screen = get_current_screen(); + + $screen->post_type = 'shop_order'; + $screen->base = 'post'; + $screen->action = 'add'; + $this->assertTrue( $controller->is_order_screen( 'shop_order', 'new' ) ); + + $screen->action = ''; + $this->assertFalse( $controller->is_order_screen( 'shop_order', 'new' ) ); + + $this->toggle_cot( true ); + global $pagenow, $plugin_page; + + $controller = new PageController(); + $pagenow = 'admin.php'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $plugin_page = 'wc-orders'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $_GET['action'] = 'new'; + $controller->setup(); + $this->assertTrue( $controller->is_order_screen( 'shop_order', 'new' ) ); + + $controller = new PageController(); + $_GET['action'] = 'edit'; + $controller->setup(); + $this->assertFalse( $controller->is_order_screen( 'shop_order', 'new' ) ); + } + } +} + +/** + * Mocks for global functions used in PageController + */ +namespace Automattic\WooCommerce\Internal\Admin\Orders { + /** + * The filter_input function will return NULL if we change the $_GET or $_POST variables at runtime, so we + * need to override it in PageController's namespace when we want it to return a specific value for testing. + * + * @return mixed + */ + function filter_input() { + global $mock_filter_input, $mock_return; + + if ( true === $mock_filter_input ) { + return $mock_return; + } else { + return call_user_func_array( '\filter_input', func_get_args() ); + } + } +}