Ticket #27504: 27504.3.patch
File 27504.3.patch, 42.4 KB (added by , 10 years ago) |
---|
-
src/wp-admin/includes/ajax-actions.php
diff --git src/wp-admin/includes/ajax-actions.php src/wp-admin/includes/ajax-actions.php index e7f4f6b..a6b47f9 100644
function wp_ajax_save_widget() { 1588 1588 } 1589 1589 1590 1590 function wp_ajax_update_widget() { 1591 WP_Customize_Widgets::wp_ajax_update_widget(); 1591 global $wp_customize; 1592 $wp_customize->widgets->wp_ajax_update_widget(); 1592 1593 } 1593 1594 1594 1595 function wp_ajax_upload_attachment() { -
src/wp-includes/class-wp-customize-control.php
diff --git src/wp-includes/class-wp-customize-control.php src/wp-includes/class-wp-customize-control.php index cce9195..e520b6d 100644
class WP_Widget_Form_Customize_Control extends WP_Customize_Control { 1080 1080 ); 1081 1081 1082 1082 $args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $args, 1 => $widget['params'][0] ) ); 1083 echo WP_Customize_Widgets::get_widget_control( $args );1083 echo $this->manager->widgets->get_widget_control( $args ); 1084 1084 } 1085 1085 } 1086 1086 -
src/wp-includes/class-wp-customize-manager.php
diff --git src/wp-includes/class-wp-customize-manager.php src/wp-includes/class-wp-customize-manager.php index 7f337f5..cc41801 100644
final class WP_Customize_Manager { 37 37 */ 38 38 protected $previewing = false; 39 39 40 /** 41 * Methods and properties deailing with managing widgets in the customizer 42 * 43 * @var WP_Customize_Widgets 44 */ 45 public $widgets; 46 40 47 protected $settings = array(); 41 48 protected $sections = array(); 42 49 protected $controls = array(); … … final class WP_Customize_Manager { 63 70 require( ABSPATH . WPINC . '/class-wp-customize-control.php' ); 64 71 require( ABSPATH . WPINC . '/class-wp-customize-widgets.php' ); 65 72 66 WP_Customize_Widgets::setup(); // This should be integrated.73 $this->widgets = new WP_Customize_Widgets( $this ); 67 74 68 75 add_filter( 'wp_die_handler', array( $this, 'wp_die_handler' ) ); 69 76 -
src/wp-includes/class-wp-customize-widgets.php
diff --git src/wp-includes/class-wp-customize-widgets.php src/wp-includes/class-wp-customize-widgets.php index 055afa2..a37cb98 100644
class WP_Customize_Widgets { 13 13 const UPDATE_WIDGET_NONCE_POST_KEY = 'update-sidebar-widgets-nonce'; 14 14 15 15 /** 16 * @access public 17 * @var WP_Customize_Manager 18 */ 19 public $manager; 20 21 /** 16 22 * All id_bases for widgets defined in core 17 23 * 18 24 * @since 3.9.0 … … class WP_Customize_Widgets { 20 26 * @access protected 21 27 * @var array 22 28 */ 23 protected static$core_widget_id_bases = array(29 protected $core_widget_id_bases = array( 24 30 'archives', 25 31 'calendar', 26 32 'categories', … … class WP_Customize_Widgets { 42 48 * @access protected 43 49 * @var 44 50 */ 45 protected static$_customized;51 protected $_customized; 46 52 47 53 /** 48 54 * @since 3.9.0 … … class WP_Customize_Widgets { 50 56 * @access protected 51 57 * @var array 52 58 */ 53 protected static$_prepreview_added_filters = array();59 protected $_prepreview_added_filters = array(); 54 60 55 61 /** 56 62 * @since 3.9.0 … … class WP_Customize_Widgets { 58 64 * @access protected 59 65 * @var array 60 66 */ 61 staticprotected $rendered_sidebars = array();67 protected $rendered_sidebars = array(); 62 68 63 69 /** 64 70 * @since 3.9.0 … … class WP_Customize_Widgets { 66 72 * @access protected 67 73 * @var array 68 74 */ 69 staticprotected $rendered_widgets = array();75 protected $rendered_widgets = array(); 70 76 71 77 /** 72 78 * Initial loader. … … class WP_Customize_Widgets { 75 81 * @static 76 82 * @access public 77 83 */ 78 static function setup() { 79 add_action( 'after_setup_theme', array( __CLASS__, 'setup_widget_addition_previews' ) ); 80 add_action( 'customize_controls_init', array( __CLASS__, 'customize_controls_init' ) ); 81 add_action( 'customize_register', array( __CLASS__, 'schedule_customize_register' ), 1 ); 82 add_action( 'customize_controls_enqueue_scripts', array( __CLASS__, 'customize_controls_enqueue_deps' ) ); 83 add_action( 'customize_controls_print_footer_scripts', array( __CLASS__, 'output_widget_control_templates' ) ); 84 add_action( 'customize_preview_init', array( __CLASS__, 'customize_preview_init' ) ); 85 86 add_action( 'dynamic_sidebar', array( __CLASS__, 'tally_rendered_widgets' ) ); 87 add_filter( 'is_active_sidebar', array( __CLASS__, 'tally_sidebars_via_is_active_sidebar_calls' ), 10, 2 ); 88 add_filter( 'dynamic_sidebar_has_widgets', array( __CLASS__, 'tally_sidebars_via_dynamic_sidebar_calls' ), 10, 2 ); 84 function __construct( WP_Customize_Manager $manager ) { 85 $this->manager = $manager; 86 87 add_action( 'after_setup_theme', array( $this, 'setup_widget_addition_previews' ) ); 88 add_action( 'customize_controls_init', array( $this, 'customize_controls_init' ) ); 89 add_action( 'customize_register', array( $this, 'schedule_customize_register' ), 1 ); 90 add_action( 'customize_controls_enqueue_scripts', array( $this, 'customize_controls_enqueue_deps' ) ); 91 add_action( 'customize_controls_print_footer_scripts', array( $this, 'output_widget_control_templates' ) ); 92 add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ) ); 93 94 add_action( 'dynamic_sidebar', array( $this, 'tally_rendered_widgets' ) ); 95 add_filter( 'is_active_sidebar', array( $this, 'tally_sidebars_via_is_active_sidebar_calls' ), 10, 2 ); 96 add_filter( 'dynamic_sidebar_has_widgets', array( $this, 'tally_sidebars_via_dynamic_sidebar_calls' ), 10, 2 ); 89 97 } 90 98 91 99 /** … … class WP_Customize_Widgets { 100 108 * @param mixed $default Default post value. 101 109 * @return mixed Unslashed post value or default value. 102 110 */ 103 staticfunction get_post_value( $name, $default = null ) {111 function get_post_value( $name, $default = null ) { 104 112 if ( ! isset( $_POST[ $name ] ) ) { 105 113 return $default; 106 114 } … … class WP_Customize_Widgets { 119 127 * @access public 120 128 * @global WP_Customize_Manager $wp_customize 121 129 */ 122 static function setup_widget_addition_previews() { 123 global $wp_customize; 130 function setup_widget_addition_previews() { 124 131 $is_customize_preview = ( 125 ( ! empty( $ wp_customize) )132 ( ! empty( $this->manager ) ) 126 133 && 127 134 ( ! is_admin() ) 128 135 && 129 ( 'on' === self::get_post_value( 'wp_customize' ) )136 ( 'on' === $this->get_post_value( 'wp_customize' ) ) 130 137 && 131 check_ajax_referer( 'preview-customize_' . $ wp_customize->get_stylesheet(), 'nonce', false )138 check_ajax_referer( 'preview-customize_' . $this->manager->get_stylesheet(), 'nonce', false ) 132 139 ); 133 140 134 141 $is_ajax_widget_update = ( 135 142 ( defined( 'DOING_AJAX' ) && DOING_AJAX ) 136 143 && 137 self::get_post_value( 'action' ) === self::UPDATE_WIDGET_AJAX_ACTION144 $this->get_post_value( 'action' ) === self::UPDATE_WIDGET_AJAX_ACTION 138 145 && 139 146 check_ajax_referer( self::UPDATE_WIDGET_AJAX_ACTION, self::UPDATE_WIDGET_NONCE_POST_KEY, false ) 140 147 ); … … class WP_Customize_Widgets { 142 149 $is_ajax_customize_save = ( 143 150 ( defined( 'DOING_AJAX' ) && DOING_AJAX ) 144 151 && 145 self::get_post_value( 'action' ) === 'customize_save'152 $this->get_post_value( 'action' ) === 'customize_save' 146 153 && 147 check_ajax_referer( 'save-customize_' . $ wp_customize->get_stylesheet(), 'nonce' )154 check_ajax_referer( 'save-customize_' . $this->manager->get_stylesheet(), 'nonce' ) 148 155 ); 149 156 150 157 $is_valid_request = ( $is_ajax_widget_update || $is_customize_preview || $is_ajax_customize_save ); … … class WP_Customize_Widgets { 154 161 155 162 // Input from customizer preview. 156 163 if ( isset( $_POST['customized'] ) ) { 157 $customized = json_decode( self::get_post_value( 'customized' ), true );164 $customized = json_decode( $this->get_post_value( 'customized' ), true ); 158 165 } 159 166 160 167 // Input from ajax widget update request. 161 168 else { 162 169 $customized = array(); 163 $id_base = self::get_post_value( 'id_base' );164 $widget_number = (int) self::get_post_value( 'widget_number' );170 $id_base = $this->get_post_value( 'id_base' ); 171 $widget_number = (int) $this->get_post_value( 'widget_number' ); 165 172 $option_name = 'widget_' . $id_base; 166 173 $customized[$option_name] = array(); 167 174 if ( false !== $widget_number ) { … … class WP_Customize_Widgets { 170 177 } 171 178 } 172 179 173 $function = array( __CLASS__, 'prepreview_added_sidebars_widgets' );180 $function = array( $this, 'prepreview_added_sidebars_widgets' ); 174 181 175 182 $hook = 'option_sidebars_widgets'; 176 183 add_filter( $hook, $function ); 177 self::$_prepreview_added_filters[] = compact( 'hook', 'function' );184 $this->_prepreview_added_filters[] = compact( 'hook', 'function' ); 178 185 179 186 $hook = 'default_option_sidebars_widgets'; 180 187 add_filter( $hook, $function ); 181 self::$_prepreview_added_filters[] = compact( 'hook', 'function' );188 $this->_prepreview_added_filters[] = compact( 'hook', 'function' ); 182 189 183 190 foreach ( $customized as $setting_id => $value ) { 184 191 if ( preg_match( '/^(widget_.+?)(\[(\d+)\])?$/', $setting_id, $matches ) ) { 185 $body = sprintf( 'return %s::prepreview_added_widget_instance( $value, %s );', __CLASS__, var_export( $setting_id, true ) ); 192 // @todo refactor using closures when PHP 5.3 is minimum requred version for WordPress 193 $body = sprintf( 'global $wp_customize; return $wp_customize->widgets->prepreview_added_widget_instance( $value, %s );', var_export( $setting_id, true ) ); 186 194 $function = create_function( '$value', $body ); 195 // @todo replace above two lines with following once PHP 5.3 happens in WordPress 196 // $self = $this; // not needed in PHP 5.4 197 // $function = function ( $value ) use ( $self, $setting_id ) { 198 // return $self->manager->widgets->prepreview_added_widget_instance( $value, $setting_id ); 199 //}; 200 187 201 $option = $matches[1]; 188 202 189 203 $hook = sprintf( 'option_%s', $option ); 190 204 add_filter( $hook, $function ); 191 self::$_prepreview_added_filters[] = compact( 'hook', 'function' );205 $this->_prepreview_added_filters[] = compact( 'hook', 'function' ); 192 206 193 207 $hook = sprintf( 'default_option_%s', $option ); 194 208 add_filter( $hook, $function ); 195 self::$_prepreview_added_filters[] = compact( 'hook', 'function' );209 $this->_prepreview_added_filters[] = compact( 'hook', 'function' ); 196 210 197 211 /** 198 212 * Make sure the option is registered so that the update_option won't fail due to … … class WP_Customize_Widgets { 202 216 } 203 217 } 204 218 205 self::$_customized = $customized;219 $this->_customized = $customized; 206 220 } 207 221 208 222 /** … … class WP_Customize_Widgets { 219 233 * @param array $sidebars_widgets Array of 220 234 * @return array 221 235 */ 222 staticfunction prepreview_added_sidebars_widgets( $sidebars_widgets ) {223 foreach ( self::$_customized as $setting_id => $value ) {236 function prepreview_added_sidebars_widgets( $sidebars_widgets ) { 237 foreach ( $this->_customized as $setting_id => $value ) { 224 238 if ( preg_match( '/^sidebars_widgets\[(.+?)\]$/', $setting_id, $matches ) ) { 225 239 $sidebar_id = $matches[1]; 226 240 $sidebars_widgets[$sidebar_id] = $value; … … class WP_Customize_Widgets { 244 258 * @param string $setting_id Widget setting ID. 245 259 * @return array Parsed widget instance. 246 260 */ 247 staticfunction prepreview_added_widget_instance( $instance, $setting_id ) {248 if ( isset( self::$_customized[$setting_id] ) ) {249 $parsed_setting_id = self::parse_widget_setting_id( $setting_id );261 function prepreview_added_widget_instance( $instance, $setting_id ) { 262 if ( isset( $this->_customized[$setting_id] ) ) { 263 $parsed_setting_id = $this->parse_widget_setting_id( $setting_id ); 250 264 $widget_number = $parsed_setting_id['number']; 251 265 252 266 // Single widget … … class WP_Customize_Widgets { 276 290 * @static 277 291 * @access public 278 292 */ 279 staticfunction remove_prepreview_filters() {280 foreach ( self::$_prepreview_added_filters as $prepreview_added_filter ) {293 function remove_prepreview_filters() { 294 foreach ( $this->_prepreview_added_filters as $prepreview_added_filter ) { 281 295 remove_filter( $prepreview_added_filter['hook'], $prepreview_added_filter['function'] ); 282 296 } 283 self::$_prepreview_added_filters = array();297 $this->_prepreview_added_filters = array(); 284 298 } 285 299 286 300 /** … … class WP_Customize_Widgets { 290 304 * @static 291 305 * @access public 292 306 */ 293 staticfunction customize_controls_init() {307 function customize_controls_init() { 294 308 do_action( 'load-widgets.php' ); 295 309 do_action( 'widgets.php' ); 296 310 do_action( 'sidebar_admin_setup' ); … … class WP_Customize_Widgets { 303 317 * @since 3.9.0 304 318 * @static 305 319 * @access public 306 *307 * @param WP_Customize_Manager $wp_customize Customizer instance.308 320 */ 309 static function schedule_customize_register( $wp_customize) {321 function schedule_customize_register() { 310 322 if ( is_admin() ) { // @todo for some reason, $wp_customize->is_preview() is true here? 311 self::customize_register( $wp_customize);323 $this->customize_register(); 312 324 } else { 313 add_action( 'wp', array( __CLASS__, 'customize_register' ) );325 add_action( 'wp', array( $this, 'customize_register' ) ); 314 326 } 315 327 } 316 328 … … class WP_Customize_Widgets { 320 332 * @since 3.9.0 321 333 * @static 322 334 * @access public 323 *324 * @param WP_Customize_Manager $wp_customize Customizer instance.325 335 */ 326 static function customize_register( $wp_customize = null) {336 function customize_register() { 327 337 global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_sidebars; 328 if ( ! ( $wp_customize instanceof WP_Customize_Manager ) ) {329 $wp_customize = $GLOBALS['wp_customize'];330 }331 338 332 339 $sidebars_widgets = array_merge( 333 340 array( 'wp_inactive_widgets' => array() ), … … class WP_Customize_Widgets { 342 349 * since a widget may get suppressed from a sidebar via a plugin (like Widget Visibility). 343 350 */ 344 351 foreach ( array_keys( $wp_registered_widgets ) as $widget_id ) { 345 $setting_id = self::get_setting_id( $widget_id );346 $setting_args = self::get_setting_args( $setting_id );347 $setting_args['sanitize_callback'] = array( __CLASS__, 'sanitize_widget_instance' );348 $setting_args['sanitize_js_callback'] = array( __CLASS__, 'sanitize_widget_js_instance' );349 $ wp_customize->add_setting( $setting_id, $setting_args );352 $setting_id = $this->get_setting_id( $widget_id ); 353 $setting_args = $this->get_setting_args( $setting_id ); 354 $setting_args['sanitize_callback'] = array( $this, 'sanitize_widget_instance' ); 355 $setting_args['sanitize_js_callback'] = array( $this, 'sanitize_widget_js_instance' ); 356 $this->manager->add_setting( $setting_id, $setting_args ); 350 357 $new_setting_ids[] = $setting_id; 351 358 } 352 359 … … class WP_Customize_Widgets { 363 370 */ 364 371 if ( $is_registered_sidebar || $is_inactive_widgets ) { 365 372 $setting_id = sprintf( 'sidebars_widgets[%s]', $sidebar_id ); 366 $setting_args = self::get_setting_args( $setting_id );367 $setting_args['sanitize_callback'] = array( __CLASS__, 'sanitize_sidebar_widgets' );368 $setting_args['sanitize_js_callback'] = array( __CLASS__, 'sanitize_sidebar_widgets_js_instance' );369 $ wp_customize->add_setting( $setting_id, $setting_args );373 $setting_args = $this->get_setting_args( $setting_id ); 374 $setting_args['sanitize_callback'] = array( $this, 'sanitize_sidebar_widgets' ); 375 $setting_args['sanitize_js_callback'] = array( $this, 'sanitize_sidebar_widgets_js_instance' ); 376 $this->manager->add_setting( $setting_id, $setting_args ); 370 377 $new_setting_ids[] = $setting_id; 371 378 372 379 /** … … class WP_Customize_Widgets { 381 388 'priority' => 1000 + array_search( $sidebar_id, array_keys( $wp_registered_sidebars ) ), 382 389 ); 383 390 $section_args = apply_filters( 'customizer_widgets_section_args', $section_args, $section_id, $sidebar_id ); 384 $ wp_customize->add_section( $section_id, $section_args );391 $this->manager->add_section( $section_id, $section_args ); 385 392 386 393 $control = new WP_Widget_Area_Customize_Control( 387 $ wp_customize,394 $this->manager, 388 395 $setting_id, 389 396 array( 390 397 'section' => $section_id, … … class WP_Customize_Widgets { 393 400 ) 394 401 ); 395 402 $new_setting_ids[] = $setting_id; 396 $ wp_customize->add_control( $control );403 $this->manager->add_control( $control ); 397 404 } 398 405 } 399 406 … … class WP_Customize_Widgets { 405 412 continue; 406 413 } 407 414 $registered_widget = $GLOBALS['wp_registered_widgets'][$widget_id]; 408 $setting_id = self::get_setting_id( $widget_id );415 $setting_id = $this->get_setting_id( $widget_id ); 409 416 $id_base = $GLOBALS['wp_registered_widget_controls'][$widget_id]['id_base']; 410 417 assert( false !== is_active_widget( $registered_widget['callback'], $registered_widget['id'], false, false ) ); 411 418 $control = new WP_Widget_Form_Customize_Control( 412 $ wp_customize,419 $this->manager, 413 420 $setting_id, 414 421 array( 415 422 'label' => $registered_widget['name'], … … class WP_Customize_Widgets { 420 427 'priority' => $i, 421 428 'width' => $wp_registered_widget_controls[$widget_id]['width'], 422 429 'height' => $wp_registered_widget_controls[$widget_id]['height'], 423 'is_wide' => self::is_wide_widget( $widget_id ),430 'is_wide' => $this->is_wide_widget( $widget_id ), 424 431 ) 425 432 ); 426 $ wp_customize->add_control( $control );433 $this->manager->add_control( $control ); 427 434 } 428 435 } 429 436 … … class WP_Customize_Widgets { 433 440 */ 434 441 if ( did_action( 'customize_preview_init' ) ) { 435 442 foreach ( $new_setting_ids as $new_setting_id ) { 436 $ wp_customize->get_setting( $new_setting_id )->preview();443 $this->manager->get_setting( $new_setting_id )->preview(); 437 444 } 438 445 } 439 446 440 self::remove_prepreview_filters();447 $this->remove_prepreview_filters(); 441 448 } 442 449 443 450 /** … … class WP_Customize_Widgets { 450 457 * @param string $widget_id Widget ID. 451 458 * @return string Maybe-parsed widget ID. 452 459 */ 453 staticfunction get_setting_id( $widget_id ) {454 $parsed_widget_id = self::parse_widget_id( $widget_id );460 function get_setting_id( $widget_id ) { 461 $parsed_widget_id = $this->parse_widget_id( $widget_id ); 455 462 $setting_id = sprintf( 'widget_%s', $parsed_widget_id['id_base'] ); 456 463 if ( ! is_null( $parsed_widget_id['number'] ) ) { 457 464 $setting_id .= sprintf( '[%d]', $parsed_widget_id['number'] ); … … class WP_Customize_Widgets { 474 481 * @param string $widget_id Widget ID. 475 482 * @return bool Whether or not the widget is a "wide" widget. 476 483 */ 477 staticfunction is_wide_widget( $widget_id ) {484 function is_wide_widget( $widget_id ) { 478 485 global $wp_registered_widget_controls; 479 $parsed_widget_id = self::parse_widget_id( $widget_id );486 $parsed_widget_id = $this->parse_widget_id( $widget_id ); 480 487 $width = $wp_registered_widget_controls[$widget_id]['width']; 481 $is_core = in_array( $parsed_widget_id['id_base'], self::$core_widget_id_bases );488 $is_core = in_array( $parsed_widget_id['id_base'], $this->core_widget_id_bases ); 482 489 $is_wide = ( $width > 250 && ! $is_core ); 483 490 484 491 /** … … class WP_Customize_Widgets { 503 510 * @param string $widget_id Widget ID. 504 511 * @return array Array containing a widget's id_base and number components. 505 512 */ 506 staticfunction parse_widget_id( $widget_id ) {513 function parse_widget_id( $widget_id ) { 507 514 $parsed = array( 508 515 'number' => null, 509 516 'id_base' => null, … … class WP_Customize_Widgets { 529 536 * @return WP_Error|array Array contain a widget's id_base and number components, 530 537 * or a WP_Error object. 531 538 */ 532 staticfunction parse_widget_setting_id( $setting_id ) {539 function parse_widget_setting_id( $setting_id ) { 533 540 if ( ! preg_match( '/^(widget_(.+?))(?:\[(\d+)\])?$/', $setting_id, $matches ) ) { 534 541 return new WP_Error( 'invalid_setting_id', 'Invalid widget setting ID' ); 535 542 } … … class WP_Customize_Widgets { 546 553 * @static 547 554 * @access public 548 555 */ 549 staticfunction customize_controls_enqueue_deps() {556 function customize_controls_enqueue_deps() { 550 557 wp_enqueue_style( 'customize-widgets' ); 551 558 wp_enqueue_script( 'customize-widgets' ); 552 559 553 560 // Export available widgets with control_tpl removed from model 554 561 // since plugins need templates to be in the DOM 555 562 $available_widgets = array(); 556 foreach ( self::get_available_widgets() as $available_widget ) {563 foreach ( $this->get_available_widgets() as $available_widget ) { 557 564 unset( $available_widget['control_tpl'] ); 558 565 $available_widgets[] = $available_widget; 559 566 } … … class WP_Customize_Widgets { 601 608 'save_btn_tooltip' => ( 'Save and preview changes before publishing them.' ), 602 609 'remove_btn_label' => __( 'Remove' ), 603 610 'remove_btn_tooltip' => ( 'Trash widget by moving it to the inactive widgets sidebar.' ), 604 'error' => __( 'An error has occurred. Please reload the page and try again.'),611 'error' => __( 'An error has occurred. Please reload the page and try again.' ), 605 612 ), 606 613 'tpl' => array( 607 614 'widget_reorder_nav' => $widget_reorder_nav_tpl, … … class WP_Customize_Widgets { 626 633 * @static 627 634 * @access public 628 635 */ 629 staticfunction output_widget_control_templates() {636 function output_widget_control_templates() { 630 637 ?> 631 638 <div id="widgets-left"><!-- compatibility with JS which looks for widget templates here --> 632 639 <div id="available-widgets"> 633 640 <div id="available-widgets-filter"> 634 641 <input type="search" placeholder="<?php esc_attr_e( 'Find widgets…' ) ?>"> 635 642 </div> 636 <?php foreach ( self::get_available_widgets() as $available_widget ): ?>643 <?php foreach ( $this->get_available_widgets() as $available_widget ): ?> 637 644 <div id="widget-tpl-<?php echo esc_attr( $available_widget['id'] ) ?>" data-widget-id="<?php echo esc_attr( $available_widget['id'] ) ?>" class="widget-tpl <?php echo esc_attr( $available_widget['id'] ) ?>" tabindex="0"> 638 645 <?php echo $available_widget['control_tpl']; // xss ok ?> 639 646 </div> … … class WP_Customize_Widgets { 654 661 * @param array $overrides Array of setting overrides. 655 662 * @return array Possibly modified setting arguments. 656 663 */ 657 staticfunction get_setting_args( $id, $overrides = array() ) {664 function get_setting_args( $id, $overrides = array() ) { 658 665 $args = array( 659 666 'type' => 'option', 660 667 'capability' => 'edit_theme_options', … … class WP_Customize_Widgets { 677 684 * @param array $widget_ids Array of widget IDs. 678 685 * @return array Array of sanitized widget IDs. 679 686 */ 680 staticfunction sanitize_sidebar_widgets( $widget_ids ) {687 function sanitize_sidebar_widgets( $widget_ids ) { 681 688 global $wp_registered_widgets; 682 689 $widget_ids = array_map( 'strval', (array) $widget_ids ); 683 690 $sanitized_widget_ids = array(); … … class WP_Customize_Widgets { 699 706 * @see wp_list_widgets() 700 707 * @return array 701 708 */ 702 staticfunction get_available_widgets() {709 function get_available_widgets() { 703 710 static $available_widgets = array(); 704 711 if ( ! empty( $available_widgets ) ) { 705 712 return $available_widgets; … … class WP_Customize_Widgets { 709 716 require_once ABSPATH . '/wp-admin/includes/widgets.php'; // for next_widget_id_number() 710 717 711 718 $sort = $wp_registered_widgets; 712 usort( $sort, array( __CLASS__, '_sort_name_callback' ) );719 usort( $sort, array( $this, '_sort_name_callback' ) ); 713 720 $done = array(); 714 721 715 722 foreach ( $sort as $widget ) { … … class WP_Customize_Widgets { 753 760 } 754 761 755 762 $list_widget_controls_args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $args, 1 => $widget['params'][0] ) ); 756 $control_tpl = self::get_widget_control( $list_widget_controls_args );763 $control_tpl = $this->get_widget_control( $list_widget_controls_args ); 757 764 758 765 // The properties here are mapped to the Backbone Widget model 759 766 $available_widget = array_merge( … … class WP_Customize_Widgets { 768 775 'transport' => 'refresh', 769 776 'width' => $wp_registered_widget_controls[$widget['id']]['width'], 770 777 'height' => $wp_registered_widget_controls[$widget['id']]['height'], 771 'is_wide' => self::is_wide_widget( $widget['id'] ),778 'is_wide' => $this->is_wide_widget( $widget['id'] ), 772 779 ) 773 780 ); 774 781 … … class WP_Customize_Widgets { 788 795 * @param array $widget_b The second widget to compare. 789 796 * @return int Reorder position for the current widget comparison. 790 797 */ 791 staticfunction _sort_name_callback( $widget_a, $widget_b ) {798 function _sort_name_callback( $widget_a, $widget_b ) { 792 799 return strnatcasecmp( $widget_a['name'], $widget_b['name'] ); 793 800 } 794 801 … … class WP_Customize_Widgets { 803 810 * @param array $args Widget control arguments. 804 811 * @return string Widget control form HTML markup. 805 812 */ 806 staticfunction get_widget_control( $args ) {813 function get_widget_control( $args ) { 807 814 ob_start(); 808 815 call_user_func_array( 'wp_widget_control', $args ); 809 816 $replacements = array( … … class WP_Customize_Widgets { 822 829 * @static 823 830 * @access public 824 831 */ 825 staticfunction customize_preview_init() {826 add_filter( 'sidebars_widgets', array( __CLASS__, 'preview_sidebars_widgets' ), 1 );827 add_action( 'wp_enqueue_scripts', array( __CLASS__, 'customize_preview_enqueue' ) );828 add_action( 'wp_print_styles', array( __CLASS__, 'inject_preview_css' ), 1 );829 add_action( 'wp_footer', array( __CLASS__, 'export_preview_data' ), 20 );832 function customize_preview_init() { 833 add_filter( 'sidebars_widgets', array( $this, 'preview_sidebars_widgets' ), 1 ); 834 add_action( 'wp_enqueue_scripts', array( $this, 'customize_preview_enqueue' ) ); 835 add_action( 'wp_print_styles', array( $this, 'inject_preview_css' ), 1 ); 836 add_action( 'wp_footer', array( $this, 'export_preview_data' ), 20 ); 830 837 } 831 838 832 839 /** … … class WP_Customize_Widgets { 843 850 * 844 851 * @param array $sidebars_widgets List of widgets for the current sidebar. 845 852 */ 846 staticfunction preview_sidebars_widgets( $sidebars_widgets ) {853 function preview_sidebars_widgets( $sidebars_widgets ) { 847 854 $sidebars_widgets = get_option( 'sidebars_widgets' ); 848 855 unset( $sidebars_widgets['array_version'] ); 849 856 return $sidebars_widgets; … … class WP_Customize_Widgets { 856 863 * @static 857 864 * @access public 858 865 */ 859 staticfunction customize_preview_enqueue() {866 function customize_preview_enqueue() { 860 867 wp_enqueue_script( 'customize-preview-widgets' ); 861 868 } 862 869 863 870 /** 864 871 * Insert default style for highlighted widget at early point so theme … … class WP_Customize_Widgets { 870 877 * 871 878 * @action wp_print_styles 872 879 */ 873 staticfunction inject_preview_css() {880 function inject_preview_css() { 874 881 ?> 875 882 <style> 876 883 .widget-customizer-highlighted-widget { … … class WP_Customize_Widgets { 891 898 * @static 892 899 * @access public 893 900 */ 894 staticfunction export_preview_data() {901 function export_preview_data() { 895 902 // Prepare customizer settings to pass to Javascript. 896 903 $settings = array( 897 'renderedSidebars' => array_fill_keys( array_unique( self::$rendered_sidebars ), true ),898 'renderedWidgets' => array_fill_keys( array_keys( self::$rendered_widgets ), true ),904 'renderedSidebars' => array_fill_keys( array_unique( $this->rendered_sidebars ), true ), 905 'renderedWidgets' => array_fill_keys( array_keys( $this->rendered_widgets ), true ), 899 906 'registeredSidebars' => array_values( $GLOBALS['wp_registered_sidebars'] ), 900 907 'registeredWidgets' => $GLOBALS['wp_registered_widgets'], 901 908 'l10n' => array( … … class WP_Customize_Widgets { 922 929 * 923 930 * @param array $widget Rendered widget to tally. 924 931 */ 925 staticfunction tally_rendered_widgets( $widget ) {926 self::$rendered_widgets[$widget['id']] = true;932 function tally_rendered_widgets( $widget ) { 933 $this->rendered_widgets[$widget['id']] = true; 927 934 } 928 935 929 936 /** … … class WP_Customize_Widgets { 937 944 * @param bool $is_active Whether the sidebar is active. 938 945 * @pasram string $sidebar_id Sidebar ID. 939 946 */ 940 staticfunction tally_sidebars_via_is_active_sidebar_calls( $is_active, $sidebar_id ) {947 function tally_sidebars_via_is_active_sidebar_calls( $is_active, $sidebar_id ) { 941 948 if ( isset( $GLOBALS['wp_registered_sidebars'][$sidebar_id] ) ) { 942 self::$rendered_sidebars[] = $sidebar_id;949 $this->rendered_sidebars[] = $sidebar_id; 943 950 } 944 951 // We may need to force this to true, and also force-true the value for dynamic_sidebar_has_widgets 945 952 // if we want to ensure that there is an area to drop widgets into, if the sidebar is empty. … … class WP_Customize_Widgets { 957 964 * @param bool $has_widgets Whether the current sidebar has widgets. 958 965 * @param string $sidebar_id Sidebar ID. 959 966 */ 960 staticfunction tally_sidebars_via_dynamic_sidebar_calls( $has_widgets, $sidebar_id ) {967 function tally_sidebars_via_dynamic_sidebar_calls( $has_widgets, $sidebar_id ) { 961 968 if ( isset( $GLOBALS['wp_registered_sidebars'][$sidebar_id] ) ) { 962 self::$rendered_sidebars[] = $sidebar_id;969 $this->rendered_sidebars[] = $sidebar_id; 963 970 } 964 971 /* 965 972 * We may need to force this to true, and also force-true the value for is_active_sidebar … … class WP_Customize_Widgets { 983 990 * @param array $instance Widget instance. 984 991 * @return string Widget instance's hash key. 985 992 */ 986 protected staticfunction get_instance_hash_key( $instance ) {993 protected function get_instance_hash_key( $instance ) { 987 994 $hash = md5( AUTH_KEY . serialize( $instance ) ); 988 995 return $hash; 989 996 } … … class WP_Customize_Widgets { 1003 1010 * @param array $value Widget instance to sanitize. 1004 1011 * @return array Sanitized widget instance. 1005 1012 */ 1006 staticfunction sanitize_widget_instance( $value ) {1013 function sanitize_widget_instance( $value ) { 1007 1014 if ( $value === array() ) { 1008 1015 return $value; 1009 1016 } … … class WP_Customize_Widgets { 1025 1032 if ( false === $instance ) { 1026 1033 return null; 1027 1034 } 1028 if ( self::get_instance_hash_key( $instance ) !== $value['instance_hash_key'] ) {1035 if ( $this->get_instance_hash_key( $instance ) !== $value['instance_hash_key'] ) { 1029 1036 return null; 1030 1037 } 1031 1038 return $instance; … … class WP_Customize_Widgets { 1043 1050 * @param array $value Widget instance to convert to JSON. 1044 1051 * @return array JSON-converted widget instance. 1045 1052 */ 1046 staticfunction sanitize_widget_js_instance( $value ) {1053 function sanitize_widget_js_instance( $value ) { 1047 1054 if ( empty( $value['is_widget_customizer_js_value'] ) ) { 1048 1055 $serialized = serialize( $value ); 1049 1056 $value = array( 1050 1057 'encoded_serialized_instance' => base64_encode( $serialized ), 1051 1058 'title' => empty( $value['title'] ) ? '' : $value['title'], 1052 1059 'is_widget_customizer_js_value' => true, 1053 'instance_hash_key' => self::get_instance_hash_key( $value ),1060 'instance_hash_key' => $this->get_instance_hash_key( $value ), 1054 1061 ); 1055 1062 } 1056 1063 return $value; … … class WP_Customize_Widgets { 1067 1074 * @param array $widget_ids List of widget IDs. 1068 1075 * @return array Parsed list of widget IDs. 1069 1076 */ 1070 staticfunction sanitize_sidebar_widgets_js_instance( $widget_ids ) {1077 function sanitize_sidebar_widgets_js_instance( $widget_ids ) { 1071 1078 global $wp_registered_widgets; 1072 1079 $widget_ids = array_values( array_intersect( $widget_ids, array_keys( $wp_registered_widgets ) ) ); 1073 1080 return $widget_ids; … … class WP_Customize_Widgets { 1085 1092 * @param string $widget_id Widget ID. 1086 1093 * @return WP_Error|array Array containing the updated widget information. WP_Error, otherwise. 1087 1094 */ 1088 staticfunction call_widget_update( $widget_id ) {1095 function call_widget_update( $widget_id ) { 1089 1096 global $wp_registered_widget_updates, $wp_registered_widget_controls; 1090 1097 1091 $options_transaction = new Options_Transaction(); 1092 1093 $options_transaction->start(); 1094 $parsed_id = self::parse_widget_id( $widget_id ); 1098 $this->start_capturing_option_updates(); 1099 $parsed_id = $this->parse_widget_id( $widget_id ); 1095 1100 $option_name = 'widget_' . $parsed_id['id_base']; 1096 1101 1097 1102 /* … … class WP_Customize_Widgets { 1100 1105 */ 1101 1106 $added_input_vars = array(); 1102 1107 if ( ! empty( $_POST['sanitized_widget_setting'] ) ) { 1103 $sanitized_widget_setting = json_decode( self::get_post_value( 'sanitized_widget_setting' ), true );1108 $sanitized_widget_setting = json_decode( $this->get_post_value( 'sanitized_widget_setting' ), true ); 1104 1109 if ( empty( $sanitized_widget_setting ) ) { 1105 $ options_transaction->rollback();1110 $this->stop_capturing_option_updates(); 1106 1111 return new WP_Error( 'malformed_data', 'Malformed sanitized_widget_setting' ); 1107 1112 } 1108 1113 1109 $instance = self::sanitize_widget_instance( $sanitized_widget_setting );1114 $instance = $this->sanitize_widget_instance( $sanitized_widget_setting ); 1110 1115 if ( is_null( $instance ) ) { 1111 $ options_transaction->rollback();1116 $this->stop_capturing_option_updates(); 1112 1117 return new WP_Error( 'unsanitary_data', 'Unsanitary sanitized_widget_setting' ); 1113 1118 } 1114 1119 … … class WP_Customize_Widgets { 1143 1148 } 1144 1149 1145 1150 // Make sure the expected option was updated. 1146 if ( 0 !== $ options_transaction->count() ) {1147 if ( count( $options_transaction->options) > 1 ) {1148 $ options_transaction->rollback();1151 if ( 0 !== $this->count_captured_options() ) { 1152 if ( $this->count_captured_options() > 1 ) { 1153 $this->stop_capturing_option_updates(); 1149 1154 return new WP_Error( 'unexpected_update', 'Widget unexpectedly updated more than one option.' ); 1150 1155 } 1151 1156 1152 $updated_option_name = key( $ options_transaction->options);1157 $updated_option_name = key( $this->get_captured_options() ); 1153 1158 if ( $updated_option_name !== $option_name ) { 1154 $ options_transaction->rollback();1159 $this->stop_capturing_option_updates(); 1155 1160 return new WP_Error( 'wrong_option', sprintf( 'Widget updated option "%1$s", but expected "%2$s".', $updated_option_name, $option_name ) ); 1156 1161 } 1157 1162 } … … class WP_Customize_Widgets { 1172 1177 $instance = $option; 1173 1178 } 1174 1179 1175 $ options_transaction->rollback();1180 $this->stop_capturing_option_updates(); 1176 1181 return compact( 'instance', 'form' ); 1177 1182 } 1178 1183 … … class WP_Customize_Widgets { 1189 1194 * @todo Reuse wp_ajax_save_widget now that we have option transactions? 1190 1195 * @action wp_ajax_update_widget 1191 1196 */ 1192 staticfunction wp_ajax_update_widget() {1197 function wp_ajax_update_widget() { 1193 1198 1194 1199 if ( ! is_user_logged_in() ) { 1195 1200 wp_die( 0 ); … … class WP_Customize_Widgets { 1211 1216 do_action( 'widgets.php' ); 1212 1217 do_action( 'sidebar_admin_setup' ); 1213 1218 1214 $widget_id = self::get_post_value( 'widget-id' );1215 $parsed_id = self::parse_widget_id( $widget_id );1219 $widget_id = $this->get_post_value( 'widget-id' ); 1220 $parsed_id = $this->parse_widget_id( $widget_id ); 1216 1221 $id_base = $parsed_id['id_base']; 1217 1222 1218 1223 if ( isset( $_POST['widget-' . $id_base] ) && is_array( $_POST['widget-' . $id_base] ) && preg_match( '/__i__|%i%/', key( $_POST['widget-' . $id_base] ) ) ) { 1219 1224 wp_send_json_error(); 1220 1225 } 1221 1226 1222 $updated_widget = self::call_widget_update( $widget_id ); // => {instance,form}1227 $updated_widget = $this->call_widget_update( $widget_id ); // => {instance,form} 1223 1228 if ( is_wp_error( $updated_widget ) ) { 1224 1229 wp_send_json_error(); 1225 1230 } 1226 1231 1227 1232 $form = $updated_widget['form']; 1228 $instance = self::sanitize_widget_js_instance( $updated_widget['instance'] );1233 $instance = $this->sanitize_widget_js_instance( $updated_widget['instance'] ); 1229 1234 1230 1235 wp_send_json_success( compact( 'form', 'instance' ) ); 1231 1236 } 1232 }1233 1237 1234 class Options_Transaction { 1238 /*************************************************************************** 1239 * Option Update Capturing 1240 ***************************************************************************/ 1235 1241 1236 1242 /** 1237 * @var array $ options values updated while transaction is open1243 * @var array $_captured_options values updated while capturing is happening 1238 1244 */ 1239 public $options = array(); 1240 1241 protected $_ignore_transients = true; 1242 protected $_is_current = false; 1243 protected $_operations = array(); 1244 1245 function __construct( $ignore_transients = true ) { 1246 $this->_ignore_transients = $ignore_transients; 1247 } 1245 protected $_captured_options = array(); 1248 1246 1249 1247 /** 1250 * Determine whether or not the transaction is open 1251 * @return bool 1248 * @var bool $_is_current whether capturing is currently happening or not 1252 1249 */ 1253 function is_current() { 1254 return $this->_is_current; 1255 } 1250 protected $_is_capturing_option_updates = false; 1256 1251 1257 1252 /** 1258 1253 * @param $option_name 1259 1254 * @return boolean 1260 1255 */ 1261 function is_option_ignored( $option_name ) {1262 return ( $this->_ignore_transients &&0 === strpos( $option_name, '_transient_' ) );1256 protected function is_option_capture_ignored( $option_name ) { 1257 return ( 0 === strpos( $option_name, '_transient_' ) ); 1263 1258 } 1264 1259 1265 1260 /** 1266 * Get the number of operations performed in the transaction1267 * @return bool1261 * Get options updated 1262 * @return array 1268 1263 */ 1269 function count() {1270 return count( $this->_operations );1264 protected function get_captured_options() { 1265 return $this->_captured_options; 1271 1266 } 1272 1267 1273 1268 /** 1274 * Start keeping track of changes to options, and cache their new values 1269 * Get the number of options updated 1270 * @return bool 1275 1271 */ 1276 function start() { 1277 $this->_is_current = true; 1278 add_action( 'added_option', array( $this, '_capture_added_option' ), 10, 2 ); 1279 add_action( 'updated_option', array( $this, '_capture_updated_option' ), 10, 3 ); 1280 add_action( 'delete_option', array( $this, '_capture_pre_deleted_option' ), 10, 1 ); 1281 add_action( 'deleted_option', array( $this, '_capture_deleted_option' ), 10, 1 ); 1272 protected function count_captured_options() { 1273 return count( $this->_captured_options ); 1282 1274 } 1283 1275 1284 1276 /** 1285 * @action added_option 1286 * @param $option_name 1287 * @param $new_value 1277 * Start keeping track of changes to options, and cache their new values 1288 1278 */ 1289 function _capture_added_option( $option_name, $new_value) {1290 if ( $this-> is_option_ignored( $option_name )) {1279 protected function start_capturing_option_updates() { 1280 if ( $this->_is_capturing_option_updates ) { 1291 1281 return; 1292 1282 } 1293 $this->options[$option_name] = $new_value; 1294 $ operation = 'add';1295 $this->_operations[] = compact( 'operation', 'option_name', 'new_value');1283 1284 $this->_is_capturing_option_updates = true; 1285 add_filter( 'pre_update_option', array( $this, '_capture_filter_pre_update_option' ), 10, 3 ); 1296 1286 } 1297 1287 1298 1288 /** 1299 * @action updated_option 1289 * @access private 1290 * @param mixed $new_value 1300 1291 * @param string $option_name 1301 1292 * @param mixed $old_value 1302 * @ param mixed $new_value1293 * @return mixed 1303 1294 */ 1304 function _capture_ updated_option( $option_name, $old_value, $new_value ) {1305 if ( $this->is_option_ ignored( $option_name ) ) {1295 function _capture_filter_pre_update_option( $new_value, $option_name, $old_value ) { 1296 if ( $this->is_option_capture_ignored( $option_name ) ) { 1306 1297 return; 1307 1298 } 1308 $this->options[$option_name] = $new_value;1309 $operation = 'update';1310 $this->_operations[] = compact( 'operation', 'option_name', 'old_value', 'new_value' );1311 }1312 1313 protected $_pending_delete_option_autoload;1314 protected $_pending_delete_option_value;1315 1299 1316 /** 1317 * It's too bad the old_value and autoload aren't passed into the deleted_option action 1318 * @action delete_option 1319 * @param string $option_name 1320 */ 1321 function _capture_pre_deleted_option( $option_name ) { 1322 if ( $this->is_option_ignored( $option_name ) ) { 1323 return; 1300 if ( ! isset( $this->_captured_options[$option_name] ) ) { 1301 add_filter( "pre_option_{$option_name}", array( $this, '_capture_filter_pre_get_option' ) ); 1324 1302 } 1325 global $wpdb; 1326 $ autoload = $wpdb->get_var( $wpdb->prepare( "SELECT autoload FROM $wpdb->options WHERE option_name = %s", $option_name ) ); // db call ok; no-cache ok1327 $this->_pending_delete_option_autoload = $autoload; 1328 $this->_pending_delete_option_value = get_option( $option_name );1303 1304 $this->_captured_options[$option_name] = $new_value; 1305 1306 return $old_value; 1329 1307 } 1330 1308 1331 1309 /** 1332 * @action deleted_option 1333 * @param string $option_name 1310 * @access private 1311 * @param mixed $value 1312 * @return mixed 1334 1313 */ 1335 function _capture_deleted_option( $option_name ) { 1336 if ( $this->is_option_ignored( $option_name ) ) { 1337 return; 1314 function _capture_filter_pre_get_option( $value ) { 1315 $option_name = preg_replace( '/^pre_option_/', '', current_filter() ); 1316 if ( isset( $this->_captured_options[$option_name] ) ) { 1317 $value = $this->_captured_options[$option_name]; 1318 $value = apply_filters( 'option_' . $option_name, $value ); 1338 1319 } 1339 unset( $this->options[$option_name] ); 1340 $operation = 'delete'; 1341 $old_value = $this->_pending_delete_option_value; 1342 $autoload = $this->_pending_delete_option_autoload; 1343 $this->_operations[] = compact( 'operation', 'option_name', 'old_value', 'autoload' ); 1320 1321 return $value; 1344 1322 } 1345 1323 1346 1324 /** 1347 1325 * Undo any changes to the options since start() was called 1348 1326 */ 1349 function rollback() { 1350 remove_action( 'updated_option', array( $this, '_capture_updated_option' ), 10, 3 ); 1351 remove_action( 'added_option', array( $this, '_capture_added_option' ), 10, 2 ); 1352 remove_action( 'delete_option', array( $this, '_capture_pre_deleted_option' ), 10, 1 ); 1353 remove_action( 'deleted_option', array( $this, '_capture_deleted_option' ), 10, 1 ); 1354 while ( 0 !== count( $this->_operations ) ) { 1355 $option_operation = array_pop( $this->_operations ); 1356 if ( 'add' === $option_operation['operation'] ) { 1357 delete_option( $option_operation['option_name'] ); 1358 } 1359 else if ( 'delete' === $option_operation['operation'] ) { 1360 add_option( $option_operation['option_name'], $option_operation['old_value'], null, $option_operation['autoload'] ); 1361 } 1362 else if ( 'update' === $option_operation['operation'] ) { 1363 update_option( $option_operation['option_name'], $option_operation['old_value'] ); 1364 } 1327 protected function stop_capturing_option_updates() { 1328 if ( ! $this->_is_capturing_option_updates ) { 1329 return; 1365 1330 } 1366 $this->_is_current = false; 1331 1332 remove_filter( '_capture_filter_pre_update_option', array( $this, '_capture_filter_pre_update_option' ), 10, 3 ); 1333 foreach ( array_keys( $this->_captured_options ) as $option_name ) { 1334 remove_filter( "pre_option_{$option_name}", array( $this, '_capture_filter_pre_get_option' ) ); 1335 } 1336 1337 $this->_captured_options = array(); 1338 $this->_is_capturing_option_updates = false; 1367 1339 } 1368 1340 } -
src/wp-includes/option.php
diff --git src/wp-includes/option.php src/wp-includes/option.php index 0091c12..4cc8c28 100644
function update_option( $option, $value ) { 255 255 */ 256 256 $value = apply_filters( 'pre_update_option_' . $option, $value, $old_value ); 257 257 258 /** 259 * Filter an option before its value is (maybe) serialized and updated. 260 * 261 * @since 3.9.0 262 * 263 * @param mixed $value The new, unserialized option value. 264 * @param string $option Name of the option. 265 * @param mixed $old_value The old option value. 266 */ 267 $value = apply_filters( 'pre_update_option', $value, $option, $old_value ); 268 258 269 // If the new and old values are the same, no need to update. 259 270 if ( $value === $old_value ) 260 271 return false;