From f896394e1ca70116d08df26ddb64276c347a8d63 Mon Sep 17 00:00:00 2001 From: jonsurrell Date: Wed, 8 Apr 2026 12:26:35 +0000 Subject: [PATCH] Block Hooks: Set ignored blocks meta in REST API response. Set `_wp_ignored_hooked_blocks` post meta in the REST API response sent from post-like endpoints that support Block Hooks (see `rest_block_hooks_post_types` filter). Previously, it was enough to set that post meta on write (i.e. save to DB). However, due to the way real-time collaboration syncs posts and reconciles them with content received from the server side, this information is now vital on the client side to ensure hooked blocks aren't duplicated. Developed in https://github.com/WordPress/wordpress-develop/pull/11410. Props bernhard-reiter, czarate, ingeniumed. Fixes #65008. Built from https://develop.svn.wordpress.org/trunk@62219 git-svn-id: http://core.svn.wordpress.org/trunk@61499 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/blocks.php | 30 ++++++++++++++++++++++++++++-- wp-includes/version.php | 2 +- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/wp-includes/blocks.php b/wp-includes/blocks.php index 170d7c0fbf..cc1ac60667 100644 --- a/wp-includes/blocks.php +++ b/wp-includes/blocks.php @@ -1196,6 +1196,7 @@ function apply_block_hooks_to_content( $content, $context = null, $callback = 'i * of the block that corresponds to the post type are handled correctly. * * @since 6.8.0 + * @since 7.0.0 Added the `$ignored_hooked_blocks_at_root` parameter. * @access private * * @param string $content Serialized content. @@ -1205,9 +1206,17 @@ function apply_block_hooks_to_content( $content, $context = null, $callback = 'i * @param callable $callback A function that will be called for each block to generate * the markup for a given list of blocks that are hooked to it. * Default: 'insert_hooked_blocks'. + * @param array|null $ignored_hooked_blocks_at_root A reference to an array that will be populated + * with the ignored hooked blocks at the root level. + * Default: `null`. * @return string The serialized markup. */ -function apply_block_hooks_to_content_from_post_object( $content, $post = null, $callback = 'insert_hooked_blocks' ) { +function apply_block_hooks_to_content_from_post_object( + $content, + $post = null, + $callback = 'insert_hooked_blocks', + &$ignored_hooked_blocks_at_root = null +) { // Default to the current post if no context is provided. if ( null === $post ) { $post = get_post(); @@ -1287,6 +1296,16 @@ function apply_block_hooks_to_content_from_post_object( $content, $post = null, $content = apply_block_hooks_to_content( $content, $post, $callback ); remove_filter( 'hooked_block_types', $suppress_blocks_from_insertion_before_and_after_wrapper_block, PHP_INT_MAX ); + if ( null !== $ignored_hooked_blocks_at_root ) { + // Check wrapper block's metadata for ignored hooked blocks at the root level, and populate the reference parameter if needed. + $wrapper_block_markup = extract_serialized_parent_block( $content ); + $wrapper_block = parse_blocks( $wrapper_block_markup )[0]; + + if ( ! empty( $wrapper_block['attrs']['metadata']['ignoredHookedBlocks'] ) ) { + $ignored_hooked_blocks_at_root = $wrapper_block['attrs']['metadata']['ignoredHookedBlocks']; + } + } + // Finally, we need to remove the temporary wrapper block. $content = remove_serialized_parent_block( $content ); @@ -1449,6 +1468,7 @@ function insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata( &$parsed_a * * @since 6.6.0 * @since 6.8.0 Support non-`wp_navigation` post types. + * @since 7.0.0 Set `_wp_ignored_hooked_blocks` meta in the response for blocks hooked at the root level. * * @param WP_REST_Response $response The response object. * @param WP_Post $post Post object. @@ -1459,12 +1479,18 @@ function insert_hooked_blocks_into_rest_response( $response, $post ) { return $response; } + $ignored_hooked_blocks_at_root = array(); $response->data['content']['raw'] = apply_block_hooks_to_content_from_post_object( $response->data['content']['raw'], $post, - 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' + 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata', + $ignored_hooked_blocks_at_root ); + if ( ! empty( $ignored_hooked_blocks_at_root ) ) { + $response->data['meta']['_wp_ignored_hooked_blocks'] = wp_json_encode( $ignored_hooked_blocks_at_root ); + } + // If the rendered content was previously empty, we leave it like that. if ( empty( $response->data['content']['rendered'] ) ) { return $response; diff --git a/wp-includes/version.php b/wp-includes/version.php index 63948d2dcc..42c64e1c43 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '7.1-alpha-62218'; +$wp_version = '7.1-alpha-62219'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.