Ticket #40854: 40854-widget-shortcodes.2.diff
File 40854-widget-shortcodes.2.diff, 11.1 KB (added by , 7 years ago) |
---|
-
src/wp-includes/default-filters.php
diff --git src/wp-includes/default-filters.php src/wp-includes/default-filters.php index a196f4ff19..1c7ae718f1 100644
add_filter( 'widget_text_content', 'capital_P_dangit', 11 ); 169 169 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 add_filter( 'widget_text_content', 'do_shortcode', 11 ); // Runs after wpautop() but note that $post global will be null when shortcodes run. 172 173 173 174 add_filter( 'date_i18n', 'wp_maybe_decline_date' ); 174 175 -
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 a79e0daa6c..f45f9aba90 100644
class WP_Widget_Text extends WP_Widget { 183 183 * 184 184 * @since 2.8.0 185 185 * 186 * @global WP_Post $post 187 * 186 188 * @param array $args Display arguments including 'before_title', 'after_title', 187 189 * 'before_widget', and 'after_widget'. 188 190 * @param array $instance Settings for the current Text widget instance. 189 191 */ 190 192 public function widget( $args, $instance ) { 193 global $post; 191 194 192 195 /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ 193 196 $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base ); … … class WP_Widget_Text extends WP_Widget { 205 208 } 206 209 207 210 /* 208 * Just-in-time temporarily upgrade Visual Text widget shortcode handling209 * (with support added by plugin) from the widget_text filter to210 * widget_text_content:11 to prevent wpautop from corrupting HTML output211 * added by the shortcode.211 * Suspend legacy plugin-supplied do_shortcode() for 'widget_text' filter for the visual Text widget to prevent 212 * shortcodes being processed twice. Now do_shortcode() is added to the 'widget_text_content' filter in core itself 213 * and it applies after wpautop() to prevent corrupting HTML output added by the shortcode. When do_shortcode() is 214 * added to 'widget_text_content' then do_shortcode() will be manually called when in legacy mode as well. 212 215 */ 213 216 $widget_text_do_shortcode_priority = has_filter( 'widget_text', 'do_shortcode' ); 214 $should_ upgrade_shortcode_handling= ( $is_visual_text_widget && false !== $widget_text_do_shortcode_priority );215 if ( $should_ upgrade_shortcode_handling) {217 $should_suspend_legacy_shortcode_support = ( $is_visual_text_widget && false !== $widget_text_do_shortcode_priority ); 218 if ( $should_suspend_legacy_shortcode_support ) { 216 219 remove_filter( 'widget_text', 'do_shortcode', $widget_text_do_shortcode_priority ); 217 add_filter( 'widget_text_content', 'do_shortcode', 11 ); 220 } 221 222 // Nullify the $post global during widget rendering to prevent shortcodes from running with the unexpected context. 223 $suspended_post = null; 224 if ( isset( $post ) ) { 225 $suspended_post = $post; 226 $post = null; 218 227 } 219 228 220 229 /** … … class WP_Widget_Text extends WP_Widget { 244 253 * @param WP_Widget_Text $this Current Text widget instance. 245 254 */ 246 255 $text = apply_filters( 'widget_text_content', $text, $instance, $this ); 256 } else { 257 if ( ! empty( $instance['filter'] ) ) { 258 $text = wpautop( $text ); // Back-compat for instances prior to 4.8. 259 } 260 261 /* 262 * Manually do shortcodes on the content when the core-added filter is present. It is added by default 263 * in core by adding do_shortcode() to the 'widget_text_content' filter to apply after wpautop(). 264 * Since the legacy Text widget runs wpautop() after 'widget_text' filters are applied, the widget in 265 * legacy mode here manually applies do_shortcode() on the content unless the default 266 * core filter for 'widget_text_content' has been removed, or if do_shortcode() has already 267 * been applied via a plugin adding do_shortcode() to 'widget_text' filters. 268 */ 269 if ( has_filter( 'widget_text_content', 'do_shortcode' ) && ! $widget_text_do_shortcode_priority ) { 270 $text = do_shortcode( $text ); 271 } 272 } 247 273 248 } elseif ( ! empty( $instance['filter'] ) ) { 249 $text = wpautop( $text ); // Back-compat for instances prior to 4.8. 274 // Restore post global. 275 if ( isset( $suspended_post ) ) { 276 $post = $suspended_post; 250 277 } 251 278 252 // Undo temporary upgrade of the plugin-supplied shortcode handling. 253 if ( $should_upgrade_shortcode_handling ) { 254 remove_filter( 'widget_text_content', 'do_shortcode', 11 ); 279 // Undo suspension of legacy plugin-supplied shortcode handling. 280 if ( $should_suspend_legacy_shortcode_support ) { 255 281 add_filter( 'widget_text', 'do_shortcode', $widget_text_do_shortcode_priority ); 256 282 } 257 283 -
tests/phpunit/tests/widgets/text-widget.php
diff --git tests/phpunit/tests/widgets/text-widget.php tests/phpunit/tests/widgets/text-widget.php index f7e810fd04..eb793d95b7 100644
class Test_WP_Widget_Text extends WP_UnitTestCase { 222 222 * 223 223 * @var string 224 224 */ 225 protected $example_shortcode_content = "<p>One\nTwo\n\nThree</p>\n<script>\ndocument.write('Test1');\n\ndocument.write('Test2');\n</script>"; 225 protected $example_shortcode_content = "<p>One\nTwo\n\nThree\n\nThis is testing the <code>[example note='This will not get processed since it is part of shortcode output itself.']</code> shortcode.</p>\n<script>\ndocument.write('Test1');\n\ndocument.write('Test2');\n</script>"; 226 227 /** 228 * The captured global post during shortcode rendering. 229 * 230 * @var WP_Post|null 231 */ 232 protected $post_during_shortcode = null; 233 234 /** 235 * Number of times the shortcode was rendered. 236 * 237 * @var int 238 */ 239 protected $shortcode_render_count = 0; 226 240 227 241 /** 228 242 * Do example shortcode. … … class Test_WP_Widget_Text extends WP_UnitTestCase { 230 244 * @return string Shortcode content. 231 245 */ 232 246 function do_example_shortcode() { 247 $this->post_during_shortcode = get_post(); 248 $this->shortcode_render_count++; 233 249 return $this->example_shortcode_content; 234 250 } 235 251 236 252 /** 237 * Test widget method w hen a plugin has added shortcode support.253 * Test widget method with shortcodes. 238 254 * 239 255 * @covers WP_Widget_Text::widget 240 256 */ 241 257 function test_widget_shortcodes() { 258 global $post; 259 $post_id = $this->factory()->post->create(); 260 $post = get_post( $post_id ); 261 242 262 $args = array( 243 263 'before_title' => '<h2>', 244 264 'after_title' => "</h2>\n", … … class Test_WP_Widget_Text extends WP_UnitTestCase { 246 266 'after_widget' => "</section>\n", 247 267 ); 248 268 $widget = new WP_Widget_Text(); 249 add_filter( 'widget_text', 'do_shortcode' );250 269 add_shortcode( 'example', array( $this, 'do_example_shortcode' ) ); 251 270 252 271 $base_instance = array( … … class Test_WP_Widget_Text extends WP_UnitTestCase { 259 278 $instance = array_merge( $base_instance, array( 260 279 'filter' => false, 261 280 ) ); 281 $this->shortcode_render_count = 0; 262 282 ob_start(); 263 283 $widget->widget( $args, $instance ); 264 284 $output = ob_get_clean(); 285 $this->assertEquals( 1, $this->shortcode_render_count ); 286 $this->assertNotContains( '[example]', $output, 'Expected shortcode to be processed in legacy widget with plugin adding filter' ); 265 287 $this->assertContains( $this->example_shortcode_content, $output, 'Shortcode was applied without wpautop corrupting it.' ); 266 $this->assert Equals( 10, has_filter( 'widget_text', 'do_shortcode' ), 'Filter was restored.');288 $this->assertNull( $this->post_during_shortcode ); 267 289 268 // Visual Text Widget. 269 $instance = array_merge( $base_instance, array( 270 'filter' => 'content', 271 ) ); 290 // Legacy text widget with plugin adding shortcode support as well. 291 add_filter( 'widget_text', 'do_shortcode' ); 292 $this->shortcode_render_count = 0; 272 293 ob_start(); 273 294 $widget->widget( $args, $instance ); 274 295 $output = ob_get_clean(); 296 $this->assertEquals( 1, $this->shortcode_render_count ); 297 $this->assertNotContains( '[example]', $output, 'Expected shortcode to be processed in legacy widget with plugin adding filter' ); 275 298 $this->assertContains( $this->example_shortcode_content, $output, 'Shortcode was applied without wpautop corrupting it.' ); 276 $this->assertEquals( 10, has_filter( 'widget_text', 'do_shortcode' ), 'Filter was restored.' ); 277 $this->assertFalse( has_filter( 'widget_text_content', 'do_shortcode' ), 'Filter was removed.' ); 278 279 // Visual Text Widget with properly-used widget_text_content filter. 299 $this->assertNull( $this->post_during_shortcode ); 280 300 remove_filter( 'widget_text', 'do_shortcode' ); 281 add_filter( 'widget_text_content', 'do_shortcode', 11 ); 301 282 302 $instance = array_merge( $base_instance, array( 283 'filter' => 'content', 303 'filter' => true, 304 'visual' => true, 284 305 ) ); 306 307 // Visual Text Widget with only core-added widget_text_content filter for do_shortcode. 308 $this->assertFalse( has_filter( 'widget_text', 'do_shortcode' ) ); 309 $this->assertEquals( 11, has_filter( 'widget_text_content', 'do_shortcode' ), 'Expected core to have set do_shortcode as widget_text_content filter.' ); 310 $this->shortcode_render_count = 0; 311 ob_start(); 312 $widget->widget( $args, $instance ); 313 $output = ob_get_clean(); 314 $this->assertEquals( 1, $this->shortcode_render_count ); 315 $this->assertContains( $this->example_shortcode_content, $output, 'Shortcode was applied without wpautop corrupting it.' ); 316 $this->assertFalse( has_filter( 'widget_text', 'do_shortcode' ), 'The widget_text filter still lacks do_shortcode handler.' ); 317 $this->assertEquals( 11, has_filter( 'widget_text_content', 'do_shortcode' ), 'The widget_text_content filter still has do_shortcode handler.' ); 318 $this->assertNull( $this->post_during_shortcode ); 319 320 // Visual Text Widget with both filters applied added, one from core and another via plugin. 321 add_filter( 'widget_text', 'do_shortcode' ); 322 $this->shortcode_render_count = 0; 285 323 ob_start(); 286 324 $widget->widget( $args, $instance ); 287 325 $output = ob_get_clean(); 326 $this->assertEquals( 1, $this->shortcode_render_count ); 288 327 $this->assertContains( $this->example_shortcode_content, $output, 'Shortcode was applied without wpautop corrupting it.' ); 289 $this->assertFalse( has_filter( 'widget_text', 'do_shortcode' ), 'Filter was not erroneously restored.' ); 328 $this->assertEquals( 10, has_filter( 'widget_text', 'do_shortcode' ), 'Expected do_shortcode to be restored to widget_text.' ); 329 $this->assertNull( $this->post_during_shortcode ); 330 $this->assertNull( $this->post_during_shortcode ); 331 remove_filter( 'widget_text', 'do_shortcode' ); 332 333 // Visual Text Widget with shortcode handling disabled via plugin removing filter. 334 remove_filter( 'widget_text_content', 'do_shortcode', 11 ); 335 remove_filter( 'widget_text', 'do_shortcode' ); 336 $this->shortcode_render_count = 0; 337 ob_start(); 338 $widget->widget( $args, $instance ); 339 $output = ob_get_clean(); 340 $this->assertEquals( 0, $this->shortcode_render_count ); 341 $this->assertContains( '[example]', $output ); 342 $this->assertNotContains( $this->example_shortcode_content, $output ); 343 $this->assertFalse( has_filter( 'widget_text', 'do_shortcode' ) ); 344 $this->assertFalse( has_filter( 'widget_text_content', 'do_shortcode' ) ); 290 345 } 291 346 292 347 /**