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
elasticpress / includes / classes / Feature / Search / Search.php

Search.php in ElasticPress 5.3.5, at includes/classes/Feature/Search/Search.php

896 lines 25.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Search feature
4 *
5 * @since 1.9
6 * @package elasticpress
7 */
8
9 namespace ElasticPress\Feature\Search;
10
11 use ElasticPress\Feature;
12 use ElasticPress\Features;
13 use ElasticPress\Indexables;
14 use ElasticPress\Utils;
15
16 /**
17 * Search feature class
18 */
19 class Search extends Feature {
20 /**
21 * Synonyms Class (Sub Feature)
22 *
23 * @var Synonyms
24 */
25 public $synonyms;
26
27 /**
28 * Weighting Class (Sub Feature)
29 *
30 * @var Weighting
31 */
32 public $weighting;
33
34 /**
35 * Highlighting tags
36 *
37 * @var array
38 */
39 public static $default_highlight_tags = [
40 'mark',
41 'span',
42 'strong',
43 'em',
44 'i',
45 ];
46
47 /**
48 * Initialize feature setting it's config
49 *
50 * @since 3.0
51 */
52 public function __construct() {
53 $this->slug = 'search';
54
55 $this->group = 'core-search';
56
57 $this->requires_install_reindex = false;
58
59 $this->default_settings = [
60 'decaying_enabled' => '1',
61 'synonyms_editor_mode' => 'simple',
62 'highlight_enabled' => '0',
63 'highlight_excerpt' => '0',
64 'highlight_tag' => 'mark',
65 ];
66
67 $this->available_during_installation = true;
68
69 parent::__construct();
70 }
71
72 /**
73 * Sets i18n strings.
74 *
75 * @return void
76 * @since 5.2.0
77 */
78 public function set_i18n_strings(): void {
79 $this->title = esc_html__( 'Post Search', 'elasticpress' );
80
81 $this->summary = '<p>' . __( 'Instantly find the content you’re looking for. The first time.', 'elasticpress' ) . '</p>' .
82 '<p>' . __( 'Overcome higher-end performance and functional limits posed by the traditional WordPress structured (SQL) database to deliver superior keyword search, instantly. ElasticPress indexes custom fields, tags, and other metadata to improve search results. Fuzzy matching accounts for misspellings and verb tenses.', 'elasticpress' ) . '</p>';
83
84 $this->docs_url = __( 'https://www.elasticpress.io/resources/articles/configuring-elasticpress-via-the-plugin-dashboard/#post-search', 'elasticpress' );
85
86 $this->field_group_map = [
87 'highlight_group' => [
88 'label' => esc_html__( 'Highlighting Options', 'elasticpress' ),
89 ],
90 ];
91 }
92
93 /**
94 * We need to delay search setup up since it will fire after protected content and protected
95 * content filters into the search setup
96 *
97 * @since 2.2
98 */
99 public function setup() {
100 Indexables::factory()->activate( 'post' );
101
102 add_action( 'init', [ $this, 'search_setup' ] );
103
104 // Set up weighting sub-module
105 $this->weighting = new Weighting();
106 $this->weighting->setup();
107
108 $this->synonyms = new Synonyms();
109 $this->synonyms->setup();
110 }
111
112 /**
113 * Setup feature on each page load
114 *
115 * @since 3.0
116 */
117 public function search_setup() {
118 add_filter( 'ep_elasticpress_enabled', [ $this, 'integrate_search_queries' ], 10, 2 );
119 add_filter( 'ep_formatted_args', [ $this, 'weight_recent' ], 11, 2 );
120 add_filter( 'ep_query_post_type', [ $this, 'filter_query_post_type_for_search' ], 10, 2 );
121
122 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
123 add_filter( 'ep_formatted_args', [ $this, 'add_search_highlight_tags' ], 10, 2 );
124 add_filter( 'ep_highlighting_tag', [ $this, 'get_highlighting_tag' ] );
125 add_action( 'ep_highlighting_pre_add_highlight', [ $this, 'allow_excerpt_html' ] );
126
127 add_action( 'init', [ $this, 'register_meta' ], 20 );
128 add_filter( 'ep_prepare_meta_allowed_keys', [ $this, 'add_exclude_from_search' ] );
129 add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] );
130 add_filter( 'ep_post_filters', [ $this, 'exclude_posts_from_search' ], 10, 3 );
131 add_action( 'post_submitbox_misc_actions', [ $this, 'output_exclude_from_search_setting' ] );
132 add_action( 'edit_post', [ $this, 'save_exclude_from_search_meta' ] );
133 add_filter( 'ep_skip_query_integration', [ $this, 'skip_query_integration' ], 10, 2 );
134
135 add_action( 'attachment_submitbox_misc_actions', [ $this, 'output_exclude_from_search_setting' ], 15 );
136 add_action( 'edit_attachment', [ $this, 'save_exclude_from_search_meta' ] );
137 }
138
139
140 /**
141 * Enqueue styles for highlighting.
142 */
143 public function enqueue_scripts() {
144 $settings = $this->get_settings();
145
146 if ( '1' !== $settings['highlight_enabled'] ) {
147 return;
148 }
149
150 wp_enqueue_style(
151 'searchterm-highlighting',
152 EP_URL . 'dist/css/highlighting-styles.css',
153 Utils\get_asset_info( 'highlighting-styles', 'dependencies' ),
154 Utils\get_asset_info( 'highlighting-styles', 'version' )
155 );
156 }
157
158 /**
159 * Set default fields to highlight, and outputs
160 * the tags on the front end.
161 *
162 * @param array $formatted_args ep_formatted_args array
163 * @param array $args WP_Query args
164 * @return array $formatted_args formatted args with search highlight tags
165 */
166 public function add_search_highlight_tags( $formatted_args, $args ) {
167
168 /**
169 * Fires before the highlighting clause is added to the Elasticsearch query
170 *
171 * @since 3.5.1
172 * @hook ep_highlighting_pre_add_highlight
173 * @param {array} $formatted_args ep_formatted_args array
174 * @param {string} $args WP_Query args
175 */
176 do_action( 'ep_highlighting_pre_add_highlight', $formatted_args, $args );
177
178 // get current config
179 $settings = $this->get_settings();
180
181 if ( '1' !== $settings['highlight_enabled'] ) {
182 return $formatted_args;
183 }
184
185 if ( empty( $args['s'] ) ) {
186 return $formatted_args;
187 }
188
189 /**
190 * Filter whether to add the `highlight` clause in the query or not.
191 *
192 * @since 3.5.6
193 * @hook ep_highlight_should_add_clause
194 * @param {bool} $add_highlight_clause True means the clause should be added.
195 * @param {array} $formatted_args ep_formatted_args array
196 * @param {array} $args WP query args
197 * @return {bool} New $add_highlight_clause value
198 */
199 $add_highlight_clause = apply_filters(
200 'ep_highlight_should_add_clause',
201 Utils\is_integrated_request( 'highlighting', [ 'public' ] ),
202 $formatted_args,
203 $args
204 );
205
206 if ( ! $add_highlight_clause ) {
207 return $formatted_args;
208 }
209
210 /**
211 * Filter the fields that should be highlighted.
212 *
213 * @since 3.5.1
214 * @hook ep_highlighting_fields
215 * @param {array} $fields Highlighting fields
216 * @param {array} $formatted_args array
217 * @param {array} $args WP_Query args
218 * @return {array} New Highlighting fields
219 */
220 $fields_to_highlight = apply_filters(
221 'ep_highlighting_fields',
222 [ 'post_title', 'post_content' ],
223 $formatted_args,
224 $args
225 );
226
227 // define the tag to use
228 $current_tag = $settings['highlight_tag'];
229
230 /**
231 * Filter the tag that wraps the search highlighted term
232 *
233 * @since 3.5
234 * @hook ep_highlighting_tag
235 * @param {string} $current_tag Highlighting tag
236 * @return {string} New highlighting tag
237 */
238 $highlight_tag = apply_filters( 'ep_highlighting_tag', $current_tag );
239
240 /**
241 * Filter class applied to search highlight tags
242 *
243 * @since 3.5
244 * @hook ep_highlighting_class
245 * @param {string} $class Highlighting class
246 * @return {string} New highlighting class
247 */
248 $highlight_class = apply_filters( 'ep_highlighting_class', 'ep-highlight' );
249
250 // tags
251 $opening_tag = '<' . $highlight_tag . " class='" . $highlight_class . "'>";
252 $closing_tag = '</' . $highlight_tag . '>';
253
254 foreach ( $fields_to_highlight as $field ) {
255 $formatted_args['highlight']['fields'][ $field ] = [
256 'pre_tags' => [ $opening_tag ],
257 'post_tags' => [ $closing_tag ],
258 'type' => 'plain',
259 /**
260 * Filter the maximum number of fragments highlighted for a searched field.
261 *
262 * @since 4.7.2
263 * @hook ep_highlight_number_of_fragments
264 * @param {int} $max_fragments Maximum number of fragments for field.
265 * @param {string} $field Search field being setup.
266 * @return {int} New maximum number of fragments to highlight for the searched field.
267 */
268 'number_of_fragments' => apply_filters( 'ep_highlight_number_of_fragments', 0, $field ),
269 ];
270 }
271
272 return $formatted_args;
273 }
274
275 /**
276 * Called by ep_highlighting_pre_add_highlight action.
277 *
278 * Replaces the default excerpt with the custom excerpt, allowing
279 * for the selected tag to be displayed in it.
280 */
281 public function allow_excerpt_html() {
282 if ( ! Utils\is_integrated_request( 'highlighting', [ 'public' ] ) ) {
283 return;
284 }
285
286 $settings = $this->get_settings();
287
288 if ( ! empty( $settings['highlight_excerpt'] ) && '1' === $settings['highlight_excerpt'] ) {
289 remove_filter( 'get_the_excerpt', 'wp_trim_excerpt' );
290 add_filter( 'get_the_excerpt', [ $this, 'ep_highlight_excerpt' ], 10, 2 );
291 add_filter( 'ep_highlighting_fields', [ $this, 'ep_highlight_add_excerpt_field' ] );
292 }
293 }
294
295 /**
296 * Called by allow_excerpt_html
297 * logic for the excerpt filter allowing the currently selected tag.
298 *
299 * @param string $text excerpt string
300 * @param WP_Post $post Post Object
301 *
302 * @return string $text the new excerpt
303 */
304 public function ep_highlight_excerpt( $text, $post ) {
305 $settings = $this->get_settings();
306
307 // reproduces wp_trim_excerpt filter, preserving the excerpt_more and excerpt_length filters
308 if ( '' === $text ) {
309 $text = get_the_content( '', false, $post );
310
311 $text = strip_shortcodes( $text );
312 $text = excerpt_remove_blocks( $text );
313
314 $text = apply_filters( 'the_content', $text );
315 $text = str_replace( '\]\]\>', ']]&gt;', $text );
316
317 $text = strip_tags( $text, '<' . esc_html( $settings['highlight_tag'] ) . '>' );
318
319 // use the defined length, if already applied...
320 $excerpt_length = apply_filters( 'excerpt_length', 55 );
321
322 // use defined excerpt_more filter if it is used
323 $excerpt_more = apply_filters( 'excerpt_more', $text );
324 $excerpt_more = $excerpt_more !== $text ? $excerpt_more : '[&hellip;]';
325
326 /**
327 * WordPress would handle this using `wp_trim_words` but that removes all tags,
328 * including the one used to highlight the search term.
329 */
330 $words = explode( ' ', $text, $excerpt_length + 1 );
331 if ( count( $words ) > $excerpt_length ) {
332 array_pop( $words );
333 array_push( $words, $excerpt_more );
334 $text = implode( ' ', $words );
335 }
336 }
337
338 return $text;
339 }
340
341 /**
342 * Add `post_content` to the list of fields to highlight.
343 *
344 * @since 3.5.1
345 * @param array $fields_to_highlight The list of fields to highlight.
346 * @return array
347 */
348 public function ep_highlight_add_excerpt_field( $fields_to_highlight ) {
349 $fields_to_highlight[] = 'post_excerpt';
350 return $fields_to_highlight;
351 }
352
353 /**
354 * Helper filter to check if the tag is allowed.
355 *
356 * @param string $tag - html tag
357 * @return string
358 */
359 public function get_highlighting_tag( $tag ) {
360 if ( ! in_array( $tag, self::$default_highlight_tags, true ) ) {
361 $tag = 'mark';
362 }
363
364 return $tag;
365 }
366
367 /**
368 * Returns searchable post types for the current site
369 *
370 * @since 1.9
371 * @return mixed|void
372 */
373 public function get_searchable_post_types() {
374 $post_types = get_post_types( array( 'exclude_from_search' => false ) );
375
376 /**
377 * Don't search attachments by default
378 *
379 * @since 3.0
380 */
381 unset( $post_types['attachment'] );
382
383 /**
384 * Filter searchable post types
385 *
386 * @hook ep_searchable_post_types
387 * @param {array} $post_types Post types
388 * @return {array} New post types
389 */
390 return apply_filters( 'ep_searchable_post_types', $post_types );
391 }
392
393 /**
394 * Make sure we don't search for "any" on a search query
395 *
396 * @param string $post_type Post type
397 * @param WP_Query $query WP Query
398 * @return string|array
399 */
400 public function filter_query_post_type_for_search( $post_type, $query ) {
401 if ( 'any' === $post_type && $query->is_search() ) {
402 $searchable_post_types = $this->get_searchable_post_types();
403
404 // If we have no searchable post types, there's no point going any further
405 if ( empty( $searchable_post_types ) ) {
406
407 // Have to return something or it improperly calculates the found_posts
408 return false;
409 }
410
411 // Conform the post types array to an acceptable format for ES
412 $post_types = [];
413
414 foreach ( $searchable_post_types as $type ) {
415 $post_types[] = $type;
416 }
417
418 // These are now the only post types we will search
419 $post_type = $post_types;
420 }
421
422 return $post_type;
423 }
424
425 /**
426 * Returns true/false if decaying is/isn't enabled
427 *
428 * @param array $args WP_Query args
429 *
430 * @return bool
431 */
432 public function is_decaying_enabled( $args = [] ) {
433 $settings = $this->get_settings();
434
435 $is_decaying_enabled = $settings['decaying_enabled'] && '0' !== $settings['decaying_enabled'];
436
437 /**
438 * Filter to modify decaying
439 *
440 * @hook ep_is_decaying_enabled
441 * @since 4.6.0
442 * @param {bool} $is_decaying_enabled Whether decay by date is enabled or not
443 * @param {array} $settings Settings
444 * @param {array} $args WP_Query args
445 * @return {bool} Decaying
446 */
447 return apply_filters( 'ep_is_decaying_enabled', $is_decaying_enabled, $settings, $args );
448 }
449
450 /**
451 * Weight more recent content in searches
452 *
453 * @param array $formatted_args Formatted ES args
454 * @param array $args WP_Query args
455 * @since 2.1
456 * @return array
457 */
458 public function weight_recent( $formatted_args, $args ) {
459 if ( empty( $args['s'] ) ) {
460 return $formatted_args;
461 }
462
463 if ( ! $this->is_decaying_enabled( $args ) ) {
464 return $formatted_args;
465 }
466
467 /**
468 * Filter search date weighting scale
469 *
470 * @hook epwr_decay_function
471 * @param {string} $decay_function Current decay function
472 * @param {array} $formatted_args Formatted Elasticsearch arguments
473 * @param {array} $args WP_Query arguments
474 * @return {string} New decay function
475 */
476 $decay_function = apply_filters( 'epwr_decay_function', 'exp', $formatted_args, $args );
477
478 /**
479 * Filter search date weighting field
480 *
481 * @hook epwr_decay_field
482 * @param {string} $field Current decay field
483 * @param {array} $formatted_args Formatted Elasticsearch arguments
484 * @param {array} $args WP_Query arguments
485 * @return {string} New decay field
486 * @since 4.3.0
487 */
488 $field = apply_filters( 'epwr_decay_field', 'post_date_gmt', $formatted_args, $args );
489 $date_score = array(
490 'function_score' => array(
491 'query' => $formatted_args['query'],
492 'functions' => array(
493 array(
494 $decay_function => array(
495 $field => array(
496 /**
497 * Filter search date weighting scale
498 *
499 * @hook epwr_scale
500 * @param {string} $scale Current scale
501 * @param {array} $formatted_args Formatted Elasticsearch arguments
502 * @param {array} $args WP_Query arguments
503 * @return {string} New scale
504 */
505 'scale' => apply_filters( 'epwr_scale', '14d', $formatted_args, $args ),
506 /**
507 * Filter search date weighting decay
508 *
509 * @hook epwr_decay
510 * @param {float} $decay Current decay
511 * @param {array} $formatted_args Formatted Elasticsearch arguments
512 * @param {array} $args WP_Query arguments
513 * @return {float} New decay
514 */
515 'decay' => apply_filters( 'epwr_decay', 0.25, $formatted_args, $args ),
516 /**
517 * Filter search date weighting offset
518 *
519 * @hook epwr_offset
520 * @param {string} $offset Current offset
521 * @param {array} $formatted_args Formatted Elasticsearch arguments
522 * @param {array} $args WP_Query arguments
523 * @return {string} New offset
524 */
525 'offset' => apply_filters( 'epwr_offset', '7d', $formatted_args, $args ),
526 ),
527 ),
528 ),
529 array(
530 /**
531 * Filter search date weight
532 *
533 * @since 3.5.6
534 * @hook epwr_weight
535 * @param {float} $weight Current weight
536 * @param {array} $formatted_args Formatted Elasticsearch arguments
537 * @param {array} $args WP_Query arguments
538 * @return {float} New weight
539 */
540 'weight' => apply_filters( 'epwr_weight', 0.001, $formatted_args, $args ),
541 ),
542 ),
543 /**
544 * Filter search date weighting score mode
545 *
546 * @hook epwr_score_mode
547 * @param {string} $score_mode Current score mode
548 * @param {array} $formatted_args Formatted Elasticsearch arguments
549 * @param {array} $args WP_Query arguments
550 * @return {string} New score mode
551 */
552 'score_mode' => apply_filters( 'epwr_score_mode', 'sum', $formatted_args, $args ),
553 /**
554 * Filter search date weighting boost mode
555 *
556 * @hook epwr_boost_mode
557 * @param {string} $boost_mode Current boost mode
558 * @param {array} $formatted_args Formatted Elasticsearch arguments
559 * @param {array} $args WP_Query arguments
560 * @return {string} New boost mode
561 */
562 'boost_mode' => apply_filters( 'epwr_boost_mode', 'multiply', $formatted_args, $args ),
563 ),
564 );
565
566 $formatted_args['query'] = $date_score;
567
568 return $formatted_args;
569 }
570
571 /**
572 * Enable integration on search queries
573 *
574 * @param bool $enabled Original enabled value
575 * @param WP_Query $query WP Query
576 * @since 2.1
577 * @return bool
578 */
579 public function integrate_search_queries( $enabled, $query ) {
580 if ( ! Utils\is_integrated_request( $this->slug ) ) {
581 return false;
582 }
583
584 if ( ! is_a( $query, 'WP_Query' ) ) {
585 return $enabled;
586 }
587
588 if ( isset( $query->query_vars['ep_integrate'] ) && ! filter_var( $query->query_vars['ep_integrate'], FILTER_VALIDATE_BOOLEAN ) ) {
589 return false;
590 }
591
592 if ( method_exists( $query, 'is_search' ) && $query->is_search() && ! empty( $query->query_vars['s'] ) ) {
593 $enabled = true;
594 }
595
596 /**
597 * Filter whether to enable integration on search queries or not.
598 *
599 * @hook ep_integrate_search_queries
600 * @since 4.2.0
601 * @param {bool} $enabled Original enabled value
602 * @param {WP_Query} $query WP_Query
603 * @return {bool} New $enabled value
604 */
605 return apply_filters( 'ep_integrate_search_queries', $enabled, $query );
606 }
607
608 /**
609 * Registers post meta for exclude from search feature.
610 */
611 public function register_meta() {
612 register_post_meta(
613 '',
614 'ep_exclude_from_search',
615 [
616 'show_in_rest' => true,
617 'single' => true,
618 'type' => 'boolean',
619 ]
620 );
621 }
622
623 /**
624 * Add ep_exclude_from_search to the allowed meta fields list.
625 *
626 * @since 5.0.0
627 * @param array $keys List of allowed meta fields
628 * @return array
629 */
630 public function add_exclude_from_search( $keys ) {
631 $keys[] = 'ep_exclude_from_search';
632 return $keys;
633 }
634
635 /**
636 * Enqueue block editor assets.
637 */
638 public function enqueue_block_editor_assets() {
639 global $post;
640
641 if ( ! $post instanceof \WP_Post ) {
642 return;
643 }
644
645 if ( ! $post->post_type || ! post_type_supports( $post->post_type, 'custom-fields' ) ) {
646 return;
647 }
648
649 wp_enqueue_script(
650 'ep-search-editor',
651 EP_URL . 'dist/js/search-editor-script.js',
652 Utils\get_asset_info( 'search-editor-script', 'dependencies' ),
653 Utils\get_asset_info( 'search-editor-script', 'version' ),
654 true
655 );
656
657 wp_set_script_translations( 'ep-search-editor', 'elasticpress' );
658 }
659
660 /**
661 * Exclude posts based on ep_exclude_from_search post meta.
662 *
663 * @param array $filters Filters to be applied to the query
664 * @param array $args WP Query args
665 * @param WP_Query $query WP Query object
666 */
667 public function exclude_posts_from_search( $filters, $args, $query ) {
668 if ( ! empty( $query->get( 'ep_skip_search_exclusion' ) ) ) {
669 return $filters;
670 }
671
672 $bypass_exclusion_from_search = ( is_admin() && ! wp_doing_ajax() ) || ! $query->is_search();
673
674 /**
675 * Filter whether the exclusion from the "exclude from search" checkbox should be applied
676 *
677 * @since 4.4.0
678 * @hook ep_bypass_exclusion_from_search
679 * @param {bool} $bypass_exclusion_from_search True means all posts will be returned
680 * @param {WP_Query} $query WP Query
681 * @return {bool} New $bypass_exclusion_from_search value
682 */
683 if ( apply_filters_deprecated(
684 'ep_bypass_exclusion_from_search',
685 [ $bypass_exclusion_from_search, $query ],
686 'ElasticPress 5.3.0',
687 'WP_Query->ep_skip_search_exclusion argument'
688 ) ) {
689 return $filters;
690 }
691
692 $filters[] = [
693 'bool' => [
694 'must_not' => [
695 [
696 'terms' => [
697 'meta.ep_exclude_from_search.raw' => [ '1' ],
698 ],
699 ],
700 ],
701 ],
702 ];
703
704 return $filters;
705 }
706
707 /**
708 * Outputs the checkbox to exclude a post from search.
709 *
710 * @param WP_POST $post Post object.
711 */
712 public function output_exclude_from_search_setting( $post ) {
713 $searchable_post_types = $this->get_searchable_post_types();
714 if ( ! in_array( $post->post_type, $searchable_post_types, true ) ) {
715 return;
716 }
717 ?>
718 <div class="misc-pub-section">
719 <input id="ep_exclude_from_search" name="ep_exclude_from_search" type="checkbox" value="1" <?php checked( get_post_meta( get_the_ID(), 'ep_exclude_from_search', true ) ); ?>>
720 <label for="ep_exclude_from_search"><?php esc_html_e( 'Exclude from search results', 'elasticpress' ); ?></label>
721 <p class="howto">
722 <?php if ( 'attachment' === $post->post_type ) : ?>
723 <?php esc_html_e( 'Excludes this media from the results of your site\'s search form while ElasticPress is active.', 'elasticpress' ); ?>
724 <?php else : ?>
725 <?php esc_html_e( 'Excludes this post from the results of your site\'s search form while ElasticPress is active.', 'elasticpress' ); ?>
726 <?php endif; ?>
727 </p>
728 <?php wp_nonce_field( 'save-exclude-from-search', 'ep-exclude-from-search-nonce' ); ?>
729 </div>
730 <?php
731 }
732
733 /**
734 * Saves exclude from search meta.
735 *
736 * @param int $post_id The post ID.
737 */
738 public function save_exclude_from_search_meta( $post_id ) {
739 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
740 return;
741 }
742
743 if ( ! isset( $_POST['ep-exclude-from-search-nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['ep-exclude-from-search-nonce'] ), 'save-exclude-from-search' ) ) {
744 return;
745 }
746
747 if ( ! current_user_can( 'edit_post', $post_id ) ) {
748 return;
749 }
750
751 if ( isset( $_POST['ep_exclude_from_search'] ) ) {
752 update_post_meta( $post_id, 'ep_exclude_from_search', true );
753 } else {
754 delete_post_meta( $post_id, 'ep_exclude_from_search' );
755 }
756 }
757
758 /**
759 * If WP_Query has unsupported orderby, skip ES query integration and use the WP query instead.
760 *
761 * @param bool $skip Whether to skip ES query integration
762 * @param \WP_Query $query WP_Query object
763 *
764 * @since 4.5
765 * @return bool
766 */
767 public function skip_query_integration( $skip, $query ) {
768 if ( ! $query instanceof \WP_Query ) {
769 return $skip;
770 }
771
772 $unsupported_orderby = [
773 'post__in',
774 'post_name__in',
775 'post_parent__in',
776 'parent',
777 ];
778
779 $orderby = is_string( $query->get( 'orderby' ) ) ? explode( ' ', $query->get( 'orderby' ) ) : $query->get( 'orderby', 'date' );
780
781 $parse_orderby = array();
782 foreach ( $orderby as $key => $value ) {
783 $parse_orderby[] = is_string( $key ) ? $key : $value;
784 }
785
786 if ( array_intersect( $parse_orderby, $unsupported_orderby ) ) {
787 return true;
788 }
789
790 return $skip;
791 }
792
793 /**
794 * Set the `settings_schema` attribute
795 *
796 * @since 5.0.0
797 */
798 protected function set_settings_schema() {
799 $this->settings_schema = [
800 [
801 'default' => '1',
802 'key' => 'decaying_enabled',
803 'label' => __( 'Weighting by date', 'elasticpress' ),
804 'options' => [
805 [
806 'label' => __( 'Don\'t weight results by date', 'elasticpress' ),
807 'value' => '0',
808 ],
809 [
810 'label' => __( 'Weight results by date', 'elasticpress' ),
811 'value' => '1',
812 ],
813 ],
814 'type' => 'radio',
815 ],
816 [
817 'default' => '0',
818 'help' => __( 'Enable to wrap search terms in HTML tags in results for custom styling. The wrapping HTML tag comes with the <code>ep-highlight</code> class for easy styling.', 'elasticpress' ),
819 'key' => 'highlight_enabled',
820 'label' => __( 'Highlight search terms', 'elasticpress' ),
821 'type' => 'checkbox',
822 'field_group_slug' => 'highlight_group',
823 ],
824 [
825 'default' => '0',
826 'help' => __( 'By default, WordPress strips HTML from content excerpts. Enable when using <code>the_excerpt()</code> to display search results.', 'elasticpress' ),
827 'key' => 'highlight_excerpt',
828 'label' => __( 'Highlight search terms in excerpts', 'elasticpress' ),
829 'type' => 'checkbox',
830 'field_group_slug' => 'highlight_group',
831 ],
832 [
833 'default' => 'mark',
834 'help' => __( 'Select the HTML tag used to highlight search terms.', 'elasticpress' ),
835 'key' => 'highlight_tag',
836 'label' => __( 'Highlight tag', 'elasticpress' ),
837 'options' => [
838 [
839 'label' => 'mark',
840 'value' => 'mark',
841 ],
842 [
843 'label' => 'span',
844 'value' => 'span',
845 ],
846 [
847 'label' => 'strong',
848 'value' => 'strong',
849 ],
850 [
851 'label' => 'em',
852 'value' => 'em',
853 ],
854 [
855 'label' => 'i',
856 'value' => 'i',
857 ],
858 ],
859 'type' => 'select',
860 'requires_fields' => [
861 'relationship' => 'OR',
862 'conditions' => [
863 'highlight_enabled' => '1',
864 'highlight_excerpt' => '1',
865 ],
866 ],
867 'field_group_slug' => 'highlight_group',
868 ],
869 [
870 'default' => 'simple',
871 'key' => 'synonyms_editor_mode',
872 'type' => 'hidden',
873 ],
874 ];
875
876 if ( ! defined( 'EP_IS_NETWORK' ) || ! EP_IS_NETWORK ) {
877 $weighting_url = esc_url( admin_url( 'admin.php?page=elasticpress-weighting' ) );
878 $synonyms_url = esc_url( admin_url( 'admin.php?page=elasticpress-synonyms' ) );
879
880 $text = sprintf(
881 '<p><a href="%1$s">%2$s</a></p><p><a href="%3$s">%4$s</a></p>',
882 $weighting_url,
883 __( 'Advanced fields and weighting settings', 'elasticpress' ),
884 $synonyms_url,
885 __( 'Add synonyms to your post searches', 'elasticpress' ),
886 );
887
888 $this->settings_schema[] = [
889 'key' => 'additional_links',
890 'label' => $text,
891 'type' => 'markup',
892 ];
893 }
894 }
895 }
896