diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index cfae702dd6c46..61ae17cc1c0e4 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -725,74 +725,115 @@ function _wp_build_title_and_description_for_taxonomy_block_template( $taxonomy, } /** - * Builds a unified template object based a post Object. + * Builds a block template object from a post object. * - * @since 5.9.0 - * @since 6.3.0 Added `modified` property to template objects. - * @since 6.4.0 Added support for a revision post to be passed to this function. + * This is a helper function that creates a block template object from a given post object. + * It is self-sufficient in that it only uses information passed as arguments; it does not + * query the database for additional information. + * + * @since 6.5.1 * @access private * - * @param WP_Post $post Template post. + * @param WP_Post $post Template post. + * @param array $terms Additional terms to inform the template object. + * @param array $meta Additional meta fields to inform the template object. * @return WP_Block_Template|WP_Error Template or error object. */ -function _build_block_template_result_from_post( $post ) { - $default_template_types = get_default_block_template_types(); - - $post_id = wp_is_post_revision( $post ); - if ( ! $post_id ) { - $post_id = $post; - } - $parent_post = get_post( $post_id ); - - $terms = get_the_terms( $parent_post, 'wp_theme' ); - - if ( is_wp_error( $terms ) ) { - return $terms; - } - - if ( ! $terms ) { +function _build_block_template_object_from_post_object( $post, $terms = array(), $meta = array() ) { + if ( empty( $terms['wp_theme'] ) ) { return new WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.' ) ); } + $theme = $terms['wp_theme']; + + $default_template_types = get_default_block_template_types(); - $theme = $terms[0]->name; $template_file = _get_block_template_file( $post->post_type, $post->post_name ); $has_theme_file = get_stylesheet() === $theme && null !== $template_file; - $origin = get_post_meta( $parent_post->ID, 'origin', true ); - $is_wp_suggestion = get_post_meta( $parent_post->ID, 'is_wp_suggestion', true ); - $template = new WP_Block_Template(); $template->wp_id = $post->ID; - $template->id = $theme . '//' . $parent_post->post_name; + $template->id = $theme . '//' . $post->post_name; $template->theme = $theme; $template->content = $post->post_content; $template->slug = $post->post_name; $template->source = 'custom'; - $template->origin = ! empty( $origin ) ? $origin : null; + $template->origin = ! empty( $meta['origin'] ) ? $meta['origin'] : null; $template->type = $post->post_type; $template->description = $post->post_excerpt; $template->title = $post->post_title; $template->status = $post->post_status; $template->has_theme_file = $has_theme_file; - $template->is_custom = empty( $is_wp_suggestion ); + $template->is_custom = empty( $meta['is_wp_suggestion'] ); $template->author = $post->post_author; $template->modified = $post->post_modified; - if ( 'wp_template' === $parent_post->post_type && $has_theme_file && isset( $template_file['postTypes'] ) ) { + if ( 'wp_template' === $post->post_type && $has_theme_file && isset( $template_file['postTypes'] ) ) { $template->post_types = $template_file['postTypes']; } - if ( 'wp_template' === $parent_post->post_type && isset( $default_template_types[ $template->slug ] ) ) { + if ( 'wp_template' === $post->post_type && isset( $default_template_types[ $template->slug ] ) ) { $template->is_custom = false; } + if ( 'wp_template_part' === $post->post_type && isset( $terms['wp_template_part_area'] ) ) { + $template->area = $terms['wp_template_part_area']; + } + + return $template; +} + +/** + * Builds a unified template object based a post Object. + * + * @since 5.9.0 + * @since 6.3.0 Added `modified` property to template objects. + * @since 6.4.0 Added support for a revision post to be passed to this function. + * @access private + * + * @param WP_Post $post Template post. + * @return WP_Block_Template|WP_Error Template or error object. + */ +function _build_block_template_result_from_post( $post ) { + $post_id = wp_is_post_revision( $post ); + if ( ! $post_id ) { + $post_id = $post; + } + $parent_post = get_post( $post_id ); + $post->post_name = $parent_post->post_name; + $post->post_type = $parent_post->post_type; + + $terms = get_the_terms( $parent_post, 'wp_theme' ); + + if ( is_wp_error( $terms ) ) { + return $terms; + } + + if ( ! $terms ) { + return new WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.' ) ); + } + + $terms = array( + 'wp_theme' => $terms[0]->name, + ); + if ( 'wp_template_part' === $parent_post->post_type ) { $type_terms = get_the_terms( $parent_post, 'wp_template_part_area' ); if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) { - $template->area = $type_terms[0]->name; + $terms['wp_template_part_area'] = $type_terms[0]->name; } } + $meta = array( + 'origin' => get_post_meta( $parent_post->ID, 'origin', true ), + 'is_wp_suggestion' => get_post_meta( $parent_post->ID, 'is_wp_suggestion', true ), + ); + + $template = _build_block_template_object_from_post_object( $post, $terms, $meta ); + + if ( is_wp_error( $template ) ) { + return $template; + } + // Check for a block template without a description and title or with a title equal to the slug. if ( 'wp_template' === $parent_post->post_type && empty( $template->description ) && ( empty( $template->title ) || $template->title === $template->slug ) ) { $matches = array(); @@ -1444,36 +1485,70 @@ function get_template_hierarchy( $slug, $is_custom = false, $template_prefix = ' * @since 6.5.0 * @access private * - * @param stdClass $post An object representing a template or template part - * prepared for inserting or updating the database. - * @param WP_REST_Request $request Request object. - * @return stdClass The updated object representing a template or template part. + * @param stdClass $changes An object representing a template or template part + * prepared for inserting or updating the database. + * @param WP_REST_Request $deprecated Deprecated. Not used. + * @return stdClass|WP_Error The updated object representing a template or template part. */ -function inject_ignored_hooked_blocks_metadata_attributes( $post, $request ) { - $filter_name = current_filter(); - if ( ! str_starts_with( $filter_name, 'rest_pre_insert_' ) ) { - return $post; +function inject_ignored_hooked_blocks_metadata_attributes( $changes, $deprecated = null ) { + if ( null !== $deprecated ) { + _deprecated_argument( __FUNCTION__, '6.5.1' ); } - $post_type = str_replace( 'rest_pre_insert_', '', $filter_name ); $hooked_blocks = get_hooked_blocks(); if ( empty( $hooked_blocks ) && ! has_filter( 'hooked_block_types' ) ) { - return $post; + return $changes; } - // At this point, the post has already been created. - // We need to build the corresponding `WP_Block_Template` object as context argument for the visitor. - // To that end, we need to suppress hooked blocks from getting inserted into the template. - add_filter( 'hooked_block_types', '__return_empty_array', 99999, 0 ); - $template = $request['id'] ? get_block_template( $request['id'], $post_type ) : null; - remove_filter( 'hooked_block_types', '__return_empty_array', 99999 ); + $meta = isset( $changes->meta_input ) ? $changes->meta_input : array(); + $terms = isset( $changes->tax_input ) ? $changes->tax_input : array(); + + if ( empty( $changes->ID ) ) { + // There's no post object for this template in the database for this template yet. + $post = $changes; + } else { + // Find the existing post object. + $post = get_post( $changes->ID ); + + // If the post is a revision, use the parent post's post_name and post_type. + $post_id = wp_is_post_revision( $post ); + if ( $post_id ) { + $parent_post = get_post( $post_id ); + $post->post_name = $parent_post->post_name; + $post->post_type = $parent_post->post_type; + } + + // Apply the changes to the existing post object. + $post = (object) array_merge( (array) $post, (array) $changes ); + + $type_terms = get_the_terms( $changes->ID, 'wp_theme' ); + $terms['wp_theme'] = ! is_wp_error( $type_terms ) && ! empty( $type_terms ) ? $type_terms[0]->name : null; + } + + // Required for the WP_Block_Template. Update the post object with the current time. + $post->post_modified = current_time( 'mysql' ); + + // If the post_author is empty, set it to the current user. + if ( empty( $post->post_author ) ) { + $post->post_author = get_current_user_id(); + } + + 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; + } + + $template = _build_block_template_object_from_post_object( new WP_Post( $post ), $terms, $meta ); + + if ( is_wp_error( $template ) ) { + return $template; + } $before_block_visitor = make_before_block_visitor( $hooked_blocks, $template, 'set_ignored_hooked_blocks_metadata' ); $after_block_visitor = make_after_block_visitor( $hooked_blocks, $template, 'set_ignored_hooked_blocks_metadata' ); - $blocks = parse_blocks( $post->post_content ); - $content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); + $blocks = parse_blocks( $changes->post_content ); + $changes->post_content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); - $post->post_content = $content; - return $post; + return $changes; } diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 884357f41350a..04c644174dbad 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -753,7 +753,7 @@ add_action( 'init', '_wp_register_default_font_collections' ); // Add ignoredHookedBlocks metadata attribute to the template and template part post types. -add_filter( 'rest_pre_insert_wp_template', 'inject_ignored_hooked_blocks_metadata_attributes', 10, 2 ); -add_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes', 10, 2 ); +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' ); unset( $filter, $action ); diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php index cec8a77e04ef9..9eb64707aea07 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php @@ -532,7 +532,7 @@ public function delete_item( $request ) { * @since 5.8.0 * * @param WP_REST_Request $request Request object. - * @return stdClass Changes to pass to wp_update_post. + * @return stdClass|WP_Error Changes to pass to wp_update_post. */ protected function prepare_item_for_database( $request ) { $template = $request['id'] ? get_block_template( $request['id'], $this->post_type ) : null; diff --git a/tests/phpunit/tests/block-template-utils.php b/tests/phpunit/tests/block-template-utils.php index 4d92c277a9730..c56d3a01bc7cd 100644 --- a/tests/phpunit/tests/block-template-utils.php +++ b/tests/phpunit/tests/block-template-utils.php @@ -403,72 +403,4 @@ public function test_wp_generate_block_templates_export_file() { } $this->assertTrue( $has_html_files, 'contains at least one html file' ); } - - /** - * @ticket 60671 - * - * @covers inject_ignored_hooked_blocks_metadata_attributes - */ - public function test_inject_ignored_hooked_blocks_metadata_attributes_into_template() { - global $wp_current_filter; - // Mock currently set filter. The $wp_current_filter global is reset during teardown by - // WP_UnitTestCase_Base::_restore_hooks() in tests/phpunit/includes/abstract-testcase.php. - $wp_current_filter[] = 'rest_pre_insert_wp_template'; - - register_block_type( - 'tests/hooked-block', - array( - 'block_hooks' => array( - 'tests/anchor-block' => 'after', - ), - ) - ); - - $id = self::TEST_THEME . '//' . 'my_template'; - $request = new WP_REST_Request( 'POST', '/wp/v2/templates/' . $id ); - - $changes = new stdClass(); - $changes->post_content = 'Hello'; - - $post = inject_ignored_hooked_blocks_metadata_attributes( $changes, $request ); - $this->assertSame( - 'Hello', - $post->post_content, - 'The hooked block was not injected into the anchor block\'s ignoredHookedBlocks metadata.' - ); - } - - /** - * @ticket 60671 - * - * @covers inject_ignored_hooked_blocks_metadata_attributes - */ - public function test_inject_ignored_hooked_blocks_metadata_attributes_into_template_part() { - global $wp_current_filter; - // Mock currently set filter. The $wp_current_filter global is reset during teardown by - // WP_UnitTestCase_Base::_restore_hooks() in tests/phpunit/includes/abstract-testcase.php. - $wp_current_filter[] = 'rest_pre_insert_wp_template_part'; - - register_block_type( - 'tests/hooked-block', - array( - 'block_hooks' => array( - 'tests/anchor-block' => 'after', - ), - ) - ); - - $id = self::TEST_THEME . '//' . 'my_template_part'; - $request = new WP_REST_Request( 'POST', '/wp/v2/template-parts/' . $id ); - - $changes = new stdClass(); - $changes->post_content = 'Hello'; - - $post = inject_ignored_hooked_blocks_metadata_attributes( $changes, $request ); - $this->assertSame( - 'Hello', - $post->post_content, - 'The hooked block was not injected into the anchor block\'s ignoredHookedBlocks metadata.' - ); - } } diff --git a/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php new file mode 100644 index 0000000000000..a7acc8df99b8e --- /dev/null +++ b/tests/phpunit/tests/block-templates/injectIgnoredHookedBlocksMetadataAttributes.php @@ -0,0 +1,422 @@ +is_registered( 'tests/hooked-block' ) ) { + unregister_block_type( 'tests/hooked-block' ); + } + + parent::tear_down(); + } + + /** + * @ticket 60754 + */ + public function test_hooked_block_types_filter_with_newly_created_template() { + $action = new MockAction(); + add_filter( 'hooked_block_types', array( $action, 'filter' ), 10, 4 ); + + $changes = new stdClass(); + $changes->post_type = 'wp_template'; + $changes->post_status = 'publish'; + $changes->post_content = 'Hello'; + $changes->tax_input = array( + 'wp_theme' => get_stylesheet(), + ); + + inject_ignored_hooked_blocks_metadata_attributes( $changes ); + + $args = $action->get_args(); + $anchor_block_type = end( $args )[2]; + $context = end( $args )[3]; + + $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + + $this->assertInstanceOf( 'WP_Block_Template', $context ); + + $this->assertSame( + $changes->post_type, + $context->type, + 'The type field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->post_status, + $context->status, + 'The status field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->post_content, + $context->content, + 'The content field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertFalse( + $context->has_theme_file, + 'The has_theme_file field of the context passed to the hooked_block_types filter isn\'t set to false.' + ); + } + + /** + * @ticket 60754 + */ + public function test_hooked_block_types_filter_with_newly_created_template_part() { + $action = new MockAction(); + add_filter( 'hooked_block_types', array( $action, 'filter' ), 10, 4 ); + + $changes = new stdClass(); + $changes->post_type = 'wp_template_part'; + $changes->post_status = 'publish'; + $changes->post_content = 'Hello'; + $changes->tax_input = array( + 'wp_theme' => get_stylesheet(), + 'wp_template_part_area' => WP_TEMPLATE_PART_AREA_HEADER, + ); + + inject_ignored_hooked_blocks_metadata_attributes( $changes ); + + $args = $action->get_args(); + $anchor_block_type = end( $args )[2]; + $context = end( $args )[3]; + + $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + + $this->assertInstanceOf( 'WP_Block_Template', $context ); + + $this->assertSame( + $changes->post_type, + $context->type, + 'The type field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->post_status, + $context->status, + 'The status field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->post_content, + $context->content, + 'The content field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertFalse( + $context->has_theme_file, + 'The has_theme_file field of the context passed to the hooked_block_types filter isn\'t set to false.' + ); + $this->assertSame( + $changes->tax_input['wp_template_part_area'], + $context->area, + 'The area field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + } + + /** + * @ticket 60754 + */ + public function test_hooked_block_types_filter_with_existing_template_file() { + $action = new MockAction(); + add_filter( 'hooked_block_types', array( $action, 'filter' ), 10, 4 ); + + $changes = new stdClass(); + $changes->post_name = 'index'; + $changes->post_type = 'wp_template'; + $changes->post_status = 'publish'; + $changes->post_content = 'Hello'; + $changes->meta_input = array( + 'origin' => 'theme', + ); + $changes->tax_input = array( + 'wp_theme' => get_stylesheet(), + ); + + inject_ignored_hooked_blocks_metadata_attributes( $changes ); + + $args = $action->get_args(); + $anchor_block_type = end( $args )[2]; + $context = end( $args )[3]; + + $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + + $this->assertInstanceOf( 'WP_Block_Template', $context ); + + $this->assertSame( + $changes->post_name, + $context->slug, + 'The slug field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->post_type, + $context->type, + 'The type field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->post_status, + $context->status, + 'The status field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->post_content, + $context->content, + 'The content field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertTrue( + $context->has_theme_file, + 'The has_theme_file field of the context passed to the hooked_block_types filter isn\'t set to true.' + ); + $this->assertSame( + $changes->meta_input['origin'], + $context->origin, + 'The origin field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + } + + /** + * @ticket 60754 + */ + public function test_hooked_block_types_filter_with_existing_template_part_file() { + $action = new MockAction(); + add_filter( 'hooked_block_types', array( $action, 'filter' ), 10, 4 ); + + $changes = new stdClass(); + $changes->post_name = 'small-header'; + $changes->post_type = 'wp_template_part'; + $changes->post_status = 'publish'; + $changes->post_content = 'Hello'; + $changes->meta_input = array( + 'origin' => 'theme', + ); + $changes->tax_input = array( + 'wp_theme' => get_stylesheet(), + 'wp_template_part_area' => WP_TEMPLATE_PART_AREA_HEADER, + ); + + inject_ignored_hooked_blocks_metadata_attributes( $changes ); + + $args = $action->get_args(); + $anchor_block_type = end( $args )[2]; + $context = end( $args )[3]; + + $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + + $this->assertInstanceOf( 'WP_Block_Template', $context ); + + $this->assertSame( + $changes->post_name, + $context->slug, + 'The slug field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->post_type, + $context->type, + 'The type field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->post_status, + $context->status, + 'The status field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->post_content, + $context->content, + 'The content field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertTrue( + $context->has_theme_file, + 'The has_theme_file field of the context passed to the hooked_block_types filter isn\'t set to true.' + ); + $this->assertSame( + $changes->meta_input['origin'], + $context->origin, + 'The origin field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->tax_input['wp_template_part_area'], + $context->area, + 'The area field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + } + + /** + * @ticket 60754 + */ + public function test_hooked_block_types_filter_with_existing_template_post() { + $action = new MockAction(); + add_filter( 'hooked_block_types', array( $action, 'filter' ), 10, 4 ); + + $changes = new stdClass(); + $changes->post_name = 'my-updated-template'; + $changes->ID = self::$template_post->ID; + $changes->post_content = 'Hello'; + + inject_ignored_hooked_blocks_metadata_attributes( $changes ); + + $args = $action->get_args(); + $anchor_block_type = end( $args )[2]; + $context = end( $args )[3]; + + $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + + $this->assertInstanceOf( 'WP_Block_Template', $context ); + + $this->assertSame( + $changes->post_name, + $context->slug, + 'The slug field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->ID, + $context->wp_id, + 'The wp_id field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + 'publish', + $context->status, + 'The status field of the context passed to the hooked_block_types filter isn\'t set to publish.' + ); + $this->assertSame( + $changes->post_content, + $context->content, + 'The content field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + + $this->assertSame( + self::$template_post->post_title, + $context->title, + 'The title field of the context passed to the hooked_block_types filter doesn\'t match the template post object.' + ); + $this->assertSame( + self::$template_post->post_excerpt, + $context->description, + 'The description field of the context passed to the hooked_block_types filter doesn\'t match the template post object.' + ); + } + + /** + * @ticket 60754 + */ + public function test_hooked_block_types_filter_with_existing_template_part_post() { + $action = new MockAction(); + add_filter( 'hooked_block_types', array( $action, 'filter' ), 10, 4 ); + + $changes = new stdClass(); + $changes->post_name = 'my-updated-template-part'; + $changes->ID = self::$template_part_post->ID; + $changes->post_content = 'Hello'; + + $changes->tax_input = array( + 'wp_template_part_area' => WP_TEMPLATE_PART_AREA_FOOTER, + ); + + inject_ignored_hooked_blocks_metadata_attributes( $changes ); + + $args = $action->get_args(); + $anchor_block_type = end( $args )[2]; + $context = end( $args )[3]; + + $this->assertSame( 'tests/anchor-block', $anchor_block_type ); + + $this->assertInstanceOf( 'WP_Block_Template', $context ); + + $this->assertSame( + $changes->post_name, + $context->slug, + 'The slug field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->ID, + $context->wp_id, + 'The wp_id field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + 'publish', + $context->status, + 'The status field of the context passed to the hooked_block_types filter isn\'t set to publish.' + ); + $this->assertSame( + $changes->post_content, + $context->content, + 'The content field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + $this->assertSame( + $changes->tax_input['wp_template_part_area'], + $context->area, + 'The area field of the context passed to the hooked_block_types filter doesn\'t match the template changes.' + ); + + $this->assertSame( + self::$template_part_post->post_title, + $context->title, + 'The title field of the context passed to the hooked_block_types filter doesn\'t match the template post object.' + ); + $this->assertSame( + self::$template_part_post->post_excerpt, + $context->description, + 'The description field of the context passed to the hooked_block_types filter doesn\'t match the template post object.' + ); + } + + /** + * @ticket 60671 + */ + public function test_inject_ignored_hooked_blocks_metadata_attributes_into_template() { + register_block_type( + 'tests/hooked-block', + array( + 'block_hooks' => array( + 'tests/anchor-block' => 'after', + ), + ) + ); + + $id = self::TEST_THEME . '//' . 'my_template'; + $template = get_block_template( $id, 'wp_template' ); + + $changes = new stdClass(); + $changes->ID = $template->wp_id; + $changes->post_content = 'Hello'; + + $post = inject_ignored_hooked_blocks_metadata_attributes( $changes ); + $this->assertSame( + 'Hello', + $post->post_content, + 'The hooked block was not injected into the anchor block\'s ignoredHookedBlocks metadata.' + ); + } + + /** + * @ticket 60671 + */ + public function test_inject_ignored_hooked_blocks_metadata_attributes_into_template_part() { + register_block_type( + 'tests/hooked-block', + array( + 'block_hooks' => array( + 'tests/anchor-block' => 'after', + ), + ) + ); + + $id = self::TEST_THEME . '//' . 'my_template_part'; + $template = get_block_template( $id, 'wp_template_part' ); + + $changes = new stdClass(); + $changes->ID = $template->wp_id; + $changes->post_content = 'Hello'; + + $post = inject_ignored_hooked_blocks_metadata_attributes( $changes ); + $this->assertSame( + 'Hello', + $post->post_content, + 'The hooked block was not injected into the anchor block\'s ignoredHookedBlocks metadata.' + ); + } +} diff --git a/tests/phpunit/tests/rest-api/wpRestTemplatesController.php b/tests/phpunit/tests/rest-api/wpRestTemplatesController.php index 9665939dd6975..50678a686d561 100644 --- a/tests/phpunit/tests/rest-api/wpRestTemplatesController.php +++ b/tests/phpunit/tests/rest-api/wpRestTemplatesController.php @@ -14,7 +14,8 @@ class Tests_REST_WpRestTemplatesController extends WP_Test_REST_Controller_Testc * @var int */ protected static $admin_id; - private static $post; + private static $template_post; + private static $template_part_post; /** * Create fake data before our tests run. @@ -29,7 +30,7 @@ public static function wpSetupBeforeClass( $factory ) { ); // Set up template post. - $args = array( + $args = array( 'post_type' => 'wp_template', 'post_name' => 'my_template', 'post_title' => 'My Template', @@ -41,12 +42,32 @@ public static function wpSetupBeforeClass( $factory ) { ), ), ); - self::$post = self::factory()->post->create_and_get( $args ); - wp_set_post_terms( self::$post->ID, get_stylesheet(), 'wp_theme' ); + self::$template_post = self::factory()->post->create_and_get( $args ); + wp_set_post_terms( self::$template_post->ID, get_stylesheet(), 'wp_theme' ); + + // Set up template part post. + $args = array( + 'post_type' => 'wp_template_part', + 'post_name' => 'my_template_part', + 'post_title' => 'My Template Part', + 'post_content' => 'Content', + 'post_excerpt' => 'Description of my template part.', + 'tax_input' => array( + 'wp_theme' => array( + get_stylesheet(), + ), + 'wp_template_part_area' => array( + WP_TEMPLATE_PART_AREA_HEADER, + ), + ), + ); + self::$template_part_post = self::factory()->post->create_and_get( $args ); + wp_set_post_terms( self::$template_part_post->ID, get_stylesheet(), 'wp_theme' ); + wp_set_post_terms( self::$template_part_post->ID, WP_TEMPLATE_PART_AREA_HEADER, 'wp_template_part_area' ); } public static function wpTearDownAfterClass() { - wp_delete_post( self::$post->ID ); + wp_delete_post( self::$template_post->ID ); } /** @@ -56,7 +77,7 @@ public static function wpTearDownAfterClass() { */ public function tear_down() { if ( has_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes' ) ) { - remove_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes', 10 ); + remove_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes' ); } if ( WP_Block_Type_Registry::get_instance()->is_registered( 'tests/block' ) ) { unregister_block_type( 'tests/hooked-block' ); @@ -130,11 +151,11 @@ public function test_get_items() { 'rendered' => 'My Template', ), 'status' => 'publish', - 'wp_id' => self::$post->ID, + 'wp_id' => self::$template_post->ID, 'has_theme_file' => false, 'is_custom' => true, 'author' => 0, - 'modified' => mysql_to_rfc3339( self::$post->post_modified ), + 'modified' => mysql_to_rfc3339( self::$template_post->post_modified ), 'author_text' => 'Test Blog', 'original_source' => 'site', ), @@ -177,11 +198,11 @@ public function test_get_item() { 'rendered' => 'My Template', ), 'status' => 'publish', - 'wp_id' => self::$post->ID, + 'wp_id' => self::$template_post->ID, 'has_theme_file' => false, 'is_custom' => true, 'author' => 0, - 'modified' => mysql_to_rfc3339( self::$post->post_modified ), + 'modified' => mysql_to_rfc3339( self::$template_post->post_modified ), 'author_text' => 'Test Blog', 'original_source' => 'site', ), @@ -216,11 +237,11 @@ public function test_get_item_works_with_a_single_slash( $endpoint_url ) { 'rendered' => 'My Template', ), 'status' => 'publish', - 'wp_id' => self::$post->ID, + 'wp_id' => self::$template_post->ID, 'has_theme_file' => false, 'is_custom' => true, 'author' => 0, - 'modified' => mysql_to_rfc3339( self::$post->post_modified ), + 'modified' => mysql_to_rfc3339( self::$template_post->post_modified ), 'author_text' => 'Test Blog', 'original_source' => 'site', ), @@ -944,16 +965,17 @@ public function test_prepare_item_for_database_injects_hooked_block() { ) ); - add_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes', 10, 2 ); + add_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes' ); $endpoint = new WP_REST_Templates_Controller( 'wp_template_part' ); $prepare_item_for_database = new ReflectionMethod( $endpoint, 'prepare_item_for_database' ); $prepare_item_for_database->setAccessible( true ); + $id = get_stylesheet() . '//' . 'my_template_part'; $body_params = array( - 'title' => 'Untitled Template Part', - 'slug' => 'untitled-template-part', + 'id' => $id, + 'slug' => 'my_template_part', 'content' => 'Hello', );