Make WordPress Core

Changeset 35580


Ignore:
Timestamp:
11/09/2015 12:47:55 AM (9 years ago)
Author:
westonruter
Message:

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.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php

    r35500 r35580  
    573573        }
    574574
     575        /** This filter is documented in wp-includes/nav-menu.php */
     576        $post->attr_title = apply_filters( 'nav_menu_attr_title', $post->attr_title );
     577
     578        /** This filter is documented in wp-includes/nav-menu.php */
     579        $post->description = apply_filters( 'nav_menu_description', wp_trim_words( $post->description, 200 ) );
     580
    575581        return $post;
    576582    }
     
    620626        $menu_item_value = array_merge( $default, $menu_item_value );
    621627        $menu_item_value = wp_array_slice_assoc( $menu_item_value, array_keys( $default ) );
    622         $menu_item_value['position'] = max( 0, intval( $menu_item_value['position'] ) );
     628        $menu_item_value['position'] = intval( $menu_item_value['position'] );
    623629
    624630        foreach ( array( 'object_id', 'menu_item_parent', 'nav_menu_term_id' ) as $key ) {
     
    639645        }
    640646
    641         foreach ( array( 'title', 'attr_title', 'description', 'original_title' ) as $key ) {
    642             // @todo Should esc_attr() the attr_title as well?
    643             $menu_item_value[ $key ] = sanitize_text_field( $menu_item_value[ $key ] );
    644         }
     647        $menu_item_value['original_title'] = sanitize_text_field( $menu_item_value['original_title'] );
     648
     649        // Apply the same filters as when calling wp_insert_post().
     650        $menu_item_value['title'] = apply_filters( 'title_save_pre', $menu_item_value['title'] );
     651        $menu_item_value['attr_title'] = apply_filters( 'excerpt_save_pre', $menu_item_value['attr_title'] );
     652        $menu_item_value['description'] = apply_filters( 'content_save_pre', $menu_item_value['description'] );
    645653
    646654        $menu_item_value['url'] = esc_url_raw( $menu_item_value['url'] );
    647         if ( ! get_post_status_object( $menu_item_value['status'] ) ) {
    648             $menu_item_value['status'] = 'publish';
     655        if ( 'publish' !== $menu_item_value['status'] ) {
     656            $menu_item_value['status'] = 'draft';
    649657        }
    650658
  • trunk/tests/phpunit/tests/customize/nav-menu-item-setting.php

    r35302 r35580  
    439439    function test_sanitize() {
    440440        do_action( 'customize_register', $this->wp_customize );
     441
     442        $menu_id = wp_create_nav_menu( 'Primary' );
    441443        $setting = new WP_Customize_Nav_Menu_Item_Setting( $this->wp_customize, 'nav_menu_item[123]' );
    442444
     
    450452            'position' => -123,
    451453            'type' => 'custom<b>',
    452             'title' => 'Hi<script>alert(1)</script>',
     454            'title' => 'Hi<script>unfilteredHtml()</script>',
    453455            'url' => 'javascript:alert(1)',
    454456            'target' => '" onclick="',
    455             'attr_title' => '<b>evil</b>',
    456             'description' => '<b>Hello world</b>',
     457            'attr_title' => '<b>bolded</b><script>unfilteredHtml()</script>',
     458            'description' => '<b>Hello world</b><script>unfilteredHtml()</script>',
    457459            'classes' => 'hello " inject="',
    458460            'xfn' => 'hello " inject="',
    459461            'status' => 'forbidden',
    460             'original_title' => 'Hi<script>alert(1)</script>',
     462            'original_title' => 'Hi<script>unfilteredHtml()</script>',
    461463            'nav_menu_term_id' => 'heilo',
    462464            '_invalid' => false,
    463465        );
    464466
     467        $expected_sanitized = array(
     468            'object_id' => 0,
     469            'object' => 'bhellob',
     470            'menu_item_parent' => 0,
     471            'position' => -123,
     472            'type' => 'customb',
     473            'title' => current_user_can( 'unfiltered_html' ) ? 'Hi<script>unfilteredHtml()</script>' : 'HiunfilteredHtml()',
     474            'url' => '',
     475            'target' => 'onclick',
     476            'attr_title' => current_user_can( 'unfiltered_html' ) ? '<b>bolded</b><script>unfilteredHtml()</script>' : '<b>bolded</b>unfilteredHtml()',
     477            'description' => current_user_can( 'unfiltered_html' ) ? '<b>Hello world</b><script>unfilteredHtml()</script>' : '<b>Hello world</b>unfilteredHtml()',
     478            'classes' => 'hello  inject',
     479            'xfn' => 'hello  inject',
     480            'status' => 'draft',
     481            'original_title' => 'Hi',
     482            'nav_menu_term_id' => 0,
     483        );
     484
    465485        $sanitized = $setting->sanitize( $unsanitized );
    466486        $this->assertEqualSets( array_keys( $unsanitized ), array_keys( $sanitized ) );
    467487
    468         $this->assertEquals( 0, $sanitized['object_id'] );
    469         $this->assertEquals( 'bhellob', $sanitized['object'] );
    470         $this->assertEquals( 0, $sanitized['menu_item_parent'] );
    471         $this->assertEquals( 0, $sanitized['position'] );
    472         $this->assertEquals( 'customb', $sanitized['type'] );
    473         $this->assertEquals( 'Hi', $sanitized['title'] );
    474         $this->assertEquals( '', $sanitized['url'] );
    475         $this->assertEquals( 'onclick', $sanitized['target'] );
    476         $this->assertEquals( 'evil', $sanitized['attr_title'] );
    477         $this->assertEquals( 'Hello world', $sanitized['description'] );
    478         $this->assertEquals( 'hello  inject', $sanitized['classes'] );
    479         $this->assertEquals( 'hello  inject', $sanitized['xfn'] );
    480         $this->assertEquals( 'publish', $sanitized['status'] );
    481         $this->assertEquals( 'Hi', $sanitized['original_title'] );
    482         $this->assertEquals( 0, $sanitized['nav_menu_term_id'] );
     488        foreach ( $expected_sanitized as $key => $value ) {
     489            $this->assertEquals( $value, $sanitized[ $key ], "Expected $key to be sanitized." );
     490        }
     491
     492        $nav_menu_item_id = wp_update_nav_menu_item( $menu_id, 0, array(
     493            'menu-item-object-id' => $unsanitized['object_id'],
     494            'menu-item-object' => $unsanitized['object'],
     495            'menu-item-parent-id' => $unsanitized['menu_item_parent'],
     496            'menu-item-position' => $unsanitized['position'],
     497            'menu-item-type' => $unsanitized['type'],
     498            'menu-item-title' => $unsanitized['title'],
     499            'menu-item-url' => $unsanitized['url'],
     500            'menu-item-description' => $unsanitized['description'],
     501            'menu-item-attr-title' => $unsanitized['attr_title'],
     502            'menu-item-target' => $unsanitized['target'],
     503            'menu-item-classes' => $unsanitized['classes'],
     504            'menu-item-xfn' => $unsanitized['xfn'],
     505            'menu-item-status' => $unsanitized['status'],
     506        ) );
     507
     508        $post = get_post( $nav_menu_item_id );
     509        $nav_menu_item = wp_setup_nav_menu_item( clone $post );
     510
     511        $this->assertEquals( $expected_sanitized['object_id'], $nav_menu_item->object_id );
     512        $this->assertEquals( $expected_sanitized['object'], $nav_menu_item->object );
     513        $this->assertEquals( $expected_sanitized['menu_item_parent'], $nav_menu_item->menu_item_parent );
     514        $this->assertEquals( $expected_sanitized['position'], $post->menu_order );
     515        $this->assertEquals( $expected_sanitized['type'], $nav_menu_item->type );
     516        $this->assertEquals( $expected_sanitized['title'], $post->post_title );
     517        $this->assertEquals( $expected_sanitized['url'], $nav_menu_item->url );
     518        $this->assertEquals( $expected_sanitized['description'], $post->post_content );
     519        $this->assertEquals( $expected_sanitized['attr_title'], $post->post_excerpt );
     520        $this->assertEquals( $expected_sanitized['target'], $nav_menu_item->target );
     521        $this->assertEquals( $expected_sanitized['classes'], implode( ' ', $nav_menu_item->classes ) );
     522        $this->assertEquals( $expected_sanitized['xfn'], $nav_menu_item->xfn );
     523        $this->assertEquals( $expected_sanitized['status'], $post->post_status );
    483524    }
    484525
     
    710751    }
    711752
     753    /**
     754     * Test WP_Customize_Nav_Menu_Item_Setting::value_as_wp_post_nav_menu_item().
     755     *
     756     * @see WP_Customize_Nav_Menu_Item_Setting::value_as_wp_post_nav_menu_item()
     757     */
     758    function test_value_as_wp_post_nav_menu_item() {
     759        $post_id = self::factory()->post->create();
     760
     761        $setting = new WP_Customize_Nav_Menu_Item_Setting(
     762            $this->wp_customize,
     763            'nav_menu_item[123]'
     764        );
     765        $post_value = array(
     766            'object_id'        => $post_id,
     767            'object'           => 'post',
     768            'menu_item_parent' => 0,
     769            'position'         => 2,
     770            'type'             => 'post_type',
     771            'title'            => 'Hello World',
     772            'url'              => '',
     773            'target'           => '',
     774            'attr_title'       => '">attempted <b>baddie</b>',
     775            'description'      => 'Attempted <b>markup</b>',
     776            'classes'          => '',
     777            'xfn'              => '',
     778            'status'           => 'publish',
     779            'original_title'   => '',
     780            'nav_menu_term_id' => 0,
     781            '_invalid'         => false,
     782        );
     783        $this->wp_customize->set_post_value( $setting->id, $post_value );
     784
     785        $setting->preview();
     786        $nav_menu_item = $setting->value_as_wp_post_nav_menu_item();
     787
     788        $this->assertObjectNotHasAttribute( 'nav_menu_term_id', $nav_menu_item );
     789        $this->assertObjectNotHasAttribute( 'status', $nav_menu_item );
     790        $this->assertEquals( 'publish', $nav_menu_item->post_status );
     791        $this->assertEquals( 'nav_menu_item', $nav_menu_item->post_type );
     792        $this->assertObjectNotHasAttribute( 'position', $nav_menu_item );
     793        $this->assertEquals( $post_value['position'], $nav_menu_item->menu_order );
     794        $this->assertEquals( $post_value['title'], $nav_menu_item->post_title );
     795        $this->assertEquals( 123, $nav_menu_item->ID );
     796        $this->assertEquals( 123, $nav_menu_item->db_id );
     797        $this->assertEquals( wp_get_current_user()->ID, $nav_menu_item->post_author );
     798        $this->assertObjectHasAttribute( 'type_label', $nav_menu_item );
     799        $this->assertEquals( '&#8220;>attempted baddie', $nav_menu_item->attr_title );
     800        $this->assertEquals( 'Attempted markup', $nav_menu_item->description );
     801    }
    712802}
Note: See TracChangeset for help on using the changeset viewer.