From 3d5b1c1b9d1aef67d51cd94cbceee6dcea7cf9ff Mon Sep 17 00:00:00 2001 From: Mohammed Razzaq Date: Thu, 25 Aug 2022 03:50:19 +0530 Subject: [PATCH 1/5] Index posts after sorting in WooCommerce --- includes/classes/Indexable/Post/SyncManager.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/includes/classes/Indexable/Post/SyncManager.php b/includes/classes/Indexable/Post/SyncManager.php index 5ac943b6e3..29cc7c29ed 100644 --- a/includes/classes/Indexable/Post/SyncManager.php +++ b/includes/classes/Indexable/Post/SyncManager.php @@ -68,6 +68,10 @@ public function setup() { add_filter( 'ep_sync_insert_permissions_bypass', array( $this, 'filter_bypass_permission_checks_for_machines' ) ); add_filter( 'ep_sync_delete_permissions_bypass', array( $this, 'filter_bypass_permission_checks_for_machines' ) ); + + // WooCommerce Sorting + add_action( 'woocommerce_after_single_product_ordering', array( $this, 'action_sync_on_woocommerce_sort_single' ), 999, 2 ); + } /** @@ -654,4 +658,16 @@ protected function should_reindex_post( $post_id, $taxonomy ) { return true; } + + /** + * Re-index post when WooCommerce sorting takes place. + * + * @param {int} $post_id ID of post + * @param {int} $index menu order + */ + public function action_sync_on_woocommerce_sort_single( $post_id, $index ) { + + $this->add_to_queue( $post_id ); + } + } From 2cf07496c335e05dfec5e275e956f74a6f96e065 Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Wed, 12 Oct 2022 14:51:15 -0300 Subject: [PATCH 2/5] Custom WC product order: notice + resync --- .../Feature/WooCommerce/WooCommerce.php | 60 +++++++++++++++++++ .../classes/Indexable/Post/SyncManager.php | 16 ----- includes/classes/SyncManager.php | 2 +- includes/utils.php | 17 ++++++ 4 files changed, 78 insertions(+), 17 deletions(-) diff --git a/includes/classes/Feature/WooCommerce/WooCommerce.php b/includes/classes/Feature/WooCommerce/WooCommerce.php index e6d1ff88f5..a44511810f 100644 --- a/includes/classes/Feature/WooCommerce/WooCommerce.php +++ b/includes/classes/Feature/WooCommerce/WooCommerce.php @@ -824,6 +824,10 @@ public function setup() { add_filter( 'ep_weighting_default_post_type_weights', [ $this, 'add_product_default_post_type_weights' ], 10, 2 ); add_filter( 'ep_prepare_meta_data', [ $this, 'add_variations_skus_meta' ], 10, 2 ); add_filter( 'request', [ $this, 'admin_product_list_request_query' ], 9 ); + + // Custom product ordering + add_action( 'ep_admin_notices', [ $this, 'maybe_display_notice_about_product_ordering' ] ); + add_action( 'woocommerce_after_product_ordering', [ $this, 'action_sync_on_woocommerce_sort_single' ], 10, 2 ); } /** @@ -1169,4 +1173,60 @@ protected function should_integrate_with_query( $query ) { return true; } + + /** + * Depending on the number of products display an admin notice in the custom sort screen for WooCommerce Products + * + * @since 4.4.0 + * @param array $notices Current ElasticPress admin notices + * @return array + */ + public function maybe_display_notice_about_product_ordering( $notices ) { + global $pagenow, $wp_query; + + /** + * Make sure we're on edit.php in admin dashboard. + */ + if ( ! is_admin() || 'edit.php' !== $pagenow || empty( $wp_query->query['orderby'] ) || 'menu_order title' !== $wp_query->query['orderby'] ) { + return $notices; + } + + /* This filter is documented in /includes/classes/IndexHelper.php */ + $documents_per_page_sync = (int) apply_filters( 'ep_index_default_per_page', Utils\get_option( 'ep_bulk_setting', 350 ) ); + if ( $documents_per_page_sync >= $wp_query->found_posts ) { + return $notices; + } + + $notices['woocommerce_custom_sort'] = [ + 'html' => sprintf( + /* translators: Sync Page URL */ + __( 'Due to the number of products in the site, you will need to resync after applying a custom order.', 'elasticpress' ), + Utils\get_sync_url() + ), + 'type' => 'warning', + 'dismiss' => true, + ]; + + return $notices; + } + + /** + * Conditionally resync products after applying a custom order. + * + * @since 4.4.0 + * @param int $sorting_id ID of post dragged and dropped + * @param array $menu_orders Post IDs and their new menu_order value + */ + public function action_sync_on_woocommerce_sort_single( $sorting_id, $menu_orders ) { + /* This filter is documented in /includes/classes/IndexHelper.php */ + $documents_per_page_sync = (int) apply_filters( 'ep_index_default_per_page', Utils\get_option( 'ep_bulk_setting', 350 ) ); + if ( $documents_per_page_sync > count( $menu_orders ) ) { + return; + } + + $sync_manager = Indexables::factory()->get( 'post' )->sync_manager; + foreach ( $menu_orders as $post_id => $order ) { + $sync_manager->add_to_queue( $post_id ); + } + } } diff --git a/includes/classes/Indexable/Post/SyncManager.php b/includes/classes/Indexable/Post/SyncManager.php index 29cc7c29ed..5ac943b6e3 100644 --- a/includes/classes/Indexable/Post/SyncManager.php +++ b/includes/classes/Indexable/Post/SyncManager.php @@ -68,10 +68,6 @@ public function setup() { add_filter( 'ep_sync_insert_permissions_bypass', array( $this, 'filter_bypass_permission_checks_for_machines' ) ); add_filter( 'ep_sync_delete_permissions_bypass', array( $this, 'filter_bypass_permission_checks_for_machines' ) ); - - // WooCommerce Sorting - add_action( 'woocommerce_after_single_product_ordering', array( $this, 'action_sync_on_woocommerce_sort_single' ), 999, 2 ); - } /** @@ -658,16 +654,4 @@ protected function should_reindex_post( $post_id, $taxonomy ) { return true; } - - /** - * Re-index post when WooCommerce sorting takes place. - * - * @param {int} $post_id ID of post - * @param {int} $index menu order - */ - public function action_sync_on_woocommerce_sort_single( $post_id, $index ) { - - $this->add_to_queue( $post_id ); - } - } diff --git a/includes/classes/SyncManager.php b/includes/classes/SyncManager.php index 56ead89c32..5372e2e23a 100644 --- a/includes/classes/SyncManager.php +++ b/includes/classes/SyncManager.php @@ -185,7 +185,7 @@ public function index_sync_queue() { } // Bulk sync them all. - Indexables::factory()->get( $this->indexable_slug )->bulk_index( array_keys( $this->sync_queue ) ); + Indexables::factory()->get( $this->indexable_slug )->bulk_index_dynamically( array_keys( $this->sync_queue ) ); /** * Make sure to reset sync queue in case an shutdown happens before a redirect diff --git a/includes/utils.php b/includes/utils.php index d86d8df2ec..ba1035afd2 100644 --- a/includes/utils.php +++ b/includes/utils.php @@ -673,3 +673,20 @@ function get_asset_info( $slug, $attribute = null ) { return $asset; } + +/** + * Return the Sync Page URL. + * + * @since 4.4.0 + * @param boolean $do_sync Whether the link should or should not start a resync. + * @return string + */ +function get_sync_url( $do_sync = false ) { + $page = 'admin.php?page=elasticpress-sync'; + if ( $do_sync ) { + $page .= '&do_sync'; + } + return ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ? + network_admin_url( $page ) : + admin_url( $page ); +} From 64de24f3a29782e5d48e7942dc4e09eb9b0d4749 Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Wed, 12 Oct 2022 14:54:03 -0300 Subject: [PATCH 3/5] Fix comparison --- includes/classes/Feature/WooCommerce/WooCommerce.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/classes/Feature/WooCommerce/WooCommerce.php b/includes/classes/Feature/WooCommerce/WooCommerce.php index a44511810f..cd014ccf20 100644 --- a/includes/classes/Feature/WooCommerce/WooCommerce.php +++ b/includes/classes/Feature/WooCommerce/WooCommerce.php @@ -1220,7 +1220,7 @@ public function maybe_display_notice_about_product_ordering( $notices ) { public function action_sync_on_woocommerce_sort_single( $sorting_id, $menu_orders ) { /* This filter is documented in /includes/classes/IndexHelper.php */ $documents_per_page_sync = (int) apply_filters( 'ep_index_default_per_page', Utils\get_option( 'ep_bulk_setting', 350 ) ); - if ( $documents_per_page_sync > count( $menu_orders ) ) { + if ( $documents_per_page_sync < count( $menu_orders ) ) { return; } From 7a5115e323b8def031a2ddf1907f06c885de283e Mon Sep 17 00:00:00 2001 From: Mohammed Razzaq Date: Tue, 1 Nov 2022 05:52:13 +0530 Subject: [PATCH 4/5] Add E2E tests and update text --- .../Feature/WooCommerce/WooCommerce.php | 2 +- package-lock.json | 17 ++++++++ package.json | 1 + .../integration/features/woocommerce.cy.js | 42 +++++++++++++++++++ tests/cypress/support/commands.js | 1 + 5 files changed, 62 insertions(+), 1 deletion(-) diff --git a/includes/classes/Feature/WooCommerce/WooCommerce.php b/includes/classes/Feature/WooCommerce/WooCommerce.php index cd014ccf20..5e9b3c1992 100644 --- a/includes/classes/Feature/WooCommerce/WooCommerce.php +++ b/includes/classes/Feature/WooCommerce/WooCommerce.php @@ -1200,7 +1200,7 @@ public function maybe_display_notice_about_product_ordering( $notices ) { $notices['woocommerce_custom_sort'] = [ 'html' => sprintf( /* translators: Sync Page URL */ - __( 'Due to the number of products in the site, you will need to resync after applying a custom order.', 'elasticpress' ), + __( 'Due to the number of products in the site, you will need to resync after applying a custom sort order.', 'elasticpress' ), Utils\get_sync_url() ), 'type' => 'warning', diff --git a/package-lock.json b/package-lock.json index b6157698c1..cdb524f65f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "uuid": "^8.3.2" }, "devDependencies": { + "@4tw/cypress-drag-drop": "^2.2.1", "@wordpress/env": "^5.0.0", "10up-toolkit": "^3.0.0", "classnames": "^2.3.1", @@ -143,6 +144,15 @@ "node": ">=12" } }, + "node_modules/@4tw/cypress-drag-drop": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@4tw/cypress-drag-drop/-/cypress-drag-drop-2.2.1.tgz", + "integrity": "sha512-+ioJSnEwx70IiMyb4pLEjOS5u6AMWRIVCV20toCY7lb0YcvA0ipbjQBa9DdxEI7Zg2E2jtcIj7Rx0e3WNUbk/w==", + "dev": true, + "peerDependencies": { + "cypress": "^2.1.0 || ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, "node_modules/@ampproject/remapping": { "version": "2.1.2", "dev": true, @@ -18967,6 +18977,13 @@ "stylelint-order": "^4.1.0" } }, + "@4tw/cypress-drag-drop": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@4tw/cypress-drag-drop/-/cypress-drag-drop-2.2.1.tgz", + "integrity": "sha512-+ioJSnEwx70IiMyb4pLEjOS5u6AMWRIVCV20toCY7lb0YcvA0ipbjQBa9DdxEI7Zg2E2jtcIj7Rx0e3WNUbk/w==", + "dev": true, + "requires": {} + }, "@ampproject/remapping": { "version": "2.1.2", "dev": true, diff --git a/package.json b/package.json index 11015307be..ee022a42c9 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "license": "GPL-2.0-or-later", "description": "A fast and flexible search and query engine for WordPress.", "devDependencies": { + "@4tw/cypress-drag-drop": "^2.2.1", "@wordpress/env": "^5.0.0", "10up-toolkit": "^3.0.0", "classnames": "^2.3.1", diff --git a/tests/cypress/integration/features/woocommerce.cy.js b/tests/cypress/integration/features/woocommerce.cy.js index 438c268467..251a11694d 100644 --- a/tests/cypress/integration/features/woocommerce.cy.js +++ b/tests/cypress/integration/features/woocommerce.cy.js @@ -98,11 +98,16 @@ describe('WooCommerce Feature', () => { }); context('Dashboard', () => { + function setPerIndexCycle(number = null) { + const newValue = number || 350; + cy.wpCli(`option set ep_bulk_setting ${newValue}`); + } before(() => { cy.login(); cy.maybeEnableFeature('protected_content'); cy.maybeEnableFeature('woocommerce'); cy.activatePlugin('woocommerce'); + setPerIndexCycle(); }); it('Can fetch orders and products from Elasticsearch', () => { @@ -237,5 +242,42 @@ describe('WooCommerce Feature', () => { `${userData.firstName} ${userData.lastName}`, ); }); + it('Can show the correct order of products after custom sort order', () => { + const dataTransfer = new DataTransfer(); + // Content Items per Index Cycle is greater than number of products + cy.visitAdminPage('edit.php?post_type=product&orderby=menu_order+title&order=ASC'); + + cy.get('#the-list #post-1837').trigger('dragstart', dataTransfer); + cy.get('#the-list #post-1969').trigger('dragover'); + cy.get('#the-list #post-1969').trigger('drop', dataTransfer); + cy.get('#the-list #post-1837').trigger('dragend'); + + cy.reload(); + cy.get('#debug-menu-target-EP_Debug_Bar_ElasticPress .ep-query-debug').should( + 'contain.text', + 'Query Response Code: HTTP 200', + ); + cy.get('#the-list tr').should('have.id', 'post-1969'); + + // Content Items per Index Cycle is greater than number of products + setPerIndexCycle(20); + cy.visitAdminPage('edit.php?post_type=product&orderby=menu_order+title&order=ASC'); + + cy.get('#the-list #post-1969').trigger('dragstart', dataTransfer); + cy.get('#the-list #post-1837').trigger('dragover'); + cy.get('#the-list #post-1837').trigger('drop', dataTransfer); + cy.get('#the-list #post-1969').trigger('dragend'); + + cy.wpCli('elasticpress index --setup --yes'); + + cy.visitAdminPage('edit.php?post_type=product&orderby=menu_order+title&order=ASC'); + cy.get('#debug-menu-target-EP_Debug_Bar_ElasticPress .ep-query-debug').should( + 'contain.text', + 'Query Response Code: HTTP 200', + ); + cy.get('#the-list tr').should('have.id', 'post-1969'); + + setPerIndexCycle(); + }); }); }); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index ff3019ab7b..ce45345b2c 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -26,6 +26,7 @@ import 'cypress-file-upload'; import './commands/block-editor'; +import '@4tw/cypress-drag-drop'; Cypress.Commands.add('login', (username = 'admin', password = 'password') => { cy.visit(`/wp-admin`); From 6c8f31ea827a1d4695f16d3133ea9f9a337e21dd Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Mon, 7 Nov 2022 12:15:55 -0300 Subject: [PATCH 5/5] Adjust tests --- .../integration/features/woocommerce.cy.js | 91 +++++++++++-------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/tests/cypress/integration/features/woocommerce.cy.js b/tests/cypress/integration/features/woocommerce.cy.js index 580d618274..73de7633b5 100644 --- a/tests/cypress/integration/features/woocommerce.cy.js +++ b/tests/cypress/integration/features/woocommerce.cy.js @@ -98,16 +98,11 @@ describe('WooCommerce Feature', () => { }); context('Dashboard', () => { - function setPerIndexCycle(number = null) { - const newValue = number || 350; - cy.wpCli(`option set ep_bulk_setting ${newValue}`); - } before(() => { cy.login(); cy.maybeEnableFeature('protected_content'); cy.maybeEnableFeature('woocommerce'); - cy.activatePlugin('woocommerce'); - setPerIndexCycle(); + cy.activatePlugin('woocommerce', 'wpCli'); }); it('Can fetch orders and products from Elasticsearch', () => { @@ -242,42 +237,62 @@ describe('WooCommerce Feature', () => { `${userData.firstName} ${userData.lastName}`, ); }); - it('Can show the correct order of products after custom sort order', () => { - const dataTransfer = new DataTransfer(); - // Content Items per Index Cycle is greater than number of products - cy.visitAdminPage('edit.php?post_type=product&orderby=menu_order+title&order=ASC'); - - cy.get('#the-list #post-1837').trigger('dragstart', dataTransfer); - cy.get('#the-list #post-1969').trigger('dragover'); - cy.get('#the-list #post-1969').trigger('drop', dataTransfer); - cy.get('#the-list #post-1837').trigger('dragend'); - - cy.reload(); - cy.get('#debug-menu-target-EP_Debug_Bar_ElasticPress .ep-query-debug').should( - 'contain.text', - 'Query Response Code: HTTP 200', - ); - cy.get('#the-list tr').should('have.id', 'post-1969'); + it('Can show the correct order of products after custom sort order', () => { // Content Items per Index Cycle is greater than number of products - setPerIndexCycle(20); + cy.setPerIndexCycle(); cy.visitAdminPage('edit.php?post_type=product&orderby=menu_order+title&order=ASC'); - - cy.get('#the-list #post-1969').trigger('dragstart', dataTransfer); - cy.get('#the-list #post-1837').trigger('dragover'); - cy.get('#the-list #post-1837').trigger('drop', dataTransfer); - cy.get('#the-list #post-1969').trigger('dragend'); - - cy.wpCli('elasticpress index --setup --yes'); - + cy.get('div[data-ep-notice="woocommerce_custom_sort"]').should('not.exist'); + + let thirdProductId = ''; + cy.get('#the-list tr:eq(2)') + .as('thirdProduct') + .invoke('attr', 'id') + .then((id) => { + thirdProductId = id; + }); + + cy.get('@thirdProduct') + .drag('#the-list tr:eq(0)', { force: true }) + .then(() => { + cy.get('#the-list tr:eq(0)').should('have.id', thirdProductId); + + cy.refreshIndex('post').then(() => { + cy.reload(); + cy.get( + '#debug-menu-target-EP_Debug_Bar_ElasticPress .ep-query-debug', + ).should('contain.text', 'Query Response Code: HTTP 200'); + cy.get('#the-list tr:eq(0)').should('have.id', thirdProductId); + }); + }); + + // Content Items per Index Cycle is lower than number of products + cy.setPerIndexCycle(20); cy.visitAdminPage('edit.php?post_type=product&orderby=menu_order+title&order=ASC'); - cy.get('#debug-menu-target-EP_Debug_Bar_ElasticPress .ep-query-debug').should( - 'contain.text', - 'Query Response Code: HTTP 200', - ); - cy.get('#the-list tr').should('have.id', 'post-1969'); - - setPerIndexCycle(); + cy.get('div[data-ep-notice="woocommerce_custom_sort"]').should('exist'); + + cy.get('#the-list tr:eq(2)') + .as('thirdProduct') + .invoke('attr', 'id') + .then((id) => { + thirdProductId = id; + }); + + cy.get('@thirdProduct') + .drag('#the-list tr:eq(0)', { force: true }) + .then(() => { + cy.get('#the-list tr:eq(0)').should('have.id', thirdProductId); + + cy.refreshIndex('post').then(() => { + cy.reload(); + cy.get( + '#debug-menu-target-EP_Debug_Bar_ElasticPress .ep-query-debug', + ).should('contain.text', 'Query Response Code: HTTP 200'); + cy.get('#the-list tr:eq(0)').should('have.not.id', thirdProductId); + }); + }); + + cy.setPerIndexCycle(); }); }); });