Ticket #32103: 32103.wip2.diff
| File 32103.wip2.diff, 30.7 KB (added by , 10 years ago) |
|---|
-
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 1505b60..e07bb8a 100644
final class WP_Customize_Manager { 899 899 do_action( 'customize_save', $this ); 900 900 901 901 foreach ( $this->settings as $setting ) { 902 /* 903 * Note that aggregated multidimensional settings will only be 904 * prepared for saving with this call. They will actually be saved 905 * in the finalize_multidimensional_update() call below. 906 */ 902 907 $setting->save(); 903 908 } 909 foreach ( $this->settings as $setting ) { 910 $setting->finalize_multidimensional_update(); 911 } 904 912 905 913 /** 906 914 * Fires after Customize settings have been saved. -
src/wp-includes/class-wp-customize-setting.php
diff --git src/wp-includes/class-wp-customize-setting.php src/wp-includes/class-wp-customize-setting.php index 98f37f9..68e2aa5 100644
class WP_Customize_Setting { 82 82 protected $id_data = array(); 83 83 84 84 /** 85 * Cache of multidimensional values to improve performance. 86 * 87 * @since 4.4.0 88 * @access protected 89 * @var array 90 * @static 91 */ 92 protected static $aggregated_multidimensionals = array(); 93 94 /** 95 * Whether the multidimensional setting is aggregated. 96 * 97 * @todo Do we need this? We can look at $aggregated_multidimensionals anyway 98 * @since 4.4.0 99 * @access protected 100 * @var bool 101 */ 102 protected $is_multidimensional_aggregated = false; 103 104 /** 85 105 * Constructor. 86 106 * 87 107 * Any supplied $args override class property defaults. … … class WP_Customize_Setting { 96 116 public function __construct( $manager, $id, $args = array() ) { 97 117 $keys = array_keys( get_object_vars( $this ) ); 98 118 foreach ( $keys as $key ) { 99 if ( isset( $args[ $key ] ) ) 119 if ( isset( $args[ $key ] ) ) { 100 120 $this->$key = $args[ $key ]; 121 } 101 122 } 102 123 103 124 $this->manager = $manager; 104 125 $this->id = $id; 105 126 106 127 // Parse the ID for array keys. 107 $this->id_data[ 'keys'] = preg_split( '/\[/', str_replace( ']', '', $this->id ) );108 $this->id_data[ 'base' ] = array_shift( $this->id_data[ 'keys'] );128 $this->id_data['keys'] = preg_split( '/\[/', str_replace( ']', '', $this->id ) ); 129 $this->id_data['base'] = array_shift( $this->id_data['keys'] ); 109 130 110 131 // Rebuild the ID. 111 132 $this->id = $this->id_data[ 'base' ]; 112 if ( ! empty( $this->id_data[ 'keys' ] ) ) 113 $this->id .= '[' . implode( '][', $this->id_data[ 'keys' ] ) . ']'; 133 if ( ! empty( $this->id_data[ 'keys' ] ) ) { 134 $this->id .= '[' . implode( '][', $this->id_data['keys'] ) . ']'; 135 } 114 136 115 if ( $this->sanitize_callback ) 137 if ( $this->sanitize_callback ) { 116 138 add_filter( "customize_sanitize_{$this->id}", $this->sanitize_callback, 10, 2 ); 117 118 if ( $this->sanitize_js_callback ) 139 } 140 if ( $this->sanitize_js_callback ) { 119 141 add_filter( "customize_sanitize_js_{$this->id}", $this->sanitize_js_callback, 10, 2 ); 142 } 143 144 if ( 'option' === $this->type || 'theme_mod' === $this->type ) { 145 // Other setting types can opt-in to aggregate multidimensional explicitly. 146 $this->aggregate_multidimensional(); 147 } 148 } 149 150 /** 151 * Get parsed ID data for multidimensional setting. 152 * 153 * @since 4.4.0 154 * @access public 155 * 156 * @return array { 157 * ID data for multidimensional setting. 158 * 159 * @type string $base ID base 160 * @type array $keys Keys for multidimensional array. 161 * } 162 */ 163 final public function id_data() { 164 return $this->id_data; 165 } 166 167 /** 168 * Set up the setting for aggregated multidimensional values. 169 * 170 * When a multidimensional setting gets aggregated, all of its preview and update 171 * calls get combined into one call, greatly improving performance. 172 * 173 * @since 4.4.0 174 * @access protected 175 */ 176 protected function aggregate_multidimensional() { 177 if ( empty( $this->id_data['keys'] ) ) { 178 return; 179 } 180 181 $id_base = $this->id_data['base']; 182 if ( ! isset( self::$aggregated_multidimensionals[ $this->type ] ) ) { 183 self::$aggregated_multidimensionals[ $this->type ] = array(); 184 } 185 if ( ! isset( self::$aggregated_multidimensionals[ $this->type ][ $id_base ] ) ) { 186 self::$aggregated_multidimensionals[ $this->type ][ $id_base ] = array( 187 'previewed_instances' => array(), // Calling preview() will add the $setting to the array. 188 'preview_applied_instances' => array(), // Flags for which settings have had their values applied. 189 'value_inspected_instances' => array(), // Keep track of which settings have had value() called. 190 191 'root_value' => $this->get_root_value( array() ), // Root value for initial state, manipulated by preview and update calls. 192 'previewed_value' => null, // Root value which has multidimensional replacements applied for each previewed instance. 193 'updated_value' => null, // Each call to update() will do the multidimensional replacements on $this->get_root_value(). 194 ); 195 } 196 $this->is_multidimensional_aggregated = true; 120 197 } 121 198 122 199 /** … … class WP_Customize_Setting { 153 230 protected $_original_value; 154 231 155 232 /** 156 * Set up filters for the setting so that the preview request 157 * will render the drafted changes. 233 * Add filters to supply the setting's value when accessed. 234 * 235 * If the setting already has a pre-existing value and there is no incoming 236 * post value for the setting, then this method will short-circuit since 237 * there is no change to preview. 158 238 * 159 239 * @since 3.4.0 240 * @since 4.4.0 Added boolean return value. 241 * @access public 242 * 243 * @return bool False when preview short-circuits due no change needing to be previewed. 160 244 */ 161 245 public function preview() { 162 if ( ! isset( $this->_original_value ) ) {163 $this->_original_value = $this->value();164 }165 246 if ( ! isset( $this->_previewed_blog_id ) ) { 166 247 $this->_previewed_blog_id = get_current_blog_id(); 167 248 } 249 $id_base = $this->id_data['base']; 250 $is_multidimensional = ! empty( $this->id_data['keys'] ); 251 $multidimensional_filter = array( $this, '_multidimensional_preview_filter' ); 252 253 /* 254 * Check if the setting has a pre-existing value (an isset check), 255 * and if doesn't have any incoming post value. If both checks are true, 256 * then the preview short-circuits because there is nothing that needs 257 * to be previewed. 258 */ 259 $undefined = new stdClass(); 260 $needs_preview = ( $undefined !== $this->post_value( $undefined ) ); 261 $value = null; 262 263 // Since no post value was defined, check if we have an initial value set. 264 if ( ! $needs_preview ) { 265 if ( $this->is_multidimensional_aggregated ) { 266 $root = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value']; 267 $value = $this->multidimensional_get( $root, $this->id_data['keys'], $undefined ); 268 } else { 269 $default = $this->default; 270 $this->default = $undefined; // Temporarily set default to undefined so we can detect if existing value is set. 271 $value = $this->value(); 272 $this->default = $default; 273 } 274 $needs_preview = ( $undefined === $value ); // Because the default needs to be supplied. 275 } 276 277 if ( ! $needs_preview ) { 278 return false; 279 } 168 280 169 switch( $this->type ) { 281 // $this->_original_value = ( $value ?: $default ); 282 switch ( $this->type ) { 170 283 case 'theme_mod' : 171 add_filter( 'theme_mod_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) ); 284 if ( ! $is_multidimensional ) { 285 add_filter( "theme_mod_{$id_base}", array( $this, '_preview_filter' ) ); 286 } else { 287 if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) { 288 // Only add this filter once for this ID base. 289 add_filter( "theme_mod_{$id_base}", $multidimensional_filter ); 290 } 291 self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'][ $this->id ] = $this; 292 } 172 293 break; 173 294 case 'option' : 174 if ( empty( $this->id_data[ 'keys' ] ) ) 175 add_filter( 'pre_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) ); 176 else { 177 add_filter( 'option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) ); 178 add_filter( 'default_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) ); 295 if ( ! $is_multidimensional ) { 296 add_filter( "pre_option_{$id_base}", array( $this, '_preview_filter' ) ); 297 } else { 298 if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) { 299 // Only add these filters once for this ID base. 300 add_filter( "option_{$id_base}", $multidimensional_filter ); 301 add_filter( "default_option_{$id_base}", $multidimensional_filter ); 302 } 303 self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'][ $this->id ] = $this; 179 304 } 180 305 break; 181 306 default : … … class WP_Customize_Setting { 204 329 */ 205 330 do_action( "customize_preview_{$this->type}", $this ); 206 331 } 332 return true; 207 333 } 208 334 209 335 /** 210 * Callback function to filter thetheme mods and options.336 * Callback function to filter non-multidimensional theme mods and options. 211 337 * 212 338 * If switch_to_blog() was called after the preview() method, and the current 213 339 * blog is now not the same blog, then this method does a no-op and returns 214 340 * the original value. 215 341 * 216 342 * @since 3.4.0 217 * @uses WP_Customize_Setting::multidimensional_replace()218 343 * 219 344 * @param mixed $original Old value. 220 345 * @return mixed New or old value. … … class WP_Customize_Setting { 224 349 return $original; 225 350 } 226 351 227 $undefined = new stdClass(); // symbol hack352 $undefined = new stdClass(); // Symbol hack. 228 353 $post_value = $this->post_value( $undefined ); 229 if ( $undefined === $post_value ) { 230 $value = $this->_original_value; 231 } else { 354 if ( $undefined !== $post_value ) { 232 355 $value = $post_value; 356 } else { 357 $value = $original; 358 } 359 return $value; 360 } 361 362 /** 363 * Callback function to filter multidimensional theme mods and options. 364 * 365 * For all multidimensional settings of a given type, the preview filter for 366 * the first setting previewed will be used to apply the values for the others. 367 * 368 * @since 4.4.0 369 * @access public 370 * 371 * @see WP_Customize_Setting::$aggregated_multidimensionals 372 * @param mixed $original Original root value. 373 * @return mixed New or old value. 374 */ 375 public function _multidimensional_preview_filter( $original ) { 376 if ( ! $this->is_current_blog_previewed() ) { 377 return $original; 378 } 379 380 $id_base = $this->id_data['base']; 381 382 // If no settings have been previewed yet (which should not be the case, since $this is), just pass through the original value. 383 if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) { 384 return $original; 233 385 } 234 386 235 return $this->multidimensional_replace( $original, $this->id_data['keys'], $value ); 387 foreach ( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] as $previewed_setting ) { 388 // Skip applying previewed value for any settings that have already been applied. 389 if ( ! empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['preview_applied_instances'][ $previewed_setting->id ] ) ) { 390 continue; 391 } 392 393 // Do the replacements of the posted/default sub value into the root value. 394 $value = $previewed_setting->post_value( $previewed_setting->default ); 395 $root = self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['root_value']; 396 $root = $previewed_setting->multidimensional_replace( $root, $previewed_setting->id_data['keys'], $value ); 397 self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['root_value'] = $root; 398 399 // Mark this setting having been applied so that it will be skipped when the filter is called again. 400 self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['preview_applied_instances'][ $previewed_setting->id ] = true; 401 } 402 403 return self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value']; 236 404 } 237 405 238 406 /** … … class WP_Customize_Setting { 299 467 } 300 468 301 469 /** 470 * Get the root value for a setting, especially for multidimensional ones. 471 * 472 * @since 4.4.0 473 * @access protected 474 * 475 * @param mixed $default Value to return if root does not exist. 476 * @return mixed 477 */ 478 protected function get_root_value( $default = null ) { 479 $id_base = $this->id_data['base']; 480 if ( 'option' === $this->type ) { 481 return get_option( $id_base, $default ); 482 } else if ( 'theme_mod' ) { 483 return get_theme_mod( $id_base, $default ); 484 } else { 485 /* 486 * Any WP_Customize_Setting subclass implementing aggregate multidimensional 487 * will need to override this method to obtain the data from the appropriate 488 * location. 489 */ 490 return $default; 491 } 492 } 493 494 /** 495 * Set the root value for a setting, especially for multidimensional ones. 496 * 497 * @since 4.4.0 498 * @access protected 499 * 500 * @param mixed $value Value to set as root of multidimensional setting. 501 * @return bool Whether the multidimensional root was updated successfully. 502 */ 503 protected function set_root_value( $value ) { 504 $id_base = $this->id_data['base']; 505 if ( 'option' === $this->type ) { 506 return update_option( $id_base, $value ); 507 } else if ( 'theme_mod' ) { 508 set_theme_mod( $id_base, $value ); 509 return true; 510 } else { 511 /* 512 * Any WP_Customize_Setting subclass implementing aggregate multidimensional 513 * will need to override this method to obtain the data from the appropriate 514 * location. 515 */ 516 return false; 517 } 518 } 519 520 /** 302 521 * Save the value of the setting, using the related API. 303 522 * 304 523 * @since 3.4.0 … … class WP_Customize_Setting { 307 526 * @return mixed The result of saving the value. 308 527 */ 309 528 protected function update( $value ) { 310 switch( $this->type ) { 311 case 'theme_mod' : 312 return $this->_update_theme_mod( $value ); 313 314 case 'option' : 315 return $this->_update_option( $value ); 316 317 default : 318 319 /** 320 * Fires when the {@see WP_Customize_Setting::update()} method is called for settings 321 * not handled as theme_mods or options. 322 * 323 * The dynamic portion of the hook name, `$this->type`, refers to the type of setting. 324 * 325 * @since 3.4.0 326 * 327 * @param mixed $value Value of the setting. 328 * @param WP_Customize_Setting $this WP_Customize_Setting instance. 329 */ 330 return do_action( 'customize_update_' . $this->type, $value, $this ); 529 $id_base = $this->id_data['base']; 530 if ( 'option' === $this->type || 'theme_mod' === $this->type ) { 531 if ( ! $this->is_multidimensional_aggregated ) { 532 return $this->set_root_value( $value ); 533 } else { 534 if ( isset( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['updated_value'] ) ) { 535 $root = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['updated_value']; 536 } else { 537 $root = $this->get_root_value( $this->default ); 538 } 539 $root = $this->multidimensional_replace( $root, $this->id_data['keys'], $value ); 540 self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['updated_value'] = $root; 541 } 542 } else { 543 /** 544 * Fires when the {@see WP_Customize_Setting::update()} method is called for settings 545 * not handled as theme_mods or options. 546 * 547 * The dynamic portion of the hook name, `$this->type`, refers to the type of setting. 548 * 549 * @since 3.4.0 550 * 551 * @param mixed $value Value of the setting. 552 * @param WP_Customize_Setting $this WP_Customize_Setting instance. 553 */ 554 do_action( 'customize_update_' . $this->type, $value, $this ); 331 555 } 332 556 } 333 557 334 558 /** 335 * Update the theme mod from the value of the parameter.559 * Deprecated method. 336 560 * 337 561 * @since 3.4.0 338 * 339 * @param mixed $value The value to update. 562 * @deprecated 4.4.0 Deprecated in favor of update() method. 340 563 */ 341 protected function _update_theme_mod( $value ) { 342 // Handle non-array theme mod. 343 if ( empty( $this->id_data[ 'keys' ] ) ) { 344 set_theme_mod( $this->id_data[ 'base' ], $value ); 345 return; 346 } 347 // Handle array-based theme mod. 348 $mods = get_theme_mod( $this->id_data[ 'base' ] ); 349 $mods = $this->multidimensional_replace( $mods, $this->id_data[ 'keys' ], $value ); 350 if ( isset( $mods ) ) { 351 set_theme_mod( $this->id_data[ 'base' ], $mods ); 352 } 564 protected function _update_theme_mod() { 565 _deprecated_function( __METHOD__, '4.4.0' ); 353 566 } 354 567 355 568 /** 356 * Update the option from the value of the setting.569 * Deprecated method. 357 570 * 358 571 * @since 3.4.0 572 * @deprecated 4.4.0 Deprecated in favor of update() method. 573 */ 574 protected function _update_option() { 575 _deprecated_function( __METHOD__, '4.4.0' ); 576 } 577 578 /** 579 * Finalize/commit the updates for multidimensional settings. 359 580 * 360 * @param mixed $value The value to update. 361 * @return bool The result of saving the value. 362 */ 363 protected function _update_option( $value ) { 364 // Handle non-array option. 365 if ( empty( $this->id_data[ 'keys' ] ) ) 366 return update_option( $this->id_data[ 'base' ], $value ); 367 368 // Handle array-based options. 369 $options = get_option( $this->id_data[ 'base' ] ); 370 $options = $this->multidimensional_replace( $options, $this->id_data[ 'keys' ], $value ); 371 if ( isset( $options ) ) 372 return update_option( $this->id_data[ 'base' ], $options ); 581 * @see WP_Customize_Manager::save() 582 * @since 4.4.0 583 * @access public 584 */ 585 final public function finalize_multidimensional_update() { 586 if ( ! $this->is_multidimensional_aggregated ) { 587 return; 588 } 589 $id_base = $this->id_data['base']; 590 if ( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['update_finalized'] ) { 591 return; 592 } 593 if ( ! isset( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['updated_value'] ) ) { 594 return; 595 } 596 $root = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['updated_value']; 597 if ( isset( $root ) ) { 598 $this->set_root_value( $root ); 599 } 600 self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['update_finalized'] = true; 373 601 } 374 602 375 603 /** … … class WP_Customize_Setting { 380 608 * @return mixed The value. 381 609 */ 382 610 public function value() { 383 // Get the callback that corresponds to the setting type. 384 switch( $this->type ) { 385 case 'theme_mod' : 386 $function = 'get_theme_mod'; 387 break; 388 case 'option' : 389 $function = 'get_option'; 390 break; 391 default : 392 393 /** 394 * Filter a Customize setting value not handled as a theme_mod or option. 395 * 396 * The dynamic portion of the hook name, `$this->id_date['base']`, refers to 397 * the base slug of the setting name. 398 * 399 * For settings handled as theme_mods or options, see those corresponding 400 * functions for available hooks. 401 * 402 * @since 3.4.0 403 * 404 * @param mixed $default The setting default value. Default empty. 405 */ 406 return apply_filters( 'customize_value_' . $this->id_data[ 'base' ], $this->default ); 611 $id_base = $this->id_data['base']; 612 $is_core_type = ( 'option' === $this->type || 'theme_mod' === $this->type ); 613 614 if ( ! $is_core_type && ! $this->is_multidimensional_aggregated ) { 615 $value = $this->get_root_value( $this->default ); // @todo Should $this->default be used here? 616 617 /** 618 * Filter a Customize setting value not handled as a theme_mod or option. 619 * 620 * The dynamic portion of the hook name, `$this->id_date['base']`, refers to 621 * the base slug of the setting name. 622 * 623 * For settings handled as theme_mods or options, see those corresponding 624 * functions for available hooks. 625 * 626 * @since 3.4.0 627 * 628 * @param mixed $default The setting default value. Default empty. 629 */ 630 $value = apply_filters( 'customize_value_' . $this->id_data['base'], $value ); 631 } else if ( $this->is_multidimensional_aggregated ) { 632 $value = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value']; 633 634 if ( ! isset( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['value_inspected_instances'][ $this->id ] ) ) { 635 $value = $this->multidimensional_get( $value, $this->id_data['keys'], $this->default ); 636 self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'] = $value; 637 638 // Mark this multidimensional setting as having had its value inspected. 639 self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['value_inspected_instances'][ $this->id ] = $this; 640 } 641 } else { 642 $value = $this->get_root_value( $this->default ); 407 643 } 408 409 // Handle non-array value 410 if ( empty( $this->id_data[ 'keys' ] ) ) 411 return $function( $this->id_data[ 'base' ], $this->default ); 412 413 // Handle array-based value 414 $values = $function( $this->id_data[ 'base' ] ); 415 return $this->multidimensional_get( $values, $this->id_data[ 'keys' ], $this->default ); 644 return $value; 416 645 } 417 646 418 647 /** … … class WP_Customize_Setting { 517 746 * @param $root 518 747 * @param $keys 519 748 * @param mixed $value The value to update. 520 * @return 749 * @return mixed 521 750 */ 522 751 final protected function multidimensional_replace( $root, $keys, $value ) { 523 752 if ( ! isset( $value ) ) … … class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting { 850 1079 } 851 1080 852 1081 /** 853 * Get the instance data for a given widgetsetting.1082 * Get the instance data for a given nav_menu_item setting. 854 1083 * 855 1084 * @since 4.3.0 856 1085 * @access public … … class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting { 984 1213 * Handle previewing the setting. 985 1214 * 986 1215 * @since 4.3.0 1216 * @since 4.4.0 Added boolean return value. 987 1217 * @access public 988 1218 * 989 1219 * @see WP_Customize_Manager::post_value() 1220 * 1221 * @return bool False if method short-circuited due to no-op. 990 1222 */ 991 1223 public function preview() { 992 1224 if ( $this->is_previewed ) { 993 return; 1225 return false; 1226 } 1227 1228 $undefined = new stdClass(); 1229 $is_placeholder = ( $this->post_id < 0 ); 1230 $is_dirty = ( $undefined !== $this->post_value( $undefined ) ); 1231 if ( ! $is_placeholder && ! $is_dirty ) { 1232 return false; 994 1233 } 995 1234 996 1235 $this->is_previewed = true; … … class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting { 1006 1245 } 1007 1246 1008 1247 // @todo Add get_post_metadata filters for plugins to add their data. 1248 1249 return true; 1009 1250 } 1010 1251 1011 1252 /** … … class WP_Customize_Nav_Menu_Setting extends WP_Customize_Setting { 1618 1859 * Handle previewing the setting. 1619 1860 * 1620 1861 * @since 4.3.0 1862 * @since 4.4.0 Added boolean return value 1621 1863 * @access public 1622 1864 * 1623 1865 * @see WP_Customize_Manager::post_value() 1866 * 1867 * @return bool False if method short-circuited due to no-op. 1624 1868 */ 1625 1869 public function preview() { 1626 1870 if ( $this->is_previewed ) { 1627 return; 1871 return false; 1872 } 1873 1874 $undefined = new stdClass(); 1875 $is_placeholder = ( $this->term_id < 0 ); 1876 $is_dirty = ( $undefined !== $this->post_value( $undefined ) ); 1877 if ( ! $is_placeholder && ! $is_dirty ) { 1878 return false; 1628 1879 } 1629 1880 1630 1881 $this->is_previewed = true; … … class WP_Customize_Nav_Menu_Setting extends WP_Customize_Setting { 1635 1886 add_filter( 'wp_get_nav_menu_object', array( $this, 'filter_wp_get_nav_menu_object' ), 10, 2 ); 1636 1887 add_filter( 'default_option_nav_menu_options', array( $this, 'filter_nav_menu_options' ) ); 1637 1888 add_filter( 'option_nav_menu_options', array( $this, 'filter_nav_menu_options' ) ); 1889 1890 return true; 1638 1891 } 1639 1892 1640 1893 /** -
tests/phpunit/tests/customize/setting.php
diff --git tests/phpunit/tests/customize/setting.php tests/phpunit/tests/customize/setting.php index 08bbc65..f3accbb 100644
class Tests_WP_Customize_Setting extends WP_UnitTestCase { 102 102 $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) ); 103 103 $this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $name, $this->undefined ) ); 104 104 $this->assertEquals( $default, $setting->value() ); 105 $ setting->preview();105 $this->assertTrue( $setting->preview(), 'Preview should not no-op since setting has no existing value.' ); 106 106 $this->assertEquals( $default, call_user_func( $type_options['getter'], $name, $this->undefined ), sprintf( 'Expected %s(%s) to return setting default: %s.', $type_options['getter'], $name, $default ) ); 107 107 $this->assertEquals( $default, $setting->value() ); 108 108 … … class Tests_WP_Customize_Setting extends WP_UnitTestCase { 114 114 $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) ); 115 115 $this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name ) ); 116 116 $this->assertEquals( $initial_value, $setting->value() ); 117 $ setting->preview();117 $this->assertFalse( $setting->preview(), 'Preview should no-op since setting value was extant and no post value was present.' ); 118 118 $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods) 119 119 $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods) 120 120 $this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name ) ); 121 121 $this->assertEquals( $initial_value, $setting->value() ); 122 122 123 // @todo What if we call the setter after preview() is called? If no post_value, should the new set value be stored? If that happens, then the following 3 assertions should be inverted124 123 $overridden_value = "overridden_value_$name"; 125 124 call_user_func( $type_options['setter'], $name, $overridden_value ); 126 $this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name ) ); 127 $this->assertEquals( $initial_value, $setting->value() ); 128 $this->assertNotEquals( $overridden_value, $setting->value() ); 125 $message = 'Initial value should be overridden because initial preview() was no-op due to setting having existing value and/or post value was absent.'; 126 $this->assertEquals( $overridden_value, call_user_func( $type_options['getter'], $name ), $message ); 127 $this->assertEquals( $overridden_value, $setting->value(), $message ); 128 $this->assertNotEquals( $initial_value, $setting->value(), $message ); 129 129 130 130 // Non-multidimensional: Test unset setting being overridden by a post value 131 131 $name = "unset_{$type}_overridden"; … … class Tests_WP_Customize_Setting extends WP_UnitTestCase { 133 133 $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) ); 134 134 $this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $name, $this->undefined ) ); 135 135 $this->assertEquals( $default, $setting->value() ); 136 $ setting->preview(); // activate post_data136 $this->assertTrue( $setting->preview(), 'Preview applies because setting has post_data_overrides.' ); // activate post_data 137 137 $this->assertEquals( $this->post_data_overrides[ $name ], call_user_func( $type_options['getter'], $name, $this->undefined ) ); 138 138 $this->assertEquals( $this->post_data_overrides[ $name ], $setting->value() ); 139 139 … … class Tests_WP_Customize_Setting extends WP_UnitTestCase { 145 145 $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) ); 146 146 $this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name, $this->undefined ) ); 147 147 $this->assertEquals( $initial_value, $setting->value() ); 148 $ setting->preview(); // activate post_data148 $this->assertTrue( $setting->preview(), 'Preview applies because setting has post_data_overrides.' ); // activate post_data 149 149 $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods) 150 150 $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods) 151 151 $this->assertEquals( $this->post_data_overrides[ $name ], call_user_func( $type_options['getter'], $name, $this->undefined ) ); … … class Tests_WP_Customize_Setting extends WP_UnitTestCase { 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 $ setting->preview();170 $this->assertTrue( $setting->preview() ); 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'] ); … … class Tests_WP_Customize_Setting extends WP_UnitTestCase { 311 311 $this->assertEquals( $initial_value, $this->custom_type_getter( $name, $this->undefined ) ); 312 312 $this->assertEquals( $initial_value, $setting->value() ); 313 313 $setting->preview(); 314 $this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ));315 $this->assertEquals( 2, did_action( "customize_preview_{$setting->type}" ) );314 $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ), 'Zero preview actions because initial value is set with no incoming post value, so there is no preview to apply.' ); 315 $this->assertEquals( 1, did_action( "customize_preview_{$setting->type}" ) ); 316 316 $this->assertEquals( $initial_value, $this->custom_type_getter( $name, $this->undefined ) ); // should be same as above 317 317 $this->assertEquals( $initial_value, $setting->value() ); // should be same as above 318 318 … … class Tests_WP_Customize_Setting extends WP_UnitTestCase { 325 325 $this->assertEquals( $this->undefined, $this->custom_type_getter( $name, $this->undefined ) ); 326 326 $this->assertEquals( $default, $setting->value() ); 327 327 $setting->preview(); 328 $this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ) );329 $this->assertEquals( 3, did_action( "customize_preview_{$setting->type}" ) );328 $this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ), 'One preview action now because initial value was not set and/or there is no incoming post value, so there is is a preview to apply.' ); 329 $this->assertEquals( 2, did_action( "customize_preview_{$setting->type}" ) ); 330 330 $this->assertEquals( $post_data_overrides[ $name ], $this->custom_type_getter( $name, $this->undefined ) ); 331 331 $this->assertEquals( $post_data_overrides[ $name ], $setting->value() ); 332 332 … … class Tests_WP_Customize_Setting extends WP_UnitTestCase { 342 342 $this->assertEquals( $initial_value, $setting->value() ); 343 343 $setting->preview(); 344 344 $this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ) ); 345 $this->assertEquals( 4, did_action( "customize_preview_{$setting->type}" ) );345 $this->assertEquals( 3, did_action( "customize_preview_{$setting->type}" ) ); 346 346 $this->assertEquals( $post_data_overrides[ $name ], $this->custom_type_getter( $name, $this->undefined ) ); 347 347 $this->assertEquals( $post_data_overrides[ $name ], $setting->value() ); 348 348