28 * Sets up a new Text widget instance. |
28 * Sets up a new Text widget instance. |
29 * |
29 * |
30 * @since 2.8.0 |
30 * @since 2.8.0 |
31 */ |
31 */ |
32 public function __construct() { |
32 public function __construct() { |
33 $widget_ops = array( |
33 $widget_ops = array( |
34 'classname' => 'widget_text', |
34 'classname' => 'widget_text', |
35 'description' => __( 'Arbitrary text.' ), |
35 'description' => __( 'Arbitrary text.' ), |
36 'customize_selective_refresh' => true, |
36 'customize_selective_refresh' => true, |
37 ); |
37 ); |
38 $control_ops = array( |
38 $control_ops = array( |
39 'width' => 400, |
39 'width' => 400, |
40 'height' => 350, |
40 'height' => 350, |
41 ); |
41 ); |
42 parent::__construct( 'text', __( 'Text' ), $widget_ops, $control_ops ); |
42 parent::__construct( 'text', __( 'Text' ), $widget_ops, $control_ops ); |
43 } |
43 } |
44 |
44 |
97 // If the text is empty, then nothing is preventing migration to TinyMCE. |
97 // If the text is empty, then nothing is preventing migration to TinyMCE. |
98 if ( empty( $instance['text'] ) ) { |
98 if ( empty( $instance['text'] ) ) { |
99 return false; |
99 return false; |
100 } |
100 } |
101 |
101 |
102 $wpautop = ! empty( $instance['filter'] ); |
102 $wpautop = ! empty( $instance['filter'] ); |
103 $has_line_breaks = ( false !== strpos( trim( $instance['text'] ), "\n" ) ); |
103 $has_line_breaks = ( false !== strpos( trim( $instance['text'] ), "\n" ) ); |
104 |
104 |
105 // If auto-paragraphs are not enabled and there are line breaks, then ensure legacy mode. |
105 // If auto-paragraphs are not enabled and there are line breaks, then ensure legacy mode. |
106 if ( ! $wpautop && $has_line_breaks ) { |
106 if ( ! $wpautop && $has_line_breaks ) { |
107 return true; |
107 return true; |
118 return true; |
118 return true; |
119 // @codeCoverageIgnoreEnd |
119 // @codeCoverageIgnoreEnd |
120 } |
120 } |
121 |
121 |
122 $doc = new DOMDocument(); |
122 $doc = new DOMDocument(); |
123 @$doc->loadHTML( sprintf( |
123 |
124 '<!DOCTYPE html><html><head><meta charset="%s"></head><body>%s</body></html>', |
124 // Suppress warnings generated by loadHTML |
125 esc_attr( get_bloginfo( 'charset' ) ), |
125 $errors = libxml_use_internal_errors( true ); |
126 $instance['text'] |
126 @$doc->loadHTML( |
127 ) ); |
127 sprintf( |
|
128 '<!DOCTYPE html><html><head><meta charset="%s"></head><body>%s</body></html>', |
|
129 esc_attr( get_bloginfo( 'charset' ) ), |
|
130 $instance['text'] |
|
131 ) |
|
132 ); |
|
133 libxml_use_internal_errors( $errors ); |
|
134 |
128 $body = $doc->getElementsByTagName( 'body' )->item( 0 ); |
135 $body = $doc->getElementsByTagName( 'body' )->item( 0 ); |
129 |
136 |
130 // See $allowedposttags. |
137 // See $allowedposttags. |
131 $safe_elements_attributes = array( |
138 $safe_elements_attributes = array( |
132 'strong' => array(), |
139 'strong' => array(), |
133 'em' => array(), |
140 'em' => array(), |
134 'b' => array(), |
141 'b' => array(), |
135 'i' => array(), |
142 'i' => array(), |
136 'u' => array(), |
143 'u' => array(), |
137 's' => array(), |
144 's' => array(), |
138 'ul' => array(), |
145 'ul' => array(), |
139 'ol' => array(), |
146 'ol' => array(), |
140 'li' => array(), |
147 'li' => array(), |
141 'hr' => array(), |
148 'hr' => array(), |
142 'abbr' => array(), |
149 'abbr' => array(), |
143 'acronym' => array(), |
150 'acronym' => array(), |
144 'code' => array(), |
151 'code' => array(), |
145 'dfn' => array(), |
152 'dfn' => array(), |
146 'a' => array( |
153 'a' => array( |
147 'href' => true, |
154 'href' => true, |
148 ), |
155 ), |
149 'img' => array( |
156 'img' => array( |
150 'src' => true, |
157 'src' => true, |
151 'alt' => true, |
158 'alt' => true, |
152 ), |
159 ), |
153 ); |
160 ); |
154 $safe_empty_elements = array( 'img', 'hr', 'iframe' ); |
161 $safe_empty_elements = array( 'img', 'hr', 'iframe' ); |
155 |
162 |
156 foreach ( $body->getElementsByTagName( '*' ) as $element ) { |
163 foreach ( $body->getElementsByTagName( '*' ) as $element ) { |
157 /** @var DOMElement $element */ |
164 /** @var DOMElement $element */ |
158 $tag_name = strtolower( $element->nodeName ); |
165 $tag_name = strtolower( $element->nodeName ); |
159 |
166 |
217 $title = ! empty( $instance['title'] ) ? $instance['title'] : ''; |
224 $title = ! empty( $instance['title'] ) ? $instance['title'] : ''; |
218 |
225 |
219 /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ |
226 /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ |
220 $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); |
227 $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); |
221 |
228 |
222 $text = ! empty( $instance['text'] ) ? $instance['text'] : ''; |
229 $text = ! empty( $instance['text'] ) ? $instance['text'] : ''; |
223 $is_visual_text_widget = ( ! empty( $instance['visual'] ) && ! empty( $instance['filter'] ) ); |
230 $is_visual_text_widget = ( ! empty( $instance['visual'] ) && ! empty( $instance['filter'] ) ); |
224 |
231 |
225 // In 4.8.0 only, visual Text widgets get filter=content, without visual prop; upgrade instance props just-in-time. |
232 // In 4.8.0 only, visual Text widgets get filter=content, without visual prop; upgrade instance props just-in-time. |
226 if ( ! $is_visual_text_widget ) { |
233 if ( ! $is_visual_text_widget ) { |
227 $is_visual_text_widget = ( isset( $instance['filter'] ) && 'content' === $instance['filter'] ); |
234 $is_visual_text_widget = ( isset( $instance['filter'] ) && 'content' === $instance['filter'] ); |
235 * Suspend legacy plugin-supplied do_shortcode() for 'widget_text' filter for the visual Text widget to prevent |
242 * Suspend legacy plugin-supplied do_shortcode() for 'widget_text' filter for the visual Text widget to prevent |
236 * shortcodes being processed twice. Now do_shortcode() is added to the 'widget_text_content' filter in core itself |
243 * shortcodes being processed twice. Now do_shortcode() is added to the 'widget_text_content' filter in core itself |
237 * and it applies after wpautop() to prevent corrupting HTML output added by the shortcode. When do_shortcode() is |
244 * and it applies after wpautop() to prevent corrupting HTML output added by the shortcode. When do_shortcode() is |
238 * added to 'widget_text_content' then do_shortcode() will be manually called when in legacy mode as well. |
245 * added to 'widget_text_content' then do_shortcode() will be manually called when in legacy mode as well. |
239 */ |
246 */ |
240 $widget_text_do_shortcode_priority = has_filter( 'widget_text', 'do_shortcode' ); |
247 $widget_text_do_shortcode_priority = has_filter( 'widget_text', 'do_shortcode' ); |
241 $should_suspend_legacy_shortcode_support = ( $is_visual_text_widget && false !== $widget_text_do_shortcode_priority ); |
248 $should_suspend_legacy_shortcode_support = ( $is_visual_text_widget && false !== $widget_text_do_shortcode_priority ); |
242 if ( $should_suspend_legacy_shortcode_support ) { |
249 if ( $should_suspend_legacy_shortcode_support ) { |
243 remove_filter( 'widget_text', 'do_shortcode', $widget_text_do_shortcode_priority ); |
250 remove_filter( 'widget_text', 'do_shortcode', $widget_text_do_shortcode_priority ); |
244 } |
251 } |
245 |
252 |
319 echo $args['before_title'] . $title . $args['after_title']; |
326 echo $args['before_title'] . $title . $args['after_title']; |
320 } |
327 } |
321 |
328 |
322 $text = preg_replace_callback( '#<(video|iframe|object|embed)\s[^>]*>#i', array( $this, 'inject_video_max_width_style' ), $text ); |
329 $text = preg_replace_callback( '#<(video|iframe|object|embed)\s[^>]*>#i', array( $this, 'inject_video_max_width_style' ), $text ); |
323 |
330 |
|
331 // Adds noreferrer and noopener relationships, without duplicating values, to all HTML A elements that have a target. |
|
332 $text = wp_targeted_link_rel( $text ); |
|
333 |
324 ?> |
334 ?> |
325 <div class="textwidget"><?php echo $text; ?></div> |
335 <div class="textwidget"><?php echo $text; ?></div> |
326 <?php |
336 <?php |
327 echo $args['after_widget']; |
337 echo $args['after_widget']; |
328 } |
338 } |
353 * WP_Widget::form(). |
363 * WP_Widget::form(). |
354 * @param array $old_instance Old settings for this instance. |
364 * @param array $old_instance Old settings for this instance. |
355 * @return array Settings to save or bool false to cancel saving. |
365 * @return array Settings to save or bool false to cancel saving. |
356 */ |
366 */ |
357 public function update( $new_instance, $old_instance ) { |
367 public function update( $new_instance, $old_instance ) { |
358 $new_instance = wp_parse_args( $new_instance, array( |
368 $new_instance = wp_parse_args( |
359 'title' => '', |
369 $new_instance, |
360 'text' => '', |
370 array( |
361 'filter' => false, // For back-compat. |
371 'title' => '', |
362 'visual' => null, // Must be explicitly defined. |
372 'text' => '', |
363 ) ); |
373 'filter' => false, // For back-compat. |
|
374 'visual' => null, // Must be explicitly defined. |
|
375 ) |
|
376 ); |
364 |
377 |
365 $instance = $old_instance; |
378 $instance = $old_instance; |
366 |
379 |
367 $instance['title'] = sanitize_text_field( $new_instance['title'] ); |
380 $instance['title'] = sanitize_text_field( $new_instance['title'] ); |
368 if ( current_user_can( 'unfiltered_html' ) ) { |
381 if ( current_user_can( 'unfiltered_html' ) ) { |
425 * Outputs the Text widget settings form. |
438 * Outputs the Text widget settings form. |
426 * |
439 * |
427 * @since 2.8.0 |
440 * @since 2.8.0 |
428 * @since 4.8.0 Form only contains hidden inputs which are synced with JS template. |
441 * @since 4.8.0 Form only contains hidden inputs which are synced with JS template. |
429 * @since 4.8.1 Restored original form to be displayed when in legacy mode. |
442 * @since 4.8.1 Restored original form to be displayed when in legacy mode. |
430 * @see WP_Widget_Visual_Text::render_control_template_scripts() |
443 * @see WP_Widget_Text::render_control_template_scripts() |
431 * @see _WP_Editors::editor() |
444 * @see _WP_Editors::editor() |
432 * |
445 * |
433 * @param array $instance Current settings. |
446 * @param array $instance Current settings. |
434 * @return void |
447 * @return void |
435 */ |
448 */ |
436 public function form( $instance ) { |
449 public function form( $instance ) { |
437 $instance = wp_parse_args( |
450 $instance = wp_parse_args( |
438 (array) $instance, |
451 (array) $instance, |
439 array( |
452 array( |
440 'title' => '', |
453 'title' => '', |
441 'text' => '', |
454 'text' => '', |
442 ) |
455 ) |
443 ); |
456 ); |
444 ?> |
457 ?> |
445 <?php if ( ! $this->is_legacy_instance( $instance ) ) : ?> |
458 <?php if ( ! $this->is_legacy_instance( $instance ) ) : ?> |
446 <?php |
459 <?php |
486 <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> |
499 <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> |
487 </p> |
500 </p> |
488 <p> |
501 <p> |
489 <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> |
502 <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> |
490 </p> |
503 </p> |
491 <?php |
504 <?php |
492 endif; |
505 endif; |
493 } |
506 } |
494 |
507 |
495 /** |
508 /** |
496 * Render form template scripts. |
509 * Render form template scripts. |