Ticket #31484: 31484.3.diff
File 31484.3.diff, 20.2 KB (added by , 10 years ago) |
---|
-
src/wp-admin/customize.php
diff --git src/wp-admin/customize.php src/wp-admin/customize.php index d6cec32..a858c25 100644
do_action( 'customize_controls_print_scripts' ); 252 252 $settings['settings'][ $id ] = array( 253 253 'value' => $setting->js_value(), 254 254 'transport' => $setting->transport, 255 'dirty' => $setting->dirty, 255 256 ); 256 257 } 257 258 -
src/wp-admin/js/customize-controls.js
diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js index d95287c..ccce08e 100644
17 17 18 18 this.id = id; 19 19 this.transport = this.transport || 'refresh'; 20 this._dirty = options.dirty || false; 20 21 21 22 this.bind( this.preview ); 22 23 }, … … 2424 2425 $.each( api.settings.settings, function( id, data ) { 2425 2426 api.create( id, id, data.value, { 2426 2427 transport: data.transport, 2427 previewer: api.previewer 2428 previewer: api.previewer, 2429 dirty: !! data.dirty 2428 2430 } ); 2429 2431 }); 2430 2432 -
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 ffdb3f3..d050345 100644
final class WP_Customize_Manager { 695 695 * Switch the theme and trigger the save() method on each setting. 696 696 * 697 697 * @since 3.4.0 698 * @since 4.1.2 Add $options param. 699 * 700 * @param array $options { 701 * Override default behaviors for save method. 702 * @type bool $check_ajax_referer Whether check_ajax_referer() is performed. Defaults to true. 703 * @type bool $send_json Whether to return json; if false, returns WP_Error on failure, and the customize_save_response array on success. Defaults to true. 704 * } 705 * @return WP_Error|array|null 698 706 */ 699 public function save() { 707 public function save( $options = array() ) { 708 $options = array_merge( 709 array( 710 'check_ajax_referer' => true, 711 'send_json' => true, 712 ), 713 empty( $options ) ? array() : $options 714 ); 715 700 716 if ( ! $this->is_preview() ) { 701 wp_send_json_error( 'not_preview' ); 717 $error = 'not_preview'; 718 if ( $options['send_json'] ) { 719 wp_send_json_error( $error ); 720 } else { 721 return new WP_Error( $error ); 722 } 702 723 } 703 724 704 725 $action = 'save-customize_' . $this->get_stylesheet(); 705 if ( ! check_ajax_referer( $action, 'nonce', false ) ) { 706 wp_send_json_error( 'invalid_nonce' ); 726 if ( $options['check_ajax_referer'] && ! check_ajax_referer( $action, 'nonce', false ) ) { 727 $error = 'invalid_nonce'; 728 if ( $options['send_json'] ) { 729 wp_send_json_error( $error ); 730 } else { 731 return new WP_Error( $error ); 732 } 707 733 } 708 734 735 /** 736 * Fires before the theme has switched in the Customizer, and before settings 737 * have been saved. 738 * 739 * @since 4.1.2 740 * 741 * @param WP_Customize_Manager $this WP_Customize_Manager instance. 742 */ 743 do_action( 'customize_save_before_switch_theme', $this ); 744 709 745 // Do we have to switch themes? 710 746 if ( ! $this->is_theme_active() ) { 711 747 // Temporarily stop previewing the theme to allow switch_themes() … … final class WP_Customize_Manager { 751 787 * @param WP_Customize_Manager $this WP_Customize_Manager instance. 752 788 */ 753 789 $response = apply_filters( 'customize_save_response', array(), $this ); 754 wp_send_json_success( $response ); 790 if ( $options['send_json'] ) { 791 wp_send_json_success( $response ); 792 } else { 793 return $response; 794 } 755 795 } 756 796 757 797 /** -
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 5618314..18598dc 100644
class WP_Customize_Setting { 60 60 public $sanitize_callback = ''; 61 61 public $sanitize_js_callback = ''; 62 62 63 /** 64 * Whether or not the setting is initially dirty when created. 65 * 66 * This is used to ensure that a setting will be sent from the pane to the 67 * preview when loading the Customizer. Normally a setting only is synced to 68 * the preview if it has been changed. This allows the setting to be sent 69 * from the start. 70 * 71 * @since 4.1.2 72 * @access public 73 * @var bool 74 */ 75 public $dirty = false; 76 63 77 protected $id_data = array(); 64 78 65 79 /** -
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 674dee6..79193f9 100644
final class WP_Customize_Widgets { 216 216 $sidebars_widgets = $this->old_sidebars_widgets; 217 217 $sidebars_widgets = retrieve_widgets( 'customize' ); 218 218 add_filter( 'option_sidebars_widgets', array( $this, 'filter_option_sidebars_widgets_for_theme_switch' ), 1 ); 219 unset( $GLOBALS['_wp_sidebars_widgets'] ); // reset global cache var used by wp_get_sidebars_widgets() 219 220 } 220 221 221 222 /** … … final class WP_Customize_Widgets { 232 233 * @access public 233 234 * 234 235 * @param array $old_sidebars_widgets 236 * @return array 235 237 */ 236 238 public function filter_customize_value_old_sidebars_widgets_data( $old_sidebars_widgets ) { 237 239 return $this->old_sidebars_widgets; … … final class WP_Customize_Widgets { 249 251 * @access public 250 252 * 251 253 * @param array $sidebars_widgets 254 * @return array 252 255 */ 253 256 public function filter_option_sidebars_widgets_for_theme_switch( $sidebars_widgets ) { 254 257 $sidebars_widgets = $GLOBALS['sidebars_widgets']; … … final class WP_Customize_Widgets { 332 335 $setting_id = 'old_sidebars_widgets_data'; 333 336 $setting_args = $this->get_setting_args( $setting_id, array( 334 337 'type' => 'global_variable', 338 'dirty' => true, 335 339 ) ); 336 340 $this->manager->add_setting( $setting_id, $setting_args ); 337 341 } … … final class WP_Customize_Widgets { 356 360 $setting_id = sprintf( 'sidebars_widgets[%s]', $sidebar_id ); 357 361 $setting_args = $this->get_setting_args( $setting_id ); 358 362 if ( ! $this->manager->get_setting( $setting_id ) ) { 363 if ( ! $this->manager->is_theme_active() ) { 364 $setting_args['dirty'] = true; 365 } 359 366 $this->manager->add_setting( $setting_id, $setting_args ); 360 367 } 361 368 $new_setting_ids[] = $setting_id; … … final class WP_Customize_Widgets { 894 901 * @access public 895 902 * 896 903 * @param array $sidebars_widgets List of widgets for the current sidebar. 904 * @return array 897 905 */ 898 906 public function preview_sidebars_widgets( $sidebars_widgets ) { 899 907 $sidebars_widgets = get_option( 'sidebars_widgets' ); -
src/wp-includes/class-wp-theme.php
diff --git src/wp-includes/class-wp-theme.php src/wp-includes/class-wp-theme.php index 523c733..41e2ca2 100644
final class WP_Theme implements ArrayAccess { 1121 1121 * @return array Array of stylesheet names. 1122 1122 */ 1123 1123 public static function get_allowed_on_network() { 1124 static $allowed_themes; 1125 if ( ! isset( $allowed_themes ) ) 1126 $allowed_themes = (array) get_site_option( 'allowedthemes' ); 1127 return $allowed_themes; 1124 return (array) get_site_option( 'allowedthemes' ); 1128 1125 } 1129 1126 1130 1127 /** … … final class WP_Theme implements ArrayAccess { 1137 1134 * @return array Array of stylesheet names. 1138 1135 */ 1139 1136 public static function get_allowed_on_site( $blog_id = null ) { 1140 static $allowed_themes = array(); 1137 $allowed_themes = wp_cache_get( 'themes_allowed_on_site' ); 1138 if ( empty( $allowed_themes ) ) { 1139 $allowed_themes = array(); 1140 } 1141 1141 1142 1142 if ( ! $blog_id || ! is_multisite() ) 1143 1143 $blog_id = get_current_blog_id(); … … final class WP_Theme implements ArrayAccess { 1191 1191 } 1192 1192 } 1193 1193 1194 return (array) $allowed_themes[ $blog_id ]; 1194 $allowed_on_site = (array) $allowed_themes[ $blog_id ]; 1195 wp_cache_set( 'themes_allowed_on_site', $allowed_on_site ); 1196 return $allowed_on_site; 1195 1197 } 1196 1198 1197 1199 /** -
src/wp-includes/theme.php
diff --git src/wp-includes/theme.php src/wp-includes/theme.php index 3f5b5e7..1ddf221 100644
function switch_theme( $stylesheet ) { 762 762 global $wp_theme_directories, $wp_customize, $sidebars_widgets; 763 763 764 764 $_sidebars_widgets = null; 765 if ( 'wp_ajax_customize_save' === current_action() ) {765 if ( did_action( 'customize_save_before_switch_theme' ) ) { 766 766 $_sidebars_widgets = $wp_customize->post_value( $wp_customize->get_setting( 'old_sidebars_widgets_data' ) ); 767 767 } elseif ( is_array( $sidebars_widgets ) ) { 768 768 $_sidebars_widgets = $sidebars_widgets; … … function switch_theme( $stylesheet ) { 807 807 * we need to to remove the theme mods to avoid overwriting changes made via 808 808 * the Customizer when accessing wp-admin/widgets.php. 809 809 */ 810 if ( 'wp_ajax_customize_save' === current_action() ) {810 if ( did_action( 'customize_save_before_switch_theme' ) ) { 811 811 remove_theme_mod( 'sidebars_widgets' ); 812 812 } 813 813 } -
tests/phpunit/includes/testcase.php
diff --git tests/phpunit/includes/testcase.php tests/phpunit/includes/testcase.php index 81f9686..a54b5e6 100644
class WP_UnitTestCase extends PHPUnit_Framework_TestCase { 87 87 function clean_up_global_scope() { 88 88 $_GET = array(); 89 89 $_POST = array(); 90 $_REQUEST = array(); 90 91 $this->flush_cache(); 91 92 } 92 93 -
tests/phpunit/tests/customize/setting.php
diff --git tests/phpunit/tests/customize/setting.php tests/phpunit/tests/customize/setting.php index a783287..bf91a7d 100644
class Tests_WP_Customize_Setting extends WP_UnitTestCase { 44 44 $this->assertEquals( '', $setting->sanitize_js_callback ); 45 45 $this->assertFalse( has_filter( "customize_sanitize_{$setting->id}" ) ); 46 46 $this->assertFalse( has_filter( "customize_sanitize_js_{$setting->id}" ) ); 47 $this->assertEquals( false, $setting->dirty ); 47 48 } 48 49 49 50 function test_constructor_with_args() { -
tests/phpunit/tests/customize/widgets.php
diff --git tests/phpunit/tests/customize/widgets.php tests/phpunit/tests/customize/widgets.php index 8decbef..a7c9e40 100644
class Tests_WP_Customize_Widgets extends WP_UnitTestCase { 12 12 */ 13 13 protected $manager; 14 14 15 protected $twentyfifteen_sidebars_widgets; 16 protected $twentythirteen_sidebars_widgets; 17 15 18 function setUp() { 16 19 parent::setUp(); 17 20 require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); … … class Tests_WP_Customize_Widgets extends WP_UnitTestCase { 21 24 unset( $GLOBALS['_wp_sidebars_widgets'] ); // clear out cache set by wp_get_sidebars_widgets() 22 25 $sidebars_widgets = wp_get_sidebars_widgets(); 23 26 $this->assertEqualSets( array( 'wp_inactive_widgets', 'sidebar-1' ), array_keys( wp_get_sidebars_widgets() ) ); 24 $this->assertContains( 'search-2', $sidebars_widgets['sidebar-1'] ); 25 $this->assertContains( 'categories-2', $sidebars_widgets['sidebar-1'] ); 27 $initial_sidebar_1 = array( 28 'search-2', 29 'recent-posts-2', 30 'recent-comments-2', 31 'archives-2', 32 'categories-2', 33 'meta-2', 34 ); 35 $this->assertEquals( $initial_sidebar_1, $sidebars_widgets['sidebar-1'] ); 26 36 $this->assertArrayHasKey( 2, get_option( 'widget_search' ) ); 27 37 $widget_categories = get_option( 'widget_categories' ); 28 38 $this->assertArrayHasKey( 2, $widget_categories ); … … class Tests_WP_Customize_Widgets extends WP_UnitTestCase { 99 109 } 100 110 101 111 /** 112 * @param string $new_theme Stylesheet 113 * 114 * @throws Exception 115 */ 116 function prepare_theme_switch_state( $new_theme ) { 117 $old_theme = get_stylesheet(); 118 if ( 'twentyfifteen' !== $old_theme ) { 119 throw new Exception( 'Currently expecting initial theme to be twentyfifteen.' ); 120 } 121 if ( $new_theme === $old_theme ) { 122 throw new Exception( 'A different theme must be supplied than the current theme.' ); 123 } 124 125 // Make sure the new theme and the old theme are both among allowed themes 126 update_site_option( 'allowedthemes', array_fill_keys( array( $new_theme, $old_theme ), true ) ); 127 $this->assertTrue( wp_get_theme( $old_theme )->is_allowed(), "Expected old theme $old_theme to be allowed." ); 128 $this->assertTrue( wp_get_theme( $new_theme )->is_allowed(), "Expected new theme $new_theme to be allowed" ); 129 130 // Set the sidebars_widgets theme mods for both the old theme and the new theme 131 $this->twentyfifteen_sidebars_widgets = wp_get_sidebars_widgets(); 132 $this->twentythirteen_sidebars_widgets = $this->twentyfifteen_sidebars_widgets; 133 $this->twentythirteen_sidebars_widgets['sidebar-2'] = array(); 134 for ( $i = 0; $i < 3; $i += 1 ) { 135 $this->twentythirteen_sidebars_widgets['sidebar-2'][] = array_pop( $this->twentythirteen_sidebars_widgets['sidebar-1'] ); 136 } 137 $this->twentythirteen_sidebars_widgets['sidebar-1'] = array_reverse( $this->twentythirteen_sidebars_widgets['sidebar-1'] ); 138 update_option( 'theme_mods_twentythirteen', array( 139 'sidebars_widgets' => array( 140 'time' => time() - 3600, 141 'data' => $this->twentythirteen_sidebars_widgets, 142 ), 143 ) ); 144 145 $this->switch_theme_and_check_switched( $new_theme ); 146 $sidebars_widgets = get_option( 'sidebars_widgets' ); 147 $this->assertEquals( $this->twentythirteen_sidebars_widgets['sidebar-1'], $sidebars_widgets['sidebar-1'] ); 148 $this->assertEquals( $this->twentythirteen_sidebars_widgets['sidebar-2'], $sidebars_widgets['sidebar-2'] ); 149 150 $this->switch_theme_and_check_switched( $old_theme ); 151 $sidebars_widgets = get_option( 'sidebars_widgets' ); 152 $this->assertEquals( $this->twentyfifteen_sidebars_widgets['sidebar-1'], $sidebars_widgets['sidebar-1'] ); 153 } 154 155 /** 156 * @param string $new_theme Stylesheet 157 * 158 * @throws Exception 159 */ 160 function switch_theme_and_check_switched( $new_theme ) { 161 switch_theme( $new_theme ); 162 $this->setup_sidebars_for_theme_switch( $new_theme ); 163 check_theme_switched(); 164 } 165 166 /** 167 * Emulate loading the theme's functions.php 168 * 169 * @param string $new_theme stylesheet 170 * 171 * @throws Exception 172 */ 173 function setup_sidebars_for_theme_switch( $new_theme ) { 174 if ( 'twentythirteen' === $new_theme ) { 175 register_sidebar( array( 176 'name' => __( 'Secondary Widget Area', 'twentythirteen' ), 177 'id' => 'sidebar-2', 178 'description' => __( 'Appears on posts and pages in the sidebar.', 'twentythirteen' ), 179 'before_widget' => '<aside id="%1$s" class="widget %2$s">', 180 'after_widget' => '</aside>', 181 'before_title' => '<h3 class="widget-title">', 182 'after_title' => '</h3>', 183 ) ); 184 } else if ( 'twentyfifteen' === $new_theme ) { 185 unregister_sidebar( 'sidebar-2' ); 186 } else { 187 throw new Exception( 'Only twentyfifteen and twentythirteen are supported.' ); 188 } 189 } 190 191 function test_override_sidebars_widgets_for_non_theme_switch() { 192 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { 193 $this->markTestSkipped( 'The WP_Customize_Widgets::override_sidebars_widgets_for_theme_switch() method short-circuits if DOING_AJAX.' ); 194 } 195 $this->do_customize_boot_actions(); 196 $this->assertEmpty( $this->manager->get_setting( 'old_sidebars_widgets_data' ) ); 197 } 198 199 /** 200 * Test WP_Customize_Widgets::override_sidebars_widgets_for_theme_switch() 201 * 202 * @todo A lot of this should be in a test for WP_Customize_Manager::setup_theme() 203 */ 204 function test_override_sidebars_widgets_for_theme_switch() { 205 global $wpdb; 206 207 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { 208 $this->markTestSkipped( 'The WP_Customize_Widgets::override_sidebars_widgets_for_theme_switch() method short-circuits if DOING_AJAX.' ); 209 } 210 $start_time = time(); 211 $old_theme = get_stylesheet(); 212 $new_theme = 'twentythirteen'; 213 $this->assertNotEquals( $new_theme, $old_theme ); 214 215 $initial_sidebars_widgets = get_option( 'sidebars_widgets' ); 216 $this->prepare_theme_switch_state( $new_theme ); 217 $checked_sidebars_widgets = get_option( 'sidebars_widgets' ); 218 $this->assertEquals( $initial_sidebars_widgets['sidebar-1'], $checked_sidebars_widgets['sidebar-1'], 'Expected sidebar-1 to be back in initial state after round-trip theme switch.' ); 219 $this->assertArrayNotHasKey( 'orphaned_widgets_1', $checked_sidebars_widgets ); 220 $this->assertArrayNotHasKey( 'sidebar-2', $checked_sidebars_widgets ); 221 222 // Initialize the Customizer with a preview for the new theme 223 $_REQUEST['theme'] = $new_theme; 224 add_action( 'start_previewing_theme', array( $this, 'set_up_sidebars_for_theme_preview' ) ); 225 $this->do_customize_boot_actions(); 226 $this->assertEquals( $new_theme, $this->manager->get_stylesheet() ); 227 228 $sidebars_widgets = get_option( 'sidebars_widgets' ); 229 $this->assertArrayNotHasKey( 'orphaned_widgets_1', $sidebars_widgets ); 230 $this->assertEquals( $this->twentythirteen_sidebars_widgets['sidebar-1'], $sidebars_widgets['sidebar-1'] ); 231 $this->assertEquals( $this->twentythirteen_sidebars_widgets['sidebar-2'], $sidebars_widgets['sidebar-2'] ); 232 unset( $sidebars_widgets['array_version'] ); 233 $this->assertEquals( $sidebars_widgets, wp_get_sidebars_widgets() ); 234 235 $db_sidebars_widgets = maybe_unserialize( $wpdb->get_var( "SELECT option_value FROM $wpdb->options WHERE option_name = 'sidebars_widgets'" ) ); 236 $this->assertEquals( $db_sidebars_widgets['sidebar-1'], $initial_sidebars_widgets['sidebar-1'] ); 237 $this->assertArrayNotHasKey( 'sidebar-2', $db_sidebars_widgets ); 238 239 $old_sidebars_widgets_setting = $this->manager->get_setting( 'old_sidebars_widgets_data' ); 240 $this->assertNotEmpty( $old_sidebars_widgets_setting ); 241 $old_sidebars_widgets_value = $old_sidebars_widgets_setting->value(); 242 $this->assertNotEmpty( $old_sidebars_widgets_value ); 243 $this->assertEquals( $this->twentyfifteen_sidebars_widgets['sidebar-1'], $old_sidebars_widgets_value['sidebar-1'] ); 244 $this->assertArrayNotHasKey( 'sidebar-2', $old_sidebars_widgets_value['sidebar-1'] ); 245 246 $expected_dirty_settings = array( 'sidebars_widgets[sidebar-1]', 'sidebars_widgets[sidebar-2]', 'old_sidebars_widgets_data' ); 247 foreach ( $expected_dirty_settings as $dirty_setting_id ) { 248 $this->assertTrue( $this->manager->get_setting( $dirty_setting_id )->dirty, "Expected setting $dirty_setting_id to be dirty." ); 249 } 250 251 // Now save the customizer, and remove preview filters which interfere with saving 252 foreach ( $this->manager->settings() as $setting ) { 253 if ( $setting->dirty ) { 254 $this->manager->set_post_value( $setting->id, $setting->value() ); 255 $id_base = preg_replace( '/\[.*/', '', $setting->id ); 256 if ( 'theme_mod' === $setting->type ) { 257 remove_filter( "theme_mod_{$id_base}", array( $setting, '_preview_filter' ) ); 258 } else if ( 'option' === $setting->type ) { 259 remove_filter( "pre_option_{$id_base}", array( $setting, '_preview_filter' ) ); 260 remove_filter( "option_{$id_base}", array( $setting, '_preview_filter' ) ); 261 remove_filter( "default_option_{$id_base}", array( $setting, '_preview_filter' ) ); 262 } 263 } 264 } 265 remove_filter( 'option_sidebars_widgets', array( $this->manager->widgets, 'filter_option_sidebars_widgets_for_theme_switch' ), 1 ); 266 $response = $this->manager->save( array( 'check_ajax_referer' => false, 'send_json' => false ) ); 267 $this->assertInternalType( 'array', $response ); // and not a WP_Error 268 269 // Ensure that the new theme's sidebars have been saved as expected 270 $db_sidebars_widgets = maybe_unserialize( $wpdb->get_var( "SELECT option_value FROM $wpdb->options WHERE option_name = 'sidebars_widgets'" ) ); 271 $this->assertEquals( $this->twentythirteen_sidebars_widgets['sidebar-1'], $db_sidebars_widgets['sidebar-1'] ); 272 $this->assertEquals( $this->twentythirteen_sidebars_widgets['sidebar-2'], $db_sidebars_widgets['sidebar-2'] ); 273 274 // Ensure that the old theme's sidebars have been placed in that theme's widgets_sidebars theme mod 275 $old_theme_sidebars_widgets_theme_mod = get_option( "theme_mods_{$old_theme}" ); 276 $this->assertEquals( $old_sidebars_widgets_value, $old_theme_sidebars_widgets_theme_mod['sidebars_widgets']['data'] ); 277 $this->assertGreaterThanOrEqual( $old_theme_sidebars_widgets_theme_mod['sidebars_widgets']['time'], $start_time ); 278 $new_theme_mods = get_option( "theme_mods_{$new_theme}" ); 279 $this->assertEmpty( $new_theme_mods['sidebars_widgets'], "Expected new theme's sidebars_widgets theme mod to be gone." ); 280 } 281 282 /** 283 * @param WP_Customize_Manager $wp_customize 284 */ 285 function set_up_sidebars_for_theme_preview( $wp_customize ) { 286 $this->setup_sidebars_for_theme_switch( $wp_customize->get_stylesheet() ); 287 } 288 289 /** 102 290 * Test WP_Customize_Widgets::get_setting_args() 103 291 */ 104 292 function test_get_setting_args() {