Block Hooks: Allow child insertion into Template Part block.

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.
Built from https://develop.svn.wordpress.org/trunk@58614


git-svn-id: http://core.svn.wordpress.org/trunk@58047 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Bernhard Reiter 2024-07-02 10:03:15 +00:00
parent df598e1d98
commit 73b9a4956f
3 changed files with 94 additions and 8 deletions

View File

@ -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;
}
@ -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;
@ -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;
}

View File

@ -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.

View File

@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
$wp_version = '6.7-alpha-58613';
$wp_version = '6.7-alpha-58614';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.