slug = 'search'; $this->title = esc_html__( 'Post Search', 'elasticpress' ); $this->summary = __( 'Instantly find the content you’re looking for. The first time.', 'elasticpress' ); $this->docs_url = __( 'https://elasticpress.zendesk.com/hc/en-us/articles/360050447492-Configuring-ElasticPress-via-the-Plugin-Dashboard#post-search', 'elasticpress' ); $this->requires_install_reindex = false; $this->default_settings = [ 'decaying_enabled' => '1', 'synonyms_editor_mode' => 'simple', 'highlight_enabled' => '0', 'highlight_excerpt' => '0', 'highlight_tag' => 'mark', ]; $this->available_during_installation = true; parent::__construct(); } /** * We need to delay search setup up since it will fire after protected content and protected * content filters into the search setup * * @since 2.2 */ public function setup() { add_action( 'init', [ $this, 'search_setup' ] ); add_filter( 'ep_sanitize_feature_settings', [ $this, 'sanitize_highlighting_settings' ] ); // Set up weighting sub-module $this->weighting = new Weighting(); $this->weighting->setup(); $this->synonyms = new Synonyms(); $this->synonyms->setup(); } /** * Setup feature on each page load * * @since 3.0 */ public function search_setup() { add_filter( 'ep_elasticpress_enabled', [ $this, 'integrate_search_queries' ], 10, 2 ); add_filter( 'ep_formatted_args', [ $this, 'weight_recent' ], 11, 2 ); add_filter( 'ep_query_post_type', [ $this, 'filter_query_post_type_for_search' ], 10, 2 ); add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); add_filter( 'ep_formatted_args', [ $this, 'add_search_highlight_tags' ], 10, 2 ); add_filter( 'ep_highlighting_tag', [ $this, 'get_highlighting_tag' ] ); add_action( 'ep_highlighting_pre_add_highlight', [ $this, 'allow_excerpt_html' ] ); add_action( 'init', [ $this, 'register_meta' ], 20 ); add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] ); add_action( 'pre_get_posts', [ $this, 'exclude_posts_from_search' ] ); add_action( 'post_submitbox_misc_actions', [ $this, 'output_exclude_from_search_setting' ] ); add_action( 'edit_post', [ $this, 'save_exclude_from_search_meta' ], 10, 2 ); } /** * Enqueue styles for highlighting. */ public function enqueue_scripts() { $settings = $this->get_settings(); if ( ! $settings ) { $settings = []; } $settings = wp_parse_args( $settings, $this->default_settings ); if ( true !== $settings['highlight_enabled'] ) { return; } wp_enqueue_style( 'searchterm-highlighting', EP_URL . 'dist/css/highlighting-styles.css', Utils\get_asset_info( 'highlighting-styles', 'dependencies' ), Utils\get_asset_info( 'highlighting-styles', 'version' ) ); } /** * Set default fields to highlight, and outputs * the tags on the front end. * * @param array $formatted_args ep_formatted_args array * @param array $args WP_Query args * @return array $formatted_args formatted args with search highlight tags */ public function add_search_highlight_tags( $formatted_args, $args ) { /** * Fires before the highlighting clause is added to the Elasticsearch query * * @since 3.5.1 * @hook ep_highlighting_pre_add_highlight * @param {array} $formatted_args ep_formatted_args array * @param {string} $args WP_Query args */ do_action( 'ep_highlighting_pre_add_highlight', $formatted_args, $args ); // get current config $settings = $this->get_settings(); if ( ! $settings ) { $settings = []; } $settings = wp_parse_args( $settings, $this->default_settings ); if ( true !== $settings['highlight_enabled'] ) { return $formatted_args; } if ( empty( $args['s'] ) ) { return $formatted_args; } /** * Filter whether to add the `highlight` clause in the query or not. * * @since 3.5.6 * @hook ep_highlight_should_add_clause * @param {bool} $add_highlight_clause True means the clause should be added. * @param {array} $formatted_args ep_formatted_args array * @param {array} $args WP query args * @return {bool} New $add_highlight_clause value */ $add_highlight_clause = apply_filters( 'ep_highlight_should_add_clause', Utils\is_integrated_request( 'highlighting', [ 'public' ] ), $formatted_args, $args ); if ( ! $add_highlight_clause ) { return $formatted_args; } /** * Filter the fields that should be highlighted. * * @since 3.5.1 * @hook ep_highlighting_fields * @param {array} $fields Highlighting fields * @param {array} $formatted_args array * @param {array} $args WP_Query args * @return {array} New Highlighting fields */ $fields_to_highlight = apply_filters( 'ep_highlighting_fields', [ 'post_title', 'post_content' ], $formatted_args, $args ); // define the tag to use $current_tag = $settings['highlight_tag']; /** * Filter the tag that wraps the search highlighted term * * @since 3.5 * @hook ep_highlighting_tag * @param {string} $current_tag Highlighting tag * @return {string} New highlighting tag */ $highlight_tag = apply_filters( 'ep_highlighting_tag', $current_tag ); /** * Filter class applied to search highlight tags * * @since 3.5 * @hook ep_highlighting_class * @param {string} $class Highlighting class * @return {string} New highlighting class */ $highlight_class = apply_filters( 'ep_highlighting_class', 'ep-highlight' ); // tags $opening_tag = '<' . $highlight_tag . " class='" . $highlight_class . "'>"; $closing_tag = ''; foreach ( $fields_to_highlight as $field ) { $formatted_args['highlight']['fields'][ $field ] = [ 'pre_tags' => [ $opening_tag ], 'post_tags' => [ $closing_tag ], 'type' => 'plain', 'number_of_fragments' => 0, ]; } return $formatted_args; } /** * Called by ep_highlighting_pre_add_highlight action. * * Replaces the default excerpt with the custom excerpt, allowing * for the selected tag to be displayed in it. */ public function allow_excerpt_html() { if ( ! Utils\is_integrated_request( 'highlighting', [ 'public' ] ) ) { return; } $settings = $this->get_settings(); if ( ! $settings ) { $settings = []; } $settings = wp_parse_args( $settings, $this->default_settings ); if ( ! empty( $settings['highlight_excerpt'] ) && true === $settings['highlight_excerpt'] ) { remove_filter( 'get_the_excerpt', 'wp_trim_excerpt' ); add_filter( 'get_the_excerpt', [ $this, 'ep_highlight_excerpt' ], 10, 2 ); add_filter( 'ep_highlighting_fields', [ $this, 'ep_highlight_add_excerpt_field' ] ); } } /** * Called by allow_excerpt_html * logic for the excerpt filter allowing the currently selected tag. * * @param string $text excerpt string * @param WP_Post $post Post Object * * @return string $text the new excerpt */ public function ep_highlight_excerpt( $text, $post ) { $settings = $this->get_settings(); if ( ! $settings ) { $settings = []; } $settings = wp_parse_args( $settings, $this->default_settings ); // reproduces wp_trim_excerpt filter, preserving the excerpt_more and excerpt_length filters if ( '' === $text ) { $text = get_the_content( '', false, $post ); $text = apply_filters( 'the_content', $text ); $text = str_replace( '\]\]\>', ']]>', $text ); $text = strip_tags( $text, '<' . esc_html( $settings['highlight_tag'] ) . '>' ); // use the defined length, if already applied... $excerpt_length = apply_filters( 'excerpt_length', 55 ); // use defined excerpt_more filter if it is used $excerpt_more = apply_filters( 'excerpt_more', $text ); $excerpt_more = $excerpt_more !== $text ? $excerpt_more : '[…]'; $words = explode( ' ', $text, $excerpt_length + 1 ); if ( count( $words ) > $excerpt_length ) { array_pop( $words ); array_push( $words, $excerpt_more ); $text = implode( ' ', $words ); } } return $text; } /** * Add `post_content` to the list of fields to highlight. * * @since 3.5.1 * @param array $fields_to_highlight The list of fields to highlight. * @return array */ public function ep_highlight_add_excerpt_field( $fields_to_highlight ) { $fields_to_highlight[] = 'post_excerpt'; return $fields_to_highlight; } /** * Helper filter to check if the tag is allowed. * * @param string $tag - html tag * @return string */ public function get_highlighting_tag( $tag ) { if ( ! in_array( $tag, self::$default_highlight_tags, true ) ) { $tag = 'mark'; } return $tag; } /** * Sanitizes our highlighting settings. * * @param array $settings Array of current settings * @return mixed */ public function sanitize_highlighting_settings( $settings ) { if ( ! empty( $settings['search']['highlight_excerpt'] ) ) { $settings['search']['highlight_excerpt'] = (bool) $settings['search']['highlight_excerpt']; } if ( ! empty( $settings['search']['highlight_enabled'] ) ) { $settings['search']['highlight_enabled'] = (bool) $settings['search']['highlight_enabled']; } return $settings; } /** * Returns searchable post types for the current site * * @since 1.9 * @return mixed|void */ public function get_searchable_post_types() { $post_types = get_post_types( array( 'exclude_from_search' => false ) ); /** * Don't search attachments by default * * @since 3.0 */ unset( $post_types['attachment'] ); /** * Filter searchable post types * * @hook ep_searchable_post_types * @param {array} $post_types Post types * @return {array} New post types */ return apply_filters( 'ep_searchable_post_types', $post_types ); } /** * Make sure we don't search for "any" on a search query * * @param string $post_type Post type * @param WP_Query $query WP Query * @return string|array */ public function filter_query_post_type_for_search( $post_type, $query ) { if ( 'any' === $post_type && $query->is_search() ) { $searchable_post_types = $this->get_searchable_post_types(); // If we have no searchable post types, there's no point going any further if ( empty( $searchable_post_types ) ) { // Have to return something or it improperly calculates the found_posts return false; } // Conform the post types array to an acceptable format for ES $post_types = []; foreach ( $searchable_post_types as $type ) { $post_types[] = $type; } // These are now the only post types we will search $post_type = $post_types; } return $post_type; } /** * Returns true/false if decaying is/isn't enabled * * @return bool */ public function is_decaying_enabled() { $settings = $this->get_settings(); $settings = wp_parse_args( $settings, [ 'decaying_enabled' => true, ] ); return (bool) $settings['decaying_enabled']; } /** * Weight more recent content in searches * * @param array $formatted_args Formatted ES args * @param array $args WP_Query args * @since 2.1 * @return array */ public function weight_recent( $formatted_args, $args ) { if ( empty( $args['s'] ) ) { return $formatted_args; } if ( ! $this->is_decaying_enabled() ) { return $formatted_args; } /** * Filter search date weighting scale * * @hook epwr_decay_function * @param {string} $decay_function Current decay function * @param {array} $formatted_args Formatted Elasticsearch arguments * @param {array} $args WP_Query arguments * @return {string} New decay function */ $decay_function = apply_filters( 'epwr_decay_function', 'exp', $formatted_args, $args ); /** * Filter search date weighting field * * @hook epwr_decay_field * @param {string} $field Current decay field * @param {array} $formatted_args Formatted Elasticsearch arguments * @param {array} $args WP_Query arguments * @return {string} New decay field * @since 4.3.0 */ $field = apply_filters( 'epwr_decay_field', 'post_date_gmt', $formatted_args, $args ); $date_score = array( 'function_score' => array( 'query' => $formatted_args['query'], 'functions' => array( array( $decay_function => array( $field => array( /** * Filter search date weighting scale * * @hook epwr_scale * @param {string} $scale Current scale * @param {array} $formatted_args Formatted Elasticsearch arguments * @param {array} $args WP_Query arguments * @return {string} New scale */ 'scale' => apply_filters( 'epwr_scale', '14d', $formatted_args, $args ), /** * Filter search date weighting decay * * @hook epwr_decay * @param {float} $decay Current decay * @param {array} $formatted_args Formatted Elasticsearch arguments * @param {array} $args WP_Query arguments * @return {float} New decay */ 'decay' => apply_filters( 'epwr_decay', 0.25, $formatted_args, $args ), /** * Filter search date weighting offset * * @hook epwr_offset * @param {string} $offset Current offset * @param {array} $formatted_args Formatted Elasticsearch arguments * @param {array} $args WP_Query arguments * @return {string} New offset */ 'offset' => apply_filters( 'epwr_offset', '7d', $formatted_args, $args ), ), ), ), array( /** * Filter search date weight * * @since 3.5.6 * @hook epwr_weight * @param {float} $weight Current weight * @param {array} $formatted_args Formatted Elasticsearch arguments * @param {array} $args WP_Query arguments * @return {float} New weight */ 'weight' => apply_filters( 'epwr_weight', 0.001, $formatted_args, $args ), ), ), /** * Filter search date weighting score mode * * @hook epwr_score_mode * @param {string} $score_mode Current score mode * @param {array} $formatted_args Formatted Elasticsearch arguments * @param {array} $args WP_Query arguments * @return {string} New score mode */ 'score_mode' => apply_filters( 'epwr_score_mode', 'sum', $formatted_args, $args ), /** * Filter search date weighting boost mode * * @hook epwr_boost_mode * @param {string} $boost_mode Current boost mode * @param {array} $formatted_args Formatted Elasticsearch arguments * @param {array} $args WP_Query arguments * @return {string} New boost mode */ 'boost_mode' => apply_filters( 'epwr_boost_mode', 'multiply', $formatted_args, $args ), ), ); $formatted_args['query'] = $date_score; return $formatted_args; } /** * Output feature box long text * * @since 3.0 */ public function output_feature_box_long() { ?>

slug ) ) { return false; } if ( ! is_a( $query, 'WP_Query' ) ) { return $enabled; } if ( isset( $query->query_vars['ep_integrate'] ) && ! filter_var( $query->query_vars['ep_integrate'], FILTER_VALIDATE_BOOLEAN ) ) { return false; } if ( method_exists( $query, 'is_search' ) && $query->is_search() && ! empty( $query->query_vars['s'] ) ) { $enabled = true; } /** * Filter whether to enable integration on search queries or not. * * @hook ep_integrate_search_queries * @since 4.2.0 * @param {bool} $enabled Original enabled value * @param {WP_Query} $query WP_Query * @return {bool} New $enabled value */ return apply_filters( 'ep_integrate_search_queries', $enabled, $query ); } /** * Display decaying settings on dashboard. * * @since 2.4 */ public function output_feature_box_settings() { $settings = $this->get_settings(); if ( ! $settings ) { $settings = []; } $settings = wp_parse_args( $settings, $this->default_settings ); ?>




true, 'single' => true, 'type' => 'boolean', ] ); } /** * Enqueue block editor assets. */ public function enqueue_block_editor_assets() { global $post; if ( ! $post instanceof \WP_Post ) { return; } wp_enqueue_script( 'ep-search-editor', EP_URL . '/dist/js/search-editor-script.js', Utils\get_asset_info( 'search-editor-script', 'dependencies' ), Utils\get_asset_info( 'search-editor-script', 'version' ), true ); } /** * Exclude posts based on ep_exclude_from_search post meta. * * @param WP_Query $query WP Query */ public function exclude_posts_from_search( $query ) { $bypass_exclusion_from_search = is_admin() || ! $query->is_search(); /** * Filter whether the exclusion from the "exclude from search" checkbox should be applied * * @since 4.4.0 * @hook ep_bypass_exclusion_from_search * @param {bool} $bypass_exclusion_from_search True means all posts will be returned * @param {WP_Query} $query WP Query * @return {bool} New $bypass_exclusion_from_search value */ if ( apply_filters( 'ep_bypass_exclusion_from_search', $bypass_exclusion_from_search, $query ) ) { return; } // Get any meta query that's being added before. $meta_query = (array) $query->get( 'meta_query' ); $exclude_from_search_query = [ 'relation' => 'or', [ 'key' => 'ep_exclude_from_search', 'compare' => 'NOT EXISTS', ], [ 'key' => 'ep_exclude_from_search', 'value' => '1', 'compare' => '!=', ], ]; /** * If the current meta query only has an `OR` clause, wrap it with an `AND` * so the criteria here is not made "optional". */ if ( empty( $meta_query ) || empty( $meta_query['relation'] ) || 'and' === strtolower( $meta_query['relation'] ) ) { $meta_query[] = $exclude_from_search_query; } else { $meta_query = [ 'relation' => 'and', $exclude_from_search_query, $meta_query, ]; } $query->set( 'meta_query', $meta_query ); } /** * Outputs the checkbox to exclude a post from search. * * @param WP_POST $post Post object. */ public function output_exclude_from_search_setting( $post ) { $searchable_post_types = $this->get_searchable_post_types(); if ( ! in_array( $post->post_type, $searchable_post_types, true ) ) { return; } ?>
>