From 2f878162a106cf03a7a718aa6b9e829ac166a12c Mon Sep 17 00:00:00 2001 From: westonruter Date: Tue, 26 Sep 2017 07:37:02 +0000 Subject: [PATCH] Customize: Extend changesets to support autosave revisions with restoration notifications, and introduce a new default linear history mode for saved changesets (with a filter for opt-in to changeset branching). * Autosaved changes made on top of `auto-draft` changesets get written on top of the `auto-draft` itself, similar to how autosaves for posts will overwrite post drafts. * Autosaved changes made to saved changesets (e.g. `draft`, `future`) will be placed into an autosave revision for that changeset and that user. * Opening the Customizer will now prompt the user to restore their most recent auto-draft changeset; if notification is dismissed or ignored then the auto-draft will be marked as dismissed and will not be prompted to user in a notification again. * Customizer will no longer automatically supply the `changeset_uuid` param in the `customize.php` URL when branching changesets are not active. * If user closes Customizer explicitly via clicking on X link, then autosave auto-draft/autosave will be dismissed so as to not be prompted again. * If there is a changeset already saved as a `draft` or `future` (UI is forthcoming) then this changeset will now be autoloaded for the user to keep making additional changes. This is the linear model for changesets. * To restore the previous behavior of the Customizer where each session started a new changeset, regardless of whether or not there was an existing changeset saved, there is now a `customize_changeset_branching` hook which can be filtered to return `true`. * `wp.customize.requestChangesetUpdate()` now supports a second with options including `autosave`, `title`, and `date`. * The window `blur` event for `customize.php` has been replaced with a `visibilitychange` event to reduce autosave requests when clicking into preview window. * Adds `autosaved` and `branching` args to `WP_Customize_Manager`. * The `changeset_uuid` param for `WP_Customize_Manager` is extended to recognize a `false` value which causes the Customizer to defer identifying the UUID until `after_setup_theme` in the new `WP_Customize_Manager::establish_loaded_changeset()` method. * A new `customize_autosaved` query parameter can now be supplied which is passed into the `autosaved` arg in `WP_Customize_Manager`; this option is an opt-in to source data from the autosave revision, allowing a user to restore autosaved changes. Props westonruter, dlh, sayedwp, JoshuaWold, melchoyce. See #39896. git-svn-id: https://develop.svn.wordpress.org/trunk@41597 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/js/customize-controls.js | 297 ++++++++++++-- .../class-wp-customize-manager.php | 370 ++++++++++++++++-- src/wp-includes/js/customize-preview.js | 34 +- src/wp-includes/script-loader.php | 2 + src/wp-includes/theme.php | 13 +- 5 files changed, 652 insertions(+), 64 deletions(-) diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js index a12ebe6e13..17ce623f6c 100644 --- a/src/wp-admin/js/customize-controls.js +++ b/src/wp-admin/js/customize-controls.js @@ -1,4 +1,4 @@ -/* global _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer, console */ +/* global _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer, console, confirm */ (function( exports, $ ){ var Container, focus, normalizedTransitionendEventName, api = wp.customize; @@ -355,14 +355,24 @@ * @since 4.7.0 * @access public * - * @param {object} [changes] Mapping of setting IDs to setting params each normally including a value property, or mapping to null. - * If not provided, then the changes will still be obtained from unsaved dirty settings. + * @param {object} [changes] - Mapping of setting IDs to setting params each normally including a value property, or mapping to null. + * If not provided, then the changes will still be obtained from unsaved dirty settings. + * @param {object} [args] - Additional options for the save request. + * @param {boolean} [args.autosave=false] - Whether changes will be stored in autosave revision if the changeset has been promoted from an auto-draft. + * @param {string} [args.title] - Title to update in the changeset. Optional. + * @param {string} [args.date] - Date to update in the changeset. Optional. * @returns {jQuery.Promise} Promise resolving with the response data. */ - api.requestChangesetUpdate = function requestChangesetUpdate( changes ) { - var deferred, request, submittedChanges = {}, data; + api.requestChangesetUpdate = function requestChangesetUpdate( changes, args ) { + var deferred, request, submittedChanges = {}, data, submittedArgs; deferred = new $.Deferred(); + submittedArgs = _.extend( { + title: null, + date: null, + autosave: false + }, args ); + if ( changes ) { _.extend( submittedChanges, changes ); } @@ -379,20 +389,30 @@ } ); // Short-circuit when there are no pending changes. - if ( _.isEmpty( submittedChanges ) ) { + if ( _.isEmpty( submittedChanges ) && null === submittedArgs.title && null === submittedArgs.date ) { deferred.resolve( {} ); return deferred.promise(); } + // Allow plugins to attach additional params to the settings. + api.trigger( 'changeset-save', submittedChanges, submittedArgs ); + + // A status would cause a revision to be made, and for this wp.customize.previewer.save() should be used. Status is also disallowed for revisions regardless. + if ( submittedArgs.status ) { + return deferred.reject( { code: 'illegal_status_in_changeset_update' } ).promise(); + } + + // Dates not beung allowed for revisions are is a technical limitation of post revisions. + if ( submittedArgs.date && submittedArgs.autosave ) { + return deferred.reject( { code: 'illegal_autosave_with_date_gmt' } ).promise(); + } + // Make sure that publishing a changeset waits for all changeset update requests to complete. api.state( 'processing' ).set( api.state( 'processing' ).get() + 1 ); deferred.always( function() { api.state( 'processing' ).set( api.state( 'processing' ).get() - 1 ); } ); - // Allow plugins to attach additional params to the settings. - api.trigger( 'changeset-save', submittedChanges ); - // Ensure that if any plugins add data to save requests by extending query() that they get included here. data = api.previewer.query( { excludeCustomizedSaved: true } ); delete data.customized; // Being sent in customize_changeset_data instead. @@ -401,6 +421,15 @@ customize_theme: api.settings.theme.stylesheet, customize_changeset_data: JSON.stringify( submittedChanges ) } ); + if ( null !== submittedArgs.title ) { + data.customize_changeset_title = submittedArgs.title; + } + if ( null !== submittedArgs.date ) { + data.customize_changeset_date = submittedArgs.date; + } + if ( false !== submittedArgs.autosave ) { + data.customize_changeset_autosave = 'true'; + } request = wp.ajax.post( 'customize_save', data ); @@ -1705,9 +1734,15 @@ api.state( 'processing' ).unbind( onceProcessingComplete ); - request = api.requestChangesetUpdate(); + request = api.requestChangesetUpdate( {}, { autosave: true } ); request.done( function() { $( window ).off( 'beforeunload.customize-confirm' ); + + // Include autosaved param to load autosave revision without prompting user to restore it. + if ( ! api.state( 'saved' ).get() ) { + urlParser.search += '&customize_autosaved=on'; + } + top.location.href = urlParser.href; deferred.resolve(); } ); @@ -4024,6 +4059,9 @@ customize_messenger_channel: previewFrame.query.customize_messenger_channel } ); + if ( ! api.state( 'saved' ).get() ) { + params.customize_autosaved = 'on'; + } urlParser.search = $.param( params ); previewFrame.iframe = $( '