PluginProbe
ElasticPress / 5.0.0
ElasticPress v5.0.0
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
elasticpress / includes / classes / Feature / Search / Synonyms.php

Synonyms.php in ElasticPress 5.0.0, at includes/classes/Feature/Search/Synonyms.php

822 lines 21.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Synonyms Feature
4 *
5 * @package elasticpress
6 */
7
8 namespace ElasticPress\Feature\Search;
9
10 use ElasticPress\Elasticsearch;
11 use ElasticPress\FeatureRequirementsStatus;
12 use ElasticPress\Features;
13 use ElasticPress\Indexables;
14 use ElasticPress\Utils;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit; // Exit if accessed directly.
18 }
19
20 /**
21 * Synonyms Feature
22 *
23 * @since 3.4
24 * @package ElasticPress\Feature\Synonyms
25 */
26 class Synonyms {
27
28 /**
29 * Internal name of the post type
30 */
31 const POST_TYPE_NAME = 'ep-synonym';
32
33 /**
34 * Indices that should receive the synonym filter.
35 *
36 * @var array
37 */
38 public $affected_indices;
39
40 /**
41 * Elasticsearch Synonym Filter Name
42 *
43 * @var string
44 */
45 public $filter_name;
46
47 /**
48 * Synonym post id.
49 *
50 * @var int
51 */
52 protected $synonym_post_id;
53
54 /**
55 * Initialize feature setting it's config
56 *
57 * @since 3.4
58 */
59 public function __construct() {
60 $this->filter_name = 'ep_synonyms_filter';
61 $this->affected_indices = [ 'post' ];
62 }
63
64 /**
65 * Get search feature.
66 *
67 * @return Search
68 */
69 public function get_search_feature() {
70 /** Features Class @var Features $features */
71 $features = Features::factory();
72
73 /** Search Feature @var Feature\Search\Search $search */
74 return $features->get_registered_feature( 'search' );
75 }
76
77 /**
78 * Returns requirements status of feature
79 *
80 * Requires the search feature to be activated
81 *
82 * @return FeatureRequirementsStatus
83 */
84 public function requirements_status() {
85 $search = $this->get_search_feature();
86
87 if ( ! $search->is_active() ) {
88 return new FeatureRequirementsStatus( 2, esc_html__( 'This feature requires the "Post Search" feature to be enabled', 'elasticpress' ) );
89 }
90
91 return new FeatureRequirementsStatus( 0 );
92 }
93
94 /**
95 * Setup Feature Functionality
96 *
97 * @return bool
98 */
99 public function setup() {
100 if ( (bool) $this->requirements_status()->code ) {
101 return false;
102 }
103
104 // Register a post type to hold the synonyms post.
105 add_action( 'init', [ $this, 'register_post_type' ] );
106
107 // Setup the UI.
108 add_action( 'admin_menu', [ $this, 'admin_menu' ], 50 );
109 add_action( 'admin_enqueue_scripts', [ $this, 'scripts' ] );
110
111 // Handle the update synonyms action.
112 $action = $this->get_action();
113 add_action( "admin_post_$action", [ $this, 'handle_update_synonyms' ] );
114
115 // Handle the admin notices.
116 add_action( 'admin_notices', [ $this, 'admin_notices' ] );
117
118 // Add the synonyms to the elasticsearch query.
119 add_filter( 'ep_config_mapping', [ $this, 'add_search_synonyms' ], 20, 2 );
120
121 return true;
122 }
123
124 /**
125 * Enqueues scripts and styles.
126 *
127 * @return void
128 */
129 public function scripts() {
130 if ( ! $this->is_synonym_page() ) {
131 return;
132 }
133
134 wp_enqueue_script(
135 'ep_synonyms_scripts',
136 EP_URL . 'dist/js/synonyms-script.js',
137 Utils\get_asset_info( 'synonyms-script', 'dependencies' ),
138 Utils\get_asset_info( 'synonyms-script', 'version' ),
139 true
140 );
141
142 wp_set_script_translations( 'ep_synonyms_scripts', 'elasticpress' );
143
144 wp_enqueue_style( 'wp-edit-post' );
145
146 wp_enqueue_style(
147 'ep_synonyms_styles',
148 EP_URL . 'dist/css/synonyms-styles.css',
149 Utils\get_asset_info( 'synonyms-styles', 'dependencies' ),
150 Utils\get_asset_info( 'synonyms-styles', 'version' ),
151 'all'
152 );
153
154 wp_localize_script(
155 'ep_synonyms_scripts',
156 'epSynonyms',
157 array(
158 'i18n' => $this->get_localized_strings(),
159 'data' => $this->get_localized_data(),
160 )
161 );
162 }
163
164 /**
165 * Adds the synonyms settings page to the admin menu.
166 *
167 * @return void
168 */
169 public function admin_menu() {
170 add_submenu_page(
171 'elasticpress',
172 esc_html__( 'ElasticPress Synonyms', 'elasticpress' ),
173 esc_html__( 'Synonyms', 'elasticpress' ),
174 Utils\get_capability(),
175 'elasticpress-synonyms',
176 [ $this, 'admin_page' ]
177 );
178 }
179
180 /**
181 * Renders the synonyms settings page.
182 *
183 * @return void
184 */
185 public function admin_page() {
186 include EP_PATH . '/includes/partials/header.php';
187
188 ?>
189 <div class="wrap">
190 <form action="<?php echo esc_url( $this->get_form_action() ); ?>" method="POST">
191 <?php $this->form_hidden_fields(); ?>
192 <div id="synonym-root"></div>
193 </form>
194 </div>
195 <?php
196 }
197
198 /**
199 * Admin notices.
200 *
201 * @return void
202 */
203 public function admin_notices() {
204 if ( ! $this->is_synonym_page() ) {
205 return;
206 }
207
208 $update = filter_input( INPUT_GET, 'ep_synonym_update', FILTER_SANITIZE_SPECIAL_CHARS );
209
210 if ( ! in_array( $update, [ 'success', 'error-update-post', 'error-update-index' ], true ) ) {
211 return;
212 }
213
214 $class = ( 'success' === $update ? 'notice-success' : 'notice-error' ) . ' notice';
215 $message = '';
216
217 switch ( $update ) {
218 case 'success':
219 $message = __( 'Successfully updated synonym filter.', 'elasticpress' );
220 break;
221 case 'error-update-post':
222 $message = __( 'There was an error storing your synonyms.', 'elasticpress' );
223 break;
224 case 'error-update-index':
225 $message = __( 'There was a problem updating the index with your synonyms. If you have not indexed your data, please run an index.', 'elasticpress' );
226 break;
227 default:
228 $message = __( 'There was an error updating the synonym list.', 'elasticpress' );
229 }
230
231 printf(
232 '<div class="%1$s"><p>%2$s</p></div>',
233 esc_attr( $class ),
234 esc_html( $message )
235 );
236 }
237
238 /**
239 * Registers a post type for our synonyms post storage.
240 *
241 * @return void
242 */
243 public function register_post_type() {
244 $args = [
245 'description' => esc_html__( 'Elasticsearch Synonyms', 'elasticpress' ),
246 'public' => false,
247 'publicly_queryable' => false,
248 'show_ui' => false,
249 'show_in_menu' => false,
250 'query_var' => true,
251 'capabilities' => Utils\get_post_map_capabilities(),
252 'has_archive' => false,
253 'hierarchical' => false,
254 'menu_position' => 100,
255 'supports' => [ 'title' ],
256 ];
257
258 register_post_type( self::POST_TYPE_NAME, $args );
259 }
260
261 /**
262 * Get the post id of the post holding our synonyms.
263 *
264 * @return int The synonym post ID.
265 */
266 public function get_synonym_post_id() {
267 if ( ! $this->synonym_post_id ) {
268 $query_synonym_post = new \WP_Query(
269 array(
270 'fields' => 'ids',
271 'post_type' => self::POST_TYPE_NAME,
272 'posts_per_page' => 1,
273 'orderby' => 'modified',
274 'post_status' => 'any',
275 )
276 );
277
278 $this->synonym_post_id = ( $query_synonym_post->post_count >= 1 ) ? $query_synonym_post->posts[0] : false;
279
280 if ( ! $this->synonym_post_id ) {
281 $this->synonym_post_id = $this->insert_default_synonym_post();
282 }
283 }
284
285 return $this->synonym_post_id;
286 }
287
288 /**
289 * Get synonyms in their raw format.
290 *
291 * @return string
292 */
293 public function get_synonyms_raw() {
294 $post = get_post( $this->get_synonym_post_id() );
295
296 if ( ! $post ) {
297 return '';
298 }
299
300 return $post->post_content;
301 }
302
303 /**
304 * Get an array of user defined synonyms.
305 *
306 * @return array
307 */
308 public function get_synonyms() {
309 $synonyms_raw = $this->get_synonyms_raw();
310 $synonyms = array_values(
311 array_filter(
312 array_map( [ $this, 'validate_synonym' ], explode( PHP_EOL, $synonyms_raw ) )
313 )
314 );
315
316 /**
317 * Filter array of synonyms to add to a custom synonym filter.
318 *
319 * @hook ep_synonyms
320 * @return {array} The new array of search synonyms.
321 */
322 return apply_filters( 'ep_synonyms', $synonyms );
323 }
324
325 /**
326 * Validate a synonym.
327 *
328 * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-tokenfilter.html#_solr_synonyms
329 * @param string $synonym The synonym.
330 * @return string|boolean String synonym if valid, boolean false if validation failed.
331 */
332 public function validate_synonym( $synonym ) {
333 // Don't use empty lines.
334 if ( empty( trim( $synonym ) ) ) {
335 return false;
336 }
337
338 // Don't use lines that start with "#", those are comments.
339 if ( 0 === strpos( $synonym, '#' ) ) {
340 return false;
341 }
342
343 // Don't use lines that start with "//" though not in Solr spec.
344 if ( 0 === strpos( $synonym, '//' ) ) {
345 return false;
346 }
347
348 return sanitize_text_field( $synonym, true );
349 }
350
351 /**
352 * Add search synonyms.
353 *
354 * @param array $mapping Elasticsearch mapping.
355 * @param string $index Index name.
356 * @return array
357 */
358 public function add_search_synonyms( $mapping, $index ) {
359 $synonyms = $this->get_synonyms();
360 $indices = $this->get_affected_indices();
361 $filter_name = $this->get_synonym_filter_name();
362
363 // Ensure we should affect this mapping.
364 if ( ! in_array( $index, $indices, true ) ) {
365 return $mapping;
366 }
367
368 // Ensure we have synonyms to add.
369 if ( ! is_array( $synonyms ) || empty( $synonyms ) ) {
370 return $mapping;
371 }
372
373 // Ensure we have filters and that it is an array.
374 if ( ! isset( $mapping['settings']['analysis']['filter'] )
375 || ! is_array( $mapping['settings']['analysis']['filter'] )
376 ) {
377 return $mapping;
378 }
379
380 // Ensure we have analyzers and that it is an array.
381 if ( ! isset( $mapping['settings']['analysis']['analyzer']['default_search']['filter'] )
382 || ! is_array( $mapping['settings']['analysis']['analyzer']['default_search']['filter'] )
383 ) {
384 return $mapping;
385 }
386
387 // Create a custom synonym filter for EP.
388 $mapping['settings']['analysis']['filter'][ $filter_name ] = $this->get_synonym_filter();
389
390 // Tell the analyzer to use our newly created filter.
391 $mapping['settings']['analysis']['analyzer']['default_search']['filter'] = array_values(
392 array_merge(
393 [ $filter_name ],
394 $mapping['settings']['analysis']['analyzer']['default_search']['filter']
395 )
396 );
397
398 return $mapping;
399 }
400
401 /**
402 * Handles updating the synonym list.
403 *
404 * @return void
405 */
406 public function handle_update_synonyms() {
407 $nonce = filter_input( INPUT_POST, $this->get_nonce_field(), FILTER_SANITIZE_SPECIAL_CHARS );
408 $referer = filter_input( INPUT_POST, '_wp_http_referer', FILTER_SANITIZE_URL );
409 $post_id = false;
410 $update = false;
411
412 if ( wp_verify_nonce( $nonce, $this->get_nonce_action() ) ) {
413 $synonyms = filter_input( INPUT_POST, $this->get_synonym_field(), FILTER_CALLBACK, [ 'options' => 'wp_strip_all_tags' ] );
414 $mode = filter_input( INPUT_POST, 'synonyms_editor_mode', FILTER_SANITIZE_SPECIAL_CHARS );
415 $content = trim( sanitize_textarea_field( $synonyms ) );
416
417 // Content can't be empty.
418 if ( empty( $content ) ) {
419 $lines = $this->example_synonym_list( true );
420 $content = implode( PHP_EOL, [ $lines[0], $lines[2], $lines[3] ] );
421 }
422
423 $post_id = $this->update_synonym_post( $content );
424
425 // Update Elasticsearch
426 $update = $this->update_synonyms();
427
428 // Save editor mode.
429 if ( in_array( $mode, [ 'advanced', 'simple' ], true ) ) {
430 $this->save_editor_mode( $mode );
431 }
432 }
433
434 $result = 'success';
435
436 if ( ! $post_id || is_wp_error( $post_id ) ) {
437 $result = 'error-update-post';
438 }
439
440 if ( ! $update ) {
441 $result = 'error-update-index';
442 }
443
444 wp_safe_redirect(
445 add_query_arg(
446 [
447 'ep_synonym_update' => $result,
448 ],
449 esc_url_raw( $referer )
450 )
451 );
452 exit;
453 }
454
455 /**
456 * Update synonyms.
457 *
458 * @return boolean
459 */
460 public function update_synonyms() {
461 return array_reduce(
462 $this->get_affected_indices(),
463 function( $success, $index ) {
464 $filter = $this->get_synonym_filter();
465 $mapping = Elasticsearch::factory()->get_mapping( $index );
466 $filters = $mapping[ $index ]['settings']['index']['analysis']['analyzer']['default_search']['filter'];
467
468 /*
469 * Due to limitations in Elasticsearch, we can't remove the filter and analyzer
470 * once set on the index settings and synonyms array can't be empty. So we set a
471 * fallback synonyms array here if the user supplied synonym array is empty.
472 */
473 if ( empty( $filter['synonyms'] ) ) {
474 $filter['synonyms'] = [ 'odd,unusual' ];
475 }
476
477 // Construct the synonym filter.
478 $setting['index']['analysis']['filter']['ep_synonyms_filter'] = $filter;
479
480 // Add the analyzer.
481 $setting['index']['analysis']['analyzer']['default_search']['filter'] = array_values(
482 array_unique(
483 array_merge(
484 [ $this->get_synonym_filter_name() ],
485 $filters
486 )
487 )
488 );
489
490 // Put it to Elasticsearch.
491 $update = Elasticsearch::factory()->update_index_settings( $index, $setting, true );
492 return $success ? $update : false;
493 },
494 true
495 );
496 }
497
498 /**
499 * Get affected indices.
500 *
501 * @return array
502 */
503 public function get_affected_indices() {
504 /**
505 * Filter the indices that use the synonym filter.
506 *
507 * @return array Array of index names.
508 */
509 $indices = apply_filters( 'ep_synonyms_affected_indices', $this->affected_indices );
510
511 return array_filter(
512 array_map(
513 function( $index ) {
514 $indexable = Indexables::factory()->get( $index );
515 return $indexable ? $indexable->get_index_name() : false;
516 },
517 $indices
518 )
519 );
520 }
521
522 /**
523 * Get synonym filter name.
524 *
525 * @return string
526 */
527 public function get_synonym_filter_name() {
528 /**
529 * Filter name of the synonym filter set in elasticsearch.
530 *
531 * @hook ep_synonyms_filter_name
532 * @return {string} The name of the synonyms filter.
533 */
534 return apply_filters( 'ep_synonyms_filter_name', $this->filter_name );
535 }
536
537 /**
538 * Get synonym filter.
539 *
540 * @return array
541 */
542 public function get_synonym_filter() {
543 /**
544 * Filter the synonym filter set in elasticsearch.
545 *
546 * @hook ep_synonyms_filter
547 * @return {array} The synonym search filter.
548 */
549 return apply_filters(
550 'ep_synonyms_filter',
551 [
552 'type' => 'synonym_graph',
553 'lenient' => true,
554 'synonyms' => $this->get_synonyms(),
555 ]
556 );
557 }
558
559 /**
560 * Get form action for admin page.
561 *
562 * @access protected
563 * @return string The admin post form action url.
564 */
565 public function get_form_action() {
566 return esc_url_raw( admin_url( 'admin-post.php' ) );
567 }
568
569 /**
570 * Render admin page form hidden fields.
571 *
572 * @return void
573 */
574 public function form_hidden_fields() {
575 wp_nonce_field( $this->get_nonce_action(), $this->get_nonce_field() );
576 ?>
577 <input type="hidden" name="action" value="<?php echo esc_attr( $this->get_action() ); ?>" />
578 <?php
579 }
580
581 /**
582 * Get nonce action for admin page form.
583 *
584 * @return string
585 */
586 public function get_nonce_action() {
587 return $this->get_action();
588 }
589
590 /**
591 * Get nonce field for admin page form.
592 *
593 * @return string
594 */
595 public function get_nonce_field() {
596 return 'ep_synonyms_nonce';
597 }
598
599 /**
600 * Get synonym field name for admin page form.
601 *
602 * @return string
603 */
604 public function get_synonym_field() {
605 return 'ep_synonyms';
606 }
607
608 /**
609 * Get the action slug for admin page form.
610 *
611 * @return string
612 */
613 public function get_action() {
614 return 'ep_synonyms_update';
615 }
616
617 /**
618 * Is this our synonym page.
619 *
620 * @return boolean
621 */
622 public function is_synonym_page() {
623 if ( ! function_exists( '\get_current_screen' ) ) {
624 return false;
625 }
626
627 $screen = get_current_screen();
628 return ( 'elasticpress_page_elasticpress-synonyms' === $screen->base );
629 }
630
631 /**
632 * An example synonym that we initialize new synonyms lists with.
633 *
634 * @param bool $as_array Optional. Return an array of synonym lines. Default false.
635 * @return string
636 */
637 public function example_synonym_list( $as_array = false ) {
638 $lines = [
639 __( '# Defined sets (equivalent synonyms).', 'elasticpress' ),
640 'sneakers, tennis shoes, trainers, runners',
641 '',
642 __( '# Defined alternatives (explicit mappings).', 'elasticpress' ),
643 'shoes => sneaker, sandal, boots, high heels',
644 ];
645
646 return $as_array ? $lines : implode( PHP_EOL, $lines );
647 }
648
649 /**
650 * Gets localized strings for use on the front end.
651 *
652 * @return array
653 */
654 public function get_localized_strings() {
655 return array(
656 'pageHeading' => __( 'Manage Synonyms', 'elasticpress' ),
657 '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' ),
658 'pageToggleAdvanceText' => __( 'Switch to Advanced Text Editor', 'elasticpress' ),
659 'pageToggleSimpleText' => __( 'Switch to Visual Editor', 'elasticpress' ),
660
661 'setsTitle' => __( 'Sets', 'elasticpress' ),
662 'setsDescription' => __( 'Sets are terms that will all match each other for search results. This is useful where all words are considered equivalent, such as product renaming or regional variations like sneakers, tennis shoes, trainers, and runners.', 'elasticpress' ),
663 'setsInputHeading' => __( 'Comma separated list of terms', 'elasticpress' ),
664 'setsAddButtonText' => __( 'Add Set', 'elasticpress' ),
665 'setsErrorMessage' => __( 'This set must contain at least 2 terms.', 'elasticpress' ),
666
667 'alternativesTitle' => __( 'Alternatives', 'elasticpress' ),
668 'alternativesDescription' => __( 'Alternatives are terms that will also be matched when you search for the primary term. For instance, a search for shoes can also include results for sneaker, sandals, boots, and high heels.', 'elasticpress' ),
669 'alternativesPrimaryHeading' => __( 'Primary term', 'elasticpress' ),
670 'alternativesInputHeading' => __( 'Comma separated list of alternatives', 'elasticpress' ),
671 'alternativesAddButtonText' => __( 'Add Alternative', 'elasticpress' ),
672 'alternativesErrorMessage' => __( 'You must enter both a primary term and at least one alternative term.', 'elasticpress' ),
673
674 'solrTitle' => __( 'Advanced Synonym Editor', 'elasticpress' ),
675 'solrDescription' => __( 'When you add Sets and Alternatives above, we reduce them to SolrSynonyms which Elasticsearch can understand. If you are an advanced user, you can edit synonyms directly using Solr synonym formatting. This is beneficial if you want to import a large dictionary of synonyms, or want to export this site\'s synonyms for use on another site.', 'elasticpress' ),
676 'solrInputHeading' => __( 'SolrSynonym Text', 'elasticpress' ),
677 'solrAlternativesErrorMessage' => __( 'Alternatives must have both a primary term and at least one alternative term.', 'elasticpress' ),
678 'solrSetsErrorMessage' => __( 'Sets must contain at least 2 terms.', 'elasticpress' ),
679
680 'removeItemText' => __( 'Remove', 'elasticpress' ),
681 'submitText' => __( 'Update Synonyms', 'elasticpress' ),
682
683 'synonymsTextareaInputName' => $this->get_synonym_field(),
684 );
685 }
686
687 /**
688 * Get data to export to the frontend with localization strings.
689 *
690 * @return array
691 */
692 public function get_localized_data() {
693 $data = array(
694 'sets' => array(),
695 'alternatives' => array(),
696 'initialMode' => $this->synonyms_editor_mode(),
697 );
698 $synonyms = $this->get_synonyms();
699
700 foreach ( $synonyms as $line ) {
701 $synonym = array();
702 if ( strpos( $line, '=>' ) ) {
703 $tokens = explode( '=>', $line );
704 array_push( $synonym, self::prepare_localized_token( $tokens[0], true ) );
705 array_push(
706 $synonym,
707 ...array_map(
708 array( __CLASS__, 'prepare_localized_token' ),
709 explode( ',', $tokens[1] )
710 )
711 );
712 array_push( $data['alternatives'], $synonym );
713 } else {
714 array_push(
715 $synonym,
716 ...array_map(
717 array( __CLASS__, 'prepare_localized_token' ),
718 explode( ',', $line )
719 )
720 );
721 array_push( $data['sets'], $synonym );
722 }
723 }
724
725 return $data;
726 }
727
728 /**
729 * Saves the editor mode.
730 *
731 * @param string $mode The mode, one of "advanced" or "simple".
732 * @return void
733 */
734 public function save_editor_mode( $mode ) {
735 $search = $this->get_search_feature();
736 $settings = $search->get_settings();
737
738 if ( isset( $settings['synonyms_editor_mode'] ) && $settings['synonyms_editor_mode'] === $mode ) {
739 return;
740 }
741
742 $settings['synonyms_editor_mode'] = $mode;
743 $features = Features::factory();
744 $features->update_feature( $search->slug, $settings, false );
745 }
746
747 /**
748 * Get the stored editor mode. Default simple.
749 *
750 * @return boolean
751 */
752 public function synonyms_editor_mode() {
753 $search = $this->get_search_feature();
754 $settings = $search->get_settings();
755 $settings = wp_parse_args( is_array( $settings ) ? $settings : [], $search->default_settings );
756 $mode = $settings['synonyms_editor_mode'];
757
758 /**
759 * Filter the default synonyms editor mode.
760 *
761 * @hook ep_synonyms_editor_mode
762 * @return {string} One of 'simple' or 'advanced'.
763 */
764 $filtered = apply_filters( 'ep_synonyms_editor_mode', $mode );
765
766 return in_array( $filtered, [ 'simple', 'advanced' ], true ) ? $filtered : 'simple';
767 }
768
769 /**
770 * Prepare localized token.
771 *
772 * @param string $token The synonym token to prepare.
773 * @param boolean $primary Whether this string is the primary term of an alternative.
774 * @return array
775 */
776 public static function prepare_localized_token( $token, $primary = false ) {
777 return array(
778 'label' => trim( sanitize_text_field( $token ) ),
779 'value' => trim( sanitize_text_field( $token ) ),
780 'primary' => $primary,
781 );
782 }
783
784 /**
785 * Insert default synonym post
786 *
787 * @return int|WP_Error
788 */
789 private function insert_default_synonym_post() {
790 return wp_insert_post(
791 [
792 'post_content' => $this->example_synonym_list(),
793 'post_type' => self::POST_TYPE_NAME,
794 ],
795 true
796 );
797 }
798
799 /**
800 * Update synonym post
801 *
802 * @param string $content The content post.
803 * @return int|WP_Error
804 */
805 private function update_synonym_post( $content ) {
806 $synonym_post_id = $this->get_synonym_post_id();
807
808 if ( ! $synonym_post_id ) {
809 return $synonym_post_id;
810 }
811
812 return wp_insert_post(
813 [
814 'ID' => $synonym_post_id,
815 'post_content' => $content,
816 'post_type' => self::POST_TYPE_NAME,
817 ],
818 true
819 );
820 }
821 }
822