Ticket #40951: 40951.5.diff
File 40951.5.diff, 26.0 KB (added by , 8 years ago) |
---|
-
src/wp-admin/js/widgets/text-widgets.js
diff --git src/wp-admin/js/widgets/text-widgets.js src/wp-admin/js/widgets/text-widgets.js index 7932606e3d..5af65f134c 100644
3 3 wp.textWidgets = ( function( $ ) { 4 4 'use strict'; 5 5 6 var component = {}; 6 var component = { 7 data: { 8 custom_html_pointer_dismissed: false, 9 l10n: { 10 pointer_heading: '', 11 pointer_text: '' 12 } 13 } 14 }; 7 15 8 16 /** 9 17 * Text widget control. … … wp.textWidgets = ( function( $ ) { 152 160 if ( restoreTextMode ) { 153 161 switchEditors.go( id, 'toggle' ); 154 162 } 163 164 $( '#' + id + '-html' ).one( 'click', function() { 165 var tabContainer; 166 if ( component.data.custom_html_pointer_dismissed ) { 167 return; 168 } 169 tabContainer = $( this ).parent(); 170 tabContainer.pointer({ 171 position: 'bottom', 172 align: 'right', 173 edge: 'right', 174 content: '<h3>' + component.data.l10n.pointer_heading + '</h3><p>' + component.data.l10n.pointer_text + '</p>', 175 close: function() { 176 wp.ajax.post( 'dismiss-wp-pointer', { 177 pointer: 'text_widget_custom_html' 178 }); 179 component.data.custom_html_pointer_dismissed = true; 180 } 181 }); 182 tabContainer.pointer( 'open' ); 183 tabContainer.pointer( 'widget' ).css( 'z-index', 999999 ); // Default z-index of 9999 is not enough in Customizer. 184 }); 155 185 }; 156 186 157 187 if ( editor.initialized ) { … … wp.textWidgets = ( function( $ ) { 233 263 return; 234 264 } 235 265 266 // Bypass using TinyMCE when widget is in legacy mode. 267 if ( widgetForm.find( '.legacy' ).length > 0 ) { 268 return; 269 } 270 236 271 /* 237 272 * Create a container element for the widget control fields. 238 273 * This is inserted into the DOM immediately before the the .widget-content … … wp.textWidgets = ( function( $ ) { 337 372 * When WordPress enqueues this script, it should have an inline script 338 373 * attached which calls wp.textWidgets.init(). 339 374 * 375 * @param {object} exports Server exports. 376 * @param {object} exports.l10n Translations. 340 377 * @returns {void} 341 378 */ 342 component.init = function init( ) {379 component.init = function init( exports ) { 343 380 var $document = $( document ); 381 component.data = exports; 344 382 $document.on( 'widget-added', component.handleWidgetAdded ); 345 383 $document.on( 'widget-synced widget-updated', component.handleWidgetUpdated ); 346 384 -
src/wp-includes/script-loader.php
diff --git src/wp-includes/script-loader.php src/wp-includes/script-loader.php index 7562e2839b..05b0fe0c84 100644
function wp_default_scripts( &$scripts ) { 608 608 $scripts->add( 'media-audio-widget', "/wp-admin/js/widgets/media-audio-widget$suffix.js", array( 'media-widgets', 'media-audiovideo' ) ); 609 609 $scripts->add( 'media-image-widget', "/wp-admin/js/widgets/media-image-widget$suffix.js", array( 'media-widgets' ) ); 610 610 $scripts->add( 'media-video-widget', "/wp-admin/js/widgets/media-video-widget$suffix.js", array( 'media-widgets', 'media-audiovideo' ) ); 611 $scripts->add( 'text-widgets', "/wp-admin/js/widgets/text-widgets$suffix.js", array( 'jquery', 'backbone', 'editor', 'wp-util' ) ); 612 $scripts->add_inline_script( 'text-widgets', 'wp.textWidgets.init();', 'after' ); 611 $scripts->add( 'text-widgets', "/wp-admin/js/widgets/text-widgets$suffix.js", array( 'jquery', 'backbone', 'editor', 'wp-util', 'wp-pointer' ) ); 612 $exports = array( 613 'custom_html_pointer_dismissed' => is_user_logged_in() && in_array( 'text_widget_custom_html', explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) ), 614 'l10n' => array( 615 'pointer_heading' => __( 'New Custom HTML Widget' ), 616 'pointer_text' => __( 'Hey, did you hear we have a "Custom HTML" widget now? Check it out to add some custom code to your site!' ), 617 ), 618 ); 619 $scripts->add_inline_script( 'text-widgets', sprintf( 'wp.textWidgets.init( %s );', wp_json_encode( $exports ) ), 'after' ); 613 620 614 621 $scripts->add( 'theme', "/wp-admin/js/theme$suffix.js", array( 'wp-backbone', 'wp-a11y' ), false, 1 ); 615 622 -
src/wp-includes/widgets/class-wp-widget-text.php
diff --git src/wp-includes/widgets/class-wp-widget-text.php src/wp-includes/widgets/class-wp-widget-text.php index 7d149c0a0f..dd88bb4105 100644
class WP_Widget_Text extends WP_Widget { 53 53 } 54 54 55 55 /** 56 * Determines whether a given instance is legacy and should bypass using TinyMCE. 57 * 58 * @since 4.8.1 59 * 60 * @param array $instance { 61 * Instance data. 62 * 63 * @type string $text Content. 64 * @type bool|string $filter Whether autop or content filters should apply. 65 * @type bool $legacy Whether widget is in legacy mode. 66 * } 67 * @return bool Whether Text widget instance contains legacy data. 68 */ 69 public function is_legacy_instance( $instance ) { 70 71 // If the widget has been updated while in legacy mode, it stays in legacy mode. 72 if ( ! empty( $instance['legacy'] ) ) { 73 return true; 74 } 75 76 // If the widget has been added/updated in 4.8 then filter prop is 'content' and it is no longer legacy. 77 if ( isset( $instance['filter'] ) && 'content' === $instance['filter'] ) { 78 return false; 79 } 80 81 // If the text is empty, then nothing is preventing migration to TinyMCE. 82 if ( empty( $instance['text'] ) ) { 83 return false; 84 } 85 86 $wpautop = ! empty( $instance['filter'] ); 87 $has_line_breaks = ( false !== strpos( $instance['text'], "\n" ) ); 88 89 // If auto-paragraphs are not enabled and there are line breaks, then ensure legacy mode. 90 if ( ! $wpautop && $has_line_breaks ) { 91 return true; 92 } 93 94 /* 95 * If a shortcode is present (with support added by a plugin), assume legacy mode 96 * since shortcodes would apply at the widget_text filter and thus be applied 97 * before wpautop runs at the widget_text_content filter. 98 */ 99 if ( preg_match( '/' . get_shortcode_regex() . '/', $instance['text'] ) ) { 100 return true; 101 } 102 103 // In the rare case that DOMDocument is not available we cannot reliably sniff content and so we assume legacy. 104 if ( ! class_exists( 'DOMDocument' ) ) { 105 // @codeCoverageIgnoreStart 106 return true; 107 // @codeCoverageIgnoreEnd 108 } 109 110 $doc = new DOMDocument(); 111 $doc->loadHTML( sprintf( 112 '<html><head><meta charset="%s"></head><body>%s</body></html>', 113 esc_attr( get_bloginfo( 'charset' ) ), 114 $instance['text'] 115 ) ); 116 $body = $doc->getElementsByTagName( 'body' )->item( 0 ); 117 118 // See $allowedposttags. 119 $safe_elements_attributes = array( 120 'strong' => array(), 121 'em' => array(), 122 'b' => array(), 123 'i' => array(), 124 'u' => array(), 125 's' => array(), 126 'ul' => array(), 127 'ol' => array(), 128 'li' => array(), 129 'hr' => array(), 130 'abbr' => array(), 131 'acronym' => array(), 132 'code' => array(), 133 'dfn' => array(), 134 'a' => array( 135 'href' => true, 136 ), 137 'img' => array( 138 'src' => true, 139 'alt' => true, 140 ), 141 ); 142 $safe_empty_elements = array( 'img', 'hr', 'iframe' ); 143 144 foreach ( $body->getElementsByTagName( '*' ) as $element ) { 145 /** @var DOMElement $element */ 146 $tag_name = strtolower( $element->nodeName ); 147 148 // If the element is not safe, then the instance is legacy. 149 if ( ! isset( $safe_elements_attributes[ $tag_name ] ) ) { 150 return true; 151 } 152 153 // If the element is not safely empty and it has empty contents, then legacy mode. 154 if ( ! in_array( $tag_name, $safe_empty_elements, true ) && '' === trim( $element->textContent ) ) { 155 return true; 156 } 157 158 // If an attribute is not recognized as safe, then the instance is legacy. 159 foreach ( $element->attributes as $attribute ) { 160 /** @var DOMAttr $attribute */ 161 $attribute_name = strtolower( $attribute->nodeName ); 162 163 if ( ! isset( $safe_elements_attributes[ $tag_name ][ $attribute_name ] ) ) { 164 return true; 165 } 166 } 167 } 168 169 // Otherwise, the text contains no elements/attributes that TinyMCE could drop, and therefore the widget does not need legacy mode. 170 return false; 171 } 172 173 /** 56 174 * Outputs the content for the current Text widget instance. 57 175 * 58 176 * @since 2.8.0 … … class WP_Widget_Text extends WP_Widget { 68 186 $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base ); 69 187 70 188 $text = ! empty( $instance['text'] ) ? $instance['text'] : ''; 189 $is_visual_text_widget = ( isset( $instance['filter'] ) && 'content' === $instance['filter'] ); 190 191 /* 192 * Just-in-time temporarily upgrade Visual Text widget shortcode handling 193 * (with support added by plugin) from the widget_text filter to 194 * widget_text_content:11 to prevent wpautop from corrupting HTML output 195 * added by the shortcode. 196 */ 197 $widget_text_do_shortcode_priority = has_filter( 'widget_text', 'do_shortcode' ); 198 $should_upgrade_shortcode_handling = ( $is_visual_text_widget && false !== $widget_text_do_shortcode_priority ); 199 if ( $should_upgrade_shortcode_handling ) { 200 remove_filter( 'widget_text', 'do_shortcode', $widget_text_do_shortcode_priority ); 201 add_filter( 'widget_text_content', 'do_shortcode', 11 ); 202 } 71 203 72 204 /** 73 205 * Filters the content of the Text widget. … … class WP_Widget_Text extends WP_Widget { 102 234 } 103 235 } 104 236 237 // Undo temporary upgrade of the plugin-supplied shortcode handling. 238 if ( $should_upgrade_shortcode_handling ) { 239 remove_filter( 'widget_text_content', 'do_shortcode', 11 ); 240 add_filter( 'widget_text', 'do_shortcode', $widget_text_do_shortcode_priority ); 241 } 242 105 243 echo $args['before_widget']; 106 244 if ( ! empty( $title ) ) { 107 245 echo $args['before_title'] . $title . $args['after_title']; … … class WP_Widget_Text extends WP_Widget { 134 272 } 135 273 136 274 /* 137 * Re-use legacy 'filter' (wpautop) property to now indicate content filters will always apply. 275 * If the Text widget is in legacy mode, then a hidden input will indicate this 276 * and the new content value for the filter prop will by bypassed. Otherwise, 277 * re-use legacy 'filter' (wpautop) property to now indicate content filters will always apply. 138 278 * Prior to 4.8, this is a boolean value used to indicate whether or not wpautop should be 139 279 * applied. By re-using this property, downgrading WordPress from 4.8 to 4.7 will ensure 140 280 * that the content for Text widgets created with TinyMCE will continue to get wpautop. 141 281 */ 142 $instance['filter'] = 'content'; 282 if ( isset( $new_instance['legacy'] ) || isset( $old_instance['legacy'] ) || ( isset( $new_instance['filter'] ) && 'content' !== $new_instance['filter'] ) ) { 283 $instance['filter'] = ! empty( $new_instance['filter'] ); 284 $instance['legacy'] = true; 285 } else { 286 $instance['filter'] = 'content'; 287 unset( $instance['legacy'] ); 288 } 143 289 144 290 return $instance; 145 291 } … … class WP_Widget_Text extends WP_Widget { 153 299 public function enqueue_admin_scripts() { 154 300 wp_enqueue_editor(); 155 301 wp_enqueue_script( 'text-widgets' ); 302 wp_enqueue_style( 'wp-pointer' ); 156 303 } 157 304 158 305 /** … … class WP_Widget_Text extends WP_Widget { 160 307 * 161 308 * @since 2.8.0 162 309 * @since 4.8.0 Form only contains hidden inputs which are synced with JS template. 310 * @since 4.8.1 Restored original form to be displayed when in legacy mode. 163 311 * @access public 164 312 * @see WP_Widget_Visual_Text::render_control_template_scripts() 165 313 * … … class WP_Widget_Text extends WP_Widget { 175 323 ) 176 324 ); 177 325 ?> 178 <input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" class="title" type="hidden" value="<?php echo esc_attr( $instance['title'] ); ?>"> 179 <input id="<?php echo $this->get_field_id( 'text' ); ?>" name="<?php echo $this->get_field_name( 'text' ); ?>" class="text" type="hidden" value="<?php echo esc_attr( $instance['text'] ); ?>"> 180 <?php 326 <?php if ( ! $this->is_legacy_instance( $instance ) ) : ?> 327 <input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" class="title" type="hidden" value="<?php echo esc_attr( $instance['title'] ); ?>"> 328 <input id="<?php echo $this->get_field_id( 'text' ); ?>" name="<?php echo $this->get_field_name( 'text' ); ?>" class="text" type="hidden" value="<?php echo esc_attr( $instance['text'] ); ?>"> 329 <?php else : ?> 330 <input name="<?php echo $this->get_field_name( 'legacy' ); ?>" type="hidden" class="legacy" value="true"> 331 <p> 332 <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label> 333 <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>"/> 334 </p> 335 <div class="notice inline notice-info notice-alt"> 336 <p><?php _e( 'This widget contains code that may work better in the new “Custom HTML” widget. How about trying that widget instead?' ); ?></p> 337 </div> 338 <p> 339 <label for="<?php echo $this->get_field_id( 'text' ); ?>"><?php _e( 'Content:' ); ?></label> 340 <textarea class="widefat" rows="16" cols="20" id="<?php echo $this->get_field_id( 'text' ); ?>" name="<?php echo $this->get_field_name( 'text' ); ?>"><?php echo esc_textarea( $instance['text'] ); ?></textarea> 341 </p> 342 <p> 343 <input id="<?php echo $this->get_field_id( 'filter' ); ?>" name="<?php echo $this->get_field_name( 'filter' ); ?>" type="checkbox"<?php checked( ! empty( $instance['filter'] ) ); ?> /> <label for="<?php echo $this->get_field_id( 'filter' ); ?>"><?php _e( 'Automatically add paragraphs' ); ?></label> 344 </p> 345 <?php endif; 181 346 } 182 347 183 348 /** -
tests/phpunit/tests/widgets/text-widget.php
diff --git tests/phpunit/tests/widgets/text-widget.php tests/phpunit/tests/widgets/text-widget.php index a28e6a668a..40b5c387eb 100644
class Test_WP_Widget_Text extends WP_UnitTestCase { 40 40 } 41 41 42 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'] ); 54 } 55 56 /** 43 57 * Test enqueue_admin_scripts method. 44 58 * 45 59 * @covers WP_Widget_Text::_register … … class Test_WP_Widget_Text extends WP_UnitTestCase { 122 136 } 123 137 124 138 /** 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 /** 125 211 * Filters the content of the Text widget. 126 212 * 127 213 * @param string $widget_text The widget content. … … class Test_WP_Widget_Text extends WP_UnitTestCase { 152 238 } 153 239 154 240 /** 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' => 'Here is a [gallery]', 294 'filter' => true, 295 ) ); 296 $this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy mode when a shortcode is present.' ); 297 298 // Check text examples that will not migrate to TinyMCE. 299 $legacy_text_examples = array( 300 '<span class="hello"></span>', 301 '<span></span>', 302 "<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>", 303 '<a href="#" class="map"></a>', 304 "<script>\n\\Line one\n\n\\Line two</script>", 305 "<style>body {\ncolor:red;\n}</style>", 306 '<span class="fa fa-cc-discover fa-2x" aria-hidden="true"></span>', 307 "<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>", 308 '<span class="sectiondown"><a href="#front-page-3"><i class="fa fa-chevron-circle-down"></i></a></span>', 309 ); 310 foreach ( $legacy_text_examples as $legacy_text_example ) { 311 $instance = array_merge( $base_instance, array( 312 'text' => $legacy_text_example, 313 'filter' => true, 314 ) ); 315 $this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when wpautop and there is HTML that is not liable to be mutated.' ); 316 317 $instance = array_merge( $base_instance, array( 318 'text' => $legacy_text_example, 319 'filter' => false, 320 ) ); 321 $this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when not-wpautop and there is HTML that is not liable to be mutated.' ); 322 } 323 324 // Check text examples that will migrate to TinyMCE, where elements and attributes are not in whitelist. 325 $migratable_text_examples = array( 326 'Check out <a href="http://example.com">Example</a>', 327 '<img src="http://example.com/img.jpg" alt="Img">', 328 '<strong><em>Hello</em></strong>', 329 '<b><i><u><s>Hello</s></u></i></b>', 330 "<ul>\n<li>One</li>\n<li>One</li>\n<li>One</li>\n</ul>", 331 "<ol>\n<li>One</li>\n<li>One</li>\n<li>One</li>\n</ol>", 332 "Text\n<hr>\nAddendum", 333 "Look at this code:\n\n<code>echo 'Hello World!';</code>", 334 ); 335 foreach ( $migratable_text_examples as $migratable_text_example ) { 336 $instance = array_merge( $base_instance, array( 337 'text' => $migratable_text_example, 338 'filter' => true, 339 ) ); 340 $this->assertFalse( $widget->is_legacy_instance( $instance ), 'Legacy when wpautop and there is HTML that is not liable to be mutated.' ); 341 } 342 } 343 344 /** 345 * Test update method. 346 * 347 * @covers WP_Widget_Text::form 348 */ 349 function test_form() { 350 $widget = new WP_Widget_Text(); 351 $instance = array( 352 'title' => 'Title', 353 'text' => 'Text', 354 'filter' => false, 355 'legacy' => true, 356 ); 357 $this->assertTrue( $widget->is_legacy_instance( $instance ) ); 358 ob_start(); 359 $widget->form( $instance ); 360 $form = ob_get_clean(); 361 $this->assertContains( 'class="legacy"', $form ); 362 363 $instance = array( 364 'title' => 'Title', 365 'text' => 'Text', 366 'filter' => 'content', 367 ); 368 $this->assertFalse( $widget->is_legacy_instance( $instance ) ); 369 ob_start(); 370 $widget->form( $instance ); 371 $form = ob_get_clean(); 372 $this->assertNotContains( 'class="legacy"', $form ); 373 } 374 375 /** 155 376 * Test update method. 156 377 * 157 378 * @covers WP_Widget_Text::update … … class Test_WP_Widget_Text extends WP_UnitTestCase { 161 382 $instance = array( 162 383 'title' => "The\nTitle", 163 384 'text' => "The\n\nText", 164 'filter' => false,385 'filter' => 'content', 165 386 ); 166 387 167 388 wp_set_current_user( $this->factory()->user->create( array( 168 389 'role' => 'administrator', 169 390 ) ) ); 170 391 171 // Should return valid instance .392 // Should return valid instance in legacy mode since filter=false and there are line breaks. 172 393 $expected = array( 173 394 'title' => sanitize_text_field( $instance['title'] ), 174 395 'text' => $instance['text'], 175 396 'filter' => 'content', 176 397 ); 177 398 $result = $widget->update( $instance, array() ); 178 $this->assertEquals( $ result, $expected);399 $this->assertEquals( $expected, $result ); 179 400 $this->assertTrue( ! empty( $expected['filter'] ), 'Expected filter prop to be truthy, to handle case where 4.8 is downgraded to 4.7.' ); 180 401 181 402 // Make sure KSES is applying as expected. … … class Test_WP_Widget_Text extends WP_UnitTestCase { 184 405 $instance['text'] = '<script>alert( "Howdy!" );</script>'; 185 406 $expected['text'] = $instance['text']; 186 407 $result = $widget->update( $instance, array() ); 187 $this->assertEquals( $ result, $expected);408 $this->assertEquals( $expected, $result ); 188 409 remove_filter( 'map_meta_cap', array( $this, 'grant_unfiltered_html_cap' ) ); 189 410 190 411 add_filter( 'map_meta_cap', array( $this, 'revoke_unfiltered_html_cap' ), 10, 2 ); … … class Test_WP_Widget_Text extends WP_UnitTestCase { 192 413 $instance['text'] = '<script>alert( "Howdy!" );</script>'; 193 414 $expected['text'] = wp_kses_post( $instance['text'] ); 194 415 $result = $widget->update( $instance, array() ); 195 $this->assertEquals( $ result, $expected);416 $this->assertEquals( $expected, $result ); 196 417 remove_filter( 'map_meta_cap', array( $this, 'revoke_unfiltered_html_cap' ), 10 ); 197 418 } 198 419 199 420 /** 421 * Test update for legacy widgets. 422 * 423 * @covers WP_Widget_Text::update 424 */ 425 function test_update_legacy() { 426 $widget = new WP_Widget_Text(); 427 428 // Updating a widget with explicit filter=true persists with legacy mode. 429 $instance = array( 430 'title' => 'Legacy', 431 'text' => 'Text', 432 'filter' => true, 433 ); 434 $result = $widget->update( $instance, array() ); 435 $expected = array_merge( $instance, array( 436 'legacy' => true, 437 'filter' => true, 438 ) ); 439 $this->assertEquals( $expected, $result ); 440 441 // Updating a widget with explicit filter=false persists with legacy mode. 442 $instance['filter'] = false; 443 $result = $widget->update( $instance, array() ); 444 $expected = array_merge( $instance, array( 445 'legacy' => true, 446 'filter' => false, 447 ) ); 448 $this->assertEquals( $expected, $result ); 449 450 // Updating a widget in legacy form results in filter=false when checkbox not checked. 451 $instance['filter'] = true; 452 $result = $widget->update( $instance, array() ); 453 $expected = array_merge( $instance, array( 454 'legacy' => true, 455 'filter' => true, 456 ) ); 457 $this->assertEquals( $expected, $result ); 458 459 // Updating a widget that previously had legacy form results in filter persisting. 460 unset( $instance['legacy'] ); 461 $instance['filter'] = true; 462 $result = $widget->update( $instance, array( 463 'legacy' => true, 464 ) ); 465 $expected = array_merge( $instance, array( 466 'legacy' => true, 467 'filter' => true, 468 ) ); 469 $this->assertEquals( $expected, $result ); 470 } 471 472 /** 200 473 * Grant unfiltered_html cap via map_meta_cap. 201 474 * 202 475 * @param array $caps Returns the user's actual capabilities.