Skip to content

Commit

Permalink
Navigation Block: Fix erroneous escaping of ampersands (etc) (#59561)
Browse files Browse the repository at this point in the history
Fix erroneous escaping of ampersands to `u0026amp;`. 

This is done by using the [`rest_pre_insert_{$this->post_type}`](https://developer.wordpress.org/reference/hooks/rest_pre_insert_this-post_type/) _filter_ rather than the `rest_insert_wp_navigation` _action_. This avoids calling `wp_update_post` twice, which was the original reason of the issue, as it removed the backslash from the already-encoded entity.

Unlinked contributors: kylekelly.

Co-authored-by: ockham <bernhard-reiter@git.wordpress.org>
Co-authored-by: t-hamano <wildworks@git.wordpress.org>
Co-authored-by: tjcafferkey <tomjcafferkey@git.wordpress.org>
Co-authored-by: youknowriad <youknowriad@git.wordpress.org>
Co-authored-by: draganescu <andraganescu@git.wordpress.org>
Co-authored-by: annezazu <annezazu@git.wordpress.org>
Co-authored-by: fabiankaegy <fabiankaegy@git.wordpress.org>
Co-authored-by: getdave <get_dave@git.wordpress.org>
Co-authored-by: swissspidy <swissspidy@git.wordpress.org>
  • Loading branch information
10 people committed Mar 5, 2024
1 parent 4927ea4 commit 5929f23
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 12 deletions.
30 changes: 18 additions & 12 deletions packages/block-library/src/navigation/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -1458,13 +1458,18 @@ function block_core_navigation_set_ignored_hooked_blocks_metadata( $inner_blocks
/**
* Updates the post meta with the list of ignored hooked blocks when the navigation is created or updated via the REST API.
*
* @param WP_Post $post Post object.
* @param stdClass $post Post object.
*/
function block_core_navigation_update_ignore_hooked_blocks_meta( $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 );
$markup = block_core_navigation_set_ignored_hooked_blocks_metadata( $blocks, $post );

// 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'] )
Expand All @@ -1480,14 +1485,8 @@ function block_core_navigation_update_ignore_hooked_blocks_meta( $post ) {
update_post_meta( $post->ID, '_wp_ignored_hooked_blocks', json_encode( $ignored_hooked_blocks ) );
}

$serialized_inner_blocks = block_core_navigation_remove_serialized_parent_block( $markup );

wp_update_post(
array(
'ID' => $post->ID,
'post_content' => $serialized_inner_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.
Expand All @@ -1497,8 +1496,15 @@ function block_core_navigation_update_ignore_hooked_blocks_meta( $post ) {

// Injection of hooked blocks into the Navigation block relies on some functions present in WP >= 6.5
// that are not present in Gutenberg's WP 6.5 compatibility layer.
if ( function_exists( 'set_ignored_hooked_blocks_metadata' ) && ! has_filter( 'rest_insert_wp_navigation', $rest_insert_wp_navigation_core_callback ) ) {
add_action( 'rest_insert_wp_navigation', 'block_core_navigation_update_ignore_hooked_blocks_meta', 10, 3 );
if ( function_exists( 'set_ignored_hooked_blocks_metadata' ) && ! 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', 10 );
}

// Previous versions of Gutenberg and WordPress 6.5 Betas were attaching the block_core_navigation_update_ignore_hooked_blocks_meta
// function to the `rest_insert_wp_navigation` _action_ (rather than the `rest_pre_insert_wp_navigation` _filter_).
// To avoid collisions, we need to remove the filter from that action if it's present.
if ( has_filter( 'rest_insert_wp_navigation', $rest_insert_wp_navigation_core_callback ) ) {
remove_filter( 'rest_insert_wp_navigation', $rest_insert_wp_navigation_core_callback, 10 );
}

/**
Expand Down
95 changes: 95 additions & 0 deletions phpunit/blocks/block-navigation-block-hooks-test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php
/**
* Navigation block block hooks tests.
*
* @package WordPress
* @subpackage Blocks
*/

/**
* Tests for the Navigation block.
*
* @group blocks
*/
class Block_Navigation_Block_Hooks_Test extends WP_UnitTestCase {
/**
* Original markup.
*
* @var string
*/
protected static $original_markup;

/**
* Post object.
*
* @var object
*/
protected static $navigation_post;

/**
* Setup method.
*/
public static function wpSetUpBeforeClass() {
//self::$original_markup = '<!-- wp:navigation-link {"label":"News & About","type":"page","id":2,"url":"http://localhost:8888/?page_id=2","kind":"post-type"} /-->';

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() {
if ( ! function_exists( 'set_ignored_hooked_blocks_metadata' ) ) {
$this->markTestSkipped( 'Test skipped on WordPress versions that do not included required Block Hooks functionalit.' );
}

register_block_type(
'tests/my-block',
array(
'block_hooks' => array(
'core/navigation' => 'last_child',
),
)
);

$original_markup = '<!-- wp:navigation-link {"label":"News & About","type":"page","id":2,"url":"http://localhost:8888/?page_id=2","kind":"post-type"} /-->';
$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.'
);
}
}

0 comments on commit 5929f23

Please sign in to comment.