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 = $('', { - name: this.name + '-loading-' + this.loaderUuid++ - }).appendTo( this.container ); + this.loading = $('').appendTo( this.container ); return this.loading; }, loaded: function() { - this.iframe.remove(); + if ( this.iframe ) + this.iframe.remove(); this.iframe = this.loading; delete this.loading; - this.iframe.prop( 'name', this.name ); + this.targetWindow( this.iframe[0].contentWindow ); }, + query: function() {}, refresh: function() { - this.loader().one( 'load', this.loaded ); + var self = this; - this.submit({ - target: this.loader().prop('name'), - action: this.url() + if ( this.request ) + this.request.abort(); + + this.request = $.post( this.url(), this.query() || {}, function( response ) { + var iframe = self.loader()[0].contentWindow; + + self.loader().one( 'load', self.loaded ); + + iframe.document.open(); + iframe.document.write( response ); + iframe.document.close(); }); - }, - submit: function( props ) { - if ( props ) - this.form.prop( props ); - this.form.submit(); - if ( props ) - this.form.prop( this._formOriginalProps ); } }); @@ -415,11 +371,42 @@ // Initialize Previewer var body = $( document.body ), - previewer = new api.Previewer({ - iframe: '#customize-preview iframe', - form: '#customize-controls', - url: api.settings.preview - }); + query, previewer, parent; + + // Prevent the form from saving when enter is pressed. + $('#customize-controls').on( 'keydown', function( e ) { + if ( 13 === e.which ) // Enter + e.preventDefault(); + }); + + previewer = new api.Previewer({ + container: '#customize-preview', + form: '#customize-controls', + url: api.settings.preview + }, { + query: function() { + return { + customize: 'on', + theme: api.settings.theme, + customized: JSON.stringify( api.get() ) + }; + }, + + nonce: $('#_wpnonce').val(), + + save: function() { + var query = $.extend( this.query(), { + action: 'customize_save', + nonce: this.nonce + }), + request = $.post( api.settings.ajax, query ); + + body.addClass('saving'); + request.always( function() { + body.removeClass('saving'); + }); + } + }); $.each( api.settings.settings, function( id, data ) { api.set( id, id, data.value, { @@ -438,6 +425,9 @@ } ) ); }); + // Load the preview frame. + previewer.refresh(); + // Temporary accordion code. $('.customize-section-title').click( function() { $( this ).parents('.customize-section').toggleClass( 'open' ); @@ -446,7 +436,7 @@ // Button bindings. $('#save').click( function( event ) { - previewer.submit(); + previewer.save(); event.preventDefault(); }); @@ -455,6 +445,21 @@ event.preventDefault(); }); + // Create a potential postMessage connection with the parent frame. + 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. + parent.bind( 'back', function( text ) { + $('.back').text( text ).click( function( event ) { + event.preventDefault(); + parent.send( 'close' ); + }); + }); + + // Initialize the connection with the parent frame. + parent.send( 'ready' ); + // Control visibility for default controls $.each({ 'background_image': {