PluginProbe
ElasticPress / 4.7.0
ElasticPress v4.7.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 / Search.php

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

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