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/Search/Synonyms.php +127 -44 4.5.05.3.5 View file →
@@ -6,14 +6,14 @@
6 6 */
7 7
8 8 namespace ElasticPress\Feature\Search;
9 9
10 -use ElasticPress\Feature;
10 +use ElasticPress\Elasticsearch;
11 +use ElasticPress\FeatureRequirementsStatus;
11 12 use ElasticPress\Features;
12 13 use ElasticPress\Indexables;
13 -use ElasticPress\Elasticsearch;
14 -use ElasticPress\FeatureRequirementsStatus;
15 -use ElasticPress\Utils as Utils;
14 +use ElasticPress\REST;
15 +use ElasticPress\Utils;
16 16
17 17 if ( ! defined( 'ABSPATH' ) ) {
18 18 exit; // Exit if accessed directly.
19 19 }
@@ -82,15 +82,17 @@
82 82 *
83 83 * @return FeatureRequirementsStatus
84 84 */
85 85 public function requirements_status() {
86 + $status = new FeatureRequirementsStatus( 0, null, $this );
86 87 $search = $this->get_search_feature();
87 88
88 89 if ( ! $search->is_active() ) {
89 - return new FeatureRequirementsStatus( 2, esc_html__( 'This feature requires the "Post Search" feature to be enabled', 'elasticpress' ) );
90 + $status->code = 2;
91 + $status->message = esc_html__( 'This feature requires the "Post Search" feature to be enabled', 'elasticpress' );
90 92 }
91 93
92 - return new FeatureRequirementsStatus( 0 );
94 + return $status;
93 95 }
94 96
95 97 /**
96 98 * Setup Feature Functionality
@@ -97,9 +99,9 @@
97 99 *
98 100 * @return bool
99 101 */
100 102 public function setup() {
101 - if ( (bool) $this->requirements_status()->code ) {
103 + if ( (bool) $this->requirements_status()->get_code() ) {
102 104 return false;
103 105 }
104 106
105 107 // Register a post type to hold the synonyms post.
@@ -108,18 +110,14 @@
108 110 // Setup the UI.
109 111 add_action( 'admin_menu', [ $this, 'admin_menu' ], 50 );
110 112 add_action( 'admin_enqueue_scripts', [ $this, 'scripts' ] );
111 113
112 - // Handle the update synonyms action.
113 - $action = $this->get_action();
114 - add_action( "admin_post_$action", [ $this, 'handle_update_synonyms' ] );
115 -
116 - // Handle the admin notices.
117 - add_action( 'admin_notices', [ $this, 'admin_notices' ] );
118 -
119 114 // Add the synonyms to the elasticsearch query.
120 115 add_filter( 'ep_config_mapping', [ $this, 'add_search_synonyms' ], 20, 2 );
121 116
117 + // Register REST routes.
118 + add_action( 'rest_api_init', [ $this, 'setup_endpoint' ] );
119 +
122 120 return true;
123 121 }
124 122
125 123 /**
@@ -144,8 +142,16 @@
144 142
145 143 wp_enqueue_style( 'wp-edit-post' );
146 144
147 145 wp_enqueue_style(
146 + 'ep_synonyms_scripts',
147 + EP_URL . 'dist/css/synonyms-script.css',
148 + [ 'wp-components', 'wp-edit-post' ],
149 + Utils\get_asset_info( 'synonyms-styles', 'version' ),
150 + 'all'
151 + );
152 +
153 + wp_enqueue_style(
148 154 'ep_synonyms_styles',
149 155 EP_URL . 'dist/css/synonyms-styles.css',
150 156 Utils\get_asset_info( 'synonyms-styles', 'dependencies' ),
151 157 Utils\get_asset_info( 'synonyms-styles', 'version' ),
@@ -151,15 +157,20 @@
151 157 Utils\get_asset_info( 'synonyms-styles', 'version' ),
152 158 'all'
153 159 );
154 160
161 + $api_url = rest_url( 'elasticpress/v1/synonyms' );
162 + $sync_url = Utils\get_sync_url();
163 +
155 164 wp_localize_script(
156 165 'ep_synonyms_scripts',
157 166 'epSynonyms',
158 - array(
159 - 'i18n' => $this->get_localized_strings(),
160 - 'data' => $this->get_localized_data(),
161 - )
167 + [
168 + 'apiUrl' => esc_url_raw( $api_url ),
169 + 'defaultIsSolr' => $this->synonyms_editor_mode() === 'advanced',
170 + 'defaultSolr' => $this->get_synonyms_raw(),
171 + 'syncUrl' => esc_url_raw( $sync_url ),
172 + ]
162 173 );
163 174 }
164 175
165 176 /**
@@ -171,9 +182,9 @@
171 182 add_submenu_page(
172 183 'elasticpress',
173 184 esc_html__( 'ElasticPress Synonyms', 'elasticpress' ),
174 185 esc_html__( 'Synonyms', 'elasticpress' ),
175 - Utils\get_capability(),
186 + Utils\get_capability( 'synonyms' ),
176 187 'elasticpress-synonyms',
177 188 [ $this, 'admin_page' ]
178 189 );
179 190 }
@@ -187,12 +198,9 @@
187 198 include EP_PATH . '/includes/partials/header.php';
188 199
189 200 ?>
190 201 <div class="wrap">
191 - <form action="<?php echo esc_url( $this->get_form_action() ); ?>" method="POST">
192 - <?php $this->form_hidden_fields(); ?>
193 - <div id="synonym-root"></div>
194 - </form>
202 + <div id="ep-synonyms"></div>
195 203 </div>
196 204 <?php
197 205 }
198 206
@@ -199,10 +207,13 @@
199 207 /**
200 208 * Admin notices.
201 209 *
202 210 * @return void
211 + * @deprecated 5.1.0
203 212 */
204 213 public function admin_notices() {
214 + _deprecated_function( 'ElasticPress\Feature\Search\Synonyms::admin_notices', '5.1.0' );
215 +
205 216 if ( ! $this->is_synonym_page() ) {
206 217 return;
207 218 }
208 219
@@ -248,9 +259,9 @@
248 259 'publicly_queryable' => false,
249 260 'show_ui' => false,
250 261 'show_in_menu' => false,
251 262 'query_var' => true,
252 - 'capabilities' => Utils\get_post_map_capabilities(),
263 + 'capabilities' => Utils\get_post_map_capabilities( 'synonyms' ),
253 264 'has_archive' => false,
254 265 'hierarchical' => false,
255 266 'menu_position' => 100,
256 267 'supports' => [ 'title' ],
@@ -309,9 +320,9 @@
309 320 public function get_synonyms() {
310 321 $synonyms_raw = $this->get_synonyms_raw();
311 322 $synonyms = array_values(
312 323 array_filter(
313 - array_map( [ $this, 'validate_synonym' ], explode( PHP_EOL, $synonyms_raw ) )
324 + array_map( [ $this, 'validate_synonym' ], preg_split( '/\r\n|\r|\n/', $synonyms_raw ) )
314 325 )
315 326 );
316 327
317 328 /**
@@ -378,10 +389,10 @@
378 389 return $mapping;
379 390 }
380 391
381 392 // Ensure we have analyzers and that it is an array.
382 - if ( ! isset( $mapping['settings']['analysis']['analyzer']['default']['filter'] )
383 - || ! is_array( $mapping['settings']['analysis']['analyzer']['default']['filter'] )
393 + if ( ! isset( $mapping['settings']['analysis']['analyzer']['default_search']['filter'] )
394 + || ! is_array( $mapping['settings']['analysis']['analyzer']['default_search']['filter'] )
384 395 ) {
385 396 return $mapping;
386 397 }
387 398
@@ -388,12 +399,14 @@
388 399 // Create a custom synonym filter for EP.
389 400 $mapping['settings']['analysis']['filter'][ $filter_name ] = $this->get_synonym_filter();
390 401
391 402 // Tell the analyzer to use our newly created filter.
392 - $mapping['settings']['analysis']['analyzer']['default']['filter'] = array_values(
393 - array_merge(
394 - [ $filter_name ],
395 - $mapping['settings']['analysis']['analyzer']['default']['filter']
403 + $mapping['settings']['analysis']['analyzer']['default_search']['filter'] = $this->maybe_change_filter_position(
404 + array_values(
405 + array_merge(
406 + [ $filter_name ],
407 + $mapping['settings']['analysis']['analyzer']['default_search']['filter'],
408 + )
396 409 )
397 410 );
398 411
399 412 return $mapping;
@@ -402,10 +415,13 @@
402 415 /**
403 416 * Handles updating the synonym list.
404 417 *
405 418 * @return void
419 + * @deprecated 5.1.0
406 420 */
407 421 public function handle_update_synonyms() {
422 + _deprecated_function( 'ElasticPress\Feature\Search\Synonyms::handle_update_synonyms', '5.1.0' );
423 +
408 424 $nonce = filter_input( INPUT_POST, $this->get_nonce_field(), FILTER_SANITIZE_SPECIAL_CHARS );
409 425 $referer = filter_input( INPUT_POST, '_wp_http_referer', FILTER_SANITIZE_URL );
410 426 $post_id = false;
411 427 $update = false;
@@ -460,13 +476,18 @@
460 476 */
461 477 public function update_synonyms() {
462 478 return array_reduce(
463 479 $this->get_affected_indices(),
464 - function( $success, $index ) {
480 + function ( $success, $index ) {
465 481 $filter = $this->get_synonym_filter();
466 482 $mapping = Elasticsearch::factory()->get_mapping( $index );
467 - $filters = $mapping[ $index ]['settings']['index']['analysis']['analyzer']['default']['filter'];
468 483
484 + if ( empty( $mapping ) || empty( $mapping[ $index ] ) ) {
485 + return false;
486 + }
487 +
488 + $filters = (array) $mapping[ $index ]['settings']['index']['analysis']['analyzer']['default_search']['filter'];
489 +
469 490 /*
470 491 * Due to limitations in Elasticsearch, we can't remove the filter and analyzer
471 492 * once set on the index settings and synonyms array can't be empty. So we set a
472 493 * fallback synonyms array here if the user supplied synonym array is empty.
@@ -478,13 +499,15 @@
478 499 // Construct the synonym filter.
479 500 $setting['index']['analysis']['filter']['ep_synonyms_filter'] = $filter;
480 501
481 502 // Add the analyzer.
482 - $setting['index']['analysis']['analyzer']['default']['filter'] = array_values(
483 - array_unique(
484 - array_merge(
485 - [ $this->get_synonym_filter_name() ],
486 - $filters
503 + $setting['index']['analysis']['analyzer']['default_search']['filter'] = $this->maybe_change_filter_position(
504 + array_values(
505 + array_unique(
506 + array_merge(
507 + [ $this->get_synonym_filter_name() ],
508 + $filters
509 + )
487 510 )
488 511 )
489 512 );
490 513
@@ -510,9 +533,9 @@
510 533 $indices = apply_filters( 'ep_synonyms_affected_indices', $this->affected_indices );
511 534
512 535 return array_filter(
513 536 array_map(
514 - function( $index ) {
537 + function ( $index ) {
515 538 $indexable = Indexables::factory()->get( $index );
516 539 return $indexable ? $indexable->get_index_name() : false;
517 540 },
518 541 $indices
@@ -561,10 +584,13 @@
561 584 * Get form action for admin page.
562 585 *
563 586 * @access protected
564 587 * @return string The admin post form action url.
588 + * @deprecated 5.1.0
565 589 */
566 590 public function get_form_action() {
591 + _deprecated_function( 'ElasticPress\Feature\Search\Synonyms::get_form_action', '5.1.0' );
592 +
567 593 return esc_url_raw( admin_url( 'admin-post.php' ) );
568 594 }
569 595
570 596 /**
@@ -570,10 +596,13 @@
570 596 /**
571 597 * Render admin page form hidden fields.
572 598 *
573 599 * @return void
600 + * @deprecated 5.1.0
574 601 */
575 602 public function form_hidden_fields() {
603 + _deprecated_function( 'ElasticPress\Feature\Search\Synonyms::get_form_action', '5.1.0', );
604 +
576 605 wp_nonce_field( $this->get_nonce_action(), $this->get_nonce_field() );
577 606 ?>
578 607 <input type="hidden" name="action" value="<?php echo esc_attr( $this->get_action() ); ?>" />
579 608 <?php
@@ -582,10 +611,13 @@
582 611 /**
583 612 * Get nonce action for admin page form.
584 613 *
585 614 * @return string
615 + * @deprecated 5.1.0
586 616 */
587 617 public function get_nonce_action() {
618 + _deprecated_function( 'ElasticPress\Feature\Search\Synonyms::get_form_action', '5.1.0', );
619 +
588 620 return $this->get_action();
589 621 }
590 622
591 623 /**
@@ -591,10 +623,13 @@
591 623 /**
592 624 * Get nonce field for admin page form.
593 625 *
594 626 * @return string
627 + * @deprecated 5.1.0
595 628 */
596 629 public function get_nonce_field() {
630 + _deprecated_function( 'ElasticPress\Feature\Search\Synonyms::get_nonce_field', '5.1.0', );
631 +
597 632 return 'ep_synonyms_nonce';
598 633 }
599 634
600 635 /**
@@ -600,10 +635,13 @@
600 635 /**
601 636 * Get synonym field name for admin page form.
602 637 *
603 638 * @return string
639 + * @deprecated 5.1.0
604 640 */
605 641 public function get_synonym_field() {
642 + _deprecated_function( 'ElasticPress\Feature\Search\Synonyms::get_synonym_field', '5.1.0', );
643 +
606 644 return 'ep_synonyms';
607 645 }
608 646
609 647 /**
@@ -609,10 +647,13 @@
609 647 /**
610 648 * Get the action slug for admin page form.
611 649 *
612 650 * @return string
651 + * @deprecated 5.1.0
613 652 */
614 653 public function get_action() {
654 + _deprecated_function( 'ElasticPress\Feature\Search\Synonyms::get_action', '5.1.0', );
655 +
615 656 return 'ep_synonyms_update';
616 657 }
617 658
618 659 /**
@@ -636,13 +677,18 @@
636 677 * @return string
637 678 */
638 679 public function example_synonym_list( $as_array = false ) {
639 680 $lines = [
640 - __( '# Defined sets (equivalent synonyms).', 'elasticpress' ),
641 - 'sneakers, tennis shoes, trainers, runners',
681 + __( '# Defined synonyms.', 'elasticpress' ),
682 + 'runner, running shoe, sneaker, tennis shoe, trainer',
642 683 '',
643 - __( '# Defined alternatives (explicit mappings).', 'elasticpress' ),
644 - 'shoes => sneaker, sandal, boots, high heels',
684 + __( '# Defined hyponyms.', 'elasticpress' ),
685 + 'blue => blue, aqua, azure, cerulean, cyan, ultramarine',
686 + '',
687 + __( '# Defined replacements.', 'elasticpress' ),
688 + 'supposably => supposedly',
689 + 'flustrated => flustered, frustrated',
690 + 'intensive purposes => intents and purposes',
645 691 ];
646 692
647 693 return $as_array ? $lines : implode( PHP_EOL, $lines );
648 694 }
@@ -650,10 +696,13 @@
650 696 /**
651 697 * Gets localized strings for use on the front end.
652 698 *
653 699 * @return array
700 + * @deprecated 5.1.0
654 701 */
655 702 public function get_localized_strings() {
703 + _deprecated_function( 'ElasticPress\Feature\Search\Synonyms::get_localized_strings', '5.1.0' );
704 +
656 705 return array(
657 706 'pageHeading' => __( 'Manage Synonyms', 'elasticpress' ),
658 707 'pageDescription' => __( 'Synonyms enable more flexible search results that show relevant results even without an exact match. Synonyms can be defined as a sets where all words are synonyms for each other, or as alternatives where searches for the primary word will also match the rest, but no vice versa.', 'elasticpress' ),
659 708 'pageToggleAdvanceText' => __( 'Switch to Advanced Text Editor', 'elasticpress' ),
@@ -688,10 +737,13 @@
688 737 /**
689 738 * Get data to export to the frontend with localization strings.
690 739 *
691 740 * @return array
741 + * @deprecated 5.1.0
692 742 */
693 743 public function get_localized_data() {
744 + _deprecated_function( 'ElasticPress\Feature\Search\Synonyms::get_localized_strings', '5.1.0' );
745 +
694 746 $data = array(
695 747 'sets' => array(),
696 748 'alternatives' => array(),
697 749 'initialMode' => $this->synonyms_editor_mode(),
@@ -772,10 +824,13 @@
772 824 *
773 825 * @param string $token The synonym token to prepare.
774 826 * @param boolean $primary Whether this string is the primary term of an alternative.
775 827 * @return array
828 + * @deprecated 5.1.0
776 829 */
777 830 public static function prepare_localized_token( $token, $primary = false ) {
831 + _deprecated_function( 'ElasticPress\Feature\Search\Synonyms::prepare_localized_token', '5.1.0' );
832 +
778 833 return array(
779 834 'label' => trim( sanitize_text_field( $token ) ),
780 835 'value' => trim( sanitize_text_field( $token ) ),
781 836 'primary' => $primary,
@@ -802,9 +857,9 @@
802 857 *
803 858 * @param string $content The content post.
804 859 * @return int|WP_Error
805 860 */
806 - private function update_synonym_post( $content ) {
861 + public function update_synonym_post( $content ) {
807 862 $synonym_post_id = $this->get_synonym_post_id();
808 863
809 864 if ( ! $synonym_post_id ) {
810 865 return $synonym_post_id;
@@ -817,6 +872,34 @@
817 872 'post_type' => self::POST_TYPE_NAME,
818 873 ],
819 874 true
820 875 );
876 + }
877 +
878 + /**
879 + * Setup REST endpoints
880 + *
881 + * @since 5.1.0
882 + */
883 + public function setup_endpoint() {
884 + $controller = new REST\Synonyms();
885 + $controller->register_routes();
886 + }
887 +
888 + /**
889 + * Change the position of the lowercase filter to the beginning of the array.
890 + *
891 + * @since 5.1.0
892 + * @param array $filters Array of filters.
893 + * @return array
894 + */
895 + protected function maybe_change_filter_position( array $filters ): array {
896 + $lowercase_filter = array_search( 'lowercase', $filters, true );
897 +
898 + if ( false !== $lowercase_filter ) {
899 + unset( $filters[ $lowercase_filter ] );
900 + array_unshift( $filters, 'lowercase' );
901 + }
902 +
903 + return $filters;
821 904 }
822 905 }