PluginProbe
ElasticPress / 5.3.5
ElasticPress v5.3.5
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
← All changes | includes/classes/Feature/SearchOrdering/SearchOrdering.php +63 -139 4.2.05.3.5 View file →
@@ -7,12 +7,12 @@
7 7
8 8 namespace ElasticPress\Feature\SearchOrdering;
9 9
10 10 use ElasticPress\Feature;
11 -use ElasticPress\FeatureRequirementsStatus as FeatureRequirementsStatus;
11 +use ElasticPress\FeatureRequirementsStatus;
12 12 use ElasticPress\Features;
13 -use ElasticPress\Indexable\Post\Post;
14 13 use ElasticPress\Indexables;
14 +use ElasticPress\REST;
15 15 use ElasticPress\Utils;
16 16
17 17 if ( ! defined( 'ABSPATH' ) ) {
18 18 exit; // Exit if accessed directly.
@@ -35,8 +35,17 @@
35 35 */
36 36 const TAXONOMY_NAME = 'ep_custom_result';
37 37
38 38 /**
39 + * Capability required to manage.
40 + *
41 + * This will be removed in future versions of ElasticPress. Please use `Utils\get_capability()` instead.
42 + *
43 + * @deprecated 4.5.0
44 + */
45 + const CAPABILITY = 'elasticpress_manage';
46 +
47 + /**
39 48 * Initialize feature setting it's config
40 49 *
41 50 * @since 3.0
42 51 */
@@ -42,17 +51,29 @@
42 51 */
43 52 public function __construct() {
44 53 $this->slug = 'searchordering';
45 54
46 - $this->title = esc_html__( 'Custom Search Results', 'elasticpress' );
55 + $this->group = 'core-search';
47 56
48 - $this->summary = __( 'Insert specific posts into search results for specific search queries.', 'elasticpress' );
57 + $this->requires_install_reindex = false;
49 58
50 - $this->docs_url = __( 'https://elasticpress.zendesk.com/hc/en-us/articles/360050447492-Configuring-ElasticPress-via-the-Plugin-Dashboard#custom-search-results', 'elasticpress' );
59 + $this->requires_feature = 'search';
51 60
52 - $this->requires_install_reindex = false;
61 + parent::__construct();
62 + }
53 63
54 - parent::__construct();
64 + /**
65 + * Sets i18n strings.
66 + *
67 + * @return void
68 + * @since 5.2.0
69 + */
70 + public function set_i18n_strings(): void {
71 + $this->title = esc_html__( 'Custom Search Results', 'elasticpress' );
72 +
73 + $this->summary = '<p>' . __( 'Selected posts will be inserted into search results in the specified position.', 'elasticpress' ) . '</p>';
74 +
75 + $this->docs_url = __( 'https://www.elasticpress.io/resources/articles/configuring-elasticpress-via-the-plugin-dashboard/#custom-search-results', 'elasticpress' );
55 76 }
56 77
57 78 /**
58 79 * Setup Feature Functionality
@@ -63,9 +84,13 @@
63 84
64 85 /** Search Feature @var Feature\Search\Search $search */
65 86 $search = $features->get_registered_feature( 'search' );
66 87
67 - if ( ! $search->is_active() && $this->is_active() ) {
88 + if ( ! Utils\is_site_indexable() ) {
89 + return false;
90 + }
91 +
92 + if ( ( ! $search->is_active() && $this->is_active() ) ) {
68 93 $features->deactivate_feature( $this->slug );
69 94 return false;
70 95 }
71 96
@@ -149,9 +174,9 @@
149 174 8 => esc_html__( 'Custom result submitted.', 'elasticpress' ),
150 175 9 => sprintf(
151 176 // translators: Scheduled date.
152 177 esc_html__( 'Custom result scheduled for: %1$s.', 'elasticpress' ),
153 - // translators: Publish box date format, see http://php.net/date
178 + // translators: Publish box date format, see https://php.net/date
154 179 date_i18n( esc_html__( 'M j, Y @ G:i', 'elasticpress' ), strtotime( $post->post_date ) )
155 180 ),
156 181 10 => esc_html__( 'Custom result draft updated.', 'elasticpress' ),
157 182 );
@@ -165,32 +190,13 @@
165 190 * Requires the search feature to be activated
166 191 *
167 192 * @return FeatureRequirementsStatus
168 193 */
169 - public function requirements_status() {
170 - /** Features Class @var Features $features */
171 - $features = Features::factory();
172 -
173 - /** Search Feature @var Feature\Search\Search $search */
174 - $search = $features->get_registered_feature( 'search' );
175 -
176 - if ( ! $search->is_active() ) {
177 - return new FeatureRequirementsStatus( 2, esc_html__( 'This feature requires the "Post Search" feature to be enabled', 'elasticpress' ) );
178 - }
179 -
180 - return parent::requirements_status();
194 + public function requirements_status(): FeatureRequirementsStatus {
195 + return new FeatureRequirementsStatus( 0, null, $this );
181 196 }
182 197
183 198 /**
184 - * Output feature box long
185 - */
186 - public function output_feature_box_long() {
187 - ?>
188 - <p><?php esc_html_e( 'Selected posts will be inserted into search results in the specified position.', 'elasticpress' ); ?></p>
189 - <?php
190 - }
191 -
192 - /**
193 199 * Adds this taxonomy as one of the taxonomies to index
194 200 *
195 201 * @param array $taxonomies Current indexable taxonomies
196 202 *
@@ -209,9 +215,9 @@
209 215 add_submenu_page(
210 216 'elasticpress',
211 217 esc_html__( 'Custom Results', 'elasticpress' ),
212 218 esc_html__( 'Custom Results', 'elasticpress' ),
213 - 'manage_options',
219 + Utils\get_capability( 'search-ordering' ),
214 220 'edit.php?post_type=' . self::POST_TYPE_NAME
215 221 );
216 222 }
217 223
@@ -262,11 +268,8 @@
262 268 /**
263 269 * Registers the pointer post type for the injected results
264 270 */
265 271 public function register_post_type() {
266 - $is_network = defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK;
267 - $menu = $is_network ? null : false;
268 -
269 272 $labels = array(
270 273 'name' => esc_html_x( 'Custom Search Results', 'post type general name', 'elasticpress' ),
271 274 'singular_name' => esc_html_x( 'Custom Search Result', 'post type singular name', 'elasticpress' ),
272 275 'menu_name' => esc_html_x( 'Custom Search Results', 'admin menu', 'elasticpress' ),
@@ -288,18 +291,18 @@
288 291 'description' => esc_html__( 'Posts to inject into search results', 'elasticpress' ),
289 292 'public' => false,
290 293 'publicly_queryable' => false,
291 294 'show_ui' => true,
292 - 'show_in_menu' => $menu,
295 + 'show_in_menu' => false,
293 296 'query_var' => true,
294 297 'rewrite' => array( 'slug' => 'ep-pointer' ),
295 - 'capability_type' => 'post',
298 + 'capabilities' => Utils\get_post_map_capabilities( 'search-ordering' ),
296 299 'has_archive' => false,
297 300 'hierarchical' => false,
298 301 'menu_position' => 100,
299 302 'supports' => [ 'title' ],
300 303 'register_meta_box_cb' => [ $this, 'register_meta_box' ],
301 - 'menu_icon' => 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNzMgNzEuMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNzMgNzEuMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik0zNi41LDQuN0MxOS40LDQuNyw1LjYsMTguNiw1LjYsMzUuN2MwLDEwLDQuNywxOC45LDEyLjEsMjQuNWw0LjUtNC41YzAuMS0wLjEsMC4xLTAuMiwwLjItMC4zbDAuNy0wLjdsNi40LTYuNGMyLjEsMS4yLDQuNSwxLjksNy4xLDEuOWM4LDAsMTQuNS02LjUsMTQuNS0xNC41cy02LjUtMTQuNS0xNC41LTE0LjVTMjIsMjcuNiwyMiwzNS42YzAsMi44LDAuOCw1LjMsMi4xLDcuNWwtNi40LDYuNGMtMi45LTMuOS00LjYtOC43LTQuNi0xMy45YzAtMTIuOSwxMC41LTIzLjQsMjMuNC0yMy40czIzLjQsMTAuNSwyMy40LDIzLjRTNDkuNCw1OSwzNi41LDU5Yy0yLjEsMC00LjEtMC4zLTYtMC44bC0wLjYsMC42bC01LjIsNS40YzMuNiwxLjUsNy42LDIuMywxMS44LDIuM2MxNy4xLDAsMzAuOS0xMy45LDMwLjktMzAuOVM1My42LDQuNywzNi41LDQuN3oiLz48L3N2Zz4=',
304 + 'menu_icon' => 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNzMgNzEuMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNzMgNzEuMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik0zNi41LDQuN0MxOS40LDQuNyw1LjYsMTguNiw1LjYsMzUuN2MwLDEwLDQuNywxOC45LDEyLjEsMjQuNWw0LjUtNC41YzAuMS0wLjEsMC4xLTAuMiwwLjItMC4zbDAuNy0wLjdsNi40LTYuNGMyLjEsMS4yLDQuNSwxLjksNy4xLDEuOWM4LDAsMTQuNS02LjUsMTQuNS0xNC41cy02LjUtMTQuNS0xNC41LTE0LjVTMjIsMjcuNiwyMiwzNS42YzAsMi44LDAuOCw1LjMsMi4xLDcuNWwtNi40LDYuNGMtMi45LTMuOS00LjYtOC43LTQuNi0xMy45YzAtMTIuOSwxMC41LTIzLjQsMjMuNC0yMy40czIzLjQsMTAuNSwyMy40LDIzLjRTNDkuNCw1OSwzNi41LDU5Yy0yLjEsMC00LjEtMC4zLTYtMC44bC0wLjYsMC42bC01LjIsNS40YzMuNiwxLjUsNy42LDIuMywxMS44LDIuM2MxNy4xLDAsMzAuOS0xMy45LDMwLjktMzAuOVM1My42LDQuNywzNi41LDQuN3oiIGZpbGw9IiNhN2FhYWQiLz48L3N2Zz4=',
302 305 );
303 306
304 307 register_post_type( self::POST_TYPE_NAME, $args );
305 308
@@ -324,8 +327,9 @@
324 327 'show_ui' => false,
325 328 'show_admin_column' => false,
326 329 'query_var' => false,
327 330 'rewrite' => false,
331 + 'public' => false,
328 332 );
329 333
330 334 /** Features Class @var Features $features */
331 335 $features = Features::factory();
@@ -379,19 +383,23 @@
379 383 [
380 384 'post_type' => 'any',
381 385 'post__in' => $post_ids,
382 386 'count' => count( $post_ids ),
387 + 'orderby' => 'post__in',
383 388 ]
384 389 );
385 390
386 - $final_posts = [];
391 + $final_posts = [];
392 + $filtered_pointers = [];
387 393
388 394 foreach ( $query->posts as $post ) {
389 395 $final_posts[ $post->ID ] = $post;
396 + // Add the post to filtered array. By doing this, we removed the posts that don't exist anymore.
397 + $filtered_pointers[] = $pointers[ array_search( $post->ID, $post_ids, true ) ];
390 398 }
391 399
392 400 return [
393 - 'pointers' => $pointers,
401 + 'pointers' => $filtered_pointers,
394 402 'posts' => $final_posts,
395 403 ];
396 404 }
397 405
@@ -405,17 +413,19 @@
405 413
406 414 if ( in_array( $pagenow, [ 'post-new.php', 'post.php' ], true ) && $screen instanceof \WP_Screen && self::POST_TYPE_NAME === $screen->post_type ) {
407 415 wp_enqueue_script(
408 416 'ep_ordering_scripts',
409 - EP_URL . 'dist/js/ordering-script.min.js',
417 + EP_URL . 'dist/js/ordering-script.js',
410 418 Utils\get_asset_info( 'ordering-script', 'dependencies' ),
411 419 Utils\get_asset_info( 'ordering-script', 'version' ),
412 420 true
413 421 );
414 422
423 + wp_set_script_translations( 'ep_ordering_scripts', 'elasticpress' );
424 +
415 425 wp_enqueue_style(
416 426 'ep_ordering_styles',
417 - EP_URL . 'dist/css/ordering-styles.min.css',
427 + EP_URL . 'dist/css/ordering-styles.css',
418 428 Utils\get_asset_info( 'ordering-styles', 'dependencies' ),
419 429 Utils\get_asset_info( 'ordering-styles', 'version' )
420 430 );
421 431
@@ -446,9 +456,9 @@
446 456 public function save_post( $post_id, $post ) {
447 457 /** Post Indexable @var Post $post_indexable */
448 458 $post_indexable = Indexables::factory()->get( 'post' );
449 459
450 - if ( ! isset( $_POST['search-ordering-nonce'] ) || ! wp_verify_nonce( $_POST['search-ordering-nonce'], 'save-search-ordering' ) ) {
460 + if ( ! isset( $_POST['search-ordering-nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['search-ordering-nonce'] ), 'save-search-ordering' ) ) {
451 461 return;
452 462 }
453 463
454 464 if ( ! current_user_can( 'edit_post', $post_id ) ) {
@@ -460,9 +470,10 @@
460 470 // Track the old IDs that aren't retained so we can delete the terms later
461 471 $previous_order_data = get_post_meta( $post_id, 'pointers', true );
462 472 $previous_post_ids = ! empty( $previous_order_data ) ? array_flip( wp_list_pluck( $previous_order_data, 'ID' ) ) : [];
463 473
464 - $ordered_posts = json_decode( wp_unslash( $_POST['ordered_posts'] ), true );
474 + $ordered_posts = isset( $_POST['ordered_posts'] ) ? wp_unslash( $_POST['ordered_posts'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
475 + $ordered_posts = json_decode( $ordered_posts, true );
465 476
466 477 $posts_per_page = (int) get_option( 'posts_per_page', 10 );
467 478
468 479 $old_search_term = get_post_meta( $post->ID, 'search_term', true );
@@ -481,8 +492,9 @@
481 492 if ( intval( $order_data['order'] ) <= $posts_per_page ) {
482 493 $final_order_data[] = [
483 494 'ID' => intval( $order_data['ID'] ),
484 495 'order' => intval( $order_data['order'] ),
496 + 'type' => ! empty( $order_data['type'] ) ? sanitize_text_field( $order_data['type'] ) : 'reordered',
485 497 ];
486 498 } else {
487 499 $previous_post_ids[ intval( $order_data['ID'] ) ] = true;
488 500 }
@@ -642,15 +654,14 @@
642 654
643 655 $to_inject = array();
644 656
645 657 foreach ( $posts as $key => &$post ) {
646 - if ( isset( $post->terms ) && isset( $post->terms[ self::TAXONOMY_NAME ] ) ) {
647 - foreach ( $post->terms[ self::TAXONOMY_NAME ] as $current_term ) {
658 + $terms = is_string( $post->terms ) ? json_decode( wp_specialchars_decode( $post->terms, ENT_QUOTES ), true ) : (array) $post->terms;
659 + if ( isset( $terms[ self::TAXONOMY_NAME ] ) ) {
660 + foreach ( $terms[ self::TAXONOMY_NAME ] as $current_term ) {
648 661 if ( strtolower( $current_term['name'] ) === $search_query ) {
649 - $to_inject[ $current_term['term_order'] ] = $post->ID;
650 -
662 + $to_inject[ $current_term['term_order'] ] = $post;
651 663 unset( $posts[ $key ] );
652 -
653 664 break;
654 665 }
655 666 }
656 667 }
@@ -663,9 +674,9 @@
663 674 ksort( $to_inject );
664 675
665 676 if ( ! empty( $to_inject ) ) {
666 677 foreach ( $to_inject as $position => $newpost ) {
667 - array_splice( $posts, $position - 1, 0, $newpost );
678 + array_splice( $posts, $position - 1, 0, array( $newpost ) );
668 679 }
669 680 }
670 681
671 682 // reindex just in case we got out of order keys
@@ -678,99 +689,13 @@
678 689 /**
679 690 * Registers the API endpoint for searching from the admin interface
680 691 */
681 692 public function rest_api_init() {
682 - register_rest_route(
683 - 'elasticpress/v1',
684 - 'pointer_search',
685 - [
686 - 'methods' => 'GET',
687 - 'callback' => [ $this, 'handle_pointer_search' ],
688 - 'permission_callback' => '__return_true',
689 - 'args' => [
690 - 's' => [
691 - 'validate_callback' => function ( $param ) {
692 - return ! empty( $param );
693 - },
694 - 'required' => true,
695 - ],
696 - ],
697 - ]
698 - );
699 -
700 - register_rest_route(
701 - 'elasticpress/v1',
702 - 'pointer_preview',
703 - [
704 - 'methods' => 'GET',
705 - 'callback' => [ $this, 'handle_pointer_preview' ],
706 - 'permission_callback' => '__return_true',
707 - 'args' => [
708 - 's' => [
709 - 'validate_callback' => function ( $param ) {
710 - return ! empty( $param );
711 - },
712 - 'required' => true,
713 - ],
714 - ],
715 - ]
716 - );
693 + $controller = new REST\SearchOrdering();
694 + $controller->register_routes();
717 695 }
718 696
719 697 /**
720 - * Handles the search for posts from the admin interface for the post type
721 - *
722 - * @param \WP_REST_Request $request Rest request
723 - *
724 - * @return array
725 - */
726 - public function handle_pointer_search( $request ) {
727 - $search = $request->get_param( 's' );
728 -
729 - /** Features Class @var Features $features */
730 - $features = Features::factory();
731 -
732 - /** Search Feature @var Feature\Search\Search $search */
733 - $search_feature = $features->get_registered_feature( 'search' );
734 -
735 - $post_types = $search_feature->get_searchable_post_types();
736 -
737 - $query = new \WP_Query(
738 - [
739 - 'post_type' => $post_types,
740 - 'post_status' => 'publish',
741 - 's' => $search,
742 - ]
743 - );
744 -
745 - return $query->posts;
746 - }
747 -
748 - /**
749 - * Handles the search preview on the pointer edit screen
750 - *
751 - * @param \WP_REST_Request $request Rest request
752 - *
753 - * @return array
754 - */
755 - public function handle_pointer_preview( $request ) {
756 - remove_filter( 'ep_searchable_post_types', [ $this, 'searchable_post_types' ] );
757 -
758 - $search = $request->get_param( 's' );
759 -
760 - $query = new \WP_Query(
761 - [
762 - 's' => $search,
763 - 'exclude_pointers' => true,
764 - ]
765 - );
766 -
767 - add_filter( 'ep_searchable_post_types', [ $this, 'searchable_post_types' ] );
768 -
769 - return $query->posts;
770 - }
771 -
772 - /**
773 698 * Removes taxonomy terms from the references posts when a pointer is deleted or trashed
774 699 *
775 700 * @param int $post_id Post ID that is being deleted
776 701 */
@@ -837,9 +762,9 @@
837 762 */
838 763 protected function assign_term_to_post( $post_id, $term_taxonomy_id, $order ) {
839 764 global $wpdb;
840 765
841 - $result = $wpdb->query(
766 + $result = $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
842 767 $wpdb->prepare(
843 768 "INSERT INTO $wpdb->term_relationships (object_id, term_taxonomy_id, term_order) VALUES ( %d, %d, %d ) ON DUPLICATE KEY UPDATE term_order = VALUES(term_order)",
844 769 $post_id,
845 770 $term_taxonomy_id,
@@ -870,6 +795,5 @@
870 795 }
871 796
872 797 return $admin_title;
873 798 }
874 -
875 799 }