diff --git a/includes/classes/Indexable/Post/SyncManager.php b/includes/classes/Indexable/Post/SyncManager.php index e4cf27dae..9c4052402 100644 --- a/includes/classes/Indexable/Post/SyncManager.php +++ b/includes/classes/Indexable/Post/SyncManager.php @@ -31,6 +31,13 @@ class SyncManager extends SyncManagerAbstract { */ public $indexable_slug = 'post'; + /** + * Delete all post meta from other posts associated with a deleted post. Useful for attachments. + * + * @var bool + */ + public $delete_all_meta = false; + /** * Setup actions and filters * @@ -51,6 +58,8 @@ public function setup() { add_action( 'delete_post', array( $this, 'action_delete_post' ) ); add_action( 'updated_post_meta', array( $this, 'action_queue_meta_sync' ), 10, 4 ); add_action( 'added_post_meta', array( $this, 'action_queue_meta_sync' ), 10, 4 ); + // Called just because we need to know somehow if $delete_all is set before action_queue_meta_sync() runs. + add_filter( 'delete_post_metadata', array( $this, 'maybe_delete_meta_for_all' ), 10, 5 ); add_action( 'deleted_post_meta', array( $this, 'action_queue_meta_sync' ), 10, 4 ); add_action( 'wp_initialize_site', array( $this, 'action_create_blog_index' ) ); @@ -58,6 +67,22 @@ public function setup() { add_filter( 'ep_sync_delete_permissions_bypass', array( $this, 'filter_bypass_permission_checks_for_machines' ) ); } + /** + * Whether to delete all meta from other posts that is associated with the deleted post. + * + * @param bool $check Whether to allow metadata deletion of the given type. + * @param int $object_id ID of the object metadata is for. + * @param string $meta_key Metadata key. + * @param mixed $meta_value Metadata value. Must be serializable if non-scalar. + * @param bool $delete_all Whether to delete the matching metadata entries + * for all objects, ignoring the specified $object_id + * @return bool + */ + public function maybe_delete_meta_for_all( $check, $object_id, $meta_key, $meta_value, $delete_all ) { + $this->delete_all_meta = $delete_all; + return $check; + } + /** * Filter to allow cron and WP CLI processes to index/delete documents * @@ -95,9 +120,6 @@ public function action_queue_meta_sync( $meta_id, $object_id, $meta_key, $meta_v $indexable = Indexables::factory()->get( $this->indexable_slug ); - $indexable_post_statuses = $indexable->get_indexable_post_status(); - $post_type = get_post_type( $object_id ); - if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { // Bypass saving if doing autosave // @codeCoverageIgnoreStart @@ -122,34 +144,63 @@ public function action_queue_meta_sync( $meta_id, $object_id, $meta_key, $meta_v return; } - $allowed_meta_to_be_indexed = $indexable->prepare_meta( $post ); - if ( ! in_array( $meta_key, array_keys( $allowed_meta_to_be_indexed ), true ) ) { - return; - } + if ( empty( $object_id ) && $this->delete_all_meta ) { + add_filter( 'ep_is_integrated_request', '__return_true' ); + + $query = new \WP_Query( + [ + 'ep_integrate' => true, + 'meta_key' => $meta_key, + 'meta_value' => $meta_value, + 'fields' => 'ids', + ] + ); + + remove_filter( 'ep_is_integrated_request', '__return_true' ); + + if ( $query->have_posts() && $query->elasticsearch_success ) { + $posts_to_be_synced = array_filter( + $query->posts, + function( $object_id ) { + return ! apply_filters( 'ep_post_sync_kill', false, $object_id, $object_id ); + } + ); + if ( ! empty( $posts_to_be_synced ) ) { + $indexable->bulk_index( $posts_to_be_synced ); + } + } + } else { + $indexable_post_statuses = $indexable->get_indexable_post_status(); + $post_type = get_post_type( $object_id ); - if ( in_array( $post->post_status, $indexable_post_statuses, true ) ) { - $indexable_post_types = $indexable->get_indexable_post_types(); + $allowed_meta_to_be_indexed = $indexable->prepare_meta( $post ); + if ( ! in_array( $meta_key, array_keys( $allowed_meta_to_be_indexed ), true ) ) { + return; + } - if ( in_array( $post_type, $indexable_post_types, true ) ) { - /** - * Filter to kill post sync - * - * @hook ep_post_sync_kill - * @param {bool} $skip True meanas kill sync for post - * @param {int} $object_id ID of post - * @param {int} $object_id ID of post - * @return {boolean} New value - */ - if ( apply_filters( 'ep_post_sync_kill', false, $object_id, $object_id ) ) { - return; + if ( in_array( $post->post_status, $indexable_post_statuses, true ) ) { + $indexable_post_types = $indexable->get_indexable_post_types(); + + if ( in_array( $post_type, $indexable_post_types, true ) ) { + /** + * Filter to kill post sync + * + * @hook ep_post_sync_kill + * @param {bool} $skip True meanas kill sync for post + * @param {int} $object_id ID of post + * @param {int} $object_id ID of post + * @return {boolean} New value + */ + if ( apply_filters( 'ep_post_sync_kill', false, $object_id, $object_id ) ) { + return; + } + + $this->add_to_queue( $object_id ); } - - $this->add_to_queue( $object_id ); } } } - /** * Delete ES post when WP post is deleted * diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php index b4e8c462c..3085a3027 100644 --- a/tests/php/indexables/TestPost.php +++ b/tests/php/indexables/TestPost.php @@ -6447,4 +6447,55 @@ public function testTaxQueryWithCategoryId() { $this->assertArrayHasKey( 'terms.category.term_id', $args['post_filter']['bool']['must'][0]['bool']['must'][0]['terms'] ); $this->assertContains( $cat, $args['post_filter']['bool']['must'][0]['bool']['must'][0]['terms']['terms.category.term_id'] ); } + + /** + * Test if EP updates all posts when `delete_metadata()` is called with `$delete_all = true` + * + * @group post + */ + public function testDeleteAllMetadata() { + Functions\create_and_sync_post( + array( 'post_title' => 'one' ), + array( + 'common_meta_one' => 'lorem', + 'common_meta_two' => 'ipsum', + ) + ); + Functions\create_and_sync_post( + array( 'post_title' => 'two' ), + array( + 'common_meta_one' => 'lorem', + 'common_meta_two' => 'ipsum', + ) + ); + + delete_metadata( 'post', null, 'common_meta_one', 'lorem', true ); + + ElasticPress\Indexables::factory()->get( 'post' )->sync_manager->index_sync_queue(); + ElasticPress\Elasticsearch::factory()->refresh_indices(); + + $query = new \WP_Query( + array( + 'post_type' => 'post', + 'ep_integrate' => true, + 'meta_key' => 'common_meta_one', + 'meta_value' => 'lorem', + ) + ); + + $this->assertTrue( $query->elasticsearch_success ); + $this->assertEquals( $query->found_posts, 0 ); + + $query = new \WP_Query( + array( + 'post_type' => 'post', + 'ep_integrate' => true, + 'meta_key' => 'common_meta_two', + 'meta_value' => 'ipsum', + ) + ); + + $this->assertTrue( $query->elasticsearch_success ); + $this->assertEquals( $query->found_posts, 2 ); + } }