← All changes
|
includes/classes/Feature/SearchOrdering/SearchOrdering.php
+46
-137
4.4.1
→
5.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. |
| @@ -36,10 +36,14 @@ | ||
| 36 | 36 | const TAXONOMY_NAME = 'ep_custom_result'; |
| 37 | 37 | |
| 38 | 38 | /** |
| 39 | 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 | |
| 40 | 44 | */ |
| 41 | - const CAPABILITY = 'manage_options'; | |
| 45 | + const CAPABILITY = 'elasticpress_manage'; | |
| 42 | 46 | |
| 43 | 47 | /** |
| 44 | 48 | * Initialize feature setting it's config |
| 45 | 49 | * |
| @@ -47,17 +51,29 @@ | ||
| 47 | 51 | */ |
| 48 | 52 | public function __construct() { |
| 49 | 53 | $this->slug = 'searchordering'; |
| 50 | 54 | |
| 51 | - $this->title = esc_html__( 'Custom Search Results', 'elasticpress' ); | |
| 55 | + $this->group = 'core-search'; | |
| 52 | 56 | |
| 53 | - $this->summary = __( 'Insert specific posts into search results for specific search queries.', 'elasticpress' ); | |
| 57 | + $this->requires_install_reindex = false; | |
| 54 | 58 | |
| 55 | - $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'; | |
| 56 | 60 | |
| 57 | - $this->requires_install_reindex = false; | |
| 61 | + parent::__construct(); | |
| 62 | + } | |
| 58 | 63 | |
| 59 | - 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' ); | |
| 60 | 76 | } |
| 61 | 77 | |
| 62 | 78 | /** |
| 63 | 79 | * Setup Feature Functionality |
| @@ -68,9 +84,13 @@ | ||
| 68 | 84 | |
| 69 | 85 | /** Search Feature @var Feature\Search\Search $search */ |
| 70 | 86 | $search = $features->get_registered_feature( 'search' ); |
| 71 | 87 | |
| 72 | - 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() ) ) { | |
| 73 | 93 | $features->deactivate_feature( $this->slug ); |
| 74 | 94 | return false; |
| 75 | 95 | } |
| 76 | 96 | |
| @@ -170,32 +190,13 @@ | ||
| 170 | 190 | * Requires the search feature to be activated |
| 171 | 191 | * |
| 172 | 192 | * @return FeatureRequirementsStatus |
| 173 | 193 | */ |
| 174 | - public function requirements_status() { | |
| 175 | - /** Features Class @var Features $features */ | |
| 176 | - $features = Features::factory(); | |
| 177 | - | |
| 178 | - /** Search Feature @var Feature\Search\Search $search */ | |
| 179 | - $search = $features->get_registered_feature( 'search' ); | |
| 180 | - | |
| 181 | - if ( ! $search->is_active() ) { | |
| 182 | - return new FeatureRequirementsStatus( 2, esc_html__( 'This feature requires the "Post Search" feature to be enabled', 'elasticpress' ) ); | |
| 183 | - } | |
| 184 | - | |
| 185 | - return parent::requirements_status(); | |
| 194 | + public function requirements_status(): FeatureRequirementsStatus { | |
| 195 | + return new FeatureRequirementsStatus( 0, null, $this ); | |
| 186 | 196 | } |
| 187 | 197 | |
| 188 | 198 | /** |
| 189 | - * Output feature box long | |
| 190 | - */ | |
| 191 | - public function output_feature_box_long() { | |
| 192 | - ?> | |
| 193 | - <p><?php esc_html_e( 'Selected posts will be inserted into search results in the specified position.', 'elasticpress' ); ?></p> | |
| 194 | - <?php | |
| 195 | - } | |
| 196 | - | |
| 197 | - /** | |
| 198 | 199 | * Adds this taxonomy as one of the taxonomies to index |
| 199 | 200 | * |
| 200 | 201 | * @param array $taxonomies Current indexable taxonomies |
| 201 | 202 | * |
| @@ -214,9 +215,9 @@ | ||
| 214 | 215 | add_submenu_page( |
| 215 | 216 | 'elasticpress', |
| 216 | 217 | esc_html__( 'Custom Results', 'elasticpress' ), |
| 217 | 218 | esc_html__( 'Custom Results', 'elasticpress' ), |
| 218 | - self::CAPABILITY, | |
| 219 | + Utils\get_capability( 'search-ordering' ), | |
| 219 | 220 | 'edit.php?post_type=' . self::POST_TYPE_NAME |
| 220 | 221 | ); |
| 221 | 222 | } |
| 222 | 223 | |
| @@ -267,11 +268,8 @@ | ||
| 267 | 268 | /** |
| 268 | 269 | * Registers the pointer post type for the injected results |
| 269 | 270 | */ |
| 270 | 271 | public function register_post_type() { |
| 271 | - $is_network = defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK; | |
| 272 | - $menu = $is_network ? null : false; | |
| 273 | - | |
| 274 | 272 | $labels = array( |
| 275 | 273 | 'name' => esc_html_x( 'Custom Search Results', 'post type general name', 'elasticpress' ), |
| 276 | 274 | 'singular_name' => esc_html_x( 'Custom Search Result', 'post type singular name', 'elasticpress' ), |
| 277 | 275 | 'menu_name' => esc_html_x( 'Custom Search Results', 'admin menu', 'elasticpress' ), |
| @@ -293,18 +291,18 @@ | ||
| 293 | 291 | 'description' => esc_html__( 'Posts to inject into search results', 'elasticpress' ), |
| 294 | 292 | 'public' => false, |
| 295 | 293 | 'publicly_queryable' => false, |
| 296 | 294 | 'show_ui' => true, |
| 297 | - 'show_in_menu' => $menu, | |
| 295 | + 'show_in_menu' => false, | |
| 298 | 296 | 'query_var' => true, |
| 299 | 297 | 'rewrite' => array( 'slug' => 'ep-pointer' ), |
| 300 | - 'capability_type' => 'post', | |
| 298 | + 'capabilities' => Utils\get_post_map_capabilities( 'search-ordering' ), | |
| 301 | 299 | 'has_archive' => false, |
| 302 | 300 | 'hierarchical' => false, |
| 303 | 301 | 'menu_position' => 100, |
| 304 | 302 | 'supports' => [ 'title' ], |
| 305 | 303 | 'register_meta_box_cb' => [ $this, 'register_meta_box' ], |
| 306 | - 'menu_icon' => 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNzMgNzEuMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNzMgNzEuMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik0zNi41LDQuN0MxOS40LDQuNyw1LjYsMTguNiw1LjYsMzUuN2MwLDEwLDQuNywxOC45LDEyLjEsMjQuNWw0LjUtNC41YzAuMS0wLjEsMC4xLTAuMiwwLjItMC4zbDAuNy0wLjdsNi40LTYuNGMyLjEsMS4yLDQuNSwxLjksNy4xLDEuOWM4LDAsMTQuNS02LjUsMTQuNS0xNC41cy02LjUtMTQuNS0xNC41LTE0LjVTMjIsMjcuNiwyMiwzNS42YzAsMi44LDAuOCw1LjMsMi4xLDcuNWwtNi40LDYuNGMtMi45LTMuOS00LjYtOC43LTQuNi0xMy45YzAtMTIuOSwxMC41LTIzLjQsMjMuNC0yMy40czIzLjQsMTAuNSwyMy40LDIzLjRTNDkuNCw1OSwzNi41LDU5Yy0yLjEsMC00LjEtMC4zLTYtMC44bC0wLjYsMC42bC01LjIsNS40YzMuNiwxLjUsNy42LDIuMywxMS44LDIuM2MxNy4xLDAsMzAuOS0xMy45LDMwLjktMzAuOVM1My42LDQuNywzNi41LDQuN3oiLz48L3N2Zz4=', | |
| 304 | + 'menu_icon' => 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNzMgNzEuMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNzMgNzEuMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik0zNi41LDQuN0MxOS40LDQuNyw1LjYsMTguNiw1LjYsMzUuN2MwLDEwLDQuNywxOC45LDEyLjEsMjQuNWw0LjUtNC41YzAuMS0wLjEsMC4xLTAuMiwwLjItMC4zbDAuNy0wLjdsNi40LTYuNGMyLjEsMS4yLDQuNSwxLjksNy4xLDEuOWM4LDAsMTQuNS02LjUsMTQuNS0xNC41cy02LjUtMTQuNS0xNC41LTE0LjVTMjIsMjcuNiwyMiwzNS42YzAsMi44LDAuOCw1LjMsMi4xLDcuNWwtNi40LDYuNGMtMi45LTMuOS00LjYtOC43LTQuNi0xMy45YzAtMTIuOSwxMC41LTIzLjQsMjMuNC0yMy40czIzLjQsMTAuNSwyMy40LDIzLjRTNDkuNCw1OSwzNi41LDU5Yy0yLjEsMC00LjEtMC4zLTYtMC44bC0wLjYsMC42bC01LjIsNS40YzMuNiwxLjUsNy42LDIuMywxMS44LDIuM2MxNy4xLDAsMzAuOS0xMy45LDMwLjktMzAuOVM1My42LDQuNywzNi41LDQuN3oiIGZpbGw9IiNhN2FhYWQiLz48L3N2Zz4=', | |
| 307 | 305 | ); |
| 308 | 306 | |
| 309 | 307 | register_post_type( self::POST_TYPE_NAME, $args ); |
| 310 | 308 | |
| @@ -329,8 +327,9 @@ | ||
| 329 | 327 | 'show_ui' => false, |
| 330 | 328 | 'show_admin_column' => false, |
| 331 | 329 | 'query_var' => false, |
| 332 | 330 | 'rewrite' => false, |
| 331 | + 'public' => false, | |
| 333 | 332 | ); |
| 334 | 333 | |
| 335 | 334 | /** Features Class @var Features $features */ |
| 336 | 335 | $features = Features::factory(); |
| @@ -457,9 +456,9 @@ | ||
| 457 | 456 | public function save_post( $post_id, $post ) { |
| 458 | 457 | /** Post Indexable @var Post $post_indexable */ |
| 459 | 458 | $post_indexable = Indexables::factory()->get( 'post' ); |
| 460 | 459 | |
| 461 | - 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' ) ) { | |
| 462 | 461 | return; |
| 463 | 462 | } |
| 464 | 463 | |
| 465 | 464 | if ( ! current_user_can( 'edit_post', $post_id ) ) { |
| @@ -471,9 +470,10 @@ | ||
| 471 | 470 | // Track the old IDs that aren't retained so we can delete the terms later |
| 472 | 471 | $previous_order_data = get_post_meta( $post_id, 'pointers', true ); |
| 473 | 472 | $previous_post_ids = ! empty( $previous_order_data ) ? array_flip( wp_list_pluck( $previous_order_data, 'ID' ) ) : []; |
| 474 | 473 | |
| 475 | - $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 ); | |
| 476 | 476 | |
| 477 | 477 | $posts_per_page = (int) get_option( 'posts_per_page', 10 ); |
| 478 | 478 | |
| 479 | 479 | $old_search_term = get_post_meta( $post->ID, 'search_term', true ); |
| @@ -492,8 +492,9 @@ | ||
| 492 | 492 | if ( intval( $order_data['order'] ) <= $posts_per_page ) { |
| 493 | 493 | $final_order_data[] = [ |
| 494 | 494 | 'ID' => intval( $order_data['ID'] ), |
| 495 | 495 | 'order' => intval( $order_data['order'] ), |
| 496 | + 'type' => ! empty( $order_data['type'] ) ? sanitize_text_field( $order_data['type'] ) : 'reordered', | |
| 496 | 497 | ]; |
| 497 | 498 | } else { |
| 498 | 499 | $previous_post_ids[ intval( $order_data['ID'] ) ] = true; |
| 499 | 500 | } |
| @@ -653,15 +654,14 @@ | ||
| 653 | 654 | |
| 654 | 655 | $to_inject = array(); |
| 655 | 656 | |
| 656 | 657 | foreach ( $posts as $key => &$post ) { |
| 657 | - if ( isset( $post->terms ) && isset( $post->terms[ self::TAXONOMY_NAME ] ) ) { | |
| 658 | - 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 ) { | |
| 659 | 661 | if ( strtolower( $current_term['name'] ) === $search_query ) { |
| 660 | 662 | $to_inject[ $current_term['term_order'] ] = $post; |
| 661 | - | |
| 662 | 663 | unset( $posts[ $key ] ); |
| 663 | - | |
| 664 | 664 | break; |
| 665 | 665 | } |
| 666 | 666 | } |
| 667 | 667 | } |
| @@ -689,103 +689,13 @@ | ||
| 689 | 689 | /** |
| 690 | 690 | * Registers the API endpoint for searching from the admin interface |
| 691 | 691 | */ |
| 692 | 692 | public function rest_api_init() { |
| 693 | - register_rest_route( | |
| 694 | - 'elasticpress/v1', | |
| 695 | - 'pointer_search', | |
| 696 | - [ | |
| 697 | - 'methods' => 'GET', | |
| 698 | - 'callback' => [ $this, 'handle_pointer_search' ], | |
| 699 | - 'permission_callback' => function() { | |
| 700 | - return current_user_can( self::CAPABILITY ); | |
| 701 | - }, | |
| 702 | - 'args' => [ | |
| 703 | - 's' => [ | |
| 704 | - 'validate_callback' => function ( $param ) { | |
| 705 | - return ! empty( $param ); | |
| 706 | - }, | |
| 707 | - 'required' => true, | |
| 708 | - ], | |
| 709 | - ], | |
| 710 | - ] | |
| 711 | - ); | |
| 712 | - | |
| 713 | - register_rest_route( | |
| 714 | - 'elasticpress/v1', | |
| 715 | - 'pointer_preview', | |
| 716 | - [ | |
| 717 | - 'methods' => 'GET', | |
| 718 | - 'callback' => [ $this, 'handle_pointer_preview' ], | |
| 719 | - 'permission_callback' => function() { | |
| 720 | - return current_user_can( self::CAPABILITY ); | |
| 721 | - }, | |
| 722 | - 'args' => [ | |
| 723 | - 's' => [ | |
| 724 | - 'validate_callback' => function ( $param ) { | |
| 725 | - return ! empty( $param ); | |
| 726 | - }, | |
| 727 | - 'required' => true, | |
| 728 | - ], | |
| 729 | - ], | |
| 730 | - ] | |
| 731 | - ); | |
| 693 | + $controller = new REST\SearchOrdering(); | |
| 694 | + $controller->register_routes(); | |
| 732 | 695 | } |
| 733 | 696 | |
| 734 | 697 | /** |
| 735 | - * Handles the search for posts from the admin interface for the post type | |
| 736 | - * | |
| 737 | - * @param \WP_REST_Request $request Rest request | |
| 738 | - * | |
| 739 | - * @return array | |
| 740 | - */ | |
| 741 | - public function handle_pointer_search( $request ) { | |
| 742 | - $search = $request->get_param( 's' ); | |
| 743 | - | |
| 744 | - /** Features Class @var Features $features */ | |
| 745 | - $features = Features::factory(); | |
| 746 | - | |
| 747 | - /** Search Feature @var Feature\Search\Search $search */ | |
| 748 | - $search_feature = $features->get_registered_feature( 'search' ); | |
| 749 | - | |
| 750 | - $post_types = $search_feature->get_searchable_post_types(); | |
| 751 | - | |
| 752 | - $query = new \WP_Query( | |
| 753 | - [ | |
| 754 | - 'post_type' => $post_types, | |
| 755 | - 'post_status' => 'publish', | |
| 756 | - 's' => $search, | |
| 757 | - ] | |
| 758 | - ); | |
| 759 | - | |
| 760 | - return $query->posts; | |
| 761 | - } | |
| 762 | - | |
| 763 | - /** | |
| 764 | - * Handles the search preview on the pointer edit screen | |
| 765 | - * | |
| 766 | - * @param \WP_REST_Request $request Rest request | |
| 767 | - * | |
| 768 | - * @return array | |
| 769 | - */ | |
| 770 | - public function handle_pointer_preview( $request ) { | |
| 771 | - remove_filter( 'ep_searchable_post_types', [ $this, 'searchable_post_types' ] ); | |
| 772 | - | |
| 773 | - $search = $request->get_param( 's' ); | |
| 774 | - | |
| 775 | - $query = new \WP_Query( | |
| 776 | - [ | |
| 777 | - 's' => $search, | |
| 778 | - 'exclude_pointers' => true, | |
| 779 | - ] | |
| 780 | - ); | |
| 781 | - | |
| 782 | - add_filter( 'ep_searchable_post_types', [ $this, 'searchable_post_types' ] ); | |
| 783 | - | |
| 784 | - return $query->posts; | |
| 785 | - } | |
| 786 | - | |
| 787 | - /** | |
| 788 | 698 | * Removes taxonomy terms from the references posts when a pointer is deleted or trashed |
| 789 | 699 | * |
| 790 | 700 | * @param int $post_id Post ID that is being deleted |
| 791 | 701 | */ |
| @@ -852,9 +762,9 @@ | ||
| 852 | 762 | */ |
| 853 | 763 | protected function assign_term_to_post( $post_id, $term_taxonomy_id, $order ) { |
| 854 | 764 | global $wpdb; |
| 855 | 765 | |
| 856 | - $result = $wpdb->query( | |
| 766 | + $result = $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery | |
| 857 | 767 | $wpdb->prepare( |
| 858 | 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)", |
| 859 | 769 | $post_id, |
| 860 | 770 | $term_taxonomy_id, |
| @@ -885,6 +795,5 @@ | ||
| 885 | 795 | } |
| 886 | 796 | |
| 887 | 797 | return $admin_title; |
| 888 | 798 | } |
| 889 | - | |
| 890 | 799 | } |