WordPress.org

Make WordPress Core


Ignore:
Timestamp:
07/14/17 17:08:20 (9 days ago)
Author:
westonruter
Message:

Widgets: Add legacy mode for Text widget and add usage pointers to default visual mode.

The Text widget in legacy mode omits TinyMCE and retains old behavior for matching pre-existing Text widgets. Usage pointers added to default visual mode appear when attempting to paste HTML code into the Visual tab and when clicking on the Text tab, informing users of the new Custom HTML widget.

Props westonruter, melchoyce, gitlost for testing, obenland for testing, dougal for testing, afercia for testing.
See #35243.
Fixes #40951.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/widgets/text-widget.php

    r40673 r41050  
    3838        $wp_scripts = null; 
    3939        $wp_styles = null; 
     40    } 
     41 
     42    /** 
     43     * Test constructor method. 
     44     * 
     45     * @covers WP_Widget_Text::__construct 
     46     */ 
     47    function test_construct() { 
     48        $widget = new WP_Widget_Text(); 
     49        $this->assertEquals( 'text', $widget->id_base ); 
     50        $this->assertEquals( 'widget_text', $widget->widget_options['classname'] ); 
     51        $this->assertTrue( $widget->widget_options['customize_selective_refresh'] ); 
     52        $this->assertEquals( 400, $widget->control_options['width'] ); 
     53        $this->assertEquals( 350, $widget->control_options['height'] ); 
    4054    } 
    4155 
     
    123137 
    124138    /** 
     139     * Example shortcode content to test for wpautop corruption. 
     140     * 
     141     * @var string 
     142     */ 
     143    protected $example_shortcode_content = "<p>One\nTwo\n\nThree</p>\n<script>\ndocument.write('Test1');\n\ndocument.write('Test2');\n</script>"; 
     144 
     145    /** 
     146     * Do example shortcode. 
     147     * 
     148     * @return string Shortcode content. 
     149     */ 
     150    function do_example_shortcode() { 
     151        return $this->example_shortcode_content; 
     152    } 
     153 
     154    /** 
     155     * Test widget method when a plugin has added shortcode support. 
     156     * 
     157     * @covers WP_Widget_Text::widget 
     158     */ 
     159    function test_widget_shortcodes() { 
     160        $args = array( 
     161            'before_title'  => '<h2>', 
     162            'after_title'   => "</h2>\n", 
     163            'before_widget' => '<section>', 
     164            'after_widget'  => "</section>\n", 
     165        ); 
     166        $widget = new WP_Widget_Text(); 
     167        add_filter( 'widget_text', 'do_shortcode' ); 
     168        add_shortcode( 'example', array( $this, 'do_example_shortcode' ) ); 
     169 
     170        $base_instance = array( 
     171            'title' => 'Example', 
     172            'text' => "This is an example:\n\n[example]", 
     173            'filter' => false, 
     174        ); 
     175 
     176        // Legacy Text Widget. 
     177        $instance = array_merge( $base_instance, array( 
     178            'filter' => false, 
     179        ) ); 
     180        ob_start(); 
     181        $widget->widget( $args, $instance ); 
     182        $output = ob_get_clean(); 
     183        $this->assertContains( $this->example_shortcode_content, $output, 'Shortcode was applied without wpautop corrupting it.' ); 
     184        $this->assertEquals( 10, has_filter( 'widget_text', 'do_shortcode' ), 'Filter was restored.' ); 
     185 
     186        // Visual Text Widget. 
     187        $instance = array_merge( $base_instance, array( 
     188            'filter' => 'content', 
     189        ) ); 
     190        ob_start(); 
     191        $widget->widget( $args, $instance ); 
     192        $output = ob_get_clean(); 
     193        $this->assertContains( $this->example_shortcode_content, $output, 'Shortcode was applied without wpautop corrupting it.' ); 
     194        $this->assertEquals( 10, has_filter( 'widget_text', 'do_shortcode' ), 'Filter was restored.' ); 
     195        $this->assertFalse( has_filter( 'widget_text_content', 'do_shortcode' ), 'Filter was removed.' ); 
     196 
     197        // Visual Text Widget with properly-used widget_text_content filter. 
     198        remove_filter( 'widget_text', 'do_shortcode' ); 
     199        add_filter( 'widget_text_content', 'do_shortcode', 11 ); 
     200        $instance = array_merge( $base_instance, array( 
     201            'filter' => 'content', 
     202        ) ); 
     203        ob_start(); 
     204        $widget->widget( $args, $instance ); 
     205        $output = ob_get_clean(); 
     206        $this->assertContains( $this->example_shortcode_content, $output, 'Shortcode was applied without wpautop corrupting it.' ); 
     207        $this->assertFalse( has_filter( 'widget_text', 'do_shortcode' ), 'Filter was not erroneously restored.' ); 
     208    } 
     209 
     210    /** 
    125211     * Filters the content of the Text widget. 
    126212     * 
     
    153239 
    154240    /** 
     241     * Test is_legacy_instance method. 
     242     * 
     243     * @covers WP_Widget_Text::is_legacy_instance 
     244     */ 
     245    function test_is_legacy_instance() { 
     246        $widget = new WP_Widget_Text(); 
     247        $base_instance = array( 
     248            'title' => 'Title', 
     249            'text' => "Hello\n\nWorld", 
     250        ); 
     251 
     252        $instance = array_merge( $base_instance, array( 
     253            'legacy' => true, 
     254        ) ); 
     255        $this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when legacy prop is present.' ); 
     256 
     257        $instance = array_merge( $base_instance, array( 
     258            'filter' => 'content', 
     259        ) ); 
     260        $this->assertFalse( $widget->is_legacy_instance( $instance ), 'Not legacy when filter is explicitly content.' ); 
     261 
     262        $instance = array_merge( $base_instance, array( 
     263            'text' => '', 
     264            'filter' => true, 
     265        ) ); 
     266        $this->assertFalse( $widget->is_legacy_instance( $instance ), 'Not legacy when text is empty.' ); 
     267 
     268        $instance = array_merge( $base_instance, array( 
     269            'text' => "One\nTwo", 
     270            'filter' => false, 
     271        ) ); 
     272        $this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when not-wpautop and there are line breaks.' ); 
     273 
     274        $instance = array_merge( $base_instance, array( 
     275            'text' => "One\n\nTwo", 
     276            'filter' => false, 
     277        ) ); 
     278        $this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when not-wpautop and there are paragraph breaks.' ); 
     279 
     280        $instance = array_merge( $base_instance, array( 
     281            'text' => "One\nTwo", 
     282            'filter' => true, 
     283        ) ); 
     284        $this->assertFalse( $widget->is_legacy_instance( $instance ), 'Not automatically legacy when wpautop and there are line breaks.' ); 
     285 
     286        $instance = array_merge( $base_instance, array( 
     287            'text' => "One\n\nTwo", 
     288            'filter' => true, 
     289        ) ); 
     290        $this->assertFalse( $widget->is_legacy_instance( $instance ), 'Not automatically legacy when wpautop and there are paragraph breaks.' ); 
     291 
     292        $instance = array_merge( $base_instance, array( 
     293            'text' => 'Test<!-- comment -->', 
     294            'filter' => true, 
     295        ) ); 
     296        $this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when HTML comment is present.' ); 
     297 
     298        $instance = array_merge( $base_instance, array( 
     299            'text' => 'Here is a [gallery]', 
     300            'filter' => true, 
     301        ) ); 
     302        $this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy mode when a shortcode is present.' ); 
     303 
     304        // Check text examples that will not migrate to TinyMCE. 
     305        $legacy_text_examples = array( 
     306            '<span class="hello"></span>', 
     307            '<span></span>', 
     308            "<ul>\n<li><a href=\"#\" class=\"location\"></a>List Item 1</li>\n<li><a href=\"#\" class=\"location\"></a>List Item 2</li>\n</ul>", 
     309            '<a href="#" class="map"></a>', 
     310            "<script>\n\\Line one\n\n\\Line two</script>", 
     311            "<style>body {\ncolor:red;\n}</style>", 
     312            '<span class="fa fa-cc-discover fa-2x" aria-hidden="true"></span>', 
     313            "<p>\nStay updated with our latest news and specials. We never sell your information and you can unsubscribe at any time.\n</p>\n\n<div class=\"custom-form-class\">\n\t<form action=\"#\" method=\"post\" name=\"mc-embedded-subscribe-form\">\n\n\t\t<label class=\"screen-reader-text\" for=\"mce-EMAIL-b\">Email </label>\n\t\t<input id=\"mce-EMAIL-b\" class=\"required email\" name=\"EMAIL\" required=\"\" type=\"email\" value=\"\" placeholder=\"Email Address*\" />\n\n\t\t<input class=\"button\" name=\"subscribe\" type=\"submit\" value=\"Go!\" />\n\n\t</form>\n</div>", 
     314            '<span class="sectiondown"><a href="#front-page-3"><i class="fa fa-chevron-circle-down"></i></a></span>', 
     315        ); 
     316        foreach ( $legacy_text_examples as $legacy_text_example ) { 
     317            $instance = array_merge( $base_instance, array( 
     318                'text' => $legacy_text_example, 
     319                'filter' => true, 
     320            ) ); 
     321            $this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when wpautop and there is HTML that is not liable to be mutated.' ); 
     322 
     323            $instance = array_merge( $base_instance, array( 
     324                'text' => $legacy_text_example, 
     325                'filter' => false, 
     326            ) ); 
     327            $this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when not-wpautop and there is HTML that is not liable to be mutated.' ); 
     328        } 
     329 
     330        // Check text examples that will migrate to TinyMCE, where elements and attributes are not in whitelist. 
     331        $migratable_text_examples = array( 
     332            'Check out <a href="http://example.com">Example</a>', 
     333            '<img src="http://example.com/img.jpg" alt="Img">', 
     334            '<strong><em>Hello</em></strong>', 
     335            '<b><i><u><s>Hello</s></u></i></b>', 
     336            "<ul>\n<li>One</li>\n<li>One</li>\n<li>One</li>\n</ul>", 
     337            "<ol>\n<li>One</li>\n<li>One</li>\n<li>One</li>\n</ol>", 
     338            "Text\n<hr>\nAddendum", 
     339            "Look at this code:\n\n<code>echo 'Hello World!';</code>", 
     340        ); 
     341        foreach ( $migratable_text_examples as $migratable_text_example ) { 
     342            $instance = array_merge( $base_instance, array( 
     343                'text' => $migratable_text_example, 
     344                'filter' => true, 
     345            ) ); 
     346            $this->assertFalse( $widget->is_legacy_instance( $instance ), 'Legacy when wpautop and there is HTML that is not liable to be mutated.' ); 
     347        } 
     348    } 
     349 
     350    /** 
     351     * Test update method. 
     352     * 
     353     * @covers WP_Widget_Text::form 
     354     */ 
     355    function test_form() { 
     356        $widget = new WP_Widget_Text(); 
     357        $instance = array( 
     358            'title' => 'Title', 
     359            'text' => 'Text', 
     360            'filter' => false, 
     361            'legacy' => true, 
     362        ); 
     363        $this->assertTrue( $widget->is_legacy_instance( $instance ) ); 
     364        ob_start(); 
     365        $widget->form( $instance ); 
     366        $form = ob_get_clean(); 
     367        $this->assertContains( 'class="legacy"', $form ); 
     368 
     369        $instance = array( 
     370            'title' => 'Title', 
     371            'text' => 'Text', 
     372            'filter' => 'content', 
     373        ); 
     374        $this->assertFalse( $widget->is_legacy_instance( $instance ) ); 
     375        ob_start(); 
     376        $widget->form( $instance ); 
     377        $form = ob_get_clean(); 
     378        $this->assertNotContains( 'class="legacy"', $form ); 
     379    } 
     380 
     381    /** 
    155382     * Test update method. 
    156383     * 
     
    162389            'title' => "The\nTitle", 
    163390            'text'  => "The\n\nText", 
    164             'filter' => false, 
     391            'filter' => 'content', 
    165392        ); 
    166393 
     
    169396        ) ) ); 
    170397 
    171         // Should return valid instance. 
     398        // Should return valid instance in legacy mode since filter=false and there are line breaks. 
    172399        $expected = array( 
    173400            'title'  => sanitize_text_field( $instance['title'] ), 
     
    176403        ); 
    177404        $result = $widget->update( $instance, array() ); 
    178         $this->assertEquals( $result, $expected ); 
     405        $this->assertEquals( $expected, $result ); 
    179406        $this->assertTrue( ! empty( $expected['filter'] ), 'Expected filter prop to be truthy, to handle case where 4.8 is downgraded to 4.7.' ); 
    180407 
     
    185412        $expected['text'] = $instance['text']; 
    186413        $result = $widget->update( $instance, array() ); 
    187         $this->assertEquals( $result, $expected ); 
     414        $this->assertEquals( $expected, $result ); 
    188415        remove_filter( 'map_meta_cap', array( $this, 'grant_unfiltered_html_cap' ) ); 
    189416 
     
    193420        $expected['text'] = wp_kses_post( $instance['text'] ); 
    194421        $result = $widget->update( $instance, array() ); 
    195         $this->assertEquals( $result, $expected ); 
     422        $this->assertEquals( $expected, $result ); 
    196423        remove_filter( 'map_meta_cap', array( $this, 'revoke_unfiltered_html_cap' ), 10 ); 
     424    } 
     425 
     426    /** 
     427     * Test update for legacy widgets. 
     428     * 
     429     * @covers WP_Widget_Text::update 
     430     */ 
     431    function test_update_legacy() { 
     432        $widget = new WP_Widget_Text(); 
     433 
     434        // Updating a widget with explicit filter=true persists with legacy mode. 
     435        $instance = array( 
     436            'title' => 'Legacy', 
     437            'text' => 'Text', 
     438            'filter' => true, 
     439        ); 
     440        $result = $widget->update( $instance, array() ); 
     441        $expected = array_merge( $instance, array( 
     442            'legacy' => true, 
     443            'filter' => true, 
     444        ) ); 
     445        $this->assertEquals( $expected, $result ); 
     446 
     447        // Updating a widget with explicit filter=false persists with legacy mode. 
     448        $instance['filter'] = false; 
     449        $result = $widget->update( $instance, array() ); 
     450        $expected = array_merge( $instance, array( 
     451            'legacy' => true, 
     452            'filter' => false, 
     453        ) ); 
     454        $this->assertEquals( $expected, $result ); 
     455 
     456        // Updating a widget in legacy form results in filter=false when checkbox not checked. 
     457        $instance['filter'] = true; 
     458        $result = $widget->update( $instance, array() ); 
     459        $expected = array_merge( $instance, array( 
     460            'legacy' => true, 
     461            'filter' => true, 
     462        ) ); 
     463        $this->assertEquals( $expected, $result ); 
     464 
     465        // Updating a widget that previously had legacy form results in filter persisting. 
     466        unset( $instance['legacy'] ); 
     467        $instance['filter'] = true; 
     468        $result = $widget->update( $instance, array( 
     469            'legacy' => true, 
     470        ) ); 
     471        $expected = array_merge( $instance, array( 
     472            'legacy' => true, 
     473            'filter' => true, 
     474        ) ); 
     475        $this->assertEquals( $expected, $result ); 
    197476    } 
    198477 
Note: See TracChangeset for help on using the changeset viewer.