Make WordPress Core


Ignore:
Timestamp:
07/14/2017 05:27:33 PM (7 years 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.

Merges [41050] to 4.8 branch.
Props westonruter, melchoyce, gitlost for testing, obenland for testing, dougal for testing, afercia for testing.
See #35243.
Fixes #40951 for 4.8.1.

Location:
branches/4.8
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/4.8

  • branches/4.8/tests/phpunit/tests/widgets/text-widget.php

    r40673 r41053  
    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.