diff --git src/wp-includes/class-wp-customize-manager.php src/wp-includes/class-wp-customize-manager.php
index baa50df..50ee7f2 100644
|
|
|
final class WP_Customize_Manager { |
| 628 | 628 | public function set_post_value( $setting_id, $value ) { |
| 629 | 629 | $this->unsanitized_post_values(); |
| 630 | 630 | $this->_post_values[ $setting_id ] = $value; |
| | 631 | |
| | 632 | /** |
| | 633 | * Announce when a setting's unsanitized post value has been set. |
| | 634 | * |
| | 635 | * Fires when the {@see WP_Customize_Manager::set_post_value()} method is called. |
| | 636 | * |
| | 637 | * This is useful for <code>WP_Customize_Setting</code> instances to watch |
| | 638 | * in order to update a cached previewed value. |
| | 639 | * |
| | 640 | * @since 4.4.0 |
| | 641 | * |
| | 642 | * @param string $setting_id Setting ID. |
| | 643 | * @param mixed $value Unsanitized setting post value. |
| | 644 | * @param WP_Customize_Manager $this WP_Customize_Manager instance. |
| | 645 | */ |
| | 646 | do_action( 'customize_post_value_set', $setting_id, $value, $this ); |
| | 647 | |
| | 648 | /** |
| | 649 | * Announce when a specific setting's unsanitized post value has been set. |
| | 650 | * |
| | 651 | * Fires when the {@see WP_Customize_Manager::set_post_value()} method is called. |
| | 652 | * |
| | 653 | * The dynamic portion of the hook name, `$setting_id`, refers to the setting ID. |
| | 654 | * |
| | 655 | * @since 4.4.0 |
| | 656 | * |
| | 657 | * @param mixed $value Unsanitized setting post value. |
| | 658 | * @param WP_Customize_Manager $this WP_Customize_Manager instance. |
| | 659 | */ |
| | 660 | do_action( "customize_post_value_set_{$setting_id}", $value, $this ); |
| 631 | 661 | } |
| 632 | 662 | |
| 633 | 663 | /** |
diff --git src/wp-includes/class-wp-customize-setting.php src/wp-includes/class-wp-customize-setting.php
index 12f76d4..230fe6f 100644
|
|
|
class WP_Customize_Setting { |
| 82 | 82 | protected $id_data = array(); |
| 83 | 83 | |
| 84 | 84 | /** |
| | 85 | * Whether or not preview() was called. |
| | 86 | * |
| | 87 | * @since 4.4.0 |
| | 88 | * @access protected |
| | 89 | * @var bool |
| | 90 | */ |
| | 91 | protected $is_previewed = false; |
| | 92 | |
| | 93 | /** |
| 85 | 94 | * Cache of multidimensional values to improve performance. |
| 86 | 95 | * |
| 87 | 96 | * @since 4.4.0 |
| … |
… |
class WP_Customize_Setting { |
| 183 | 192 | self::$aggregated_multidimensionals[ $this->type ] = array(); |
| 184 | 193 | } |
| 185 | 194 | if ( ! isset( self::$aggregated_multidimensionals[ $this->type ][ $id_base ] ) ) { |
| | 195 | // Only add one action per multidimensional value. |
| | 196 | add_action( 'customize_post_value_set', array( $this, '_clear_aggregated_multidimensional_perview_applied_flag' ) ); |
| | 197 | |
| 186 | 198 | self::$aggregated_multidimensionals[ $this->type ][ $id_base ] = array( |
| 187 | 199 | 'previewed_instances' => array(), // Calling preview() will add the $setting to the array. |
| 188 | 200 | 'preview_applied_instances' => array(), // Flags for which settings have had their values applied. |
| … |
… |
class WP_Customize_Setting { |
| 245 | 257 | if ( ! isset( $this->_previewed_blog_id ) ) { |
| 246 | 258 | $this->_previewed_blog_id = get_current_blog_id(); |
| 247 | 259 | } |
| | 260 | |
| | 261 | // Prevent re-previewing an already-previewed setting. |
| | 262 | if ( $this->is_previewed ) { |
| | 263 | return true; |
| | 264 | } |
| | 265 | |
| 248 | 266 | $id_base = $this->id_data['base']; |
| 249 | 267 | $is_multidimensional = ! empty( $this->id_data['keys'] ); |
| 250 | 268 | $multidimensional_filter = array( $this, '_multidimensional_preview_filter' ); |
| … |
… |
class WP_Customize_Setting { |
| 273 | 291 | $needs_preview = ( $undefined === $value ); // Because the default needs to be supplied. |
| 274 | 292 | } |
| 275 | 293 | |
| | 294 | // If the setting does not need previewing now, defer to when it has a value to preview. |
| 276 | 295 | if ( ! $needs_preview ) { |
| | 296 | if ( ! has_action( "customize_post_value_set_{$this->id}", array( $this, 'preview' ) ) ) { |
| | 297 | add_action( "customize_post_value_set_{$this->id}", array( $this, 'preview' ) ); |
| | 298 | } |
| 277 | 299 | return false; |
| 278 | 300 | } |
| 279 | 301 | |
| … |
… |
class WP_Customize_Setting { |
| 327 | 349 | */ |
| 328 | 350 | do_action( "customize_preview_{$this->type}", $this ); |
| 329 | 351 | } |
| | 352 | |
| | 353 | $this->is_previewed = true; |
| | 354 | |
| 330 | 355 | return true; |
| 331 | 356 | } |
| 332 | 357 | |
| 333 | 358 | /** |
| | 359 | * Clear out the previewed-applied flag for a multidimensional-aggregated value whenever its post value is updated. |
| | 360 | * |
| | 361 | * This ensures that the new value will get sanitized and used the next time |
| | 362 | * that <code>WP_Customize_Setting::_multidimensional_preview_filter()</code> |
| | 363 | * is called for this setting. |
| | 364 | * |
| | 365 | * @access private |
| | 366 | * @see WP_Customize_Manager::set_post_value() |
| | 367 | * @see WP_Customize_Setting::_multidimensional_preview_filter() |
| | 368 | * |
| | 369 | * @param string $setting_id Setting ID. |
| | 370 | */ |
| | 371 | final public function _clear_aggregated_multidimensional_perview_applied_flag( $setting_id ) { |
| | 372 | if ( 0 === strpos( $setting_id, $this->id_data['base'] . '[' ) ) { |
| | 373 | unset( self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['preview_applied_instances'][ $setting_id ] ); |
| | 374 | } |
| | 375 | } |
| | 376 | |
| | 377 | /** |
| 334 | 378 | * Callback function to filter non-multidimensional theme mods and options. |
| 335 | 379 | * |
| 336 | 380 | * If switch_to_blog() was called after the preview() method, and the current |
| … |
… |
class WP_Customize_Setting { |
| 369 | 413 | * the first setting previewed will be used to apply the values for the others. |
| 370 | 414 | * |
| 371 | 415 | * @since 4.4.0 |
| 372 | | * @access public |
| | 416 | * @access private |
| 373 | 417 | * |
| 374 | 418 | * @see WP_Customize_Setting::$aggregated_multidimensionals |
| 375 | 419 | * @param mixed $original Original root value. |
| 376 | 420 | * @return mixed New or old value. |
| 377 | 421 | */ |
| 378 | | public function _multidimensional_preview_filter( $original ) { |
| | 422 | final public function _multidimensional_preview_filter( $original ) { |
| | 423 | $id_base = $this->id_data['base']; |
| | 424 | |
| 379 | 425 | if ( ! $this->is_current_blog_previewed() ) { |
| 380 | 426 | return $original; |
| 381 | 427 | } |
| 382 | 428 | |
| 383 | | $id_base = $this->id_data['base']; |
| 384 | | |
| 385 | 429 | // If no settings have been previewed yet (which should not be the case, since $this is), just pass through the original value. |
| 386 | 430 | if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) { |
| 387 | 431 | return $original; |
| 388 | 432 | } |
| 389 | 433 | |
| 390 | 434 | foreach ( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] as $previewed_setting ) { |
| 391 | | // Skip applying previewed value for any settings that have already been applied. |
| 392 | | if ( ! empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['preview_applied_instances'][ $previewed_setting->id ] ) ) { |
| 393 | | continue; |
| 394 | | } |
| | 435 | $previewed_setting->apply_multidimensional_preview_value(); |
| | 436 | } |
| 395 | 437 | |
| 396 | | // Do the replacements of the posted/default sub value into the root value. |
| 397 | | $value = $previewed_setting->post_value( $previewed_setting->default ); |
| 398 | | $root = self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['root_value']; |
| 399 | | $root = $previewed_setting->multidimensional_replace( $root, $previewed_setting->id_data['keys'], $value ); |
| 400 | | self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['root_value'] = $root; |
| | 438 | return self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value']; |
| | 439 | } |
| 401 | 440 | |
| 402 | | // Mark this setting having been applied so that it will be skipped when the filter is called again. |
| 403 | | self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['preview_applied_instances'][ $previewed_setting->id ] = true; |
| | 441 | /** |
| | 442 | * Ensure that a multidimensional setting's post value is applied to preview. |
| | 443 | * |
| | 444 | * @since 4.4.0 |
| | 445 | * @access private |
| | 446 | * |
| | 447 | * @see WP_Customize_Setting::_multidimensional_preview_filter() |
| | 448 | */ |
| | 449 | final protected function apply_multidimensional_preview_value() { |
| | 450 | $id_base = $this->id_data['base']; |
| | 451 | |
| | 452 | // Skip applying previewed value for any settings that have already been applied. |
| | 453 | if ( ! empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['preview_applied_instances'][ $this->id ] ) ) { |
| | 454 | return; |
| 404 | 455 | } |
| 405 | 456 | |
| 406 | | return self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value']; |
| | 457 | // Do the replacements of the posted/default sub value into the root value. |
| | 458 | $value = $this->post_value( $this->default ); |
| | 459 | $root = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value']; |
| | 460 | $root = $this->multidimensional_replace( $root, $this->id_data['keys'], $value ); |
| | 461 | self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'] = $root; |
| | 462 | |
| | 463 | // Mark this setting having been applied so that it will be skipped when the filter is called again. |
| | 464 | self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['preview_applied_instances'][ $this->id ] = true; |
| 407 | 465 | } |
| 408 | 466 | |
| 409 | 467 | /** |
| … |
… |
class WP_Customize_Setting { |
| 610 | 668 | */ |
| 611 | 669 | $value = apply_filters( "customize_value_{$id_base}", $value ); |
| 612 | 670 | } else if ( $this->is_multidimensional_aggregated ) { |
| | 671 | $undefined = new stdClass(); |
| | 672 | if ( $this->is_previewed && $undefined !== $this->post_value( $undefined ) ) { |
| | 673 | $this->apply_multidimensional_preview_value(); |
| | 674 | } |
| 613 | 675 | $root_value = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value']; |
| 614 | 676 | $value = $this->multidimensional_get( $root_value, $this->id_data['keys'], $this->default ); |
| 615 | 677 | } else { |
diff --git src/wp-includes/class-wp-customize-widgets.php src/wp-includes/class-wp-customize-widgets.php
index 6ee6942..7639d50 100644
|
|
|
final class WP_Customize_Widgets { |
| 1380 | 1380 | * in place from WP_Customize_Setting::preview() will use this value |
| 1381 | 1381 | * instead of the default widget instance value (an empty array). |
| 1382 | 1382 | */ |
| 1383 | | $this->manager->set_post_value( $setting_id, $instance ); |
| | 1383 | $this->manager->set_post_value( $setting_id, $this->sanitize_widget_js_instance( $instance ) ); |
| 1384 | 1384 | |
| 1385 | 1385 | // Obtain the widget control with the updated instance in place. |
| 1386 | 1386 | ob_start(); |
diff --git src/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php src/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php
index 2fa0b5c..073423e 100644
|
|
|
class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting { |
| 120 | 120 | public $original_nav_menu_term_id; |
| 121 | 121 | |
| 122 | 122 | /** |
| 123 | | * Whether or not preview() was called. |
| 124 | | * |
| 125 | | * @since 4.3.0 |
| 126 | | * @access protected |
| 127 | | * @var bool |
| 128 | | */ |
| 129 | | protected $is_previewed = false; |
| 130 | | |
| 131 | | /** |
| 132 | 123 | * Whether or not update() was called. |
| 133 | 124 | * |
| 134 | 125 | * @since 4.3.0 |
diff --git src/wp-includes/customize/class-wp-customize-nav-menu-setting.php src/wp-includes/customize/class-wp-customize-nav-menu-setting.php
index 766099e..5562a8d 100644
|
|
|
class WP_Customize_Nav_Menu_Setting extends WP_Customize_Setting { |
| 89 | 89 | public $previous_term_id; |
| 90 | 90 | |
| 91 | 91 | /** |
| 92 | | * Whether or not preview() was called. |
| 93 | | * |
| 94 | | * @since 4.3.0 |
| 95 | | * @access protected |
| 96 | | * @var bool |
| 97 | | */ |
| 98 | | protected $is_previewed = false; |
| 99 | | |
| 100 | | /** |
| 101 | 92 | * Whether or not update() was called. |
| 102 | 93 | * |
| 103 | 94 | * @since 4.3.0 |
diff --git tests/phpunit/tests/customize/setting.php tests/phpunit/tests/customize/setting.php
index da789b1..939a7ff 100644
|
|
|
class Tests_WP_Customize_Setting extends WP_UnitTestCase { |
| 160 | 160 | $_POST['customized'] = wp_slash( wp_json_encode( $this->post_data_overrides ) ); |
| 161 | 161 | |
| 162 | 162 | foreach ( $this->standard_type_configs as $type => $type_options ) { |
| 163 | | // Multidimensional: See what effect the preview filter has on a non-existent setting (default value should be seen) |
| | 163 | // Multidimensional: See what effect the preview filter has on a non-existent setting (default value should be seen). |
| 164 | 164 | $base_name = "unset_{$type}_multi"; |
| 165 | 165 | $name = $base_name . '[foo]'; |
| 166 | 166 | $default = "default_value_{$name}"; |
| 167 | 167 | $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) ); |
| 168 | 168 | $this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $base_name, $this->undefined ) ); |
| 169 | 169 | $this->assertEquals( $default, $setting->value() ); |
| 170 | | $this->assertTrue( $setting->preview() ); |
| | 170 | $this->assertTrue( $setting->preview(), $setting->id ); |
| 171 | 171 | $base_value = call_user_func( $type_options['getter'], $base_name, $this->undefined ); |
| 172 | 172 | $this->assertArrayHasKey( 'foo', $base_value ); |
| 173 | 173 | $this->assertEquals( $default, $base_value['foo'] ); |
| 174 | 174 | |
| 175 | | // Multidimensional: See what effect the preview has on an extant setting (default value should not be seen) |
| | 175 | // Multidimensional: See what effect the preview has on an extant setting (default value should not be seen). |
| 176 | 176 | $base_name = "set_{$type}_multi"; |
| 177 | 177 | $name = $base_name . '[foo]'; |
| 178 | 178 | $default = "default_value_{$name}"; |
| … |
… |
class Tests_WP_Customize_Setting extends WP_UnitTestCase { |
| 184 | 184 | $this->assertEquals( $initial_value, $base_value['foo'] ); |
| 185 | 185 | $this->assertEquals( $initial_value, $setting->value() ); |
| 186 | 186 | $setting->preview(); |
| 187 | | $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods) |
| 188 | | $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods) |
| | 187 | $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods). |
| | 188 | $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods). |
| 189 | 189 | $base_value = call_user_func( $type_options['getter'], $base_name, array() ); |
| 190 | 190 | $this->assertEquals( $initial_value, $base_value['foo'] ); |
| 191 | 191 | $this->assertEquals( $initial_value, $setting->value() ); |
| 192 | 192 | |
| 193 | | // Multidimensional: Test unset setting being overridden by a post value |
| | 193 | // Multidimensional: Test unset setting being overridden by a post value. |
| 194 | 194 | $base_name = "unset_{$type}_multi_overridden"; |
| 195 | 195 | $name = $base_name . '[foo]'; |
| 196 | 196 | $default = "default_value_{$name}"; |
| … |
… |
class Tests_WP_Customize_Setting extends WP_UnitTestCase { |
| 198 | 198 | $this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $base_name, $this->undefined ) ); |
| 199 | 199 | $this->assertEquals( $default, $setting->value() ); |
| 200 | 200 | $setting->preview(); |
| 201 | | $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods) |
| 202 | | $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods) |
| | 201 | $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods). |
| | 202 | $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods). |
| 203 | 203 | $base_value = call_user_func( $type_options['getter'], $base_name, $this->undefined ); |
| 204 | 204 | $this->assertArrayHasKey( 'foo', $base_value ); |
| 205 | 205 | $this->assertEquals( $this->post_data_overrides[ $name ], $base_value['foo'] ); |
| 206 | 206 | |
| 207 | | // Multidimemsional: Test set setting being overridden by a post value |
| | 207 | // Multidimensional: Test set setting being overridden by a post value. |
| 208 | 208 | $base_name = "set_{$type}_multi_overridden"; |
| 209 | 209 | $name = $base_name . '[foo]'; |
| 210 | 210 | $default = "default_value_{$name}"; |
| … |
… |
class Tests_WP_Customize_Setting extends WP_UnitTestCase { |
| 221 | 221 | $this->assertEquals( $base_initial_value['bar'], $getter['bar'] ); |
| 222 | 222 | $this->assertEquals( $initial_value, $setting->value() ); |
| 223 | 223 | $setting->preview(); |
| 224 | | $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods) |
| 225 | | $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods) |
| | 224 | $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods). |
| | 225 | $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods). |
| 226 | 226 | $base_value = call_user_func( $type_options['getter'], $base_name, $this->undefined ); |
| 227 | 227 | $this->assertArrayHasKey( 'foo', $base_value ); |
| 228 | 228 | $this->assertEquals( $this->post_data_overrides[ $name ], $base_value['foo'] ); |