Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Option to exclude post from Search #3100

Merged
merged 21 commits into from
Nov 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions assets/js/search/editor/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* WordPress dependencies.
*/
import { registerPlugin } from '@wordpress/plugins';

/**
* Internal dependencies.
*/
import ExcludeFromSearch from './plugins/exclude-from-search';

registerPlugin('ep-exclude-from-search', {
render: ExcludeFromSearch,
icon: null,
});
29 changes: 29 additions & 0 deletions assets/js/search/editor/plugins/exclude-from-search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* WordPress dependencies.
*/
import { CheckboxControl } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import { PluginPostStatusInfo } from '@wordpress/edit-post';
import { __ } from '@wordpress/i18n';

export default () => {
const { editPost } = useDispatch('core/editor');

const { ep_exclude_from_search = false, ...meta } = useSelect((select) =>
select('core/editor').getEditedPostAttribute('meta'),
);

const onChange = (ep_exclude_from_search) => {
editPost({ meta: { ...meta, ep_exclude_from_search } });
};

return (
<PluginPostStatusInfo>
<CheckboxControl
label={__('Exclude from search', 'elasticpress')}
checked={ep_exclude_from_search}
onChange={onChange}
/>
</PluginPostStatusInfo>
);
};
123 changes: 123 additions & 0 deletions includes/classes/Feature/Search/Search.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ public function search_setup() {
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 );
}


Expand Down Expand Up @@ -663,4 +669,121 @@ public function output_feature_box_settings() {

<?php
}

/**
* Registers post meta for exclude from search feature.
*/
public function register_meta() {
register_post_meta(
'',
'ep_exclude_from_search',
[
'show_in_rest' => true,
'single' => true,
'type' => 'boolean',
]
);
}

/**
* Enqueue block editor assets.
*/
public function enqueue_block_editor_assets() {
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 ) {

if ( ! $query->is_search() || $query->is_admin() ) {
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;
}
?>
<div class="misc-pub-section">
<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 ) ); ?>>
<label for="ep_exclude_from_search"><?php esc_html_e( 'Exclude from Search', 'elasticpress' ); ?></label>
<?php wp_nonce_field( 'save-exclude-from-search', 'ep-exclude-from-search-nonce' ); ?>
</div>
<?php
}

/**
* Saves exclude from search meta.
*
* @param int $post_id The post ID.
* @param WP_Post $post Post object.
*/
public function save_exclude_from_search_meta( $post_id, $post ) {

if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}

if ( ! isset( $_POST['ep-exclude-from-search-nonce'] ) || ! wp_verify_nonce( $_POST['ep-exclude-from-search-nonce'], 'save-exclude-from-search' ) ) {
return;
}

if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}

$exclude_from_search = isset( $_POST['ep_exclude_from_search'] ) ? true : false;

update_post_meta( $post_id, 'ep_exclude_from_search', $exclude_from_search );
}
}
25 changes: 0 additions & 25 deletions includes/classes/Indexable.php
Original file line number Diff line number Diff line change
Expand Up @@ -778,31 +778,6 @@ public function build_meta_query( $meta_queries ) {
];

foreach ( $meta_queries as $single_meta_query ) {

/**
* There is a strange case where meta_query looks like this:
* array(
* "something" => array(
* array(
* 'key' => ...
* ...
* )
* )
* )
*
* Somehow WordPress (WooCommerce) handles that case so we need to as well.
*
* @since 2.1
*/
if ( is_array( $single_meta_query ) && empty( $single_meta_query['key'] ) ) {
reset( $single_meta_query );
$first_key = key( $single_meta_query );

if ( is_array( $single_meta_query[ $first_key ] ) ) {
$single_meta_query = $single_meta_query[ $first_key ];
}
}

if ( ! empty( $single_meta_query['key'] ) ) {

$terms_obj = false;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"synonyms-script": "./assets/js/synonyms/index.js",
"admin-script": "./assets/js/admin.js",
"weighting-script": "./assets/js/weighting.js",
"search-editor-script": "./assets/js/search/editor/index.js",
"autosuggest-styles": "./assets/css/autosuggest.css",
"comments-styles": "./assets/css/comments.css",
"dashboard-styles": "./assets/css/dashboard.css",
Expand Down
104 changes: 104 additions & 0 deletions tests/php/indexables/TestPost.php
Original file line number Diff line number Diff line change
Expand Up @@ -7633,6 +7633,36 @@ public function testHighlightTags() {

}

/**
* Tests query doesn't return the post in if `ep_exclude_from_search` meta is set.
*/
public function testExcludeFromSearchQuery() {

$this->ep_factory->post->create_many(
2,
array(
'post_content' => 'find me in search',
'meta_input' => array( 'ep_exclude_from_search' => false ),
)
);
$this->ep_factory->post->create(
array(
'post_content' => 'exlcude from search',
'meta_input' => array( 'ep_exclude_from_search' => true ),
)
);

ElasticPress\Elasticsearch::factory()->refresh_indices();

$args = array(
's' => 'search',
);
$query = new \WP_Query( $args );

$this->assertTrue( $query->elasticsearch_success );
$this->assertEquals( 2, $query->post_count );
}

/**
* Tests search term is wrapped in html tag with custom class
*/
Expand Down Expand Up @@ -7786,4 +7816,78 @@ function( $preempt, $parsed_args, $url ) {
$this->assertTrue( $query->elasticsearch_success );
}

/**
* Tests exclude from search only works on the search query.
*/
public function testExcludeFromSearchOnlyRunOnSearchQuery() {

$this->ep_factory->post->create_many(
5,
array(
'post_content' => 'test post',
'meta_input' => array( 'ep_exclude_from_search' => true ),
)
);

ElasticPress\Elasticsearch::factory()->refresh_indices();

$args = array(
'ep_integrate' => true,
);
$query = new \WP_Query( $args );

$this->assertTrue( $query->elasticsearch_success );
$this->assertEquals( 5, $query->post_count );

$args = array(
's' => 'test',
);
$query = new \WP_Query( $args );

$this->assertTrue( $query->elasticsearch_success );
$this->assertEquals( 0, $query->post_count );
}

/**
* Test exclude from search when meta query is set.
*/
public function testExcludeFromSearchQueryWithMetaQuery() {

$this->ep_factory->post->create(
array(
'post_content' => 'test post',
'meta_input' => array(
'ep_exclude_from_search' => true,
'test_key' => 'test',
),
)
);

$this->ep_factory->post->create(
array(
'post_content' => 'test post',
'meta_input' => array(
'ep_exclude_from_search' => false,
'test_key' => 'test',
),
)
);

ElasticPress\Elasticsearch::factory()->refresh_indices();

$args = array(
's' => 'test',
'meta_query' => array(
array(
'key' => 'test_key',
'value' => 'test',
),
),
);
$query = new \WP_Query( $args );

$this->assertTrue( $query->elasticsearch_success );
$this->assertEquals( 1, $query->post_count );

}
}