Skip to content

Commit 502624f

Browse files
committed
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 #11410. Props bernhard-reiter, czarate, ingeniumed. Fixes #65008. git-svn-id: https://develop.svn.wordpress.org/trunk@62219 602fd350-edb4-49c9-b593-d223f7449a82
1 parent ac186d7 commit 502624f

File tree

2 files changed

+92
-9
lines changed

2 files changed

+92
-9
lines changed

src/wp-includes/blocks.php

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,7 @@ function apply_block_hooks_to_content( $content, $context = null, $callback = 'i
11961196
* of the block that corresponds to the post type are handled correctly.
11971197
*
11981198
* @since 6.8.0
1199+
* @since 7.0.0 Added the `$ignored_hooked_blocks_at_root` parameter.
11991200
* @access private
12001201
*
12011202
* @param string $content Serialized content.
@@ -1205,9 +1206,17 @@ function apply_block_hooks_to_content( $content, $context = null, $callback = 'i
12051206
* @param callable $callback A function that will be called for each block to generate
12061207
* the markup for a given list of blocks that are hooked to it.
12071208
* Default: 'insert_hooked_blocks'.
1209+
* @param array|null $ignored_hooked_blocks_at_root A reference to an array that will be populated
1210+
* with the ignored hooked blocks at the root level.
1211+
* Default: `null`.
12081212
* @return string The serialized markup.
12091213
*/
1210-
function apply_block_hooks_to_content_from_post_object( $content, $post = null, $callback = 'insert_hooked_blocks' ) {
1214+
function apply_block_hooks_to_content_from_post_object(
1215+
$content,
1216+
$post = null,
1217+
$callback = 'insert_hooked_blocks',
1218+
&$ignored_hooked_blocks_at_root = null
1219+
) {
12111220
// Default to the current post if no context is provided.
12121221
if ( null === $post ) {
12131222
$post = get_post();
@@ -1287,6 +1296,16 @@ function apply_block_hooks_to_content_from_post_object( $content, $post = null,
12871296
$content = apply_block_hooks_to_content( $content, $post, $callback );
12881297
remove_filter( 'hooked_block_types', $suppress_blocks_from_insertion_before_and_after_wrapper_block, PHP_INT_MAX );
12891298

1299+
if ( null !== $ignored_hooked_blocks_at_root ) {
1300+
// Check wrapper block's metadata for ignored hooked blocks at the root level, and populate the reference parameter if needed.
1301+
$wrapper_block_markup = extract_serialized_parent_block( $content );
1302+
$wrapper_block = parse_blocks( $wrapper_block_markup )[0];
1303+
1304+
if ( ! empty( $wrapper_block['attrs']['metadata']['ignoredHookedBlocks'] ) ) {
1305+
$ignored_hooked_blocks_at_root = $wrapper_block['attrs']['metadata']['ignoredHookedBlocks'];
1306+
}
1307+
}
1308+
12901309
// Finally, we need to remove the temporary wrapper block.
12911310
$content = remove_serialized_parent_block( $content );
12921311

@@ -1449,6 +1468,7 @@ function insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata( &$parsed_a
14491468
*
14501469
* @since 6.6.0
14511470
* @since 6.8.0 Support non-`wp_navigation` post types.
1471+
* @since 7.0.0 Set `_wp_ignored_hooked_blocks` meta in the response for blocks hooked at the root level.
14521472
*
14531473
* @param WP_REST_Response $response The response object.
14541474
* @param WP_Post $post Post object.
@@ -1459,12 +1479,18 @@ function insert_hooked_blocks_into_rest_response( $response, $post ) {
14591479
return $response;
14601480
}
14611481

1482+
$ignored_hooked_blocks_at_root = array();
14621483
$response->data['content']['raw'] = apply_block_hooks_to_content_from_post_object(
14631484
$response->data['content']['raw'],
14641485
$post,
1465-
'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata'
1486+
'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata',
1487+
$ignored_hooked_blocks_at_root
14661488
);
14671489

1490+
if ( ! empty( $ignored_hooked_blocks_at_root ) ) {
1491+
$response->data['meta']['_wp_ignored_hooked_blocks'] = wp_json_encode( $ignored_hooked_blocks_at_root );
1492+
}
1493+
14681494
// If the rendered content was previously empty, we leave it like that.
14691495
if ( empty( $response->data['content']['rendered'] ) ) {
14701496
return $response;

tests/phpunit/tests/blocks/applyBlockHooksToContentFromPostObject.php

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,21 +130,59 @@ public function test_apply_block_hooks_to_content_from_post_object_inserts_hooke
130130
$this->assertSame( $expected, $actual );
131131
}
132132

133+
/**
134+
* @ticket 65008
135+
*/
136+
public function test_apply_block_hooks_to_content_from_post_object_sets_ignored_hooked_blocks() {
137+
$ignored_hooked_blocks_at_root = array();
138+
139+
$expected = '<!-- wp:tests/hooked-block-first-child /-->' .
140+
'<!-- wp:heading {"level":1,"metadata":{"ignoredHookedBlocks":["tests/hooked-block"]}} -->' .
141+
'<h1>Hello World!</h1>' .
142+
'<!-- /wp:heading -->' .
143+
'<!-- wp:tests/hooked-block /-->';
144+
$actual = apply_block_hooks_to_content_from_post_object(
145+
self::$post->post_content,
146+
self::$post,
147+
'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata',
148+
$ignored_hooked_blocks_at_root
149+
);
150+
$this->assertSame( $expected, $actual, "Markup wasn't updated correctly." );
151+
$this->assertSame(
152+
array( 'tests/hooked-block-first-child' ),
153+
$ignored_hooked_blocks_at_root,
154+
"Hooked block added at 'first_child' position wasn't added to ignoredHookedBlocks metadata."
155+
);
156+
}
157+
133158
/**
134159
* @ticket 62716
160+
* @ticket 65008
135161
*/
136162
public function test_apply_block_hooks_to_content_from_post_object_respects_ignored_hooked_blocks_post_meta() {
137-
$expected = self::$post_with_ignored_hooked_block->post_content . '<!-- wp:tests/hooked-block /-->';
163+
$ignored_hooked_blocks_at_root = array();
164+
165+
$expected = '<!-- wp:heading {"level":1,"metadata":{"ignoredHookedBlocks":["tests/hooked-block"]}} -->' .
166+
'<h1>Hello World!</h1>' .
167+
'<!-- /wp:heading -->' .
168+
'<!-- wp:tests/hooked-block /-->';
138169
$actual = apply_block_hooks_to_content_from_post_object(
139170
self::$post_with_ignored_hooked_block->post_content,
140171
self::$post_with_ignored_hooked_block,
141-
'insert_hooked_blocks'
172+
'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata',
173+
$ignored_hooked_blocks_at_root
142174
);
143175
$this->assertSame( $expected, $actual );
176+
$this->assertSame(
177+
array( 'tests/hooked-block-first-child' ),
178+
$ignored_hooked_blocks_at_root,
179+
"Pre-existing ignored hooked block at root level wasn't reflected in metadata."
180+
);
144181
}
145182

146183
/**
147184
* @ticket 63287
185+
* @ticket 65008
148186
*/
149187
public function test_apply_block_hooks_to_content_from_post_object_does_not_insert_hooked_block_before_container_block() {
150188
$filter = function ( $hooked_block_types, $relative_position, $anchor_block_type ) {
@@ -155,31 +193,50 @@ public function test_apply_block_hooks_to_content_from_post_object_does_not_inse
155193
return $hooked_block_types;
156194
};
157195

196+
$ignored_hooked_blocks_at_root = array();
197+
158198
$expected = '<!-- wp:tests/hooked-block-first-child /-->' .
159-
self::$post->post_content .
199+
'<!-- wp:heading {"level":1,"metadata":{"ignoredHookedBlocks":["tests/hooked-block"]}} -->' .
200+
'<h1>Hello World!</h1>' .
201+
'<!-- /wp:heading -->' .
160202
'<!-- wp:tests/hooked-block /-->';
161203

162204
add_filter( 'hooked_block_types', $filter, 10, 3 );
163205
$actual = apply_block_hooks_to_content_from_post_object(
164206
self::$post->post_content,
165207
self::$post,
166-
'insert_hooked_blocks'
208+
'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata',
209+
$ignored_hooked_blocks_at_root
167210
);
168211
remove_filter( 'hooked_block_types', $filter, 10 );
169212

170-
$this->assertSame( $expected, $actual );
213+
$this->assertSame( $expected, $actual, "Hooked block added before 'core/post-content' block shouldn't be inserted." );
214+
$this->assertSame(
215+
array( 'tests/hooked-block-first-child' ),
216+
$ignored_hooked_blocks_at_root,
217+
"ignoredHookedBlocks metadata wasn't set correctly."
218+
);
171219
}
172220

173221
/**
174222
* @ticket 62716
223+
* @ticket 65008
175224
*/
176225
public function test_apply_block_hooks_to_content_from_post_object_inserts_hooked_block_if_content_contains_no_blocks() {
226+
$ignored_hooked_blocks_at_root = array();
227+
177228
$expected = '<!-- wp:tests/hooked-block-first-child /-->' . self::$post_with_non_block_content->post_content;
178229
$actual = apply_block_hooks_to_content_from_post_object(
179230
self::$post_with_non_block_content->post_content,
180231
self::$post_with_non_block_content,
181-
'insert_hooked_blocks'
232+
'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata',
233+
$ignored_hooked_blocks_at_root
234+
);
235+
$this->assertSame( $expected, $actual, "Markup wasn't updated correctly." );
236+
$this->assertSame(
237+
array( 'tests/hooked-block-first-child' ),
238+
$ignored_hooked_blocks_at_root,
239+
"Hooked block added at 'first_child' position wasn't added to ignoredHookedBlocks metadata."
182240
);
183-
$this->assertSame( $expected, $actual );
184241
}
185242
}

0 commit comments

Comments
 (0)