Customize: Improve alignment of WP_Customize_Nav_Menu_Item_Setting::sanitize()
behavior with wp_update_nav_menu_item()
.
* Apply `title_save_pre`, `excerpt_save_pre`, and `content_save_pre` filters on a nav menu item's `title`, `attr_title`, and `description` properties respectively. This ensures that arbitrary markup can be supplied if the user has `unfiltered_html` cap, and for these fields to have markup stripped if not. * Ensure a nav menu item's `post_status` is sanitized as `publish` or `draft` using the same conditions as `wp_update_nav_menu_item()`. * Align `WP_Customize_Nav_Menu_Item_Setting::sanitize()` behavior for sanitizing `position` to be the same as `wp_update_nav_menu_item()`. * Also apply `nav_menu_attr_title` and `nav_menu_description` filters in `WP_Customize_Nav_Menu_Item_Setting::value_as_wp_post_nav_menu_item()` to ensure that previewing markup entered into menu item description will preview the same way as when the nav menu item is saved. * Add unit tests. Fixes #32812. git-svn-id: https://develop.svn.wordpress.org/trunk@35580 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
28ab6b9578
commit
2e2ea4876b
@ -572,6 +572,12 @@ class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting {
|
||||
}
|
||||
}
|
||||
|
||||
/** This filter is documented in wp-includes/nav-menu.php */
|
||||
$post->attr_title = apply_filters( 'nav_menu_attr_title', $post->attr_title );
|
||||
|
||||
/** This filter is documented in wp-includes/nav-menu.php */
|
||||
$post->description = apply_filters( 'nav_menu_description', wp_trim_words( $post->description, 200 ) );
|
||||
|
||||
return $post;
|
||||
}
|
||||
|
||||
@ -619,7 +625,7 @@ class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting {
|
||||
);
|
||||
$menu_item_value = array_merge( $default, $menu_item_value );
|
||||
$menu_item_value = wp_array_slice_assoc( $menu_item_value, array_keys( $default ) );
|
||||
$menu_item_value['position'] = max( 0, intval( $menu_item_value['position'] ) );
|
||||
$menu_item_value['position'] = intval( $menu_item_value['position'] );
|
||||
|
||||
foreach ( array( 'object_id', 'menu_item_parent', 'nav_menu_term_id' ) as $key ) {
|
||||
// Note we need to allow negative-integer IDs for previewed objects not inserted yet.
|
||||
@ -638,14 +644,16 @@ class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting {
|
||||
$menu_item_value[ $key ] = implode( ' ', array_map( 'sanitize_html_class', $value ) );
|
||||
}
|
||||
|
||||
foreach ( array( 'title', 'attr_title', 'description', 'original_title' ) as $key ) {
|
||||
// @todo Should esc_attr() the attr_title as well?
|
||||
$menu_item_value[ $key ] = sanitize_text_field( $menu_item_value[ $key ] );
|
||||
}
|
||||
$menu_item_value['original_title'] = sanitize_text_field( $menu_item_value['original_title'] );
|
||||
|
||||
// Apply the same filters as when calling wp_insert_post().
|
||||
$menu_item_value['title'] = apply_filters( 'title_save_pre', $menu_item_value['title'] );
|
||||
$menu_item_value['attr_title'] = apply_filters( 'excerpt_save_pre', $menu_item_value['attr_title'] );
|
||||
$menu_item_value['description'] = apply_filters( 'content_save_pre', $menu_item_value['description'] );
|
||||
|
||||
$menu_item_value['url'] = esc_url_raw( $menu_item_value['url'] );
|
||||
if ( ! get_post_status_object( $menu_item_value['status'] ) ) {
|
||||
$menu_item_value['status'] = 'publish';
|
||||
if ( 'publish' !== $menu_item_value['status'] ) {
|
||||
$menu_item_value['status'] = 'draft';
|
||||
}
|
||||
|
||||
$menu_item_value['_invalid'] = (bool) $menu_item_value['_invalid'];
|
||||
|
@ -438,6 +438,8 @@ class Test_WP_Customize_Nav_Menu_Item_Setting extends WP_UnitTestCase {
|
||||
*/
|
||||
function test_sanitize() {
|
||||
do_action( 'customize_register', $this->wp_customize );
|
||||
|
||||
$menu_id = wp_create_nav_menu( 'Primary' );
|
||||
$setting = new WP_Customize_Nav_Menu_Item_Setting( $this->wp_customize, 'nav_menu_item[123]' );
|
||||
|
||||
$this->assertNull( $setting->sanitize( 'not an array' ) );
|
||||
@ -449,37 +451,76 @@ class Test_WP_Customize_Nav_Menu_Item_Setting extends WP_UnitTestCase {
|
||||
'menu_item_parent' => 'asdasd',
|
||||
'position' => -123,
|
||||
'type' => 'custom<b>',
|
||||
'title' => 'Hi<script>alert(1)</script>',
|
||||
'title' => 'Hi<script>unfilteredHtml()</script>',
|
||||
'url' => 'javascript:alert(1)',
|
||||
'target' => '" onclick="',
|
||||
'attr_title' => '<b>evil</b>',
|
||||
'description' => '<b>Hello world</b>',
|
||||
'attr_title' => '<b>bolded</b><script>unfilteredHtml()</script>',
|
||||
'description' => '<b>Hello world</b><script>unfilteredHtml()</script>',
|
||||
'classes' => 'hello " inject="',
|
||||
'xfn' => 'hello " inject="',
|
||||
'status' => 'forbidden',
|
||||
'original_title' => 'Hi<script>alert(1)</script>',
|
||||
'original_title' => 'Hi<script>unfilteredHtml()</script>',
|
||||
'nav_menu_term_id' => 'heilo',
|
||||
'_invalid' => false,
|
||||
);
|
||||
|
||||
$expected_sanitized = array(
|
||||
'object_id' => 0,
|
||||
'object' => 'bhellob',
|
||||
'menu_item_parent' => 0,
|
||||
'position' => -123,
|
||||
'type' => 'customb',
|
||||
'title' => current_user_can( 'unfiltered_html' ) ? 'Hi<script>unfilteredHtml()</script>' : 'HiunfilteredHtml()',
|
||||
'url' => '',
|
||||
'target' => 'onclick',
|
||||
'attr_title' => current_user_can( 'unfiltered_html' ) ? '<b>bolded</b><script>unfilteredHtml()</script>' : '<b>bolded</b>unfilteredHtml()',
|
||||
'description' => current_user_can( 'unfiltered_html' ) ? '<b>Hello world</b><script>unfilteredHtml()</script>' : '<b>Hello world</b>unfilteredHtml()',
|
||||
'classes' => 'hello inject',
|
||||
'xfn' => 'hello inject',
|
||||
'status' => 'draft',
|
||||
'original_title' => 'Hi',
|
||||
'nav_menu_term_id' => 0,
|
||||
);
|
||||
|
||||
$sanitized = $setting->sanitize( $unsanitized );
|
||||
$this->assertEqualSets( array_keys( $unsanitized ), array_keys( $sanitized ) );
|
||||
|
||||
$this->assertEquals( 0, $sanitized['object_id'] );
|
||||
$this->assertEquals( 'bhellob', $sanitized['object'] );
|
||||
$this->assertEquals( 0, $sanitized['menu_item_parent'] );
|
||||
$this->assertEquals( 0, $sanitized['position'] );
|
||||
$this->assertEquals( 'customb', $sanitized['type'] );
|
||||
$this->assertEquals( 'Hi', $sanitized['title'] );
|
||||
$this->assertEquals( '', $sanitized['url'] );
|
||||
$this->assertEquals( 'onclick', $sanitized['target'] );
|
||||
$this->assertEquals( 'evil', $sanitized['attr_title'] );
|
||||
$this->assertEquals( 'Hello world', $sanitized['description'] );
|
||||
$this->assertEquals( 'hello inject', $sanitized['classes'] );
|
||||
$this->assertEquals( 'hello inject', $sanitized['xfn'] );
|
||||
$this->assertEquals( 'publish', $sanitized['status'] );
|
||||
$this->assertEquals( 'Hi', $sanitized['original_title'] );
|
||||
$this->assertEquals( 0, $sanitized['nav_menu_term_id'] );
|
||||
foreach ( $expected_sanitized as $key => $value ) {
|
||||
$this->assertEquals( $value, $sanitized[ $key ], "Expected $key to be sanitized." );
|
||||
}
|
||||
|
||||
$nav_menu_item_id = wp_update_nav_menu_item( $menu_id, 0, array(
|
||||
'menu-item-object-id' => $unsanitized['object_id'],
|
||||
'menu-item-object' => $unsanitized['object'],
|
||||
'menu-item-parent-id' => $unsanitized['menu_item_parent'],
|
||||
'menu-item-position' => $unsanitized['position'],
|
||||
'menu-item-type' => $unsanitized['type'],
|
||||
'menu-item-title' => $unsanitized['title'],
|
||||
'menu-item-url' => $unsanitized['url'],
|
||||
'menu-item-description' => $unsanitized['description'],
|
||||
'menu-item-attr-title' => $unsanitized['attr_title'],
|
||||
'menu-item-target' => $unsanitized['target'],
|
||||
'menu-item-classes' => $unsanitized['classes'],
|
||||
'menu-item-xfn' => $unsanitized['xfn'],
|
||||
'menu-item-status' => $unsanitized['status'],
|
||||
) );
|
||||
|
||||
$post = get_post( $nav_menu_item_id );
|
||||
$nav_menu_item = wp_setup_nav_menu_item( clone $post );
|
||||
|
||||
$this->assertEquals( $expected_sanitized['object_id'], $nav_menu_item->object_id );
|
||||
$this->assertEquals( $expected_sanitized['object'], $nav_menu_item->object );
|
||||
$this->assertEquals( $expected_sanitized['menu_item_parent'], $nav_menu_item->menu_item_parent );
|
||||
$this->assertEquals( $expected_sanitized['position'], $post->menu_order );
|
||||
$this->assertEquals( $expected_sanitized['type'], $nav_menu_item->type );
|
||||
$this->assertEquals( $expected_sanitized['title'], $post->post_title );
|
||||
$this->assertEquals( $expected_sanitized['url'], $nav_menu_item->url );
|
||||
$this->assertEquals( $expected_sanitized['description'], $post->post_content );
|
||||
$this->assertEquals( $expected_sanitized['attr_title'], $post->post_excerpt );
|
||||
$this->assertEquals( $expected_sanitized['target'], $nav_menu_item->target );
|
||||
$this->assertEquals( $expected_sanitized['classes'], implode( ' ', $nav_menu_item->classes ) );
|
||||
$this->assertEquals( $expected_sanitized['xfn'], $nav_menu_item->xfn );
|
||||
$this->assertEquals( $expected_sanitized['status'], $post->post_status );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -709,4 +750,53 @@ class Test_WP_Customize_Nav_Menu_Item_Setting extends WP_UnitTestCase {
|
||||
$this->assertTrue( $value_object->_invalid );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test WP_Customize_Nav_Menu_Item_Setting::value_as_wp_post_nav_menu_item().
|
||||
*
|
||||
* @see WP_Customize_Nav_Menu_Item_Setting::value_as_wp_post_nav_menu_item()
|
||||
*/
|
||||
function test_value_as_wp_post_nav_menu_item() {
|
||||
$post_id = self::factory()->post->create();
|
||||
|
||||
$setting = new WP_Customize_Nav_Menu_Item_Setting(
|
||||
$this->wp_customize,
|
||||
'nav_menu_item[123]'
|
||||
);
|
||||
$post_value = array(
|
||||
'object_id' => $post_id,
|
||||
'object' => 'post',
|
||||
'menu_item_parent' => 0,
|
||||
'position' => 2,
|
||||
'type' => 'post_type',
|
||||
'title' => 'Hello World',
|
||||
'url' => '',
|
||||
'target' => '',
|
||||
'attr_title' => '">attempted <b>baddie</b>',
|
||||
'description' => 'Attempted <b>markup</b>',
|
||||
'classes' => '',
|
||||
'xfn' => '',
|
||||
'status' => 'publish',
|
||||
'original_title' => '',
|
||||
'nav_menu_term_id' => 0,
|
||||
'_invalid' => false,
|
||||
);
|
||||
$this->wp_customize->set_post_value( $setting->id, $post_value );
|
||||
|
||||
$setting->preview();
|
||||
$nav_menu_item = $setting->value_as_wp_post_nav_menu_item();
|
||||
|
||||
$this->assertObjectNotHasAttribute( 'nav_menu_term_id', $nav_menu_item );
|
||||
$this->assertObjectNotHasAttribute( 'status', $nav_menu_item );
|
||||
$this->assertEquals( 'publish', $nav_menu_item->post_status );
|
||||
$this->assertEquals( 'nav_menu_item', $nav_menu_item->post_type );
|
||||
$this->assertObjectNotHasAttribute( 'position', $nav_menu_item );
|
||||
$this->assertEquals( $post_value['position'], $nav_menu_item->menu_order );
|
||||
$this->assertEquals( $post_value['title'], $nav_menu_item->post_title );
|
||||
$this->assertEquals( 123, $nav_menu_item->ID );
|
||||
$this->assertEquals( 123, $nav_menu_item->db_id );
|
||||
$this->assertEquals( wp_get_current_user()->ID, $nav_menu_item->post_author );
|
||||
$this->assertObjectHasAttribute( 'type_label', $nav_menu_item );
|
||||
$this->assertEquals( '“>attempted baddie', $nav_menu_item->attr_title );
|
||||
$this->assertEquals( 'Attempted markup', $nav_menu_item->description );
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user