diff --git a/lib/class-wp-rest-menu-items-controller.php b/lib/class-wp-rest-menu-items-controller.php index c0f70208c4279..f6cf192a80722 100644 --- a/lib/class-wp-rest-menu-items-controller.php +++ b/lib/class-wp-rest-menu-items-controller.php @@ -569,9 +569,15 @@ public function prepare_item_for_response( $post, $request ) { if ( in_array( 'title', $fields, true ) ) { add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); + /** This filter is documented in wp-includes/post-template.php */ + $title = apply_filters( 'the_title', $menu_item->title, $menu_item->ID ); + + /** This filter is documented in wp-includes/class-walker-nav-menu.php */ + $title = apply_filters( 'nav_menu_item_title', $title, $menu_item, null, 0 ); + $data['title'] = array( - 'raw' => $menu_item->post_title, - 'rendered' => $menu_item->title, + 'raw' => $menu_item->title, + 'rendered' => $title, ); remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); diff --git a/packages/block-library/src/navigation/placeholder.js b/packages/block-library/src/navigation/placeholder.js index ed2aa4b43afea..0e50078babbb8 100644 --- a/packages/block-library/src/navigation/placeholder.js +++ b/packages/block-library/src/navigation/placeholder.js @@ -1,8 +1,6 @@ /** * External dependencies */ - -import { escape } from 'lodash'; import classnames from 'classnames'; /** @@ -93,9 +91,7 @@ function mapMenuItemsToBlocks( nodes ) { type, id, url, - label: ! title.rendered - ? __( '(no title)' ) - : escape( title.rendered ), + label: ! title.rendered ? __( '(no title)' ) : title.rendered, opensInNewTab: false, }, innerBlocks @@ -136,9 +132,7 @@ function convertPagesToBlocks( pages ) { type, id, url, - label: ! title.rendered - ? __( '(no title)' ) - : escape( title.rendered ), + label: ! title.rendered ? __( '(no title)' ) : title.rendered, opensInNewTab: false, } ) ); diff --git a/phpunit/class-rest-nav-menu-items-controller-test.php b/phpunit/class-rest-nav-menu-items-controller-test.php index dcc8c5ef4a3fa..69593227a32ac 100644 --- a/phpunit/class-rest-nav-menu-items-controller-test.php +++ b/phpunit/class-rest-nav-menu-items-controller-test.php @@ -177,6 +177,66 @@ public function test_get_item() { $this->check_get_menu_item_response( $response, 'view' ); } + /** + * Test that title.raw contains the verbatim title and that title.rendered + * has been passed through the_title which escapes & characters. + * + * @see https://github.com/WordPress/gutenberg/pull/24673 + */ + public function test_get_item_escapes_title() { + wp_set_current_user( self::$admin_id ); + + $menu_item_id = wp_update_nav_menu_item( + $this->menu_id, + 0, + array( + 'menu-item-type' => 'taxonomy', + 'menu-item-object' => 'post_tag', + 'menu-item-object-id' => $this->tag_id, + 'menu-item-status' => 'publish', + 'menu-item-title' => 'Foo & bar', + ) + ); + + $request = new WP_REST_Request( + 'GET', + "/__experimental/menu-items/$menu_item_id" + ); + $request->set_query_params( + array( + 'context' => 'edit', + ) + ); + + $response = rest_get_server()->dispatch( $request ); + + if ( ! is_multisite() ) { + // Check that title.raw is the unescaped title and that + // title.rendered has been run through the_title. + $this->assertEquals( + array( + 'rendered' => 'Foo & bar', + 'raw' => 'Foo & bar', + ), + $response->get_data()['title'] + ); + } else { + // In a multisite, administrators do not have unfiltered_html and + // post_title is ran through wp_kses before being saved in the + // database. Running the title through the_title does nothing in + // this case. + $this->assertEquals( + array( + 'rendered' => 'Foo & bar', + 'raw' => 'Foo & bar', + ), + $response->get_data()['title'] + ); + } + + wp_delete_post( $menu_item_id ); + } + /** * */ @@ -726,7 +786,7 @@ protected function check_menu_item_data( $post, $data, $context, $links ) { $this->assertEquals( $post->title, $data['title']['rendered'] ); remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); if ( 'edit' === $context ) { - $this->assertEquals( $post->post_title, $data['title']['raw'] ); + $this->assertEquals( $post->title, $data['title']['raw'] ); } else { $this->assertFalse( isset( $data['title']['raw'] ) ); }