From 2f2cb0926ffa133bf8e93a3262e639420e5647ae Mon Sep 17 00:00:00 2001 From: koopersmith Date: Mon, 30 Apr 2012 15:46:17 +0000 Subject: [PATCH] Theme Customizer: Migrate to an ajax-based solution for refreshing the preview and saving. see #20507, #19910. * Use ajax-based saving, add saving indicator. * Use ajax-based refreshing instead of form targets. * Instead of using hidden inputs with prefixed names to track the canonical data, use the values stored in wp.customize. Encode the values as JSON before sending to avoid bugs with ids that contain square brackets (PHP mangles POST values with nested brackets). * Use wp.customize.Previewer solely for the purpose of previewing; move the postMessage connection with the parent frame and other unrelated code snippets into the 'ready' handler. git-svn-id: http://svn.automattic.com/wordpress/trunk@20645 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/class-wp-customize-setting.php | 13 +- wp-includes/class-wp-customize.php | 35 ++++- wp-includes/css/customize-controls.dev.css | 10 ++ wp-includes/customize-controls.php | 13 +- wp-includes/js/customize-base.dev.js | 14 +- wp-includes/js/customize-controls.dev.js | 149 +++++++++++---------- 6 files changed, 136 insertions(+), 98 deletions(-) diff --git a/wp-includes/class-wp-customize-setting.php b/wp-includes/class-wp-customize-setting.php index 087d5e3496..dd55d374c4 100644 --- a/wp-includes/class-wp-customize-setting.php +++ b/wp-includes/class-wp-customize-setting.php @@ -21,9 +21,6 @@ class WP_Customize_Setting { protected $id_data = array(); private $_post_value; // Cached, sanitized $_POST value. - // Prefix for $_POST values to prevent naming conflicts. - const name_prefix = 'customize_'; - /** * Constructor. * @@ -121,16 +118,8 @@ class WP_Customize_Setting { if ( isset( $this->_post_value ) ) return $this->_post_value; - $base = self::name_prefix . $this->id_data[ 'base' ]; + $result = $this->manager->post_value( $this ); - if ( ! isset( $_POST[ $base ] ) ) - return $default; - - $result = $this->multidimensional_get( $_POST[ $base ], $this->id_data[ 'keys' ] ); - if ( ! isset( $result ) ) - return $default; - - $result = $this->sanitize( $result ); if ( isset( $result ) ) return $this->_post_value = $result; else diff --git a/wp-includes/class-wp-customize.php b/wp-includes/class-wp-customize.php index 73b75c05c0..cdb4f60ba8 100644 --- a/wp-includes/class-wp-customize.php +++ b/wp-includes/class-wp-customize.php @@ -17,6 +17,10 @@ final class WP_Customize { protected $sections = array(); protected $controls = array(); + protected $customized; + + private $_post_values; + /** * Constructor. * @@ -31,6 +35,8 @@ final class WP_Customize { add_action( 'admin_init', array( $this, 'admin_init' ) ); add_action( 'wp_loaded', array( $this, 'wp_loaded' ) ); + add_action( 'wp_ajax_customize_save', array( $this, 'save' ) ); + add_action( 'customize_register', array( $this, 'register_controls' ) ); add_action( 'customize_controls_init', array( $this, 'prepare_controls' ) ); add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) ); @@ -148,6 +154,24 @@ final class WP_Customize { $this->customize_preview_init(); } + /** + * Decode the $_POST attribute used to override the WP_Customize_Setting values. + * + * @since 3.4.0 + */ + public function post_value( $setting ) { + if ( ! isset( $this->_post_values ) ) { + if ( isset( $_POST['customized'] ) ) + $this->_post_values = json_decode( stripslashes( $_POST['customized'] ), true ); + else + $this->_post_values = false; + } + + if ( isset( $this->_post_values[ $setting->id ] ) ) + return $setting->sanitize( $this->_post_values[ $setting->id ] ); + } + + /** * Print javascript settings. * @@ -267,9 +291,6 @@ final class WP_Customize { * @since 3.4.0 */ public function admin_init() { - if ( isset( $_REQUEST['save_customize_controls'] ) ) - $this->save(); - if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) return; @@ -297,14 +318,14 @@ final class WP_Customize { */ public function save() { if ( ! $this->is_preview() ) - return; + die; - check_admin_referer( 'customize_controls' ); + check_ajax_referer( 'customize_controls', 'nonce' ); // Do we have to switch themes? if ( $this->get_stylesheet() != $this->original_stylesheet ) { if ( ! current_user_can( 'switch_themes' ) ) - return; + die; // Temporarily stop previewing the theme to allow switch_themes() // to operate properly. @@ -320,6 +341,8 @@ final class WP_Customize { } add_action( 'admin_notices', array( $this, '_save_feedback' ) ); + + die; } /** diff --git a/wp-includes/css/customize-controls.dev.css b/wp-includes/css/customize-controls.dev.css index 45fdde4097..51e06ffb52 100644 --- a/wp-includes/css/customize-controls.dev.css +++ b/wp-includes/css/customize-controls.dev.css @@ -128,6 +128,16 @@ body { margin: 0; } +#customize-footer-actions img { + display: none; + position: absolute; + top: 18px; + margin-left: 4px; +} +.saving #customize-footer-actions img { + display: inline; +} + .customize-control { float: left; clear: both; diff --git a/wp-includes/customize-controls.php b/wp-includes/customize-controls.php index 32a2519035..0e562298a7 100644 --- a/wp-includes/customize-controls.php +++ b/wp-includes/customize-controls.php @@ -41,10 +41,8 @@ do_action( 'customize_controls_print_scripts' ); ?> -
+ - -
-
- -
+
$this->get_stylesheet(), 'preview' => esc_url( home_url( '/', $scheme ) ), 'settings' => array(), 'controls' => array(), - 'prefix' => WP_Customize_Setting::name_prefix, 'parent' => esc_url( admin_url() ), + 'ajax' => esc_url( admin_url( 'admin-ajax.php', 'relative' ) ), ); foreach ( $this->settings as $id => $setting ) { diff --git a/wp-includes/js/customize-base.dev.js b/wp-includes/js/customize-base.dev.js index 67756d3035..bac92c96b8 100644 --- a/wp-includes/js/customize-base.dev.js +++ b/wp-includes/js/customize-base.dev.js @@ -265,6 +265,18 @@ if ( typeof wp === 'undefined' ) return this._value[ id ]; }, + get: function() { + var result = {}; + + if ( arguments.length ) + return this.pass( 'get', arguments ); + + $.each( this._value, function( key, obj ) { + result[ key ] = obj.get(); + } ); + return result; + }, + set: function( id ) { if ( this.has( id ) ) return this.pass( 'set', arguments ); @@ -326,7 +338,7 @@ if ( typeof wp === 'undefined' ) } }); - $.each( [ 'get', 'bind', 'unbind', 'link', 'unlink', 'sync', 'unsync', 'setter', 'resetSetter' ], function( i, method ) { + $.each( [ 'bind', 'unbind', 'link', 'unlink', 'sync', 'unsync', 'setter', 'resetSetter' ], function( i, method ) { api.Values.prototype[ method ] = function() { return this.pass( method, arguments ); }; diff --git a/wp-includes/js/customize-controls.dev.js b/wp-includes/js/customize-controls.dev.js index ec63f44c83..f405a87d64 100644 --- a/wp-includes/js/customize-controls.dev.js +++ b/wp-includes/js/customize-controls.dev.js @@ -15,16 +15,6 @@ this.id = id; this.transport = this.transport || 'refresh'; - element = $( '', { - type: 'hidden', - value: this.get(), - name: api.settings.prefix + id - }); - - element.appendTo( this.previewer.form ); - this.element = new api.Element( element ); - - this.sync( this.element ); this.bind( this.preview ); }, preview: function() { @@ -271,9 +261,8 @@ /** * Requires params: - * - iframe - a selector or jQuery element - * - form - a selector or jQuery element - * - url - the URL of preview frame + * - container - a selector or jQuery element + * - url - the URL of preview frame */ initialize: function( params, options ) { var self = this; @@ -282,8 +271,6 @@ this.loaded = $.proxy( this.loaded, this ); - this.loaderUuid = 0; - /* * Wrap this.refresh to prevent it from hammering the servers: * @@ -320,82 +307,51 @@ }; })( this ); - this.iframe = api.ensure( params.iframe ); - this.form = api.ensure( params.form ); - this.name = this.iframe.prop('name'); + this.container = api.ensure( params.container ); - this.container = this.iframe.parent(); - - api.Messenger.prototype.initialize.call( this, params.url, this.iframe[0].contentWindow ); - - this._formOriginalProps = { - target: this.form.prop('target'), - action: this.form.prop('action') - }; + api.Messenger.prototype.initialize.call( this, params.url ); this.bind( 'url', function( url ) { // Bail if we're navigating to the current url, to a different origin, or wp-admin. - if ( this.url() == url || 0 !== url.indexOf( this.origin() + '/' ) || -1 !== url.indexOf( 'wp-admin' ) ) + if ( this.url() == url || 0 !== url.indexOf( this.origin() + '/' ) || -1 !== url.indexOf( 'wp-admin' ) ) return; this.url( url ); this.refresh(); }); - - this.refresh(); - - // Prevent the form from saving when enter is pressed. - this.form.on( 'keydown', function( e ) { - if ( 13 === e.which ) // Enter - e.preventDefault(); - }); - - // Create a potential postMessage connection with the parent frame. - this.parent = new api.Messenger( api.settings.parent ); - - // If we receive a 'back' event, we're inside an iframe. - // Send any clicks to the 'Return' link to the parent page. - this.parent.bind( 'back', function( text ) { - self.form.find('.back').text( text ).click( function( event ) { - event.preventDefault(); - self.parent.send( 'close' ); - }); - }); - - // Initialize the connection with the parent frame. - this.parent.send( 'ready' ); }, loader: function() { if ( this.loading ) return this.loading; - this.loading = $('