Skip to content

Commit b815a36

Browse files
committed
Block Hooks API: Insert metadata at the same time as hooked blocks.
The Block Hooks UI relies on the `ignoredHookedBlocks` metadata when determining whether to show the toggle in the Site Editor. The problem is that for uncustomized templates we don't add this metadata. Currently the visitor functions have a default callback of `insert_hooked_blocks` and we only add the metadata when we're writing to the database via an available filter in the template controller. This changeset creates a new callback which both inserts the hooked blocks and adds the `ignoredHookedBlocks` metadata to the anchor block, and uses this new callback explicitly in the visitor functions that are run upon reading from the database. We continue to set the `ignoredHookedBlocks` metadata when writing to the database, i.e. this operation happens twice. Although not ideal, this is necessary to cover the following scenarios: * When the user adds an anchor block within the editor, we still need to add the `ignoredHookedBlocks` meta to it to prevent hooked blocks hooking on to it unexpectedly on the frontend. This is required to keep parity between the frontend and editor. * When a user writes template data to the database directly through the API (instead of the editor), we need to again ensure we're not inserting hooked blocks unexpectedly. It is worth noting that with this change, the first hooked block to insert relative to its anchor block will be accepted. Any additional blocks of the same type (e.g. a second `core/loginout` block) trying to hook onto the same anchor block will be ignored, irrespective of the position. Props tomjcafferkey, bernhard-reiter, gziolo. Fixes #59574. git-svn-id: https://develop.svn.wordpress.org/trunk@58186 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 6f6bd30 commit b815a36

5 files changed

Lines changed: 289 additions & 20 deletions

File tree

src/wp-includes/block-template-utils.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -598,8 +598,8 @@ function _build_block_template_result_from_file( $template_file, $template_type
598598
$after_block_visitor = null;
599599
$hooked_blocks = get_hooked_blocks();
600600
if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
601-
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $template );
602-
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $template );
601+
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' );
602+
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' );
603603
}
604604
$blocks = parse_blocks( $template->content );
605605
$template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
@@ -984,8 +984,8 @@ function _build_block_template_result_from_post( $post ) {
984984

985985
$hooked_blocks = get_hooked_blocks();
986986
if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
987-
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $template );
988-
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $template );
987+
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' );
988+
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' );
989989
$blocks = parse_blocks( $template->content );
990990
$template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
991991
}

