Skip to content

Commit

Permalink
Block Hooks: Allow child insertion into Template Part block.
Browse files Browse the repository at this point in the history
The Block Hooks mechanism was previously extended to allow insertion of a block as a Navigation block's first or last child. This was implemented by storing the `ignoredHookedBlocks` array in the corresponding `wp_navigation` post's post meta (instead of a metadata attribute on the anchor block).

This changeset extends that mechanism to Template Part blocks, by storing said metadata in the corresponding `wp_template_part` post's post meta, thus allowing extenders to use Block Hooks to insert a block as a Template Part block's first or last child, respectively.

Props tomjcafferkey, bernhard-reiter.
Fixes #60854.

git-svn-id: https://develop.svn.wordpress.org/trunk@58614 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
ockham committed Jul 2, 2024
1 parent cf064ef commit fdebb53
Show file tree
Hide file tree
Showing 5 changed files with 457 additions and 37 deletions.
83 changes: 76 additions & 7 deletions src/wp-includes/block-template-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -606,15 +606,35 @@ function _build_block_template_result_from_file( $template_file, $template_type
$template->area = $template_file['area'];
}

$hooked_blocks = get_hooked_blocks();
$has_hooked_blocks = ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' );
$before_block_visitor = '_inject_theme_attribute_in_template_part_block';
$after_block_visitor = null;
$hooked_blocks = get_hooked_blocks();
if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {

if ( $has_hooked_blocks ) {
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' );
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' );
}
$blocks = parse_blocks( $template->content );
$template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );

if ( 'wp_template_part' === $template->type && $has_hooked_blocks ) {
/**
* In order for hooked blocks to be inserted at positions first_child and last_child in a template part,
* we need to wrap its content a mock template part block and traverse it.
*/
$content = get_comment_delimited_block_content(
'core/template-part',
array(),
$template->content
);
$content = traverse_and_serialize_blocks( parse_blocks( $content ), $before_block_visitor, $after_block_visitor );
$template->content = remove_serialized_parent_block( $content );
} else {
$template->content = traverse_and_serialize_blocks(
parse_blocks( $template->content ),
$before_block_visitor,
$after_block_visitor
);
}

return $template;
}
Expand Down Expand Up @@ -998,8 +1018,28 @@ function _build_block_template_result_from_post( $post ) {
if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' );
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' );
$blocks = parse_blocks( $template->content );
$template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
if ( 'wp_template_part' === $template->type ) {
$existing_ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true );
$attributes = ! empty( $existing_ignored_hooked_blocks ) ? array( 'metadata' => array( 'ignoredHookedBlocks' => json_decode( $existing_ignored_hooked_blocks, true ) ) ) : array();

/**
* In order for hooked blocks to be inserted at positions first_child and last_child in a template part,
* we need to wrap its content a mock template part block and traverse it.
*/
$content = get_comment_delimited_block_content(
'core/template-part',
$attributes,
$template->content
);
$content = traverse_and_serialize_blocks( parse_blocks( $content ), $before_block_visitor, $after_block_visitor );
$template->content = remove_serialized_parent_block( $content );
} else {
$template->content = traverse_and_serialize_blocks(
parse_blocks( $template->content ),
$before_block_visitor,
$after_block_visitor
);
}
}

return $template;
Expand Down Expand Up @@ -1611,7 +1651,36 @@ function inject_ignored_hooked_blocks_metadata_attributes( $changes, $deprecated
return $template;
}

$changes->post_content = apply_block_hooks_to_content( $changes->post_content, $template, 'set_ignored_hooked_blocks_metadata' );
if ( 'wp_template_part' === $post->post_type ) {
$attributes = array();
$existing_ignored_hooked_blocks = isset( $post->ID ) ? get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ) : '';

if ( ! empty( $existing_ignored_hooked_blocks ) ) {
$attributes['metadata'] = array(
'ignoredHookedBlocks' => json_decode( $existing_ignored_hooked_blocks, true ),
);
}

$content = get_comment_delimited_block_content(
'core/template-part',
$attributes,
$changes->post_content
);
$content = apply_block_hooks_to_content( $content, $template, 'set_ignored_hooked_blocks_metadata' );
$changes->post_content = remove_serialized_parent_block( $content );

$wrapper_block_markup = extract_serialized_parent_block( $content );
$wrapper_block = parse_blocks( $wrapper_block_markup )[0];
$ignored_hooked_blocks = $wrapper_block['attrs']['metadata']['ignoredHookedBlocks'] ?? array();
if ( ! empty( $ignored_hooked_blocks ) ) {
if ( ! isset( $changes->meta_input ) ) {
$changes->meta_input = array();
}
$changes->meta_input['_wp_ignored_hooked_blocks'] = wp_json_encode( $ignored_hooked_blocks );
}
} else {
$changes->post_content = apply_block_hooks_to_content( $changes->post_content, $template, 'set_ignored_hooked_blocks_metadata' );
}

