From 7b29243929dc9e80ab538d638446be76e2e3b1f2 Mon Sep 17 00:00:00 2001
From: Jonny Harris
Date: Mon, 19 Jun 2023 14:37:50 +0100
Subject: [PATCH 01/45] Use wp_cache_supports over
wp_cache_supports_group_flush
---
includes/classes/Feature/Autosuggest/Autosuggest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/includes/classes/Feature/Autosuggest/Autosuggest.php b/includes/classes/Feature/Autosuggest/Autosuggest.php
index 7a474c213d..b62c43ae86 100644
--- a/includes/classes/Feature/Autosuggest/Autosuggest.php
+++ b/includes/classes/Feature/Autosuggest/Autosuggest.php
@@ -660,7 +660,7 @@ public function intercept_search_request( $response, $query = [], $args = [], $f
public function delete_cached_query() {
global $wp_object_cache;
if ( wp_using_ext_object_cache() ) {
- if ( function_exists( 'wp_cache_supports_group_flush' ) && wp_cache_supports_group_flush() ) {
+ if ( function_exists( 'wp_cache_supports' ) && wp_cache_supports( 'flush_group' ) ) {
wp_cache_flush_group( 'ep_autosuggest' );
} else {
// Try to delete the entire group.
From 6e6f5ceed767cb5a6003009910cd88d2e0ff18bc Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Mon, 19 Jun 2023 17:16:05 -0300
Subject: [PATCH 02/45] Initial pass of WooCommerce code split
---
.../classes/Feature/WooCommerce/Orders.php | 653 +++-------
.../Feature/WooCommerce/OrdersAutosuggest.php | 605 +++++++++
.../classes/Feature/WooCommerce/Products.php | 585 +++++++++
.../Feature/WooCommerce/WooCommerce.php | 1119 +++++------------
4 files changed, 1680 insertions(+), 1282 deletions(-)
create mode 100644 includes/classes/Feature/WooCommerce/OrdersAutosuggest.php
create mode 100644 includes/classes/Feature/WooCommerce/Products.php
diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php
index 461a1b51d5..d4cd0bf941 100644
--- a/includes/classes/Feature/WooCommerce/Orders.php
+++ b/includes/classes/Feature/WooCommerce/Orders.php
@@ -1,605 +1,248 @@
index = Indexables::factory()->get( 'post' )->get_index_name();
- }
-
- /**
- * Setup feature functionality.
- *
- * @return void
- */
- public function setup() {
- add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_assets' ] );
- add_filter( 'ep_after_update_feature', [ $this, 'after_update_feature' ], 10, 3 );
- add_filter( 'ep_after_sync_index', [ $this, 'epio_save_search_template' ] );
- add_filter( 'ep_saved_weighting_configuration', [ $this, 'epio_save_search_template' ] );
- add_filter( 'ep_indexable_post_status', [ $this, 'post_statuses' ] );
- add_filter( 'ep_indexable_post_types', [ $this, 'post_types' ] );
- add_action( 'rest_api_init', [ $this, 'rest_api_init' ] );
- add_filter( 'ep_post_sync_args', [ $this, 'filter_term_suggest' ], 10 );
- add_filter( 'ep_post_mapping', [ $this, 'mapping' ] );
- add_action( 'ep_woocommerce_shop_order_search_fields', [ $this, 'set_search_fields' ], 10, 2 );
- add_filter( 'ep_index_posts_args', [ $this, 'maybe_query_password_protected_posts' ] );
- add_filter( 'posts_where', [ $this, 'maybe_set_posts_where' ], 10, 2 );
- }
+ protected $woocommerce;
/**
- * Get the endpoint for WooCommerce Orders search.
+ * Class constructor
*
- * @return string WooCommerce orders search endpoint.
+ * @param WooCommerce $woocommerce WooCommerce feature object instance
*/
- public function get_search_endpoint() {
- /**
- * Filters the WooCommerce Orders search endpoint.
- *
- * @since 4.5.0
- * @hook ep_woocommerce_order_search_endpoint
- * @param {string} $endpoint Endpoint path.
- * @param {string} $index Elasticsearch index.
- */
- return apply_filters( 'ep_woocommerce_order_search_endpoint', "api/v1/search/orders/{$this->index}", $this->index );
+ public function __construct( WooCommerce $woocommerce ) {
+ $this->woocommerce = $woocommerce;
}
/**
- * Get the endpoint for the WooCommerce Orders search template.
- *
- * @return string WooCommerce Orders search template endpoint.
+ * Setup order related hooks
*/
- public function get_template_endpoint() {
- /**
- * Filters the WooCommerce Orders search template API endpoint.
- *
- * @since 4.5.0
- * @hook ep_woocommerce_order_search_template_endpoint
- * @param {string} $endpoint Endpoint path.
- * @param {string} $index Elasticsearch index.
- * @returns {string} Search template API endpoint.
- */
- return apply_filters( 'ep_woocommerce_order_search_template_endpoint', "api/v1/search/orders/{$this->index}/template", $this->index );
- }
-
- /**
- * Get the endpoint for temporary tokens.
- *
- * @return string Temporary token endpoint.
- */
- public function get_token_endpoint() {
- /**
- * Filters the temporary token API endpoint.
- *
- * @since 4.5.0
- * @hook ep_token_endpoint
- * @param {string} $endpoint Endpoint path.
- * @returns {string} Token API endpoint.
- */
- return apply_filters( 'ep_token_endpoint', 'api/v1/token' );
- }
-
- /**
- * Registers the API endpoint to get a token.
- *
- * @return void
- */
- public function rest_api_init() {
- register_rest_route(
- 'elasticpress/v1',
- 'token',
- [
- [
- 'callback' => [ $this, 'get_token' ],
- 'permission_callback' => [ $this, 'check_token_permission' ],
- 'methods' => 'GET',
- ],
- [
- 'callback' => [ $this, 'refresh_token' ],
- 'permission_callback' => [ $this, 'check_token_permission' ],
- 'methods' => 'POST',
- ],
- ]
- );
- }
-
- /**
- * Enqueue admin assets.
- *
- * @param string $hook_suffix The current admin page.
- */
- public function enqueue_admin_assets( $hook_suffix ) {
- if ( 'edit.php' !== $hook_suffix ) {
- return;
- }
-
- if ( ! isset( $_GET['post_type'] ) || 'shop_order' !== $_GET['post_type'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
- return;
- }
-
- wp_enqueue_style(
- 'elasticpress-woocommerce-order-search',
- EP_URL . 'dist/css/woocommerce-order-search-styles.css',
- Utils\get_asset_info( 'woocommerce-order-search-styles', 'dependencies' ),
- Utils\get_asset_info( 'woocommerce-order-search-styles', 'version' )
- );
-
- wp_enqueue_script(
- 'elasticpress-woocommerce-order-search',
- EP_URL . 'dist/js/woocommerce-order-search-script.js',
- Utils\get_asset_info( 'woocommerce-order-search-script', 'dependencies' ),
- Utils\get_asset_info( 'woocommerce-order-search-script', 'version' ),
- true
- );
-
- wp_set_script_translations( 'elasticpress-woocommerce-order-search', 'elasticpress' );
-
- $api_endpoint = $this->get_search_endpoint();
- $api_host = Utils\get_host();
-
- wp_localize_script(
- 'elasticpress-woocommerce-order-search',
- 'epWooCommerceOrderSearch',
- array(
- 'adminUrl' => admin_url( 'post.php' ),
- 'apiEndpoint' => $api_endpoint,
- 'apiHost' => ( 0 !== strpos( $api_endpoint, 'http' ) ) ? trailingslashit( esc_url_raw( $api_host ) ) : '',
- 'argsSchema' => $this->get_args_schema(),
- 'credentialsApiUrl' => rest_url( 'elasticpress/v1/token' ),
- 'credentialsNonce' => wp_create_nonce( 'wp_rest' ),
- 'dateFormat' => wc_date_format(),
- 'statusLabels' => wc_get_order_statuses(),
- 'timeFormat' => wc_time_format(),
- 'requestIdBase' => Utils\get_request_id_base(),
- )
- );
+ public function setup() {
+ add_filter( 'ep_sync_insert_permissions_bypass', [ $this, 'bypass_order_permissions_check' ], 10, 2 );
+ add_filter( 'ep_prepare_meta_allowed_protected_keys', [ $this, 'allow_meta_keys' ] );
+ add_filter( 'ep_post_sync_args_post_prepare_meta', [ $this, 'add_order_items_search' ], 20, 2 );
+ add_action( 'parse_query', [ $this, 'maybe_hook_woocommerce_search_fields' ], 1 );
+ add_action( 'parse_query', [ $this, 'search_order' ], 11 );
}
/**
- * Save or delete the search template on ElasticPress.io based on whether
- * the WooCommerce feature is being activated or deactivated.
+ * Allow order creations on the front end to get synced
*
- * @param string $feature Feature slug
- * @param array $settings Feature settings
- * @param array $data Feature activation data
- *
- * @return void
+ * @param bool $override Original order perms check value
+ * @param int $post_id Post ID
+ * @return bool
*/
- public function after_update_feature( $feature, $settings, $data ) {
- if ( 'woocommerce' !== $feature ) {
- return;
- }
+ public function bypass_order_permissions_check( $override, $post_id ) {
+ $searchable_post_types = $this->get_admin_searchable_post_types();
- if ( true === $data['active'] ) {
- $this->epio_save_search_template();
- } else {
- $this->epio_delete_search_template();
+ if ( in_array( get_post_type( $post_id ), $searchable_post_types, true ) ) {
+ return true;
}
- }
-
- /**
- * Save the search template to ElasticPress.io.
- *
- * @return void
- */
- public function epio_save_search_template() {
- $endpoint = $this->get_template_endpoint();
- $template = $this->get_search_template();
- Elasticsearch::factory()->remote_request(
- $endpoint,
- [
- 'blocking' => false,
- 'body' => $template,
- 'method' => 'PUT',
- ]
- );
-
- /**
- * Fires after the request is sent the search template API endpoint.
- *
- * @since 4.5.0
- * @hook ep_woocommerce_order_search_template_saved
- * @param {string} $template The search template (JSON).
- * @param {string} $index Index name.
- */
- do_action( 'ep_woocommerce_order_search_template_saved', $template, $this->index );
+ return $override;
}
/**
- * Delete the search template from ElasticPress.io.
+ * Returns the WooCommerce-oriented post types in admin that EP will search
*
- * @return void
+ * @return array
*/
- public function epio_delete_search_template() {
- $endpoint = $this->get_template_endpoint();
-
- Elasticsearch::factory()->remote_request(
- $endpoint,
- [
- 'blocking' => false,
- 'method' => 'DELETE',
- ]
- );
+ public function get_admin_searchable_post_types() {
+ $searchable_post_types = array( 'shop_order' );
/**
- * Fires after the request is sent the search template API endpoint.
+ * Filter admin searchable WooCommerce post types
*
- * @since 4.5.0
- * @hook ep_woocommerce_order_search_template_deleted
- * @param {string} $index Index name.
+ * @hook ep_woocommerce_admin_searchable_post_types
+ * @since 4.4.0
+ * @param {array} $post_types Post types
+ * @return {array} New post types
*/
- do_action( 'ep_woocommerce_order_search_template_deleted', $this->index );
+ return apply_filters( 'ep_woocommerce_admin_searchable_post_types', $searchable_post_types );
}
/**
- * Get the saved search template from ElasticPress.io.
+ * Index WooCommerce orders meta fields
*
- * @return string|WP_Error Search template if found, WP_Error on error.
- */
- public function epio_get_search_template() {
- $endpoint = $this->get_template_endpoint();
- $request = Elasticsearch::factory()->remote_request( $endpoint );
-
- if ( is_wp_error( $request ) ) {
- return $request;
- }
-
- $response = wp_remote_retrieve_body( $request );
-
- return $response;
- }
-
- /**
- * Generate a search template.
- *
- * A search template is the JSON for an Elasticsearch query with a
- * placeholder search term. The template is sent to ElasticPress.io where
- * it's used to make Elasticsearch queries using search terms sent from
- * the front end.
- *
- * @return string The search template as JSON.
+ * @param array $meta Existing post meta
+ * @return array
*/
- public function get_search_template() {
- $order_statuses = wc_get_order_statuses();
-
- add_filter( 'ep_bypass_exclusion_from_search', '__return_true', 10 );
- add_filter( 'ep_intercept_remote_request', '__return_true' );
- add_filter( 'ep_do_intercept_request', [ $this, 'intercept_search_request' ], 10, 4 );
- add_filter( 'ep_is_integrated_request', [ $this, 'is_integrated_request' ], 10, 2 );
-
- $query = new \WP_Query(
- array(
- 'ep_integrate' => true,
- 'ep_order_search_template' => true,
- 'post_status' => array_keys( $order_statuses ),
- 'post_type' => 'shop_order',
- 's' => '{{ep_placeholder}}',
+ public function allow_meta_keys( $meta ) {
+ return array_unique(
+ array_merge(
+ $meta,
+ array(
+ '_customer_user',
+ '_order_key',
+ '_billing_company',
+ '_billing_address_1',
+ '_billing_address_2',
+ '_billing_city',
+ '_billing_postcode',
+ '_billing_country',
+ '_billing_state',
+ '_billing_email',
+ '_billing_phone',
+ '_shipping_address_1',
+ '_shipping_address_2',
+ '_shipping_city',
+ '_shipping_postcode',
+ '_shipping_country',
+ '_shipping_state',
+ '_billing_last_name',
+ '_billing_first_name',
+ '_shipping_first_name',
+ '_shipping_last_name',
+ '_variations_skus',
+ )
)
);
-
- remove_filter( 'ep_bypass_exclusion_from_search', '__return_true', 10 );
- remove_filter( 'ep_intercept_remote_request', '__return_true' );
- remove_filter( 'ep_do_intercept_request', [ $this, 'intercept_search_request' ], 10 );
- remove_filter( 'ep_is_integrated_request', [ $this, 'is_integrated_request' ], 10 );
-
- return $this->search_template;
}
/**
- * Return true if a given feature is supported by WooCommerce Orders.
+ * Add order items as a searchable string.
*
- * Applied as a filter on Utils\is_integrated_request() so that features
- * are enabled for the query that is used to generate the search template,
- * regardless of the request type. This avoids the need to send a request
- * to the front end.
+ * This mimics how WooCommerce currently does in the order_itemmeta
+ * table. They combine the titles of the products and put them in a
+ * meta field called "Items".
*
- * @param bool $is_integrated Whether queries for the request will be
- * integrated.
- * @param string $context Context for the original check. Usually the
- * slug of the feature doing the check.
- * @return bool True if the check is for a feature supported by WooCommerce
- * Order search.
- */
- public function is_integrated_request( $is_integrated, $context ) {
- $supported_contexts = [
- 'search',
- 'woocommerce',
- ];
-
- return in_array( $context, $supported_contexts, true );
- }
-
- /**
- * Store intercepted request body and return request result.
+ * @param array $post_args Post arguments
+ * @param string|int $post_id Post id
*
- * @param object $response Response
- * @param array $query Query
- * @param array $args WP_Query argument array
- * @param int $failures Count of failures in request loop
- * @return object $response Response
- */
- public function intercept_search_request( $response, $query = [], $args = [], $failures = 0 ) {
- $this->search_template = $query['args']['body'];
-
- return wp_remote_request( $query['url'], $args );
- }
-
- /**
- * Get schema for search args.
- *
- * @return array Search args schema.
- */
- public function get_args_schema() {
- $args = array(
- 'customer' => array(
- 'type' => 'number',
- ),
- 'm' => array(
- 'type' => 'string',
- ),
- 'offset' => array(
- 'type' => 'number',
- 'default' => 0,
- ),
- 'per_page' => array(
- 'type' => 'number',
- 'default' => 6,
- ),
- 'search' => array(
- 'type' => 'string',
- 'default' => '',
- ),
- );
-
- return $args;
- }
-
- /**
- * Get a temporary token.
- *
- * @return string|false Authorization header, or false on failure.
- */
- public function get_token() {
- $user_id = get_current_user_id();
-
- $credentials = get_user_meta( $user_id, 'ep_token', true );
-
- if ( $credentials ) {
- return $credentials;
- }
-
- return $this->refresh_token();
- }
-
- /**
- * Refresh the temporary token.
- *
- * @return string|false Authorization header, or false on failure.
- */
- public function refresh_token() {
- $user_id = get_current_user_id();
-
- $endpoint = $this->get_token_endpoint();
- $response = Elasticsearch::factory()->remote_request( $endpoint, [ 'method' => 'POST' ] );
-
- if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
- return false;
- }
-
- $response = wp_remote_retrieve_body( $response );
- $response = json_decode( $response );
-
- $credentials = base64_encode( "$response->username:$response->clear_password" ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
-
- update_user_meta( $user_id, 'ep_token', $credentials );
-
- return $credentials;
- }
-
- /**
- * Checks if the token API can be used.
- *
- * @return boolean Whether the token API can be used.
- */
- public function check_token_permission() {
- /**
- * Filters the capability required to use the token API.
- *
- * @since 4.5.0
- * @hook ep_token_capability
- * @param {string} $capability Required capability.
- */
- $capability = apply_filters( 'ep_token_capability', 'edit_others_shop_orders' );
-
- return current_user_can( $capability );
- }
-
- /**
- * Index shop orders.
- *
- * @param array $post_types Indexable post types.
- * @return array Indexable post types.
- */
- public function post_types( $post_types ) {
- $post_types['shop_order'] = 'shop_order';
-
- return $post_types;
- }
-
- /**
- * Index order statuses.
- *
- * @param array $post_statuses Indexable post statuses.
- * @return array Indexable post statuses.
- */
- public function post_statuses( $post_statuses ) {
- $order_statuses = wc_get_order_statuses();
-
- return array_unique( array_merge( $post_statuses, array_keys( $order_statuses ) ) );
- }
-
- /**
- * Add term suggestions to be indexed
- *
- * @param array $post_args Array of ES args.
* @return array
*/
- public function filter_term_suggest( $post_args ) {
- if ( empty( $post_args['post_type'] ) || 'shop_order' !== $post_args['post_type'] ) {
- return $post_args;
- }
+ public function add_order_items_search( $post_args, $post_id ) {
+ $searchable_post_types = $this->get_admin_searchable_post_types();
- if ( empty( $post_args['meta'] ) ) {
+ // Make sure it is only WooCommerce orders we touch.
+ if ( ! in_array( $post_args['post_type'], $searchable_post_types, true ) ) {
return $post_args;
}
- /**
- * Add the order number as a meta (text) field, so we can freely search on it.
- */
- $order_id = $post_args['ID'];
- if ( function_exists( 'wc_get_order' ) ) {
- $order = wc_get_order( $post_args['ID'] );
- if ( $order && is_a( $order, 'WC_Order' ) && method_exists( $order, 'get_order_number' ) ) {
- $order_id = $order->get_order_number();
- }
- }
-
- $post_args['meta']['order_number'] = [
- [
- 'raw' => $order_id,
- 'value' => $order_id,
- ],
- ];
-
- $suggest = [];
-
- $fields_to_ngram = [
- '_billing_email',
- '_billing_last_name',
- '_billing_first_name',
- ];
+ $post_indexable = Indexables::factory()->get( 'post' );
- foreach ( $fields_to_ngram as $field_to_ngram ) {
- if ( ! empty( $post_args['meta'][ $field_to_ngram ] )
- && ! empty( $post_args['meta'][ $field_to_ngram ][0] )
- && ! empty( $post_args['meta'][ $field_to_ngram ][0]['value'] ) ) {
- $suggest[] = $post_args['meta'][ $field_to_ngram ][0]['value'];
+ // Get order items.
+ $order = wc_get_order( $post_id );
+ $item_meta = [];
+ foreach ( $order->get_items() as $delta => $product_item ) {
+ // WooCommerce 3.x uses WC_Order_Item_Product instance while 2.x an array
+ if ( is_object( $product_item ) && method_exists( $product_item, 'get_name' ) ) {
+ $item_meta['_items'][] = $product_item->get_name( 'edit' );
+ } elseif ( is_array( $product_item ) && isset( $product_item['name'] ) ) {
+ $item_meta['_items'][] = $product_item['name'];
}
}
- if ( ! empty( $suggest ) ) {
- $post_args['term_suggest'] = $suggest;
- }
+ // Prepare order items.
+ $item_meta['_items'] = empty( $item_meta['_items'] ) ? '' : implode( '|', $item_meta['_items'] );
+ $post_args['meta'] = array_merge( $post_args['meta'], $post_indexable->prepare_meta_types( $item_meta ) );
return $post_args;
}
/**
- * Add mapping for suggest fields
+ * Prevent order fields from being removed.
*
- * @param array $mapping ES mapping.
- * @return array
- */
- public function mapping( $mapping ) {
- $post_indexable = Indexables::factory()->get( 'post' );
-
- $mapping = $post_indexable->add_ngram_analyzer( $mapping );
- $mapping = $post_indexable->add_term_suggest_field( $mapping );
-
- return $mapping;
- }
-
- /**
- * Set the search_fields parameter in the search template.
+ * When Protected Content is enabled, all posts with password have their content removed.
+ * This can't happen for orders, as the order key is added in that field.
+ *
+ * @see https://github.com/10up/ElasticPress/issues/2726
*
- * @param array $search_fields Current search fields
- * @param \WP_Query $query Query being executed
- * @return array New search fields
+ * @param bool $skip Whether the password protected content should have their content, and meta removed
+ * @param array $post_args Post arguments
+ * @return bool
*/
- public function set_search_fields( array $search_fields, \WP_Query $query ) : array {
- $is_orders_search_template = (bool) $query->get( 'ep_order_search_template' );
+ public function keep_order_fields( $skip, $post_args ) {
+ $searchable_post_types = $this->get_admin_searchable_post_types();
- if ( $is_orders_search_template ) {
- $search_fields = [
- 'meta.order_number.value',
- 'term_suggest',
- 'meta' => [
- '_billing_email',
- '_billing_last_name',
- '_billing_first_name',
- ],
- ];
+ if ( in_array( $post_args['post_type'], $searchable_post_types, true ) ) {
+ return true;
}
- return $search_fields;
+ return $skip;
}
/**
- * Allow password protected to be indexed.
+ * Sets woocommerce meta search fields to an empty array if we are integrating the main query with ElasticSearch
*
- * If Protected Content is enabled, do nothing. Otherwise, allow pw protected posts to be indexed.
- * The feature restricts it back in maybe_set_posts_where()
+ * Woocommerce calls this action as part of its own callback on parse_query. We add this filter only if the query
+ * is integrated with ElasticSearch.
+ * If we were to always return array() on this filter, we'd break admin searches when WooCommerce module is activated
+ * without the Protected Content Module
*
- * @see maybe_set_posts_where()
- * @param array $args WP_Query args
- * @return array
+ * @param \WP_Query $query Current query
*/
- public function maybe_query_password_protected_posts( $args ) {
- // Password protected posts are already being indexed, no need to do anything.
- if ( isset( $args['has_password'] ) && is_null( $args['has_password'] ) ) {
- return $args;
+ public function maybe_hook_woocommerce_search_fields( $query ) {
+ global $pagenow, $wp, $wc_list_table;
+
+ if ( ! $this->woocommerce->should_integrate_with_query( $query ) ) {
+ return;
}
/**
- * Set a flag in the query but allow it to index all password protected posts for now,
- * so WP does not inject its own where clause.
+ * Determines actions to be applied, or removed, if doing a WooCommerce serarch
+ *
+ * @hook ep_woocommerce_hook_search_fields
+ * @since 4.4.0
*/
- $args['ep_orders_has_password'] = true;
- $args['has_password'] = null;
+ do_action( 'ep_woocommerce_hook_search_fields' );
+
+ if ( 'edit.php' !== $pagenow || empty( $wp->query_vars['s'] ) || 'shop_order' !== $wp->query_vars['post_type'] || ! isset( $_GET['s'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
+ return;
+ }
- return $args;
+ remove_action( 'parse_query', [ $wc_list_table, 'search_custom_fields' ] );
}
/**
- * Restrict password protected posts back but allow orders.
+ * Enhance WooCommerce search order by order id, email, phone number, name, etc..
+ * What this function does:
+ * 1. Reverse the woocommerce shop_order_search_custom_fields query
+ * 2. If the search key is integer and it is an Order Id, just query with post__in
+ * 3. If the search key is integer but not an order id ( might be phone number ), use ES to find it
*
- * @see maybe_query_password_protected_posts
- * @param string $where Current where clause
- * @param WP_Query $query WP_Query
- * @return string
+ * @param WP_Query $wp WP Query
*/
- public function maybe_set_posts_where( $where, $query ) {
- global $wpdb;
-
- if ( ! $query->get( 'ep_orders_has_password' ) ) {
- return $where;
+ public function search_order( $wp ) {
+ if ( ! $this->woocommerce->should_integrate_with_query( $wp ) ) {
+ return;
}
- $where .= " AND ( {$wpdb->posts}.post_password = '' OR {$wpdb->posts}.post_type = 'shop_order' )";
+ global $pagenow;
- return $where;
+ $searchable_post_types = $this->get_admin_searchable_post_types();
+
+ if ( 'edit.php' !== $pagenow || empty( $wp->query_vars['post_type'] ) || ! in_array( $wp->query_vars['post_type'], $searchable_post_types, true ) ||
+ ( empty( $wp->query_vars['s'] ) && empty( $wp->query_vars['shop_order_search'] ) ) ) {
+ return;
+ }
+
+ // phpcs:disable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
+ if ( isset( $_GET['s'] ) ) {
+ $search_key_safe = str_replace( array( 'Order #', '#' ), '', wc_clean( $_GET['s'] ) );
+ unset( $wp->query_vars['post__in'] );
+ $wp->query_vars['s'] = $search_key_safe;
+ }
+ // phpcs:enable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
}
}
diff --git a/includes/classes/Feature/WooCommerce/OrdersAutosuggest.php b/includes/classes/Feature/WooCommerce/OrdersAutosuggest.php
new file mode 100644
index 0000000000..c68766da47
--- /dev/null
+++ b/includes/classes/Feature/WooCommerce/OrdersAutosuggest.php
@@ -0,0 +1,605 @@
+index = Indexables::factory()->get( 'post' )->get_index_name();
+ }
+
+ /**
+ * Setup feature functionality.
+ *
+ * @return void
+ */
+ public function setup() {
+ add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_assets' ] );
+ add_filter( 'ep_after_update_feature', [ $this, 'after_update_feature' ], 10, 3 );
+ add_filter( 'ep_after_sync_index', [ $this, 'epio_save_search_template' ] );
+ add_filter( 'ep_saved_weighting_configuration', [ $this, 'epio_save_search_template' ] );
+ add_filter( 'ep_indexable_post_status', [ $this, 'post_statuses' ] );
+ add_filter( 'ep_indexable_post_types', [ $this, 'post_types' ] );
+ add_action( 'rest_api_init', [ $this, 'rest_api_init' ] );
+ add_filter( 'ep_post_sync_args', [ $this, 'filter_term_suggest' ], 10 );
+ add_filter( 'ep_post_mapping', [ $this, 'mapping' ] );
+ add_action( 'ep_woocommerce_shop_order_search_fields', [ $this, 'set_search_fields' ], 10, 2 );
+ add_filter( 'ep_index_posts_args', [ $this, 'maybe_query_password_protected_posts' ] );
+ add_filter( 'posts_where', [ $this, 'maybe_set_posts_where' ], 10, 2 );
+ }
+
+ /**
+ * Get the endpoint for WooCommerce Orders search.
+ *
+ * @return string WooCommerce orders search endpoint.
+ */
+ public function get_search_endpoint() {
+ /**
+ * Filters the WooCommerce Orders search endpoint.
+ *
+ * @since 4.5.0
+ * @hook ep_woocommerce_order_search_endpoint
+ * @param {string} $endpoint Endpoint path.
+ * @param {string} $index Elasticsearch index.
+ */
+ return apply_filters( 'ep_woocommerce_order_search_endpoint', "api/v1/search/orders/{$this->index}", $this->index );
+ }
+
+ /**
+ * Get the endpoint for the WooCommerce Orders search template.
+ *
+ * @return string WooCommerce Orders search template endpoint.
+ */
+ public function get_template_endpoint() {
+ /**
+ * Filters the WooCommerce Orders search template API endpoint.
+ *
+ * @since 4.5.0
+ * @hook ep_woocommerce_order_search_template_endpoint
+ * @param {string} $endpoint Endpoint path.
+ * @param {string} $index Elasticsearch index.
+ * @returns {string} Search template API endpoint.
+ */
+ return apply_filters( 'ep_woocommerce_order_search_template_endpoint', "api/v1/search/orders/{$this->index}/template", $this->index );
+ }
+
+ /**
+ * Get the endpoint for temporary tokens.
+ *
+ * @return string Temporary token endpoint.
+ */
+ public function get_token_endpoint() {
+ /**
+ * Filters the temporary token API endpoint.
+ *
+ * @since 4.5.0
+ * @hook ep_token_endpoint
+ * @param {string} $endpoint Endpoint path.
+ * @returns {string} Token API endpoint.
+ */
+ return apply_filters( 'ep_token_endpoint', 'api/v1/token' );
+ }
+
+ /**
+ * Registers the API endpoint to get a token.
+ *
+ * @return void
+ */
+ public function rest_api_init() {
+ register_rest_route(
+ 'elasticpress/v1',
+ 'token',
+ [
+ [
+ 'callback' => [ $this, 'get_token' ],
+ 'permission_callback' => [ $this, 'check_token_permission' ],
+ 'methods' => 'GET',
+ ],
+ [
+ 'callback' => [ $this, 'refresh_token' ],
+ 'permission_callback' => [ $this, 'check_token_permission' ],
+ 'methods' => 'POST',
+ ],
+ ]
+ );
+ }
+
+ /**
+ * Enqueue admin assets.
+ *
+ * @param string $hook_suffix The current admin page.
+ */
+ public function enqueue_admin_assets( $hook_suffix ) {
+ if ( 'edit.php' !== $hook_suffix ) {
+ return;
+ }
+
+ if ( ! isset( $_GET['post_type'] ) || 'shop_order' !== $_GET['post_type'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ return;
+ }
+
+ wp_enqueue_style(
+ 'elasticpress-woocommerce-order-search',
+ EP_URL . 'dist/css/woocommerce-order-search-styles.css',
+ Utils\get_asset_info( 'woocommerce-order-search-styles', 'dependencies' ),
+ Utils\get_asset_info( 'woocommerce-order-search-styles', 'version' )
+ );
+
+ wp_enqueue_script(
+ 'elasticpress-woocommerce-order-search',
+ EP_URL . 'dist/js/woocommerce-order-search-script.js',
+ Utils\get_asset_info( 'woocommerce-order-search-script', 'dependencies' ),
+ Utils\get_asset_info( 'woocommerce-order-search-script', 'version' ),
+ true
+ );
+
+ wp_set_script_translations( 'elasticpress-woocommerce-order-search', 'elasticpress' );
+
+ $api_endpoint = $this->get_search_endpoint();
+ $api_host = Utils\get_host();
+
+ wp_localize_script(
+ 'elasticpress-woocommerce-order-search',
+ 'epWooCommerceOrderSearch',
+ array(
+ 'adminUrl' => admin_url( 'post.php' ),
+ 'apiEndpoint' => $api_endpoint,
+ 'apiHost' => ( 0 !== strpos( $api_endpoint, 'http' ) ) ? trailingslashit( esc_url_raw( $api_host ) ) : '',
+ 'argsSchema' => $this->get_args_schema(),
+ 'credentialsApiUrl' => rest_url( 'elasticpress/v1/token' ),
+ 'credentialsNonce' => wp_create_nonce( 'wp_rest' ),
+ 'dateFormat' => wc_date_format(),
+ 'statusLabels' => wc_get_order_statuses(),
+ 'timeFormat' => wc_time_format(),
+ 'requestIdBase' => Utils\get_request_id_base(),
+ )
+ );
+ }
+
+ /**
+ * Save or delete the search template on ElasticPress.io based on whether
+ * the WooCommerce feature is being activated or deactivated.
+ *
+ * @param string $feature Feature slug
+ * @param array $settings Feature settings
+ * @param array $data Feature activation data
+ *
+ * @return void
+ */
+ public function after_update_feature( $feature, $settings, $data ) {
+ if ( 'woocommerce' !== $feature ) {
+ return;
+ }
+
+ if ( true === $data['active'] ) {
+ $this->epio_save_search_template();
+ } else {
+ $this->epio_delete_search_template();
+ }
+ }
+
+ /**
+ * Save the search template to ElasticPress.io.
+ *
+ * @return void
+ */
+ public function epio_save_search_template() {
+ $endpoint = $this->get_template_endpoint();
+ $template = $this->get_search_template();
+
+ Elasticsearch::factory()->remote_request(
+ $endpoint,
+ [
+ 'blocking' => false,
+ 'body' => $template,
+ 'method' => 'PUT',
+ ]
+ );
+
+ /**
+ * Fires after the request is sent the search template API endpoint.
+ *
+ * @since 4.5.0
+ * @hook ep_woocommerce_order_search_template_saved
+ * @param {string} $template The search template (JSON).
+ * @param {string} $index Index name.
+ */
+ do_action( 'ep_woocommerce_order_search_template_saved', $template, $this->index );
+ }
+
+ /**
+ * Delete the search template from ElasticPress.io.
+ *
+ * @return void
+ */
+ public function epio_delete_search_template() {
+ $endpoint = $this->get_template_endpoint();
+
+ Elasticsearch::factory()->remote_request(
+ $endpoint,
+ [
+ 'blocking' => false,
+ 'method' => 'DELETE',
+ ]
+ );
+
+ /**
+ * Fires after the request is sent the search template API endpoint.
+ *
+ * @since 4.5.0
+ * @hook ep_woocommerce_order_search_template_deleted
+ * @param {string} $index Index name.
+ */
+ do_action( 'ep_woocommerce_order_search_template_deleted', $this->index );
+ }
+
+ /**
+ * Get the saved search template from ElasticPress.io.
+ *
+ * @return string|WP_Error Search template if found, WP_Error on error.
+ */
+ public function epio_get_search_template() {
+ $endpoint = $this->get_template_endpoint();
+ $request = Elasticsearch::factory()->remote_request( $endpoint );
+
+ if ( is_wp_error( $request ) ) {
+ return $request;
+ }
+
+ $response = wp_remote_retrieve_body( $request );
+
+ return $response;
+ }
+
+ /**
+ * Generate a search template.
+ *
+ * A search template is the JSON for an Elasticsearch query with a
+ * placeholder search term. The template is sent to ElasticPress.io where
+ * it's used to make Elasticsearch queries using search terms sent from
+ * the front end.
+ *
+ * @return string The search template as JSON.
+ */
+ public function get_search_template() {
+ $order_statuses = wc_get_order_statuses();
+
+ add_filter( 'ep_bypass_exclusion_from_search', '__return_true', 10 );
+ add_filter( 'ep_intercept_remote_request', '__return_true' );
+ add_filter( 'ep_do_intercept_request', [ $this, 'intercept_search_request' ], 10, 4 );
+ add_filter( 'ep_is_integrated_request', [ $this, 'is_integrated_request' ], 10, 2 );
+
+ $query = new \WP_Query(
+ array(
+ 'ep_integrate' => true,
+ 'ep_order_search_template' => true,
+ 'post_status' => array_keys( $order_statuses ),
+ 'post_type' => 'shop_order',
+ 's' => '{{ep_placeholder}}',
+ )
+ );
+
+ remove_filter( 'ep_bypass_exclusion_from_search', '__return_true', 10 );
+ remove_filter( 'ep_intercept_remote_request', '__return_true' );
+ remove_filter( 'ep_do_intercept_request', [ $this, 'intercept_search_request' ], 10 );
+ remove_filter( 'ep_is_integrated_request', [ $this, 'is_integrated_request' ], 10 );
+
+ return $this->search_template;
+ }
+
+ /**
+ * Return true if a given feature is supported by WooCommerce Orders.
+ *
+ * Applied as a filter on Utils\is_integrated_request() so that features
+ * are enabled for the query that is used to generate the search template,
+ * regardless of the request type. This avoids the need to send a request
+ * to the front end.
+ *
+ * @param bool $is_integrated Whether queries for the request will be
+ * integrated.
+ * @param string $context Context for the original check. Usually the
+ * slug of the feature doing the check.
+ * @return bool True if the check is for a feature supported by WooCommerce
+ * Order search.
+ */
+ public function is_integrated_request( $is_integrated, $context ) {
+ $supported_contexts = [
+ 'search',
+ 'woocommerce',
+ ];
+
+ return in_array( $context, $supported_contexts, true );
+ }
+
+ /**
+ * Store intercepted request body and return request result.
+ *
+ * @param object $response Response
+ * @param array $query Query
+ * @param array $args WP_Query argument array
+ * @param int $failures Count of failures in request loop
+ * @return object $response Response
+ */
+ public function intercept_search_request( $response, $query = [], $args = [], $failures = 0 ) {
+ $this->search_template = $query['args']['body'];
+
+ return wp_remote_request( $query['url'], $args );
+ }
+
+ /**
+ * Get schema for search args.
+ *
+ * @return array Search args schema.
+ */
+ public function get_args_schema() {
+ $args = array(
+ 'customer' => array(
+ 'type' => 'number',
+ ),
+ 'm' => array(
+ 'type' => 'string',
+ ),
+ 'offset' => array(
+ 'type' => 'number',
+ 'default' => 0,
+ ),
+ 'per_page' => array(
+ 'type' => 'number',
+ 'default' => 6,
+ ),
+ 'search' => array(
+ 'type' => 'string',
+ 'default' => '',
+ ),
+ );
+
+ return $args;
+ }
+
+ /**
+ * Get a temporary token.
+ *
+ * @return string|false Authorization header, or false on failure.
+ */
+ public function get_token() {
+ $user_id = get_current_user_id();
+
+ $credentials = get_user_meta( $user_id, 'ep_token', true );
+
+ if ( $credentials ) {
+ return $credentials;
+ }
+
+ return $this->refresh_token();
+ }
+
+ /**
+ * Refresh the temporary token.
+ *
+ * @return string|false Authorization header, or false on failure.
+ */
+ public function refresh_token() {
+ $user_id = get_current_user_id();
+
+ $endpoint = $this->get_token_endpoint();
+ $response = Elasticsearch::factory()->remote_request( $endpoint, [ 'method' => 'POST' ] );
+
+ if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
+ return false;
+ }
+
+ $response = wp_remote_retrieve_body( $response );
+ $response = json_decode( $response );
+
+ $credentials = base64_encode( "$response->username:$response->clear_password" ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
+
+ update_user_meta( $user_id, 'ep_token', $credentials );
+
+ return $credentials;
+ }
+
+ /**
+ * Checks if the token API can be used.
+ *
+ * @return boolean Whether the token API can be used.
+ */
+ public function check_token_permission() {
+ /**
+ * Filters the capability required to use the token API.
+ *
+ * @since 4.5.0
+ * @hook ep_token_capability
+ * @param {string} $capability Required capability.
+ */
+ $capability = apply_filters( 'ep_token_capability', 'edit_others_shop_orders' );
+
+ return current_user_can( $capability );
+ }
+
+ /**
+ * Index shop orders.
+ *
+ * @param array $post_types Indexable post types.
+ * @return array Indexable post types.
+ */
+ public function post_types( $post_types ) {
+ $post_types['shop_order'] = 'shop_order';
+
+ return $post_types;
+ }
+
+ /**
+ * Index order statuses.
+ *
+ * @param array $post_statuses Indexable post statuses.
+ * @return array Indexable post statuses.
+ */
+ public function post_statuses( $post_statuses ) {
+ $order_statuses = wc_get_order_statuses();
+
+ return array_unique( array_merge( $post_statuses, array_keys( $order_statuses ) ) );
+ }
+
+ /**
+ * Add term suggestions to be indexed
+ *
+ * @param array $post_args Array of ES args.
+ * @return array
+ */
+ public function filter_term_suggest( $post_args ) {
+ if ( empty( $post_args['post_type'] ) || 'shop_order' !== $post_args['post_type'] ) {
+ return $post_args;
+ }
+
+ if ( empty( $post_args['meta'] ) ) {
+ return $post_args;
+ }
+
+ /**
+ * Add the order number as a meta (text) field, so we can freely search on it.
+ */
+ $order_id = $post_args['ID'];
+ if ( function_exists( 'wc_get_order' ) ) {
+ $order = wc_get_order( $post_args['ID'] );
+ if ( $order && is_a( $order, 'WC_Order' ) && method_exists( $order, 'get_order_number' ) ) {
+ $order_id = $order->get_order_number();
+ }
+ }
+
+ $post_args['meta']['order_number'] = [
+ [
+ 'raw' => $order_id,
+ 'value' => $order_id,
+ ],
+ ];
+
+ $suggest = [];
+
+ $fields_to_ngram = [
+ '_billing_email',
+ '_billing_last_name',
+ '_billing_first_name',
+ ];
+
+ foreach ( $fields_to_ngram as $field_to_ngram ) {
+ if ( ! empty( $post_args['meta'][ $field_to_ngram ] )
+ && ! empty( $post_args['meta'][ $field_to_ngram ][0] )
+ && ! empty( $post_args['meta'][ $field_to_ngram ][0]['value'] ) ) {
+ $suggest[] = $post_args['meta'][ $field_to_ngram ][0]['value'];
+ }
+ }
+
+ if ( ! empty( $suggest ) ) {
+ $post_args['term_suggest'] = $suggest;
+ }
+
+ return $post_args;
+ }
+
+ /**
+ * Add mapping for suggest fields
+ *
+ * @param array $mapping ES mapping.
+ * @return array
+ */
+ public function mapping( $mapping ) {
+ $post_indexable = Indexables::factory()->get( 'post' );
+
+ $mapping = $post_indexable->add_ngram_analyzer( $mapping );
+ $mapping = $post_indexable->add_term_suggest_field( $mapping );
+
+ return $mapping;
+ }
+
+ /**
+ * Set the search_fields parameter in the search template.
+ *
+ * @param array $search_fields Current search fields
+ * @param \WP_Query $query Query being executed
+ * @return array New search fields
+ */
+ public function set_search_fields( array $search_fields, \WP_Query $query ) : array {
+ $is_orders_search_template = (bool) $query->get( 'ep_order_search_template' );
+
+ if ( $is_orders_search_template ) {
+ $search_fields = [
+ 'meta.order_number.value',
+ 'term_suggest',
+ 'meta' => [
+ '_billing_email',
+ '_billing_last_name',
+ '_billing_first_name',
+ ],
+ ];
+ }
+
+ return $search_fields;
+ }
+
+ /**
+ * Allow password protected to be indexed.
+ *
+ * If Protected Content is enabled, do nothing. Otherwise, allow pw protected posts to be indexed.
+ * The feature restricts it back in maybe_set_posts_where()
+ *
+ * @see maybe_set_posts_where()
+ * @param array $args WP_Query args
+ * @return array
+ */
+ public function maybe_query_password_protected_posts( $args ) {
+ // Password protected posts are already being indexed, no need to do anything.
+ if ( isset( $args['has_password'] ) && is_null( $args['has_password'] ) ) {
+ return $args;
+ }
+
+ /**
+ * Set a flag in the query but allow it to index all password protected posts for now,
+ * so WP does not inject its own where clause.
+ */
+ $args['ep_orders_has_password'] = true;
+ $args['has_password'] = null;
+
+ return $args;
+ }
+
+ /**
+ * Restrict password protected posts back but allow orders.
+ *
+ * @see maybe_query_password_protected_posts
+ * @param string $where Current where clause
+ * @param WP_Query $query WP_Query
+ * @return string
+ */
+ public function maybe_set_posts_where( $where, $query ) {
+ global $wpdb;
+
+ if ( ! $query->get( 'ep_orders_has_password' ) ) {
+ return $where;
+ }
+
+ $where .= " AND ( {$wpdb->posts}.post_password = '' OR {$wpdb->posts}.post_type = 'shop_order' )";
+
+ return $where;
+ }
+}
diff --git a/includes/classes/Feature/WooCommerce/Products.php b/includes/classes/Feature/WooCommerce/Products.php
new file mode 100644
index 0000000000..b94179dc83
--- /dev/null
+++ b/includes/classes/Feature/WooCommerce/Products.php
@@ -0,0 +1,585 @@
+woocommerce = $woocommerce;
+ }
+
+ /**
+ * Setup product related hooks
+ */
+ public function setup() {
+ add_action( 'ep_formatted_args', [ $this, 'price_filter' ], 10, 3 );
+ add_filter( 'ep_prepare_meta_allowed_protected_keys', [ $this, 'allow_meta_keys' ] );
+ add_filter( 'ep_sync_taxonomies', [ $this, 'sync_taxonomies' ] );
+ add_filter( 'ep_term_suggest_post_type', [ $this, 'suggest_wc_add_post_type' ] );
+ add_filter( 'ep_facet_include_taxonomies', [ $this, 'add_product_attributes' ] );
+ add_filter( 'ep_weighting_fields_for_post_type', [ $this, 'add_product_attributes_to_weighting' ], 10, 2 );
+ 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 );
+
+ // Settings for Weight results by date
+ add_action( 'ep_weight_settings_after_search', [ $this, 'add_weight_settings_search' ] );
+ add_filter( 'ep_is_decaying_enabled', [ $this, 'maybe_disable_decaying' ], 10, 3 );
+ }
+
+ /**
+ * Modifies main query to allow filtering by price with WooCommerce "Filter by price" widget.
+ *
+ * @param array $args ES args
+ * @param array $query_args WP_Query args
+ * @param WP_Query $query WP_Query object
+ * @return array
+ */
+ public function price_filter( $args, $query_args, $query ) {
+ // Only can use widget on main query
+ if ( ! $query->is_main_query() ) {
+ return $args;
+ }
+
+ // Only can use widget on shop, product taxonomy, or search
+ if ( ! is_shop() && ! is_product_taxonomy() && ! is_search() ) {
+ return $args;
+ }
+
+ // phpcs:disable WordPress.Security.NonceVerification
+ if ( empty( $_GET['min_price'] ) && empty( $_GET['max_price'] ) ) {
+ return $args;
+ }
+
+ $min_price = ! empty( $_GET['min_price'] ) ? sanitize_text_field( wp_unslash( $_GET['min_price'] ) ) : null;
+ $max_price = ! empty( $_GET['max_price'] ) ? sanitize_text_field( wp_unslash( $_GET['max_price'] ) ) : null;
+ // phpcs:enable WordPress.Security.NonceVerification
+
+ if ( $query->is_search() ) {
+ /**
+ * This logic is iffy but the WC price filter widget is not intended for use with search anyway
+ */
+ $old_query = $args['query']['bool'];
+ unset( $args['query']['bool']['should'] );
+
+ if ( ! empty( $min_price ) ) {
+ $args['query']['bool']['must'][0]['range']['meta._price.long']['gte'] = $min_price;
+ }
+
+ if ( ! empty( $max_price ) ) {
+ $args['query']['bool']['must'][0]['range']['meta._price.long']['lte'] = $max_price;
+ }
+
+ $args['query']['bool']['must'][0]['range']['meta._price.long']['boost'] = 2.0;
+ $args['query']['bool']['must'][1]['bool'] = $old_query;
+ } else {
+ unset( $args['query']['match_all'] );
+
+ $args['query']['range']['meta._price.long']['gte'] = ! empty( $min_price ) ? $min_price : 0;
+
+ if ( ! empty( $min_price ) ) {
+ $args['query']['range']['meta._price.long']['gte'] = $min_price;
+ }
+
+ if ( ! empty( $max_price ) ) {
+ $args['query']['range']['meta._price.long']['lte'] = $max_price;
+ }
+
+ $args['query']['range']['meta._price.long']['boost'] = 2.0;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Index WooCommerce products meta fields
+ *
+ * @param array $meta Existing post meta
+ * @return array
+ */
+ public function allow_meta_keys( $meta ) {
+ return array_unique(
+ array_merge(
+ $meta,
+ array(
+ '_thumbnail_id',
+ '_product_attributes',
+ '_wpb_vc_js_status',
+ '_swatch_type',
+ 'total_sales',
+ '_downloadable',
+ '_virtual',
+ '_regular_price',
+ '_sale_price',
+ '_tax_status',
+ '_tax_class',
+ '_purchase_note',
+ '_featured',
+ '_weight',
+ '_length',
+ '_width',
+ '_height',
+ '_visibility',
+ '_sku',
+ '_sale_price_dates_from',
+ '_sale_price_dates_to',
+ '_price',
+ '_sold_individually',
+ '_manage_stock',
+ '_backorders',
+ '_stock',
+ '_upsell_ids',
+ '_crosssell_ids',
+ '_stock_status',
+ '_product_version',
+ '_product_tabs',
+ '_override_tab_layout',
+ '_suggested_price',
+ '_min_price',
+ '_variable_billing',
+ '_wc_average_rating',
+ '_product_image_gallery',
+ '_bj_lazy_load_skip_post',
+ '_min_variation_price',
+ '_max_variation_price',
+ '_min_price_variation_id',
+ '_max_price_variation_id',
+ '_min_variation_regular_price',
+ '_max_variation_regular_price',
+ '_min_regular_price_variation_id',
+ '_max_regular_price_variation_id',
+ '_min_variation_sale_price',
+ '_max_variation_sale_price',
+ '_min_sale_price_variation_id',
+ '_max_sale_price_variation_id',
+ '_default_attributes',
+ '_swatch_type_options',
+ '_variations_skus',
+ )
+ )
+ );
+ }
+
+ /**
+ * Index WooCommerce taxonomies
+ *
+ * @param array $taxonomies Index taxonomies array
+ * @return array
+ */
+ public function sync_taxonomies( $taxonomies ) {
+ $woo_taxonomies = [];
+
+ $product_type = get_taxonomy( 'product_type' );
+ if ( false !== $product_type ) {
+ $woo_taxonomies[] = $product_type;
+ }
+
+ $product_visibility = get_taxonomy( 'product_visibility' );
+ if ( false !== $product_visibility ) {
+ $woo_taxonomies[] = $product_visibility;
+ }
+
+ /**
+ * Note product_shipping_class, product_cat, and product_tag are already public. Make
+ * sure to index non-attribute taxonomies.
+ */
+ $attribute_taxonomies = wc_get_attribute_taxonomies();
+
+ if ( ! empty( $attribute_taxonomies ) ) {
+ foreach ( $attribute_taxonomies as $tax ) {
+ $name = wc_attribute_taxonomy_name( $tax->attribute_name );
+
+ if ( ! empty( $name ) ) {
+ if ( empty( $tax->attribute_ ) ) {
+ $woo_taxonomies[] = get_taxonomy( $name );
+ }
+ }
+ }
+ }
+
+ return array_merge( $taxonomies, $woo_taxonomies );
+ }
+
+ /**
+ * Add WC product post type to autosuggest
+ *
+ * @param array $post_types Array of post types (e.g. post, page)
+ * @return array
+ */
+ public function suggest_wc_add_post_type( $post_types ) {
+ if ( ! in_array( 'product', $post_types, true ) ) {
+ $post_types[] = 'product';
+ }
+
+ return $post_types;
+ }
+
+ /**
+ * Add WooCommerce Product Attributes to EP Facets.
+ *
+ * @param array $taxonomies Taxonomies array
+ * @return array
+ */
+ public function add_product_attributes( $taxonomies = [] ) {
+ $attribute_names = wc_get_attribute_taxonomy_names();
+
+ foreach ( $attribute_names as $name ) {
+ if ( ! taxonomy_exists( $name ) ) {
+ continue;
+ }
+ $taxonomies[ $name ] = get_taxonomy( $name );
+ }
+
+ return $taxonomies;
+ }
+
+ /**
+ * Add WooCommerce Fields to the Weighting Dashboard.
+ *
+ * @param array $fields Current weighting fields.
+ * @param string $post_type Current post type.
+ * @return array New fields.
+ */
+ public function add_product_attributes_to_weighting( $fields, $post_type ) {
+ if ( 'product' !== $post_type ) {
+ return $fields;
+ }
+
+ if ( ! empty( $fields['attributes']['children']['author_name'] ) ) {
+ unset( $fields['attributes']['children']['author_name'] );
+ }
+
+ $sku_key = 'meta._sku.value';
+
+ $fields['attributes']['children'][ $sku_key ] = array(
+ 'key' => $sku_key,
+ 'label' => __( 'SKU', 'elasticpress' ),
+ );
+
+ $variations_skus_key = 'meta._variations_skus.value';
+
+ $fields['attributes']['children'][ $variations_skus_key ] = array(
+ 'key' => $variations_skus_key,
+ 'label' => __( 'Variations SKUs', 'elasticpress' ),
+ );
+
+ return $fields;
+ }
+
+ /**
+ * Add WooCommerce Fields to the default values of the Weighting Dashboard.
+ *
+ * @param array $defaults Default values for the post type.
+ * @param string $post_type Current post type.
+ * @return array
+ */
+ public function add_product_default_post_type_weights( $defaults, $post_type ) {
+ if ( 'product' !== $post_type ) {
+ return $defaults;
+ }
+
+ if ( ! empty( $defaults['author_name'] ) ) {
+ unset( $defaults['author_name'] );
+ }
+
+ $defaults['meta._sku.value'] = array(
+ 'enabled' => true,
+ 'weight' => 1,
+ );
+
+ $defaults['meta._variations_skus.value'] = array(
+ 'enabled' => true,
+ 'weight' => 1,
+ );
+
+ return $defaults;
+ }
+
+ /**
+ * Add a new `_variations_skus` meta field to the product to be indexed in Elasticsearch.
+ *
+ * @param array $post_meta Post meta
+ * @param WP_Post $post Post object
+ * @return array
+ */
+ public function add_variations_skus_meta( $post_meta, $post ) {
+ if ( 'product' !== $post->post_type ) {
+ return $post_meta;
+ }
+
+ $product = wc_get_product( $post );
+ if ( ! $product ) {
+ return $post_meta;
+ }
+
+ $variations_ids = $product->get_children();
+
+ $post_meta['_variations_skus'] = array_reduce(
+ $variations_ids,
+ function ( $variations_skus, $current_id ) {
+ $variation = wc_get_product( $current_id );
+ if ( ! $variation || ! $variation->exists() ) {
+ return $variations_skus;
+ }
+ $variation_sku = $variation->get_sku();
+ if ( ! $variation_sku ) {
+ return $variations_skus;
+ }
+ $variations_skus[] = $variation_sku;
+ return $variations_skus;
+ },
+ []
+ );
+
+ return $post_meta;
+ }
+
+ /**
+ * Integrate ElasticPress with the WooCommerce Admin Product List.
+ *
+ * WooCommerce uses its `WC_Admin_List_Table_Products` class to control that screen. This
+ * function adds all necessary hooks to bypass the default behavior and integrate with ElasticPress.
+ * By default, WC runs a SQL query to get the Product IDs that match the list criteria and passes
+ * that list of IDs to the main WP_Query. This integration changes that process to a single query, run
+ * by ElasticPress.
+ *
+ * @param array $query_vars Query vars.
+ * @return array
+ */
+ public function admin_product_list_request_query( $query_vars ) {
+ global $typenow, $wc_list_table;
+
+ // Return if not in the correct screen.
+ if ( ! is_a( $wc_list_table, 'WC_Admin_List_Table_Products' ) || 'product' !== $typenow ) {
+ return $query_vars;
+ }
+
+ // Return if admin WP_Query integration is not turned on, i.e., Protect Content is not enabled.
+ if ( ! has_filter( 'ep_admin_wp_query_integration', '__return_true' ) ) {
+ return $query_vars;
+ }
+
+ /**
+ * Filter to skip integration with WooCommerce Admin Product List.
+ *
+ * @hook ep_woocommerce_integrate_admin_products_list
+ * @since 4.2.0
+ * @param {bool} $integrate True to integrate, false to preserve original behavior. Defaults to true.
+ * @param {array} $query_vars Query vars.
+ * @return {bool} New integrate value
+ */
+ if ( ! apply_filters( 'ep_woocommerce_integrate_admin_products_list', true, $query_vars ) ) {
+ return $query_vars;
+ }
+
+ add_action( 'pre_get_posts', [ $this, 'translate_args_admin_products_list' ], 12 );
+
+ // This short-circuits WooCommerce search for product IDs.
+ add_filter( 'woocommerce_product_pre_search_products', '__return_empty_array' );
+
+ return $query_vars;
+ }
+
+ /**
+ * Apply the necessary changes to WP_Query in WooCommerce Admin Product List.
+ *
+ * @param WP_Query $query The WP Query being executed.
+ */
+ public function translate_args_admin_products_list( $query ) {
+ // The `translate_args()` method sets it to `true` if we should integrate it.
+ if ( ! $query->get( 'ep_integrate', false ) ) {
+ return;
+ }
+
+ // WooCommerce unsets the search term right after using it to fetch product IDs. Here we add it back.
+ $search_term = ! empty( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
+ if ( ! empty( $search_term ) ) {
+ $query->set( 's', sanitize_text_field( $search_term ) ); // phpcs:ignore WordPress.Security.NonceVerification
+
+ /**
+ * Filter the fields used in WooCommerce Admin Product Search.
+ *
+ * ```
+ * add_filter(
+ * 'ep_woocommerce_admin_products_list_search_fields',
+ * function ( $wc_admin_search_fields ) {
+ * $wc_admin_search_fields['meta'][] = 'custom_field';
+ * return $wc_admin_search_fields;
+ * }
+ * );
+ * ```
+ *
+ * @hook ep_woocommerce_admin_products_list_search_fields
+ * @since 4.2.0
+ * @param {array} $wc_admin_search_fields Fields to be used in the WooCommerce Admin Product Search
+ * @return {array} New fields
+ */
+ $search_fields = apply_filters(
+ 'ep_woocommerce_admin_products_list_search_fields',
+ [
+ 'post_title',
+ 'post_content',
+ 'post_excerpt',
+ 'meta' => [
+ '_sku',
+ '_variations_skus',
+ ],
+ ]
+ );
+
+ $query->set( 'search_fields', $search_fields );
+ }
+
+ // Sets the meta query for `product_type` if needed. Also removed from the WP_Query by WC in `WC_Admin_List_Table_Products::query_filters()`.
+ $product_type_query = $query->get( 'product_type', '' );
+ $product_type_url = ! empty( $_GET['product_type'] ) ? sanitize_text_field( wp_unslash( $_GET['product_type'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
+ $allowed_prod_types = [ 'virtual', 'downloadable' ];
+ if ( empty( $product_type_query ) && ! empty( $product_type_url ) && in_array( $product_type_url, $allowed_prod_types, true ) ) {
+ $meta_query = $query->get( 'meta_query', [] );
+ $meta_query[] = [
+ 'key' => "_{$product_type_url}",
+ 'value' => 'yes',
+ ];
+ $query->set( 'meta_query', $meta_query );
+ }
+
+ // Sets the meta query for `stock_status` if needed.
+ $stock_status_query = $query->get( 'stock_status', '' );
+ $stock_status_url = ! empty( $_GET['stock_status'] ) ? sanitize_text_field( wp_unslash( $_GET['stock_status'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
+ $allowed_stock_status = [ 'instock', 'outofstock', 'onbackorder' ];
+ if ( empty( $stock_status_query ) && ! empty( $stock_status_url ) && in_array( $stock_status_url, $allowed_stock_status, true ) ) {
+ $meta_query = $query->get( 'meta_query', [] );
+ $meta_query[] = [
+ 'key' => '_stock_status',
+ 'value' => $stock_status_url,
+ ];
+ $query->set( 'meta_query', $meta_query );
+ }
+ }
+
+ /**
+ * Depending on the number of products display an admin notice in the custom sort screen for WooCommerce Products
+ *
+ * @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;
+ }
+
+ $documents_per_page_sync = IndexHelper::factory()->get_index_default_per_page();
+ 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 sort order.', 'elasticpress' ),
+ Utils\get_sync_url()
+ ),
+ 'type' => 'warning',
+ 'dismiss' => true,
+ ];
+
+ return $notices;
+ }
+
+ /**
+ * Conditionally resync products after applying a custom order.
+ *
+ * @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 ) {
+ $documents_per_page_sync = IndexHelper::factory()->get_index_default_per_page();
+ 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 );
+ }
+ }
+
+ /**
+ * Add weight by date settings related to WooCommerce
+ *
+ * @param array $settings Current settings.
+ */
+ public function add_weight_settings_search( $settings ) {
+ ?>
+
+
+ 1 ) {
+ return $is_decaying_enabled;
+ }
+
+ if ( 'disabled_includes_products' === $settings['decaying_enabled'] && ! in_array( 'product', $post_types, true ) ) {
+ return $is_decaying_enabled;
+ }
+
+ return false;
+ }
+}
diff --git a/includes/classes/Feature/WooCommerce/WooCommerce.php b/includes/classes/Feature/WooCommerce/WooCommerce.php
index 5bbfe41df8..6ac5a7c68e 100644
--- a/includes/classes/Feature/WooCommerce/WooCommerce.php
+++ b/includes/classes/Feature/WooCommerce/WooCommerce.php
@@ -23,7 +23,23 @@
*/
class WooCommerce extends Feature {
/**
- * If enabled, receive the Orders object instance
+ * If enabled, receive the OrdersAutosuggest object instance
+ *
+ * @since 4.7.0
+ * @var null|OrdersAutosuggest
+ */
+ public $orders_autosuggest = null;
+
+ /**
+ * Receive the Products object instance
+ *
+ * @since 4.7.0
+ * @var null|Products
+ */
+ public $products = null;
+
+ /**
+ * Receive the Orders object instance
*
* @since 4.5.0
* @var null|Orders
@@ -54,172 +70,39 @@ public function __construct() {
'orders' => '0',
];
- $this->orders = new Orders();
+ $this->orders = new Orders( $this );
+ $this->products = new Products( $this );
+ $this->orders_autosuggest = new OrdersAutosuggest();
parent::__construct();
}
/**
- * Index Woocommerce meta
- *
- * @param array $meta Existing post meta.
- * @param array $post Post arguments array.
- * @since 2.1
- * @return array
- */
- public function whitelist_meta_keys( $meta, $post ) {
- return array_unique(
- array_merge(
- $meta,
- array(
- '_thumbnail_id',
- '_product_attributes',
- '_wpb_vc_js_status',
- '_swatch_type',
- 'total_sales',
- '_downloadable',
- '_virtual',
- '_regular_price',
- '_sale_price',
- '_tax_status',
- '_tax_class',
- '_purchase_note',
- '_featured',
- '_weight',
- '_length',
- '_width',
- '_height',
- '_visibility',
- '_sku',
- '_sale_price_dates_from',
- '_sale_price_dates_to',
- '_price',
- '_sold_individually',
- '_manage_stock',
- '_backorders',
- '_stock',
- '_upsell_ids',
- '_crosssell_ids',
- '_stock_status',
- '_product_version',
- '_product_tabs',
- '_override_tab_layout',
- '_suggested_price',
- '_min_price',
- '_customer_user',
- '_variable_billing',
- '_wc_average_rating',
- '_product_image_gallery',
- '_bj_lazy_load_skip_post',
- '_min_variation_price',
- '_max_variation_price',
- '_min_price_variation_id',
- '_max_price_variation_id',
- '_min_variation_regular_price',
- '_max_variation_regular_price',
- '_min_regular_price_variation_id',
- '_max_regular_price_variation_id',
- '_min_variation_sale_price',
- '_max_variation_sale_price',
- '_min_sale_price_variation_id',
- '_max_sale_price_variation_id',
- '_default_attributes',
- '_swatch_type_options',
- '_order_key',
- '_billing_company',
- '_billing_address_1',
- '_billing_address_2',
- '_billing_city',
- '_billing_postcode',
- '_billing_country',
- '_billing_state',
- '_billing_email',
- '_billing_phone',
- '_shipping_address_1',
- '_shipping_address_2',
- '_shipping_city',
- '_shipping_postcode',
- '_shipping_country',
- '_shipping_state',
- '_billing_last_name',
- '_billing_first_name',
- '_shipping_first_name',
- '_shipping_last_name',
- '_variations_skus',
- )
- )
- );
- }
-
- /**
- * Make sure all loop shop post ins are IDS. We have to pass post objects here since we override
- * the fields=>id query for the layered filter nav query
- *
- * @param array $posts Post object array.
- * @since 2.1
- * @return array
- */
- public function convert_post_object_to_id( $posts ) {
- _doing_it_wrong( __METHOD__, 'This filter was removed from WooCommerce and will be removed from ElasticPress in a future release.', '4.5.0' );
- return $posts;
- }
-
- /**
- * Index Woocommerce taxonomies
+ * Setup all feature filters
*
- * @param array $taxonomies Index taxonomies array.
- * @param array $post Post properties array.
- * @since 2.1
- * @return array
+ * @since 2.1
*/
- public function whitelist_taxonomies( $taxonomies, $post ) {
- $woo_taxonomies = [];
-
- $product_type = get_taxonomy( 'product_type' );
- if ( false !== $product_type ) {
- $woo_taxonomies[] = $product_type;
- }
-
- $product_visibility = get_taxonomy( 'product_visibility' );
- if ( false !== $product_visibility ) {
- $woo_taxonomies[] = $product_visibility;
+ public function setup() {
+ if ( ! function_exists( 'WC' ) ) {
+ return;
}
- /**
- * Note product_shipping_class, product_cat, and product_tag are already public. Make
- * sure to index non-attribute taxonomies.
- */
- $attribute_taxonomies = wc_get_attribute_taxonomies();
-
- if ( ! empty( $attribute_taxonomies ) ) {
- foreach ( $attribute_taxonomies as $tax ) {
- $name = wc_attribute_taxonomy_name( $tax->attribute_name );
+ $this->products->setup();
+ $this->orders->setup();
- if ( ! empty( $name ) ) {
- if ( empty( $tax->attribute_ ) ) {
- $woo_taxonomies[] = get_taxonomy( $name );
- }
- }
- }
- }
+ add_filter( 'ep_integrate_search_queries', [ $this, 'disallow_coupons' ], 10, 2 );
- return array_merge( $taxonomies, $woo_taxonomies );
- }
+ // These hooks are deprecated and will be removed in an upcoming major version of ElasticPress
+ add_filter( 'woocommerce_layered_nav_query_post_ids', [ $this, 'convert_post_object_to_id' ], 10, 4 );
+ add_filter( 'woocommerce_unfiltered_product_ids', [ $this, 'convert_post_object_to_id' ], 10, 4 );
+ add_action( 'ep_wp_query_search_cached_posts', [ $this, 'disallow_duplicated_query' ], 10, 2 );
- /**
- * Disallow duplicated ES queries on Orders page.
- *
- * @since 2.4
- *
- * @param array $value Original filter values.
- * @param WP_Query $query WP_Query
- *
- * @return array
- */
- public function disallow_duplicated_query( $value, $query ) {
- _doing_it_wrong( __METHOD__, 'This filter was removed from WooCommerce and will be removed from ElasticPress in a future release.', '4.5.0' );
+ add_action( 'pre_get_posts', [ $this, 'translate_args' ], 11, 1 );
- return $value;
+ // Orders Autosuggest feature.
+ if ( $this->is_orders_autosuggest_enabled() ) {
+ $this->orders_autosuggest->setup();
+ }
}
/**
@@ -591,36 +474,15 @@ public function get_orderby_meta_mapping( $meta_key ) {
return 'date';
}
-
- /**
- * Returns the WooCommerce-oriented post types in admin that EP will search
- *
- * @since 4.4.0
- * @return mixed|void
- */
- public function get_admin_searchable_post_types() {
- $searchable_post_types = array( 'shop_order' );
-
- /**
- * Filter admin searchable WooCommerce post types
- *
- * @hook ep_woocommerce_admin_searchable_post_types
- * @since 4.4.0
- * @param {array} $post_types Post types
- * @return {array} New post types
- */
- return apply_filters( 'ep_woocommerce_admin_searchable_post_types', $searchable_post_types );
- }
-
/**
* Make search coupons don't go through ES
*
* @param bool $enabled Coupons enabled or not
* @param WP_Query $query WP Query
- * @since 2.1
+ * @since 4.7.0
* @return bool
*/
- public function blacklist_coupons( $enabled, $query ) {
+ public function disallow_coupons( $enabled, $query ) {
if ( is_admin() ) {
return $enabled;
}
@@ -632,271 +494,6 @@ public function blacklist_coupons( $enabled, $query ) {
return $enabled;
}
- /**
- * Allow order creations on the front end to get synced
- *
- * @since 2.1
- * @param bool $override Original order perms check value
- * @param int $post_id Post ID
- * @return bool
- */
- public function bypass_order_permissions_check( $override, $post_id ) {
- $searchable_post_types = $this->get_admin_searchable_post_types();
-
- if ( in_array( get_post_type( $post_id ), $searchable_post_types, true ) ) {
- return true;
- }
-
- return $override;
- }
-
- /**
- * Sets woocommerce meta search fields to an empty array if we are integrating the main query with ElasticSearch
- *
- * Woocommerce calls this action as part of its own callback on parse_query. We add this filter only if the query
- * is integrated with ElasticSearch.
- * If we were to always return array() on this filter, we'd break admin searches when WooCommerce module is activated
- * without the Protected Content Module
- *
- * @param \WP_Query $query Current query
- */
- public function maybe_hook_woocommerce_search_fields( $query ) {
- global $pagenow, $wp, $wc_list_table, $wp_filter;
-
- if ( ! $this->should_integrate_with_query( $query ) ) {
- return;
- }
-
- /**
- * Determines actions to be applied, or removed, if doing a WooCommerce serarch
- *
- * @hook ep_woocommerce_hook_search_fields
- * @since 4.4.0
- */
- do_action( 'ep_woocommerce_hook_search_fields' );
-
- if ( 'edit.php' !== $pagenow || empty( $wp->query_vars['s'] ) || 'shop_order' !== $wp->query_vars['post_type'] || ! isset( $_GET['s'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
- return;
- }
-
- remove_action( 'parse_query', [ $wc_list_table, 'search_custom_fields' ] );
- }
-
- /**
- * Enhance WooCommerce search order by order id, email, phone number, name, etc..
- * What this function does:
- * 1. Reverse the woocommerce shop_order_search_custom_fields query
- * 2. If the search key is integer and it is an Order Id, just query with post__in
- * 3. If the search key is integer but not an order id ( might be phone number ), use ES to find it
- *
- * @param WP_Query $wp WP Query
- * @since 2.3
- */
- public function search_order( $wp ) {
- if ( ! $this->should_integrate_with_query( $wp ) ) {
- return;
- }
-
- global $pagenow;
-
- $searchable_post_types = $this->get_admin_searchable_post_types();
-
- if ( 'edit.php' !== $pagenow || empty( $wp->query_vars['post_type'] ) || ! in_array( $wp->query_vars['post_type'], $searchable_post_types, true ) ||
- ( empty( $wp->query_vars['s'] ) && empty( $wp->query_vars['shop_order_search'] ) ) ) {
- return;
- }
-
- // phpcs:disable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
- if ( isset( $_GET['s'] ) ) {
- $search_key_safe = str_replace( array( 'Order #', '#' ), '', wc_clean( $_GET['s'] ) );
- unset( $wp->query_vars['post__in'] );
- $wp->query_vars['s'] = $search_key_safe;
- }
- // phpcs:enable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
- }
-
- /**
- * Add order items as a searchable string.
- *
- * This mimics how WooCommerce currently does in the order_itemmeta
- * table. They combine the titles of the products and put them in a
- * meta field called "Items".
- *
- * @since 2.4
- *
- * @param array $post_args Post arguments
- * @param string|int $post_id Post id
- *
- * @return array
- */
- public function add_order_items_search( $post_args, $post_id ) {
- $searchable_post_types = $this->get_admin_searchable_post_types();
-
- // Make sure it is only WooCommerce orders we touch.
- if ( ! in_array( $post_args['post_type'], $searchable_post_types, true ) ) {
- return $post_args;
- }
-
- $post_indexable = Indexables::factory()->get( 'post' );
-
- // Get order items.
- $order = wc_get_order( $post_id );
- $item_meta = [];
- foreach ( $order->get_items() as $delta => $product_item ) {
- // WooCommerce 3.x uses WC_Order_Item_Product instance while 2.x an array
- if ( is_object( $product_item ) && method_exists( $product_item, 'get_name' ) ) {
- $item_meta['_items'][] = $product_item->get_name( 'edit' );
- } elseif ( is_array( $product_item ) && isset( $product_item['name'] ) ) {
- $item_meta['_items'][] = $product_item['name'];
- }
- }
-
- // Prepare order items.
- $item_meta['_items'] = empty( $item_meta['_items'] ) ? '' : implode( '|', $item_meta['_items'] );
- $post_args['meta'] = array_merge( $post_args['meta'], $post_indexable->prepare_meta_types( $item_meta ) );
-
- return $post_args;
- }
-
- /**
- * Add WooCommerce Product Attributes to EP Facets.
- *
- * @param array $taxonomies Taxonomies array
- * @return array
- */
- public function add_product_attributes( $taxonomies = [] ) {
- $attribute_names = wc_get_attribute_taxonomy_names();
-
- foreach ( $attribute_names as $name ) {
- if ( ! taxonomy_exists( $name ) ) {
- continue;
- }
- $taxonomies[ $name ] = get_taxonomy( $name );
- }
-
- return $taxonomies;
- }
-
- /**
- * Add WooCommerce Fields to the Weighting Dashboard.
- *
- * @since 3.x
- *
- * @param array $fields Current weighting fields.
- * @param string $post_type Current post type.
- * @return array New fields.
- */
- public function add_product_attributes_to_weighting( $fields, $post_type ) {
- if ( 'product' === $post_type ) {
- if ( ! empty( $fields['attributes']['children']['author_name'] ) ) {
- unset( $fields['attributes']['children']['author_name'] );
- }
-
- $sku_key = 'meta._sku.value';
-
- $fields['attributes']['children'][ $sku_key ] = array(
- 'key' => $sku_key,
- 'label' => __( 'SKU', 'elasticpress' ),
- );
-
- $variations_skus_key = 'meta._variations_skus.value';
-
- $fields['attributes']['children'][ $variations_skus_key ] = array(
- 'key' => $variations_skus_key,
- 'label' => __( 'Variations SKUs', 'elasticpress' ),
- );
- }
- return $fields;
- }
-
- /**
- * Add WooCommerce Fields to the default values of the Weighting Dashboard.
- *
- * @since 3.x
- *
- * @param array $defaults Default values for the post type.
- * @param string $post_type Current post type.
- * @return array
- */
- public function add_product_default_post_type_weights( $defaults, $post_type ) {
- if ( 'product' === $post_type ) {
- if ( ! empty( $defaults['author_name'] ) ) {
- unset( $defaults['author_name'] );
- }
-
- $defaults['meta._sku.value'] = array(
- 'enabled' => true,
- 'weight' => 1,
- );
-
- $defaults['meta._variations_skus.value'] = array(
- 'enabled' => true,
- 'weight' => 1,
- );
- }
- return $defaults;
- }
-
- /**
- * Add WC post type to autosuggest
- *
- * @param array $post_types Array of post types (e.g. post, page).
- * @since 2.6
- * @return array
- */
- public function suggest_wc_add_post_type( $post_types ) {
- if ( ! in_array( 'product', $post_types, true ) ) {
- $post_types[] = 'product';
- }
-
- return $post_types;
- }
-
- /**
- * Setup all feature filters
- *
- * @since 2.1
- */
- public function setup() {
- if ( ! function_exists( 'WC' ) ) {
- return;
- }
-
- add_action( 'ep_formatted_args', [ $this, 'price_filter' ], 10, 3 );
- add_filter( 'ep_sync_insert_permissions_bypass', [ $this, 'bypass_order_permissions_check' ], 10, 2 );
- add_filter( 'ep_integrate_search_queries', [ $this, 'blacklist_coupons' ], 10, 2 );
- add_filter( 'ep_prepare_meta_allowed_protected_keys', [ $this, 'whitelist_meta_keys' ], 10, 2 );
- add_filter( 'woocommerce_layered_nav_query_post_ids', [ $this, 'convert_post_object_to_id' ], 10, 4 );
- add_filter( 'woocommerce_unfiltered_product_ids', [ $this, 'convert_post_object_to_id' ], 10, 4 );
- add_filter( 'ep_sync_taxonomies', [ $this, 'whitelist_taxonomies' ], 10, 2 );
- add_filter( 'ep_post_sync_args_post_prepare_meta', [ $this, 'add_order_items_search' ], 20, 2 );
- add_filter( 'ep_pc_skip_post_content_cleanup', [ $this, 'keep_order_fields' ], 20, 2 );
- add_action( 'pre_get_posts', [ $this, 'translate_args' ], 11, 1 );
- add_action( 'ep_wp_query_search_cached_posts', [ $this, 'disallow_duplicated_query' ], 10, 2 );
- add_action( 'parse_query', [ $this, 'maybe_hook_woocommerce_search_fields' ], 1 );
- add_action( 'parse_query', [ $this, 'search_order' ], 11 );
- add_filter( 'ep_term_suggest_post_type', [ $this, 'suggest_wc_add_post_type' ] );
- add_filter( 'ep_facet_include_taxonomies', [ $this, 'add_product_attributes' ] );
- add_filter( 'ep_weighting_fields_for_post_type', [ $this, 'add_product_attributes_to_weighting' ], 10, 2 );
- 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 );
-
- // Orders Autosuggest feature.
- if ( $this->is_orders_autosuggest_enabled() ) {
- $this->orders->setup();
- }
-
- // Add WooCommerce Settings for Weight results by date
- add_action( 'ep_weight_settings_after_search', [ $this, 'add_weight_settings_search' ] );
- // Modify decaying based on WooCommerce Settings
- add_filter( 'ep_is_decaying_enabled', [ $this, 'maybe_disable_decaying' ], 10, 3 );
- }
-
/**
* Output feature box long
*
@@ -981,265 +578,13 @@ public function requirements_status() {
}
/**
- * Modifies main query to allow filtering by price with WooCommerce "Filter by price" widget.
- *
- * @param array $args ES args
- * @param array $query_args WP_Query args
- * @param WP_Query $query WP_Query object
- * @since 3.2
- * @return array
- */
- public function price_filter( $args, $query_args, $query ) {
- // Only can use widget on main query
- if ( ! $query->is_main_query() ) {
- return $args;
- }
-
- // Only can use widget on shop, product taxonomy, or search
- if ( ! is_shop() && ! is_product_taxonomy() && ! is_search() ) {
- return $args;
- }
-
- // phpcs:disable WordPress.Security.NonceVerification
- if ( empty( $_GET['min_price'] ) && empty( $_GET['max_price'] ) ) {
- return $args;
- }
-
- $min_price = ! empty( $_GET['min_price'] ) ? sanitize_text_field( wp_unslash( $_GET['min_price'] ) ) : null;
- $max_price = ! empty( $_GET['max_price'] ) ? sanitize_text_field( wp_unslash( $_GET['max_price'] ) ) : null;
- // phpcs:enable WordPress.Security.NonceVerification
-
- if ( $query->is_search() ) {
- /**
- * This logic is iffy but the WC price filter widget is not intended for use with search anyway
- */
- $old_query = $args['query']['bool'];
- unset( $args['query']['bool']['should'] );
-
- if ( ! empty( $min_price ) ) {
- $args['query']['bool']['must'][0]['range']['meta._price.long']['gte'] = $min_price;
- }
-
- if ( ! empty( $max_price ) ) {
- $args['query']['bool']['must'][0]['range']['meta._price.long']['lte'] = $max_price;
- }
-
- $args['query']['bool']['must'][0]['range']['meta._price.long']['boost'] = 2.0;
- $args['query']['bool']['must'][1]['bool'] = $old_query;
- } else {
- unset( $args['query']['match_all'] );
-
- $args['query']['range']['meta._price.long']['gte'] = ! empty( $min_price ) ? $min_price : 0;
-
- if ( ! empty( $min_price ) ) {
- $args['query']['range']['meta._price.long']['gte'] = $min_price;
- }
-
- if ( ! empty( $max_price ) ) {
- $args['query']['range']['meta._price.long']['lte'] = $max_price;
- }
-
- $args['query']['range']['meta._price.long']['boost'] = 2.0;
- }
-
- return $args;
- }
-
- /**
- * Prevent order fields from being removed.
- *
- * When Protected Content is enabled, all posts with password have their content removed.
- * This can't happen for orders, as the order key is added in that field.
- *
- * @see https://github.com/10up/ElasticPress/issues/2726
- *
- * @since 4.2.0
- * @param bool $skip Whether the password protected content should have their content, and meta removed
- * @param array $post_args Post arguments
- * @return bool
- */
- public function keep_order_fields( $skip, $post_args ) {
- $searchable_post_types = $this->get_admin_searchable_post_types();
-
- if ( in_array( $post_args['post_type'], $searchable_post_types, true ) ) {
- return true;
- }
-
- return $skip;
- }
-
- /**
- * Add a new `_variations_skus` meta field to the product to be indexed in Elasticsearch.
- *
- * @since 4.2.0
- * @param array $post_meta Post meta
- * @param WP_Post $post Post object
- * @return array
- */
- public function add_variations_skus_meta( $post_meta, $post ) {
- if ( 'product' !== $post->post_type ) {
- return $post_meta;
- }
-
- $product = wc_get_product( $post );
- if ( ! $product ) {
- return $post_meta;
- }
-
- $variations_ids = $product->get_children();
-
- $post_meta['_variations_skus'] = array_reduce(
- $variations_ids,
- function ( $variations_skus, $current_id ) {
- $variation = wc_get_product( $current_id );
- if ( ! $variation || ! $variation->exists() ) {
- return $variations_skus;
- }
- $variation_sku = $variation->get_sku();
- if ( ! $variation_sku ) {
- return $variations_skus;
- }
- $variations_skus[] = $variation_sku;
- return $variations_skus;
- },
- []
- );
-
- return $post_meta;
- }
-
- /**
- * Integrate ElasticPress with the WooCommerce Admin Product List.
- *
- * WooCommerce uses its `WC_Admin_List_Table_Products` class to control that screen. This
- * function adds all necessary hooks to bypass the default behavior and integrate with ElasticPress.
- * By default, WC runs a SQL query to get the Product IDs that match the list criteria and passes
- * that list of IDs to the main WP_Query. This integration changes that process to a single query, run
- * by ElasticPress.
- *
- * @since 4.2.0
- * @param array $query_vars Query vars.
- * @return array
- */
- public function admin_product_list_request_query( $query_vars ) {
- global $typenow, $wc_list_table;
-
- // Return if not in the correct screen.
- if ( ! is_a( $wc_list_table, 'WC_Admin_List_Table_Products' ) || 'product' !== $typenow ) {
- return $query_vars;
- }
-
- // Return if admin WP_Query integration is not turned on, i.e., Protect Content is not enabled.
- if ( ! has_filter( 'ep_admin_wp_query_integration', '__return_true' ) ) {
- return $query_vars;
- }
-
- /**
- * Filter to skip integration with WooCommerce Admin Product List.
- *
- * @hook ep_woocommerce_integrate_admin_products_list
- * @since 4.2.0
- * @param {bool} $integrate True to integrate, false to preserve original behavior. Defaults to true.
- * @param {array} $query_vars Query vars.
- * @return {bool} New integrate value
- */
- if ( ! apply_filters( 'ep_woocommerce_integrate_admin_products_list', true, $query_vars ) ) {
- return $query_vars;
- }
-
- add_action( 'pre_get_posts', [ $this, 'translate_args_admin_products_list' ], 12 );
-
- // This short-circuits WooCommerce search for product IDs.
- add_filter( 'woocommerce_product_pre_search_products', '__return_empty_array' );
-
- return $query_vars;
- }
-
- /**
- * Apply the necessary changes to WP_Query in WooCommerce Admin Product List.
- *
- * @param WP_Query $query The WP Query being executed.
- */
- public function translate_args_admin_products_list( $query ) {
- // The `translate_args()` method sets it to `true` if we should integrate it.
- if ( ! $query->get( 'ep_integrate', false ) ) {
- return;
- }
-
- // WooCommerce unsets the search term right after using it to fetch product IDs. Here we add it back.
- $search_term = ! empty( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
- if ( ! empty( $search_term ) ) {
- $query->set( 's', sanitize_text_field( $search_term ) ); // phpcs:ignore WordPress.Security.NonceVerification
-
- /**
- * Filter the fields used in WooCommerce Admin Product Search.
- *
- * ```
- * add_filter(
- * 'ep_woocommerce_admin_products_list_search_fields',
- * function ( $wc_admin_search_fields ) {
- * $wc_admin_search_fields['meta'][] = 'custom_field';
- * return $wc_admin_search_fields;
- * }
- * );
- * ```
- *
- * @hook ep_woocommerce_admin_products_list_search_fields
- * @since 4.2.0
- * @param {array} $wc_admin_search_fields Fields to be used in the WooCommerce Admin Product Search
- * @return {array} New fields
- */
- $search_fields = apply_filters(
- 'ep_woocommerce_admin_products_list_search_fields',
- [
- 'post_title',
- 'post_content',
- 'post_excerpt',
- 'meta' => [
- '_sku',
- '_variations_skus',
- ],
- ]
- );
-
- $query->set( 'search_fields', $search_fields );
- }
-
- // Sets the meta query for `product_type` if needed. Also removed from the WP_Query by WC in `WC_Admin_List_Table_Products::query_filters()`.
- $product_type_query = $query->get( 'product_type', '' );
- $product_type_url = ! empty( $_GET['product_type'] ) ? sanitize_text_field( wp_unslash( $_GET['product_type'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
- $allowed_prod_types = [ 'virtual', 'downloadable' ];
- if ( empty( $product_type_query ) && ! empty( $product_type_url ) && in_array( $product_type_url, $allowed_prod_types, true ) ) {
- $meta_query = $query->get( 'meta_query', [] );
- $meta_query[] = [
- 'key' => "_{$product_type_url}",
- 'value' => 'yes',
- ];
- $query->set( 'meta_query', $meta_query );
- }
-
- // Sets the meta query for `stock_status` if needed.
- $stock_status_query = $query->get( 'stock_status', '' );
- $stock_status_url = ! empty( $_GET['stock_status'] ) ? sanitize_text_field( wp_unslash( $_GET['stock_status'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
- $allowed_stock_status = [ 'instock', 'outofstock', 'onbackorder' ];
- if ( empty( $stock_status_query ) && ! empty( $stock_status_url ) && in_array( $stock_status_url, $allowed_stock_status, true ) ) {
- $meta_query = $query->get( 'meta_query', [] );
- $meta_query[] = [
- 'key' => '_stock_status',
- 'value' => $stock_status_url,
- ];
- $query->set( 'meta_query', $meta_query );
- }
- }
-
- /**
- * Determines whether or not ES should be integrating with the provided query
+ * Determines whether or not ES should be integrating with the provided query
*
* @param \WP_Query $query Query we might integrate with
*
* @return bool
*/
- protected function should_integrate_with_query( $query ) {
+ public function should_integrate_with_query( $query ) {
// Lets make sure this doesn't interfere with the CLI
if ( defined( 'WP_CLI' ) && WP_CLI ) {
return false;
@@ -1295,61 +640,6 @@ 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;
- }
-
- $documents_per_page_sync = IndexHelper::factory()->get_index_default_per_page();
- 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 sort 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 ) {
-
- $documents_per_page_sync = IndexHelper::factory()->get_index_default_per_page();
- 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 );
- }
- }
-
/**
* Whether orders autosuggest is available or not
*
@@ -1378,21 +668,314 @@ public function is_orders_autosuggest_enabled() : bool {
return $this->is_orders_autosuggest_available() && '1' === $this->get_setting( 'orders' );
}
+
+ /**
+ * DEPRECATED. Index Woocommerce meta
+ *
+ * @param array $meta Existing post meta.
+ * @param array $post Post arguments array.
+ * @since 2.1
+ * @return array
+ */
+ public function whitelist_meta_keys( $meta, $post ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->allow_meta_keys() AND/OR \ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders->allow_meta_keys()" );
+ return array_unique(
+ array_merge(
+ $this->products->allow_meta_keys( $meta ),
+ $this->orders->allow_meta_keys( $meta )
+ )
+ );
+ }
+
+ /**
+ * DEPRECATED. Make sure all loop shop post ins are IDS. We have to pass post objects here since we override
+ * the fields=>id query for the layered filter nav query
+ *
+ * @param array $posts Post object array.
+ * @since 2.1
+ * @return array
+ */
+ public function convert_post_object_to_id( $posts ) {
+ _doing_it_wrong( __METHOD__, 'This filter was removed from WooCommerce and will be removed from ElasticPress in a future release.', '4.5.0' );
+ return $posts;
+ }
+
/**
- * Add weight by date settings related to WooCommerce
+ * DEPRECATED. Index Woocommerce taxonomies
+ *
+ * @param array $taxonomies Index taxonomies array.
+ * @param array $post Post properties array.
+ * @since 2.1
+ * @return array
+ */
+ public function whitelist_taxonomies( $taxonomies, $post ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->sync_taxonomies()" );
+ return $this->products->sync_taxonomies( $taxonomies );
+ }
+
+ /**
+ * DEPRECATED. Disallow duplicated ES queries on Orders page.
+ *
+ * @since 2.4
+ *
+ * @param array $value Original filter values.
+ * @param WP_Query $query WP_Query
+ *
+ * @return array
+ */
+ public function disallow_duplicated_query( $value, $query ) {
+ _doing_it_wrong( __METHOD__, 'This filter was removed from WooCommerce and will be removed from ElasticPress in a future release.', '4.5.0' );
+
+ return $value;
+ }
+
+ /**
+ * DEPRECATED. Returns the WooCommerce-oriented post types in admin that EP will search
+ *
+ * @since 4.4.0
+ * @return mixed|void
+ */
+ public function get_admin_searchable_post_types() {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders->get_admin_searchable_post_types()" );
+ return $this->orders->get_admin_searchable_post_types();
+ }
+
+ /**
+ * DEPRECATED. Make search coupons don't go through ES
+ *
+ * @param bool $enabled Coupons enabled or not
+ * @param WP_Query $query WP Query
+ * @since 2.1
+ * @return bool
+ */
+ public function blacklist_coupons( $enabled, $query ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->disallow_coupons()" );
+ return $this->disallow_coupons( $enabled, $query );
+ }
+
+ /**
+ * DEPRECATED. Allow order creations on the front end to get synced
+ *
+ * @since 2.1
+ * @param bool $override Original order perms check value
+ * @param int $post_id Post ID
+ * @return bool
+ */
+ public function bypass_order_permissions_check( $override, $post_id ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders->price_filter()" );
+ return $this->orders->bypass_order_permissions_check( $override, $post_id );
+ }
+
+ /**
+ * DEPRECATED. Sets woocommerce meta search fields to an empty array if we are integrating the main query with ElasticSearch
+ *
+ * Woocommerce calls this action as part of its own callback on parse_query. We add this filter only if the query
+ * is integrated with ElasticSearch.
+ * If we were to always return array() on this filter, we'd break admin searches when WooCommerce module is activated
+ * without the Protected Content Module
+ *
+ * @param \WP_Query $query Current query
+ */
+ public function maybe_hook_woocommerce_search_fields( $query ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders->maybe_hook_woocommerce_search_fields()" );
+ return $this->orders->maybe_hook_woocommerce_search_fields( $query );
+ }
+
+ /**
+ * DEPRECATED. Enhance WooCommerce search order by order id, email, phone number, name, etc..
+ * What this function does:
+ * 1. Reverse the woocommerce shop_order_search_custom_fields query
+ * 2. If the search key is integer and it is an Order Id, just query with post__in
+ * 3. If the search key is integer but not an order id ( might be phone number ), use ES to find it
+ *
+ * @param WP_Query $wp WP Query
+ * @since 2.3
+ */
+ public function search_order( $wp ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders->search_order()" );
+ return $this->orders->search_order( $wp );
+ }
+
+ /**
+ * DEPRECATED. Add order items as a searchable string.
+ *
+ * This mimics how WooCommerce currently does in the order_itemmeta
+ * table. They combine the titles of the products and put them in a
+ * meta field called "Items".
+ *
+ * @since 2.4
+ *
+ * @param array $post_args Post arguments
+ * @param string|int $post_id Post id
+ *
+ * @return array
+ */
+ public function add_order_items_search( $post_args, $post_id ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders->add_order_items_search()" );
+ return $this->orders->add_order_items_search( $post_args, $post_id );
+ }
+
+ /**
+ * DEPRECATED. Add WooCommerce Product Attributes to EP Facets.
+ *
+ * @param array $taxonomies Taxonomies array
+ * @return array
+ */
+ public function add_product_attributes( $taxonomies = [] ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->add_product_attributes()" );
+ return $this->products->add_product_attributes( $taxonomies );
+ }
+
+ /**
+ * DEPRECATED. Add WooCommerce Fields to the Weighting Dashboard.
+ *
+ * @since 3.x
+ *
+ * @param array $fields Current weighting fields.
+ * @param string $post_type Current post type.
+ * @return array New fields.
+ */
+ public function add_product_attributes_to_weighting( $fields, $post_type ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->add_product_attributes_to_weighting()" );
+ return $this->products->add_product_attributes_to_weighting( $fields, $post_type );
+ }
+
+ /**
+ * DEPRECATED. Add WooCommerce Fields to the default values of the Weighting Dashboard.
+ *
+ * @since 3.x
+ *
+ * @param array $defaults Default values for the post type.
+ * @param string $post_type Current post type.
+ * @return array
+ */
+ public function add_product_default_post_type_weights( $defaults, $post_type ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->add_product_default_post_type_weights()" );
+ return $this->products->add_product_default_post_type_weights( $defaults, $post_type );
+ }
+
+ /**
+ * DEPRECATED. Add WC post type to autosuggest
+ *
+ * @param array $post_types Array of post types (e.g. post, page).
+ * @since 2.6
+ * @return array
+ */
+ public function suggest_wc_add_post_type( $post_types ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->suggest_wc_add_post_type()" );
+ return $this->products->suggest_wc_add_post_type( $post_types );
+ }
+
+ /**
+ * DEPRECATED. Modifies main query to allow filtering by price with WooCommerce "Filter by price" widget.
+ *
+ * @param array $args ES args
+ * @param array $query_args WP_Query args
+ * @param WP_Query $query WP_Query object
+ * @since 3.2
+ * @return array
+ */
+ public function price_filter( $args, $query_args, $query ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->price_filter()" );
+ return $this->products->price_filter( $args, $query_args, $query );
+ }
+
+ /**
+ * DEPRECATED. Prevent order fields from being removed.
+ *
+ * When Protected Content is enabled, all posts with password have their content removed.
+ * This can't happen for orders, as the order key is added in that field.
+ *
+ * @see https://github.com/10up/ElasticPress/issues/2726
+ *
+ * @since 4.2.0
+ * @param bool $skip Whether the password protected content should have their content, and meta removed
+ * @param array $post_args Post arguments
+ * @return bool
+ */
+ public function keep_order_fields( $skip, $post_args ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders->keep_order_fields()" );
+ return $this->orders->keep_order_fields( $skip, $post_args );
+ }
+
+ /**
+ * DEPRECATED. Add a new `_variations_skus` meta field to the product to be indexed in Elasticsearch.
+ *
+ * @since 4.2.0
+ * @param array $post_meta Post meta
+ * @param WP_Post $post Post object
+ * @return array
+ */
+ public function add_variations_skus_meta( $post_meta, $post ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->add_variations_skus_meta()" );
+ return $this->products->add_variations_skus_meta( $post_meta, $post );
+ }
+
+ /**
+ * DEPRECATED. Integrate ElasticPress with the WooCommerce Admin Product List.
+ *
+ * WooCommerce uses its `WC_Admin_List_Table_Products` class to control that screen. This
+ * function adds all necessary hooks to bypass the default behavior and integrate with ElasticPress.
+ * By default, WC runs a SQL query to get the Product IDs that match the list criteria and passes
+ * that list of IDs to the main WP_Query. This integration changes that process to a single query, run
+ * by ElasticPress.
+ *
+ * @since 4.2.0
+ * @param array $query_vars Query vars.
+ * @return array
+ */
+ public function admin_product_list_request_query( $query_vars ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->admin_product_list_request_query()" );
+ return $this->products->admin_product_list_request_query( $query_vars );
+ }
+
+ /**
+ * DEPRECATED. Apply the necessary changes to WP_Query in WooCommerce Admin Product List.
+ *
+ * @param WP_Query $query The WP Query being executed.
+ */
+ public function translate_args_admin_products_list( $query ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->price_filter()" );
+ $this->products->translate_args_admin_products_list( $query );
+ }
+
+ /**
+ * DEPRECATED. 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 ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->maybe_display_notice_about_product_ordering()" );
+ return $this->products->maybe_display_notice_about_product_ordering( $notices );
+ }
+
+ /**
+ * DEPRECATED. 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 ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->action_sync_on_woocommerce_sort_single()" );
+ return $this->products->action_sync_on_woocommerce_sort_single( $sorting_id, $menu_orders );
+ }
+
+ /**
+ * DEPRECATED. Add weight by date settings related to WooCommerce
*
* @since 4.6.0
* @param array $settings Current settings.
*/
public function add_weight_settings_search( $settings ) {
- ?>
-
-
- get_registered_feature( 'woocommerce' )->products->add_weight_settings_search()" );
+ $this->products->add_weight_settings_search( $settings );
}
/**
- * Conditionally disable decaying by date based on WooCommerce Decay settings.
+ * DEPRECATED. Conditionally disable decaying by date based on WooCommerce Decay settings.
*
* @since 4.6.0
* @param bool $is_decaying_enabled Whether decay by date is enabled or not
@@ -1401,25 +984,7 @@ public function add_weight_settings_search( $settings ) {
* @return bool
*/
public function maybe_disable_decaying( $is_decaying_enabled, $settings, $args ) {
- if ( ! in_array( $settings['decaying_enabled'], [ 'disabled_only_products', 'disabled_includes_products' ], true ) ) {
- return $is_decaying_enabled;
- }
-
- if ( ! isset( $args['post_type'] ) || ! in_array( 'product', (array) $args['post_type'], true ) ) {
- return $is_decaying_enabled;
- }
-
- $post_types = (array) $args['post_type'];
-
- if ( 'disabled_only_products' === $settings['decaying_enabled'] && count( $post_types ) > 1 ) {
- return $is_decaying_enabled;
- }
-
- if ( 'disabled_includes_products' === $settings['decaying_enabled'] && ! in_array( 'product', $post_types, true ) ) {
- return $is_decaying_enabled;
- }
-
- return false;
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->maybe_disable_decaying()" );
+ return $this->products->maybe_disable_decaying( $is_decaying_enabled, $settings, $args );
}
-
}
From 5ab766487dd77e798b701db485636c8865a2ff03 Mon Sep 17 00:00:00 2001
From: Martin Widmann
Date: Wed, 21 Jun 2023 14:25:01 +0200
Subject: [PATCH 03/45] Update Orders.php
Fixes a deprecation notice because the `index` property is undeclared and created dynamically.
---
includes/classes/Feature/WooCommerce/Orders.php | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php
index 461a1b51d5..666f696939 100644
--- a/includes/classes/Feature/WooCommerce/Orders.php
+++ b/includes/classes/Feature/WooCommerce/Orders.php
@@ -20,6 +20,13 @@
* WooCommerce Orders Feature
*/
class Orders {
+ /**
+ * The name of the index;
+ *
+ * @var string
+ */
+ private $index;
+
/**
* Initialize feature.
*
From 4fb7a08e585166c7fc3eed993c91415d3c2603a7 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Wed, 21 Jun 2023 15:53:16 -0300
Subject: [PATCH 04/45] Move and adjust tests and ->orders references
---
.../Feature/WooCommerce/WooCommerce.php | 2 +-
.../classes/StatusReport/ElasticPressIo.php | 2 +-
.../{ => WooCommerce}/TestWooCommerce.php | 0
.../TestWooCommerceOrdersAutosuggest.php} | 40 +++++++++----------
4 files changed, 22 insertions(+), 22 deletions(-)
rename tests/php/features/{ => WooCommerce}/TestWooCommerce.php (100%)
rename tests/php/features/{TestWooCommerceOrders.php => WooCommerce/TestWooCommerceOrdersAutosuggest.php} (81%)
diff --git a/includes/classes/Feature/WooCommerce/WooCommerce.php b/includes/classes/Feature/WooCommerce/WooCommerce.php
index 6ac5a7c68e..3a1038d764 100644
--- a/includes/classes/Feature/WooCommerce/WooCommerce.php
+++ b/includes/classes/Feature/WooCommerce/WooCommerce.php
@@ -284,7 +284,7 @@ public function translate_args( $query ) {
if ( ! empty( $s ) ) {
- $searchable_post_types = $this->get_admin_searchable_post_types();
+ $searchable_post_types = $this->orders->get_admin_searchable_post_types();
if ( in_array( $post_type, $searchable_post_types, true ) ) {
$default_search_fields = array( 'post_title', 'post_content', 'post_excerpt' );
diff --git a/includes/classes/StatusReport/ElasticPressIo.php b/includes/classes/StatusReport/ElasticPressIo.php
index 4ebd236952..31f67f6096 100644
--- a/includes/classes/StatusReport/ElasticPressIo.php
+++ b/includes/classes/StatusReport/ElasticPressIo.php
@@ -248,7 +248,7 @@ protected function get_orders_search_field() : array {
}
$woocommerce_feature = \ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' );
- $template = $woocommerce_feature->orders->get_search_template();
+ $template = $woocommerce_feature->orders_autosuggest->get_search_template();
if ( is_wp_error( $template ) ) {
return [
diff --git a/tests/php/features/TestWooCommerce.php b/tests/php/features/WooCommerce/TestWooCommerce.php
similarity index 100%
rename from tests/php/features/TestWooCommerce.php
rename to tests/php/features/WooCommerce/TestWooCommerce.php
diff --git a/tests/php/features/TestWooCommerceOrders.php b/tests/php/features/WooCommerce/TestWooCommerceOrdersAutosuggest.php
similarity index 81%
rename from tests/php/features/TestWooCommerceOrders.php
rename to tests/php/features/WooCommerce/TestWooCommerceOrdersAutosuggest.php
index 1160d78124..1fc969c718 100644
--- a/tests/php/features/TestWooCommerceOrders.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceOrdersAutosuggest.php
@@ -13,7 +13,7 @@
/**
* WC Orders test class
*/
-class TestWooCommerceOrders extends BaseTestCase {
+class TestWooCommerceOrdersAutosuggest extends BaseTestCase {
/**
* Instance of the feature
*
@@ -24,14 +24,14 @@ class TestWooCommerceOrders extends BaseTestCase {
/**
* Orders instance
*
- * @var \ElasticPress\Feature\WooCommerce\Orders
+ * @var \ElasticPress\Feature\WooCommerce\OrdersAutosuggest
*/
- public $orders;
+ public $orders_autosuggest;
/**
* Setup each test.
*
- * @group WooCommerceOrders
+ * @group WooCommerceOrdersAutosuggest
*/
public function set_up() {
parent::set_up();
@@ -45,13 +45,13 @@ public function set_up() {
ElasticPress\Features::factory()->setup_features();
- $this->orders = $this->woocommerce_feature->orders;
+ $this->orders_autosuggest = $this->woocommerce_feature->orders_autosuggest;
}
/**
* Test the `filter_term_suggest` method
*
- * @group WooCommerceOrders
+ * @group WooCommerceOrdersAutosuggest
*/
public function testFilterTermSuggest() {
$order = [
@@ -70,7 +70,7 @@ public function testFilterTermSuggest() {
],
];
- $order_with_suggest = $this->orders->filter_term_suggest( $order );
+ $order_with_suggest = $this->orders_autosuggest->filter_term_suggest( $order );
$this->assertArrayHasKey( 'term_suggest', $order_with_suggest );
$this->assertContains( '_billing_email_example', $order_with_suggest['term_suggest'] );
@@ -86,16 +86,16 @@ public function testFilterTermSuggest() {
);
unset( $order['post_type'] );
- $order_with_suggest = $this->orders->filter_term_suggest( $order );
+ $order_with_suggest = $this->orders_autosuggest->filter_term_suggest( $order );
$this->assertArrayNotHasKey( 'term_suggest', $order_with_suggest );
$order['post_type'] = 'not_shop_order';
- $order_with_suggest = $this->orders->filter_term_suggest( $order );
+ $order_with_suggest = $this->orders_autosuggest->filter_term_suggest( $order );
$this->assertArrayNotHasKey( 'term_suggest', $order_with_suggest );
$order['post_type'] = 'shop_order';
unset( $order['meta'] );
- $order_with_suggest = $this->orders->filter_term_suggest( $order );
+ $order_with_suggest = $this->orders_autosuggest->filter_term_suggest( $order );
$this->assertArrayNotHasKey( 'term_suggest', $order_with_suggest );
}
@@ -104,7 +104,7 @@ public function testFilterTermSuggest() {
*
* This method steps into WooCommerce functionality a bit.
*
- * @group WooCommerceOrders
+ * @group WooCommerceOrdersAutosuggest
*/
public function testFilterTermSuggestWithCustomOrderId() {
$shop_order_1 = new \WC_Order();
@@ -115,7 +115,7 @@ public function testFilterTermSuggestWithCustomOrderId() {
$shop_order_id_1 = (string) $shop_order_1->get_id();
$prepared_shop_order = ElasticPress\Indexables::factory()->get( 'post' )->prepare_document( $shop_order_id_1 );
- $order_with_suggest = $this->orders->filter_term_suggest( $prepared_shop_order );
+ $order_with_suggest = $this->orders_autosuggest->filter_term_suggest( $prepared_shop_order );
$this->assertSame(
[
@@ -133,7 +133,7 @@ public function testFilterTermSuggestWithCustomOrderId() {
};
add_filter( 'woocommerce_order_number', $set_custom_order_id );
- $order_with_suggest = $this->orders->filter_term_suggest( $prepared_shop_order );
+ $order_with_suggest = $this->orders_autosuggest->filter_term_suggest( $prepared_shop_order );
$this->assertSame(
[
@@ -147,7 +147,7 @@ public function testFilterTermSuggestWithCustomOrderId() {
/**
* Test the `mapping` method with the ES 7 mapping
*
- * @group WooCommerceOrders
+ * @group WooCommerceOrdersAutosuggest
*/
public function testMappingEs7() {
$original_mapping = [
@@ -157,7 +157,7 @@ public function testMappingEs7() {
],
],
];
- $changed_mapping = $this->orders->mapping( $original_mapping );
+ $changed_mapping = $this->orders_autosuggest->mapping( $original_mapping );
$expected_mapping = [
'mappings' => [
@@ -192,7 +192,7 @@ public function testMappingEs7() {
/**
* Test the `mapping` method with the ES 5 mapping
*
- * @group WooCommerceOrders
+ * @group WooCommerceOrdersAutosuggest
*/
public function testMappingEs5() {
$change_es_version = function() {
@@ -210,7 +210,7 @@ public function testMappingEs5() {
],
];
- $changed_mapping = $this->orders->mapping( $original_mapping );
+ $changed_mapping = $this->orders_autosuggest->mapping( $original_mapping );
$expected_mapping = [
'mappings' => [
@@ -247,7 +247,7 @@ public function testMappingEs5() {
/**
* Test the `set_search_fields` method
*
- * @group WooCommerceOrders
+ * @group WooCommerceOrdersAutosuggest
*/
public function testSetSearchFields() {
$original_search_fields = [ 'old_search_field' ];
@@ -261,7 +261,7 @@ public function testSetSearchFields() {
]
);
- $changed_search_fields = $this->orders->set_search_fields( $original_search_fields, $wp_query );
+ $changed_search_fields = $this->orders_autosuggest->set_search_fields( $original_search_fields, $wp_query );
$this->assertSame( $original_search_fields, $changed_search_fields );
@@ -274,7 +274,7 @@ public function testSetSearchFields() {
]
);
- $changed_search_fields = $this->orders->set_search_fields( $original_search_fields, $wp_query );
+ $changed_search_fields = $this->orders_autosuggest->set_search_fields( $original_search_fields, $wp_query );
$expected_fields = [
'meta.order_number.value',
From 57e6958a825f2b6e14b563ccd6b48fad51b6f4aa Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Wed, 21 Jun 2023 16:28:05 -0300
Subject: [PATCH 05/45] More tests fix
---
.../features/WooCommerce/TestWooCommerce.php | 3 +
.../WooCommerce/TestWooCommerceOrders.php | 89 +++++++++++++++++++
.../WooCommerce/TestWooCommerceProduct.php | 53 +++++++++++
3 files changed, 145 insertions(+)
create mode 100644 tests/php/features/WooCommerce/TestWooCommerceOrders.php
create mode 100644 tests/php/features/WooCommerce/TestWooCommerceProduct.php
diff --git a/tests/php/features/WooCommerce/TestWooCommerce.php b/tests/php/features/WooCommerce/TestWooCommerce.php
index 8e5be36887..0304961a71 100644
--- a/tests/php/features/WooCommerce/TestWooCommerce.php
+++ b/tests/php/features/WooCommerce/TestWooCommerce.php
@@ -332,6 +332,7 @@ public function testSearchOnAllFrontEnd() {
*
* @since 4.2.0
* @group woocommerce
+ * @expectedDeprecated ElasticPress\Feature\WooCommerce\WooCommerce::add_variations_skus_meta
*/
public function testAddVariationsSkusMeta() {
ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
@@ -369,6 +370,7 @@ public function testAddVariationsSkusMeta() {
*
* @since 4.2.0
* @group woocommerce
+ * @expectedDeprecated ElasticPress\Feature\WooCommerce\WooCommerce::translate_args_admin_products_list
*/
public function testTranslateArgsAdminProductsList() {
ElasticPress\Features::factory()->activate_feature( 'protected_content' );
@@ -411,6 +413,7 @@ public function testTranslateArgsAdminProductsList() {
*
* @since 4.2.0
* @group woocommerce
+ * @expectedDeprecated ElasticPress\Feature\WooCommerce\WooCommerce::translate_args_admin_products_list
*/
public function testEPWoocommerceAdminProductsListSearchFields() {
ElasticPress\Features::factory()->activate_feature( 'protected_content' );
diff --git a/tests/php/features/WooCommerce/TestWooCommerceOrders.php b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
new file mode 100644
index 0000000000..73ad63f7b6
--- /dev/null
+++ b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
@@ -0,0 +1,89 @@
+activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ parse_str( 'post_type=product&s=product&product_type=downloadable&stock_status=instock', $_GET );
+
+ $query_args = [
+ 'ep_integrate' => true,
+ ];
+
+ $woocommerce_feature = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' );
+ add_action( 'pre_get_posts', [ $woocommerce_feature->orders, 'translate_args_admin_products_list' ] );
+
+ $query = new \WP_Query( $query_args );
+
+ $this->assertTrue( $query->elasticsearch_success );
+ $this->assertEquals( $query->query_vars['s'], 'product' );
+ $this->assertEquals( $query->query_vars['meta_query'][0]['key'], '_downloadable' );
+ $this->assertEquals( $query->query_vars['meta_query'][0]['value'], 'yes' );
+ $this->assertEquals( $query->query_vars['meta_query'][1]['key'], '_stock_status' );
+ $this->assertEquals( $query->query_vars['meta_query'][1]['value'], 'instock' );
+ $this->assertEquals(
+ $query->query_vars['search_fields'],
+ [
+ 'post_title',
+ 'post_content',
+ 'post_excerpt',
+ 'meta' => [
+ '_sku',
+ '_variations_skus',
+ ],
+ ]
+ );
+ }
+
+ /**
+ * Test the ep_woocommerce_admin_products_list_search_fields filter
+ *
+ * @group woocommerce
+ */
+ public function testEPWoocommerceAdminProductsListSearchFields() {
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ parse_str( 'post_type=product&s=product&product_type=downloadable', $_GET );
+
+ $query_args = [
+ 'ep_integrate' => true,
+ ];
+
+ $woocommerce_feature = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' );
+ add_action( 'pre_get_posts', [ $woocommerce_feature->orders, 'translate_args_admin_products_list' ] );
+
+ $search_fields_function = function () {
+ return [ 'post_title', 'post_content' ];
+ };
+ add_filter( 'ep_woocommerce_admin_products_list_search_fields', $search_fields_function );
+
+ $query = new \WP_Query( $query_args );
+ $this->assertEquals(
+ $query->query_vars['search_fields'],
+ [ 'post_title', 'post_content' ]
+ );
+ }
+}
diff --git a/tests/php/features/WooCommerce/TestWooCommerceProduct.php b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
new file mode 100644
index 0000000000..54df9ed8fc
--- /dev/null
+++ b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
@@ -0,0 +1,53 @@
+activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->assertTrue( class_exists( '\WC_Product_Variable' ) );
+ $this->assertTrue( class_exists( '\WC_Product_Variation' ) );
+
+ $main_product = new \WC_Product_Variable();
+ $main_product->set_sku( 'main-product_sku' );
+ $main_product_id = $main_product->save();
+
+ $variation_1 = new \WC_Product_Variation();
+ $variation_1->set_parent_id( $main_product_id );
+ $variation_1->set_sku( 'child-sku-1' );
+ $variation_1->save();
+
+ $variation_2 = new \WC_Product_Variation();
+ $variation_2->set_parent_id( $main_product_id );
+ $variation_2->set_sku( 'child-sku-2' );
+ $variation_2->save();
+
+ $main_product_as_post = get_post( $main_product_id );
+ $product_meta_to_index = ElasticPress\Features::factory()
+ ->get_registered_feature( 'woocommerce' )
+ ->products
+ ->add_variations_skus_meta( [], $main_product_as_post );
+
+ $this->assertArrayHasKey( '_variations_skus', $product_meta_to_index );
+ $this->assertContains( 'child-sku-1', $product_meta_to_index['_variations_skus'] );
+ $this->assertContains( 'child-sku-2', $product_meta_to_index['_variations_skus'] );
+ }
+}
From 56f9602b0a6b31403c6438db201108962dfa29c4 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Wed, 21 Jun 2023 16:34:14 -0300
Subject: [PATCH 06/45] More fixes
---
.../WooCommerce/TestWooCommerceOrders.php | 71 ------------------
.../WooCommerce/TestWooCommerceProduct.php | 72 +++++++++++++++++++
2 files changed, 72 insertions(+), 71 deletions(-)
diff --git a/tests/php/features/WooCommerce/TestWooCommerceOrders.php b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
index 73ad63f7b6..b29f746634 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceOrders.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
@@ -15,75 +15,4 @@
*/
class TestWooCommerceOrders extends BaseTestCase {
- /**
- * Test the translate_args_admin_products_list method
- *
- * @group woocommerce
- */
- public function testTranslateArgsAdminProductsList() {
- ElasticPress\Features::factory()->activate_feature( 'protected_content' );
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- parse_str( 'post_type=product&s=product&product_type=downloadable&stock_status=instock', $_GET );
-
- $query_args = [
- 'ep_integrate' => true,
- ];
-
- $woocommerce_feature = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' );
- add_action( 'pre_get_posts', [ $woocommerce_feature->orders, 'translate_args_admin_products_list' ] );
-
- $query = new \WP_Query( $query_args );
-
- $this->assertTrue( $query->elasticsearch_success );
- $this->assertEquals( $query->query_vars['s'], 'product' );
- $this->assertEquals( $query->query_vars['meta_query'][0]['key'], '_downloadable' );
- $this->assertEquals( $query->query_vars['meta_query'][0]['value'], 'yes' );
- $this->assertEquals( $query->query_vars['meta_query'][1]['key'], '_stock_status' );
- $this->assertEquals( $query->query_vars['meta_query'][1]['value'], 'instock' );
- $this->assertEquals(
- $query->query_vars['search_fields'],
- [
- 'post_title',
- 'post_content',
- 'post_excerpt',
- 'meta' => [
- '_sku',
- '_variations_skus',
- ],
- ]
- );
- }
-
- /**
- * Test the ep_woocommerce_admin_products_list_search_fields filter
- *
- * @group woocommerce
- */
- public function testEPWoocommerceAdminProductsListSearchFields() {
- ElasticPress\Features::factory()->activate_feature( 'protected_content' );
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- parse_str( 'post_type=product&s=product&product_type=downloadable', $_GET );
-
- $query_args = [
- 'ep_integrate' => true,
- ];
-
- $woocommerce_feature = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' );
- add_action( 'pre_get_posts', [ $woocommerce_feature->orders, 'translate_args_admin_products_list' ] );
-
- $search_fields_function = function () {
- return [ 'post_title', 'post_content' ];
- };
- add_filter( 'ep_woocommerce_admin_products_list_search_fields', $search_fields_function );
-
- $query = new \WP_Query( $query_args );
- $this->assertEquals(
- $query->query_vars['search_fields'],
- [ 'post_title', 'post_content' ]
- );
- }
}
diff --git a/tests/php/features/WooCommerce/TestWooCommerceProduct.php b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
index 54df9ed8fc..80e375d0db 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceProduct.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
@@ -50,4 +50,76 @@ public function testAddVariationsSkusMeta() {
$this->assertContains( 'child-sku-1', $product_meta_to_index['_variations_skus'] );
$this->assertContains( 'child-sku-2', $product_meta_to_index['_variations_skus'] );
}
+
+ /**
+ * Test the translate_args_admin_products_list method
+ *
+ * @group woocommerce
+ */
+ public function testTranslateArgsAdminProductsList() {
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ parse_str( 'post_type=product&s=product&product_type=downloadable&stock_status=instock', $_GET );
+
+ $query_args = [
+ 'ep_integrate' => true,
+ ];
+
+ $woocommerce_feature = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' );
+ add_action( 'pre_get_posts', [ $woocommerce_feature->products, 'translate_args_admin_products_list' ] );
+
+ $query = new \WP_Query( $query_args );
+
+ $this->assertTrue( $query->elasticsearch_success );
+ $this->assertEquals( $query->query_vars['s'], 'product' );
+ $this->assertEquals( $query->query_vars['meta_query'][0]['key'], '_downloadable' );
+ $this->assertEquals( $query->query_vars['meta_query'][0]['value'], 'yes' );
+ $this->assertEquals( $query->query_vars['meta_query'][1]['key'], '_stock_status' );
+ $this->assertEquals( $query->query_vars['meta_query'][1]['value'], 'instock' );
+ $this->assertEquals(
+ $query->query_vars['search_fields'],
+ [
+ 'post_title',
+ 'post_content',
+ 'post_excerpt',
+ 'meta' => [
+ '_sku',
+ '_variations_skus',
+ ],
+ ]
+ );
+ }
+
+ /**
+ * Test the ep_woocommerce_admin_products_list_search_fields filter
+ *
+ * @group woocommerce
+ */
+ public function testEPWoocommerceAdminProductsListSearchFields() {
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ parse_str( 'post_type=product&s=product&product_type=downloadable', $_GET );
+
+ $query_args = [
+ 'ep_integrate' => true,
+ ];
+
+ $woocommerce_feature = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' );
+ add_action( 'pre_get_posts', [ $woocommerce_feature->products, 'translate_args_admin_products_list' ] );
+
+ $search_fields_function = function () {
+ return [ 'post_title', 'post_content' ];
+ };
+ add_filter( 'ep_woocommerce_admin_products_list_search_fields', $search_fields_function );
+
+ $query = new \WP_Query( $query_args );
+ $this->assertEquals(
+ $query->query_vars['search_fields'],
+ [ 'post_title', 'post_content' ]
+ );
+ }
}
From 37bb1678643c4f9ad99d8d463dad90af56b4854e Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Wed, 21 Jun 2023 17:08:19 -0300
Subject: [PATCH 07/45] More tests
---
.../classes/Feature/WooCommerce/Orders.php | 5 ++-
.../features/WooCommerce/TestWooCommerce.php | 41 -------------------
.../WooCommerce/TestWooCommerceOrders.php | 40 +++++++++++++++++-
3 files changed, 42 insertions(+), 44 deletions(-)
diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php
index d4cd0bf941..b2f2a1c6ff 100644
--- a/includes/classes/Feature/WooCommerce/Orders.php
+++ b/includes/classes/Feature/WooCommerce/Orders.php
@@ -41,6 +41,7 @@ public function setup() {
add_filter( 'ep_sync_insert_permissions_bypass', [ $this, 'bypass_order_permissions_check' ], 10, 2 );
add_filter( 'ep_prepare_meta_allowed_protected_keys', [ $this, 'allow_meta_keys' ] );
add_filter( 'ep_post_sync_args_post_prepare_meta', [ $this, 'add_order_items_search' ], 20, 2 );
+ add_filter( 'ep_pc_skip_post_content_cleanup', [ $this, 'keep_order_fields' ], 20, 2 );
add_action( 'parse_query', [ $this, 'maybe_hook_woocommerce_search_fields' ], 1 );
add_action( 'parse_query', [ $this, 'search_order' ], 11 );
}
@@ -224,12 +225,12 @@ public function maybe_hook_woocommerce_search_fields( $query ) {
* @param WP_Query $wp WP Query
*/
public function search_order( $wp ) {
+ global $pagenow;
+
if ( ! $this->woocommerce->should_integrate_with_query( $wp ) ) {
return;
}
- global $pagenow;
-
$searchable_post_types = $this->get_admin_searchable_post_types();
if ( 'edit.php' !== $pagenow || empty( $wp->query_vars['post_type'] ) || ! in_array( $wp->query_vars['post_type'], $searchable_post_types, true ) ||
diff --git a/tests/php/features/WooCommerce/TestWooCommerce.php b/tests/php/features/WooCommerce/TestWooCommerce.php
index 0304961a71..527248ea30 100644
--- a/tests/php/features/WooCommerce/TestWooCommerce.php
+++ b/tests/php/features/WooCommerce/TestWooCommerce.php
@@ -264,47 +264,6 @@ public function testSearchShopOrderById() {
$this->assertEquals( 1, $query->found_posts );
}
- /**
- * Test search for shop orders matching field and ID.
- *
- * If searching for a number that is an order ID and part of another order's metadata,
- * both should be returned.
- *
- * @since 4.0.0
- * @group woocommerce
- */
- public function testSearchShopOrderByMetaFieldAndId() {
- ElasticPress\Features::factory()->activate_feature( 'protected_content' );
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $this->assertTrue( class_exists( '\WC_Order' ) );
-
- $shop_order_1 = new \WC_Order();
- $shop_order_1->save();
- $shop_order_id_1 = $shop_order_1->get_id();
- ElasticPress\Indexables::factory()->get( 'post' )->index( $shop_order_id_1, true );
-
- $shop_order_2 = new \WC_Order();
- $shop_order_2->set_billing_phone( 'Phone number that matches an order ID: ' . $shop_order_id_1 );
- $shop_order_2->save();
- ElasticPress\Indexables::factory()->get( 'post' )->index( $shop_order_2->get_id(), true );
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
- $args = array(
- 's' => (string) $shop_order_id_1,
- 'post_type' => 'shop_order',
- 'post_status' => 'any',
- );
-
- $query = new \WP_Query( $args );
-
- $this->assertTrue( $query->elasticsearch_success );
- $this->assertEquals( 2, $query->post_count );
- $this->assertEquals( 2, $query->found_posts );
- }
-
/**
* Test search integration is on in general for product searches
*
diff --git a/tests/php/features/WooCommerce/TestWooCommerceOrders.php b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
index b29f746634..b581a2a3cd 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceOrders.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
@@ -13,6 +13,44 @@
/**
* WC orders test class
*/
-class TestWooCommerceOrders extends BaseTestCase {
+class TestWooCommerceOrders extends TestWooCommerce {
+ /**
+ * Test search for shop orders matching field and ID.
+ *
+ * If searching for a number that is an order ID and part of another order's metadata,
+ * both should be returned.
+ *
+ * @group woocommerce
+ */
+ public function testSearchShopOrderByMetaFieldAndId() {
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+ $this->assertTrue( class_exists( '\WC_Order' ) );
+
+ $shop_order_1 = new \WC_Order();
+ $shop_order_1->save();
+ $shop_order_id_1 = $shop_order_1->get_id();
+ ElasticPress\Indexables::factory()->get( 'post' )->index( $shop_order_id_1, true );
+
+ $shop_order_2 = new \WC_Order();
+ $shop_order_2->set_billing_phone( 'Phone number that matches an order ID: ' . $shop_order_id_1 );
+ $shop_order_2->save();
+ ElasticPress\Indexables::factory()->get( 'post' )->index( $shop_order_2->get_id(), true );
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ $args = array(
+ 's' => (string) $shop_order_id_1,
+ 'post_type' => 'shop_order',
+ 'post_status' => 'any',
+ );
+
+ $query = new \WP_Query( $args );
+
+ $this->assertTrue( $query->elasticsearch_success );
+ $this->assertEquals( 2, $query->post_count );
+ $this->assertEquals( 2, $query->found_posts );
+ }
}
From 00a9132a8e6f8c9cd2952ec9c487c4ce43d7caac Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Thu, 22 Jun 2023 13:03:36 -0300
Subject: [PATCH 08/45] Split translate_args
---
.../classes/Feature/WooCommerce/Orders.php | 151 +++++++
.../classes/Feature/WooCommerce/Products.php | 390 ++++++++++++++++
.../Feature/WooCommerce/WooCommerce.php | 425 ++----------------
3 files changed, 584 insertions(+), 382 deletions(-)
diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php
index b2f2a1c6ff..9d127f965b 100644
--- a/includes/classes/Feature/WooCommerce/Orders.php
+++ b/includes/classes/Feature/WooCommerce/Orders.php
@@ -44,6 +44,7 @@ public function setup() {
add_filter( 'ep_pc_skip_post_content_cleanup', [ $this, 'keep_order_fields' ], 20, 2 );
add_action( 'parse_query', [ $this, 'maybe_hook_woocommerce_search_fields' ], 1 );
add_action( 'parse_query', [ $this, 'search_order' ], 11 );
+ add_action( 'pre_get_posts', [ $this, 'translate_args' ], 11, 1 );
}
/**
@@ -246,4 +247,154 @@ public function search_order( $wp ) {
}
// phpcs:enable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
}
+
+ /**
+ * Determines whether or not ES should be integrating with the provided query
+ *
+ * @param \WP_Query $query Query we might integrate with
+ * @return bool
+ */
+ public function should_integrate_with_query( \WP_Query $query ) : bool {
+ /**
+ * Check the post type
+ */
+ $supported_post_types = $this->get_supported_post_types( $query );
+ $post_type = $query->get( 'post_type', false );
+ if ( ! empty( $post_type ) &&
+ ( in_array( $post_type, $supported_post_types, true ) ||
+ ( is_array( $post_type ) && ! array_diff( $post_type, $supported_post_types ) ) )
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the supported post types for Order related queries
+ *
+ * @return array
+ */
+ public function get_supported_post_types() : array {
+ $post_types = [ 'shop_order', 'shop_order_refund' ];
+
+ /**
+ * Expands or contracts the post_types eligible for indexing.
+ *
+ * @hook ep_woocommerce_default_supported_post_types
+ * @since 4.4.0
+ * @param {array} $post_types Post types
+ * @return {array} New post types
+ */
+ $supported_post_types = apply_filters( 'ep_woocommerce_default_supported_post_types', $post_types );
+
+ $supported_post_types = array_intersect(
+ $supported_post_types,
+ Indexables::factory()->get( 'post' )->get_indexable_post_types()
+ );
+
+ return $supported_post_types;
+ }
+
+ /**
+ * If the query has a search term, add the order fields that need to be searched.
+ *
+ * @param \WP_Query $query The WP_Query
+ * @return \WP_Query
+ */
+ public function maybe_set_search_fields( \WP_Query $query ) {
+ $search_term = $this->woocommerce->get_search_term( $query );
+ if ( empty( $search_term ) ) {
+ return $query;
+ }
+
+ $searchable_post_types = $this->get_admin_searchable_post_types();
+
+ $post_type = $query->get( 'post_type', false );
+ if ( ! in_array( $post_type, $searchable_post_types, true ) ) {
+ return $query;
+ }
+
+ $default_search_fields = array( 'post_title', 'post_content', 'post_excerpt' );
+ if ( ctype_digit( $search_term ) ) {
+ $default_search_fields[] = 'ID';
+ }
+ $search_fields = $query->get( 'search_fields', $default_search_fields );
+
+ $search_fields['meta'] = array_map(
+ 'wc_clean',
+ /**
+ * Filter shop order meta fields to search for WooCommerce
+ *
+ * @hook shop_order_search_fields
+ * @param {array} $fields Shop order fields
+ * @return {array} New fields
+ */
+ apply_filters(
+ 'shop_order_search_fields',
+ array(
+ '_order_key',
+ '_billing_company',
+ '_billing_address_1',
+ '_billing_address_2',
+ '_billing_city',
+ '_billing_postcode',
+ '_billing_country',
+ '_billing_state',
+ '_billing_email',
+ '_billing_phone',
+ '_shipping_address_1',
+ '_shipping_address_2',
+ '_shipping_city',
+ '_shipping_postcode',
+ '_shipping_country',
+ '_shipping_state',
+ '_billing_last_name',
+ '_billing_first_name',
+ '_shipping_first_name',
+ '_shipping_last_name',
+ '_items',
+ )
+ )
+ );
+
+ $query->set(
+ 'search_fields',
+ /**
+ * Filter all the shop order fields to search for WooCommerce
+ *
+ * @hook ep_woocommerce_shop_order_search_fields
+ * @since 4.0.0
+ * @param {array} $fields Shop order fields
+ * @param {WP_Query} $query WP Query
+ * @return {array} New fields
+ */
+ apply_filters( 'ep_woocommerce_shop_order_search_fields', $search_fields, $query )
+ );
+ }
+
+ /**
+ * Translate args to ElasticPress compat format. This is the meat of what the feature does
+ *
+ * @param \WP_Query $query WP Query
+ */
+ public function translate_args( $query ) {
+ if ( ! $this->woocommerce->should_integrate_with_query( $query ) ) {
+ return;
+ }
+
+ if ( ! $this->should_integrate_with_query( $query ) ) {
+ return;
+ }
+
+ $query->set( 'ep_integrate', true );
+
+ /**
+ * Make sure filters are suppressed
+ */
+ $query->query['suppress_filters'] = false;
+ $query->set( 'suppress_filters', false );
+
+ $this->maybe_set_search_fields( $query );
+ }
}
diff --git a/includes/classes/Feature/WooCommerce/Products.php b/includes/classes/Feature/WooCommerce/Products.php
index b94179dc83..d8a480de68 100644
--- a/includes/classes/Feature/WooCommerce/Products.php
+++ b/includes/classes/Feature/WooCommerce/Products.php
@@ -50,6 +50,8 @@ public function setup() {
add_filter( 'ep_prepare_meta_data', [ $this, 'add_variations_skus_meta' ], 10, 2 );
add_filter( 'request', [ $this, 'admin_product_list_request_query' ], 9 );
+ add_action( 'pre_get_posts', [ $this, 'translate_args' ], 11, 1 );
+
// 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 );
@@ -582,4 +584,392 @@ public function maybe_disable_decaying( $is_decaying_enabled, $settings, $args )
return false;
}
+
+ /**
+ * Translate args to ElasticPress compat format. This is the meat of what the feature does
+ *
+ * @param \WP_Query $query WP Query
+ */
+ public function translate_args( $query ) {
+ if ( ! $this->woocommerce->should_integrate_with_query( $query ) ) {
+ return;
+ }
+
+ if ( ! $this->should_integrate_with_query( $query ) ) {
+ return;
+ }
+
+ /**
+ * Make sure filters are suppressed
+ */
+ $query->query['suppress_filters'] = false;
+ $query->set( 'suppress_filters', false );
+
+ $query->set( 'ep_integrate', true );
+
+ $this->maybe_update_tax_query( $query );
+ $this->maybe_update_post_type( $query );
+ $this->maybe_update_meta_query( $query );
+
+ $this->maybe_handle_top_rated( $query );
+
+ $this->maybe_set_search_fields( $query );
+ $this->maybe_set_orderby( $query );
+ }
+
+ /**
+ * Determines whether or not ES should be integrating with the provided query
+ *
+ * @param \WP_Query $query Query we might integrate with
+ * @return bool
+ */
+ public function should_integrate_with_query( \WP_Query $query ) : bool {
+ /**
+ * Check for taxonomies
+ */
+ $supported_taxonomies = $this->get_supported_taxonomies();
+ $tax_query = $query->get( 'tax_query', [] );
+ $taxonomies_queried = array_merge(
+ array_column( $tax_query, 'taxonomy' ),
+ array_keys( $query->query_vars )
+ );
+ if ( ! empty( array_intersect( $supported_taxonomies, $taxonomies_queried ) ) ) {
+ return true;
+ }
+
+ /**
+ * Check the post type
+ */
+ $supported_post_types = $this->get_supported_post_types( $query );
+ $post_type = $query->get( 'post_type', false );
+ if ( ! empty( $post_type ) && ( in_array( $post_type, $supported_post_types, true ) || ( is_array( $post_type ) && ! array_diff( $post_type, $supported_post_types ) ) ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the WooCommerce supported taxonomies (related to products.)
+ *
+ * @return array
+ */
+ public function get_supported_taxonomies() : array {
+ $supported_taxonomies = array(
+ 'product_cat',
+ 'product_tag',
+ 'product_type',
+ 'product_visibility',
+ 'product_shipping_class',
+ );
+
+ // Add in any attribute taxonomies that exist
+ $attribute_taxonomies = wc_get_attribute_taxonomy_names();
+
+ $supported_taxonomies = array_merge( $supported_taxonomies, $attribute_taxonomies );
+
+ /**
+ * DEPRECATED. Filter supported custom taxonomies for WooCommerce integration.
+ *
+ * @param {array} $supported_taxonomies An array of default taxonomies.
+ * @hook ep_woocommerce_supported_taxonomies
+ * @since 2.3.0
+ * @return {array} New taxonomies
+ */
+ $supported_taxonomies = apply_filters_deprecated(
+ 'ep_woocommerce_supported_taxonomies',
+ [ $supported_taxonomies ],
+ '4.7.0',
+ 'ep_woocommerce_products_supported_taxonomies'
+ );
+
+ /**
+ * Filter supported custom taxonomies for WooCommerce product queries integration
+ *
+ * @param {array} $supported_taxonomies An array of default taxonomies.
+ * @hook ep_woocommerce_products_supported_taxonomies
+ * @since 4.7.0
+ * @return {array} New taxonomies
+ */
+ return apply_filters( 'ep_woocommerce_products_supported_taxonomies', $supported_taxonomies );
+ }
+
+ /**
+ * Get the WooCommerce supported post types (related to products.)
+ *
+ * @param \WP_Query $query The WP_Query object
+ * @return array
+ */
+ public function get_supported_post_types( \WP_Query $query ) : array {
+ $post_types = [ 'product_variation' ];
+
+ $is_main_post_type_archive = $query->is_main_query() && $query->is_post_type_archive( 'product' );
+ $has_ep_integrate_set_true = isset( $query->query_vars['ep_integrate'] ) && filter_var( $query->query_vars['ep_integrate'], FILTER_VALIDATE_BOOLEAN );
+ if ( $is_main_post_type_archive || $has_ep_integrate_set_true ) {
+ $post_types[] = 'product';
+ }
+
+ /**
+ * Expands or contracts the post_types eligible for indexing.
+ *
+ * @hook ep_woocommerce_default_supported_post_types
+ * @since 4.4.0
+ * @param {array} $post_types Post types
+ * @return {array} New post types
+ */
+ $supported_post_types = apply_filters( 'ep_woocommerce_default_supported_post_types', $post_types );
+
+ $supported_post_types = array_intersect(
+ $supported_post_types,
+ Indexables::factory()->get( 'post' )->get_indexable_post_types()
+ );
+
+ return $supported_post_types;
+ }
+
+ /**
+ * If needed, update the `'tax_query'` parameter
+ *
+ * If a supported taxonomy was added in the root of the args array,
+ * this method moves it to the `'tax_query'`
+ *
+ * @param \WP_Query $query The WP_Query object
+ */
+ protected function maybe_update_tax_query( \WP_Query $query ) {
+ $supported_taxonomies = $this->get_supported_taxonomies();
+ $tax_query = $query->get( 'tax_query', [] );
+
+ foreach ( $supported_taxonomies as $taxonomy ) {
+ $term = $query->get( $taxonomy, false );
+
+ if ( ! empty( $term ) ) {
+ $tax_query[] = array(
+ 'taxonomy' => $taxonomy,
+ 'field' => 'slug',
+ 'terms' => (array) $term,
+ );
+ }
+ }
+
+ $query->set( 'tax_query', $tax_query );
+ }
+
+ /**
+ * Set the post_type to product if empty
+ *
+ * @param \WP_Query $query The WP_Query object
+ */
+ protected function maybe_update_post_type( \WP_Query $query ) {
+ $post_type = $query->get( 'post_type', false );
+
+ if ( empty( $post_type ) ) {
+ $query->set( 'post_type', 'product' );
+ }
+ }
+
+ /**
+ * If the `'meta_key'` or `'meta_value'` parameters were set,
+ * move them to `'meta_query'`
+ *
+ * @param \WP_Query $query The WP_Query object
+ */
+ protected function maybe_update_meta_query( \WP_Query $query ) {
+ /**
+ * Handle meta queries
+ */
+ $meta_query = $query->get( 'meta_query', [] );
+ $meta_key = $query->get( 'meta_key', false );
+ $meta_value = $query->get( 'meta_value', false );
+
+ if ( ! empty( $meta_key ) && ! empty( $meta_value ) ) {
+ $meta_query[] = array(
+ 'key' => $meta_key,
+ 'value' => $meta_value,
+ );
+
+ $query->set( 'meta_query', $meta_query );
+ }
+ }
+
+ /**
+ * Handle the WC Top Rated Widget
+ *
+ * @param \WP_Query $query The WP_Query object
+ * @return void
+ */
+ protected function maybe_handle_top_rated( \WP_Query $query ) {
+ if ( ! has_filter( 'posts_clauses', array( WC()->query, 'order_by_rating_post_clauses' ) ) ) {
+ return;
+ }
+
+ remove_filter( 'posts_clauses', array( WC()->query, 'order_by_rating_post_clauses' ) );
+ $query->set( 'orderby', 'meta_value_num' );
+ $query->set( 'meta_key', '_wc_average_rating' );
+ }
+
+ /**
+ * If the query has a search term and the weighting dashboard is not
+ * available, add the needed fields
+ *
+ * @param \WP_Query $query The WP_Query
+ * @return \WP_Query
+ */
+ protected function maybe_set_search_fields( \WP_Query $query ) {
+ $search_term = $this->woocommerce->get_search_term( $query );
+ if ( empty( $search_term ) ) {
+ return $query;
+ }
+
+ $post_type = $query->get( 'post_type', false );
+ if ( 'product' !== $post_type || ! defined( 'EP_IS_NETWORK' ) || ! EP_IS_NETWORK ) {
+ return;
+ }
+
+ $search_fields = $query->get( 'search_fields', array( 'post_title', 'post_content', 'post_excerpt' ) );
+
+ // Remove author_name from this search.
+ $search_fields = $this->remove_author( $search_fields );
+
+ $search_fields['meta'] = ( ! empty( $search_fields['meta'] ) ) ? $search_fields['meta'] : [];
+ $search_fields['taxonomies'] = ( ! empty( $search_fields['taxonomies'] ) ) ? $search_fields['taxonomies'] : [];
+
+ $search_fields['meta'] = array_merge( $search_fields['meta'], array( '_sku' ) );
+ $search_fields['taxonomies'] = array_merge( $search_fields['taxonomies'], array( 'category', 'post_tag', 'product_tag', 'product_cat' ) );
+
+ $query->set( 'search_fields', $search_fields );
+ }
+
+ /**
+ * Remove the author_name from search fields.
+ *
+ * @param array $search_fields Array of search fields.
+ * @return array
+ */
+ public function remove_author( array $search_fields ) : array {
+ foreach ( $search_fields as $field_key => $field ) {
+ if ( 'author_name' === $field ) {
+ unset( $search_fields[ $field_key ] );
+ }
+ }
+
+ return $search_fields;
+ }
+
+ /**
+ * If needed, set the `'order'` and `'orderby'` parameters
+ *
+ * @param \WP_Query $query The WP_Query object
+ */
+ protected function maybe_set_orderby( \WP_Query $query ) {
+ $search_term = $this->woocommerce->get_search_term( $query );
+
+ if ( empty( $search_term ) ) {
+ /**
+ * For default sorting by popularity (total_sales) and rating
+ * Woocommerce doesn't set the orderby correctly.
+ * These lines will check the meta_key and correct the orderby based on that.
+ * And this won't run in search result and only run in main query
+ */
+ $meta_key = $query->get( 'meta_key', false );
+ if ( $meta_key && $query->is_main_query() ) {
+ switch ( $meta_key ) {
+ case 'total_sales':
+ $query->set( 'orderby', $this->get_orderby_meta_mapping( 'total_sales' ) );
+ $query->set( 'order', 'DESC' );
+ break;
+ case '_wc_average_rating':
+ $query->set( 'orderby', $this->get_orderby_meta_mapping( '_wc_average_rating' ) );
+ $query->set( 'order', 'DESC' );
+ break;
+ }
+ }
+ }
+
+ /**
+ * Set orderby and order for price/popularity when GET param not set
+ */
+ if ( isset( $query->query_vars['orderby'], $query->query_vars['order'] ) && $query->is_main_query() ) {
+ switch ( $query->query_vars['orderby'] ) {
+ case 'price':
+ $query->set( 'order', $query->query_vars['order'] );
+ $query->set( 'orderby', $this->get_orderby_meta_mapping( '_price' ) );
+ break;
+ case 'popularity':
+ $query->set( 'orderby', $this->get_orderby_meta_mapping( 'total_sales' ) );
+ $query->set( 'order', 'DESC' );
+ break;
+ }
+ }
+
+ /**
+ * Set orderby from GET param
+ * Also make sure the orderby param affects only the main query
+ */
+ if ( ! empty( $_GET['orderby'] ) && $query->is_main_query() ) { // phpcs:ignore WordPress.Security.NonceVerification
+ $orderby = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
+ switch ( $orderby ) { // phpcs:ignore WordPress.Security.NonceVerification
+ case 'popularity':
+ $query->set( 'orderby', $this->get_orderby_meta_mapping( 'total_sales' ) );
+ $query->set( 'order', 'DESC' );
+ break;
+ case 'price':
+ $query->set( 'order', $query->get( 'order', 'ASC' ) );
+ $query->set( 'orderby', $this->get_orderby_meta_mapping( '_price' ) );
+ break;
+ case 'price-desc':
+ $query->set( 'order', 'DESC' );
+ $query->set( 'orderby', $this->get_orderby_meta_mapping( '_price' ) );
+ break;
+ case 'rating':
+ $query->set( 'orderby', $this->get_orderby_meta_mapping( '_wc_average_rating' ) );
+ $query->set( 'order', 'DESC' );
+ break;
+ case 'date':
+ case 'title':
+ case 'ID':
+ $query->set( 'orderby', $this->get_orderby_meta_mapping( $orderby ) );
+ break;
+ case 'sku':
+ $query->set( 'orderby', $this->get_orderby_meta_mapping( '_sku' ) );
+ break;
+ default:
+ $query->set( 'orderby', $this->get_orderby_meta_mapping( 'menu_order' ) ); // Order by menu and title.
+ }
+ }
+ }
+
+ /**
+ * Fetch the ES related meta mapping for orderby
+ *
+ * @param array $meta_key The meta key to get the mapping for.
+ * @return string The mapped meta key.
+ */
+ public function get_orderby_meta_mapping( $meta_key ) : string {
+ /**
+ * Filter WooCommerce to Elasticsearch meta mapping
+ *
+ * @hook orderby_meta_mapping
+ * @param {array} $mapping Meta mapping
+ * @return {array} New mapping
+ */
+ $mapping = apply_filters(
+ 'orderby_meta_mapping',
+ array(
+ 'ID' => 'ID',
+ 'title' => 'title date',
+ 'menu_order' => 'menu_order title date',
+ 'menu_order title' => 'menu_order title date',
+ 'total_sales' => 'meta.total_sales.double date',
+ '_wc_average_rating' => 'meta._wc_average_rating.double date',
+ '_price' => 'meta._price.double date',
+ '_sku' => 'meta._sku.value.sortable date',
+ )
+ );
+
+ if ( isset( $mapping[ $meta_key ] ) ) {
+ return $mapping[ $meta_key ];
+ }
+
+ return 'date';
+ }
}
diff --git a/includes/classes/Feature/WooCommerce/WooCommerce.php b/includes/classes/Feature/WooCommerce/WooCommerce.php
index 3a1038d764..25cb8868ea 100644
--- a/includes/classes/Feature/WooCommerce/WooCommerce.php
+++ b/includes/classes/Feature/WooCommerce/WooCommerce.php
@@ -97,8 +97,6 @@ public function setup() {
add_filter( 'woocommerce_unfiltered_product_ids', [ $this, 'convert_post_object_to_id' ], 10, 4 );
add_action( 'ep_wp_query_search_cached_posts', [ $this, 'disallow_duplicated_query' ], 10, 2 );
- add_action( 'pre_get_posts', [ $this, 'translate_args' ], 11, 1 );
-
// Orders Autosuggest feature.
if ( $this->is_orders_autosuggest_enabled() ) {
$this->orders_autosuggest->setup();
@@ -106,372 +104,17 @@ public function setup() {
}
/**
- * Translate args to ElasticPress compat format. This is the meat of what the feature does
+ * Given a WP_Query object, return its search term (if any)
*
- * @param WP_Query $query WP Query
- * @since 2.1
- */
- public function translate_args( $query ) {
- if ( ! $this->should_integrate_with_query( $query ) ) {
- return;
- }
-
- // Flag to check and make sure we are in a WooCommerce specific query
- $integrate = false;
-
- /**
- * Force ElasticPress if we are querying WC taxonomy
- */
- $tax_query = $query->get( 'tax_query', [] );
-
- $supported_taxonomies = array(
- 'product_cat',
- 'product_tag',
- 'product_type',
- 'product_visibility',
- 'product_shipping_class',
- );
-
- // Add in any attribute taxonomies that exist
- $attribute_taxonomies = wc_get_attribute_taxonomy_names();
-
- $supported_taxonomies = array_merge( $supported_taxonomies, $attribute_taxonomies );
-
- /**
- * Filter supported custom taxonomies for WooCommerce integration
- *
- * @param {array} $supported_taxonomies An array of default taxonomies.
- * @hook ep_woocommerce_supported_taxonomies
- * @since 2.3.0
- * @return {array} New taxonomies
- */
- $supported_taxonomies = apply_filters( 'ep_woocommerce_supported_taxonomies', $supported_taxonomies );
-
- if ( ! empty( $tax_query ) ) {
-
- /**
- * First check if already set taxonomies are supported WC taxes
- */
- foreach ( $tax_query as $taxonomy_array ) {
- if ( isset( $taxonomy_array['taxonomy'] ) && in_array( $taxonomy_array['taxonomy'], $supported_taxonomies, true ) ) {
- $integrate = true;
- }
- }
- }
-
- /**
- * Next check if any taxonomies are in the root of query vars (shorthand form)
- */
- foreach ( $supported_taxonomies as $taxonomy ) {
- $term = $query->get( $taxonomy, false );
-
- if ( ! empty( $term ) ) {
- $integrate = true;
-
- $tax_query[] = array(
- 'taxonomy' => $taxonomy,
- 'field' => 'slug',
- 'terms' => (array) $term,
- );
- }
- }
-
- /**
- * Force ElasticPress if product post type query
- */
- $post_type = $query->get( 'post_type', false );
-
- // Act only on a defined subset of all indexable post types here
- $post_types = array(
- 'shop_order',
- 'shop_order_refund',
- 'product_variation',
- );
-
- $is_main_post_type_archive = $query->is_main_query() && $query->is_post_type_archive( 'product' );
- $has_ep_integrate_set_true = isset( $query->query_vars['ep_integrate'] ) && filter_var( $query->query_vars['ep_integrate'], FILTER_VALIDATE_BOOLEAN );
- if ( $is_main_post_type_archive || $has_ep_integrate_set_true ) {
- $post_types[] = 'product';
- }
-
- /**
- * Expands or contracts the post_types eligible for indexing.
- *
- * @hook ep_woocommerce_default_supported_post_types
- * @since 4.4.0
- * @param {array} $post_types Post types
- * @return {array} New post types
- */
- $supported_post_types = apply_filters( 'ep_woocommerce_default_supported_post_types', $post_types );
-
- $supported_post_types = array_intersect(
- $supported_post_types,
- Indexables::factory()->get( 'post' )->get_indexable_post_types()
- );
-
- // For orders it queries an array of shop_order and shop_order_refund post types, hence an array_diff
- if ( ! empty( $post_type ) && ( in_array( $post_type, $supported_post_types, true ) || ( is_array( $post_type ) && ! array_diff( $post_type, $supported_post_types ) ) ) ) {
- $integrate = true;
- }
-
- /**
- * If we have a WooCommerce specific query, lets hook it to ElasticPress and make the query ElasticSearch friendly
- */
- if ( ! $integrate ) {
- return;
- }
-
- // Set tax_query again since we may have added things
- $query->set( 'tax_query', $tax_query );
-
- // Default to product if no post type is set
- if ( empty( $post_type ) ) {
- $post_type = 'product';
- $query->set( 'post_type', 'product' );
- }
-
- // Handles the WC Top Rated Widget
- if ( has_filter( 'posts_clauses', array( WC()->query, 'order_by_rating_post_clauses' ) ) ) {
- remove_filter( 'posts_clauses', array( WC()->query, 'order_by_rating_post_clauses' ) );
- $query->set( 'orderby', 'meta_value_num' );
- $query->set( 'meta_key', '_wc_average_rating' );
- }
-
- /**
- * WordPress have to be version 4.6 or newer to have "fields" support
- * since it requires the "posts_pre_query" filter.
- *
- * @see WP_Query::get_posts
- */
- $fields = $query->get( 'fields', false );
- if ( ! version_compare( get_bloginfo( 'version' ), '4.6', '>=' ) && ( 'ids' === $fields || 'id=>parent' === $fields ) ) {
- $query->set( 'fields', 'default' );
- }
-
- /**
- * Handle meta queries
- */
- $meta_query = $query->get( 'meta_query', [] );
- $meta_key = $query->get( 'meta_key', false );
- $meta_value = $query->get( 'meta_value', false );
-
- if ( ! empty( $meta_key ) && ! empty( $meta_value ) ) {
- $meta_query[] = array(
- 'key' => $meta_key,
- 'value' => $meta_value,
- );
-
- $query->set( 'meta_query', $meta_query );
- }
-
- /**
- * Make sure filters are suppressed
- */
- $query->query['suppress_filters'] = false;
- $query->set( 'suppress_filters', false );
-
- // Integrate with WooCommerce custom searches as well
- $search = $query->get( 'search' );
- if ( ! empty( $search ) ) {
- $s = $search;
- $query->set( 's', $s );
- } else {
- $s = $query->get( 's' );
- }
-
- $query->query_vars['ep_integrate'] = true;
- $query->query['ep_integrate'] = true;
-
- if ( ! empty( $s ) ) {
-
- $searchable_post_types = $this->orders->get_admin_searchable_post_types();
-
- if ( in_array( $post_type, $searchable_post_types, true ) ) {
- $default_search_fields = array( 'post_title', 'post_content', 'post_excerpt' );
- if ( ctype_digit( $s ) ) {
- $default_search_fields[] = 'ID';
- }
- $search_fields = $query->get( 'search_fields', $default_search_fields );
-
- $search_fields['meta'] = array_map(
- 'wc_clean',
- /**
- * Filter shop order meta fields to search for WooCommerce
- *
- * @hook shop_order_search_fields
- * @param {array} $fields Shop order fields
- * @return {array} New fields
- */
- apply_filters(
- 'shop_order_search_fields',
- array(
- '_order_key',
- '_billing_company',
- '_billing_address_1',
- '_billing_address_2',
- '_billing_city',
- '_billing_postcode',
- '_billing_country',
- '_billing_state',
- '_billing_email',
- '_billing_phone',
- '_shipping_address_1',
- '_shipping_address_2',
- '_shipping_city',
- '_shipping_postcode',
- '_shipping_country',
- '_shipping_state',
- '_billing_last_name',
- '_billing_first_name',
- '_shipping_first_name',
- '_shipping_last_name',
- '_items',
- )
- )
- );
-
- $query->set(
- 'search_fields',
- /**
- * Filter all the shop order fields to search for WooCommerce
- *
- * @hook ep_woocommerce_shop_order_search_fields
- * @since 4.0.0
- * @param {array} $fields Shop order fields
- * @param {WP_Query} $query WP Query
- * @return {array} New fields
- */
- apply_filters( 'ep_woocommerce_shop_order_search_fields', $search_fields, $query )
- );
- } elseif ( 'product' === $post_type && defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
- $search_fields = $query->get( 'search_fields', array( 'post_title', 'post_content', 'post_excerpt' ) );
-
- // Remove author_name from this search.
- $search_fields = $this->remove_author( $search_fields );
-
- foreach ( $search_fields as $field_key => $field ) {
- if ( 'author_name' === $field ) {
- unset( $search_fields[ $field_key ] );
- }
- }
-
- $search_fields['meta'] = ( ! empty( $search_fields['meta'] ) ) ? $search_fields['meta'] : [];
- $search_fields['taxonomies'] = ( ! empty( $search_fields['taxonomies'] ) ) ? $search_fields['taxonomies'] : [];
-
- $search_fields['meta'] = array_merge( $search_fields['meta'], array( '_sku' ) );
- $search_fields['taxonomies'] = array_merge( $search_fields['taxonomies'], array( 'category', 'post_tag', 'product_tag', 'product_cat' ) );
-
- $query->set( 'search_fields', $search_fields );
- }
- } else {
- /**
- * For default sorting by popularity (total_sales) and rating
- * Woocommerce doesn't set the orderby correctly.
- * These lines will check the meta_key and correct the orderby based on that.
- * And this won't run in search result and only run in main query
- */
- $meta_key = $query->get( 'meta_key', false );
- if ( $meta_key && $query->is_main_query() ) {
- switch ( $meta_key ) {
- case 'total_sales':
- $query->set( 'orderby', $this->get_orderby_meta_mapping( 'total_sales' ) );
- $query->set( 'order', 'DESC' );
- break;
- case '_wc_average_rating':
- $query->set( 'orderby', $this->get_orderby_meta_mapping( '_wc_average_rating' ) );
- $query->set( 'order', 'DESC' );
- break;
- }
- }
- }
-
- /**
- * Set orderby and order for price/popularity when GET param not set
- */
- if ( isset( $query->query_vars['orderby'], $query->query_vars['order'] ) && $query->is_main_query() ) {
- switch ( $query->query_vars['orderby'] ) {
- case 'price':
- $query->set( 'order', $query->query_vars['order'] );
- $query->set( 'orderby', $this->get_orderby_meta_mapping( '_price' ) );
- break;
- case 'popularity':
- $query->set( 'orderby', $this->get_orderby_meta_mapping( 'total_sales' ) );
- $query->set( 'order', 'DESC' );
- break;
- }
- }
-
- /**
- * Set orderby from GET param
- * Also make sure the orderby param affects only the main query
- */
- if ( ! empty( $_GET['orderby'] ) && $query->is_main_query() ) { // phpcs:ignore WordPress.Security.NonceVerification
- $orderby = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
- switch ( $orderby ) { // phpcs:ignore WordPress.Security.NonceVerification
- case 'popularity':
- $query->set( 'orderby', $this->get_orderby_meta_mapping( 'total_sales' ) );
- $query->set( 'order', 'DESC' );
- break;
- case 'price':
- $query->set( 'order', $query->get( 'order', 'ASC' ) );
- $query->set( 'orderby', $this->get_orderby_meta_mapping( '_price' ) );
- break;
- case 'price-desc':
- $query->set( 'order', 'DESC' );
- $query->set( 'orderby', $this->get_orderby_meta_mapping( '_price' ) );
- break;
- case 'rating':
- $query->set( 'orderby', $this->get_orderby_meta_mapping( '_wc_average_rating' ) );
- $query->set( 'order', 'DESC' );
- break;
- case 'date':
- case 'title':
- case 'ID':
- $query->set( 'orderby', $this->get_orderby_meta_mapping( $orderby ) );
- break;
- case 'sku':
- $query->set( 'orderby', $this->get_orderby_meta_mapping( '_sku' ) );
- break;
- default:
- $query->set( 'orderby', $this->get_orderby_meta_mapping( 'menu_order' ) ); // Order by menu and title.
- }
- }
- }
-
- /**
- * Fetch the ES related meta mapping for orderby
+ * This method also accounts for the `'search'` parameter used by
+ * WooCommerce, in addition to the regular `'s'` parameter.
*
- * @param array $meta_key The meta key to get the mapping for.
- * @since 2.1
- * @return string The mapped meta key.
+ * @param \WP_Query $query The WP_Query object
+ * @return string
*/
- public function get_orderby_meta_mapping( $meta_key ) {
- /**
- * Filter WooCommerce to Elasticsearch meta mapping
- *
- * @hook orderby_meta_mapping
- * @param {array} $mapping Meta mapping
- * @return {array} New mapping
- */
- $mapping = apply_filters(
- 'orderby_meta_mapping',
- array(
- 'ID' => 'ID',
- 'title' => 'title date',
- 'menu_order' => 'menu_order title date',
- 'menu_order title' => 'menu_order title date',
- 'total_sales' => 'meta.total_sales.double date',
- '_wc_average_rating' => 'meta._wc_average_rating.double date',
- '_price' => 'meta._price.double date',
- '_sku' => 'meta._sku.value.sortable date',
- )
- );
-
- if ( isset( $mapping[ $meta_key ] ) ) {
- return $mapping[ $meta_key ];
- }
-
- return 'date';
+ public function get_search_term( \WP_Query $query ) : string {
+ $search = $query->get( 'search' );
+ return ( ! empty( $search ) ) ? $search : $query->get( 's', '' );
}
/**
@@ -543,23 +186,6 @@ public function output_feature_box_settings() {
$field ) {
- if ( 'author_name' === $field ) {
- unset( $search_fields[ $field_key ] );
- }
- }
-
- return $search_fields;
- }
-
/**
* Determine WC feature reqs status
*
@@ -668,6 +294,41 @@ public function is_orders_autosuggest_enabled() : bool {
return $this->is_orders_autosuggest_available() && '1' === $this->get_setting( 'orders' );
}
+ /**
+ * DEPRECATED. Translate args to ElasticPress compat format. This is the meat of what the feature does
+ *
+ * @param \WP_Query $query WP Query
+ * @since 2.1
+ */
+ public function translate_args( $query ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->translate_args() OR \ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders->translate_args()" );
+ $this->products->translate_args( $query );
+ $this->orders->translate_args( $query );
+ }
+
+ /**
+ * DEPRECATED. Fetch the ES related meta mapping for orderby
+ *
+ * @param array $meta_key The meta key to get the mapping for.
+ * @since 2.1
+ * @return string The mapped meta key.
+ */
+ public function get_orderby_meta_mapping( $meta_key ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->get_orderby_meta_mapping()" );
+ return $this->products->get_orderby_meta_mapping( $meta_key );
+ }
+
+ /**
+ * DEPRECATED. Remove the author_name from search fields.
+ *
+ * @param array $search_fields Array of search fields.
+ * @since 3.0
+ * @return array
+ */
+ public function remove_author( $search_fields ) {
+ _deprecated_function( __METHOD__, '4.7.0', "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products->remove_author()" );
+ return $this->products->remove_author( $search_fields );
+ }
/**
* DEPRECATED. Index Woocommerce meta
From 145bcbaeb1a3ec3e388dd1e84c1c755fcd722222 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Thu, 22 Jun 2023 13:37:13 -0300
Subject: [PATCH 09/45] Move tests to their new places
---
.../classes/Feature/WooCommerce/Orders.php | 46 +
.../Feature/WooCommerce/OrdersAutosuggest.php | 7 +
.../features/WooCommerce/TestWooCommerce.php | 1075 ++---------------
.../WooCommerce/TestWooCommerceOrders.php | 160 +++
.../WooCommerce/TestWooCommerceProduct.php | 769 +++++++++++-
5 files changed, 1081 insertions(+), 976 deletions(-)
diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php
index 9d127f965b..499546c48a 100644
--- a/includes/classes/Feature/WooCommerce/Orders.php
+++ b/includes/classes/Feature/WooCommerce/Orders.php
@@ -397,4 +397,50 @@ public function translate_args( $query ) {
$this->maybe_set_search_fields( $query );
}
+
+ /**
+ * Handle calls to OrdersAutosuggest methods
+ *
+ * @param string $method_name The method name
+ * @param array $arguments Array of arguments
+ */
+ public function __call( $method_name, $arguments ) {
+ $orders_autosuggest_methods = [
+ 'after_update_feature',
+ 'check_token_permission',
+ 'enqueue_admin_assets',
+ 'epio_delete_search_template',
+ 'epio_get_search_template',
+ 'epio_save_search_template',
+ 'filter_term_suggest',
+ 'get_args_schema',
+ 'get_search_endpoint',
+ 'get_search_template',
+ 'get_template_endpoint',
+ 'get_token',
+ 'get_token_endpoint',
+ 'intercept_search_request',
+ 'is_integrated_request',
+ 'post_statuses',
+ 'post_types',
+ 'mapping',
+ 'maybe_query_password_protected_posts',
+ 'maybe_set_posts_where',
+ 'refresh_token',
+ 'rest_api_init',
+ 'set_search_fields',
+ ];
+
+ if ( in_array( $method_name, $orders_autosuggest_methods, true ) ) {
+ _deprecated_function(
+ __METHOD__,
+ '4.7.0',
+ "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders_autosuggest->{$method_name}()" // phpcs:ignore
+ );
+
+ if ( $this->woocommerce->is_orders_autosuggest_enabled() && method_exists( $this->woocommerce->orders_autosuggest, $method_name ) ) {
+ call_user_func_array( [ $this->woocommerce->orders_autosuggest, $method_name ], $arguments );
+ }
+ }
+ }
}
diff --git a/includes/classes/Feature/WooCommerce/OrdersAutosuggest.php b/includes/classes/Feature/WooCommerce/OrdersAutosuggest.php
index c68766da47..d2543e9362 100644
--- a/includes/classes/Feature/WooCommerce/OrdersAutosuggest.php
+++ b/includes/classes/Feature/WooCommerce/OrdersAutosuggest.php
@@ -20,6 +20,13 @@
* WooCommerce OrdersAutosuggest Feature
*/
class OrdersAutosuggest {
+ /**
+ * The name of the index.
+ *
+ * @var string
+ */
+ protected $index;
+
/**
* Initialize feature.
*
diff --git a/tests/php/features/WooCommerce/TestWooCommerce.php b/tests/php/features/WooCommerce/TestWooCommerce.php
index 527248ea30..886f90ba68 100644
--- a/tests/php/features/WooCommerce/TestWooCommerce.php
+++ b/tests/php/features/WooCommerce/TestWooCommerce.php
@@ -50,240 +50,195 @@ public function tear_down() {
}
/**
- * Test products post type query does not get integrated when the feature is active
+ * Test search integration is on in general for product searches
*
* @since 2.1
* @group woocommerce
*/
- public function testProductsPostTypeQueryOn() {
+ public function testSearchOnAllFrontEnd() {
ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
ElasticPress\Features::factory()->setup_features();
- $this->ep_factory->post->create();
- $this->ep_factory->product->create(
- array(
- 'description' => 'product 1',
- )
- );
-
ElasticPress\Elasticsearch::factory()->refresh_indices();
$args = array(
+ 's' => 'findme',
'post_type' => 'product',
);
$query = new \WP_Query( $args );
- $this->assertNull( $query->elasticsearch_success );
- $this->assertEquals( 1, $query->post_count );
- $this->assertEquals( 1, $query->found_posts );
- }
-
- /**
- * Test products post type query does get integrated when querying WC product_cat taxonomy
- *
- * @since 2.1
- * @group woocommerce
- */
- public function testProductsPostTypeQueryProductCatTax() {
- ElasticPress\Features::factory()->activate_feature( 'admin' );
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $this->ep_factory->post->create();
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
- $args = array(
- 'tax_query' => array(
- array(
- 'taxonomy' => 'product_cat',
- 'terms' => array( 'cat' ),
- 'field' => 'slug',
- ),
- ),
- );
-
- $query = new \WP_Query( $args );
-
$this->assertTrue( $query->elasticsearch_success );
}
/**
- * Test search integration is on for shop orders
+ * Tests the search query for a shop_coupon.
*
- * @since 2.1
+ * @since 4.4.1
* @group woocommerce
*/
- public function testSearchOnShopOrderAdmin() {
- ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ public function testSearchQueryForCoupon() {
+
ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
ElasticPress\Features::factory()->setup_features();
- $this->ep_factory->post->create(
- array(
- 'post_content' => 'findme',
- 'post_type' => 'shop_order',
- )
+ // ensures that the search query doesn't use Elasticsearch.
+ $query = new \WP_Query(
+ [
+ 'post_type' => 'shop_coupon',
+ 's' => 'test-coupon',
+ ]
);
+ $this->assertNull( $query->elasticsearch_success );
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
- // mock the pagenow to bypass the search_order checks
- global $pagenow;
- $pagenow = 'edit.php';
-
- parse_str( 's=findme', $_GET );
- $args = array(
- 's' => 'findme',
- 'post_type' => 'shop_order',
+ // ensures that the search query doesn't use Elasticsearch when ep_integrate set to false.
+ $query = new \WP_Query(
+ [
+ 'post_type' => 'shop_coupon',
+ 's' => 'test-coupon',
+ 'ep_integrate' => false,
+ ]
);
+ $this->assertNull( $query->elasticsearch_success );
- $query = new \WP_Query( $args );
-
+ // ensures that the search query use Elasticsearch when ep_integrate set to true.
+ $query = new \WP_Query(
+ [
+ 'post_type' => 'shop_coupon',
+ 's' => 'test-coupon',
+ 'ep_integrate' => true,
+ ]
+ );
$this->assertTrue( $query->elasticsearch_success );
- $this->assertEquals( 1, $query->post_count );
- $this->assertEquals( 1, $query->found_posts );
-
- $pagenow = 'index.php';
}
/**
- * Test Shop Order post type query does not get integrated when the protected content feature is deactivated.
+ * Tests the search query for a shop_coupon in admin use Elasticsearch when protected content is enabled.
*
- * @since 4.5
+ * @since 4.4.1
+ * @group woocommerce
*/
- public function testShopOrderPostTypeQueryOn() {
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $this->ep_factory->post->create();
- $this->ep_factory->post->create(
- array(
- 'post_type' => 'shop_order',
- )
- );
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
- $args = array(
- 'post_type' => 'shop_order',
- );
- $query = new \WP_Query( $args );
+ public function testSearchQueryForCouponWhenProtectedContentIsEnable() {
- $this->assertNull( $query->elasticsearch_success );
- $this->assertEquals( 1, $query->post_count );
- $this->assertEquals( 1, $query->found_posts );
- }
+ set_current_screen( 'dashboard' );
+ $this->assertTrue( is_admin() );
- /**
- * Test Shop Order post type query does get integrated when the protected content feature is activated.
- *
- * @since 4.5
- */
- public function testShopOrderPostTypeQueryWhenProtectedContentEnable() {
ElasticPress\Features::factory()->activate_feature( 'protected_content' );
ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
ElasticPress\Features::factory()->setup_features();
- $this->ep_factory->post->create();
$this->ep_factory->post->create(
array(
- 'post_type' => 'shop_order',
+ 'post_content' => 'test-coupon',
+ 'post_type' => 'shop_coupon',
)
);
ElasticPress\Elasticsearch::factory()->refresh_indices();
- $args = array(
- 'post_type' => 'shop_order',
+ $query = new \WP_Query(
+ [
+ 'post_type' => 'shop_coupon',
+ 's' => 'test-coupon',
+ ]
);
- $query = new \WP_Query( $args );
$this->assertTrue( $query->elasticsearch_success );
$this->assertEquals( 1, $query->post_count );
- $this->assertEquals( 1, $query->found_posts );
}
/**
- * Test Shop Order post type query does not get integrated when the protected content feature is activated and ep_integrate is set to false.
+ * Tests the search query for a shop_coupon in admin does not use Elasticsearch when protected content is not enabled.
*
- * @since 4.5
+ * @since 4.4.1
+ * @group woocommerce
*/
- public function testShopOrderPostTypeQueryWhenEPIntegrateSetFalse() {
- ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ public function testSearchQueryForCouponWhenProtectedContentIsNotEnable() {
+
+ set_current_screen( 'dashboard' );
+ $this->assertTrue( is_admin() );
+
ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
ElasticPress\Features::factory()->setup_features();
- $this->ep_factory->post->create();
$this->ep_factory->post->create(
array(
- 'post_type' => 'shop_order',
+ 'post_content' => 'test-coupon',
+ 'post_type' => 'shop_coupon',
)
);
ElasticPress\Elasticsearch::factory()->refresh_indices();
- $args = array(
- 'post_type' => 'shop_order',
- 'ep_integrate' => false,
+ $query = new \WP_Query(
+ [
+ 'post_type' => 'shop_coupon',
+ 's' => 'test-coupon',
+ 'ep_integrate' => true,
+ ]
);
- $query = new \WP_Query( $args );
$this->assertNull( $query->elasticsearch_success );
+ $this->assertEquals( 1, $query->post_count );
}
/**
- * Test search for shop orders by order ID
+ * Test the `is_orders_autosuggest_available` method
*
- * @since 4.0.0
+ * @since 4.5.0
* @group woocommerce
*/
- public function testSearchShopOrderById() {
- ElasticPress\Features::factory()->activate_feature( 'protected_content' );
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $shop_order_id = $this->ep_factory->post->create(
- array(
- 'post_type' => 'shop_order',
- )
- );
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
+ public function testIsOrdersAutosuggestAvailable() {
+ $woocommerce_feature = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' );
- $args = array(
- 's' => (string) $shop_order_id,
- 'post_type' => 'shop_order',
- );
+ $this->assertSame( $woocommerce_feature->is_orders_autosuggest_available(), \ElasticPress\Utils\is_epio() );
- $query = new \WP_Query( $args );
+ /**
+ * Test the `ep_woocommerce_orders_autosuggest_available` filter
+ */
+ add_filter( 'ep_woocommerce_orders_autosuggest_available', '__return_true' );
+ $this->assertTrue( $woocommerce_feature->is_orders_autosuggest_available() );
- $this->assertTrue( $query->elasticsearch_success );
- $this->assertEquals( 1, $query->post_count );
- $this->assertEquals( 1, $query->found_posts );
+ add_filter( 'ep_woocommerce_orders_autosuggest_available', '__return_false' );
+ $this->assertFalse( $woocommerce_feature->is_orders_autosuggest_available() );
}
/**
- * Test search integration is on in general for product searches
+ * Test the `is_orders_autosuggest_available` method
*
- * @since 2.1
+ * @since 4.5.0
* @group woocommerce
*/
- public function testSearchOnAllFrontEnd() {
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
+ public function testIsOrdersAutosuggestEnabled() {
+ $woocommerce_feature = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' );
- ElasticPress\Elasticsearch::factory()->refresh_indices();
+ $this->assertFalse( $woocommerce_feature->is_orders_autosuggest_enabled() );
- $args = array(
- 's' => 'findme',
- 'post_type' => 'product',
- );
+ /**
+ * Make it available but it won't be enabled
+ */
+ add_filter( 'ep_woocommerce_orders_autosuggest_available', '__return_true' );
+ $this->assertFalse( $woocommerce_feature->is_orders_autosuggest_enabled() );
- $query = new \WP_Query( $args );
+ /**
+ * Enable it
+ */
+ $filter = function() {
+ return [
+ 'woocommerce' => [
+ 'orders' => '1',
+ ],
+ ];
+ };
+ add_filter( 'pre_site_option_ep_feature_settings', $filter );
+ add_filter( 'pre_option_ep_feature_settings', $filter );
+ $this->assertTrue( $woocommerce_feature->is_orders_autosuggest_enabled() );
- $this->assertTrue( $query->elasticsearch_success );
+ /**
+ * Make it unavailable. Even activated, it should not be considered enabled if not available anymore.
+ */
+ remove_filter( 'ep_woocommerce_orders_autosuggest_available', '__return_true' );
+ $this->assertFalse( $woocommerce_feature->is_orders_autosuggest_enabled() );
}
/**
@@ -399,834 +354,4 @@ public function testEPWoocommerceAdminProductsListSearchFields() {
[ 'post_title', 'post_content' ]
);
}
-
- /**
- * Tests the search query for a shop_coupon.
- *
- * @since 4.4.1
- */
- public function testSearchQueryForCoupon() {
-
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- // ensures that the search query doesn't use Elasticsearch.
- $query = new \WP_Query(
- [
- 'post_type' => 'shop_coupon',
- 's' => 'test-coupon',
- ]
- );
- $this->assertNull( $query->elasticsearch_success );
-
- // ensures that the search query doesn't use Elasticsearch when ep_integrate set to false.
- $query = new \WP_Query(
- [
- 'post_type' => 'shop_coupon',
- 's' => 'test-coupon',
- 'ep_integrate' => false,
- ]
- );
- $this->assertNull( $query->elasticsearch_success );
-
- // ensures that the search query use Elasticsearch when ep_integrate set to true.
- $query = new \WP_Query(
- [
- 'post_type' => 'shop_coupon',
- 's' => 'test-coupon',
- 'ep_integrate' => true,
- ]
- );
- $this->assertTrue( $query->elasticsearch_success );
- }
-
- /**
- * Tests the search query for a shop_coupon in admin use Elasticsearch when protected content is enabled.
- *
- * @since 4.4.1
- */
- public function testSearchQueryForCouponWhenProtectedContentIsEnable() {
-
- set_current_screen( 'dashboard' );
- $this->assertTrue( is_admin() );
-
- ElasticPress\Features::factory()->activate_feature( 'protected_content' );
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $this->ep_factory->post->create(
- array(
- 'post_content' => 'test-coupon',
- 'post_type' => 'shop_coupon',
- )
- );
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
- $query = new \WP_Query(
- [
- 'post_type' => 'shop_coupon',
- 's' => 'test-coupon',
- ]
- );
-
- $this->assertTrue( $query->elasticsearch_success );
- $this->assertEquals( 1, $query->post_count );
- }
-
- /**
- * Tests the search query for a shop_coupon in admin does not use Elasticsearch when protected content is not enabled.
- *
- * @since 4.4.1
- */
- public function testSearchQueryForCouponWhenProtectedContentIsNotEnable() {
-
- set_current_screen( 'dashboard' );
- $this->assertTrue( is_admin() );
-
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $this->ep_factory->post->create(
- array(
- 'post_content' => 'test-coupon',
- 'post_type' => 'shop_coupon',
- )
- );
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
- $query = new \WP_Query(
- [
- 'post_type' => 'shop_coupon',
- 's' => 'test-coupon',
- 'ep_integrate' => true,
- ]
- );
-
- $this->assertNull( $query->elasticsearch_success );
- $this->assertEquals( 1, $query->post_count );
- }
-
- /**
- * Test all the product attributes are synced.
- *
- * @since 4.5.0
- */
- public function testWoocommerceAttributeTaxonomiesAreSync() {
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $product_id = $this->ep_factory->product->create_variation_product();
-
- $post = new \ElasticPress\Indexable\Post\Post();
- $document = $post->prepare_document( $product_id );
-
- $this->assertArrayHasKey( 'pa_size', $document['terms'] );
- $this->assertArrayHasKey( 'pa_colour', $document['terms'] );
- $this->assertArrayHasKey( 'pa_number', $document['terms'] );
- }
-
- /**
- * Data provider for the testProductQueryOrder method.
- *
- * @return array
- */
- public function productQueryOrderDataProvider() : array {
- return [
- [
- 'total_sales',
- [ 'meta_key' => 'total_sales' ],
- false,
- [
- 0 => [ 'meta.total_sales.double' => [ 'order' => 'desc' ] ],
- 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
- ],
- ],
- [
- 'average_rating',
- [ 'meta_key' => '_wc_average_rating' ],
- false,
- [
- 0 => [ 'meta._wc_average_rating.double' => [ 'order' => 'desc' ] ],
- 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
- ],
- ],
- [
- 'regular_price',
- [
- 'orderby' => 'price',
- 'order' => 'DESC',
- ],
- false,
- [
- 0 => [ 'meta._price.double' => [ 'order' => 'desc' ] ],
- 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
- ],
- ],
- [
- 'total_sales',
- [
- 'orderby' => 'popularity',
- 'order' => 'DESC',
- ],
- false,
- [
- 0 => [ 'meta.total_sales.double' => [ 'order' => 'desc' ] ],
- 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
- ],
- ],
- [
- 'total_sales',
- [],
- 'popularity',
- [
- 0 => [ 'meta.total_sales.double' => [ 'order' => 'desc' ] ],
- 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
- ],
- ],
- [
- 'regular_price',
- [],
- 'price-desc',
- [
- 0 => [ 'meta._price.double' => [ 'order' => 'desc' ] ],
- 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
- ],
- ],
- [
- 'average_rating',
- [],
- 'rating',
- [
- 0 => [ 'meta._wc_average_rating.double' => [ 'order' => 'desc' ] ],
- 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
- ],
- ],
- [
- 'regular_price',
- [],
- 'price',
- [
- 0 => [ 'meta._price.double' => [ 'order' => 'asc' ] ],
- 1 => [ 'post_date' => [ 'order' => 'asc' ] ],
- ],
- 'asc',
- ],
- [
- 'sku',
- [],
- 'sku',
- [
- 0 => [ 'meta._sku.value.sortable' => [ 'order' => 'asc' ] ],
- 1 => [ 'post_date' => [ 'order' => 'asc' ] ],
- ],
- 'asc',
- ],
- [
- 'name',
- [],
- 'title',
- [
- 0 => [ 'post_title.sortable' => [ 'order' => 'asc' ] ],
- 1 => [ 'post_date' => [ 'order' => 'asc' ] ],
- ],
- 'asc',
- ],
- [
- '',
- [],
- 'default',
- [
- 0 => [ 'menu_order' => [ 'order' => 'asc' ] ],
- 1 => [ 'post_title.sortable' => [ 'order' => 'asc' ] ],
- 2 => [ 'post_date' => [ 'order' => 'asc' ] ],
- ],
- ],
- [ '', [], '', [ 0 => [ 'post_date' => [ 'order' => 'desc' ] ] ] ],
- ];
- }
-
- /**
- * Test the product query order.
- *
- * @param string $product_arg_key Field slug
- * @param array $query_args Query array
- * @param bool $query_string Query string
- * @param array $expected Value expected
- * @param string $order Order
- * @dataProvider productQueryOrderDataProvider
- * @since 4.5.0
- */
- public function testProductQueryOrder( $product_arg_key, $query_args, $query_string, $expected, $order = '' ) {
- global $wp_the_query;
-
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $product_1 = $this->ep_factory->product->create(
- array(
- $product_arg_key => 200,
- )
- );
-
- $product_2 = $this->ep_factory->product->create(
- array(
- $product_arg_key => 100,
- )
- );
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
- if ( $query_string ) {
- parse_str( 'orderby=' . $query_string, $_GET );
-
- // mock the query as post type archive
- add_action(
- 'parse_query',
- function( \WP_Query $query ) {
- $query->is_post_type_archive = true;
- }
- );
- }
-
- $args = array(
- 'post_type' => 'product',
- );
- $args = array_merge( $args, $query_args );
- $query = new \WP_Query( $args );
-
- // mock the query as main query
- $wp_the_query = $query;
-
- add_filter(
- 'ep_post_formatted_args',
- function ( $formatted_args ) use ( $expected ) {
- $this->assertEquals( $expected, $formatted_args['sort'] );
- return $formatted_args;
- }
- );
-
- $query = $query->query( $args );
-
- $this->assertTrue( $wp_the_query->elasticsearch_success, 'Elasticsearch query failed' );
- $this->assertEquals( 2, count( $query ) );
-
- if ( 'asc' === $order ) {
- $this->assertEquals( $product_2, $query[0]->ID );
- $this->assertEquals( $product_1, $query[1]->ID );
- } elseif ( 'desc' === $order ) {
- $this->assertEquals( $product_1, $query[0]->ID );
- $this->assertEquals( $product_2, $query[1]->ID );
- }
-
- \WC_Query::reset_chosen_attributes();
- }
-
- /**
- * Test the product query not use Elasticsearch if preview.
- *
- * @since 4.5.0
- */
- public function testQueryShouldNotUseElasticsearchIfPreview() {
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $args = array(
- 'post_type' => 'product',
- 'preview' => true,
- );
-
- $query = new \WP_Query( $args );
-
- $this->assertNull( $query->elasticsearch_success );
- }
-
- /**
- * Test that on Admin Product List use Elasticsearch.
- *
- * @since 4.5.0
- */
- public function testProductListInAdminUseElasticSearch() {
- global $typenow, $wc_list_table;
-
- set_current_screen( 'edit.php' );
- $this->assertTrue( is_admin() );
-
- // load required files
- include_once ABSPATH . 'wp-admin/includes/class-wp-posts-list-table.php';
- include_once WC()->plugin_path() . '/includes/admin/list-tables/class-wc-admin-list-table-products.php';
-
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->activate_feature( 'protected_content' );
- ElasticPress\Features::factory()->setup_features();
-
- // mock the global variables
- $typenow = 'product';
- $wc_list_table = new \WC_Admin_List_Table_Products();
-
- add_filter(
- 'ep_post_filters',
- function( $filters, $args, $query ) {
- $expected_result = array(
- 'terms' => array(
- 'post_type.raw' => array(
- 'product',
- ),
- ),
- );
-
- $this->assertEquals( $expected_result, $filters['post_type'] );
- return $filters;
- },
- 10,
- 3
- );
-
- parse_str( 'post_type=product&s=product', $_GET );
-
- $wp_list_table = new \WP_Posts_List_Table();
- $wp_list_table->prepare_items();
- }
-
- /**
- * Test that Search in Admin Product List use Elasticsearch.
- *
- * @since 4.5.0
- */
- public function testProductListSearchInAdminUseElasticSearch() {
- global $typenow, $wc_list_table;
-
- set_current_screen( 'edit.php' );
- $this->assertTrue( is_admin() );
-
- // load required files
- include_once ABSPATH . 'wp-admin/includes/class-wp-posts-list-table.php';
- include_once WC()->plugin_path() . '/includes/admin/list-tables/class-wc-admin-list-table-products.php';
-
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->activate_feature( 'protected_content' );
- ElasticPress\Features::factory()->setup_features();
-
- // mock the global variables
- $typenow = 'product';
- $wc_list_table = new \WC_Admin_List_Table_Products();
-
- add_filter(
- 'ep_post_formatted_args',
- function ( $formatted_args, $args, $wp_query ) {
- $this->assertEquals( 'findme', $formatted_args['query']['function_score']['query']['bool']['should'][0]['multi_match']['query'] );
- $this->assertEquals(
- $args['search_fields'],
- [
- 'post_title',
- 'post_content',
- 'post_excerpt',
- 'meta' => [
- '_sku',
- '_variations_skus',
- ],
- ]
- );
-
- return $formatted_args;
- },
- 10,
- 3
- );
-
- parse_str( 'post_type=product&s=findme', $_GET );
-
- $wp_list_table = new \WP_Posts_List_Table();
- $wp_list_table->prepare_items();
- }
-
- /**
- * Test the product query when price filter is set.
- *
- * @since 4.5.0
- */
- public function testPriceFilter() {
- global $wp_the_query, $wp_query;
-
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $this->ep_factory->product->create(
- [
- 'name' => 'Cap 1',
- 'regular_price' => 100,
- ]
- );
- $this->ep_factory->product->create(
- [
- 'name' => 'Cap 2',
- 'regular_price' => 800,
- ]
- );
- $this->ep_factory->product->create(
- [
- 'name' => 'Cap 3',
- 'regular_price' => 10000,
- ]
- );
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
- parse_str( 'min_price=1&max_price=999', $_GET );
-
- $args = array(
- 'post_type' => 'product',
- );
- $query = new \WP_Query( $args );
-
- // mock the query as main query and is_search
- $wp_the_query = $query;
- $wp_query->is_search = true;
-
- add_filter(
- 'ep_post_formatted_args',
- function ( $formatted_args ) {
-
- $expected_result = array(
- 'range' => array(
- 'meta._price.long' => array(
- 'gte' => 1,
- 'lte' => 999,
- 'boost' => 2,
- ),
- ),
- );
-
- $this->assertEquals( $expected_result, $formatted_args['query'] );
- return $formatted_args;
- },
- 15
- );
-
- $query = $query->query( $args );
-
- $this->assertTrue( $wp_the_query->elasticsearch_success, 'Elasticsearch query failed' );
- $this->assertEquals( 2, count( $query ) );
- }
-
- /**
- * Test the product search query when price filter is set.
- *
- * @since 4.5.0
- */
- public function testPriceFilterWithSearchQuery() {
- global $wp_the_query, $wp_query;
-
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $this->ep_factory->product->create(
- [
- 'name' => 'Cap 1',
- 'regular_price' => 100,
- ]
- );
-
- $this->ep_factory->product->create(
- [
- 'name' => 'Cap 2',
- 'regular_price' => 1000,
- ]
- );
-
- $this->ep_factory->product->create(
- [
- 'name' => 'Cap 3',
- 'regular_price' => 10000,
- ]
- );
-
- $this->ep_factory->product->create(
- [
- 'name' => 'Cap 4',
- 'regular_price' => 800,
- ]
- );
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
- parse_str( 'min_price=1&max_price=999', $_GET );
-
- $args = array(
- 's' => 'Cap',
- 'post_type' => 'product',
- );
- $query = new \WP_Query( $args );
-
- // mock the query as main query and is_search
- $wp_the_query = $query;
- $wp_query->is_search = true;
-
- $query = $query->query( $args );
-
- $this->assertTrue( $wp_the_query->elasticsearch_success, 'Elasticsearch query failed' );
- $this->assertEquals( 2, count( $query ) );
- }
-
- /**
- * Tests that attributes filter uses Elasticsearch.
- *
- * @since 4.5.0
- */
- public function testAttributesFilterUseES() {
- global $wp_the_query;
-
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $this->ep_factory->product->create_variation_product(
- [
- 'name' => 'Cap',
- ]
- );
-
- $this->ep_factory->product->create(
- [
- 'name' => 'Shoes',
- ]
- );
-
- $this->ep_factory->product->create(
- [
- 'name' => 'T-Shirt',
- ]
- );
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
- // mock the query as post type archive
- add_action(
- 'parse_query',
- function( \WP_Query $query ) {
- $query->is_post_type_archive = true;
- }
- );
-
- parse_str( 'filter_colour=blue', $_GET );
-
- $args = array(
- 'post_type' => 'product',
- );
- $query = new \WP_Query( $args );
-
- // mock the query as main query
- $wp_the_query = $query;
-
- $query = $query->query( $args );
-
- $this->assertTrue( $wp_the_query->elasticsearch_success );
- $this->assertEquals( 1, count( $query ) );
- $this->assertEquals( 'Cap', $query[0]->post_title );
- }
-
- /**
- * Tests that get_posts() uses Elasticsearch when ep_integrate is true.
- *
- * @since 4.5.0
- */
- public function testGetPosts() {
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $this->ep_factory->product->create();
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
- $posts = get_posts(
- [
- 'post_type' => 'product',
- 'ep_integrate' => true,
- ]
- );
-
- $this->assertTrue( $posts[0]->elasticsearch );
- }
-
- /**
- * Tests that get_posts() does not use Elasticsearch when ep_integrate is not set.
- *
- * @since 4.5.0
- */
- public function testGetPostQueryDoesNotUseElasticSearchByDefault() {
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $this->ep_factory->product->create();
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
- $posts = get_posts(
- [
- 'post_type' => 'product',
- ]
- );
-
- $properties = get_object_vars( $posts[0] );
- $this->assertArrayNotHasKey( 'elasticsearch', $properties );
- }
-
- /**
- * Tests that Weighting dashboard shows SKU and Variation SKUs option.
- *
- * @since 4.5.0
- */
- public function testSkuOptionAddInWeightDashboard() {
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $search = ElasticPress\Features::factory()->get_registered_feature( 'search' );
- $fields = $search->weighting->get_weightable_fields_for_post_type( 'product' );
-
- $this->assertArrayHasKey( 'meta._sku.value', $fields['attributes']['children'] );
- $this->assertArrayHasKey( 'meta._variations_skus.value', $fields['attributes']['children'] );
-
- $this->assertEquals( 'meta._sku.value', $fields['attributes']['children']['meta._sku.value']['key'] );
- $this->assertEquals( 'SKU', $fields['attributes']['children']['meta._sku.value']['label'] );
-
- $this->assertEquals( 'meta._variations_skus.value', $fields['attributes']['children']['meta._variations_skus.value']['key'] );
- $this->assertEquals( 'Variations SKUs', $fields['attributes']['children']['meta._variations_skus.value']['label'] );
- }
-
- /**
- * Test the `is_orders_autosuggest_available` method
- *
- * @since 4.5.0
- */
- public function testIsOrdersAutosuggestAvailable() {
- $woocommerce_feature = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' );
-
- $this->assertSame( $woocommerce_feature->is_orders_autosuggest_available(), \ElasticPress\Utils\is_epio() );
-
- /**
- * Test the `ep_woocommerce_orders_autosuggest_available` filter
- */
- add_filter( 'ep_woocommerce_orders_autosuggest_available', '__return_true' );
- $this->assertTrue( $woocommerce_feature->is_orders_autosuggest_available() );
-
- add_filter( 'ep_woocommerce_orders_autosuggest_available', '__return_false' );
- $this->assertFalse( $woocommerce_feature->is_orders_autosuggest_available() );
- }
-
- /**
- * Test the `is_orders_autosuggest_available` method
- *
- * @since 4.5.0
- */
- public function testIsOrdersAutosuggestEnabled() {
- $woocommerce_feature = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' );
-
- $this->assertFalse( $woocommerce_feature->is_orders_autosuggest_enabled() );
-
- /**
- * Make it available but it won't be enabled
- */
- add_filter( 'ep_woocommerce_orders_autosuggest_available', '__return_true' );
- $this->assertFalse( $woocommerce_feature->is_orders_autosuggest_enabled() );
-
- /**
- * Enable it
- */
- $filter = function() {
- return [
- 'woocommerce' => [
- 'orders' => '1',
- ],
- ];
- };
- add_filter( 'pre_site_option_ep_feature_settings', $filter );
- add_filter( 'pre_option_ep_feature_settings', $filter );
- $this->assertTrue( $woocommerce_feature->is_orders_autosuggest_enabled() );
-
- /**
- * Make it unavailable. Even activated, it should not be considered enabled if not available anymore.
- */
- remove_filter( 'ep_woocommerce_orders_autosuggest_available', '__return_true' );
- $this->assertFalse( $woocommerce_feature->is_orders_autosuggest_enabled() );
- }
-
- /**
- * Test if decaying is disabled on products.
- *
- * @since 4.6.0
- * @dataProvider decayingDisabledOnProductsProvider
- * @group woocommerce
- *
- * @param string $setting Value for `decaying_enabled`
- * @param array|string $post_type Post types to be queried
- * @param string $assert Assert method name (`assertDecayDisabled` or `assertDecayEnabled`)
- */
- public function testDecayingDisabledOnProducts( $setting, $post_type, $assert ) {
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- // Test decaying for product query when disabled_only_products is enabled
- ElasticPress\Features::factory()->update_feature(
- 'search',
- [
- 'active' => true,
- 'decaying_enabled' => $setting,
- ]
- );
-
- $query = new \WP_Query();
- $query_args = [
- 's' => 'test',
- 'post_type' => $post_type,
- ];
- $formatted_args = \ElasticPress\Indexables::factory()->get( 'post' )->format_args( $query_args, $query );
-
- $this->$assert( $formatted_args['query'] );
- }
-
- /**
- * Data provider for the testDecayingDisabledOnProducts method.
- *
- * @since 4.6.0
- * @return array
- */
- public function decayingDisabledOnProductsProvider() : array {
- return [
- [
- 'disabled_only_products',
- 'product',
- 'assertDecayDisabled',
- ],
- [
- 'disabled_only_products',
- [ 'product' ],
- 'assertDecayDisabled',
- ],
- [
- 'disabled_only_products',
- [ 'product', 'post' ],
- 'assertDecayEnabled',
- ],
- [
- 'disabled_includes_products',
- 'product',
- 'assertDecayDisabled',
- ],
- [
- 'disabled_includes_products',
- [ 'product' ],
- 'assertDecayDisabled',
- ],
- [
- 'disabled_includes_products',
- [ 'product', 'post' ],
- 'assertDecayDisabled',
- ],
- [
- 'disabled_includes_products',
- [ 'post', 'page' ],
- 'assertDecayEnabled',
- ],
- ];
- }
}
diff --git a/tests/php/features/WooCommerce/TestWooCommerceOrders.php b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
index b581a2a3cd..852c631e9f 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceOrders.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
@@ -14,6 +14,165 @@
* WC orders test class
*/
class TestWooCommerceOrders extends TestWooCommerce {
+ /**
+ * Test search integration is on for shop orders
+ *
+ * @group woocommerce
+ * @group woocommerce-orders
+ */
+ public function testSearchOnShopOrderAdmin() {
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->ep_factory->post->create(
+ array(
+ 'post_content' => 'findme',
+ 'post_type' => 'shop_order',
+ )
+ );
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ // mock the pagenow to bypass the search_order checks
+ global $pagenow;
+ $pagenow = 'edit.php';
+
+ parse_str( 's=findme', $_GET );
+ $args = array(
+ 's' => 'findme',
+ 'post_type' => 'shop_order',
+ );
+
+ $query = new \WP_Query( $args );
+
+ $this->assertTrue( $query->elasticsearch_success );
+ $this->assertEquals( 1, $query->post_count );
+ $this->assertEquals( 1, $query->found_posts );
+
+ $pagenow = 'index.php';
+ }
+
+ /**
+ * Test Shop Order post type query does not get integrated when the protected content feature is deactivated.
+ *
+ * @group woocommerce
+ * @group woocommerce-orders
+ */
+ public function testShopOrderPostTypeQueryOn() {
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->ep_factory->post->create();
+ $this->ep_factory->post->create(
+ array(
+ 'post_type' => 'shop_order',
+ )
+ );
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ $args = array(
+ 'post_type' => 'shop_order',
+ );
+ $query = new \WP_Query( $args );
+
+ $this->assertNull( $query->elasticsearch_success );
+ $this->assertEquals( 1, $query->post_count );
+ $this->assertEquals( 1, $query->found_posts );
+ }
+
+
+ /**
+ * Test Shop Order post type query does get integrated when the protected content feature is activated.
+ *
+ * @group woocommerce
+ * @group woocommerce-orders
+ */
+ public function testShopOrderPostTypeQueryWhenProtectedContentEnable() {
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->ep_factory->post->create();
+ $this->ep_factory->post->create(
+ array(
+ 'post_type' => 'shop_order',
+ )
+ );
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ $args = array(
+ 'post_type' => 'shop_order',
+ );
+ $query = new \WP_Query( $args );
+
+ $this->assertTrue( $query->elasticsearch_success );
+ $this->assertEquals( 1, $query->post_count );
+ $this->assertEquals( 1, $query->found_posts );
+ }
+
+ /**
+ * Test Shop Order post type query does not get integrated when the protected content feature is activated and ep_integrate is set to false.
+ *
+ * @group woocommerce
+ * @group woocommerce-orders
+ */
+ public function testShopOrderPostTypeQueryWhenEPIntegrateSetFalse() {
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->ep_factory->post->create();
+ $this->ep_factory->post->create(
+ array(
+ 'post_type' => 'shop_order',
+ )
+ );
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ $args = array(
+ 'post_type' => 'shop_order',
+ 'ep_integrate' => false,
+ );
+ $query = new \WP_Query( $args );
+
+ $this->assertNull( $query->elasticsearch_success );
+ }
+
+ /**
+ * Test search for shop orders by order ID
+ *
+ * @group woocommerce
+ * @group woocommerce-orders
+ */
+ public function testSearchShopOrderById() {
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $shop_order_id = $this->ep_factory->post->create(
+ array(
+ 'post_type' => 'shop_order',
+ )
+ );
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ $args = array(
+ 's' => (string) $shop_order_id,
+ 'post_type' => 'shop_order',
+ );
+
+ $query = new \WP_Query( $args );
+
+ $this->assertTrue( $query->elasticsearch_success );
+ $this->assertEquals( 1, $query->post_count );
+ $this->assertEquals( 1, $query->found_posts );
+ }
+
/**
* Test search for shop orders matching field and ID.
*
@@ -21,6 +180,7 @@ class TestWooCommerceOrders extends TestWooCommerce {
* both should be returned.
*
* @group woocommerce
+ * @group woocommerce-orders
*/
public function testSearchShopOrderByMetaFieldAndId() {
ElasticPress\Features::factory()->activate_feature( 'protected_content' );
diff --git a/tests/php/features/WooCommerce/TestWooCommerceProduct.php b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
index 80e375d0db..dc53a9edad 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceProduct.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
@@ -13,11 +13,697 @@
/**
* WC products test class
*/
-class TestWooCommerceProduct extends BaseTestCase {
+class TestWooCommerceProduct extends TestWooCommerce {
+ /**
+ * Test products post type query does not get integrated when the feature is active
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testProductsPostTypeQueryOn() {
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->ep_factory->post->create();
+ $this->ep_factory->product->create(
+ array(
+ 'description' => 'product 1',
+ )
+ );
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ $args = array(
+ 'post_type' => 'product',
+ );
+
+ $query = new \WP_Query( $args );
+
+ $this->assertNull( $query->elasticsearch_success );
+ $this->assertEquals( 1, $query->post_count );
+ $this->assertEquals( 1, $query->found_posts );
+ }
+
+ /**
+ * Test products post type query does get integrated when querying WC product_cat taxonomy
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testProductsPostTypeQueryProductCatTax() {
+ ElasticPress\Features::factory()->activate_feature( 'admin' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->ep_factory->post->create();
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ $args = array(
+ 'tax_query' => array(
+ array(
+ 'taxonomy' => 'product_cat',
+ 'terms' => array( 'cat' ),
+ 'field' => 'slug',
+ ),
+ ),
+ );
+
+ $query = new \WP_Query( $args );
+
+ $this->assertTrue( $query->elasticsearch_success );
+
+ $args = [ 'product_cat' => 'cat' ];
+
+ $query = new \WP_Query( $args );
+
+ $this->assertTrue( $query->elasticsearch_success );
+ }
+
+
+ /**
+ * Test search integration is on in general for product searches
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testSearchOnAllFrontEnd() {
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ $args = array(
+ 's' => 'findme',
+ 'post_type' => 'product',
+ );
+
+ $query = new \WP_Query( $args );
+
+ $this->assertTrue( $query->elasticsearch_success );
+ }
+
+ /**
+ * Test all the product attributes are synced.
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testWoocommerceAttributeTaxonomiesAreSync() {
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $product_id = $this->ep_factory->product->create_variation_product();
+
+ $post = new \ElasticPress\Indexable\Post\Post();
+ $document = $post->prepare_document( $product_id );
+
+ $this->assertArrayHasKey( 'pa_size', $document['terms'] );
+ $this->assertArrayHasKey( 'pa_colour', $document['terms'] );
+ $this->assertArrayHasKey( 'pa_number', $document['terms'] );
+ }
+
+ /**
+ * Data provider for the testProductQueryOrder method.
+ *
+ * @return array
+ */
+ public function productQueryOrderDataProvider() : array {
+ return [
+ [
+ 'total_sales',
+ [ 'meta_key' => 'total_sales' ],
+ false,
+ [
+ 0 => [ 'meta.total_sales.double' => [ 'order' => 'desc' ] ],
+ 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
+ ],
+ ],
+ [
+ 'average_rating',
+ [ 'meta_key' => '_wc_average_rating' ],
+ false,
+ [
+ 0 => [ 'meta._wc_average_rating.double' => [ 'order' => 'desc' ] ],
+ 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
+ ],
+ ],
+ [
+ 'regular_price',
+ [
+ 'orderby' => 'price',
+ 'order' => 'DESC',
+ ],
+ false,
+ [
+ 0 => [ 'meta._price.double' => [ 'order' => 'desc' ] ],
+ 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
+ ],
+ ],
+ [
+ 'total_sales',
+ [
+ 'orderby' => 'popularity',
+ 'order' => 'DESC',
+ ],
+ false,
+ [
+ 0 => [ 'meta.total_sales.double' => [ 'order' => 'desc' ] ],
+ 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
+ ],
+ ],
+ [
+ 'total_sales',
+ [],
+ 'popularity',
+ [
+ 0 => [ 'meta.total_sales.double' => [ 'order' => 'desc' ] ],
+ 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
+ ],
+ ],
+ [
+ 'regular_price',
+ [],
+ 'price-desc',
+ [
+ 0 => [ 'meta._price.double' => [ 'order' => 'desc' ] ],
+ 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
+ ],
+ ],
+ [
+ 'average_rating',
+ [],
+ 'rating',
+ [
+ 0 => [ 'meta._wc_average_rating.double' => [ 'order' => 'desc' ] ],
+ 1 => [ 'post_date' => [ 'order' => 'desc' ] ],
+ ],
+ ],
+ [
+ 'regular_price',
+ [],
+ 'price',
+ [
+ 0 => [ 'meta._price.double' => [ 'order' => 'asc' ] ],
+ 1 => [ 'post_date' => [ 'order' => 'asc' ] ],
+ ],
+ 'asc',
+ ],
+ [
+ 'sku',
+ [],
+ 'sku',
+ [
+ 0 => [ 'meta._sku.value.sortable' => [ 'order' => 'asc' ] ],
+ 1 => [ 'post_date' => [ 'order' => 'asc' ] ],
+ ],
+ 'asc',
+ ],
+ [
+ 'name',
+ [],
+ 'title',
+ [
+ 0 => [ 'post_title.sortable' => [ 'order' => 'asc' ] ],
+ 1 => [ 'post_date' => [ 'order' => 'asc' ] ],
+ ],
+ 'asc',
+ ],
+ [
+ '',
+ [],
+ 'default',
+ [
+ 0 => [ 'menu_order' => [ 'order' => 'asc' ] ],
+ 1 => [ 'post_title.sortable' => [ 'order' => 'asc' ] ],
+ 2 => [ 'post_date' => [ 'order' => 'asc' ] ],
+ ],
+ ],
+ [ '', [], '', [ 0 => [ 'post_date' => [ 'order' => 'desc' ] ] ] ],
+ ];
+ }
+
+ /**
+ * Test the product query order.
+ *
+ * @param string $product_arg_key Field slug
+ * @param array $query_args Query array
+ * @param bool $query_string Query string
+ * @param array $expected Value expected
+ * @param string $order Order
+ * @dataProvider productQueryOrderDataProvider
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testProductQueryOrder( $product_arg_key, $query_args, $query_string, $expected, $order = '' ) {
+ global $wp_the_query;
+
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $product_1 = $this->ep_factory->product->create(
+ array(
+ $product_arg_key => 200,
+ )
+ );
+
+ $product_2 = $this->ep_factory->product->create(
+ array(
+ $product_arg_key => 100,
+ )
+ );
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ if ( $query_string ) {
+ parse_str( 'orderby=' . $query_string, $_GET );
+
+ // mock the query as post type archive
+ add_action(
+ 'parse_query',
+ function( \WP_Query $query ) {
+ $query->is_post_type_archive = true;
+ }
+ );
+ }
+
+ $args = array(
+ 'post_type' => 'product',
+ );
+ $args = array_merge( $args, $query_args );
+ $query = new \WP_Query( $args );
+
+ // mock the query as main query
+ $wp_the_query = $query;
+
+ add_filter(
+ 'ep_post_formatted_args',
+ function ( $formatted_args ) use ( $expected ) {
+ $this->assertEquals( $expected, $formatted_args['sort'] );
+ return $formatted_args;
+ }
+ );
+
+ $query = $query->query( $args );
+
+ $this->assertTrue( $wp_the_query->elasticsearch_success, 'Elasticsearch query failed' );
+ $this->assertEquals( 2, count( $query ) );
+
+ if ( 'asc' === $order ) {
+ $this->assertEquals( $product_2, $query[0]->ID );
+ $this->assertEquals( $product_1, $query[1]->ID );
+ } elseif ( 'desc' === $order ) {
+ $this->assertEquals( $product_1, $query[0]->ID );
+ $this->assertEquals( $product_2, $query[1]->ID );
+ }
+
+ \WC_Query::reset_chosen_attributes();
+ }
+
+ /**
+ * Test the product query not use Elasticsearch if preview.
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testQueryShouldNotUseElasticsearchIfPreview() {
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $args = array(
+ 'post_type' => 'product',
+ 'preview' => true,
+ );
+
+ $query = new \WP_Query( $args );
+
+ $this->assertNull( $query->elasticsearch_success );
+ }
+
+
+ /**
+ * Test that on Admin Product List use Elasticsearch.
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testProductListInAdminUseElasticSearch() {
+ global $typenow, $wc_list_table;
+
+ set_current_screen( 'edit.php' );
+ $this->assertTrue( is_admin() );
+
+ // load required files
+ include_once ABSPATH . 'wp-admin/includes/class-wp-posts-list-table.php';
+ include_once WC()->plugin_path() . '/includes/admin/list-tables/class-wc-admin-list-table-products.php';
+
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->setup_features();
+
+ // mock the global variables
+ $typenow = 'product';
+ $wc_list_table = new \WC_Admin_List_Table_Products();
+
+ add_filter(
+ 'ep_post_filters',
+ function( $filters, $args, $query ) {
+ $expected_result = array(
+ 'terms' => array(
+ 'post_type.raw' => array(
+ 'product',
+ ),
+ ),
+ );
+
+ $this->assertEquals( $expected_result, $filters['post_type'] );
+ return $filters;
+ },
+ 10,
+ 3
+ );
+
+ parse_str( 'post_type=product&s=product', $_GET );
+
+ $wp_list_table = new \WP_Posts_List_Table();
+ $wp_list_table->prepare_items();
+ }
+
+ /**
+ * Test that Search in Admin Product List use Elasticsearch.
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testProductListSearchInAdminUseElasticSearch() {
+ global $typenow, $wc_list_table;
+
+ set_current_screen( 'edit.php' );
+ $this->assertTrue( is_admin() );
+
+ // load required files
+ include_once ABSPATH . 'wp-admin/includes/class-wp-posts-list-table.php';
+ include_once WC()->plugin_path() . '/includes/admin/list-tables/class-wc-admin-list-table-products.php';
+
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->setup_features();
+
+ // mock the global variables
+ $typenow = 'product';
+ $wc_list_table = new \WC_Admin_List_Table_Products();
+
+ add_filter(
+ 'ep_post_formatted_args',
+ function ( $formatted_args, $args, $wp_query ) {
+ $this->assertEquals( 'findme', $formatted_args['query']['function_score']['query']['bool']['should'][0]['multi_match']['query'] );
+ $this->assertEquals(
+ $args['search_fields'],
+ [
+ 'post_title',
+ 'post_content',
+ 'post_excerpt',
+ 'meta' => [
+ '_sku',
+ '_variations_skus',
+ ],
+ ]
+ );
+
+ return $formatted_args;
+ },
+ 10,
+ 3
+ );
+
+ parse_str( 'post_type=product&s=findme', $_GET );
+
+ $wp_list_table = new \WP_Posts_List_Table();
+ $wp_list_table->prepare_items();
+ }
+
+ /**
+ * Test the product query when price filter is set.
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testPriceFilter() {
+ global $wp_the_query, $wp_query;
+
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->ep_factory->product->create(
+ [
+ 'name' => 'Cap 1',
+ 'regular_price' => 100,
+ ]
+ );
+ $this->ep_factory->product->create(
+ [
+ 'name' => 'Cap 2',
+ 'regular_price' => 800,
+ ]
+ );
+ $this->ep_factory->product->create(
+ [
+ 'name' => 'Cap 3',
+ 'regular_price' => 10000,
+ ]
+ );
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ parse_str( 'min_price=1&max_price=999', $_GET );
+
+ $args = array(
+ 'post_type' => 'product',
+ );
+ $query = new \WP_Query( $args );
+
+ // mock the query as main query and is_search
+ $wp_the_query = $query;
+ $wp_query->is_search = true;
+
+ add_filter(
+ 'ep_post_formatted_args',
+ function ( $formatted_args ) {
+
+ $expected_result = array(
+ 'range' => array(
+ 'meta._price.long' => array(
+ 'gte' => 1,
+ 'lte' => 999,
+ 'boost' => 2,
+ ),
+ ),
+ );
+
+ $this->assertEquals( $expected_result, $formatted_args['query'] );
+ return $formatted_args;
+ },
+ 15
+ );
+
+ $query = $query->query( $args );
+
+ $this->assertTrue( $wp_the_query->elasticsearch_success, 'Elasticsearch query failed' );
+ $this->assertEquals( 2, count( $query ) );
+ }
+
+ /**
+ * Test the product search query when price filter is set.
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testPriceFilterWithSearchQuery() {
+ global $wp_the_query, $wp_query;
+
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->ep_factory->product->create(
+ [
+ 'name' => 'Cap 1',
+ 'regular_price' => 100,
+ ]
+ );
+
+ $this->ep_factory->product->create(
+ [
+ 'name' => 'Cap 2',
+ 'regular_price' => 1000,
+ ]
+ );
+
+ $this->ep_factory->product->create(
+ [
+ 'name' => 'Cap 3',
+ 'regular_price' => 10000,
+ ]
+ );
+
+ $this->ep_factory->product->create(
+ [
+ 'name' => 'Cap 4',
+ 'regular_price' => 800,
+ ]
+ );
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ parse_str( 'min_price=1&max_price=999', $_GET );
+
+ $args = array(
+ 's' => 'Cap',
+ 'post_type' => 'product',
+ );
+ $query = new \WP_Query( $args );
+
+ // mock the query as main query and is_search
+ $wp_the_query = $query;
+ $wp_query->is_search = true;
+
+ $query = $query->query( $args );
+
+ $this->assertTrue( $wp_the_query->elasticsearch_success, 'Elasticsearch query failed' );
+ $this->assertEquals( 2, count( $query ) );
+ }
+
+ /**
+ * Tests that attributes filter uses Elasticsearch.
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testAttributesFilterUseES() {
+ global $wp_the_query;
+
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->ep_factory->product->create_variation_product(
+ [
+ 'name' => 'Cap',
+ ]
+ );
+
+ $this->ep_factory->product->create(
+ [
+ 'name' => 'Shoes',
+ ]
+ );
+
+ $this->ep_factory->product->create(
+ [
+ 'name' => 'T-Shirt',
+ ]
+ );
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ // mock the query as post type archive
+ add_action(
+ 'parse_query',
+ function( \WP_Query $query ) {
+ $query->is_post_type_archive = true;
+ }
+ );
+
+ parse_str( 'filter_colour=blue', $_GET );
+
+ $args = array(
+ 'post_type' => 'product',
+ );
+ $query = new \WP_Query( $args );
+
+ // mock the query as main query
+ $wp_the_query = $query;
+
+ $query = $query->query( $args );
+
+ $this->assertTrue( $wp_the_query->elasticsearch_success );
+ $this->assertEquals( 1, count( $query ) );
+ $this->assertEquals( 'Cap', $query[0]->post_title );
+ }
+
+ /**
+ * Tests that get_posts() uses Elasticsearch when ep_integrate is true.
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testGetPosts() {
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->ep_factory->product->create();
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ $posts = get_posts(
+ [
+ 'post_type' => 'product',
+ 'ep_integrate' => true,
+ ]
+ );
+
+ $this->assertTrue( $posts[0]->elasticsearch );
+ }
+
+ /**
+ * Tests that get_posts() does not use Elasticsearch when ep_integrate is not set.
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testGetPostQueryDoesNotUseElasticSearchByDefault() {
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->ep_factory->product->create();
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ $posts = get_posts(
+ [
+ 'post_type' => 'product',
+ ]
+ );
+
+ $properties = get_object_vars( $posts[0] );
+ $this->assertArrayNotHasKey( 'elasticsearch', $properties );
+ }
+
+ /**
+ * Tests that Weighting dashboard shows SKU and Variation SKUs option.
+ *
+ * @group woocommerce
+ */
+ public function testSkuOptionAddInWeightDashboard() {
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $search = ElasticPress\Features::factory()->get_registered_feature( 'search' );
+ $fields = $search->weighting->get_weightable_fields_for_post_type( 'product' );
+
+ $this->assertArrayHasKey( 'meta._sku.value', $fields['attributes']['children'] );
+ $this->assertArrayHasKey( 'meta._variations_skus.value', $fields['attributes']['children'] );
+
+ $this->assertEquals( 'meta._sku.value', $fields['attributes']['children']['meta._sku.value']['key'] );
+ $this->assertEquals( 'SKU', $fields['attributes']['children']['meta._sku.value']['label'] );
+
+ $this->assertEquals( 'meta._variations_skus.value', $fields['attributes']['children']['meta._variations_skus.value']['key'] );
+ $this->assertEquals( 'Variations SKUs', $fields['attributes']['children']['meta._variations_skus.value']['label'] );
+ }
+
/**
* Test the addition of variations skus to product meta
*
* @group woocommerce
+ * @group woocommerce-products
*/
public function testAddVariationsSkusMeta() {
ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
@@ -55,6 +741,7 @@ public function testAddVariationsSkusMeta() {
* Test the translate_args_admin_products_list method
*
* @group woocommerce
+ * @group woocommerce-products
*/
public function testTranslateArgsAdminProductsList() {
ElasticPress\Features::factory()->activate_feature( 'protected_content' );
@@ -96,6 +783,7 @@ public function testTranslateArgsAdminProductsList() {
* Test the ep_woocommerce_admin_products_list_search_fields filter
*
* @group woocommerce
+ * @group woocommerce-products
*/
public function testEPWoocommerceAdminProductsListSearchFields() {
ElasticPress\Features::factory()->activate_feature( 'protected_content' );
@@ -122,4 +810,83 @@ public function testEPWoocommerceAdminProductsListSearchFields() {
[ 'post_title', 'post_content' ]
);
}
+
+ /**
+ * Test if decaying is disabled on products.
+ *
+ * @dataProvider decayingDisabledOnProductsProvider
+ * @group woocommerce
+ * @group woocommerce-products
+ *
+ * @param string $setting Value for `decaying_enabled`
+ * @param array|string $post_type Post types to be queried
+ * @param string $assert Assert method name (`assertDecayDisabled` or `assertDecayEnabled`)
+ */
+ public function testDecayingDisabledOnProducts( $setting, $post_type, $assert ) {
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ // Test decaying for product query when disabled_only_products is enabled
+ ElasticPress\Features::factory()->update_feature(
+ 'search',
+ [
+ 'active' => true,
+ 'decaying_enabled' => $setting,
+ ]
+ );
+
+ $query = new \WP_Query();
+ $query_args = [
+ 's' => 'test',
+ 'post_type' => $post_type,
+ ];
+ $formatted_args = \ElasticPress\Indexables::factory()->get( 'post' )->format_args( $query_args, $query );
+
+ $this->$assert( $formatted_args['query'] );
+ }
+
+ /**
+ * Data provider for the testDecayingDisabledOnProducts method.
+ *
+ * @return array
+ */
+ public function decayingDisabledOnProductsProvider() : array {
+ return [
+ [
+ 'disabled_only_products',
+ 'product',
+ 'assertDecayDisabled',
+ ],
+ [
+ 'disabled_only_products',
+ [ 'product' ],
+ 'assertDecayDisabled',
+ ],
+ [
+ 'disabled_only_products',
+ [ 'product', 'post' ],
+ 'assertDecayEnabled',
+ ],
+ [
+ 'disabled_includes_products',
+ 'product',
+ 'assertDecayDisabled',
+ ],
+ [
+ 'disabled_includes_products',
+ [ 'product' ],
+ 'assertDecayDisabled',
+ ],
+ [
+ 'disabled_includes_products',
+ [ 'product', 'post' ],
+ 'assertDecayDisabled',
+ ],
+ [
+ 'disabled_includes_products',
+ [ 'post', 'page' ],
+ 'assertDecayEnabled',
+ ],
+ ];
+ }
}
From b47d9c49f6e2ef3390883c157908da43caba4d11 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Thu, 22 Jun 2023 13:59:04 -0300
Subject: [PATCH 10/45] Test deprecation of moved methods
---
.../classes/Feature/WooCommerce/Orders.php | 2 +-
.../WooCommerce/TestWooCommerceOrders.php | 48 +++++++++++++++++++
.../WooCommerce/TestWooCommerceProduct.php | 2 +-
3 files changed, 50 insertions(+), 2 deletions(-)
diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php
index 499546c48a..6a2ffafa0c 100644
--- a/includes/classes/Feature/WooCommerce/Orders.php
+++ b/includes/classes/Feature/WooCommerce/Orders.php
@@ -433,7 +433,7 @@ public function __call( $method_name, $arguments ) {
if ( in_array( $method_name, $orders_autosuggest_methods, true ) ) {
_deprecated_function(
- __METHOD__,
+ "\ElasticPress\Feature\WooCommerce\WooCommerce\Orders::{$method_name}", // phpcs:ignore
'4.7.0',
"\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders_autosuggest->{$method_name}()" // phpcs:ignore
);
diff --git a/tests/php/features/WooCommerce/TestWooCommerceOrders.php b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
index 852c631e9f..73860ac3d8 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceOrders.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
@@ -213,4 +213,52 @@ public function testSearchShopOrderByMetaFieldAndId() {
$this->assertEquals( 2, $query->post_count );
$this->assertEquals( 2, $query->found_posts );
}
+
+ /**
+ * Test if methods moved to OrdersAutosuggest are correctly flagged
+ *
+ * @param string $method The method name
+ * @param array $args Method arguments
+ * @dataProvider ordersAutosuggestMethodsDataProvider
+ * @group woocommerce
+ * @group woocommerce-orders
+ */
+ public function testOrdersAutosuggestMethods( $method, $args ) {
+ $this->setExpectedDeprecated( "\ElasticPress\Feature\WooCommerce\WooCommerce\Orders::{$method}" );
+ $orders = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders;
+ $orders->$method( ...$args );
+ }
+
+ /**
+ * Data provider for the testOrdersAutosuggestMethods method.
+ *
+ * @return array
+ */
+ public function ordersAutosuggestMethodsDataProvider() : array {
+ return [
+ [ 'after_update_feature', [ 'test', [], [] ] ],
+ [ 'check_token_permission', [] ],
+ [ 'enqueue_admin_assets', [ '' ] ],
+ [ 'epio_delete_search_template', [] ],
+ [ 'epio_get_search_template', [] ],
+ [ 'epio_save_search_template', [] ],
+ [ 'filter_term_suggest', [ [] ] ],
+ [ 'get_args_schema', [] ],
+ [ 'get_search_endpoint', [] ],
+ [ 'get_search_template', [] ],
+ [ 'get_template_endpoint', [] ],
+ [ 'get_token', [] ],
+ [ 'get_token_endpoint', [] ],
+ [ 'intercept_search_request', [ (object) [] ] ],
+ [ 'is_integrated_request', [ true, [] ] ],
+ [ 'post_statuses', [ [] ] ],
+ [ 'post_types', [ [] ] ],
+ [ 'mapping', [ [] ] ],
+ [ 'maybe_query_password_protected_posts', [ [] ] ],
+ [ 'maybe_set_posts_where', [ '', new \WP_Query( [] ) ] ],
+ [ 'refresh_token', [] ],
+ [ 'rest_api_init', [] ],
+ [ 'set_search_fields', [] ],
+ ];
+ }
}
diff --git a/tests/php/features/WooCommerce/TestWooCommerceProduct.php b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
index dc53a9edad..4cf1d66713 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceProduct.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
@@ -244,7 +244,7 @@ public function productQueryOrderDataProvider() : array {
}
/**
- * Test the product query order.
+ * Test the product query order.
*
* @param string $product_arg_key Field slug
* @param array $query_args Query array
From 95cb4592fbacfa6ac373f0721e884cc1cf851dfd Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Thu, 22 Jun 2023 14:44:38 -0300
Subject: [PATCH 11/45] Unit tests for filters
---
.../classes/Feature/WooCommerce/Orders.php | 21 ++-
.../classes/Feature/WooCommerce/Products.php | 24 +++-
.../WooCommerce/TestWooCommerceOrders.php | 74 +++++++++-
.../TestWooCommerceOrdersAutosuggest.php | 18 ++-
.../WooCommerce/TestWooCommerceProduct.php | 132 ++++++++++++++++++
5 files changed, 254 insertions(+), 15 deletions(-)
diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php
index 6a2ffafa0c..8dd4da7928 100644
--- a/includes/classes/Feature/WooCommerce/Orders.php
+++ b/includes/classes/Feature/WooCommerce/Orders.php
@@ -279,14 +279,29 @@ public function get_supported_post_types() : array {
$post_types = [ 'shop_order', 'shop_order_refund' ];
/**
- * Expands or contracts the post_types eligible for indexing.
+ * DEPRECATED. Expands or contracts the post_types eligible for indexing.
*
* @hook ep_woocommerce_default_supported_post_types
* @since 4.4.0
* @param {array} $post_types Post types
* @return {array} New post types
*/
- $supported_post_types = apply_filters( 'ep_woocommerce_default_supported_post_types', $post_types );
+ $supported_post_types = apply_filters_deprecated(
+ 'ep_woocommerce_default_supported_post_types',
+ [ $post_types ],
+ '4.7.0',
+ 'ep_woocommerce_orders_supported_post_types'
+ );
+
+ /**
+ * Expands or contracts the post_types related to orders eligible for indexing.
+ *
+ * @hook ep_woocommerce_orders_supported_post_types
+ * @since 4.7.0
+ * @param {array} $post_types Post types
+ * @return {array} New post types
+ */
+ $supported_post_types = apply_filters( 'ep_woocommerce_orders_supported_post_types', $post_types );
$supported_post_types = array_intersect(
$supported_post_types,
@@ -302,7 +317,7 @@ public function get_supported_post_types() : array {
* @param \WP_Query $query The WP_Query
* @return \WP_Query
*/
- public function maybe_set_search_fields( \WP_Query $query ) {
+ protected function maybe_set_search_fields( \WP_Query $query ) {
$search_term = $this->woocommerce->get_search_term( $query );
if ( empty( $search_term ) ) {
return $query;
diff --git a/includes/classes/Feature/WooCommerce/Products.php b/includes/classes/Feature/WooCommerce/Products.php
index d8a480de68..ce38098bf1 100644
--- a/includes/classes/Feature/WooCommerce/Products.php
+++ b/includes/classes/Feature/WooCommerce/Products.php
@@ -710,14 +710,30 @@ public function get_supported_post_types( \WP_Query $query ) : array {
}
/**
- * Expands or contracts the post_types eligible for indexing.
+ * DEPRECATED. Expands or contracts the post_types eligible for indexing.
*
* @hook ep_woocommerce_default_supported_post_types
* @since 4.4.0
- * @param {array} $post_types Post types
- * @return {array} New post types
+ * @param {array} $post_types Post types
+ * @return {array} New post types
*/
- $supported_post_types = apply_filters( 'ep_woocommerce_default_supported_post_types', $post_types );
+ $supported_post_types = apply_filters_deprecated(
+ 'ep_woocommerce_default_supported_post_types',
+ [ $post_types ],
+ '4.7.0',
+ 'ep_woocommerce_products_supported_post_types'
+ );
+
+ /**
+ * Expands or contracts the post_types related to products eligible for indexing.
+ *
+ * @hook ep_woocommerce_products_supported_post_types
+ * @since 4.7.0
+ * @param {array} $post_types Post types
+ * @param {WP_Query} $query The WP_Query object
+ * @return {array} New post types
+ */
+ $supported_post_types = apply_filters( 'ep_woocommerce_products_supported_post_types', $post_types, $query );
$supported_post_types = array_intersect(
$supported_post_types,
diff --git a/tests/php/features/WooCommerce/TestWooCommerceOrders.php b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
index 73860ac3d8..9c1eff486e 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceOrders.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
@@ -14,6 +14,24 @@
* WC orders test class
*/
class TestWooCommerceOrders extends TestWooCommerce {
+ /**
+ * Orders instance
+ *
+ * @var Orders
+ */
+ protected $orders;
+
+ /**
+ * Setup each test.
+ *
+ * @group woocommerce
+ * @group woocommerce-orders
+ */
+ public function set_up() {
+ parent::set_up();
+ $this->orders = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders;
+ }
+
/**
* Test search integration is on for shop orders
*
@@ -214,6 +232,59 @@ public function testSearchShopOrderByMetaFieldAndId() {
$this->assertEquals( 2, $query->found_posts );
}
+ /**
+ * Test the `get_admin_searchable_post_types` method
+ *
+ * @group woocommerce
+ * @group woocommerce-orders
+ */
+ public function testGetAdminSearchablePostTypes() {
+ $default_post_types = $this->orders->get_admin_searchable_post_types();
+ $this->assertSame( $default_post_types, [ 'shop_order' ] );
+
+ /**
+ * Test the `ep_woocommerce_admin_searchable_post_types` filter
+ */
+ $add_post_type = function ( $post_types ) {
+ $post_types[] = 'shop_order_custom';
+ return $post_types;
+ };
+ add_filter( 'ep_woocommerce_admin_searchable_post_types', $add_post_type );
+
+ $new_post_types = $this->orders->get_admin_searchable_post_types();
+ $this->assertSame( $new_post_types, [ 'shop_order', 'shop_order_custom' ] );
+ }
+
+ /**
+ * Test the `get_supported_post_types` method
+ *
+ * @group woocommerce
+ * @group woocommerce-orders
+ */
+ public function testGetSupportedPostTypes() {
+ $default_supported = $this->orders->get_supported_post_types();
+ $this->assertSame( $default_supported, [] );
+
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $default_supported = $this->orders->get_supported_post_types();
+ $this->assertSame( $default_supported, [ 'shop_order', 'shop_order_refund' ] );
+
+ /**
+ * Test the `ep_woocommerce_orders_supported_post_types` filter
+ */
+ $add_post_type = function( $post_types ) {
+ $post_types[] = 'shop_order_custom';
+ return $post_types;
+ };
+ add_filter( 'ep_woocommerce_orders_supported_post_types', $add_post_type );
+
+ $custom_supported = $this->orders->get_supported_post_types();
+ $this->assertSame( $custom_supported, [ 'shop_order', 'shop_order_refund' ] );
+ }
+
/**
* Test if methods moved to OrdersAutosuggest are correctly flagged
*
@@ -225,8 +296,7 @@ public function testSearchShopOrderByMetaFieldAndId() {
*/
public function testOrdersAutosuggestMethods( $method, $args ) {
$this->setExpectedDeprecated( "\ElasticPress\Feature\WooCommerce\WooCommerce\Orders::{$method}" );
- $orders = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders;
- $orders->$method( ...$args );
+ $this->orders->$method( ...$args );
}
/**
diff --git a/tests/php/features/WooCommerce/TestWooCommerceOrdersAutosuggest.php b/tests/php/features/WooCommerce/TestWooCommerceOrdersAutosuggest.php
index 1fc969c718..de3682e8d6 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceOrdersAutosuggest.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceOrdersAutosuggest.php
@@ -31,7 +31,8 @@ class TestWooCommerceOrdersAutosuggest extends BaseTestCase {
/**
* Setup each test.
*
- * @group WooCommerceOrdersAutosuggest
+ * @group woocommerce
+ * @group woocommerce-orders-autosuggest
*/
public function set_up() {
parent::set_up();
@@ -51,7 +52,8 @@ public function set_up() {
/**
* Test the `filter_term_suggest` method
*
- * @group WooCommerceOrdersAutosuggest
+ * @group woocommerce
+ * @group woocommerce-orders-autosuggest
*/
public function testFilterTermSuggest() {
$order = [
@@ -104,7 +106,8 @@ public function testFilterTermSuggest() {
*
* This method steps into WooCommerce functionality a bit.
*
- * @group WooCommerceOrdersAutosuggest
+ * @group woocommerce
+ * @group woocommerce-orders-autosuggest
*/
public function testFilterTermSuggestWithCustomOrderId() {
$shop_order_1 = new \WC_Order();
@@ -147,7 +150,8 @@ public function testFilterTermSuggestWithCustomOrderId() {
/**
* Test the `mapping` method with the ES 7 mapping
*
- * @group WooCommerceOrdersAutosuggest
+ * @group woocommerce
+ * @group woocommerce-orders-autosuggest
*/
public function testMappingEs7() {
$original_mapping = [
@@ -192,7 +196,8 @@ public function testMappingEs7() {
/**
* Test the `mapping` method with the ES 5 mapping
*
- * @group WooCommerceOrdersAutosuggest
+ * @group woocommerce
+ * @group woocommerce-orders-autosuggest
*/
public function testMappingEs5() {
$change_es_version = function() {
@@ -247,7 +252,8 @@ public function testMappingEs5() {
/**
* Test the `set_search_fields` method
*
- * @group WooCommerceOrdersAutosuggest
+ * @group woocommerce
+ * @group woocommerce-orders-autosuggest
*/
public function testSetSearchFields() {
$original_search_fields = [ 'old_search_field' ];
diff --git a/tests/php/features/WooCommerce/TestWooCommerceProduct.php b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
index 4cf1d66713..bb1fda8696 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceProduct.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
@@ -14,6 +14,24 @@
* WC products test class
*/
class TestWooCommerceProduct extends TestWooCommerce {
+ /**
+ * Products instance
+ *
+ * @var Products
+ */
+ protected $products;
+
+ /**
+ * Setup each test.
+ *
+ * @group woocommerce
+ * @group woocommerce-orders
+ */
+ public function set_up() {
+ parent::set_up();
+ $this->products = ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->products;
+ }
+
/**
* Test products post type query does not get integrated when the feature is active
*
@@ -889,4 +907,118 @@ public function decayingDisabledOnProductsProvider() : array {
],
];
}
+
+ /**
+ * Test the `get_supported_post_types` method
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testGetSupportedPostTypes() {
+ $query = new \WP_Query( [] );
+
+ $default_supported = $this->products->get_supported_post_types( $query );
+ $this->assertSame( $default_supported, [] );
+
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $default_supported = $this->products->get_supported_post_types( $query );
+ $this->assertSame( $default_supported, [ 'product_variation' ] );
+
+ /**
+ * Test the `ep_woocommerce_products_supported_post_types` filter
+ */
+ $add_post_type = function( $post_types, $filter_query ) use ( $query ) {
+ $this->assertSame( $filter_query, $query );
+ $post_types[] = 'post';
+ return $post_types;
+ };
+ add_filter( 'ep_woocommerce_products_supported_post_types', $add_post_type, 10, 2 );
+
+ $custom_supported = $this->products->get_supported_post_types( $query );
+ $this->assertSame( $custom_supported, [ 'product_variation', 'post' ] );
+
+ $this->markTestIncomplete( 'This test should also test the addition of the `product` post type under some circumstances.' );
+ }
+
+ /**
+ * Test the `get_supported_taxonomies` method
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testGetSupportedTaxonomies() {
+ $default_supported = $this->products->get_supported_taxonomies();
+ $expected = [
+ 'product_cat',
+ 'product_tag',
+ 'product_type',
+ 'product_visibility',
+ 'product_shipping_class',
+ ];
+ $this->assertSame( $default_supported, $expected );
+
+ /**
+ * Test the `ep_woocommerce_products_supported_taxonomies` filter
+ */
+ $add_taxonomy = function( $taxonomies ) {
+ $taxonomies[] = 'custom_category';
+ return $taxonomies;
+ };
+ add_filter( 'ep_woocommerce_products_supported_taxonomies', $add_taxonomy );
+
+ $custom_supported = $this->products->get_supported_taxonomies();
+ $this->assertSame( $custom_supported, array_merge( $expected, [ 'custom_category' ] ) );
+ }
+
+ /**
+ * Test the `get_orderby_meta_mapping` method
+ *
+ * @dataProvider orderbyMetaMappingDataProvider
+ * @group woocommerce
+ * @group woocommerce-products
+ *
+ * @param string $meta_key Original meta key value
+ * @param string $translated Expected translated version
+ */
+ public function testOrderbyMetaMapping( $meta_key, $translated ) {
+ $this->assertSame( $this->products->get_orderby_meta_mapping( $meta_key ), $translated );
+ }
+
+ /**
+ * Data provider for the testOrderbyMetaMapping method.
+ *
+ * @return array
+ */
+ public function orderbyMetaMappingDataProvider() {
+ return [
+ [ 'ID', 'ID' ],
+ [ 'title', 'title date' ],
+ [ 'menu_order', 'menu_order title date' ],
+ [ 'menu_order title', 'menu_order title date' ],
+ [ 'total_sales', 'meta.total_sales.double date' ],
+ [ '_wc_average_rating', 'meta._wc_average_rating.double date' ],
+ [ '_price', 'meta._price.double date' ],
+ [ '_sku', 'meta._sku.value.sortable date' ],
+ [ 'custom_parameter', 'date' ],
+ ];
+ }
+
+ /**
+ * Test the `orderby_meta_mapping` filter
+ *
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testOrderbyMetaMappingFilter() {
+ $add_value = function ( $mapping ) {
+ $mapping['custom_parameter'] = 'meta.custom_parameter.long';
+ return $mapping;
+ };
+ add_filter( 'orderby_meta_mapping', $add_value );
+
+ $this->assertSame( $this->products->get_orderby_meta_mapping( 'custom_parameter' ), 'meta.custom_parameter.long' );
+ }
}
From f99bbc3e54b63eb8afe573dcba3bf4efdc6d1f95 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Fri, 23 Jun 2023 11:24:15 -0300
Subject: [PATCH 12/45] Fix and expand the orderby clause with meta fields
---
includes/classes/Indexable/Post/Post.php | 57 ++++++++++++++++++------
tests/php/indexables/TestPost.php | 49 ++++++++++++++++++++
2 files changed, 92 insertions(+), 14 deletions(-)
diff --git a/includes/classes/Indexable/Post/Post.php b/includes/classes/Indexable/Post/Post.php
index e3116739fa..5ca85caba2 100644
--- a/includes/classes/Indexable/Post/Post.php
+++ b/includes/classes/Indexable/Post/Post.php
@@ -1290,6 +1290,12 @@ protected function parse_orderby_meta_fields( $orderby_clause, $args ) {
'unsigned' => 'long',
];
+ // Code is targeting Elasticsearch directly
+ if ( preg_match( '/^meta\.(.*?)\.(.*)/', $orderby_clause, $match_meta ) ) {
+ return $orderby_clause;
+ }
+
+ // WordPress meta_value_* compatibility
if ( preg_match( '/^meta_value_?(.*)/', $orderby_clause, $match_type ) ) {
$meta_type = $from_to_metatypes[ strtolower( $match_type[1] ) ] ?? 'value.sortable';
}
@@ -1298,25 +1304,48 @@ protected function parse_orderby_meta_fields( $orderby_clause, $args ) {
$meta_field = $args['meta_key'];
}
- if ( ( ! isset( $meta_type ) || ! isset( $meta_field ) ) && ! empty( $args['meta_query'] ) ) {
- $meta_query = new \WP_Meta_Query( $args['meta_query'] );
- // Calling get_sql() to populate the WP_Meta_Query->clauses attribute
- $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
+ // Already have everything needed
+ if ( isset( $meta_type ) && isset( $meta_field ) ) {
+ return "meta.{$meta_field}.{$meta_type}";
+ }
+
+ // Don't have any other ways to guess
+ if ( empty( $args['meta_query'] ) ) {
+ return $orderby_clause;
+ }
+
+ $meta_query = new \WP_Meta_Query( $args['meta_query'] );
+ // Calling get_sql() to populate the WP_Meta_Query->clauses attribute
+ $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
- $clauses = $meta_query->get_clauses();
+ $clauses = $meta_query->get_clauses();
- if ( ! empty( $clauses[ $orderby_clause ] ) ) {
- $meta_field = $clauses[ $orderby_clause ]['key'];
- $clause_meta_type = strtolower( $clauses[ $orderby_clause ]['type'] ?? $clauses[ $orderby_clause ]['cast'] );
+ // If it refers to a named meta_query clause
+ if ( ! empty( $clauses[ $orderby_clause ] ) ) {
+ $meta_field = $clauses[ $orderby_clause ]['key'];
+ $clause_meta_type = strtolower( $clauses[ $orderby_clause ]['type'] ?? $clauses[ $orderby_clause ]['cast'] );
+ } else {
+ /**
+ * At this point we:
+ * 1. Try to find the meta key in any meta_query clause and use the type WP found
+ * 2. If ordering by `meta_value*`, use the first meta_query clause
+ * 3. Give up and use the orderby clause as is (code could be capturing it later on)
+ */
+ $meta_keys_and_types = wp_list_pluck( $clauses, 'cast', 'key' );
+ if ( isset( $meta_keys_and_types[ $orderby_clause ] ) ) {
+ $meta_field = $orderby_clause;
+ $clause_meta_type = strtolower( $meta_keys_and_types[ $orderby_clause ] ?? $meta_keys_and_types[ $orderby_clause ] );
+ } elseif ( isset( $meta_type ) ) {
+ $primary_clause = reset( $clauses );
+ $meta_field = $primary_clause['key'];
} else {
- $primary_clause = reset( $clauses );
- $meta_field = $primary_clause['key'];
- $clause_meta_type = strtolower( $primary_clause['type'] ?? $primary_clause['cast'] );
+ unset( $meta_type );
+ unset( $meta_field );
}
+ }
- if ( ! isset( $meta_type ) ) {
- $meta_type = $from_to_metatypes[ $clause_meta_type ] ?? 'value.sortable';
- }
+ if ( ! isset( $meta_type ) && isset( $clause_meta_type ) ) {
+ $meta_type = $from_to_metatypes[ $clause_meta_type ] ?? 'value.sortable';
}
if ( isset( $meta_type ) && isset( $meta_field ) ) {
diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php
index dabc9c83f6..11be1723ba 100644
--- a/tests/php/indexables/TestPost.php
+++ b/tests/php/indexables/TestPost.php
@@ -6867,6 +6867,55 @@ public function testParseOrderbyMetaQueryTypes( $meta_value_type, $es_type ) {
$this->assertGreaterThanOrEqual( 1, did_filter( 'ep_formatted_args' ) );
}
+ /**
+ * Test the parse_orderby_meta_fields() method when dealing with multiple meta fields
+ *
+ * @see https://github.com/10up/ElasticPress/issues/3509
+ * @since 4.6.1
+ * @group post
+ */
+ public function testParseOrderbyMetaMultiple() {
+ $method_executed = false;
+
+ $query_args = [
+ 'ep_integrate' => true,
+ 'orderby' => [
+ 'meta_field1' => 'desc',
+ 'meta.meta_field3.double' => 'asc',
+ 'meta.meta_field2.double' => 'asc',
+ ],
+ 'meta_query' => [
+ 'date_clause' => [
+ 'key' => 'meta_field2',
+ 'value' => '20230622',
+ 'compare' => '>=',
+ ],
+ ],
+ ];
+
+ $assert_callback = function( $args ) use ( &$method_executed ) {
+ $method_executed = true;
+
+ $expected_sort = [
+ [ 'meta_field1' => [ 'order' => 'desc' ] ],
+ [ 'meta.meta_field3.double' => [ 'order' => 'asc' ] ],
+ [ 'meta.meta_field2.double' => [ 'order' => 'asc' ] ],
+ ];
+
+ $this->assertSame( $expected_sort, $args['sort'] );
+
+ return $args;
+ };
+
+ // Run the tests.
+ add_filter( 'ep_formatted_args', $assert_callback );
+ $query = new \WP_Query( $query_args );
+ remove_filter( 'ep_formatted_args', $assert_callback );
+
+ $this->assertTrue( $method_executed );
+ $this->assertGreaterThanOrEqual( 1, did_filter( 'ep_formatted_args' ) );
+ }
+
/**
* Tests additional nested tax queries in parse_tax_query().
*
From e4160a45e75124b3db78af9b030c229d29be5e56 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Fri, 23 Jun 2023 14:18:36 -0300
Subject: [PATCH 13/45] Refactor + comments for maybe_disable_decaying
---
includes/classes/Feature/WooCommerce/Products.php | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/includes/classes/Feature/WooCommerce/Products.php b/includes/classes/Feature/WooCommerce/Products.php
index ce38098bf1..6bcc7e5428 100644
--- a/includes/classes/Feature/WooCommerce/Products.php
+++ b/includes/classes/Feature/WooCommerce/Products.php
@@ -564,24 +564,23 @@ public function add_weight_settings_search( $settings ) {
* @return bool
*/
public function maybe_disable_decaying( $is_decaying_enabled, $settings, $args ) {
+ // If the decay setting isn't a WooCommerce related option, return
if ( ! in_array( $settings['decaying_enabled'], [ 'disabled_only_products', 'disabled_includes_products' ], true ) ) {
return $is_decaying_enabled;
}
+ // If the query is not dealing with products, return
if ( ! isset( $args['post_type'] ) || ! in_array( 'product', (array) $args['post_type'], true ) ) {
return $is_decaying_enabled;
}
$post_types = (array) $args['post_type'];
+ // If set to disable decay on product-only queries and have more than one post type, return
if ( 'disabled_only_products' === $settings['decaying_enabled'] && count( $post_types ) > 1 ) {
return $is_decaying_enabled;
}
- if ( 'disabled_includes_products' === $settings['decaying_enabled'] && ! in_array( 'product', $post_types, true ) ) {
- return $is_decaying_enabled;
- }
-
return false;
}
From 3782903458b83e74431f4158bb0ebff610984c75 Mon Sep 17 00:00:00 2001
From: Martin Widmann
Date: Tue, 27 Jun 2023 08:51:32 +0200
Subject: [PATCH 14/45] Update Orders.php
Setting visibility of `$index` to protected as requested by @felipeelia .
---
includes/classes/Feature/WooCommerce/Orders.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php
index 666f696939..81a53a1284 100644
--- a/includes/classes/Feature/WooCommerce/Orders.php
+++ b/includes/classes/Feature/WooCommerce/Orders.php
@@ -25,7 +25,7 @@ class Orders {
*
* @var string
*/
- private $index;
+ protected $index;
/**
* Initialize feature.
From b1c266412d6f8a4247b38b3764e81e6e96ff847a Mon Sep 17 00:00:00 2001
From: "mohammed.razzaq"
Date: Tue, 27 Jun 2023 18:20:58 +0530
Subject: [PATCH 15/45] Exclude meta with empty post meta keys
---
includes/classes/Indexable/Post/Post.php | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/includes/classes/Indexable/Post/Post.php b/includes/classes/Indexable/Post/Post.php
index e3116739fa..6bdf3e7b33 100644
--- a/includes/classes/Indexable/Post/Post.php
+++ b/includes/classes/Indexable/Post/Post.php
@@ -936,7 +936,9 @@ public function prepare_meta( $post ) {
$prepared_meta = [];
foreach ( $filtered_metas as $key => $value ) {
- $prepared_meta[ $key ] = maybe_unserialize( $value );
+ if ( ! empty( $key ) ) {
+ $prepared_meta[ $key ] = maybe_unserialize( $value );
+ }
}
/**
From c493265aee4c15a482e57e94c45618b132bbffe1 Mon Sep 17 00:00:00 2001
From: "mohammed.razzaq"
Date: Thu, 29 Jun 2023 14:43:56 +0530
Subject: [PATCH 16/45] Add Test
---
tests/php/indexables/TestPost.php | 39 +++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php
index dabc9c83f6..007446fe7a 100644
--- a/tests/php/indexables/TestPost.php
+++ b/tests/php/indexables/TestPost.php
@@ -3886,6 +3886,45 @@ public function filter_ep_prepare_meta_excluded_public_keys( $meta_keys ) {
}
+ /**
+ * Test to verify that empty meta key should be excluded before sync.
+ *
+ * @since 4.7.0
+ * @group post
+ */
+ public function testEmptyMetaKey() {
+ global $wpdb;
+ $post_id = $this->ep_factory->post->create();
+ $post = get_post( $post_id );
+ $meta_key = '';
+ $meta_value_1 = 'Meta value for empty key';
+ $meta_values = array(
+ 'value 1',
+ 'value 2',
+ );
+ add_post_meta( $post_id, 'test_meta_1', $meta_values );
+
+ $wpdb->insert(
+ $wpdb->postmeta,
+ array(
+ 'post_id' => $post_id,
+ 'meta_key' => $meta_key,
+ 'meta_value' => $meta_value_1,
+ ),
+ array( '%d', '%s', '%s' )
+ );
+ $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE meta_key=%s AND post_id = %d", $meta_key, $post_id ) );
+
+ $this->assertSame( $meta_key, $row->meta_key );
+ $this->assertSame( $meta_value_1, $row->meta_value );
+
+ $meta_data = ElasticPress\Indexables::factory()->get( 'post' )->prepare_meta( $post );
+
+ $this->assertTrue( is_array( $meta_data ) && 1 === count( $meta_data ) );
+ $this->assertTrue( is_array( $meta_data ) && array_key_exists( 'test_meta_1', $meta_data ) );
+
+ }
+
/**
* Test meta preparation
*
From 9cad2407c7233478743017fcb8e5efd8d953a486 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Thu, 29 Jun 2023 17:35:20 -0300
Subject: [PATCH 17/45] Fix linting
---
includes/classes/Feature/WooCommerce/Orders.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php
index 81a53a1284..9fb7ba10ff 100644
--- a/includes/classes/Feature/WooCommerce/Orders.php
+++ b/includes/classes/Feature/WooCommerce/Orders.php
@@ -26,7 +26,7 @@ class Orders {
* @var string
*/
protected $index;
-
+
/**
* Initialize feature.
*
From ce3a9fe52c32037a8657e16931fcaea1c0d0ef26 Mon Sep 17 00:00:00 2001
From: "mohammed.razzaq"
Date: Fri, 30 Jun 2023 14:31:56 +0530
Subject: [PATCH 18/45] Avoid empty meta fields
---
includes/classes/Feature/Search/Search.php | 7 ++++--
.../Feature/SearchOrdering/SearchOrdering.php | 6 ++++-
tests/php/indexables/TestPost.php | 22 +++++++++++++++++++
3 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/includes/classes/Feature/Search/Search.php b/includes/classes/Feature/Search/Search.php
index 736d71303f..74ace30779 100644
--- a/includes/classes/Feature/Search/Search.php
+++ b/includes/classes/Feature/Search/Search.php
@@ -810,9 +810,12 @@ public function save_exclude_from_search_meta( $post_id, $post ) {
return;
}
- $exclude_from_search = isset( $_POST['ep_exclude_from_search'] ) ? true : false;
+ if ( isset( $_POST['ep_exclude_from_search'] ) ) {
+ update_post_meta( $post_id, 'ep_exclude_from_search', true );
+ } else {
+ delete_post_meta( $post_id, 'ep_exclude_from_search' );
+ }
- update_post_meta( $post_id, 'ep_exclude_from_search', $exclude_from_search );
}
/**
diff --git a/includes/classes/Feature/SearchOrdering/SearchOrdering.php b/includes/classes/Feature/SearchOrdering/SearchOrdering.php
index 33529ed924..a8268b1079 100644
--- a/includes/classes/Feature/SearchOrdering/SearchOrdering.php
+++ b/includes/classes/Feature/SearchOrdering/SearchOrdering.php
@@ -533,8 +533,12 @@ public function save_post( $post_id, $post ) {
}
}
- update_post_meta( $post_id, 'pointers', $final_order_data );
update_post_meta( $post_id, 'search_term', $post->post_title );
+ if ( ! empty( $final_order_data ) ) {
+ update_post_meta( $post_id, 'pointers', $final_order_data );
+ } else {
+ delete_post_meta( $post_id, 'pointers' );
+ }
}
/**
diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php
index dabc9c83f6..14c85702bf 100644
--- a/tests/php/indexables/TestPost.php
+++ b/tests/php/indexables/TestPost.php
@@ -8023,6 +8023,28 @@ public function testExcludeFromSearchQuery() {
$this->assertEquals( 2, $query->post_count );
}
+ /**
+ * Tests that post meta value should be empty when it is not set.
+ */
+ public function testMetaValueNotSet() {
+ $post_ids = array();
+ $post_ids[0] = $this->ep_factory->post->create(
+ array(
+ 'post_content' => 'find me in search',
+ )
+ );
+ $post_ids[1] = $this->ep_factory->post->create(
+ array(
+ 'post_content' => 'exlcude from search',
+ 'meta_input' => array( 'ep_exclude_from_search' => true ),
+ )
+ );
+
+ $this->assertEmpty( get_post_meta( $post_ids[0], 'ep_exclude_from_search', true ) );
+ $this->assertEquals( 1, get_post_meta( $post_ids[1], 'ep_exclude_from_search', true ) );
+
+ }
+
/**
* Tests search term is wrapped in html tag with custom class
*/
From 2906ead23667bb10f1510d6a4e0152fb5a511ef9 Mon Sep 17 00:00:00 2001
From: Burhan Nasir
Date: Mon, 3 Jul 2023 19:57:58 +0500
Subject: [PATCH 19/45] Fix: Don't apply facet filter to query if filter value
is empty
---
includes/classes/Feature/Facets/Facets.php | 3 +++
tests/php/features/TestFacet.php | 6 ++++++
2 files changed, 9 insertions(+)
diff --git a/includes/classes/Feature/Facets/Facets.php b/includes/classes/Feature/Facets/Facets.php
index 58760a3198..48e6c24a98 100644
--- a/includes/classes/Feature/Facets/Facets.php
+++ b/includes/classes/Feature/Facets/Facets.php
@@ -401,6 +401,9 @@ public function get_selected() {
foreach ( $filter_names as $filter_name => $type_obj ) {
if ( 0 === strpos( $key, $filter_name ) ) {
+ if ( empty( $value ) ) {
+ continue;
+ }
$facet = str_replace( $filter_name, '', $key );
$filters = $type_obj->format_selected( $facet, $value, $filters );
diff --git a/tests/php/features/TestFacet.php b/tests/php/features/TestFacet.php
index bcf9551424..57531fedb5 100644
--- a/tests/php/features/TestFacet.php
+++ b/tests/php/features/TestFacet.php
@@ -105,6 +105,12 @@ public function testGetSelected() {
$this->assertSelectedTax( array( $term->slug => true ), 'taxonomy', $selected );
$this->assertArrayHasKey( 'post_type', $selected );
$this->assertSame( 'posttype', $selected['post_type'] );
+
+ // test when filter is empty
+ parse_str( 'ep_filter_category=&ep_filter_othertax=amet&s=', $_GET );
+ $selected = $facet_feature->get_selected();
+ $this->assertArrayNotHasKey( 'category', $selected['taxonomies'] );
+ $this->assertArrayHasKey( 's', $selected );
}
/**
From 7f0cfd4884336b2ec6e30c7de836d209d22672eb Mon Sep 17 00:00:00 2001
From: Burhan Nasir
Date: Mon, 3 Jul 2023 20:04:39 +0500
Subject: [PATCH 20/45] Minor change
---
tests/php/features/TestFacet.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/php/features/TestFacet.php b/tests/php/features/TestFacet.php
index 57531fedb5..381ca387a7 100644
--- a/tests/php/features/TestFacet.php
+++ b/tests/php/features/TestFacet.php
@@ -106,7 +106,7 @@ public function testGetSelected() {
$this->assertArrayHasKey( 'post_type', $selected );
$this->assertSame( 'posttype', $selected['post_type'] );
- // test when filter is empty
+ // test when filter value is empty.
parse_str( 'ep_filter_category=&ep_filter_othertax=amet&s=', $_GET );
$selected = $facet_feature->get_selected();
$this->assertArrayNotHasKey( 'category', $selected['taxonomies'] );
From d8ad1aeee05b0c225365ef60801e4ff02d424bfc Mon Sep 17 00:00:00 2001
From: Burhan Nasir
Date: Mon, 3 Jul 2023 20:30:22 +0500
Subject: [PATCH 21/45] Fix: Documentation for ep_facet_search_get_terms_args
filter
---
.../classes/Feature/Facets/Types/Taxonomy/Renderer.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/includes/classes/Feature/Facets/Types/Taxonomy/Renderer.php b/includes/classes/Feature/Facets/Types/Taxonomy/Renderer.php
index 97f50cf0bd..7a6e32304d 100644
--- a/includes/classes/Feature/Facets/Types/Taxonomy/Renderer.php
+++ b/includes/classes/Feature/Facets/Types/Taxonomy/Renderer.php
@@ -95,10 +95,10 @@ public function render( $args, $instance ) {
*
* @since 3.5.0
* @hook ep_facet_search_get_terms_args
- * @param {array} $query Weighting query
- * @param {string} $post_type Post type
- * @param {array} $args WP Query arguments
- * @return {array} New query
+ * @param {array} $terms_args Array of arguments passed to get_terms()
+ * @param {array} $args Widget args
+ * @param {array} $instance Instance settings
+ * @return {array} New terms args
*/
apply_filters(
'ep_facet_search_get_terms_args',
From 640070f0b41896351684232c2167eb0e80a45098 Mon Sep 17 00:00:00 2001
From: "mohammed.razzaq"
Date: Tue, 4 Jul 2023 18:48:00 +0530
Subject: [PATCH 22/45] Revert the code for searchOrdering
---
includes/classes/Feature/SearchOrdering/SearchOrdering.php | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/includes/classes/Feature/SearchOrdering/SearchOrdering.php b/includes/classes/Feature/SearchOrdering/SearchOrdering.php
index a8268b1079..33529ed924 100644
--- a/includes/classes/Feature/SearchOrdering/SearchOrdering.php
+++ b/includes/classes/Feature/SearchOrdering/SearchOrdering.php
@@ -533,12 +533,8 @@ public function save_post( $post_id, $post ) {
}
}
+ update_post_meta( $post_id, 'pointers', $final_order_data );
update_post_meta( $post_id, 'search_term', $post->post_title );
- if ( ! empty( $final_order_data ) ) {
- update_post_meta( $post_id, 'pointers', $final_order_data );
- } else {
- delete_post_meta( $post_id, 'pointers' );
- }
}
/**
From d67374e9373696b519a87754bd088262b8e1076e Mon Sep 17 00:00:00 2001
From: "mohammed.razzaq"
Date: Tue, 4 Jul 2023 19:06:53 +0530
Subject: [PATCH 23/45] Improve tests
---
tests/php/indexables/TestPost.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php
index 007446fe7a..30e8443914 100644
--- a/tests/php/indexables/TestPost.php
+++ b/tests/php/indexables/TestPost.php
@@ -3920,9 +3920,9 @@ public function testEmptyMetaKey() {
$meta_data = ElasticPress\Indexables::factory()->get( 'post' )->prepare_meta( $post );
- $this->assertTrue( is_array( $meta_data ) && 1 === count( $meta_data ) );
- $this->assertTrue( is_array( $meta_data ) && array_key_exists( 'test_meta_1', $meta_data ) );
-
+ $this->assertIsArray( $meta_data );
+ $this->assertCount( 1, $meta_data );
+ $this->assertArrayHasKey( 'test_meta_1', $meta_data );
}
/**
From c2d817e1e657d63f252f2399272fe5f10e6e8dd2 Mon Sep 17 00:00:00 2001
From: "mohammed.razzaq"
Date: Tue, 4 Jul 2023 19:33:32 +0530
Subject: [PATCH 24/45] Remove empty space
---
tests/php/indexables/TestPost.php | 1 -
1 file changed, 1 deletion(-)
diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php
index 14c85702bf..69f836df42 100644
--- a/tests/php/indexables/TestPost.php
+++ b/tests/php/indexables/TestPost.php
@@ -8042,7 +8042,6 @@ public function testMetaValueNotSet() {
$this->assertEmpty( get_post_meta( $post_ids[0], 'ep_exclude_from_search', true ) );
$this->assertEquals( 1, get_post_meta( $post_ids[1], 'ep_exclude_from_search', true ) );
-
}
/**
From 142ebc8ec032f36f1b3b3a82d76d4befc0b18be3 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Tue, 4 Jul 2023 11:34:14 -0300
Subject: [PATCH 25/45] docblocks of tests
---
tests/php/indexables/TestPost.php | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php
index 23a3129a6d..566fb959de 100644
--- a/tests/php/indexables/TestPost.php
+++ b/tests/php/indexables/TestPost.php
@@ -3889,7 +3889,7 @@ public function filter_ep_prepare_meta_excluded_public_keys( $meta_keys ) {
/**
* Test to verify that empty meta key should be excluded before sync.
*
- * @since 4.7.0
+ * @since 4.6.1
* @group post
*/
public function testEmptyMetaKey() {
@@ -8113,6 +8113,9 @@ public function testExcludeFromSearchQuery() {
/**
* Tests that post meta value should be empty when it is not set.
+ *
+ * @since 4.6.1
+ * @group post
*/
public function testMetaValueNotSet() {
$post_ids = array();
From 4b7c9a1e5a9be10bc5b4ff1cb8b2746b3b38e55a Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Tue, 4 Jul 2023 11:52:29 -0300
Subject: [PATCH 26/45] Version bump
---
elasticpress.php | 4 ++--
package-lock.json | 4 ++--
package.json | 2 +-
readme.txt | 2 +-
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/elasticpress.php b/elasticpress.php
index 0b90abe5dc..afd0d1335b 100644
--- a/elasticpress.php
+++ b/elasticpress.php
@@ -3,7 +3,7 @@
* Plugin Name: ElasticPress
* Plugin URI: https://github.com/10up/ElasticPress
* Description: A fast and flexible search and query engine for WordPress.
- * Version: 4.6.0
+ * Version: 4.6.1
* Requires at least: 5.6
* Requires PHP: 7.0
* Author: 10up
@@ -32,7 +32,7 @@
define( 'EP_URL', plugin_dir_url( __FILE__ ) );
define( 'EP_PATH', plugin_dir_path( __FILE__ ) );
define( 'EP_FILE', plugin_basename( __FILE__ ) );
-define( 'EP_VERSION', '4.6.0' );
+define( 'EP_VERSION', '4.6.1' );
/**
* PSR-4-ish autoloading
diff --git a/package-lock.json b/package-lock.json
index 9f7af18fc5..68998ce0ce 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "elasticpress",
- "version": "4.6.0",
+ "version": "4.6.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "elasticpress",
- "version": "4.6.0",
+ "version": "4.6.1",
"license": "GPL-2.0-or-later",
"dependencies": {
"@10up/component-tooltip": "^2.0.0",
diff --git a/package.json b/package.json
index 67672da575..059e2f5778 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "elasticpress",
- "version": "4.6.0",
+ "version": "4.6.1",
"license": "GPL-2.0-or-later",
"description": "A fast and flexible search and query engine for WordPress.",
"devDependencies": {
diff --git a/readme.txt b/readme.txt
index 0cc59bf530..8b0e487388 100644
--- a/readme.txt
+++ b/readme.txt
@@ -2,7 +2,7 @@
Contributors: 10up, tlovett1, vhauri, tott, oscarssanchez, cmmarslender
Tags: performance, slow, search, elasticsearch, fuzzy, facet, aggregation, searching, autosuggest, suggest, elastic, advanced search, woocommerce, related posts, woocommerce
Tested up to: 6.2
-Stable tag: 4.6.0
+Stable tag: 4.6.1
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
From 03a24609f5b94ed8229f5fba9d29681f9228b6e6 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Tue, 4 Jul 2023 12:45:38 -0300
Subject: [PATCH 27/45] Changelog for 4.6.1
---
CHANGELOG.md | 16 ++++++++++++++++
CREDITS.md | 1 +
2 files changed, 17 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 51d3dd5f65..60faa09b5c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,21 @@ All notable changes to this project will be documented in this file, per [the Ke
### Security
-->
+## [4.6.1] - 2023-07-XX
+
+**Note that starting from the ElasticPress 5.0.0 release the `Users` feature will be moved to the [ElasticPress Labs](https://github.com/10up/ElasticPressLabs) plugin. The `Terms` and `Comments` features will remain in ElasticPress but will be available only if enabled via code. Check [our blog post](https://www.elasticpress.io/blog/2023/03/enabling-comments-and-terms-in-elasticpress-5-0) for more info.**
+
+### Changed
+* Use wp_cache_supports over wp_cache_supports_group_flush. Props [@spacedmonkey](https://github.com/spacedmonkey) via [#3501](https://github.com/10up/ElasticPress/pull/3501).
+* Update the `ep_exclude_from_search` post meta only if it is set or has some value. Props [@MARQAS](https://github.com/MARQAS) and [@columbian-chris](https://github.com/columbian-chris) via [#3521](https://github.com/10up/ElasticPress/pull/3521).
+
+### Fixed
+* Deprecation notice in `ElasticPress\Feature\WooCommerce\Orders`. Props [@mwidmann](https://github.com/mwidmann) via [#3507](https://github.com/10up/ElasticPress/pull/3507).
+* Restrict applying a facet filter to the query if the filter value is empty. Props [@burhandodhy](https://github.com/burhandodhy) via [#3524](https://github.com/10up/ElasticPress/pull/3524).
+* Syncing a post with empty post meta key. Props [@MARQAS](https://github.com/MARQAS) and [@oscarssanchez](https://github.com/oscarssanchez) via [#3516](https://github.com/10up/ElasticPress/pull/3516).
+* Order by clauses with Elasticsearch field formats are not changed anymore. Props [@felipeelia](https://github.com/felipeelia) and [@tlovett1](https://github.com/tlovett1) via [#3512](https://github.com/10up/ElasticPress/pull/3512).
+* Documentation of the `ep_facet_search_get_terms_args` filter. Props [@burhandodhy](https://github.com/burhandodhy) via [#3525](https://github.com/10up/ElasticPress/pull/3525).
+
## [4.6.0] - 2023-06-13
**Note that starting from the ElasticPress 5.0.0 release the `Users` feature will be moved to the [ElasticPress Labs](https://github.com/10up/ElasticPressLabs) plugin. The `Terms` and `Comments` features will remain in ElasticPress but will be available only if enabled via code. Check [our blog post](https://www.elasticpress.io/blog/2023/03/enabling-comments-and-terms-in-elasticpress-5-0) for more info.**
@@ -1831,6 +1846,7 @@ This is a bug fix release with some filter additions.
- Initial plugin release
[Unreleased]: https://github.com/10up/ElasticPress/compare/trunk...develop
+[4.6.1]: https://github.com/10up/ElasticPress/compare/4.6.0...4.6.1
[4.6.0]: https://github.com/10up/ElasticPress/compare/4.5.2...4.6.0
[4.5.2]: https://github.com/10up/ElasticPress/compare/4.5.1...4.5.2
[4.5.1]: https://github.com/10up/ElasticPress/compare/4.5.0...4.5.1
diff --git a/CREDITS.md b/CREDITS.md
index d0bb519dc3..96d60613a2 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -213,6 +213,7 @@ Thank you to all the people who have already contributed to this repository via
[Alexander Z (@TorlockC)](https://github.com/TorlockC),
[Nabi (@navidabdi)](https://github.com/navidabdi)
[Dmitry Seleznyov (@selim13)](https://github.com/selim13)
+[Martin Widmann (@mwidmann)](https://github.com/mwidmann)
and
[@qazaqstan2025](https://github.com/qazaqstan2025).
From 2ea5ac7db938f652585b0087c489081766460a24 Mon Sep 17 00:00:00 2001
From: Burhan Nasir
Date: Wed, 5 Jul 2023 11:14:17 +0500
Subject: [PATCH 28/45] Fix warning on the health page
---
includes/classes/Screen/HealthInfo.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/includes/classes/Screen/HealthInfo.php b/includes/classes/Screen/HealthInfo.php
index ca4a6ac1dd..166799dc7a 100644
--- a/includes/classes/Screen/HealthInfo.php
+++ b/includes/classes/Screen/HealthInfo.php
@@ -37,7 +37,7 @@ public function last_sync_health_info( $debug_info ) {
$debug_info['ep-last-sync'] = [
'label' => esc_html__( 'ElasticPress - Last Sync', 'elasticpress' ),
- 'fields' => $first_group['fields'],
+ 'fields' => $first_group['fields'] ?? [],
];
return $debug_info;
From a872f6c73bad82bab0b939f951e7118d28d81b02 Mon Sep 17 00:00:00 2001
From: Burhan Nasir
Date: Wed, 5 Jul 2023 16:41:09 +0500
Subject: [PATCH 29/45] Add Did You Mean Doc url
---
includes/classes/Feature/DidYouMean/DidYouMean.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/includes/classes/Feature/DidYouMean/DidYouMean.php b/includes/classes/Feature/DidYouMean/DidYouMean.php
index 86f4f9b154..00228d1c19 100644
--- a/includes/classes/Feature/DidYouMean/DidYouMean.php
+++ b/includes/classes/Feature/DidYouMean/DidYouMean.php
@@ -25,6 +25,8 @@ public function __construct() {
$this->summary = __( 'Recommend alternative search terms for misspelled queries or terms with no results.', 'elasticpress' );
+ $this->docs_url = __( 'https://elasticpress.zendesk.com/hc/en-us/articles/16673223107085-Did-You-Mean', 'elasticpress' );
+
$this->requires_install_reindex = true;
$this->available_during_installation = true;
From 5d2179d775dac4946e2ffcfeb0c0fc55e6ffe767 Mon Sep 17 00:00:00 2001
From: Burhan Nasir
Date: Wed, 5 Jul 2023 16:53:45 +0500
Subject: [PATCH 30/45] Fix: PHP Deprecated: json_encode(): Passing null to
parameter #2
---
includes/classes/Command.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/includes/classes/Command.php b/includes/classes/Command.php
index 784e8083d2..823cf82849 100644
--- a/includes/classes/Command.php
+++ b/includes/classes/Command.php
@@ -1516,7 +1516,7 @@ protected function print_json_response( $response, $pretty ) {
* @param boolean $pretty_print_flag Whether it should or not be formatted.
*/
protected function pretty_json_encode( $json_obj, $pretty_print_flag ) {
- $flag = $pretty_print_flag ? JSON_PRETTY_PRINT : null;
+ $flag = $pretty_print_flag ? JSON_PRETTY_PRINT : 0;
WP_CLI::line( wp_json_encode( $json_obj, $flag ) );
}
From 8fcf6fe05278d98dc6969cf4c1ff6384b5fa0b0e Mon Sep 17 00:00:00 2001
From: Burhan Nasir
Date: Wed, 5 Jul 2023 18:09:49 +0500
Subject: [PATCH 31/45] Fix: Failed Query logs automatically cleared on page
refresh
---
includes/classes/StatusReport/FailedQueries.php | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/includes/classes/StatusReport/FailedQueries.php b/includes/classes/StatusReport/FailedQueries.php
index f118918592..9206dfb32d 100644
--- a/includes/classes/StatusReport/FailedQueries.php
+++ b/includes/classes/StatusReport/FailedQueries.php
@@ -137,6 +137,15 @@ protected function maybe_clear_logs() {
}
$this->query_logger->clear_logs();
+
+ if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
+ $redirect_url = network_admin_url( 'admin.php?page=elasticpress-status-report' );
+ } else {
+ $redirect_url = admin_url( 'admin.php?page=elasticpress-status-report' );
+ }
+
+ wp_safe_redirect( $redirect_url );
+ exit();
}
/**
From 73d843e6abb0ffc57b948d837823d3fc6b3e9424 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Wed, 5 Jul 2023 11:19:23 -0300
Subject: [PATCH 32/45] Add #3529, #3533, #3530, and #3532 to the changelog
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 60faa09b5c..b6b3cd8480 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,9 @@ All notable changes to this project will be documented in this file, per [the Ke
**Note that starting from the ElasticPress 5.0.0 release the `Users` feature will be moved to the [ElasticPress Labs](https://github.com/10up/ElasticPressLabs) plugin. The `Terms` and `Comments` features will remain in ElasticPress but will be available only if enabled via code. Check [our blog post](https://www.elasticpress.io/blog/2023/03/enabling-comments-and-terms-in-elasticpress-5-0) for more info.**
+### Added
+* Add doc url for "Did You Mean" feature. Props [@burhandodhy](https://github.com/burhandodhy) via [#3529](https://github.com/10up/ElasticPress/pull/3529).
+
### Changed
* Use wp_cache_supports over wp_cache_supports_group_flush. Props [@spacedmonkey](https://github.com/spacedmonkey) via [#3501](https://github.com/10up/ElasticPress/pull/3501).
* Update the `ep_exclude_from_search` post meta only if it is set or has some value. Props [@MARQAS](https://github.com/MARQAS) and [@columbian-chris](https://github.com/columbian-chris) via [#3521](https://github.com/10up/ElasticPress/pull/3521).
@@ -26,6 +29,9 @@ All notable changes to this project will be documented in this file, per [the Ke
* Restrict applying a facet filter to the query if the filter value is empty. Props [@burhandodhy](https://github.com/burhandodhy) via [#3524](https://github.com/10up/ElasticPress/pull/3524).
* Syncing a post with empty post meta key. Props [@MARQAS](https://github.com/MARQAS) and [@oscarssanchez](https://github.com/oscarssanchez) via [#3516](https://github.com/10up/ElasticPress/pull/3516).
* Order by clauses with Elasticsearch field formats are not changed anymore. Props [@felipeelia](https://github.com/felipeelia) and [@tlovett1](https://github.com/tlovett1) via [#3512](https://github.com/10up/ElasticPress/pull/3512).
+* Failed Query logs are automatically cleared on refreshing the "Status Report" page. Props [@burhandodhy](https://github.com/burhandodhy) via [#3533](https://github.com/10up/ElasticPress/pull/3533).
+* Warning message on Health page when Last Sync information is not available. Props [@burhandodhy](https://github.com/burhandodhy) via [#3530](https://github.com/10up/ElasticPress/pull/3530).
+* Deprecation notice: json_encode(): Passing null to parameter #2. Props [@burhandodhy](https://github.com/burhandodhy) via [#3532](https://github.com/10up/ElasticPress/pull/3532).
* Documentation of the `ep_facet_search_get_terms_args` filter. Props [@burhandodhy](https://github.com/burhandodhy) via [#3525](https://github.com/10up/ElasticPress/pull/3525).
## [4.6.0] - 2023-06-13
From b2c26d151ff12ced9353b75c6c21e040c0568951 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Wed, 5 Jul 2023 13:23:44 -0300
Subject: [PATCH 33/45] Changelog updates
---
CHANGELOG.md | 6 +++---
readme.txt | 24 ++++++++++++++++++++++++
2 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b6b3cd8480..eb0c86ca96 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,7 +13,7 @@ All notable changes to this project will be documented in this file, per [the Ke
### Security
-->
-## [4.6.1] - 2023-07-XX
+## [4.6.1] - 2023-07-05
**Note that starting from the ElasticPress 5.0.0 release the `Users` feature will be moved to the [ElasticPress Labs](https://github.com/10up/ElasticPressLabs) plugin. The `Terms` and `Comments` features will remain in ElasticPress but will be available only if enabled via code. Check [our blog post](https://www.elasticpress.io/blog/2023/03/enabling-comments-and-terms-in-elasticpress-5-0) for more info.**
@@ -21,12 +21,12 @@ All notable changes to this project will be documented in this file, per [the Ke
* Add doc url for "Did You Mean" feature. Props [@burhandodhy](https://github.com/burhandodhy) via [#3529](https://github.com/10up/ElasticPress/pull/3529).
### Changed
-* Use wp_cache_supports over wp_cache_supports_group_flush. Props [@spacedmonkey](https://github.com/spacedmonkey) via [#3501](https://github.com/10up/ElasticPress/pull/3501).
+* Use `wp_cache_supports` over `wp_cache_supports_group_flush`. Props [@spacedmonkey](https://github.com/spacedmonkey) via [#3501](https://github.com/10up/ElasticPress/pull/3501).
* Update the `ep_exclude_from_search` post meta only if it is set or has some value. Props [@MARQAS](https://github.com/MARQAS) and [@columbian-chris](https://github.com/columbian-chris) via [#3521](https://github.com/10up/ElasticPress/pull/3521).
### Fixed
* Deprecation notice in `ElasticPress\Feature\WooCommerce\Orders`. Props [@mwidmann](https://github.com/mwidmann) via [#3507](https://github.com/10up/ElasticPress/pull/3507).
-* Restrict applying a facet filter to the query if the filter value is empty. Props [@burhandodhy](https://github.com/burhandodhy) via [#3524](https://github.com/10up/ElasticPress/pull/3524).
+* Don't apply a facet filter to the query if the filter value is empty. Props [@burhandodhy](https://github.com/burhandodhy) via [#3524](https://github.com/10up/ElasticPress/pull/3524).
* Syncing a post with empty post meta key. Props [@MARQAS](https://github.com/MARQAS) and [@oscarssanchez](https://github.com/oscarssanchez) via [#3516](https://github.com/10up/ElasticPress/pull/3516).
* Order by clauses with Elasticsearch field formats are not changed anymore. Props [@felipeelia](https://github.com/felipeelia) and [@tlovett1](https://github.com/tlovett1) via [#3512](https://github.com/10up/ElasticPress/pull/3512).
* Failed Query logs are automatically cleared on refreshing the "Status Report" page. Props [@burhandodhy](https://github.com/burhandodhy) via [#3533](https://github.com/10up/ElasticPress/pull/3533).
diff --git a/readme.txt b/readme.txt
index 8b0e487388..e75eccd80a 100644
--- a/readme.txt
+++ b/readme.txt
@@ -81,6 +81,30 @@ For sure! Feel free to submit ideas or feedback in general to our [GitHub repo](
== Changelog ==
+= 4.6.1 - 2023-07-05 =
+
+**Note that starting from the ElasticPress 5.0.0 release the `Users` feature will be moved to the [ElasticPress Labs](https://github.com/10up/ElasticPressLabs) plugin. The `Terms` and `Comments` features will remain in ElasticPress but will be available only if enabled via code. Check [our blog post](https://www.elasticpress.io/blog/2023/03/enabling-comments-and-terms-in-elasticpress-5-0) for more info.**
+
+__Added:__
+
+* Add doc url for "Did You Mean" feature. Props [@burhandodhy](https://github.com/burhandodhy).
+
+__Changed:__
+
+* Use `wp_cache_supports` over `wp_cache_supports_group_flush`. Props [@spacedmonkey](https://github.com/spacedmonkey).
+* Update the `ep_exclude_from_search` post meta only if it is set or has some value. Props [@MARQAS](https://github.com/MARQAS) and [@columbian-chris](https://github.com/columbian-chris).
+
+__Fixed:__
+
+* Deprecation notice in `ElasticPress\Feature\WooCommerce\Orders`. Props [@mwidmann](https://github.com/mwidmann).
+* Don't apply a facet filter to the query if the filter value is empty. Props [@burhandodhy](https://github.com/burhandodhy).
+* Syncing a post with empty post meta key. Props [@MARQAS](https://github.com/MARQAS) and [@oscarssanchez](https://github.com/oscarssanchez).
+* Order by clauses with Elasticsearch field formats are not changed anymore. Props [@felipeelia](https://github.com/felipeelia) and [@tlovett1](https://github.com/tlovett1).
+* Failed Query logs are automatically cleared on refreshing the "Status Report" page. Props [@burhandodhy](https://github.com/burhandodhy).
+* Warning message on Health page when Last Sync information is not available. Props [@burhandodhy](https://github.com/burhandodhy).
+* Deprecation notice: json_encode(): Passing null to parameter #2. Props [@burhandodhy](https://github.com/burhandodhy).
+* Documentation of the `ep_facet_search_get_terms_args` filter. Props [@burhandodhy](https://github.com/burhandodhy).
+
= 4.6.0 - 2023-06-13 =
**Note that starting from the ElasticPress 5.0.0 release the `Users` feature will be moved to the [ElasticPress Labs](https://github.com/10up/ElasticPressLabs) plugin. The `Terms` and `Comments` features will remain in ElasticPress but will be available only if enabled via code. Check [our blog post](https://www.elasticpress.io/blog/2023/03/enabling-comments-and-terms-in-elasticpress-5-0) for more info.**
From 6fc365927bbf4cbe7faf01f3c189fcc53499880b Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Wed, 5 Jul 2023 13:25:15 -0300
Subject: [PATCH 34/45] Update the .pot file
---
lang/elasticpress.pot | 30 +++++++++++++++++-------------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/lang/elasticpress.pot b/lang/elasticpress.pot
index 7efd6cdc80..3f301a8cf5 100644
--- a/lang/elasticpress.pot
+++ b/lang/elasticpress.pot
@@ -2,14 +2,14 @@
# This file is distributed under the GPL v2 or later.
msgid ""
msgstr ""
-"Project-Id-Version: ElasticPress 4.6.0\n"
+"Project-Id-Version: ElasticPress 4.6.1\n"
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/elasticpress\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"POT-Creation-Date: 2023-06-13T13:37:04+00:00\n"
+"POT-Creation-Date: 2023-07-05T16:24:55+00:00\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"X-Generator: WP-CLI 2.7.1\n"
"X-Domain: elasticpress\n"
@@ -585,46 +585,50 @@ msgstr ""
msgid "Recommend alternative search terms for misspelled queries or terms with no results."
msgstr ""
+#: includes/classes/Feature/DidYouMean/DidYouMean.php:28
+msgid "https://elasticpress.zendesk.com/hc/en-us/articles/16673223107085-Did-You-Mean"
+msgstr ""
+
#. translators: Tutorial URL
-#: includes/classes/Feature/DidYouMean/DidYouMean.php:64
+#: includes/classes/Feature/DidYouMean/DidYouMean.php:66
msgid "\"Did You Mean\" search feature provides alternative suggestions for misspelled or ambiguous search queries, enhancing search accuracy and user experience. To display suggestions in your theme, please follow this tutorial."
msgstr ""
-#: includes/classes/Feature/DidYouMean/DidYouMean.php:142
+#: includes/classes/Feature/DidYouMean/DidYouMean.php:144
msgid "Did you mean"
msgstr ""
-#: includes/classes/Feature/DidYouMean/DidYouMean.php:227
+#: includes/classes/Feature/DidYouMean/DidYouMean.php:229
#: includes/classes/Feature/Search/Synonyms.php:89
#: includes/classes/Feature/SearchOrdering/SearchOrdering.php:186
msgid "This feature requires the \"Post Search\" feature to be enabled"
msgstr ""
-#: includes/classes/Feature/DidYouMean/DidYouMean.php:242
+#: includes/classes/Feature/DidYouMean/DidYouMean.php:244
msgid "Search behavior when no result is found"
msgstr ""
-#: includes/classes/Feature/DidYouMean/DidYouMean.php:244
+#: includes/classes/Feature/DidYouMean/DidYouMean.php:246
msgid "Display the top suggestion"
msgstr ""
-#: includes/classes/Feature/DidYouMean/DidYouMean.php:245
+#: includes/classes/Feature/DidYouMean/DidYouMean.php:247
msgid "Display all the suggestions"
msgstr ""
-#: includes/classes/Feature/DidYouMean/DidYouMean.php:246
+#: includes/classes/Feature/DidYouMean/DidYouMean.php:248
msgid "Automatically redirect the user to the top suggestion"
msgstr ""
-#: includes/classes/Feature/DidYouMean/DidYouMean.php:284
+#: includes/classes/Feature/DidYouMean/DidYouMean.php:286
msgid "Other suggestions:"
msgstr ""
-#: includes/classes/Feature/DidYouMean/DidYouMean.php:384
+#: includes/classes/Feature/DidYouMean/DidYouMean.php:386
msgid "Showing results for: "
msgstr ""
-#: includes/classes/Feature/DidYouMean/DidYouMean.php:386
+#: includes/classes/Feature/DidYouMean/DidYouMean.php:388
msgid "No results for: "
msgstr ""
@@ -699,7 +703,7 @@ msgid "\"All\" will only show content that matches all facets. \"Any\" will show
msgstr ""
#. translators: URL
-#: includes/classes/Feature/Facets/Facets.php:502
+#: includes/classes/Feature/Facets/Facets.php:505
msgid "Adds a Facet widget that administrators can add to the website's sidebars (widgetized areas), so that visitors can filter applicable content and search results by one or more taxonomy terms."
msgstr ""
From 683454238784fca827fbc47edbacc77c27226b18 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Fri, 7 Jul 2023 11:24:27 -0300
Subject: [PATCH 35/45] Translate WC's "price" and "popularity" in any WP_Query
---
.../classes/Feature/WooCommerce/Products.php | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/includes/classes/Feature/WooCommerce/Products.php b/includes/classes/Feature/WooCommerce/Products.php
index 6bcc7e5428..c415ed5fd6 100644
--- a/includes/classes/Feature/WooCommerce/Products.php
+++ b/includes/classes/Feature/WooCommerce/Products.php
@@ -903,17 +903,13 @@ protected function maybe_set_orderby( \WP_Query $query ) {
/**
* Set orderby and order for price/popularity when GET param not set
*/
- if ( isset( $query->query_vars['orderby'], $query->query_vars['order'] ) && $query->is_main_query() ) {
- switch ( $query->query_vars['orderby'] ) {
- case 'price':
- $query->set( 'order', $query->query_vars['order'] );
- $query->set( 'orderby', $this->get_orderby_meta_mapping( '_price' ) );
- break;
- case 'popularity':
- $query->set( 'orderby', $this->get_orderby_meta_mapping( 'total_sales' ) );
- $query->set( 'order', 'DESC' );
- break;
- }
+ $orderby = $query->get( 'orderby', null );
+ if ( $orderby && in_array( $orderby, [ 'price', 'popularity' ], true ) ) {
+ $order = $query->get( 'order', 'DESC' );
+ $query->set( 'order', $order );
+
+ $orderby_field = 'price' === $orderby ? '_price' : 'total_sales';
+ $query->set( 'orderby', $this->get_orderby_meta_mapping( $orderby_field ) );
}
/**
From c9058c1ca2e99aa465d556b0bc07c78f948e46df Mon Sep 17 00:00:00 2001
From: Burhan Nasir
Date: Mon, 17 Jul 2023 16:58:33 +0500
Subject: [PATCH 36/45] Remove Media Attachments from search
---
includes/classes/Feature/Search/Search.php | 21 ++++++++----
tests/php/features/TestDocuments.php | 39 ++++++++++++++++++++++
2 files changed, 53 insertions(+), 7 deletions(-)
diff --git a/includes/classes/Feature/Search/Search.php b/includes/classes/Feature/Search/Search.php
index 74ace30779..efeda0fabb 100644
--- a/includes/classes/Feature/Search/Search.php
+++ b/includes/classes/Feature/Search/Search.php
@@ -110,8 +110,11 @@ public function search_setup() {
add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] );
add_filter( 'ep_post_filters', [ $this, 'exclude_posts_from_search' ], 10, 3 );
add_action( 'post_submitbox_misc_actions', [ $this, 'output_exclude_from_search_setting' ] );
- add_action( 'edit_post', [ $this, 'save_exclude_from_search_meta' ], 10, 2 );
+ add_action( 'edit_post', [ $this, 'save_exclude_from_search_meta' ] );
add_filter( 'ep_skip_query_integration', [ $this, 'skip_query_integration' ], 10, 2 );
+
+ add_action( 'attachment_submitbox_misc_actions', [ $this, 'output_exclude_from_search_setting' ], 15 );
+ add_action( 'edit_attachment', [ $this, 'save_exclude_from_search_meta' ] );
}
@@ -775,7 +778,6 @@ public function exclude_posts_from_search( $filters, $args, $query ) {
* @param WP_POST $post Post object.
*/
public function output_exclude_from_search_setting( $post ) {
-
$searchable_post_types = $this->get_searchable_post_types();
if ( ! in_array( $post->post_type, $searchable_post_types, true ) ) {
return;
@@ -784,7 +786,14 @@ public function output_exclude_from_search_setting( $post ) {
>
-
+
+ post_type ) : ?>
+
+
+
+
+
+
assertTrue( $query->elasticsearch_success );
$this->assertEquals( 1, count( $query->posts ) );
}
+
+ /**
+ * Tests query doesn't return the post if `ep_exclude_from_search` meta is set.
+ *
+ * @since 4.7.0
+ */
+ public function testExcludeFromSearchQuery() {
+ ElasticPress\Features::factory()->activate_feature( 'search' );
+ ElasticPress\Features::factory()->activate_feature( 'documents' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $this->ep_factory->post->create_many(
+ 2,
+ array(
+ 'post_content' => 'find me in search',
+ 'meta_input' => array( 'ep_exclude_from_search' => false ),
+ 'post_type' => 'attachment',
+ 'post_mime_type' => 'application/msword',
+ )
+ );
+ $this->ep_factory->post->create(
+ array(
+ 'post_content' => 'exclude from search',
+ 'meta_input' => array( 'ep_exclude_from_search' => true ),
+ 'post_type' => 'attachment',
+ 'post_mime_type' => 'application/msword',
+ )
+ );
+
+ ElasticPress\Elasticsearch::factory()->refresh_indices();
+
+ $args = array(
+ 's' => 'search',
+ );
+ $query = new \WP_Query( $args );
+
+ $this->assertTrue( $query->elasticsearch_success );
+ $this->assertEquals( 2, $query->post_count );
+ }
}
From 2a618495d07cf71cf45ca381fb1cffd9b3e34058 Mon Sep 17 00:00:00 2001
From: Burhan Nasir
Date: Mon, 17 Jul 2023 20:55:07 +0500
Subject: [PATCH 37/45] Minor change
---
includes/classes/Feature/Search/Search.php | 1 -
1 file changed, 1 deletion(-)
diff --git a/includes/classes/Feature/Search/Search.php b/includes/classes/Feature/Search/Search.php
index efeda0fabb..a0cd3ffb76 100644
--- a/includes/classes/Feature/Search/Search.php
+++ b/includes/classes/Feature/Search/Search.php
@@ -792,7 +792,6 @@ public function output_exclude_from_search_setting( $post ) {
-
From 5b6c073eaea353e1d5cf917aff2bc730d7b7991b Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Wed, 19 Jul 2023 14:52:24 -0300
Subject: [PATCH 38/45] Only integrate WC queries if main query, search, or has
ep_integrate
---
.../classes/Feature/WooCommerce/Orders.php | 12 +++-
.../classes/Feature/WooCommerce/Products.php | 14 +++-
.../WooCommerce/TestWooCommerceOrders.php | 44 +++++++++++-
.../WooCommerce/TestWooCommerceProduct.php | 69 +++++++++++++++++--
4 files changed, 128 insertions(+), 11 deletions(-)
diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php
index 8dd4da7928..2595ca8b0f 100644
--- a/includes/classes/Feature/WooCommerce/Orders.php
+++ b/includes/classes/Feature/WooCommerce/Orders.php
@@ -249,12 +249,22 @@ public function search_order( $wp ) {
}
/**
- * Determines whether or not ES should be integrating with the provided query
+ * Determines whether or not ES should be integrating with the provided query.
+ *
+ * An order-related query will be integrated if:
+ * * Is the main query OR is a search OR has `ep_integrate` set as true
+ * * Is querying a supported post type like `order`
*
* @param \WP_Query $query Query we might integrate with
* @return bool
*/
public function should_integrate_with_query( \WP_Query $query ) : bool {
+ $has_ep_integrate = isset( $query->query_vars['ep_integrate'] ) && filter_var( $query->query_vars['ep_integrate'], FILTER_VALIDATE_BOOLEAN );
+
+ if ( ! $query->is_main_query() && ! $query->is_search() && ! $has_ep_integrate ) {
+ return false;
+ }
+
/**
* Check the post type
*/
diff --git a/includes/classes/Feature/WooCommerce/Products.php b/includes/classes/Feature/WooCommerce/Products.php
index c415ed5fd6..08ffda2396 100644
--- a/includes/classes/Feature/WooCommerce/Products.php
+++ b/includes/classes/Feature/WooCommerce/Products.php
@@ -617,12 +617,24 @@ public function translate_args( $query ) {
}
/**
- * Determines whether or not ES should be integrating with the provided query
+ * Determines whether or not ES should be integrating with the provided query.
+ *
+ * A product-related query will be integrated if:
+ * * Is the main query OR is a search OR has `ep_integrate` set as true
+ * * Is querying a supported taxonomy like product attributes
+ * * Is querying a supported post type like `product`
*
* @param \WP_Query $query Query we might integrate with
* @return bool
*/
public function should_integrate_with_query( \WP_Query $query ) : bool {
+ $has_ep_integrate = isset( $query->query_vars['ep_integrate'] ) && filter_var( $query->query_vars['ep_integrate'], FILTER_VALIDATE_BOOLEAN );
+ $is_search = '' !== $this->woocommerce->get_search_term( $query );
+
+ if ( ! $query->is_main_query() && ! $is_search && ! $has_ep_integrate ) {
+ return false;
+ }
+
/**
* Check for taxonomies
*/
diff --git a/tests/php/features/WooCommerce/TestWooCommerceOrders.php b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
index 9c1eff486e..2831f7d57a 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceOrders.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
@@ -102,7 +102,7 @@ public function testShopOrderPostTypeQueryOn() {
/**
- * Test Shop Order post type query does get integrated when the protected content feature is activated.
+ * Test Shop Order post type query does not get automatically integrated when the protected content feature is activated.
*
* @group woocommerce
* @group woocommerce-orders
@@ -126,7 +126,7 @@ public function testShopOrderPostTypeQueryWhenProtectedContentEnable() {
);
$query = new \WP_Query( $args );
- $this->assertTrue( $query->elasticsearch_success );
+ $this->assertNull( $query->elasticsearch_success );
$this->assertEquals( 1, $query->post_count );
$this->assertEquals( 1, $query->found_posts );
}
@@ -160,6 +160,46 @@ public function testShopOrderPostTypeQueryWhenEPIntegrateSetFalse() {
$this->assertNull( $query->elasticsearch_success );
}
+ /**
+ * Test Shop Order post type query does get integrated when the protected content feature is activated and ep_integrate is set to true.
+ *
+ * @since 4.7.0
+ * @group woocommerce
+ * @group woocommerce-orders
+ */
+ public function testShopOrderPostTypeQueryWhenEPIntegrateSetTrue() {
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $args = array(
+ 'post_type' => 'shop_order',
+ 'ep_integrate' => true,
+ );
+ $query = new \WP_Query( $args );
+
+ $this->assertTrue( $query->elasticsearch_success );
+ }
+
+ /**
+ * Test Shop Order post type query does get integrated when the protected content feature is activated and it is the main query.
+ *
+ * @since 4.7.0
+ * @group woocommerce
+ * @group woocommerce-orders
+ */
+ public function testShopOrderPostTypeQueryWhenMainQuery() {
+ global $wp_the_query;
+
+ ElasticPress\Features::factory()->activate_feature( 'protected_content' );
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $wp_the_query->query( [ 'post_type' => 'shop_order' ] );
+
+ $this->assertTrue( $wp_the_query->elasticsearch_success );
+ }
+
/**
* Test search for shop orders by order ID
*
diff --git a/tests/php/features/WooCommerce/TestWooCommerceProduct.php b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
index bb1fda8696..5df555b367 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceProduct.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
@@ -63,20 +63,15 @@ public function testProductsPostTypeQueryOn() {
}
/**
- * Test products post type query does get integrated when querying WC product_cat taxonomy
+ * Test products post type query does not get automatically integrated when querying WC product_cat taxonomy
*
* @group woocommerce
* @group woocommerce-products
*/
public function testProductsPostTypeQueryProductCatTax() {
- ElasticPress\Features::factory()->activate_feature( 'admin' );
ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
ElasticPress\Features::factory()->setup_features();
- $this->ep_factory->post->create();
-
- ElasticPress\Elasticsearch::factory()->refresh_indices();
-
$args = array(
'tax_query' => array(
array(
@@ -89,15 +84,75 @@ public function testProductsPostTypeQueryProductCatTax() {
$query = new \WP_Query( $args );
- $this->assertTrue( $query->elasticsearch_success );
+ $this->assertNull( $query->elasticsearch_success );
$args = [ 'product_cat' => 'cat' ];
$query = new \WP_Query( $args );
+ $this->assertNull( $query->elasticsearch_success );
+ }
+
+ /**
+ * Test WC product_cat taxonomy queries do get automatically integrated when ep_integrate is set to true
+ *
+ * @since 4.7.0
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testProductsPostTypeQueryProductCatTaxWhenEPIntegrateSetTrue() {
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $args = [
+ 'product_cat' => 'cat',
+ 'ep_integrate' => true,
+ ];
+
+ $query = new \WP_Query( $args );
+
$this->assertTrue( $query->elasticsearch_success );
}
+ /**
+ * Test WC product_cat taxonomy queries do get automatically integrated for the main query
+ *
+ * @since 4.7.0
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testProductsPostTypeQueryProductCatTaxWhenMainQuery() {
+ global $wp_the_query;
+
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $args = [
+ 'product_cat' => 'cat',
+ ];
+
+ $wp_the_query->query( $args );
+
+ $this->assertTrue( $wp_the_query->elasticsearch_success );
+ }
+
+ /**
+ * Test products post type query does get automatically integrated for the main query
+ *
+ * @since 4.7.0
+ * @group woocommerce
+ * @group woocommerce-products
+ */
+ public function testProductsPostTypeQueryProductWhenMainQuery() {
+ global $wp_the_query;
+
+ ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
+ ElasticPress\Features::factory()->setup_features();
+
+ $wp_the_query->query( [ 'post_type' => 'product' ] );
+
+ $this->assertTrue( $wp_the_query->elasticsearch_success );
+ }
/**
* Test search integration is on in general for product searches
From 553aee955e54dee328592b930e270a3c29e3f9fc Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Wed, 19 Jul 2023 15:06:22 -0300
Subject: [PATCH 39/45] Fix lint
---
tests/php/features/WooCommerce/TestWooCommerceProduct.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/php/features/WooCommerce/TestWooCommerceProduct.php b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
index 5df555b367..826dd8eeb7 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceProduct.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceProduct.php
@@ -128,7 +128,7 @@ public function testProductsPostTypeQueryProductCatTaxWhenMainQuery() {
ElasticPress\Features::factory()->setup_features();
$args = [
- 'product_cat' => 'cat',
+ 'product_cat' => 'cat',
];
$wp_the_query->query( $args );
From f0cf8d5c3915378b66e5eb2835d9d75e2f38b9df Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Wed, 19 Jul 2023 15:59:29 -0300
Subject: [PATCH 40/45] Add the get-index-settings WP-CLI command
---
includes/classes/Command.php | 25 +++++++++++++++++++++++++
tests/php/TestCommands.php | 25 +++++++++++++++++++++++++
2 files changed, 50 insertions(+)
diff --git a/includes/classes/Command.php b/includes/classes/Command.php
index 823cf82849..6873bd417a 100644
--- a/includes/classes/Command.php
+++ b/includes/classes/Command.php
@@ -1565,4 +1565,29 @@ public function delete_search_template() {
$instant_results->epio_delete_search_template();
WP_CLI::success( esc_html__( 'Done.', 'elasticpress' ) );
}
+
+ /**
+ * Get an index settings
+ *
+ * ## OPTIONS
+ *
+ *
+ * : Index name
+ *
+ * [--pretty]
+ * : Use this flag to render a pretty-printed version of the JSON response.
+ *
+ * @subcommand get-index-settings
+ *
+ * @since 4.7.0
+ *
+ * @param array $args Positional CLI args.
+ * @param array $assoc_args Associative CLI args.
+ */
+ public function get_index_settings( $args, $assoc_args ) {
+ $response = Elasticsearch::factory()->get_index_settings( $args[0] );
+ $pretty = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pretty' );
+
+ $this->pretty_json_encode( $response, $pretty );
+ }
}
diff --git a/tests/php/TestCommands.php b/tests/php/TestCommands.php
index 721577a940..ede446e1f7 100644
--- a/tests/php/TestCommands.php
+++ b/tests/php/TestCommands.php
@@ -1032,6 +1032,31 @@ public function testThrowsErrorWhenHostIsNotSet() {
$this->command->sync( [], [] );
}
+ /**
+ * Test get-index-settings command returns an index settings.
+ *
+ * @since 4.7.0
+ */
+ public function testGetIndexSettings() {
+ $this->command->get_index_settings( [ 'exampleorg-post-1' ], [] );
+
+ $output = $this->getActualOutputForAssertion();
+ $this->assertStringStartsWith( '{', $output );
+ $this->assertStringContainsString( 'index.mapping.total_fields.limit', $output );
+
+ // clean output buffer
+ ob_clean();
+
+ // test with --pretty flag
+ $this->command->get_index_settings( [ 'exampleorg-post-1' ], [ 'pretty' => true ] );
+
+ $output = $this->getActualOutputForAssertion();
+ $this->assertStringStartsWith( "{\n", $output );
+
+ // clean output buffer
+ ob_clean();
+ }
+
/**
* Test commands throws an error if indexing is already happening.
*/
From 18233c404d075f34ee17cbbd848a64eb0c231172 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Wed, 19 Jul 2023 16:51:07 -0300
Subject: [PATCH 41/45] Refactor how transients are deleted by option name
---
tests/php/TestUninstall.php | 68 +++++++++++++++++++++++++++++++++++++
uninstall.php | 56 ++++++++++++++++--------------
2 files changed, 99 insertions(+), 25 deletions(-)
create mode 100644 tests/php/TestUninstall.php
diff --git a/tests/php/TestUninstall.php b/tests/php/TestUninstall.php
new file mode 100644
index 0000000000..86258962cf
--- /dev/null
+++ b/tests/php/TestUninstall.php
@@ -0,0 +1,68 @@
+uninstaller = new \EP_Uninstaller();
+
+ parent::set_up();
+ }
+
+ /**
+ * Test the `delete_transients_by_option_name` method
+ *
+ * @group uninstall
+ */
+ public function test_delete_transients_by_option_name() {
+ set_transient( 'ep_total_fields_limit_test', 'test' );
+ set_transient( 'ep_total_fields_limit_test_2', 'test' );
+ set_transient( 'ep_related_posts_test', 'test' );
+ set_transient( 'ep_related_posts_test_2', 'test' );
+
+ $method = $this->get_protected_method( 'delete_transients_by_option_name' );
+ $method->invoke( $this->uninstaller );
+
+ $this->assertFalse( get_transient( 'ep_total_fields_limit_test' ) );
+ $this->assertFalse( get_transient( 'ep_total_fields_limit_test_2' ) );
+ $this->assertFalse( get_transient( 'ep_related_posts_test' ) );
+ $this->assertFalse( get_transient( 'ep_related_posts_test_2' ) );
+ }
+
+ /**
+ * Return a protected method made public.
+ *
+ * This should NOT be copied to any other class.
+ *
+ * @param string $method_name The method name
+ * @return \ReflectionMethod
+ */
+ protected function get_protected_method( string $method_name ) : \ReflectionMethod {
+ $reflection = new \ReflectionClass( '\EP_Uninstaller' );
+ $method = $reflection->getMethod( $method_name );
+ $method->setAccessible( true );
+
+ return $method;
+ }
+}
diff --git a/uninstall.php b/uninstall.php
index 42fad27a1f..e171a8e931 100644
--- a/uninstall.php
+++ b/uninstall.php
@@ -81,12 +81,16 @@ class EP_Uninstaller {
* @since 1.7
*/
public function __construct() {
-
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
$this->exit_uninstaller();
}
+ // If testing, do not do anything automatically
+ if ( defined( 'EP_UNIT_TESTS' ) && EP_UNIT_TESTS ) {
+ return;
+ }
+
// EP_MANUAL_SETTINGS_RESET is used by the `settings-reset` WP-CLI command.
if ( ! defined( 'EP_MANUAL_SETTINGS_RESET' ) || ! EP_MANUAL_SETTINGS_RESET ) {
// Not uninstalling.
@@ -124,37 +128,41 @@ protected function delete_transients() {
}
/**
- * Delete all transients of the Related Posts feature.
+ * Delete remaining transients by their option names.
+ *
+ * @since 4.7.0
*/
- protected function delete_related_posts_transients() {
+ protected function delete_transients_by_option_name() {
global $wpdb;
- $related_posts_transients = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
- "SELECT option_name FROM {$wpdb->prefix}options WHERE option_name LIKE '_transient_ep_related_posts_%'"
+ $transients = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
+ "SELECT option_name
+ FROM {$wpdb->prefix}options
+ WHERE
+ option_name LIKE '_transient_ep_total_fields_limit_%'
+ OR option_name LIKE '_transient_ep_related_posts_%'
+ "
);
- foreach ( $related_posts_transients as $related_posts_transient ) {
- $related_posts_transient = str_replace( '_transient_', '', $related_posts_transient );
- delete_site_transient( $related_posts_transient );
- delete_transient( $related_posts_transient );
+ foreach ( $transients as $transient ) {
+ $transient_name = str_replace( '_transient_', '', $transient );
+ delete_site_transient( $transient_name );
+ delete_transient( $transient_name );
}
}
/**
- * Delete all transients of the total fields limit.
+ * DEPRECATED. Delete all transients of the Related Posts feature.
*/
- protected function delete_total_fields_limit_transients() {
- global $wpdb;
-
- $related_posts_transients = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
- "SELECT option_name FROM {$wpdb->prefix}options WHERE option_name LIKE '_transient_ep_total_fields_limit_%'"
- );
+ protected function delete_related_posts_transients() {
+ _deprecated_function( __METHOD__, '4.7.0', '\EP_Uninstaller::delete_transients_by_name()' );
+ }
- foreach ( $related_posts_transients as $related_posts_transient ) {
- $related_posts_transient = str_replace( '_transient_', '', $related_posts_transient );
- delete_site_transient( $related_posts_transient );
- delete_transient( $related_posts_transient );
- }
+ /**
+ * DEPRECATED. Delete all transients of the total fields limit.
+ */
+ protected function delete_total_fields_limit_transients() {
+ _deprecated_function( __METHOD__, '4.7.0', '\EP_Uninstaller::delete_transients_by_name()' );
}
/**
@@ -180,16 +188,14 @@ protected function clean_options_and_transients() {
$this->delete_options();
$this->delete_transients();
- $this->delete_related_posts_transients();
- $this->delete_total_fields_limit_transients();
+ $this->delete_transients_by_option_name();
restore_current_blog();
}
} else {
$this->delete_options();
$this->delete_transients();
- $this->delete_related_posts_transients();
- $this->delete_total_fields_limit_transients();
+ $this->delete_transients_by_option_name();
}
}
From dbe3ec5ab7340ea0f585fcf2d30db0ec42923405 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Thu, 20 Jul 2023 13:46:39 -0300
Subject: [PATCH 42/45] Revert changes on orders
---
.../classes/Feature/WooCommerce/Orders.php | 8 +---
.../WooCommerce/TestWooCommerceOrders.php | 44 +------------------
2 files changed, 3 insertions(+), 49 deletions(-)
diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php
index 2595ca8b0f..a2d75b60a4 100644
--- a/includes/classes/Feature/WooCommerce/Orders.php
+++ b/includes/classes/Feature/WooCommerce/Orders.php
@@ -249,7 +249,7 @@ public function search_order( $wp ) {
}
/**
- * Determines whether or not ES should be integrating with the provided query.
+ * Determines whether or not ES should be integrating with the provided query
*
* An order-related query will be integrated if:
* * Is the main query OR is a search OR has `ep_integrate` set as true
@@ -259,12 +259,6 @@ public function search_order( $wp ) {
* @return bool
*/
public function should_integrate_with_query( \WP_Query $query ) : bool {
- $has_ep_integrate = isset( $query->query_vars['ep_integrate'] ) && filter_var( $query->query_vars['ep_integrate'], FILTER_VALIDATE_BOOLEAN );
-
- if ( ! $query->is_main_query() && ! $query->is_search() && ! $has_ep_integrate ) {
- return false;
- }
-
/**
* Check the post type
*/
diff --git a/tests/php/features/WooCommerce/TestWooCommerceOrders.php b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
index 2831f7d57a..9c1eff486e 100644
--- a/tests/php/features/WooCommerce/TestWooCommerceOrders.php
+++ b/tests/php/features/WooCommerce/TestWooCommerceOrders.php
@@ -102,7 +102,7 @@ public function testShopOrderPostTypeQueryOn() {
/**
- * Test Shop Order post type query does not get automatically integrated when the protected content feature is activated.
+ * Test Shop Order post type query does get integrated when the protected content feature is activated.
*
* @group woocommerce
* @group woocommerce-orders
@@ -126,7 +126,7 @@ public function testShopOrderPostTypeQueryWhenProtectedContentEnable() {
);
$query = new \WP_Query( $args );
- $this->assertNull( $query->elasticsearch_success );
+ $this->assertTrue( $query->elasticsearch_success );
$this->assertEquals( 1, $query->post_count );
$this->assertEquals( 1, $query->found_posts );
}
@@ -160,46 +160,6 @@ public function testShopOrderPostTypeQueryWhenEPIntegrateSetFalse() {
$this->assertNull( $query->elasticsearch_success );
}
- /**
- * Test Shop Order post type query does get integrated when the protected content feature is activated and ep_integrate is set to true.
- *
- * @since 4.7.0
- * @group woocommerce
- * @group woocommerce-orders
- */
- public function testShopOrderPostTypeQueryWhenEPIntegrateSetTrue() {
- ElasticPress\Features::factory()->activate_feature( 'protected_content' );
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $args = array(
- 'post_type' => 'shop_order',
- 'ep_integrate' => true,
- );
- $query = new \WP_Query( $args );
-
- $this->assertTrue( $query->elasticsearch_success );
- }
-
- /**
- * Test Shop Order post type query does get integrated when the protected content feature is activated and it is the main query.
- *
- * @since 4.7.0
- * @group woocommerce
- * @group woocommerce-orders
- */
- public function testShopOrderPostTypeQueryWhenMainQuery() {
- global $wp_the_query;
-
- ElasticPress\Features::factory()->activate_feature( 'protected_content' );
- ElasticPress\Features::factory()->activate_feature( 'woocommerce' );
- ElasticPress\Features::factory()->setup_features();
-
- $wp_the_query->query( [ 'post_type' => 'shop_order' ] );
-
- $this->assertTrue( $wp_the_query->elasticsearch_success );
- }
-
/**
* Test search for shop orders by order ID
*
From c05426be818ca95636981a7e4152ccfc85107fc4 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Thu, 20 Jul 2023 13:47:35 -0300
Subject: [PATCH 43/45] Remove change in docblock
---
includes/classes/Feature/WooCommerce/Orders.php | 4 ----
1 file changed, 4 deletions(-)
diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php
index a2d75b60a4..8dd4da7928 100644
--- a/includes/classes/Feature/WooCommerce/Orders.php
+++ b/includes/classes/Feature/WooCommerce/Orders.php
@@ -251,10 +251,6 @@ public function search_order( $wp ) {
/**
* Determines whether or not ES should be integrating with the provided query
*
- * An order-related query will be integrated if:
- * * Is the main query OR is a search OR has `ep_integrate` set as true
- * * Is querying a supported post type like `order`
- *
* @param \WP_Query $query Query we might integrate with
* @return bool
*/
From 74e24ed97ffa3e33fd8ba0ce3b4f4fef2f036f29 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Thu, 20 Jul 2023 16:32:48 -0300
Subject: [PATCH 44/45] Add ep_stop filter
---
includes/dashboard.php | 16 ++++++++++------
includes/mappings/comment/7-0.php | 26 +++++++++-----------------
includes/mappings/comment/initial.php | 26 +++++++++-----------------
includes/mappings/post/5-2.php | 26 +++++++++-----------------
includes/mappings/post/7-0.php | 17 ++++++++---------
includes/mappings/term/7-0.php | 10 +++++++++-
includes/mappings/term/initial.php | 9 ++++++++-
includes/mappings/user/7-0.php | 26 +++++++++-----------------
includes/mappings/user/initial.php | 26 +++++++++-----------------
9 files changed, 80 insertions(+), 102 deletions(-)
diff --git a/includes/dashboard.php b/includes/dashboard.php
index 044bda8501..9a11e5eb52 100644
--- a/includes/dashboard.php
+++ b/includes/dashboard.php
@@ -733,13 +733,13 @@ function use_language_in_setting( $language = 'english', $context = '' ) {
require_once ABSPATH . 'wp-admin/includes/translation-install.php';
$translations = wp_get_available_translations();
- // Bail early if not in the array of available translations.
- if ( empty( $translations[ $ep_language ]['english_name'] ) ) {
- return $language;
+ // Default to en_US if not in the array of available translations.
+ if ( ! empty( $translations[ $ep_language ]['english_name'] ) ) {
+ $wp_language = $translations[ $ep_language ]['language'];
+ } else {
+ $wp_language = 'en_US';
}
- $wp_language = $translations[ $ep_language ]['language'];
-
/**
* Languages supported in Elasticsearch mappings.
* Array format: Elasticsearch analyzer name => WordPress language package name
@@ -758,7 +758,7 @@ function use_language_in_setting( $language = 'english', $context = '' ) {
'czech' => [ 'cs' ],
'danish' => [ 'da' ],
'dutch' => [ 'nl_NL_formal', 'nl_NL', 'nl_BE' ],
- 'english' => [ 'en', 'en_AU', 'en_GB', 'en_NZ', 'en_CA', 'en_ZA' ],
+ 'english' => [ 'en', 'en_AU', 'en_GB', 'en_NZ', 'en_CA', 'en_US', 'en_ZA' ],
'estonian' => [ 'et' ],
'finnish' => [ 'fi' ],
'french' => [ 'fr', 'fr_CA', 'fr_FR', 'fr_BE' ],
@@ -830,6 +830,10 @@ function use_language_in_setting( $language = 'english', $context = '' ) {
return 'English';
}
+ if ( 'filter_ep_stop' === $context ) {
+ return "_{$language}_";
+ }
+
return $language;
}
diff --git a/includes/mappings/comment/7-0.php b/includes/mappings/comment/7-0.php
index ea6f6105b1..9dfe859c60 100644
--- a/includes/mappings/comment/7-0.php
+++ b/includes/mappings/comment/7-0.php
@@ -64,15 +64,8 @@
'analyzer' => [
'default' => [
'tokenizer' => 'standard',
- 'filter' => [ 'ewp_word_delimiter', 'lowercase', 'stop', 'ewp_snowball' ],
- /**
- * Filter Elasticsearch default language in mapping
- *
- * @hook ep_analyzer_language
- * @param {string} $lang Default language
- * @param {string} $lang_context Language context
- * @return {string} New language
- */
+ 'filter' => [ 'ewp_word_delimiter', 'lowercase', 'ep_stop', 'ewp_snowball' ],
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'analyzer_default' ),
],
'shingle_analyzer' => [
@@ -98,14 +91,7 @@
],
'ewp_snowball' => [
'type' => 'snowball',
- /**
- * Filter Elasticsearch default language in mapping
- *
- * @hook ep_analyzer_language
- * @param {string} $lang Default language
- * @param {string} $lang_context Language context
- * @return {string} New language
- */
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ewp_snowball' ),
],
'edge_ngram' => [
@@ -114,6 +100,12 @@
'min_gram' => 3,
'type' => 'edge_ngram',
],
+ 'ep_stop' => [
+ 'type' => 'stop',
+ 'ignore_case' => true,
+ /* This filter is documented in includes/mappings/post/7-0.php */
+ 'stopwords' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ep_stop' ),
+ ],
],
'normalizer' => [
'lowerasciinormalizer' => [
diff --git a/includes/mappings/comment/initial.php b/includes/mappings/comment/initial.php
index 4888a635af..90cfe957b8 100644
--- a/includes/mappings/comment/initial.php
+++ b/includes/mappings/comment/initial.php
@@ -48,15 +48,8 @@
'analyzer' => [
'default' => [
'tokenizer' => 'standard',
- 'filter' => [ 'standard', 'ewp_word_delimiter', 'lowercase', 'stop', 'ewp_snowball' ],
- /**
- * Filter Elasticsearch default language in mapping
- *
- * @hook ep_analyzer_language
- * @param {string} $lang Default language
- * @param {string} $lang_context Language context
- * @return {string} New language
- */
+ 'filter' => [ 'standard', 'ewp_word_delimiter', 'lowercase', 'ep_stop', 'ewp_snowball' ],
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'analyzer_default' ),
],
'shingle_analyzer' => [
@@ -82,14 +75,7 @@
],
'ewp_snowball' => [
'type' => 'snowball',
- /**
- * Filter Elasticsearch default language in mapping
- *
- * @hook ep_analyzer_language
- * @param {string} $lang Default language
- * @param {string} $lang_context Language context
- * @return {string} New language
- */
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ewp_snowball' ),
],
'edge_ngram' => [
@@ -98,6 +84,12 @@
'min_gram' => 3,
'type' => 'edgeNGram',
],
+ 'ep_stop' => [
+ 'type' => 'stop',
+ 'ignore_case' => true,
+ /* This filter is documented in includes/mappings/post/7-0.php */
+ 'stopwords' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ep_stop' ),
+ ],
],
'normalizer' => [
'lowerasciinormalizer' => [
diff --git a/includes/mappings/post/5-2.php b/includes/mappings/post/5-2.php
index fd3086bbbb..fae2c0f86f 100644
--- a/includes/mappings/post/5-2.php
+++ b/includes/mappings/post/5-2.php
@@ -56,7 +56,7 @@
* @param {array} $filters Default filters
* @return {array} New filters
*/
- 'filter' => apply_filters( 'ep_default_analyzer_filters', array( 'standard', 'ewp_word_delimiter', 'lowercase', 'stop', 'ewp_snowball' ) ),
+ 'filter' => apply_filters( 'ep_default_analyzer_filters', array( 'standard', 'ewp_word_delimiter', 'lowercase', 'ep_stop', 'ewp_snowball' ) ),
/**
* Filter Elasticsearch default analyzer's char_filter
*
@@ -66,14 +66,7 @@
* @return {array} New filters
*/
'char_filter' => apply_filters( 'ep_default_analyzer_char_filters', array( 'html_strip' ) ),
- /**
- * Filter Elasticsearch default language in mapping
- *
- * @hook ep_analyzer_language
- * @param {string} $lang Default language
- * @param {string} $lang_context Language context
- * @return {string} New language
- */
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'analyzer_default' ),
),
'shingle_analyzer' => array(
@@ -99,14 +92,7 @@
),
'ewp_snowball' => array(
'type' => 'snowball',
- /**
- * Filter Elasticsearch default language in mapping
- *
- * @hook ep_analyzer_language
- * @param {string} $lang Default language
- * @param {string} $lang_context Language context
- * @return {string} New language
- */
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ewp_snowball' ),
),
'edge_ngram' => array(
@@ -115,6 +101,12 @@
'min_gram' => 3,
'type' => 'edgeNGram',
),
+ 'ep_stop' => [
+ 'type' => 'stop',
+ 'ignore_case' => true,
+ /* This filter is documented in includes/mappings/post/7-0.php */
+ 'stopwords' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ep_stop' ),
+ ],
),
'normalizer' => array(
'lowerasciinormalizer' => array(
diff --git a/includes/mappings/post/7-0.php b/includes/mappings/post/7-0.php
index 617e024e74..1abbc07b7e 100644
--- a/includes/mappings/post/7-0.php
+++ b/includes/mappings/post/7-0.php
@@ -72,7 +72,7 @@
* @param {array} $filters Default filters
* @return {array} New filters
*/
- 'filter' => apply_filters( 'ep_default_analyzer_filters', array( 'ewp_word_delimiter', 'lowercase', 'stop', 'ewp_snowball' ) ),
+ 'filter' => apply_filters( 'ep_default_analyzer_filters', array( 'ewp_word_delimiter', 'lowercase', 'ep_stop', 'ewp_snowball' ) ),
/**
* Filter Elasticsearch default analyzer's char_filter
*
@@ -115,14 +115,7 @@
),
'ewp_snowball' => array(
'type' => 'snowball',
- /**
- * Filter Elasticsearch default language in mapping
- *
- * @hook ep_analyzer_language
- * @param {string} $lang Default language
- * @param {string} $lang_context Language context
- * @return {string} New language
- */
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ewp_snowball' ),
),
'edge_ngram' => array(
@@ -131,6 +124,12 @@
'min_gram' => 3,
'type' => 'edge_ngram',
),
+ 'ep_stop' => [
+ 'type' => 'stop',
+ 'ignore_case' => true,
+ /* This filter is documented in includes/mappings/post/7-0.php */
+ 'stopwords' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ep_stop' ),
+ ],
),
'normalizer' => array(
'lowerasciinormalizer' => array(
diff --git a/includes/mappings/term/7-0.php b/includes/mappings/term/7-0.php
index a9cb0e3a0c..96579dde52 100644
--- a/includes/mappings/term/7-0.php
+++ b/includes/mappings/term/7-0.php
@@ -28,7 +28,8 @@
'analyzer' => [
'default' => [
'tokenizer' => 'standard',
- 'filter' => [ 'ewp_word_delimiter', 'lowercase', 'stop', 'ewp_snowball' ],
+ 'filter' => [ 'ewp_word_delimiter', 'lowercase', 'ep_stop', 'ewp_snowball' ],
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'analyzer_default' ),
],
'shingle_analyzer' => [
@@ -54,6 +55,7 @@
],
'ewp_snowball' => [
'type' => 'snowball',
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ewp_snowball' ),
],
'edge_ngram' => [
@@ -62,6 +64,12 @@
'min_gram' => 3,
'type' => 'edge_ngram',
],
+ 'ep_stop' => [
+ 'type' => 'stop',
+ 'ignore_case' => true,
+ /* This filter is documented in includes/mappings/post/7-0.php */
+ 'stopwords' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ep_stop' ),
+ ],
],
'normalizer' => [
'lowerasciinormalizer' => [
diff --git a/includes/mappings/term/initial.php b/includes/mappings/term/initial.php
index d1772ee40d..dcdfc0a163 100644
--- a/includes/mappings/term/initial.php
+++ b/includes/mappings/term/initial.php
@@ -18,7 +18,7 @@
'analyzer' => [
'default' => [
'tokenizer' => 'standard',
- 'filter' => [ 'standard', 'ewp_word_delimiter', 'lowercase', 'stop', 'ewp_snowball' ],
+ 'filter' => [ 'standard', 'ewp_word_delimiter', 'lowercase', 'ep_stop', 'ewp_snowball' ],
'language' => apply_filters( 'ep_analyzer_language', 'english', 'analyzer_default' ),
],
'shingle_analyzer' => [
@@ -44,6 +44,7 @@
],
'ewp_snowball' => [
'type' => 'snowball',
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ewp_snowball' ),
],
'edge_ngram' => [
@@ -52,6 +53,12 @@
'min_gram' => 3,
'type' => 'edgeNGram',
],
+ 'ep_stop' => [
+ 'type' => 'stop',
+ 'ignore_case' => true,
+ /* This filter is documented in includes/mappings/post/7-0.php */
+ 'stopwords' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ep_stop' ),
+ ],
],
'normalizer' => [
'lowerasciinormalizer' => [
diff --git a/includes/mappings/user/7-0.php b/includes/mappings/user/7-0.php
index 20a3b6f49b..cf4301fc6b 100644
--- a/includes/mappings/user/7-0.php
+++ b/includes/mappings/user/7-0.php
@@ -64,15 +64,8 @@
'analyzer' => array(
'default' => array(
'tokenizer' => 'standard',
- 'filter' => array( 'ewp_word_delimiter', 'lowercase', 'stop', 'ewp_snowball' ),
- /**
- * Filter Elasticsearch default language in mapping
- *
- * @hook ep_analyzer_language
- * @param {string} $lang Default language
- * @param {string} $lang_context Language context
- * @return {string} New language
- */
+ 'filter' => array( 'ewp_word_delimiter', 'lowercase', 'ep_stop', 'ewp_snowball' ),
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'analyzer_default' ),
),
'shingle_analyzer' => array(
@@ -98,14 +91,7 @@
),
'ewp_snowball' => array(
'type' => 'snowball',
- /**
- * Filter Elasticsearch default language in mapping
- *
- * @hook ep_analyzer_language
- * @param {string} $lang Default language
- * @param {string} $lang_context Language context
- * @return {string} New language
- */
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ewp_snowball' ),
),
'edge_ngram' => array(
@@ -114,6 +100,12 @@
'min_gram' => 3,
'type' => 'edge_ngram',
),
+ 'ep_stop' => [
+ 'type' => 'stop',
+ 'ignore_case' => true,
+ /* This filter is documented in includes/mappings/post/7-0.php */
+ 'stopwords' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ep_stop' ),
+ ],
),
'normalizer' => array(
'lowerasciinormalizer' => array(
diff --git a/includes/mappings/user/initial.php b/includes/mappings/user/initial.php
index 063a671f39..76dc598468 100644
--- a/includes/mappings/user/initial.php
+++ b/includes/mappings/user/initial.php
@@ -48,15 +48,8 @@
'analyzer' => array(
'default' => array(
'tokenizer' => 'standard',
- 'filter' => array( 'standard', 'ewp_word_delimiter', 'lowercase', 'stop', 'ewp_snowball' ),
- /**
- * Filter Elasticsearch default language in mapping
- *
- * @hook ep_analyzer_language
- * @param {string} $lang Default language
- * @param {string} $lang_context Language context
- * @return {string} New language
- */
+ 'filter' => array( 'standard', 'ewp_word_delimiter', 'lowercase', 'ep_stop', 'ewp_snowball' ),
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'analyzer_default' ),
),
'shingle_analyzer' => array(
@@ -82,14 +75,7 @@
),
'ewp_snowball' => array(
'type' => 'snowball',
- /**
- * Filter Elasticsearch default language in mapping
- *
- * @hook ep_analyzer_language
- * @param {string} $lang Default language
- * @param {string} $lang_context Language context
- * @return {string} New language
- */
+ /* This filter is documented in includes/mappings/post/7-0.php */
'language' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ewp_snowball' ),
),
'edge_ngram' => array(
@@ -98,6 +84,12 @@
'min_gram' => 3,
'type' => 'edgeNGram',
),
+ 'ep_stop' => [
+ 'type' => 'stop',
+ 'ignore_case' => true,
+ /* This filter is documented in includes/mappings/post/7-0.php */
+ 'stopwords' => apply_filters( 'ep_analyzer_language', 'english', 'filter_ep_stop' ),
+ ],
),
'normalizer' => array(
'lowerasciinormalizer' => array(
From 2e97abadf1a9a654ac9c292a6b98792d69dcedf1 Mon Sep 17 00:00:00 2001
From: Felipe Elia
Date: Thu, 20 Jul 2023 16:33:19 -0300
Subject: [PATCH 45/45] Unit tests
---
tests/php/TestDashboard.php | 93 ++++++++++++++++++++++++++++
tests/php/indexables/TestComment.php | 28 +++++++++
tests/php/indexables/TestPost.php | 28 +++++++++
tests/php/indexables/TestTerm.php | 28 +++++++++
tests/php/indexables/TestUser.php | 27 ++++++++
5 files changed, 204 insertions(+)
create mode 100644 tests/php/TestDashboard.php
diff --git a/tests/php/TestDashboard.php b/tests/php/TestDashboard.php
new file mode 100644
index 0000000000..b9b697e765
--- /dev/null
+++ b/tests/php/TestDashboard.php
@@ -0,0 +1,93 @@
+assertSame( 'english', Dashboard\use_language_in_setting() );
+
+ $existing_lang = function () {
+ return 'ar';
+ };
+ add_filter( 'ep_default_language', $existing_lang );
+ $this->assertSame( 'arabic', Dashboard\use_language_in_setting() );
+
+ $existing_lang = function () {
+ return 'non-existent';
+ };
+ add_filter( 'ep_default_language', $existing_lang );
+ $this->assertSame( 'english', Dashboard\use_language_in_setting() );
+ }
+
+ /**
+ * Test the default behavior of the `use_language_in_setting` function for the `filter_ewp_snowball` context
+ *
+ * @group dashboard
+ */
+ public function test_use_language_in_setting_for_snowball() {
+ $this->assertSame( 'English', Dashboard\use_language_in_setting( '', 'filter_ewp_snowball' ) );
+
+ $existing_lang = function () {
+ return 'hy';
+ };
+ add_filter( 'ep_default_language', $existing_lang );
+ $this->assertSame( 'Armenian', Dashboard\use_language_in_setting( '', 'filter_ewp_snowball' ) );
+
+ $existing_lang = function () {
+ return 'non-existent';
+ };
+ add_filter( 'ep_default_language', $existing_lang );
+ $this->assertSame( 'English', Dashboard\use_language_in_setting( '', 'filter_ewp_snowball' ) );
+ }
+
+ /**
+ * Test the default behavior of the `use_language_in_setting` function for the `filter_ewp_snowball` context
+ *
+ * @group dashboard
+ */
+ public function test_use_language_in_setting_for_stop() {
+ $this->assertSame( '_english_', Dashboard\use_language_in_setting( '', 'filter_ep_stop' ) );
+
+ $existing_lang = function () {
+ return 'ar';
+ };
+ add_filter( 'ep_default_language', $existing_lang );
+ $this->assertSame( '_arabic_', Dashboard\use_language_in_setting( '', 'filter_ep_stop' ) );
+
+ $existing_lang = function () {
+ return 'non-existent';
+ };
+ add_filter( 'ep_default_language', $existing_lang );
+ $this->assertSame( '_english_', Dashboard\use_language_in_setting( '', 'filter_ep_stop' ) );
+ }
+}
diff --git a/tests/php/indexables/TestComment.php b/tests/php/indexables/TestComment.php
index 815b6bfd22..dbfa2fa7ef 100644
--- a/tests/php/indexables/TestComment.php
+++ b/tests/php/indexables/TestComment.php
@@ -2457,4 +2457,32 @@ public function testCommentIndexingWithProtectedContentEnabled() {
$this->assertCount( 2, $comments );
}
+
+ /**
+ * Test if the mapping applies the ep_stop filter correctly
+ *
+ * @since 4.7.0
+ * @group comments
+ */
+ public function test_mapping_ep_stop_filter() {
+ $indexable = ElasticPress\Indexables::factory()->get( 'comment' );
+ $index_name = $indexable->get_index_name();
+ $settings = ElasticPress\Elasticsearch::factory()->get_index_settings( $index_name );
+ $index_settings = $settings[ $index_name ]['settings'];
+
+ $this->assertContains( 'ep_stop', $index_settings['index.analysis.analyzer.default.filter'] );
+ $this->assertSame( '_english_', $index_settings['index.analysis.filter.ep_stop.stopwords'] );
+
+ $change_lang = function( $lang, $context ) {
+ return 'filter_ep_stop' === $context ? '_arabic_' : $lang;
+ };
+ add_filter( 'ep_analyzer_language', $change_lang, 11, 2 );
+
+ ElasticPress\Elasticsearch::factory()->delete_all_indices();
+ $indexable->put_mapping();
+
+ $settings = ElasticPress\Elasticsearch::factory()->get_index_settings( $index_name );
+ $index_settings = $settings[ $index_name ]['settings'];
+ $this->assertSame( '_arabic_', $index_settings['index.analysis.filter.ep_stop.stopwords'] );
+ }
}
diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php
index 566fb959de..ca9e65c7a9 100644
--- a/tests/php/indexables/TestPost.php
+++ b/tests/php/indexables/TestPost.php
@@ -8990,4 +8990,32 @@ public function testKillSyncForPasswordProtected() {
add_filter( 'ep_pre_kill_sync_for_password_protected', $dont_kill_pw_post, 10, 3 );
$this->assertFalse( $sync_manager->kill_sync_for_password_protected( false, $pw_post ) );
}
+
+ /**
+ * Test if the mapping applies the ep_stop filter correctly
+ *
+ * @since 4.7.0
+ * @group post
+ */
+ public function test_mapping_ep_stop_filter() {
+ $indexable = ElasticPress\Indexables::factory()->get( 'post' );
+ $index_name = $indexable->get_index_name();
+ $settings = ElasticPress\Elasticsearch::factory()->get_index_settings( $index_name );
+ $index_settings = $settings[ $index_name ]['settings'];
+
+ $this->assertContains( 'ep_stop', $index_settings['index.analysis.analyzer.default.filter'] );
+ $this->assertSame( '_english_', $index_settings['index.analysis.filter.ep_stop.stopwords'] );
+
+ $change_lang = function( $lang, $context ) {
+ return 'filter_ep_stop' === $context ? '_arabic_' : $lang;
+ };
+ add_filter( 'ep_analyzer_language', $change_lang, 11, 2 );
+
+ ElasticPress\Elasticsearch::factory()->delete_all_indices();
+ $indexable->put_mapping();
+
+ $settings = ElasticPress\Elasticsearch::factory()->get_index_settings( $index_name );
+ $index_settings = $settings[ $index_name ]['settings'];
+ $this->assertSame( '_arabic_', $index_settings['index.analysis.filter.ep_stop.stopwords'] );
+ }
}
diff --git a/tests/php/indexables/TestTerm.php b/tests/php/indexables/TestTerm.php
index ab5f9a8981..6696d558c6 100644
--- a/tests/php/indexables/TestTerm.php
+++ b/tests/php/indexables/TestTerm.php
@@ -1722,4 +1722,32 @@ public function testQueryForNonPublicTaxonomies() {
$this->assertArrayNotHasKey( 'elasticsearch_success', $properties );
$this->assertEquals( 4, count( $term_query->terms ) );
}
+
+ /**
+ * Test if the mapping applies the ep_stop filter correctly
+ *
+ * @since 4.7.0
+ * @group term
+ */
+ public function test_mapping_ep_stop_filter() {
+ $indexable = ElasticPress\Indexables::factory()->get( 'term' );
+ $index_name = $indexable->get_index_name();
+ $settings = ElasticPress\Elasticsearch::factory()->get_index_settings( $index_name );
+ $index_settings = $settings[ $index_name ]['settings'];
+
+ $this->assertContains( 'ep_stop', $index_settings['index.analysis.analyzer.default.filter'] );
+ $this->assertSame( '_english_', $index_settings['index.analysis.filter.ep_stop.stopwords'] );
+
+ $change_lang = function( $lang, $context ) {
+ return 'filter_ep_stop' === $context ? '_arabic_' : $lang;
+ };
+ add_filter( 'ep_analyzer_language', $change_lang, 11, 2 );
+
+ ElasticPress\Elasticsearch::factory()->delete_all_indices();
+ $indexable->put_mapping();
+
+ $settings = ElasticPress\Elasticsearch::factory()->get_index_settings( $index_name );
+ $index_settings = $settings[ $index_name ]['settings'];
+ $this->assertSame( '_arabic_', $index_settings['index.analysis.filter.ep_stop.stopwords'] );
+ }
}
diff --git a/tests/php/indexables/TestUser.php b/tests/php/indexables/TestUser.php
index 99cf28dde3..88353f5c6b 100644
--- a/tests/php/indexables/TestUser.php
+++ b/tests/php/indexables/TestUser.php
@@ -1599,4 +1599,31 @@ public function testQueryDb() {
$this->assertEquals( $user_1, $results['objects'][0]->ID );
}
+ /**
+ * Test if the mapping applies the ep_stop filter correctly
+ *
+ * @since 4.7.0
+ * @group user
+ */
+ public function test_mapping_ep_stop_filter() {
+ $indexable = ElasticPress\Indexables::factory()->get( 'user' );
+ $index_name = $indexable->get_index_name();
+ $settings = ElasticPress\Elasticsearch::factory()->get_index_settings( $index_name );
+ $index_settings = $settings[ $index_name ]['settings'];
+
+ $this->assertContains( 'ep_stop', $index_settings['index.analysis.analyzer.default.filter'] );
+ $this->assertSame( '_english_', $index_settings['index.analysis.filter.ep_stop.stopwords'] );
+
+ $change_lang = function( $lang, $context ) {
+ return 'filter_ep_stop' === $context ? '_arabic_' : $lang;
+ };
+ add_filter( 'ep_analyzer_language', $change_lang, 11, 2 );
+
+ ElasticPress\Elasticsearch::factory()->delete_all_indices();
+ $indexable->put_mapping();
+
+ $settings = ElasticPress\Elasticsearch::factory()->get_index_settings( $index_name );
+ $index_settings = $settings[ $index_name ]['settings'];
+ $this->assertSame( '_arabic_', $index_settings['index.analysis.filter.ep_stop.stopwords'] );
+ }
}