From b020b063e67afe6db6a991b3cd7e7db49f363b00 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Thu, 20 Jun 2024 15:21:34 +0100 Subject: [PATCH 01/30] Update template utils to accommodate first_child and last_child hooked blocks for template parts --- src/wp-includes/block-template-utils.php | 39 +++++++++++++++++++++--- src/wp-includes/blocks.php | 34 ++++++++++++++++++--- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index 2681acb5686b3..5bacc7ed6bab1 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -612,9 +612,19 @@ function _build_block_template_result_from_file( $template_file, $template_type 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 ); + if ( 'wp_template_part' === $template->type ) { + /** + * In order for hooked blocks to be inserted at positions first_child and last_child in a template part, + * we need to create a mock template part block and traverse it. + */ + $mock_template_part_block = _make_mock_parsed_block( 'core/template-part', array(), $blocks ); + $content = traverse_and_serialize_blocks( array( $mock_template_part_block ), $before_block_visitor, $after_block_visitor ); + $template->content = remove_serialized_parent_block( $content ); + } else { + $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); + } } - $blocks = parse_blocks( $template->content ); - $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); return $template; } @@ -999,7 +1009,19 @@ function _build_block_template_result_from_post( $post ) { $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 ) { + /** + * In order for hooked blocks to be inserted at positions first_child and last_child in a template part, + * we need to create a mock template part block and traverse it. + */ + $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(); + $mock_template_part_block = _make_mock_parsed_block( 'core/template-part', $attributes, $blocks ); + $content = traverse_and_serialize_blocks( array( $mock_template_part_block ), $before_block_visitor, $after_block_visitor ); + $template->content = remove_serialized_parent_block( $content ); + } else { + $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); + } } return $template; @@ -1600,12 +1622,21 @@ function inject_ignored_hooked_blocks_metadata_attributes( $changes, $deprecated $post->post_author = get_current_user_id(); } + $wp_post = new WP_Post( $post ); + if ( 'wp_template_part' === $post->post_type && ! isset( $terms['wp_template_part_area'] ) ) { $area_terms = get_the_terms( $changes->ID, 'wp_template_part_area' ); $terms['wp_template_part_area'] = ! is_wp_error( $area_terms ) && ! empty( $area_terms ) ? $area_terms[0]->name : null; + + /** + * Hooked blocks that are ignored from a template part as first_child or last_child + * are not inserted into the template part's content because they have no parent block. + * Instead, they are inserted into the postmeta. + */ + update_ignored_hooked_blocks_postmeta( $wp_post ); } - $template = _build_block_template_object_from_post_object( new WP_Post( $post ), $terms, $meta ); + $template = _build_block_template_object_from_post_object( $wp_post, $terms, $meta ); if ( is_wp_error( $template ) ) { return $template; diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 2edcaa8c1ba04..34733cb659f31 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1045,6 +1045,26 @@ function remove_serialized_parent_block( $serialized_block ) { return substr( $serialized_block, $start, $end - $start ); } +/** + * Returns a mock parsed block array for a given block name. + * + * @since 6.7.0 + * @access private + * + * @param string $block_name + * @param array $attrs + * @param array $inner_blocks + * @return array Parsed block. + */ +function _make_mock_parsed_block( $block_name, $attrs = array(), $inner_blocks = array() ) { + return array( + 'blockName' => $block_name, + 'attrs' => $attrs, + 'innerBlocks' => $inner_blocks, + 'innerContent' => array_fill( 0, count( $inner_blocks ), null ), + ); +} + /** * 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. @@ -1072,14 +1092,20 @@ function update_ignored_hooked_blocks_postmeta( $post ) { return $post; } + $post_type_to_block_name_map = array( + 'wp_navigation' => 'core/navigation', + 'wp_template_part' => 'core/template-part', + ); + /* - * Skip meta generation when the post content is not a navigation block. + * Skip meta generation when the post content is not in the above map. */ - if ( ! isset( $post->post_type ) || 'wp_navigation' !== $post->post_type ) { + if ( ! isset( $post->post_type ) || ! isset( $post_type_to_block_name_map[ $post->post_type ] ) ) { return $post; } - $attributes = array(); + $parent_block_name = $post_type_to_block_name_map[ $post->post_type ]; + $attributes = array(); $ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); if ( ! empty( $ignored_hooked_blocks ) ) { @@ -1090,7 +1116,7 @@ function update_ignored_hooked_blocks_postmeta( $post ) { } $markup = get_comment_delimited_block_content( - 'core/navigation', + $parent_block_name, $attributes, $post->post_content ); From 6b78fcd54203d738d003eddcd20e8d22fb005f2a Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Mon, 24 Jun 2024 14:19:44 +0100 Subject: [PATCH 02/30] Insert ignoredHookedBlocks postmeta via rest_pre_insert_wp_template_part filter --- src/wp-includes/block-template-utils.php | 11 +---------- src/wp-includes/blocks.php | 9 +++++++-- src/wp-includes/default-filters.php | 3 ++- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index 5bacc7ed6bab1..31024c71a7f95 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -1622,21 +1622,12 @@ function inject_ignored_hooked_blocks_metadata_attributes( $changes, $deprecated $post->post_author = get_current_user_id(); } - $wp_post = new WP_Post( $post ); - if ( 'wp_template_part' === $post->post_type && ! isset( $terms['wp_template_part_area'] ) ) { $area_terms = get_the_terms( $changes->ID, 'wp_template_part_area' ); $terms['wp_template_part_area'] = ! is_wp_error( $area_terms ) && ! empty( $area_terms ) ? $area_terms[0]->name : null; - - /** - * Hooked blocks that are ignored from a template part as first_child or last_child - * are not inserted into the template part's content because they have no parent block. - * Instead, they are inserted into the postmeta. - */ - update_ignored_hooked_blocks_postmeta( $wp_post ); } - $template = _build_block_template_object_from_post_object( $wp_post, $terms, $meta ); + $template = _build_block_template_object_from_post_object( new WP_Post( $post ), $terms, $meta ); if ( is_wp_error( $template ) ) { return $template; diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 34733cb659f31..17affda87ba01 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1092,6 +1092,11 @@ function update_ignored_hooked_blocks_postmeta( $post ) { return $post; } + /* + * We only hook into filters for the `wp_navigation` and `wp_template_part` post types. + * So if the post type is not set we can assume it's a `wp_template_part`. + */ + $post_type = isset( $post->post_type ) ? $post->post_type : 'wp_template_part'; $post_type_to_block_name_map = array( 'wp_navigation' => 'core/navigation', 'wp_template_part' => 'core/template-part', @@ -1100,11 +1105,11 @@ function update_ignored_hooked_blocks_postmeta( $post ) { /* * Skip meta generation when the post content is not in the above map. */ - if ( ! isset( $post->post_type ) || ! isset( $post_type_to_block_name_map[ $post->post_type ] ) ) { + if ( ! isset( $post_type_to_block_name_map[ $post_type ] ) ) { return $post; } - $parent_block_name = $post_type_to_block_name_map[ $post->post_type ]; + $parent_block_name = $post_type_to_block_name_map[ $post_type ]; $attributes = array(); $ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 2c182747a5bc0..4282a0a54a8d1 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -757,8 +757,9 @@ add_filter( 'rest_pre_insert_wp_template', 'inject_ignored_hooked_blocks_metadata_attributes' ); add_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes' ); -// Update ignoredHookedBlocks postmeta for wp_navigation post type. +// Update ignoredHookedBlocks postmeta for wp_navigation and wp_template_part post types. add_filter( 'rest_pre_insert_wp_navigation', 'update_ignored_hooked_blocks_postmeta' ); +add_filter( 'rest_pre_insert_wp_template_part', 'update_ignored_hooked_blocks_postmeta' ); // Inject hooked blocks into the wp_navigation post type REST response. add_filter( 'rest_prepare_wp_navigation', 'insert_hooked_blocks_into_rest_response', 10, 2 ); From 1dccc508a96317baee337a803c8394a4e1f87037 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Mon, 24 Jun 2024 14:21:33 +0100 Subject: [PATCH 03/30] Update comment --- src/wp-includes/blocks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 17affda87ba01..d90fb390acec3 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1067,7 +1067,7 @@ function _make_mock_parsed_block( $block_name, $attrs = array(), $inner_blocks = /** * 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. + * Currently only supports `wp_navigation` and `wp_template_part` post types. * * @since 6.6.0 * @access private From 87462d5da9f11651a0689c9085cd10f77e78ad9c Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Tue, 25 Jun 2024 15:35:32 +0100 Subject: [PATCH 04/30] Update tests --- .../updateIgnoredHookedBlocksPostMeta.php | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php b/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php index 7b0a830dd52dd..77b6730c1d59f 100644 --- a/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php +++ b/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php @@ -18,17 +18,31 @@ class Tests_Blocks_UpdateIgnoredHookedBlocksPostMeta extends WP_UnitTestCase { */ protected static $navigation_post; + /** + * Post object. + * + * @var object + */ + protected static $template_part_post; + /** * Setup method. */ public static function wpSetUpBeforeClass() { - self::$navigation_post = self::factory()->post->create_and_get( + self::$navigation_post = self::factory()->post->create_and_get( array( 'post_type' => 'wp_navigation', 'post_title' => 'Navigation Menu', 'post_content' => 'Original content', ) ); + self::$template_part_post = self::factory()->post->create_and_get( + array( + 'post_type' => 'wp_template_part', + 'post_title' => 'Template Part block', + 'post_content' => 'Original content', + ) + ); } /** @@ -142,12 +156,12 @@ public function test_update_ignored_hooked_blocks_postmeta_retains_content_if_no /** * @ticket 60759 */ - public function test_update_ignored_hooked_blocks_postmeta_dont_modify_if_not_navigation() { + public function test_update_ignored_hooked_blocks_postmeta_dont_modify_if_incompatible_post_type() { register_block_type( 'tests/my-block', array( 'block_hooks' => array( - 'core/navigation' => 'last_child', + 'core/navigation-link' => 'after', ), ) ); @@ -169,28 +183,29 @@ public function test_update_ignored_hooked_blocks_postmeta_dont_modify_if_not_na /** * @ticket 60759 + * @ticket 60854 */ - public function test_update_ignored_hooked_blocks_postmeta_dont_modify_if_no_post_type() { + public function test_update_ignored_hooked_blocks_postmeta_imply_tempate_part_if_no_post_type() { register_block_type( 'tests/my-block', array( 'block_hooks' => array( - 'core/navigation' => 'last_child', + 'core/template-part' => 'last_child', ), ) ); - $original_markup = ''; + $original_markup = '

test paragraph

'; $post = new stdClass(); - $post->ID = self::$navigation_post->ID; + $post->ID = self::$template_part_post->ID; $post->post_content = $original_markup; $post = update_ignored_hooked_blocks_postmeta( $post ); $this->assertSame( - $original_markup, - $post->post_content, - 'Post content did not match the original markup.' + array( 'tests/my-block' ), + json_decode( get_post_meta( self::$template_part_post->ID, '_wp_ignored_hooked_blocks', true ), true ), + 'Block was not added to ignored hooked blocks metadata.' ); } } From 8978b80cb9608ec62cbd6ef62c54628145e3ee7f Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 25 Jun 2024 18:48:09 +0200 Subject: [PATCH 05/30] Add assertion, change anchor block --- .../tests/blocks/updateIgnoredHookedBlocksPostMeta.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php b/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php index 77b6730c1d59f..00c668438c3b3 100644 --- a/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php +++ b/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php @@ -161,7 +161,7 @@ public function test_update_ignored_hooked_blocks_postmeta_dont_modify_if_incomp 'tests/my-block', array( 'block_hooks' => array( - 'core/navigation-link' => 'after', + 'core/navigation' => 'last_child', ), ) ); @@ -179,6 +179,10 @@ public function test_update_ignored_hooked_blocks_postmeta_dont_modify_if_incomp $post->post_content, 'Post content did not match the original markup.' ); + $this->assertNull( + json_decode( get_post_meta( self::$template_part_post->ID, '_wp_ignored_hooked_blocks', true ), true ), + 'Block should not have been added to ignored hooked blocks post meta.' + ); } /** From d5f6c30b7ac04418ebfd9cc12bddd8024c6839e6 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Wed, 26 Jun 2024 10:56:09 +0100 Subject: [PATCH 06/30] Update tests --- .../tests/blocks/updateIgnoredHookedBlocksPostMeta.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php b/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php index 00c668438c3b3..b93c7b24667cf 100644 --- a/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php +++ b/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php @@ -179,9 +179,9 @@ public function test_update_ignored_hooked_blocks_postmeta_dont_modify_if_incomp $post->post_content, 'Post content did not match the original markup.' ); - $this->assertNull( - json_decode( get_post_meta( self::$template_part_post->ID, '_wp_ignored_hooked_blocks', true ), true ), - 'Block should not have been added to ignored hooked blocks post meta.' + $this->assertFalse( + property_exists( $post, 'meta_input' ), + 'Meta input should not be set.' ); } @@ -208,7 +208,7 @@ public function test_update_ignored_hooked_blocks_postmeta_imply_tempate_part_if $this->assertSame( array( 'tests/my-block' ), - json_decode( get_post_meta( self::$template_part_post->ID, '_wp_ignored_hooked_blocks', true ), true ), + json_decode( $post->meta_input['_wp_ignored_hooked_blocks'], true ), 'Block was not added to ignored hooked blocks metadata.' ); } From 0ed6e89859b7a615163e80811e1ae545cab9ef8a Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Wed, 26 Jun 2024 14:53:35 +0100 Subject: [PATCH 07/30] Remove duplciate get_post_meta call --- src/wp-includes/blocks.php | 26 ++++++------------- .../updateIgnoredHookedBlocksPostMeta.php | 7 ++++- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index d90fb390acec3..819039642c749 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1070,20 +1070,13 @@ function _make_mock_parsed_block( $block_name, $attrs = array(), $inner_blocks = * Currently only supports `wp_navigation` and `wp_template_part` post types. * * @since 6.6.0 + * @since 6.7.0 Support `wp_template_part` post type. * @access private * * @param stdClass $post Post object. * @return stdClass The updated post object. */ function update_ignored_hooked_blocks_postmeta( $post ) { - /* - * In this scenario the user has likely tried to create a navigation via the REST API. - * In which case we won't have a post ID to work with and store meta against. - */ - if ( empty( $post->ID ) ) { - return $post; - } - /* * Skip meta generation when consumers intentionally update specific Navigation fields * and omit the content update. @@ -1109,14 +1102,13 @@ function update_ignored_hooked_blocks_postmeta( $post ) { return $post; } - $parent_block_name = $post_type_to_block_name_map[ $post_type ]; - $attributes = array(); + $parent_block_name = $post_type_to_block_name_map[ $post_type ]; + $attributes = array(); + $existing_ignored_hooked_blocks = isset( $post->ID ) ? get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ) : ''; - $ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); - if ( ! empty( $ignored_hooked_blocks ) ) { - $ignored_hooked_blocks = json_decode( $ignored_hooked_blocks, true ); + if ( ! empty( $existing_ignored_hooked_blocks ) ) { $attributes['metadata'] = array( - 'ignoredHookedBlocks' => $ignored_hooked_blocks, + 'ignoredHookedBlocks' => json_decode( $existing_ignored_hooked_blocks, true ), ); } @@ -1126,7 +1118,7 @@ function update_ignored_hooked_blocks_postmeta( $post ) { $post->post_content ); - $existing_post = get_post( $post->ID ); + $existing_post = isset( $post->ID ) ? get_post( $post->ID ) : array(); // Merge the existing post object with the updated post object to pass to the block hooks algorithm for context. $context = (object) array_merge( (array) $existing_post, (array) $post ); $serialized_block = apply_block_hooks_to_content( $markup, $context, 'set_ignored_hooked_blocks_metadata' ); @@ -1137,10 +1129,8 @@ function update_ignored_hooked_blocks_postmeta( $post ) { : array(); if ( ! empty( $ignored_hooked_blocks ) ) { - $existing_ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); if ( ! empty( $existing_ignored_hooked_blocks ) ) { - $existing_ignored_hooked_blocks = json_decode( $existing_ignored_hooked_blocks, true ); - $ignored_hooked_blocks = array_unique( array_merge( $ignored_hooked_blocks, $existing_ignored_hooked_blocks ) ); + $ignored_hooked_blocks = array_unique( array_merge( $ignored_hooked_blocks, json_decode( $existing_ignored_hooked_blocks, true ) ) ); } if ( ! isset( $post->meta_input ) ) { diff --git a/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php b/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php index b93c7b24667cf..94d740e5a4c40 100644 --- a/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php +++ b/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php @@ -97,7 +97,7 @@ public function test_update_ignored_hooked_blocks_postmeta_preserves_entities() /** * @ticket 60759 */ - public function test_update_ignored_hooked_blocks_postmeta_dont_modify_no_post_id() { + public function test_update_ignored_hooked_blocks_postmeta_modify_no_post_id() { register_block_type( 'tests/my-block', array( @@ -119,6 +119,11 @@ public function test_update_ignored_hooked_blocks_postmeta_dont_modify_no_post_i $post->post_content, 'Post content did not match the original markup.' ); + $this->assertSame( + array( 'tests/my-block' ), + json_decode( $post->meta_input['_wp_ignored_hooked_blocks'], true ), + 'Block was not added to ignored hooked blocks metadata.' + ); } /** From 83040a67811fd679f85dfac964db77192c9d3d47 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Wed, 26 Jun 2024 15:58:22 +0100 Subject: [PATCH 08/30] Build correct context for the block hooks API when the post type is a wp_template_part --- src/wp-includes/blocks.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 819039642c749..2632723921528 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1118,9 +1118,25 @@ function update_ignored_hooked_blocks_postmeta( $post ) { $post->post_content ); + /** + * We need to merge incoming changes with existing data so the block hooks API gets the correct context. + * For `wp_template_part` post type, we need to convert the post object to a block template object. + */ $existing_post = isset( $post->ID ) ? get_post( $post->ID ) : array(); - // Merge the existing post object with the updated post object to pass to the block hooks algorithm for context. - $context = (object) array_merge( (array) $existing_post, (array) $post ); + $context = (object) array_merge( (array) $existing_post, (array) $post ); + // Convert the $context object to a WP_Post object. + $context = get_post( $context ); + if ( 'wp_template_part' === $post_type ) { + // Convert the $context object to a WP_Block_Template object. + if ( isset( $post->ID ) ) { + $context = _build_block_template_result_from_post( $context ); + } else { + $meta = isset( $context->meta_input ) ? $context->meta_input : array(); + $terms = isset( $context->tax_input ) ? $context->tax_input : array(); + $context = _build_block_template_object_from_post_object( get_post( $context ), $terms, $meta ); + } + } + $serialized_block = apply_block_hooks_to_content( $markup, $context, 'set_ignored_hooked_blocks_metadata' ); $root_block = parse_blocks( $serialized_block )[0]; From 5e4876c4aaee127d1bced7a72aff8237833a09f5 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Thu, 27 Jun 2024 15:20:20 +0100 Subject: [PATCH 09/30] Test coverage --- .../buildBlockTemplateResultFromFile.php | 88 +++++++++++++++++++ .../buildBlockTemplateResultFromPost.php | 44 ++++++++++ 2 files changed, 132 insertions(+) diff --git a/tests/phpunit/tests/block-templates/buildBlockTemplateResultFromFile.php b/tests/phpunit/tests/block-templates/buildBlockTemplateResultFromFile.php index 3aedcf0c2c7b8..b2c2f0af75be7 100644 --- a/tests/phpunit/tests/block-templates/buildBlockTemplateResultFromFile.php +++ b/tests/phpunit/tests/block-templates/buildBlockTemplateResultFromFile.php @@ -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 @@ -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( '', $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( '', $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( '', $template_part->content ); + } } diff --git a/tests/phpunit/tests/block-templates/buildBlockTemplateResultFromPost.php b/tests/phpunit/tests/block-templates/buildBlockTemplateResultFromPost.php index 586e9beded17b..839e5788bc6c3 100644 --- a/tests/phpunit/tests/block-templates/buildBlockTemplateResultFromPost.php +++ b/tests/phpunit/tests/block-templates/buildBlockTemplateResultFromPost.php @@ -111,6 +111,50 @@ public function test_should_inject_hooked_block_into_template_part() { $this->assertStringEndsWith( '', $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( '', $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( '', $template_part->content ); + } + /** * @ticket 59646 * @ticket 60506 From 1e3e362cd72ca47fde4f6e86457cee92798d86f8 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Thu, 27 Jun 2024 15:21:26 +0100 Subject: [PATCH 10/30] Ensure that for template files _inject_theme_attribute_in_template_part_block is run as a visitor callback --- src/wp-includes/block-template-utils.php | 31 +++++++++++++----------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index 31024c71a7f95..ff21b7ce5145d 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -606,24 +606,27 @@ function _build_block_template_result_from_file( $template_file, $template_type $template->area = $template_file['area']; } + $blocks = parse_blocks( $template->content ); + $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 ); - if ( 'wp_template_part' === $template->type ) { - /** - * In order for hooked blocks to be inserted at positions first_child and last_child in a template part, - * we need to create a mock template part block and traverse it. - */ - $mock_template_part_block = _make_mock_parsed_block( 'core/template-part', array(), $blocks ); - $content = traverse_and_serialize_blocks( array( $mock_template_part_block ), $before_block_visitor, $after_block_visitor ); - $template->content = remove_serialized_parent_block( $content ); - } else { - $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 create a mock template part block and traverse it. + */ + $mock_template_part_block = _make_mock_parsed_block( 'core/template-part', array(), $blocks ); + $content = traverse_and_serialize_blocks( array( $mock_template_part_block ), $before_block_visitor, $after_block_visitor ); + $template->content = remove_serialized_parent_block( $content ); + } else { + $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); } return $template; From f4062ade625de2d8c4407e7bfff02b8021b1ff50 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Fri, 28 Jun 2024 10:40:05 +0100 Subject: [PATCH 11/30] Update comments --- src/wp-includes/blocks.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 2632723921528..5061727ab49d4 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1124,10 +1124,10 @@ function update_ignored_hooked_blocks_postmeta( $post ) { */ $existing_post = isset( $post->ID ) ? get_post( $post->ID ) : array(); $context = (object) array_merge( (array) $existing_post, (array) $post ); - // Convert the $context object to a WP_Post object. + // Given a post object ($context), get_post will return a WP_Post object. $context = get_post( $context ); if ( 'wp_template_part' === $post_type ) { - // Convert the $context object to a WP_Block_Template object. + // Convert the $context WP_Post object to a WP_Block_Template object. if ( isset( $post->ID ) ) { $context = _build_block_template_result_from_post( $context ); } else { From 6c95919281ea9352a8643b99f0da126efd93c323 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 14:28:38 +0200 Subject: [PATCH 12/30] Remove update_ignored_hooked_blocks_postmeta from rest_pre_insert_wp_template_part hook --- src/wp-includes/default-filters.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 4282a0a54a8d1..2c182747a5bc0 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -757,9 +757,8 @@ add_filter( 'rest_pre_insert_wp_template', 'inject_ignored_hooked_blocks_metadata_attributes' ); add_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes' ); -// Update ignoredHookedBlocks postmeta for wp_navigation and wp_template_part post types. +// Update ignoredHookedBlocks postmeta for wp_navigation post type. add_filter( 'rest_pre_insert_wp_navigation', 'update_ignored_hooked_blocks_postmeta' ); -add_filter( 'rest_pre_insert_wp_template_part', 'update_ignored_hooked_blocks_postmeta' ); // Inject hooked blocks into the wp_navigation post type REST response. add_filter( 'rest_prepare_wp_navigation', 'insert_hooked_blocks_into_rest_response', 10, 2 ); From 0ceb66b23899287e15a060f99db36f1a4696b42e Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 14:29:04 +0200 Subject: [PATCH 13/30] Add extract_serialized_parent_block helper --- src/wp-includes/blocks.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 5061727ab49d4..d7614adaa5236 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -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, 'Hello'; + + $post = inject_ignored_hooked_blocks_metadata_attributes( $changes ); + $this->assertSame( + array( 'tests/hooked-block' ), + json_decode( $post->meta_input['_wp_ignored_hooked_blocks'], true ), + 'The hooked block was not injected into the wp_template_part\'s _wp_ignored_hooked_blocks postmeta.' + ); + $this->assertSame( + $changes->post_content, + $post->post_content, + 'The template part\'s post content was modified.' + ); + } } From 560dcced510415d5908ad024663225a5660e6906 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 14:56:02 +0200 Subject: [PATCH 18/30] Revert changes to updateIgnoredHookedBlocksPostMeta test --- .../updateIgnoredHookedBlocksPostMeta.php | 44 +++++-------------- 1 file changed, 10 insertions(+), 34 deletions(-) diff --git a/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php b/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php index 94d740e5a4c40..7b0a830dd52dd 100644 --- a/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php +++ b/tests/phpunit/tests/blocks/updateIgnoredHookedBlocksPostMeta.php @@ -18,31 +18,17 @@ class Tests_Blocks_UpdateIgnoredHookedBlocksPostMeta extends WP_UnitTestCase { */ protected static $navigation_post; - /** - * Post object. - * - * @var object - */ - protected static $template_part_post; - /** * Setup method. */ public static function wpSetUpBeforeClass() { - self::$navigation_post = self::factory()->post->create_and_get( + self::$navigation_post = self::factory()->post->create_and_get( array( 'post_type' => 'wp_navigation', 'post_title' => 'Navigation Menu', 'post_content' => 'Original content', ) ); - self::$template_part_post = self::factory()->post->create_and_get( - array( - 'post_type' => 'wp_template_part', - 'post_title' => 'Template Part block', - 'post_content' => 'Original content', - ) - ); } /** @@ -97,7 +83,7 @@ public function test_update_ignored_hooked_blocks_postmeta_preserves_entities() /** * @ticket 60759 */ - public function test_update_ignored_hooked_blocks_postmeta_modify_no_post_id() { + public function test_update_ignored_hooked_blocks_postmeta_dont_modify_no_post_id() { register_block_type( 'tests/my-block', array( @@ -119,11 +105,6 @@ public function test_update_ignored_hooked_blocks_postmeta_modify_no_post_id() { $post->post_content, 'Post content did not match the original markup.' ); - $this->assertSame( - array( 'tests/my-block' ), - json_decode( $post->meta_input['_wp_ignored_hooked_blocks'], true ), - 'Block was not added to ignored hooked blocks metadata.' - ); } /** @@ -161,7 +142,7 @@ public function test_update_ignored_hooked_blocks_postmeta_retains_content_if_no /** * @ticket 60759 */ - public function test_update_ignored_hooked_blocks_postmeta_dont_modify_if_incompatible_post_type() { + public function test_update_ignored_hooked_blocks_postmeta_dont_modify_if_not_navigation() { register_block_type( 'tests/my-block', array( @@ -184,37 +165,32 @@ public function test_update_ignored_hooked_blocks_postmeta_dont_modify_if_incomp $post->post_content, 'Post content did not match the original markup.' ); - $this->assertFalse( - property_exists( $post, 'meta_input' ), - 'Meta input should not be set.' - ); } /** * @ticket 60759 - * @ticket 60854 */ - public function test_update_ignored_hooked_blocks_postmeta_imply_tempate_part_if_no_post_type() { + public function test_update_ignored_hooked_blocks_postmeta_dont_modify_if_no_post_type() { register_block_type( 'tests/my-block', array( 'block_hooks' => array( - 'core/template-part' => 'last_child', + 'core/navigation' => 'last_child', ), ) ); - $original_markup = '

test paragraph

'; + $original_markup = ''; $post = new stdClass(); - $post->ID = self::$template_part_post->ID; + $post->ID = self::$navigation_post->ID; $post->post_content = $original_markup; $post = update_ignored_hooked_blocks_postmeta( $post ); $this->assertSame( - array( 'tests/my-block' ), - json_decode( $post->meta_input['_wp_ignored_hooked_blocks'], true ), - 'Block was not added to ignored hooked blocks metadata.' + $original_markup, + $post->post_content, + 'Post content did not match the original markup.' ); } } From 58c66892c45cb34d46987142fdf7ec2599ebc386 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 15:01:56 +0200 Subject: [PATCH 19/30] Clean up post meta --- .../injectIgnoredHookedBlocksMetadataAttributes.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php index df6c5cc401c88..1ec255a0dcb78 100644 --- a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php +++ b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php @@ -17,6 +17,7 @@ public function tear_down() { if ( WP_Block_Type_Registry::get_instance()->is_registered( 'tests/hooked-block' ) ) { unregister_block_type( 'tests/hooked-block' ); } + delete_post_meta( self::$template_part_post->ID, '_wp_ignored_hooked_blocks' ); parent::tear_down(); } From e7db202fb1c47dc8699db68aac111add2db691c4 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 15:14:05 +0200 Subject: [PATCH 20/30] Start fixing tests --- ...tIgnoredHookedBlocksMetadataAttributes.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php index 1ec255a0dcb78..69d7e23a9140c 100644 --- a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php +++ b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php @@ -86,12 +86,15 @@ public function test_hooked_block_types_filter_with_newly_created_template_part( inject_ignored_hooked_blocks_metadata_attributes( $changes ); - $args = $action->get_args(); - $anchor_block_type = end( $args )[2]; - $context = end( $args )[3]; + $args = $action->get_args(); - $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + $args_for_template_part = end( $args ); + $relative_position = $args_for_template_part[1]; + $anchor_block_type = $args_for_template_part[2]; + $context = $args_for_template_part[3]; + $this->assertSame( 'last_child', $relative_position ); + $this->assertSame( 'core/template-part', $anchor_block_type ); $this->assertInstanceOf( 'WP_Block_Template', $context ); $this->assertSame( @@ -118,6 +121,14 @@ public function test_hooked_block_types_filter_with_newly_created_template_part( $context->area, 'The area field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' ); + + $args_for_anchor_block = prev( $args ); + $relative_position = $args_for_template_part[1]; + $anchor_block_type = $args_for_anchor_block[2]; + + $this->assertSame( 'last_child', $relative_position ); + $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + $this->assertSame( $context, $args_for_anchor_block[3] ); } /** From adacaae533129e3ff170f58695a69ab764d6fe48 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 15:34:56 +0200 Subject: [PATCH 21/30] Polish test --- ...tIgnoredHookedBlocksMetadataAttributes.php | 50 +++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php index 69d7e23a9140c..43ec644799a99 100644 --- a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php +++ b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php @@ -88,13 +88,43 @@ public function test_hooked_block_types_filter_with_newly_created_template_part( $args = $action->get_args(); - $args_for_template_part = end( $args ); - $relative_position = $args_for_template_part[1]; - $anchor_block_type = $args_for_template_part[2]; - $context = $args_for_template_part[3]; + $relative_positions = array_column( $args, 1 ); + $anchor_block_types = array_column( $args, 2 ); + $contexts = array_column( $args, 3 ); + + $this->assertSame( + array( + 'before', + 'after', + 'first_child', + 'before', + 'after', + 'last_child', + ), + $relative_positions, + "The relative positions passed to the hooked_block_types filter are incorrect." + ); + + $this->assertSame( + array( + 'core/template-part', + 'core/template-part', + 'core/template-part', + 'tests/anchor-block', + 'tests/anchor-block', + 'core/template-part', + ), + $anchor_block_types, + "The anchor block types passed to the hooked_block_types filter are incorrect." + ); + + $context = $contexts[0]; + $this->assertSame( + array_fill( 0, count( $contexts ), $context ), + $contexts, + "The context passed to the hooked_block_types filter is not the same for all calls." + ); - $this->assertSame( 'last_child', $relative_position ); - $this->assertSame( 'core/template-part', $anchor_block_type ); $this->assertInstanceOf( 'WP_Block_Template', $context ); $this->assertSame( @@ -121,14 +151,6 @@ public function test_hooked_block_types_filter_with_newly_created_template_part( $context->area, 'The area field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' ); - - $args_for_anchor_block = prev( $args ); - $relative_position = $args_for_template_part[1]; - $anchor_block_type = $args_for_anchor_block[2]; - - $this->assertSame( 'last_child', $relative_position ); - $this->assertSame( 'tests/anchor-block', $anchor_block_type ); - $this->assertSame( $context, $args_for_anchor_block[3] ); } /** From b3377b3192d675bfe5f43316865090ae2f72d9db Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 15:50:20 +0200 Subject: [PATCH 22/30] Fix other tests --- ...tIgnoredHookedBlocksMetadataAttributes.php | 101 +++++++++++++++--- 1 file changed, 85 insertions(+), 16 deletions(-) diff --git a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php index 43ec644799a99..d204fc1b46a9b 100644 --- a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php +++ b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php @@ -86,8 +86,7 @@ public function test_hooked_block_types_filter_with_newly_created_template_part( inject_ignored_hooked_blocks_metadata_attributes( $changes ); - $args = $action->get_args(); - + $args = $action->get_args(); $relative_positions = array_column( $args, 1 ); $anchor_block_types = array_column( $args, 2 ); $contexts = array_column( $args, 3 ); @@ -122,11 +121,13 @@ public function test_hooked_block_types_filter_with_newly_created_template_part( $this->assertSame( array_fill( 0, count( $contexts ), $context ), $contexts, - "The context passed to the hooked_block_types filter is not the same for all calls." + "The context passed to the hooked_block_types filter should be the same for all calls." + ); + $this->assertInstanceOf( + 'WP_Block_Template', + $context, + 'The context passed to the hooked_block_types filter is not an instance of WP_Block_Template.' ); - - $this->assertInstanceOf( 'WP_Block_Template', $context ); - $this->assertSame( $changes->post_type, $context->type, @@ -235,14 +236,48 @@ public function test_hooked_block_types_filter_with_existing_template_part_file( inject_ignored_hooked_blocks_metadata_attributes( $changes ); - $args = $action->get_args(); - $anchor_block_type = end( $args )[2]; - $context = end( $args )[3]; + $args = $action->get_args(); + $relative_positions = array_column( $args, 1 ); + $anchor_block_types = array_column( $args, 2 ); + $contexts = array_column( $args, 3 ); - $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + $this->assertSame( + array( + 'before', + 'after', + 'first_child', + 'before', + 'after', + 'last_child', + ), + $relative_positions, + "The relative positions passed to the hooked_block_types filter are incorrect." + ); - $this->assertInstanceOf( 'WP_Block_Template', $context ); + $this->assertSame( + array( + 'core/template-part', + 'core/template-part', + 'core/template-part', + 'tests/anchor-block', + 'tests/anchor-block', + 'core/template-part', + ), + $anchor_block_types, + "The anchor block types passed to the hooked_block_types filter are incorrect." + ); + $context = $contexts[0]; + $this->assertSame( + array_fill( 0, count( $contexts ), $context ), + $contexts, + "The context passed to the hooked_block_types filter should be the same for all calls." + ); + $this->assertInstanceOf( + 'WP_Block_Template', + $context, + 'The context passed to the hooked_block_types filter is not an instance of WP_Block_Template.' + ); $this->assertSame( $changes->post_name, $context->slug, @@ -352,14 +387,48 @@ public function test_hooked_block_types_filter_with_existing_template_part_post( inject_ignored_hooked_blocks_metadata_attributes( $changes ); - $args = $action->get_args(); - $anchor_block_type = end( $args )[2]; - $context = end( $args )[3]; + $args = $action->get_args(); + $relative_positions = array_column( $args, 1 ); + $anchor_block_types = array_column( $args, 2 ); + $contexts = array_column( $args, 3 ); - $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + $this->assertSame( + array( + 'before', + 'after', + 'first_child', + 'before', + 'after', + 'last_child', + ), + $relative_positions, + "The relative positions passed to the hooked_block_types filter are incorrect." + ); - $this->assertInstanceOf( 'WP_Block_Template', $context ); + $this->assertSame( + array( + 'core/template-part', + 'core/template-part', + 'core/template-part', + 'tests/anchor-block', + 'tests/anchor-block', + 'core/template-part', + ), + $anchor_block_types, + "The anchor block types passed to the hooked_block_types filter are incorrect." + ); + $context = $contexts[0]; + $this->assertSame( + array_fill( 0, count( $contexts ), $context ), + $contexts, + "The context passed to the hooked_block_types filter should be the same for all calls." + ); + $this->assertInstanceOf( + 'WP_Block_Template', + $context, + 'The context passed to the hooked_block_types filter is not an instance of WP_Block_Template.' + ); $this->assertSame( $changes->post_name, $context->slug, From 97d4624898304a4097579d9e4450f04b1afe879e Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 15:52:37 +0200 Subject: [PATCH 23/30] Add ticket numbers --- .../injectIgnoredHookedBlocksMetadataAttributes.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php index d204fc1b46a9b..28aeded163004 100644 --- a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php +++ b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php @@ -70,6 +70,7 @@ public function test_hooked_block_types_filter_with_newly_created_template() { /** * @ticket 60754 + * @ticket 60854 */ public function test_hooked_block_types_filter_with_newly_created_template_part() { $action = new MockAction(); @@ -216,6 +217,7 @@ public function test_hooked_block_types_filter_with_existing_template_file() { /** * @ticket 60754 + * @ticket 60854 */ public function test_hooked_block_types_filter_with_existing_template_part_file() { $action = new MockAction(); @@ -371,6 +373,7 @@ public function test_hooked_block_types_filter_with_existing_template_post() { /** * @ticket 60754 + * @ticket 60854 */ public function test_hooked_block_types_filter_with_existing_template_part_post() { $action = new MockAction(); From f324f96a871815630eec2d4e1268604afc1fe764 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 16:05:11 +0200 Subject: [PATCH 24/30] Single quotes --- ...ctIgnoredHookedBlocksMetadataAttributes.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php index 28aeded163004..af6ab7883b5ab 100644 --- a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php +++ b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php @@ -102,7 +102,7 @@ public function test_hooked_block_types_filter_with_newly_created_template_part( 'last_child', ), $relative_positions, - "The relative positions passed to the hooked_block_types filter are incorrect." + 'The relative positions passed to the hooked_block_types filter are incorrect.' ); $this->assertSame( @@ -115,14 +115,14 @@ public function test_hooked_block_types_filter_with_newly_created_template_part( 'core/template-part', ), $anchor_block_types, - "The anchor block types passed to the hooked_block_types filter are incorrect." + 'The anchor block types passed to the hooked_block_types filter are incorrect.' ); $context = $contexts[0]; $this->assertSame( array_fill( 0, count( $contexts ), $context ), $contexts, - "The context passed to the hooked_block_types filter should be the same for all calls." + 'The context passed to the hooked_block_types filter should be the same for all calls.' ); $this->assertInstanceOf( 'WP_Block_Template', @@ -253,7 +253,7 @@ public function test_hooked_block_types_filter_with_existing_template_part_file( 'last_child', ), $relative_positions, - "The relative positions passed to the hooked_block_types filter are incorrect." + 'The relative positions passed to the hooked_block_types filter are incorrect.' ); $this->assertSame( @@ -266,14 +266,14 @@ public function test_hooked_block_types_filter_with_existing_template_part_file( 'core/template-part', ), $anchor_block_types, - "The anchor block types passed to the hooked_block_types filter are incorrect." + 'The anchor block types passed to the hooked_block_types filter are incorrect.' ); $context = $contexts[0]; $this->assertSame( array_fill( 0, count( $contexts ), $context ), $contexts, - "The context passed to the hooked_block_types filter should be the same for all calls." + 'The context passed to the hooked_block_types filter should be the same for all calls.' ); $this->assertInstanceOf( 'WP_Block_Template', @@ -405,7 +405,7 @@ public function test_hooked_block_types_filter_with_existing_template_part_post( 'last_child', ), $relative_positions, - "The relative positions passed to the hooked_block_types filter are incorrect." + 'The relative positions passed to the hooked_block_types filter are incorrect.' ); $this->assertSame( @@ -418,14 +418,14 @@ public function test_hooked_block_types_filter_with_existing_template_part_post( 'core/template-part', ), $anchor_block_types, - "The anchor block types passed to the hooked_block_types filter are incorrect." + 'The anchor block types passed to the hooked_block_types filter are incorrect.' ); $context = $contexts[0]; $this->assertSame( array_fill( 0, count( $contexts ), $context ), $contexts, - "The context passed to the hooked_block_types filter should be the same for all calls." + 'The context passed to the hooked_block_types filter should be the same for all calls.' ); $this->assertInstanceOf( 'WP_Block_Template', From 83cfa726fdee0ffa5a4488004c9f18d8502f2342 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 16:20:28 +0200 Subject: [PATCH 25/30] Polish other tests --- ...tIgnoredHookedBlocksMetadataAttributes.php | 93 ++++++++++++++++--- 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php index af6ab7883b5ab..1f8a66ee8546f 100644 --- a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php +++ b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php @@ -39,14 +39,35 @@ public function test_hooked_block_types_filter_with_newly_created_template() { inject_ignored_hooked_blocks_metadata_attributes( $changes ); - $args = $action->get_args(); - $anchor_block_type = end( $args )[2]; - $context = end( $args )[3]; + $args = $action->get_args(); + $relative_positions = array_column( $args, 1 ); + $anchor_block_types = array_column( $args, 2 ); + $contexts = array_column( $args, 3 ); - $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + $this->assertSame( + array( + 'before', + 'after', + ), + $relative_positions, + 'The relative positions passed to the hooked_block_types filter are incorrect.' + ); - $this->assertInstanceOf( 'WP_Block_Template', $context ); + $this->assertSame( + array( + 'tests/anchor-block', + 'tests/anchor-block', + ), + $anchor_block_types, + 'The anchor block types passed to the hooked_block_types filter are incorrect.' + ); + $context = $contexts[0]; + $this->assertSame( + array_fill( 0, count( $contexts ), $context ), + $contexts, + 'The context passed to the hooked_block_types filter should be the same for all calls.' + ); $this->assertSame( $changes->post_type, $context->type, @@ -176,14 +197,35 @@ public function test_hooked_block_types_filter_with_existing_template_file() { inject_ignored_hooked_blocks_metadata_attributes( $changes ); - $args = $action->get_args(); - $anchor_block_type = end( $args )[2]; - $context = end( $args )[3]; + $args = $action->get_args(); + $relative_positions = array_column( $args, 1 ); + $anchor_block_types = array_column( $args, 2 ); + $contexts = array_column( $args, 3 ); - $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + $this->assertSame( + array( + 'before', + 'after', + ), + $relative_positions, + 'The relative positions passed to the hooked_block_types filter are incorrect.' + ); - $this->assertInstanceOf( 'WP_Block_Template', $context ); + $this->assertSame( + array( + 'tests/anchor-block', + 'tests/anchor-block', + ), + $anchor_block_types, + 'The anchor block types passed to the hooked_block_types filter are incorrect.' + ); + $context = $contexts[0]; + $this->assertSame( + array_fill( 0, count( $contexts ), $context ), + $contexts, + 'The context passed to the hooked_block_types filter should be the same for all calls.' + ); $this->assertSame( $changes->post_name, $context->slug, @@ -330,14 +372,35 @@ public function test_hooked_block_types_filter_with_existing_template_post() { inject_ignored_hooked_blocks_metadata_attributes( $changes ); - $args = $action->get_args(); - $anchor_block_type = end( $args )[2]; - $context = end( $args )[3]; + $args = $action->get_args(); + $relative_positions = array_column( $args, 1 ); + $anchor_block_types = array_column( $args, 2 ); + $contexts = array_column( $args, 3 ); - $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + $this->assertSame( + array( + 'before', + 'after', + ), + $relative_positions, + 'The relative positions passed to the hooked_block_types filter are incorrect.' + ); - $this->assertInstanceOf( 'WP_Block_Template', $context ); + $this->assertSame( + array( + 'tests/anchor-block', + 'tests/anchor-block', + ), + $anchor_block_types, + 'The anchor block types passed to the hooked_block_types filter are incorrect.' + ); + $context = $contexts[0]; + $this->assertSame( + array_fill( 0, count( $contexts ), $context ), + $contexts, + 'The context passed to the hooked_block_types filter should be the same for all calls.' + ); $this->assertSame( $changes->post_name, $context->slug, From 1ddf9d3b6db10d8f8bcc9517c49bcfa455194daa Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 16:27:07 +0200 Subject: [PATCH 26/30] Add PHPDoc descriptions to _make_mock_parsed_block --- src/wp-includes/blocks.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 677dc48effe09..c83ae4c14df8d 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1070,10 +1070,10 @@ function extract_serialized_parent_block( $serialized_block ) { * @since 6.7.0 * @access private * - * @param string $block_name - * @param array $attrs - * @param array $inner_blocks - * @return array Parsed block. + * @param string $block_name The block name for the mock parsed block. + * @param array $attrs Optional. Block attributes. Default empty array. + * @param array $inner_blocks Optional. Inner blocks. Default empty array. + * @return array The mock parsed block array. */ function _make_mock_parsed_block( $block_name, $attrs = array(), $inner_blocks = array() ) { return array( From 40464cecd016e6216b73aac53ff5c64c25cd6147 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 16:39:23 +0200 Subject: [PATCH 27/30] Wrap a line --- src/wp-includes/block-template-utils.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index 74fd5f2931d78..3d02866d40b67 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -1018,7 +1018,13 @@ function _build_block_template_result_from_post( $post ) { * we need to create a mock template part block and traverse it. */ $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(); + $attributes = ! empty( $existing_ignored_hooked_blocks ) + ? array( + 'metadata' => array( + 'ignoredHookedBlocks' => json_decode( $existing_ignored_hooked_blocks, true ) + ) + ) + : array(); $mock_template_part_block = _make_mock_parsed_block( 'core/template-part', $attributes, $blocks ); $content = traverse_and_serialize_blocks( array( $mock_template_part_block ), $before_block_visitor, $after_block_visitor ); $template->content = remove_serialized_parent_block( $content ); From 5df18602d0f522f87d2a347c2990162ad61aec99 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 16:40:22 +0200 Subject: [PATCH 28/30] Revert "Wrap a line" This reverts commit 87d9b0391fe811cee6fec6d115e9dbfccfa89e0e. --- src/wp-includes/block-template-utils.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index 3d02866d40b67..74fd5f2931d78 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -1018,13 +1018,7 @@ function _build_block_template_result_from_post( $post ) { * we need to create a mock template part block and traverse it. */ $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(); + $attributes = ! empty( $existing_ignored_hooked_blocks ) ? array( 'metadata' => array( 'ignoredHookedBlocks' => json_decode( $existing_ignored_hooked_blocks, true ) ) ) : array(); $mock_template_part_block = _make_mock_parsed_block( 'core/template-part', $attributes, $blocks ); $content = traverse_and_serialize_blocks( array( $mock_template_part_block ), $before_block_visitor, $after_block_visitor ); $template->content = remove_serialized_parent_block( $content ); From 1517c44659c530dda4a4b389c280276532a749ac Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 16:48:12 +0200 Subject: [PATCH 29/30] Rewrite to remove _make_mock_parsed_block --- src/wp-includes/block-template-utils.php | 45 ++++++++++++++++-------- src/wp-includes/blocks.php | 22 ------------ 2 files changed, 30 insertions(+), 37 deletions(-) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index 74fd5f2931d78..d7c2d11dae13d 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -606,7 +606,6 @@ function _build_block_template_result_from_file( $template_file, $template_type $template->area = $template_file['area']; } - $blocks = parse_blocks( $template->content ); $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'; @@ -620,13 +619,21 @@ function _build_block_template_result_from_file( $template_file, $template_type 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 create a mock template part block and traverse it. + * we need to wrap its content a mock template part block and traverse it. */ - $mock_template_part_block = _make_mock_parsed_block( 'core/template-part', array(), $blocks ); - $content = traverse_and_serialize_blocks( array( $mock_template_part_block ), $before_block_visitor, $after_block_visitor ); - $template->content = remove_serialized_parent_block( $content ); + $content = get_comment_delimited_block_content( + 'core/template-part', + array(), + $changes->post_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( $blocks, $before_block_visitor, $after_block_visitor ); + $template->content = traverse_and_serialize_blocks( + parse_blocks( $template->content ), + $before_block_visitor, + $after_block_visitor + ); } return $template; @@ -1011,19 +1018,27 @@ 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 ); if ( 'wp_template_part' === $template->type ) { - /** - * In order for hooked blocks to be inserted at positions first_child and last_child in a template part, - * we need to create a mock template part block and traverse it. - */ $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(); - $mock_template_part_block = _make_mock_parsed_block( 'core/template-part', $attributes, $blocks ); - $content = traverse_and_serialize_blocks( array( $mock_template_part_block ), $before_block_visitor, $after_block_visitor ); - $template->content = remove_serialized_parent_block( $content ); + + /** + * 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, + $changes->post_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( $blocks, $before_block_visitor, $after_block_visitor ); + $template->content = traverse_and_serialize_blocks( + parse_blocks( $template->content ), + $before_block_visitor, + $after_block_visitor + ); } } diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index c83ae4c14df8d..3b1fc25d48824 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1062,28 +1062,6 @@ function extract_serialized_parent_block( $serialized_block ) { return substr( $serialized_block, 0, $start ) . substr( $serialized_block, $end ); } -/** - * Returns a mock parsed block array for a given block name. - * - * This function is meant for internal use only. - * - * @since 6.7.0 - * @access private - * - * @param string $block_name The block name for the mock parsed block. - * @param array $attrs Optional. Block attributes. Default empty array. - * @param array $inner_blocks Optional. Inner blocks. Default empty array. - * @return array The mock parsed block array. - */ -function _make_mock_parsed_block( $block_name, $attrs = array(), $inner_blocks = array() ) { - return array( - 'blockName' => $block_name, - 'attrs' => $attrs, - 'innerBlocks' => $inner_blocks, - 'innerContent' => array_fill( 0, count( $inner_blocks ), null ), - ); -} - /** * 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. From b679595004c95ec9711295b5f921fb3f51743809 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 1 Jul 2024 16:52:36 +0200 Subject: [PATCH 30/30] Variable names --- src/wp-includes/block-template-utils.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index d7c2d11dae13d..6a4df84a11f7c 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -624,7 +624,7 @@ function _build_block_template_result_from_file( $template_file, $template_type $content = get_comment_delimited_block_content( 'core/template-part', array(), - $changes->post_content + $template->content ); $content = traverse_and_serialize_blocks( parse_blocks( $content ), $before_block_visitor, $after_block_visitor ); $template->content = remove_serialized_parent_block( $content ); @@ -1029,7 +1029,7 @@ function _build_block_template_result_from_post( $post ) { $content = get_comment_delimited_block_content( 'core/template-part', $attributes, - $changes->post_content + $template->content ); $content = traverse_and_serialize_blocks( parse_blocks( $content ), $before_block_visitor, $after_block_visitor ); $template->content = remove_serialized_parent_block( $content );