return $changes;
}
17 changes: 17 additions & 0 deletions src/wp-includes/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,23 @@ function remove_serialized_parent_block( $serialized_block ) {
return substr( $serialized_block, $start, $end - $start );
}

/**
* Accepts the serialized markup of a block and its inner blocks, and returns serialized markup of the wrapper block.
*
* @since 6.7.0
* @access private
*
* @see remove_serialized_parent_block()
*
* @param string $serialized_block The serialized markup of a block and its inner blocks.
* @return string The serialized markup of the wrapper block.
*/
function extract_serialized_parent_block( $serialized_block ) {
$start = strpos( $serialized_block, '-->' ) + strlen( '-->' );
$end = strrpos( $serialized_block, '<!--' );
return substr( $serialized_block, 0, $start ) . substr( $serialized_block, $end );
}

/**
* Updates the wp_postmeta with the list of ignored hooked blocks where the inner blocks are stored as post content.
* Currently only supports `wp_navigation` post types.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@
* @covers ::_build_block_template_result_from_file
*/
class Tests_Block_Templates_BuildBlockTemplateResultFromFile extends WP_Block_Templates_UnitTestCase {
/**
* Tear down each test method.
*
* @since 6.7.0
*/
public function tear_down() {
$registry = WP_Block_Type_Registry::get_instance();

if ( $registry->is_registered( 'tests/my-block' ) ) {
$registry->unregister( 'tests/my-block' );
}

parent::tear_down();
}

/**
* @ticket 54335
Expand Down Expand Up @@ -178,4 +192,78 @@ public function test_should_ignore_post_types_property_when_building_template_pa

$this->assertEmpty( $template->post_types );
}

/**
* @ticket 60506
*/
public function test_should_inject_hooked_block_into_template_part() {
register_block_type(
'tests/my-block',
array(
'block_hooks' => array(
'core/paragraph' => 'after',
),
)
);

$template_part = _build_block_template_result_from_file(
array(
'slug' => 'header',
'postTypes' => array( 'post' ),
'path' => DIR_TESTDATA . '/templates/template.html',
),
'wp_template_part'
);
$this->assertStringEndsWith( '<!-- wp:tests/my-block /-->', $template_part->content );
}

/*
* @ticket 60506
* @ticket 60854
*/
public function test_should_injected_hooked_block_into_template_part_first_child() {
register_block_type(
'tests/my-block',
array(
'block_hooks' => array(
'core/template-part' => 'first_child',
),
)
);

$template_part = _build_block_template_result_from_file(
array(
'slug' => 'header',
'postTypes' => array( 'post' ),
'path' => DIR_TESTDATA . '/templates/template.html',
),
'wp_template_part'
);
$this->assertStringStartsWith( '<!-- wp:tests/my-block /-->', $template_part->content );
}

/*
* @ticket 60506
* @ticket 60854
*/
public function test_should_injected_hooked_block_into_template_part_last_child() {
register_block_type(
'tests/my-block',
array(
'block_hooks' => array(
'core/template-part' => 'last_child',
),
)
);

$template_part = _build_block_template_result_from_file(
array(
'slug' => 'header',
'postTypes' => array( 'post' ),
'path' => DIR_TESTDATA . '/templates/template.html',
),
'wp_template_part'
);
$this->assertStringEndsWith( '<!-- wp:tests/my-block /-->', $template_part->content );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,50 @@ public function test_should_inject_hooked_block_into_template_part() {
$this->assertStringEndsWith( '<!-- wp:tests/my-block /-->', $template_part->content );
}

/*
* @ticket 59646
* @ticket 60506
* @ticket 60854
*/
public function test_should_injected_hooked_block_into_template_part_first_child() {
register_block_type(
'tests/my-block',
array(
'block_hooks' => array(
'core/template-part' => 'first_child',
),
)
);

$template_part = _build_block_template_result_from_post(
self::$template_part_post,
'wp_template_part'
);
$this->assertStringStartsWith( '<!-- wp:tests/my-block /-->', $template_part->content );
}

/*
* @ticket 59646
* @ticket 60506
* @ticket 60854
*/
public function test_should_injected_hooked_block_into_template_part_last_child() {
register_block_type(
'tests/my-block',
array(
'block_hooks' => array(
'core/template-part' => 'last_child',
),
)
);

$template_part = _build_block_template_result_from_post(
self::$template_part_post,
'wp_template_part'
);
$this->assertStringEndsWith( '<!-- wp:tests/my-block /-->', $template_part->content );
}

/**
* @ticket 59646
* @ticket 60506
Expand Down
Loading

0 comments on commit fdebb53

Please sign in to comment.