Skip to content

Commit

Permalink
Navigation: Fix creating Navigation from pages or menu with HTML in t…
Browse files Browse the repository at this point in the history
…itle (#24673)

* Navigation: Fix creating Navigation from pages or menu with HTML in title

Pages and menu items in WordPress allow HTML in the title. We should
therefore not escape the menu item title when creating a Navigation
block from top-level pages or an existing menu.

Additionally, the menu items REST API endpoint was setting title.raw to
the menu item CPT's post_title and title.rendered to the referenced
post's post_title. Both title.raw and title.rendered should be set to
the menu item's title, which is properly set by
wp_setup_nav_menu_item(). The only difference is that title.rendered
should be passed through the_title, which escapes & characters, and
nav_menu_item_title, which provides plugins a chance to customise menu
item titles before display. This matches what Walker_Nav_Menu does.

* REST API: Test that menu items have their title properly escaped

* REST API: Fix menu items unit tests for multisite environments

* REST API: Use is_multisite() instead of MULTISITE

Co-authored-by: Timothy Jacobs <[email protected]>

Co-authored-by: Timothy Jacobs <[email protected]>
  • Loading branch information
noisysocks and TimothyBJacobs authored Sep 1, 2020
1 parent b418bf7 commit dda3a9c
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 11 deletions.
10 changes: 8 additions & 2 deletions lib/class-wp-rest-menu-items-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' ) );
Expand Down
10 changes: 2 additions & 8 deletions packages/block-library/src/navigation/placeholder.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/**
* External dependencies
*/

import { escape } from 'lodash';
import classnames from 'classnames';

/**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
} )
);
Expand Down
62 changes: 61 additions & 1 deletion phpunit/class-rest-nav-menu-items-controller-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => '<strong>Foo</strong> & 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' => '<strong>Foo</strong> &#038; bar',
'raw' => '<strong>Foo</strong> & 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' => '<strong>Foo</strong> &amp; bar',
'raw' => '<strong>Foo</strong> &amp; bar',
),
$response->get_data()['title']
);
}

wp_delete_post( $menu_item_id );
}

/**
*
*/
Expand Down Expand Up @@ -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'] ) );
}
Expand Down

0 comments on commit dda3a9c

Please sign in to comment.