diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index dd300eb12c6feb..ae3b9620a33584 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -1520,153 +1520,3 @@ function block_core_navigation_insert_hooked_blocks( $inner_blocks, $post ) { return traverse_and_serialize_block( $mock_navigation_block, $before_block_visitor, $after_block_visitor ); } - -/** - * Insert ignoredHookedBlocks meta into the Navigation block and its inner blocks. - * - * Given a Navigation block's inner blocks and its corresponding `wp_navigation` post object, - * this function inserts ignoredHookedBlocks meta into it, and returns the serialized inner blocks in a - * mock Navigation block wrapper. - * - * @since 6.5.0 - * - * @param array $inner_blocks Parsed inner blocks of a Navigation block. - * @param WP_Post $post `wp_navigation` post object corresponding to the block. - * @return string Serialized inner blocks in mock Navigation block wrapper, with hooked blocks inserted, if any. - */ -function block_core_navigation_set_ignored_hooked_blocks_metadata( $inner_blocks, $post ) { - $mock_navigation_block = block_core_navigation_mock_parsed_block( $inner_blocks, $post ); - $hooked_blocks = get_hooked_blocks(); - $before_block_visitor = null; - $after_block_visitor = null; - - if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) { - $before_block_visitor = make_before_block_visitor( $hooked_blocks, $post, 'set_ignored_hooked_blocks_metadata' ); - $after_block_visitor = make_after_block_visitor( $hooked_blocks, $post, 'set_ignored_hooked_blocks_metadata' ); - } - - return traverse_and_serialize_block( $mock_navigation_block, $before_block_visitor, $after_block_visitor ); -} - -/** - * Updates the post meta with the list of ignored hooked blocks when the navigation is created or updated via the REST API. - * - * @access private - * @since 6.5.0 - * - * @param stdClass $post Post object. - * @return stdClass The updated post object. - */ -function block_core_navigation_update_ignore_hooked_blocks_meta( $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. - */ - if ( ! isset( $post->post_content ) ) { - return $post; - } - - /* - * We run the Block Hooks mechanism to inject the `metadata.ignoredHookedBlocks` attribute into - * all anchor blocks. For the root level, we create a mock Navigation and extract them from there. - */ - $blocks = parse_blocks( $post->post_content ); - - /* - * Block Hooks logic requires a `WP_Post` object (rather than the `stdClass` with the updates that - * we're getting from the `rest_pre_insert_wp_navigation` filter) as its second argument (to be - * used as context for hooked blocks insertion). - * We thus have to look it up from the DB,based on `$post->ID`. - */ - $markup = block_core_navigation_set_ignored_hooked_blocks_metadata( $blocks, get_post( $post->ID ) ); - - $root_nav_block = parse_blocks( $markup )[0]; - $ignored_hooked_blocks = isset( $root_nav_block['attrs']['metadata']['ignoredHookedBlocks'] ) - ? $root_nav_block['attrs']['metadata']['ignoredHookedBlocks'] - : 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 ) ); - } - update_post_meta( $post->ID, '_wp_ignored_hooked_blocks', json_encode( $ignored_hooked_blocks ) ); - } - - $post->post_content = block_core_navigation_remove_serialized_parent_block( $markup ); - return $post; -} - -/* - * Before adding our filter, we verify if it's already added in Core. - * However, during the build process, Gutenberg automatically prefixes our functions with "gutenberg_". - * Therefore, we concatenate the Core's function name to circumvent this prefix for our check. - */ -$rest_insert_wp_navigation_core_callback = 'block_core_navigation_' . 'update_ignore_hooked_blocks_meta'; // phpcs:ignore Generic.Strings.UnnecessaryStringConcat.Found - -/* - * Do not add the `block_core_navigation_update_ignore_hooked_blocks_meta` filter in the following cases: - * - If Core has added the `update_ignored_hooked_blocks_postmeta` filter already (WP >= 6.6); - * - or if the `$rest_insert_wp_navigation_core_callback` filter has already been added. - */ -if ( - ! has_filter( 'rest_pre_insert_wp_navigation', 'update_ignored_hooked_blocks_postmeta' ) && - ! has_filter( 'rest_pre_insert_wp_navigation', $rest_insert_wp_navigation_core_callback ) -) { - add_filter( 'rest_pre_insert_wp_navigation', 'block_core_navigation_update_ignore_hooked_blocks_meta' ); -} - -/** - * Hooks into the REST API response for the core/navigation block and adds the first and last inner blocks. - * - * @since 6.5.0 - * - * @param WP_REST_Response $response The response object. - * @param WP_Post $post Post object. - * @return WP_REST_Response The response object. - */ -function block_core_navigation_insert_hooked_blocks_into_rest_response( $response, $post ) { - if ( ! isset( $response->data['content']['raw'] ) || ! isset( $response->data['content']['rendered'] ) ) { - return $response; - } - $parsed_blocks = parse_blocks( $response->data['content']['raw'] ); - $content = block_core_navigation_insert_hooked_blocks( $parsed_blocks, $post ); - - // Remove mock Navigation block wrapper. - $content = block_core_navigation_remove_serialized_parent_block( $content ); - - $response->data['content']['raw'] = $content; - - /** This filter is documented in wp-includes/post-template.php */ - $response->data['content']['rendered'] = apply_filters( 'the_content', $content ); - - return $response; -} - -/* - * Before adding our filter, we verify if it's already added in Core. - * However, during the build process, Gutenberg automatically prefixes our functions with "gutenberg_". - * Therefore, we concatenate the Core's function name to circumvent this prefix for our check. - */ -$rest_prepare_wp_navigation_core_callback = 'block_core_navigation_' . 'insert_hooked_blocks_into_rest_response'; - -/* - * Do not add the `block_core_navigation_insert_hooked_blocks_into_rest_response` filter in the following cases: - * - If Core has added the `insert_hooked_blocks_into_rest_response` filter already (WP >= 6.6); - * - or if the `$rest_prepare_wp_navigation_core_callback` filter has already been added. - */ -if ( - ! has_filter( 'rest_prepare_wp_navigation', 'insert_hooked_blocks_into_rest_response' ) && - ! has_filter( 'rest_prepare_wp_navigation', $rest_prepare_wp_navigation_core_callback ) -) { - add_filter( 'rest_prepare_wp_navigation', 'block_core_navigation_insert_hooked_blocks_into_rest_response', 10, 3 ); -} diff --git a/phpunit/blocks/block-navigation-block-hooks-test.php b/phpunit/blocks/block-navigation-block-hooks-test.php deleted file mode 100644 index 1d3c86bf4bca4b..00000000000000 --- a/phpunit/blocks/block-navigation-block-hooks-test.php +++ /dev/null @@ -1,148 +0,0 @@ -'; - - self::$navigation_post = self::factory()->post->create_and_get( - array( - 'post_type' => 'wp_navigation', - 'post_title' => 'Navigation Menu', - 'post_content' => 'Original content', - ) - ); - } - - /** - * Tear down each test method. - */ - 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(); - } - - /** - * @covers ::gutenberg_block_core_navigation_update_ignore_hooked_blocks_meta - */ - public function test_block_core_navigation_update_ignore_hooked_blocks_meta_preserves_entities() { - register_block_type( - 'tests/my-block', - array( - 'block_hooks' => array( - 'core/navigation' => 'last_child', - ), - ) - ); - - $original_markup = ''; - $post = new stdClass(); - $post->ID = self::$navigation_post->ID; - $post->post_content = $original_markup; - - $post = gutenberg_block_core_navigation_update_ignore_hooked_blocks_meta( $post ); - - // We expect the '&' character to be replaced with its unicode representation. - $expected_markup = str_replace( '&', '\u0026', $original_markup ); - - $this->assertSame( - $expected_markup, - $post->post_content, - 'Post content did not match expected markup with entities escaped.' - ); - $this->assertSame( - array( 'tests/my-block' ), - json_decode( get_post_meta( self::$navigation_post->ID, '_wp_ignored_hooked_blocks', true ), true ), - 'Block was not added to ignored hooked blocks metadata.' - ); - } - - /** - * @covers ::gutenberg_block_core_navigation_update_ignore_hooked_blocks_meta - */ - public function test_block_core_navigation_dont_modify_no_post_id() { - register_block_type( - 'tests/my-block', - array( - 'block_hooks' => array( - 'core/navigation' => 'last_child', - ), - ) - ); - - $original_markup = ''; - $post = new stdClass(); - $post->post_content = $original_markup; - - $post = gutenberg_block_core_navigation_update_ignore_hooked_blocks_meta( $post ); - - $this->assertSame( - $original_markup, - $post->post_content, - 'Post content did not match the original markup.' - ); - } - - /** - * @covers ::gutenberg_block_core_navigation_update_ignore_hooked_blocks_meta - */ - public function test_block_core_navigation_retains_content_if_not_set() { - register_block_type( - 'tests/my-block', - array( - 'block_hooks' => array( - 'core/navigation' => 'last_child', - ), - ) - ); - - $post = new stdClass(); - $post->ID = self::$navigation_post->ID; - $post->post_title = 'Navigation Menu with changes'; - - $post = gutenberg_block_core_navigation_update_ignore_hooked_blocks_meta( $post ); - - $this->assertSame( - 'Navigation Menu with changes', - $post->post_title, - 'Post title was changed.' - ); - - $this->assertFalse( - isset( $post->post_content ), - 'Post content should not be set.' - ); - } -}