Make WordPress Core


Ignore:
Timestamp:
11/21/2015 02:51:57 AM (9 years ago)
Author:
westonruter
Message:

Customize: Ensure that a setting (especially a multidimensional one) can still be previewed when the post value to preview is set after preview() is invoked.

  • Introduce customize_post_value_set_{$setting_id} and customize_post_value_set actions which are done when WP_Customize_Manager::set_post_value() is called.
  • Clear the preview_applied flag for aggregated multidimensional settings when a post value is set. This ensures the new value is used instead of a previously-cached previewed value.
  • Move $is_preview property from subclasses to WP_Customize_Setting parent class.
  • Deferred preview: Ensure that when preview() short-circuits due to not being applicable that it will be called again later when the post value is set.
  • Populate post value for updated-widget with the (unsanitized) JS-value in WP_Customize_Widgets::call_widget_update() so that value will be properly sanitized when accessed in WP_Customize_Manager::post_value().

Includes unit tests with assertions to check the reported issues and validate the fixes.

Fixes defect introduced in [35007].
See #32103.
Fixes #34738.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/customize/setting.php

    r35305 r35724  
    9595        $_POST['customized'] = wp_slash( wp_json_encode( $this->post_data_overrides ) );
    9696
    97         // Try non-multidimensional settings
     97        // Try non-multidimensional settings.
    9898        foreach ( $this->standard_type_configs as $type => $type_options ) {
    99             // Non-multidimensional: See what effect the preview filter has on a non-existent setting (default value should be seen)
     99            // Non-multidimensional: See what effect the preview filter has on a non-existent setting (default value should be seen).
    100100            $name = "unset_{$type}_without_post_value";
    101101            $default = "default_value_{$name}";
     
    107107            $this->assertEquals( $default, $setting->value() );
    108108
    109             // Non-multidimensional: See what effect the preview has on an extant setting (default value should not be seen)
     109            // Non-multidimensional: See what effect the preview has on an extant setting (default value should not be seen).
    110110            $name = "set_{$type}_without_post_value";
    111111            $default = "default_value_{$name}";
     
    116116            $this->assertEquals( $initial_value, $setting->value() );
    117117            $this->assertFalse( $setting->preview(), 'Preview should no-op since setting value was extant and no post value was present.' );
    118             $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods)
    119             $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods)
     118            $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods).
     119            $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods).
    120120            $this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name ) );
    121121            $this->assertEquals( $initial_value, $setting->value() );
    122122
     123            // Non-multidimensional: Try updating a value that had a no-op preview.
    123124            $overridden_value = "overridden_value_$name";
    124125            call_user_func( $type_options['setter'], $name, $overridden_value );
     
    128129            $this->assertNotEquals( $initial_value, $setting->value(), $message );
    129130
    130             // Non-multidimensional: Test unset setting being overridden by a post value
     131            // Non-multidimensional: Ensure that setting a post value *after* preview() is called results in the post value being seen (deferred preview).
     132            $post_value = "post_value_for_{$setting->id}_set_after_preview_called";
     133            $this->assertEquals( 0, did_action( "customize_post_value_set_{$setting->id}" ) );
     134            $this->manager->set_post_value( $setting->id, $post_value );
     135            $this->assertEquals( 1, did_action( "customize_post_value_set_{$setting->id}" ) );
     136            $this->assertNotEquals( $overridden_value, $setting->value() );
     137            $this->assertEquals( $post_value, call_user_func( $type_options['getter'], $name ) );
     138            $this->assertEquals( $post_value, $setting->value() );
     139
     140            // Non-multidimensional: Test unset setting being overridden by a post value.
    131141            $name = "unset_{$type}_overridden";
    132142            $default = "default_value_{$name}";
     
    134144            $this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $name, $this->undefined ) );
    135145            $this->assertEquals( $default, $setting->value() );
    136             $this->assertTrue( $setting->preview(), 'Preview applies because setting has post_data_overrides.' ); // activate post_data
     146            $this->assertTrue( $setting->preview(), 'Preview applies because setting has post_data_overrides.' ); // Activate post_data.
    137147            $this->assertEquals( $this->post_data_overrides[ $name ], call_user_func( $type_options['getter'], $name, $this->undefined ) );
    138148            $this->assertEquals( $this->post_data_overrides[ $name ], $setting->value() );
    139149
    140             // Non-multidimensional: Test set setting being overridden by a post value
     150            // Non-multidimensional: Test set setting being overridden by a post value.
    141151            $name = "set_{$type}_overridden";
    142152            $default = "default_value_{$name}";
     
    146156            $this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name, $this->undefined ) );
    147157            $this->assertEquals( $initial_value, $setting->value() );
    148             $this->assertTrue( $setting->preview(), 'Preview applies because setting has post_data_overrides.' ); // activate post_data
    149             $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods)
    150             $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods)
     158            $this->assertTrue( $setting->preview(), 'Preview applies because setting has post_data_overrides.' ); // Activate post_data.
     159            $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods).
     160            $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods).
    151161            $this->assertEquals( $this->post_data_overrides[ $name ], call_user_func( $type_options['getter'], $name, $this->undefined ) );
    152162            $this->assertEquals( $this->post_data_overrides[ $name ], $setting->value() );
     
    156166    /**
    157167     * Run assertions on multidimensional standard settings.
     168     *
     169     * @see WP_Customize_Setting::preview()
    158170     */
    159171    function test_preview_standard_types_multidimensional() {
     
    161173
    162174        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)
     175            // Multidimensional: See what effect the preview filter has on a non-existent setting (default value should be seen).
    164176            $base_name = "unset_{$type}_multi";
    165177            $name = $base_name . '[foo]';
     
    168180            $this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $base_name, $this->undefined ) );
    169181            $this->assertEquals( $default, $setting->value() );
    170             $this->assertTrue( $setting->preview() );
     182            $this->assertTrue( $setting->preview(), "Preview for $setting->id should apply because setting is not in DB." );
    171183            $base_value = call_user_func( $type_options['getter'], $base_name, $this->undefined );
    172184            $this->assertArrayHasKey( 'foo', $base_value );
    173185            $this->assertEquals( $default, $base_value['foo'] );
    174186
    175             // Multidimensional: See what effect the preview has on an extant setting (default value should not be seen)
     187            // Multidimensional: See what effect the preview has on an extant setting (default value should not be seen) without post value.
    176188            $base_name = "set_{$type}_multi";
    177189            $name = $base_name . '[foo]';
     
    184196            $this->assertEquals( $initial_value, $base_value['foo'] );
    185197            $this->assertEquals( $initial_value, $setting->value() );
    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)
     198            $this->assertFalse( $setting->preview(), "Preview for $setting->id should no-op because setting is in DB and post value is absent." );
     199            $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods).
     200            $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods).
    189201            $base_value = call_user_func( $type_options['getter'], $base_name, array() );
    190202            $this->assertEquals( $initial_value, $base_value['foo'] );
    191203            $this->assertEquals( $initial_value, $setting->value() );
    192204
    193             // Multidimensional: Test unset setting being overridden by a post value
     205            // Multidimensional: Ensure that setting a post value *after* preview() is called results in the post value being seen (deferred preview).
     206            $override_value = "post_value_for_{$setting->id}_set_after_preview_called";
     207            $this->manager->set_post_value( $setting->id, $override_value );
     208            $base_value = call_user_func( $type_options['getter'], $base_name, array() );
     209            $this->assertEquals( $override_value, $base_value['foo'] );
     210            $this->assertEquals( $override_value, $setting->value() );
     211
     212            // Multidimensional: Test unset setting being overridden by a post value.
    194213            $base_name = "unset_{$type}_multi_overridden";
    195214            $name = $base_name . '[foo]';
     
    198217            $this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $base_name, $this->undefined ) );
    199218            $this->assertEquals( $default, $setting->value() );
    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)
     219            $this->assertTrue( $setting->preview(), "Preview for $setting->id should apply because a post value is present." );
     220            $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods).
     221            $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods).
    203222            $base_value = call_user_func( $type_options['getter'], $base_name, $this->undefined );
    204223            $this->assertArrayHasKey( 'foo', $base_value );
    205224            $this->assertEquals( $this->post_data_overrides[ $name ], $base_value['foo'] );
    206225
    207             // Multidimemsional: Test set setting being overridden by a post value
     226            // Multidimensional: Test set setting being overridden by a post value.
    208227            $base_name = "set_{$type}_multi_overridden";
    209228            $name = $base_name . '[foo]';
     
    214233            $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
    215234            $base_value = call_user_func( $type_options['getter'], $base_name, $this->undefined );
    216             $this->arrayHasKey( 'foo', $base_value );
    217             $this->arrayHasKey( 'bar', $base_value );
     235            $this->assertArrayHasKey( 'foo', $base_value );
     236            $this->assertArrayHasKey( 'bar', $base_value );
    218237            $this->assertEquals( $base_initial_value['foo'], $base_value['foo'] );
    219238
     
    221240            $this->assertEquals( $base_initial_value['bar'], $getter['bar'] );
    222241            $this->assertEquals( $initial_value, $setting->value() );
    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)
     242            $this->assertTrue( $setting->preview(), "Preview for $setting->id should apply because post value is present." );
     243            $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods).
     244            $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods).
    226245            $base_value = call_user_func( $type_options['getter'], $base_name, $this->undefined );
    227246            $this->assertArrayHasKey( 'foo', $base_value );
    228247            $this->assertEquals( $this->post_data_overrides[ $name ], $base_value['foo'] );
    229             $this->arrayHasKey( 'bar', call_user_func( $type_options['getter'], $base_name, $this->undefined ) );
     248            $this->assertArrayHasKey( 'bar', call_user_func( $type_options['getter'], $base_name, $this->undefined ) );
    230249
    231250            $getter = call_user_func( $type_options['getter'], $base_name, $this->undefined );
     
    273292        }
    274293    }
    275 
     294    /**
     295     * Run assertions on custom settings.
     296     *
     297     * @see WP_Customize_Setting::preview()
     298     */
    276299    function test_preview_custom_type() {
    277300        $type = 'custom_type';
     
    287310        add_action( "customize_preview_{$type}", array( $this, 'custom_type_preview' ) );
    288311
    289         // Custom type not existing and no post value override
     312        // Custom type not existing and no post value override.
    290313        $name = "unset_{$type}_without_post_value";
    291314        $default = "default_value_{$name}";
    292315        $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
    293         // Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need
     316        // Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need.
    294317        add_filter( "customize_value_{$name}", array( $this, 'custom_type_value_filter' ) );
    295318        $this->assertEquals( $this->undefined, $this->custom_type_getter( $name, $this->undefined ) );
    296319        $this->assertEquals( $default, $setting->value() );
    297         $setting->preview();
     320        $this->assertTrue( $setting->preview() );
    298321        $this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ) );
    299322        $this->assertEquals( 1, did_action( "customize_preview_{$setting->type}" ) );
    300323        $this->assertEquals( $this->undefined, $this->custom_type_getter( $name, $this->undefined ) ); // Note: for a non-custom type this is $default
    301         $this->assertEquals( $default, $setting->value() ); // should be same as above
    302 
    303         // Custom type existing and no post value override
     324        $this->assertEquals( $default, $setting->value() ); // Should be same as above.
     325
     326        // Custom type existing and no post value override.
    304327        $name = "set_{$type}_without_post_value";
    305328        $default = "default_value_{$name}";
     
    307330        $this->custom_type_setter( $name, $initial_value );
    308331        $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
    309         // Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need
     332        // Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need.
    310333        add_filter( "customize_value_{$name}", array( $this, 'custom_type_value_filter' ) );
    311334        $this->assertEquals( $initial_value, $this->custom_type_getter( $name, $this->undefined ) );
    312335        $this->assertEquals( $initial_value, $setting->value() );
    313         $setting->preview();
     336        $this->assertFalse( $setting->preview(), "Preview for $setting->id should not apply because existing type without an override." );
    314337        $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.' );
    315338        $this->assertEquals( 1, did_action( "customize_preview_{$setting->type}" ) );
    316         $this->assertEquals( $initial_value, $this->custom_type_getter( $name, $this->undefined ) ); // should be same as above
    317         $this->assertEquals( $initial_value, $setting->value() ); // should be same as above
    318 
    319         // Custom type not existing and with a post value override
     339        $this->assertEquals( $initial_value, $this->custom_type_getter( $name, $this->undefined ) ); // Should be same as above.
     340        $this->assertEquals( $initial_value, $setting->value() ); // Should be same as above.
     341
     342        // Custom type deferred preview (setting post value after preview ran).
     343        $override_value = "custom_type_value_{$name}_override_deferred_preview";
     344        $this->manager->set_post_value( $setting->id, $override_value );
     345        $this->assertEquals( $override_value, $this->custom_type_getter( $name, $this->undefined ) ); // Should be same as above.
     346        $this->assertEquals( $override_value, $setting->value() ); // Should be same as above.
     347
     348        // Custom type not existing and with a post value override.
    320349        $name = "unset_{$type}_with_post_value";
    321350        $default = "default_value_{$name}";
    322351        $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
    323         // Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need
     352        // Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need.
    324353        add_filter( "customize_value_{$name}", array( $this, 'custom_type_value_filter' ) );
    325354        $this->assertEquals( $this->undefined, $this->custom_type_getter( $name, $this->undefined ) );
    326355        $this->assertEquals( $default, $setting->value() );
    327         $setting->preview();
     356        $this->assertTrue( $setting->preview() );
    328357        $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}" ) );
     358        $this->assertEquals( 3, did_action( "customize_preview_{$setting->type}" ) );
    330359        $this->assertEquals( $post_data_overrides[ $name ], $this->custom_type_getter( $name, $this->undefined ) );
    331360        $this->assertEquals( $post_data_overrides[ $name ], $setting->value() );
    332361
    333         // Custom type not existing and with a post value override
     362        // Custom type not existing and with a post value override.
    334363        $name = "set_{$type}_with_post_value";
    335364        $default = "default_value_{$name}";
     
    337366        $this->custom_type_setter( $name, $initial_value );
    338367        $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
    339         // Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need
     368        // Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need.
    340369        add_filter( "customize_value_{$name}", array( $this, 'custom_type_value_filter' ) );
    341370        $this->assertEquals( $initial_value, $this->custom_type_getter( $name, $this->undefined ) );
    342371        $this->assertEquals( $initial_value, $setting->value() );
    343         $setting->preview();
     372        $this->assertTrue( $setting->preview() );
    344373        $this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ) );
    345         $this->assertEquals( 3, did_action( "customize_preview_{$setting->type}" ) );
     374        $this->assertEquals( 4, did_action( "customize_preview_{$setting->type}" ) );
    346375        $this->assertEquals( $post_data_overrides[ $name ], $this->custom_type_getter( $name, $this->undefined ) );
    347376        $this->assertEquals( $post_data_overrides[ $name ], $setting->value() );
     
    362391        $this->assertEquals( $this->undefined, get_option( $name, $this->undefined ) );
    363392        $this->assertEquals( $default, $setting->value() );
    364         $setting->preview();
     393        $this->assertTrue( $setting->preview() );
    365394        $this->assertEquals( $default, get_option( $name, $this->undefined ), sprintf( 'Expected get_option(%s) to return setting default: %s.', $name, $default ) );
    366395        $this->assertEquals( $default, $setting->value() );
     
    439468        $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type' ) );
    440469        $this->assertFalse( $setting->is_current_blog_previewed() );
    441         $setting->preview();
     470        $this->assertTrue( $setting->preview() );
    442471        $this->assertTrue( $setting->is_current_blog_previewed() );
    443472
     
    463492        $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type' ) );
    464493        $this->assertFalse( $setting->is_current_blog_previewed() );
    465         $setting->preview();
     494        $this->assertTrue( $setting->preview() );
    466495        $this->assertTrue( $setting->is_current_blog_previewed() );
    467496
Note: See TracChangeset for help on using the changeset viewer.