src/wp-includes/blocks.php

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -858,11 +858,11 @@ function get_hooked_blocks() {
858858
* @since 6.5.0
859859
* @access private
860860
*
861-
* @param array $parsed_anchor_block The anchor block, in parsed block array format.
862-
* @param string $relative_position The relative position of the hooked blocks.
863-
* Can be one of 'before', 'after', 'first_child', or 'last_child'.
864-
* @param array $hooked_blocks An array of hooked block types, grouped by anchor block and relative position.
865-
* @param WP_Block_Template|array $context The block template, template part, or pattern that the anchor block belongs to.
861+
* @param array $parsed_anchor_block The anchor block, in parsed block array format.
862+
* @param string $relative_position The relative position of the hooked blocks.
863+
* Can be one of 'before', 'after', 'first_child', or 'last_child'.
864+
* @param array $hooked_blocks An array of hooked block types, grouped by anchor block and relative position.
865+
* @param WP_Block_Template|WP_Post|array $context The block template, template part, or pattern that the anchor block belongs to.
866866
* @return string
867867
*/
868868
function insert_hooked_blocks( &$parsed_anchor_block, $relative_position, $hooked_blocks, $context ) {
@@ -949,12 +949,12 @@ function insert_hooked_blocks( &$parsed_anchor_block, $relative_position, $hooke
949949
* @since 6.5.0
950950
* @access private
951951
*
952-
* @param array $parsed_anchor_block The anchor block, in parsed block array format.
953-
* @param string $relative_position The relative position of the hooked blocks.
954-
* Can be one of 'before', 'after', 'first_child', or 'last_child'.
955-
* @param array $hooked_blocks An array of hooked block types, grouped by anchor block and relative position.
956-
* @param WP_Block_Template|array $context The block template, template part, or pattern that the anchor block belongs to.
957-
* @return string An empty string.
952+
* @param array $parsed_anchor_block The anchor block, in parsed block array format.
953+
* @param string $relative_position The relative position of the hooked blocks.
954+
* Can be one of 'before', 'after', 'first_child', or 'last_child'.
955+
* @param array $hooked_blocks An array of hooked block types, grouped by anchor block and relative position.
956+
* @param WP_Block_Template|WP_Post|array $context The block template, template part, or pattern that the anchor block belongs to.
957+
* @return string Empty string.
958958
*/
959959
function set_ignored_hooked_blocks_metadata( &$parsed_anchor_block, $relative_position, $hooked_blocks, $context ) {
960960
$anchor_block_type = $parsed_anchor_block['blockName'];
@@ -1002,6 +1002,29 @@ function set_ignored_hooked_blocks_metadata( &$parsed_anchor_block, $relative_po
10021002
return '';
10031003
}
10041004

1005+
/**
1006+
* Returns the markup for blocks hooked to the given anchor block in a specific relative position and then
1007+
* adds a list of hooked block types to an anchor block's ignored hooked block types.
1008+
*
1009+
* This function is meant for internal use only.
1010+
*
1011+
* @since 6.6.0
1012+
* @access private
1013+
*
1014+
* @param array $parsed_anchor_block The anchor block, in parsed block array format.
1015+
* @param string $relative_position The relative position of the hooked blocks.
1016+
* Can be one of 'before', 'after', 'first_child', or 'last_child'.
1017+
* @param array $hooked_blocks An array of hooked block types, grouped by anchor block and relative position.
1018+
* @param WP_Block_Template|WP_Post|array $context The block template, template part, or pattern that the anchor block belongs to.
1019+
* @return string
1020+
*/
1021+
function insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata( &$parsed_anchor_block, $relative_position, $hooked_blocks, $context ) {
1022+
$markup = insert_hooked_blocks( $parsed_anchor_block, $relative_position, $hooked_blocks, $context );
1023+
$markup .= set_ignored_hooked_blocks_metadata( $parsed_anchor_block, $relative_position, $hooked_blocks, $context );
1024+
1025+
return $markup;
1026+
}
1027+
10051028
/**
10061029
* Returns a function that injects the theme attribute into, and hooked blocks before, a given block.
10071030
*

src/wp-includes/class-wp-block-patterns-registry.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@ private function prepare_content( $pattern, $hooked_blocks ) {
174174
$before_block_visitor = '_inject_theme_attribute_in_template_part_block';
175175
$after_block_visitor = null;
176176
if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
177-
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $pattern );
178-
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $pattern );
177+
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $pattern, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' );
178+
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $pattern, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' );
179179
}
180180
$blocks = parse_blocks( $content );
181181
$content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );

tests/phpunit/tests/blocks/getHookedBlocks.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ public function test_loading_template_with_hooked_blocks() {
151151
$template->content
152152
);
153153
$this->assertStringContainsString(
154-
'<!-- wp:post-content {"layout":{"type":"constrained"}} /-->'
154+
'<!-- wp:post-content {"layout":{"type":"constrained"},"metadata":{"ignoredHookedBlocks":["tests/hooked-after"]}} /-->'
155155
. '<!-- wp:tests/hooked-after /-->',
156156
$template->content
157157
);
@@ -180,7 +180,7 @@ public function test_loading_template_part_with_hooked_blocks() {
180180

181181
$this->assertStringContainsString(
182182
'<!-- wp:tests/hooked-before /-->'
183-
. '<!-- wp:navigation {"layout":{"type":"flex","setCascadingProperties":true,"justifyContent":"right"}} /-->',
183+
. '<!-- wp:navigation {"layout":{"type":"flex","setCascadingProperties":true,"justifyContent":"right"},"metadata":{"ignoredHookedBlocks":["tests/hooked-before"]}} /-->',
184184
$template->content
185185
);
186186
$this->assertStringNotContainsString(
@@ -221,7 +221,7 @@ public function test_loading_pattern_with_hooked_blocks() {
221221
$pattern['content']
222222
);
223223
$this->assertStringContainsString(
224-
'<!-- wp:comments -->'
224+
'<!-- wp:comments {"metadata":{"ignoredHookedBlocks":["tests/hooked-first-child"]}} -->'
225225
. '<div class="wp-block-comments">'
226226
. '<!-- wp:tests/hooked-first-child /-->',
227227
str_replace( array( "\n", "\t" ), '', $pattern['content'] )
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
<?php
2+
/**
3+
* Tests for the insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata function.
4+
*
5+
* @package WordPress
6+
* @subpackage Blocks
7+
*
8+
* @since 6.6.0
9+
*
10+
* @group blocks
11+
* @group block-hooks
12+
* @covers ::insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata
13+
*/
14+
class Tests_Blocks_InsertHookedBlocksAndSetIgnoredHookedBlocksMetadata extends WP_UnitTestCase {
15+
const ANCHOR_BLOCK_TYPE = 'tests/anchor-block';
16+
const HOOKED_BLOCK_TYPE = 'tests/hooked-block';
17+
const OTHER_HOOKED_BLOCK_TYPE = 'tests/other-hooked-block';
18+
19+
const HOOKED_BLOCKS = array(
20+
self::ANCHOR_BLOCK_TYPE => array(
21+
'after' => array( self::HOOKED_BLOCK_TYPE ),
22+
'before' => array( self::OTHER_HOOKED_BLOCK_TYPE ),
23+
),
24+
);
25+
26+
/**
27+
* @ticket 59574
28+
*/
29+
private static function create_block_template_object() {
30+
$template = new WP_Block_Template();
31+
$template->type = 'wp_template';
32+
$template->theme = 'test-theme';
33+
$template->slug = 'single';
34+
$template->id = $template->theme . '//' . $template->slug;
35+
$template->title = 'Single';
36+
$template->content = '<!-- wp:tests/anchor-block /-->';
37+
$template->description = 'Description of my template';
38+
39+
return $template;
40+
}
41+
42+
/**
43+
* @ticket 59574
44+
*/
45+
public function test_insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata_returns_correct_markup_and_sets_metadata() {
46+
$anchor_block = array(
47+
'blockName' => self::ANCHOR_BLOCK_TYPE,
48+
);
49+
50+
$actual = insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata( $anchor_block, 'after', self::HOOKED_BLOCKS, array() );
51+
$this->assertSame(
52+
'<!-- wp:' . self::HOOKED_BLOCK_TYPE . ' /-->',
53+
$actual,
54+
"Markup for hooked block wasn't generated correctly."
55+
);
56+
$this->assertSame(
57+
array( 'tests/hooked-block' ),
58+
$anchor_block['attrs']['metadata']['ignoredHookedBlocks'],
59+
"Block wasn't added to ignoredHookedBlocks metadata."
60+
);
61+
}
62+
63+
/**
64+
* @ticket 59574
65+
*/
66+
public function test_insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata_if_block_is_ignored() {
67+
$anchor_block = array(
68+
'blockName' => 'tests/anchor-block',
69+
'attrs' => array(
70+
'metadata' => array(
71+
'ignoredHookedBlocks' => array( self::HOOKED_BLOCK_TYPE ),
72+
),
73+
),
74+
);
75+
76+
$actual = insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata( $anchor_block, 'after', self::HOOKED_BLOCKS, array() );
77+
$this->assertSame(
78+
'',
79+
$actual,
80+
"No markup should've been generated for ignored hooked block."
81+
);
82+
$this->assertSame(
83+
array( 'tests/hooked-block' ),
84+
$anchor_block['attrs']['metadata']['ignoredHookedBlocks'],
85+
"ignoredHookedBlocks metadata shouldn't have been modified."
86+
);
87+
}
88+
89+
/**
90+
* @ticket 59574
91+
*/
92+
public function test_insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata_if_other_block_is_ignored() {
93+
$anchor_block = array(
94+
'blockName' => 'tests/anchor-block',
95+
'attrs' => array(
96+
'metadata' => array(
97+
'ignoredHookedBlocks' => array( 'tests/other-ignored-block' ),
98+
),
99+
),
100+
);
101+
102+
$hooked_blocks = array(
103+
'tests/anchor-block' => array(
104+
'after' => array( 'tests/hooked-block' ),
105+
),
106+
);
107+
108+
$actual = insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata( $anchor_block, 'after', $hooked_blocks, array() );
109+
$this->assertSame(
110+
'<!-- wp:' . self::HOOKED_BLOCK_TYPE . ' /-->',
111+
$actual,
112+
"Markup for newly hooked block should've been generated."
113+
);
114+
$this->assertSame(
115+
array( 'tests/other-ignored-block', 'tests/hooked-block' ),
116+
$anchor_block['attrs']['metadata']['ignoredHookedBlocks']
117+
);
118+
}
119+
120+
/**
121+
* @ticket 59574
122+
*/
123+
public function test_insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata_filter_can_suppress_hooked_block() {
124+
$anchor_block = array(
125+
'blockName' => self::ANCHOR_BLOCK_TYPE,
126+
'attrs' => array(
127+
'layout' => array(
128+
'type' => 'flex',
129+
),
130+
),
131+
'innerContent' => array(),
132+
);
133+
134+
$filter = function ( $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block ) {
135+
// Is the hooked block adjacent to the anchor block?
136+
if ( 'before' !== $relative_position && 'after' !== $relative_position ) {
137+
return $parsed_hooked_block;
138+
}
139+
140+
if (
141+
isset( $parsed_anchor_block['attrs']['layout']['type'] ) &&
142+
'flex' === $parsed_anchor_block['attrs']['layout']['type']
143+
) {
144+
return null;
145+
}
146+
147+
return $parsed_hooked_block;
148+
};
149+
add_filter( 'hooked_block_' . self::HOOKED_BLOCK_TYPE, $filter, 10, 4 );
150+
$actual = insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata( $anchor_block, 'after', self::HOOKED_BLOCKS, array() );
151+
remove_filter( 'hooked_block_' . self::HOOKED_BLOCK_TYPE, $filter );
152+
153+
$this->assertSame( '', $actual, "No markup should've been generated for hooked block suppressed by filter." );
154+
$this->assertSame(
155+
array(),
156+
$anchor_block['attrs']['metadata']['ignoredHookedBlocks'],
157+
"No block should've been added to ignoredHookedBlocks metadata."
158+
);
159+
}
160+
161+
/**
162+
* @ticket 59574
163+
*/
164+
public function test_insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata_added_by_context_aware_filter() {
165+
$anchor_block = array(
166+
'blockName' => 'tests/anchor-block',
167+
'attrs' => array(),
168+
);
169+
170+
$filter = function ( $hooked_block_types, $relative_position, $anchor_block_type, $context ) {
171+
if (
172+
! $context instanceof WP_Block_Template ||
173+
! property_exists( $context, 'slug' ) ||
174+
'single' !== $context->slug
175+
) {
176+
return $hooked_block_types;
177+
}
178+
179+
if ( 'tests/anchor-block' === $anchor_block_type && 'after' === $relative_position ) {
180+
$hooked_block_types[] = 'tests/hooked-block-added-by-filter';
181+
}
182+
183+
return $hooked_block_types;
184+
};
185+
186+
$template = self::create_block_template_object();
187+
188+
add_filter( 'hooked_block_types', $filter, 10, 4 );
189+
$actual = insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata( $anchor_block, 'after', array(), $template );
190+
remove_filter( 'hooked_block_types', $filter, 10 );
191+
192+
$this->assertSame(
193+
'<!-- wp:tests/hooked-block-added-by-filter /-->',
194+
$actual,
195+
"Markup for hooked block added by filter wasn't generated correctly."
196+
);
197+
$this->assertSame(
198+
array( 'tests/hooked-block-added-by-filter' ),
199+
$anchor_block['attrs']['metadata']['ignoredHookedBlocks'],
200+
"Block added by filter wasn't added to ignoredHookedBlocks metadata."
201+
);
202+
}
203+
204+
/**
205+
* @ticket 59574
206+
*/
207+
public function test_insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata_for_block_suppressed_by_filter() {
208+
$anchor_block = array(
209+
'blockName' => 'tests/anchor-block',
210+
'attrs' => array(),
211+
);
212+
213+
$hooked_blocks = array(
214+
'tests/anchor-block' => array(
215+
'after' => array( 'tests/hooked-block', 'tests/hooked-block-suppressed-by-filter' ),
216+
),
217+
);
218+
219+
$filter = function ( $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block ) {
220+
if (
221+
'tests/hooked-block-suppressed-by-filter' === $hooked_block_type &&
222+
'after' === $relative_position &&
223+
'tests/anchor-block' === $parsed_anchor_block['blockName']
224+
) {
225+
return null;
226+
}
227+
228+
return $parsed_hooked_block;
229+
};
230+
231+
add_filter( 'hooked_block', $filter, 10, 4 );
232+
$actual = insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata( $anchor_block, 'after', $hooked_blocks, null );
233+
remove_filter( 'hooked_block', $filter );
234+
235+
$this->assertSame(
236+
'<!-- wp:tests/hooked-block /-->',
237+
$actual,
238+
"Markup for hooked block wasn't generated correctly."
239+
);
240+
$this->assertSame(
241+
array( 'tests/hooked-block' ),
242+
$anchor_block['attrs']['metadata']['ignoredHookedBlocks'],
243+
"ignoredHookedBlocks metadata wasn't set correctly."
244+
);
245+
}
246+
}

0 commit comments

Comments
 (0)