Ticket #40907: 40907.1.diff
File 40907.1.diff, 11.1 KB (added by , 8 years ago) |
---|
-
src/wp-includes/default-filters.php
diff --git src/wp-includes/default-filters.php src/wp-includes/default-filters.php index 8f0237028d..6f64ffab5d 100644
add_filter( 'widget_text_content', 'wptexturize' ); 170 170 add_filter( 'widget_text_content', 'convert_smilies', 20 ); 171 171 add_filter( 'widget_text_content', 'wpautop' ); 172 172 173 add_filter( 'widget_html_code_content', 'balanceTags' ); 174 173 175 add_filter( 'date_i18n', 'wp_maybe_decline_date' ); 174 176 175 177 // RSS filters -
src/wp-includes/default-widgets.php
diff --git src/wp-includes/default-widgets.php src/wp-includes/default-widgets.php index 87ad9dbfb3..32a908ac11 100644
require_once( ABSPATH . WPINC . '/widgets/class-wp-widget-tag-cloud.php' ); 60 60 61 61 /** WP_Nav_Menu_Widget class */ 62 62 require_once( ABSPATH . WPINC . '/widgets/class-wp-nav-menu-widget.php' ); 63 64 /** WP_Widget_HTML_Code class */ 65 require_once( ABSPATH . WPINC . '/widgets/class-wp-widget-html-code.php' ); -
src/wp-includes/widgets.php
diff --git src/wp-includes/widgets.php src/wp-includes/widgets.php index caa575b149..b44048d641 100644
function wp_widgets_init() { 1474 1474 1475 1475 register_widget( 'WP_Nav_Menu_Widget' ); 1476 1476 1477 register_widget( 'WP_Widget_HTML_Code' ); 1478 1477 1479 /** 1478 1480 * Fires after all default WordPress widgets have been registered. 1479 1481 * -
new file src/wp-includes/widgets/class-wp-widget-html-code.php
diff --git src/wp-includes/widgets/class-wp-widget-html-code.php src/wp-includes/widgets/class-wp-widget-html-code.php new file mode 100644 index 0000000000..d16900183f
- + 1 <?php 2 /** 3 * Widget API: WP_Widget_HTML_Code class 4 * 5 * @package WordPress 6 * @subpackage Widgets 7 * @since 4.8.1 8 */ 9 10 /** 11 * Core class used to implement a HTML Code widget. 12 * 13 * @since 4.8.1 14 * 15 * @see WP_Widget 16 */ 17 class WP_Widget_HTML_Code extends WP_Widget { 18 19 /** 20 * Default instance. 21 * 22 * @since 4.8.1 23 * @var array 24 */ 25 protected $default_instance = array( 26 'title' => '', 27 'content' => '', 28 ); 29 30 /** 31 * Sets up a new HTML Code widget instance. 32 * 33 * @since 4.8.1 34 */ 35 public function __construct() { 36 $widget_ops = array( 37 'classname' => 'widget_html_code', 38 'description' => __( 'Arbitrary HTML code.' ), 39 'customize_selective_refresh' => true, 40 ); 41 $control_ops = array(); 42 parent::__construct( 'html_code', __( 'HTML Code' ), $widget_ops, $control_ops ); 43 } 44 45 /** 46 * Outputs the content for the current HTML Code widget instance. 47 * 48 * @since 4.8.1 49 * 50 * @param array $args Display arguments including 'before_title', 'after_title', 51 * 'before_widget', and 'after_widget'. 52 * @param array $instance Settings for the current HTML Code widget instance. 53 */ 54 public function widget( $args, $instance ) { 55 56 $instance = array_merge( $this->default_instance, $instance ); 57 58 /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ 59 $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ); 60 61 $content = $instance['content']; 62 63 /** 64 * Filters the content of the HTML Code widget. 65 * 66 * @since 4.8.1 67 * 68 * @param string $content The widget content. 69 * @param array $instance Array of settings for the current widget. 70 * @param WP_Widget_HTML_Code $this Current HTML Code widget instance. 71 */ 72 $content = apply_filters( 'widget_html_code_content', $content, $instance, $this ); 73 74 echo $args['before_widget']; 75 if ( ! empty( $title ) ) { 76 echo $args['before_title'] . $title . $args['after_title']; 77 } 78 echo $content; 79 echo $args['after_widget']; 80 } 81 82 /** 83 * Handles updating settings for the current HTML Code widget instance. 84 * 85 * @since 4.8.1 86 * 87 * @param array $new_instance New settings for this instance as input by the user via 88 * WP_Widget::form(). 89 * @param array $old_instance Old settings for this instance. 90 * @return array Settings to save or bool false to cancel saving. 91 */ 92 public function update( $new_instance, $old_instance ) { 93 $instance = array_merge( $this->default_instance, $old_instance ); 94 $instance['title'] = sanitize_text_field( $new_instance['title'] ); 95 if ( current_user_can( 'unfiltered_html' ) ) { 96 $instance['content'] = $new_instance['content']; 97 } else { 98 $instance['content'] = wp_kses_post( $new_instance['content'] ); 99 } 100 return $instance; 101 } 102 103 /** 104 * Outputs the HTML Code widget settings form. 105 * 106 * @since 4.8.1 107 * 108 * @param array $instance Current instance. 109 * @returns void 110 */ 111 public function form( $instance ) { 112 $instance = wp_parse_args( (array) $instance, $this->default_instance ); 113 ?> 114 <p> 115 <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label> 116 <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'] ); ?>"/> 117 </p> 118 119 <p> 120 <label for="<?php echo $this->get_field_id( 'content' ); ?>"><?php _e( 'Content:' ); ?></label> 121 <textarea class="widefat code" rows="16" cols="20" id="<?php echo $this->get_field_id( 'content' ); ?>" name="<?php echo $this->get_field_name( 'content' ); ?>"><?php echo esc_textarea( $instance['content'] ); ?></textarea> 122 </p> 123 124 <?php if ( ! current_user_can( 'unfiltered_html' ) ) : ?> 125 <?php 126 $probably_unsafe_html = array( 'script', 'iframe', 'form', 'input', 'style' ); 127 $allowed_html = wp_kses_allowed_html( 'post' ); 128 $disallowed_html = array_diff( $probably_unsafe_html, array_keys( $allowed_html ) ); 129 ?> 130 <?php if ( ! empty( $disallowed_html ) ) : ?> 131 <p> 132 <?php _e( 'Some HTML tags are not permitted, including:' ); ?> 133 <code><?php echo join( '</code>, <code>', $disallowed_html ); ?></code> 134 </p> 135 <?php endif; ?> 136 <?php endif; ?> 137 <?php 138 } 139 } -
new file tests/phpunit/tests/widgets/html-code-widget.php
diff --git tests/phpunit/tests/widgets/html-code-widget.php tests/phpunit/tests/widgets/html-code-widget.php new file mode 100644 index 0000000000..9ab5a18b94
- + 1 <?php 2 /** 3 * Unit tests covering WP_Widget_HTML_Code functionality. 4 * 5 * @package WordPress 6 * @subpackage widgets 7 */ 8 9 /** 10 * Test wp-includes/widgets/class-wp-widget-html-code.php 11 * 12 * @group widgets 13 */ 14 class Test_WP_Widget_HTML_Code extends WP_UnitTestCase { 15 16 /** 17 * Args passed to the widget_html_code_content filter. 18 * 19 * @var array 20 */ 21 protected $widget_html_code_content_args; 22 23 /** 24 * Test widget method. 25 * 26 * @covers WP_Widget_HTML_Code::widget 27 */ 28 function test_widget() { 29 $widget = new WP_Widget_HTML_Code(); 30 $content = "<i>Custom HTML</i>\n\n<b>CODE</b>\nLast line.<u>unclosed"; 31 32 $args = array( 33 'before_title' => '<h2>', 34 'after_title' => "</h2>\n", 35 'before_widget' => '<section>', 36 'after_widget' => "</section>\n", 37 ); 38 $instance = array( 39 'title' => 'Foo', 40 'content' => $content, 41 ); 42 43 $this->assertEquals( 10, has_filter( 'widget_html_code_content', 'balanceTags' ) ); 44 45 update_option( 'use_balanceTags', 0 ); 46 add_filter( 'widget_html_code_content', array( $this, 'filter_widget_html_code_content' ), 5, 3 ); 47 ob_start(); 48 $this->widget_html_code_content_args = null; 49 $widget->widget( $args, $instance ); 50 $output = ob_get_clean(); 51 $this->assertNotEmpty( $this->widget_html_code_content_args ); 52 $this->assertContains( '[filter:widget_html_code_content]', $output ); 53 $this->assertNotContains( '<p>', $output ); 54 $this->assertNotContains( '<br>', $output ); 55 $this->assertNotContains( '</u>', $output ); 56 $this->assertEquals( $instance, $this->widget_html_code_content_args[1] ); 57 $this->assertSame( $widget, $this->widget_html_code_content_args[2] ); 58 remove_filter( 'widget_html_code_content', array( $this, 'filter_widget_html_code_content' ), 5, 3 ); 59 60 update_option( 'use_balanceTags', 1 ); 61 ob_start(); 62 $widget->widget( $args, $instance ); 63 $output = ob_get_clean(); 64 $this->assertContains( '</u>', $output ); 65 } 66 67 /** 68 * Filters the content of the HTML Code widget. 69 * 70 * @param string $widget_content The widget content. 71 * @param array $instance Array of settings for the current widget. 72 * @param WP_Widget_HTML_Code $widget Current HTML Code widget instance. 73 * @return string Widget content. 74 */ 75 function filter_widget_html_code_content( $widget_content, $instance, $widget ) { 76 $this->widget_html_code_content_args = func_get_args(); 77 78 $widget_content .= '[filter:widget_html_code_content]'; 79 return $widget_content; 80 } 81 82 /** 83 * Test update method. 84 * 85 * @covers WP_Widget_HTML_Code::update 86 */ 87 function test_update() { 88 $widget = new WP_Widget_HTML_Code(); 89 $instance = array( 90 'title' => "The\n<b>Title</b>", 91 'content' => "The\n\n<b>Code</b>", 92 ); 93 94 wp_set_current_user( $this->factory()->user->create( array( 95 'role' => 'administrator', 96 ) ) ); 97 98 // Should return valid instance. 99 $expected = array( 100 'title' => sanitize_text_field( $instance['title'] ), 101 'content' => $instance['content'], 102 ); 103 $result = $widget->update( $instance, array() ); 104 $this->assertEquals( $result, $expected ); 105 106 // Make sure KSES is applying as expected. 107 add_filter( 'map_meta_cap', array( $this, 'grant_unfiltered_html_cap' ), 10, 2 ); 108 $this->assertTrue( current_user_can( 'unfiltered_html' ) ); 109 $instance['content'] = '<script>alert( "Howdy!" );</script>'; 110 $expected['content'] = $instance['content']; 111 $result = $widget->update( $instance, array() ); 112 $this->assertEquals( $result, $expected ); 113 remove_filter( 'map_meta_cap', array( $this, 'grant_unfiltered_html_cap' ) ); 114 115 add_filter( 'map_meta_cap', array( $this, 'revoke_unfiltered_html_cap' ), 10, 2 ); 116 $this->assertFalse( current_user_can( 'unfiltered_html' ) ); 117 $instance['content'] = '<script>alert( "Howdy!" );</script>'; 118 $expected['content'] = wp_kses_post( $instance['content'] ); 119 $result = $widget->update( $instance, array() ); 120 $this->assertEquals( $result, $expected ); 121 remove_filter( 'map_meta_cap', array( $this, 'revoke_unfiltered_html_cap' ), 10 ); 122 } 123 124 /** 125 * Grant unfiltered_html cap via map_meta_cap. 126 * 127 * @param array $caps Returns the user's actual capabilities. 128 * @param string $cap Capability name. 129 * @return array Caps. 130 */ 131 function grant_unfiltered_html_cap( $caps, $cap ) { 132 if ( 'unfiltered_html' === $cap ) { 133 $caps = array_diff( $caps, array( 'do_not_allow' ) ); 134 $caps[] = 'unfiltered_html'; 135 } 136 return $caps; 137 } 138 139 /** 140 * Revoke unfiltered_html cap via map_meta_cap. 141 * 142 * @param array $caps Returns the user's actual capabilities. 143 * @param string $cap Capability name. 144 * @return array Caps. 145 */ 146 function revoke_unfiltered_html_cap( $caps, $cap ) { 147 if ( 'unfiltered_html' === $cap ) { 148 $caps = array_diff( $caps, array( 'unfiltered_html' ) ); 149 $caps[] = 'do_not_allow'; 150 } 151 return $caps; 152 } 153 }