Customize: Add infrastructure for trashing/reverting of unpublished changes; introduce full-screen `OverlayNotification` for trashing and theme install/preview.

* Introduce a new `wp.customize.previewer.trash()` JS API to trash the current changeset, along with logic to `WP_Customize_Manager` to handle deleting changeset drafts.
* Add `trashing` to `wp.customize.state` which is then used to update the UI.
* UI for trashing is pending design feedback. One possibility is to add a new trash button to Publish Settings section that invokes `wp.customize.previewer.trash()`.
* Improve logic for managing the visibility and disabled states for publish buttons.
* Prevent attempting `requestChangesetUpdate` while processing and bump processing while doing `save`.
* Update `changeset_date` state only if sent in save response.
* Merge `ThemesSection#loadThemePreview()` into `ThemesPanel#loadThemePreview()`.
* Remove unused `autosaved` state.
* Start autosaving and prompting at beforeunload after a change first happens. This is key for theme previews since even if a user did not make any changes, there were still dirty settings which would get stored in an auto-draft unexpectedly.
* Allow `Notification` to accept additional `classes` to be added to `container`.
* Introduce `OverlayNotification` and use for theme installing, previewing, and trashing. Such overlay notifications take over the entire window.

Props westonruter, celloexpressions.
See #37661, #39896, #21666, #35210.

Built from https://develop.svn.wordpress.org/trunk@41667


git-svn-id: http://core.svn.wordpress.org/trunk@41501 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Weston Ruter 2017-10-02 03:37:46 +00:00
parent bdd50f28b4
commit 728e44ab5a
16 changed files with 447 additions and 216 deletions

View File

@ -29,11 +29,13 @@ body {
margin-top: 9px;
}
body:not(.ready) #customize-save-button-wrapper .save {
visibility: hidden;
}
#customize-save-button-wrapper .save {
float: right;
border-radius: 3px;
box-shadow: none; /* @todo Adjust box shadow based on the disable states of paired button. */
display: none; /* Shown when ready. */
margin-top: 0;
}
#customize-save-button-wrapper .save.has-next-sibling {
@ -106,18 +108,24 @@ body.outer-section-open .wp-full-overlay.expanded .wp-full-overlay-main {
font-size: 14px;
width: 30px;
float: right;
display: none; /* Shown when ready. */
-webkit-transform: none;
transform: none;
margin-top: 0;
}
body:not(.ready) #publish-settings,
body.trashing #customize-save-button-wrapper .save,
body.trashing #publish-settings {
display: none;
}
#customize-header-actions .spinner {
margin-top: 13px;
margin-left: 4px;
}
.saving #customize-header-actions .spinner {
.saving #customize-header-actions .spinner,
.trashing #customize-header-actions .spinner {
visibility: visible;
}
@ -147,6 +155,23 @@ body.outer-section-open .wp-full-overlay.expanded .wp-full-overlay-main {
padding-left: 12px;
}
#customize-control-trash_changeset {
margin-top: 20px;
}
#customize-control-trash_changeset button {
position: relative;
padding-right: 24px;
display: inline-block;
}
#customize-control-trash_changeset button:before {
content: "\f182";
font: normal 22px dashicons;
text-decoration: none;
position: absolute;
right: 0;
top: -2px;
}
#customize-controls .date-input:invalid {
border-color: red;
}
@ -1126,6 +1151,43 @@ p.customize-section-description {
margin-top: 1px;
}
@-webkit-keyframes customize-fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes customize-fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
#customize-controls .notice.notification-overlay,
#customize-controls #customize-notifications-area .notice.notification-overlay {
margin: 0;
border-right: 0; /* @todo Appropriate styles could be added for notice-error, notice-warning, notice-success, etc */
}
#customize-controls .customize-control-notifications-container.has-overlay-notifications {
-webkit-animation: customize-fade-in 0.5s;
animation: customize-fade-in 0.5s;
z-index: 30;
}
/* Note: Styles for this are also defined in themes.css */
#customize-controls #customize-notifications-area .notice.notification-overlay .notification-message {
clear: both;
color: #191e23;
font-size: 18px;
font-style: normal;
margin: 0;
padding: 2em 0;
text-align: center;
width: 100%;
display: block;
top: 50%;
position: relative;
}
/* Style for custom settings */
/**
@ -1550,40 +1612,6 @@ p.customize-section-description {
* Themes
*/
@-webkit-keyframes customize-reload {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes customize-reload {
0% { opacity: 0; }
100% { opacity: 1; }
}
.wp-customizer .customize-loading #customize-themes-loading-container {
display: block;
-webkit-animation: customize-reload .5s; /* Can't use `transition` because `display` changes here. */
animation: customize-reload .5s;
}
.customize-loading #customize-themes-loading-container span {
clear: both;
color: #191e23;
font-size: 18px;
font-style: normal;
margin: 0;
padding: 2em 0;
text-align: center;
width: 100%;
display: block;
top: 50%;
position: relative;
}
.customize-loading #customize-themes-loading-container .customize-loading-text {
display: none;
}
#customize-theme-controls .control-panel-themes {
border-bottom: none;
}
@ -1673,8 +1701,6 @@ p.customize-section-description {
transition: .18s bottom ease-in-out;
}
.in-themes-panel:not(.animating) #customize-header-actions .save,
.in-themes-panel:not(.animating) #customize-header-actions #publish-settings,
.in-themes-panel:not(.animating) #customize-header-actions .spinner,
.in-themes-panel:not(.animating) #customize-header-actions .customize-controls-preview-toggle,
.in-themes-panel:not(.animating) #customize-preview,

File diff suppressed because one or more lines are too long

View File

@ -29,11 +29,13 @@ body {
margin-top: 9px;
}
body:not(.ready) #customize-save-button-wrapper .save {
visibility: hidden;
}
#customize-save-button-wrapper .save {
float: left;
border-radius: 3px;
box-shadow: none; /* @todo Adjust box shadow based on the disable states of paired button. */
display: none; /* Shown when ready. */
margin-top: 0;
}
#customize-save-button-wrapper .save.has-next-sibling {
@ -106,18 +108,24 @@ body.outer-section-open .wp-full-overlay.expanded .wp-full-overlay-main {
font-size: 14px;
width: 30px;
float: left;
display: none; /* Shown when ready. */
-webkit-transform: none;
transform: none;
margin-top: 0;
}
body:not(.ready) #publish-settings,
body.trashing #customize-save-button-wrapper .save,
body.trashing #publish-settings {
display: none;
}
#customize-header-actions .spinner {
margin-top: 13px;
margin-right: 4px;
}
.saving #customize-header-actions .spinner {
.saving #customize-header-actions .spinner,
.trashing #customize-header-actions .spinner {
visibility: visible;
}
@ -147,6 +155,23 @@ body.outer-section-open .wp-full-overlay.expanded .wp-full-overlay-main {
padding-right: 12px;
}
#customize-control-trash_changeset {
margin-top: 20px;
}
#customize-control-trash_changeset button {
position: relative;
padding-left: 24px;
display: inline-block;
}
#customize-control-trash_changeset button:before {
content: "\f182";
font: normal 22px dashicons;
text-decoration: none;
position: absolute;
left: 0;
top: -2px;
}
#customize-controls .date-input:invalid {
border-color: red;
}
@ -1126,6 +1151,43 @@ p.customize-section-description {
margin-top: 1px;
}
@-webkit-keyframes customize-fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes customize-fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
#customize-controls .notice.notification-overlay,
#customize-controls #customize-notifications-area .notice.notification-overlay {
margin: 0;
border-left: 0; /* @todo Appropriate styles could be added for notice-error, notice-warning, notice-success, etc */
}
#customize-controls .customize-control-notifications-container.has-overlay-notifications {
-webkit-animation: customize-fade-in 0.5s;
animation: customize-fade-in 0.5s;
z-index: 30;
}
/* Note: Styles for this are also defined in themes.css */
#customize-controls #customize-notifications-area .notice.notification-overlay .notification-message {
clear: both;
color: #191e23;
font-size: 18px;
font-style: normal;
margin: 0;
padding: 2em 0;
text-align: center;
width: 100%;
display: block;
top: 50%;
position: relative;
}
/* Style for custom settings */
/**
@ -1550,40 +1612,6 @@ p.customize-section-description {
* Themes
*/
@-webkit-keyframes customize-reload {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes customize-reload {
0% { opacity: 0; }
100% { opacity: 1; }
}
.wp-customizer .customize-loading #customize-themes-loading-container {
display: block;
-webkit-animation: customize-reload .5s; /* Can't use `transition` because `display` changes here. */
animation: customize-reload .5s;
}
.customize-loading #customize-themes-loading-container span {
clear: both;
color: #191e23;
font-size: 18px;
font-style: normal;
margin: 0;
padding: 2em 0;
text-align: center;
width: 100%;
display: block;
top: 50%;
position: relative;
}
.customize-loading #customize-themes-loading-container .customize-loading-text {
display: none;
}
#customize-theme-controls .control-panel-themes {
border-bottom: none;
}
@ -1673,8 +1701,6 @@ p.customize-section-description {
transition: .18s bottom ease-in-out;
}
.in-themes-panel:not(.animating) #customize-header-actions .save,
.in-themes-panel:not(.animating) #customize-header-actions #publish-settings,
.in-themes-panel:not(.animating) #customize-header-actions .spinner,
.in-themes-panel:not(.animating) #customize-header-actions .customize-controls-preview-toggle,
.in-themes-panel:not(.animating) #customize-preview,

File diff suppressed because one or more lines are too long

View File

@ -1707,8 +1707,7 @@ body.full-overlay-active {
}
#customize-container,
#customize-themes-loading-container {
display: none;
#customize-controls .notice.notification-overlay {
background: #eee;
z-index: 500000;
position: fixed;
@ -1719,10 +1718,12 @@ body.full-overlay-active {
left: 0;
height: 100%;
}
#customize-container {
display: none;
}
/* Make the Customizer and Theme installer overlays the only available content. */
#customize-container,
#customize-themes-loading-container,
.theme-install-overlay {
visibility: visible;
}
@ -1827,7 +1828,7 @@ body.full-overlay-active {
#customize-preview.wp-full-overlay-main:before,
.customize-loading #customize-container:before,
.customize-loading #customize-themes-loading-container:before,
#customize-controls .notice.notification-overlay.notification-loading:before,
.theme-install-overlay .wp-full-overlay-main:before {
content: "";
display: block;
@ -1865,7 +1866,7 @@ body.full-overlay-active {
#customize-preview.wp-full-overlay-main:before,
.customize-loading #customize-container:before,
.customize-loading #customize-themes-loading-container:before,
#customize-controls .notice.notification-overlay.notification-loading:before,
.theme-install-overlay .wp-full-overlay-main:before {
background-image: url(../images/spinner-2x.gif);
}

File diff suppressed because one or more lines are too long

View File

@ -1707,8 +1707,7 @@ body.full-overlay-active {
}
#customize-container,
#customize-themes-loading-container {
display: none;
#customize-controls .notice.notification-overlay {
background: #eee;
z-index: 500000;
position: fixed;
@ -1719,10 +1718,12 @@ body.full-overlay-active {
right: 0;
height: 100%;
}
#customize-container {
display: none;
}
/* Make the Customizer and Theme installer overlays the only available content. */
#customize-container,
#customize-themes-loading-container,
.theme-install-overlay {
visibility: visible;
}
@ -1827,7 +1828,7 @@ body.full-overlay-active {
#customize-preview.wp-full-overlay-main:before,
.customize-loading #customize-container:before,
.customize-loading #customize-themes-loading-container:before,
#customize-controls .notice.notification-overlay.notification-loading:before,
.theme-install-overlay .wp-full-overlay-main:before {
content: "";
display: block;
@ -1865,7 +1866,7 @@ body.full-overlay-active {
#customize-preview.wp-full-overlay-main:before,
.customize-loading #customize-container:before,
.customize-loading #customize-themes-loading-container:before,
#customize-controls .notice.notification-overlay.notification-loading:before,
.theme-install-overlay .wp-full-overlay-main:before {
background-image: url(../images/spinner-2x.gif);
}

File diff suppressed because one or more lines are too long

View File

@ -2,6 +2,41 @@
(function( exports, $ ){
var Container, focus, normalizedTransitionendEventName, api = wp.customize;
/**
* A notification that is displayed in a full-screen overlay.
*
* @since 4.9.0
* @class
* @augments wp.customize.Notification
*/
api.OverlayNotification = api.Notification.extend({
/**
* Whether the notification should show a loading spinner.
*
* @since 4.9.0
* @var {boolean}
*/
loading: false,
/**
* Initialize.
*
* @since 4.9.0
*
* @param {string} code - Code.
* @param {object} params - Params.
*/
initialize: function( code, params ) {
var notification = this;
api.Notification.prototype.initialize.call( notification, code, params );
notification.classes += ' notification-overlay';
if ( notification.loading ) {
notification.classes += ' notification-loading';
}
}
});
/**
* A collection of observable notifications.
*
@ -145,7 +180,7 @@
*/
render: function() {
var collection = this,
notifications,
notifications, hadOverlayNotification = false, hasOverlayNotification,
previousNotificationsByCode = {},
listElement;
@ -178,12 +213,30 @@
// Add all notifications in the sorted order.
_.each( notifications, function( notification ) {
var notificationContainer;
if ( wp.a11y && ( ! previousNotificationsByCode[ notification.code ] || ! _.isEqual( notification.message, previousNotificationsByCode[ notification.code ].message ) ) ) {
wp.a11y.speak( notification.message, 'assertive' );
}
listElement.append( $( notification.render() ) ); // @todo Consider slideDown() as enhancement.
notificationContainer = $( notification.render() );
listElement.append( notificationContainer ); // @todo Consider slideDown() as enhancement.
// @todo Constraing focus in notificationContainer if notification.extended( api.OverlayNotification ).
});
hasOverlayNotification = Boolean( _.find( notifications, function( notification ) {
return notification.extended( api.OverlayNotification );
} ) );
if ( collection.previousNotifications ) {
hadOverlayNotification = Boolean( _.find( collection.previousNotifications, function( notification ) {
return notification.extended( api.OverlayNotification );
} ) );
}
if ( hasOverlayNotification !== hadOverlayNotification ) {
$( document.body ).toggleClass( 'customize-loading', hasOverlayNotification );
collection.container.toggleClass( 'has-overlay-notifications', hasOverlayNotification );
}
collection.previousNotifications = notifications;
collection.previousContainer = collection.container;
collection.trigger( 'rendered' );
@ -368,6 +421,12 @@
var deferred, request, submittedChanges = {}, data, submittedArgs;
deferred = new $.Deferred();
// Prevent attempting changeset update while request is being made.
if ( 0 !== api.state( 'processing' ).get() ) {
deferred.reject( 'already_processing' );
return deferred.promise();
}
submittedArgs = _.extend( {
title: null,
date: null,
@ -1572,12 +1631,7 @@
// Preview installed themes.
section.container.on( 'click', '.theme-actions .preview-theme', function() {
var themeId = $( this ).data( 'slug' );
$( '.wp-full-overlay' ).addClass( 'customize-loading' );
api.panel( 'themes' ).loadThemePreview( themeId ).fail( function() {
$( '.wp-full-overlay' ).removeClass( 'customize-loading' );
} );
api.panel( 'themes' ).loadThemePreview( $( this ).data( 'slug' ) );
});
// Theme navigation in details view.
@ -1763,7 +1817,7 @@
// Parameters for every API query. Additional params are set in PHP.
page = Math.ceil( section.loaded / 100 ) + 1;
params = {
'switch-themes-nonce': api.settings.nonce['switch-themes'],
'nonce': api.settings.nonce.switch_themes,
'wp_customize': 'on',
'theme_action': section.params.action,
'customized_theme': api.settings.theme.stylesheet,
@ -1780,7 +1834,7 @@
section.headContainer.closest( '.wp-full-overlay' ).addClass( 'loading' );
section.loading = true;
section.container.find( '.no-themes' ).hide();
request = wp.ajax.post( 'customize-load-themes', params );
request = wp.ajax.post( 'customize_load_themes', params );
request.done(function( data ) {
var themes = data.themes, themeControl, newThemeControls;
@ -2213,58 +2267,12 @@
* @since 4.7.0
* @access public
*
* @deprecated
* @param {string} themeId Theme ID.
* @returns {jQuery.promise} Promise.
*/
loadThemePreview: function( themeId ) {
var deferred = $.Deferred(), onceProcessingComplete, overlay, urlParser;
urlParser = document.createElement( 'a' );
urlParser.href = location.href;
urlParser.search = $.param( _.extend(
api.utils.parseQueryString( urlParser.search.substr( 1 ) ),
{
theme: themeId,
changeset_uuid: api.settings.changeset.uuid
}
) );
overlay = $( '.wp-full-overlay' );
overlay.addClass( 'customize-loading' );
onceProcessingComplete = function() {
var request;
if ( api.state( 'processing' ).get() > 0 ) {
return;
}
api.state( 'processing' ).unbind( onceProcessingComplete );
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();
} );
request.fail( function() {
overlay.removeClass( 'customize-loading' );
deferred.reject();
} );
};
if ( 0 === api.state( 'processing' ).get() ) {
onceProcessingComplete();
} else {
api.state( 'processing' ).bind( onceProcessingComplete );
}
return deferred.promise();
return api.ThemesPanel.prototype.loadThemePreview.call( this, themeId );
},
/**
@ -2844,10 +2852,9 @@
$( document ).one( 'wp-theme-install-success', function( event, response ) {
var theme = false, customizeId, themeControl;
if ( preview ) {
api.notifications.remove( 'theme_installing' );
panel.loadThemePreview( slug ).fail( function() {
$( '.wp-full-overlay' ).removeClass( 'customize-loading' );
} );
panel.loadThemePreview( slug );
} else {
api.control.each( function( control ) {
@ -2899,8 +2906,12 @@
// Also preview the theme as the event is triggered on Install & Preview.
if ( $( event.target ).hasClass( 'preview' ) ) {
preview = true;
$( '.wp-full-overlay' ).addClass( 'customize-loading' );
wp.a11y.speak( $( '#customize-themes-loading-container .customize-loading-text-installing-theme' ).text() );
api.notifications.add( 'theme_installing', new api.OverlayNotification( 'theme_installing', {
message: api.l10n.themeDownloading,
type: 'info',
loading: true
} ) );
}
},
@ -2913,24 +2924,31 @@
* @returns {jQuery.promise} Promise.
*/
loadThemePreview: function( themeId ) {
var deferred = $.Deferred(), onceProcessingComplete, overlay, urlParser;
var deferred = $.Deferred(), onceProcessingComplete, urlParser, queryParams;
urlParser = document.createElement( 'a' );
urlParser.href = location.href;
urlParser.search = $.param( _.extend(
queryParams = _.extend(
api.utils.parseQueryString( urlParser.search.substr( 1 ) ),
{
theme: themeId,
changeset_uuid: api.settings.changeset.uuid
}
) );
);
// Include autosaved param to load autosave revision without prompting user to restore it.
if ( ! api.state( 'saved' ).get() ) {
queryParams.customize_autosaved = 'on';
}
urlParser.search = $.param( queryParams );
// Update loading message. Everything else is handled by reloading the page.
$( '#customize-themes-loading-container span' ).hide();
$( '#customize-themes-loading-container .customize-loading-text' ).css( 'display', 'block' );
wp.a11y.speak( $( '#customize-themes-loading-container .customize-loading-text' ).text() );
overlay = $( '.wp-full-overlay' );
overlay.addClass( 'customize-loading' );
api.notifications.add( 'theme_previewing', new api.OverlayNotification( 'theme_previewing', {
message: api.l10n.themePreviewWait,
type: 'info',
loading: true
} ) );
onceProcessingComplete = function() {
var request;
@ -2940,14 +2958,17 @@
api.state( 'processing' ).unbind( onceProcessingComplete );
request = api.requestChangesetUpdate();
request = api.requestChangesetUpdate( {}, { autosave: true } );
request.done( function() {
deferred.resolve();
$( window ).off( 'beforeunload.customize-confirm' );
window.location.href = urlParser.href;
window.location.href = urlParser.href; // @todo Use location.replace()?
} );
request.fail( function() {
overlay.removeClass( 'customize-loading' );
// @todo Show notification regarding failure.
api.notifications.remove( 'theme_previewing' );
deferred.reject();
} );
};
@ -6230,8 +6251,8 @@
api.state = new api.Values();
_.each( [
'saved',
'autosaved',
'saving',
'trashing',
'activated',
'processing',
'paneVisible',
@ -6279,8 +6300,6 @@
publishSettingsBtn = $( '#publish-settings' ),
footerActions = $( '#customize-footer-actions' );
saveBtn.show();
api.section( 'publish_settings', function( section ) {
var updateButtonsState, previewLinkControl, previewLinkControlId = 'changeset_preview_link', updateSectionActive, isSectionActive;
@ -6301,7 +6320,10 @@
* @return {boolean} Is section active.
*/
isSectionActive = function() {
if ( ! api.state( 'activated' ) ) {
if ( ! api.state( 'activated' ).get() ) {
return false;
}
if ( api.state( 'trashing' ).get() || 'trash' === api.state( 'changesetStatus' ).get() ) {
return false;
}
if ( '' === api.state( 'changesetStatus' ).get() && api.state( 'saved' ).get() ) {
@ -6316,6 +6338,7 @@
section.active.set( isSectionActive() );
};
api.state( 'activated' ).bind( updateSectionActive );
api.state( 'trashing' ).bind( updateSectionActive );
api.state( 'saved' ).bind( updateSectionActive );
api.state( 'changesetStatus' ).bind( updateSectionActive );
updateSectionActive();
@ -6551,15 +6574,13 @@
* due to dependencies on other settings.
*/
request = wp.ajax.post( 'customize_save', query );
// Disable save button during the save request.
saveBtn.prop( 'disabled', true );
api.state( 'processing' ).set( api.state( 'processing' ).get() + 1 );
api.trigger( 'save', request );
request.always( function () {
api.state( 'processing' ).set( api.state( 'processing' ).get() - 1 );
api.state( 'saving' ).set( false );
saveBtn.prop( 'disabled', false );
api.unbind( 'change', captureSettingModifiedDuringSave );
} );
@ -6636,7 +6657,9 @@
previewer.send( 'saved', response );
api.state( 'changesetStatus' ).set( response.changeset_status );
api.state( 'changesetDate' ).set( response.changeset_date );
if ( response.changeset_date ) {
api.state( 'changesetDate' ).set( response.changeset_date );
}
if ( 'publish' === response.changeset_status ) {
@ -6693,6 +6716,74 @@
return deferred.promise();
},
/**
* Trash the current changes.
*
* Revert the Customizer to it's previously-published state.
*
* @since 4.9.0
*
* @returns {jQuery.promise} Promise.
*/
trash: function trash() {
var request, success, fail;
api.state( 'trashing' ).set( true );
api.state( 'processing' ).set( api.state( 'processing' ).get() + 1 );
request = wp.ajax.post( 'customize_trash', {
customize_changeset_uuid: api.settings.changeset.uuid,
nonce: api.settings.nonce.trash
} );
api.notifications.add( 'changeset_trashing', new api.OverlayNotification( 'changeset_trashing', {
type: 'info',
message: api.l10n.revertingChanges,
loading: true
} ) );
success = function() {
var urlParser = document.createElement( 'a' ), queryParams;
api.state( 'changesetStatus' ).set( 'trash' );
api.each( function( setting ) {
setting._dirty = false;
} );
api.state( 'saved' ).set( true );
// Go back to Customizer without changeset.
urlParser.href = location.href;
queryParams = api.utils.parseQueryString( urlParser.search.substr( 1 ) );
delete queryParams.changeset_uuid;
urlParser.search = $.param( queryParams );
location.replace( urlParser.href );
};
fail = function( code, message ) {
var notificationCode = code || 'unknown_error';
api.state( 'processing' ).set( api.state( 'processing' ).get() - 1 );
api.state( 'trashing' ).set( false );
api.notifications.remove( 'changeset_trashing' );
api.notifications.add( notificationCode, new api.Notification( notificationCode, {
message: message || api.l10n.unknownError,
dismissible: true,
type: 'error'
} ) );
};
request.done( function( response ) {
success( response.message );
} );
request.fail( function( response ) {
var code = response.code || 'trashing_failed';
if ( response.success || 'non_existent_changeset' === code || 'changeset_already_trashed' === code ) {
success( response.message );
} else {
fail( code, response.message );
}
} );
},
/**
* Builds the front preview url with the current state of customizer.
*
@ -6842,6 +6933,7 @@
(function( state ) {
var saved = state.instance( 'saved' ),
saving = state.instance( 'saving' ),
trashing = state.instance( 'trashing' ),
activated = state.instance( 'activated' ),
processing = state.instance( 'processing' ),
paneVisible = state.instance( 'paneVisible' ),
@ -6863,7 +6955,6 @@
if ( ! activated() ) {
saveBtn.val( api.l10n.activate );
closeBtn.find( '.screen-reader-text' ).text( api.l10n.cancel );
publishSettingsBtn.prop( 'disabled', false );
} else if ( '' === changesetStatus.get() && saved() ) {
if ( api.settings.changeset.currentUserCanPublish ) {
@ -6871,7 +6962,6 @@
} else {
saveBtn.val( api.l10n.saved );
}
publishSettingsBtn.prop( 'disabled', true );
closeBtn.find( '.screen-reader-text' ).text( api.l10n.close );
} else {
@ -6899,14 +6989,13 @@
saveBtn.val( api.l10n.publish );
}
closeBtn.find( '.screen-reader-text' ).text( api.l10n.cancel );
publishSettingsBtn.prop( 'disabled', false );
}
/*
* Save (publish) button should be enabled if saving is not currently happening,
* and if the theme is not active or the changeset exists but is not published.
*/
canSave = ! saving() && ( ! activated() || ! saved() || ( changesetStatus() !== selectedChangesetStatus() && '' !== changesetStatus() ) || ( 'future' === selectedChangesetStatus() && changesetDate.get() !== selectedChangesetDate.get() ) );
canSave = ! saving() && ! trashing() && ( ! activated() || ! saved() || ( changesetStatus() !== selectedChangesetStatus() && '' !== changesetStatus() ) || ( 'future' === selectedChangesetStatus() && changesetDate.get() !== selectedChangesetDate.get() ) );
saveBtn.prop( 'disabled', ! canSave );
});
@ -6958,6 +7047,9 @@
saving.bind( function( isSaving ) {
body.toggleClass( 'saving', isSaving );
} );
trashing.bind( function( isTrashing ) {
body.toggleClass( 'trashing', isTrashing );
} );
api.bind( 'saved', function( response ) {
state('saved').set( true );
@ -7028,7 +7120,7 @@
// Show changeset UUID in URL when in branching mode and there is a saved changeset.
if ( api.settings.changeset.branching ) {
changesetStatus.bind( function( newStatus ) {
populateChangesetUuidParam( '' !== newStatus && 'publish' !== newStatus );
populateChangesetUuidParam( '' !== newStatus && 'publish' !== newStatus && 'trash' !== newStatus );
} );
}
}( api.state ) );
@ -7105,7 +7197,7 @@
// Handle dismissal of notice.
li.find( '.notice-dismiss' ).on( 'click', function() {
wp.ajax.post( 'dismiss_customize_changeset_autosave', {
wp.ajax.post( 'customize_dismiss_autosave', {
wp_customize: 'on',
customize_theme: api.settings.theme.stylesheet,
customize_changeset_uuid: api.settings.changeset.uuid,
@ -7532,15 +7624,20 @@
isInsideIframe = true;
});
// Prompt user with AYS dialog if leaving the Customizer with unsaved changes
$( window ).on( 'beforeunload.customize-confirm', function() {
if ( ! isCleanState() ) {
setTimeout( function() {
overlay.removeClass( 'customize-loading' );
}, 1 );
return api.l10n.saveAlert;
}
});
function startPromptingBeforeUnload() {
api.unbind( 'change', startPromptingBeforeUnload );
// Prompt user with AYS dialog if leaving the Customizer with unsaved changes
$( window ).on( 'beforeunload.customize-confirm', function() {
if ( ! isCleanState() ) {
setTimeout( function() {
overlay.removeClass( 'customize-loading' );
}, 1 );
return api.l10n.saveAlert;
}
});
}
api.bind( 'change', startPromptingBeforeUnload );
closeBtn.on( 'click.customize-controls-close', function( event ) {
var clearedToClose = $.Deferred();
@ -7566,7 +7663,7 @@
if ( '' === api.state( 'changesetStatus' ).get() ) {
clearedToClose.resolve();
} else {
wp.ajax.send( 'dismiss_customize_changeset_autosave', {
wp.ajax.send( 'customize_dismiss_autosave', {
timeout: 500, // Don't wait too long.
data: {
wp_customize: 'on',
@ -7987,9 +8084,11 @@
} );
// Autosave changeset.
( function() {
function startAutosaving() {
var timeoutId, updateChangesetWithReschedule, scheduleChangesetUpdate, updatePending = false;
api.unbind( 'change', startAutosaving ); // Ensure startAutosaving only fires once.
api.state( 'saved' ).bind( function( isSaved ) {
if ( ! isSaved && ! api.settings.changeset.autosaved ) {
api.settings.changeset.autosaved = true; // Once a change is made then autosaving kicks in.
@ -8040,7 +8139,8 @@
$( window ).on( 'beforeunload.wp-customize-changeset-update', function() {
updateChangesetWithReschedule();
} );
} ());
}
api.bind( 'change', startAutosaving );
// Make sure TinyMCE dialogs appear above Customizer UI.
$( document ).one( 'wp-before-tinymce-init', function() {
@ -8049,6 +8149,7 @@
}
} );
body.addClass( 'ready' );
api.trigger( 'ready' );
});

File diff suppressed because one or more lines are too long

View File

@ -374,10 +374,11 @@ final class WP_Customize_Manager {
remove_action( 'admin_init', '_maybe_update_plugins' );
remove_action( 'admin_init', '_maybe_update_themes' );
add_action( 'wp_ajax_customize_save', array( $this, 'save' ) );
add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) );
add_action( 'wp_ajax_customize-load-themes', array( $this, 'load_themes_ajax' ) );
add_action( 'wp_ajax_dismiss_customize_changeset_autosave', array( $this, 'handle_dismiss_changeset_autosave_request' ) );
add_action( 'wp_ajax_customize_save', array( $this, 'save' ) );
add_action( 'wp_ajax_customize_trash', array( $this, 'handle_changeset_trash_request' ) );
add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) );
add_action( 'wp_ajax_customize_load_themes', array( $this, 'handle_load_themes_request' ) );
add_action( 'wp_ajax_customize_dismiss_autosave', array( $this, 'handle_dismiss_autosave_request' ) );
add_action( 'customize_register', array( $this, 'register_controls' ) );
add_action( 'customize_register', array( $this, 'register_dynamic_settings' ), 11 ); // allow code to create settings first
@ -2829,6 +2830,65 @@ final class WP_Customize_Manager {
return $response;
}
/**
* Handle request to trash a changeset.
*
* @since 4.9.0
*/
public function handle_changeset_trash_request() {
if ( ! is_user_logged_in() ) {
wp_send_json_error( 'unauthenticated' );
}
if ( ! $this->is_preview() ) {
wp_send_json_error( 'not_preview' );
}
if ( ! check_ajax_referer( 'trash_customize_changeset', 'nonce', false ) ) {
wp_send_json_error( array(
'code' => 'invalid_nonce',
'message' => __( 'There was an authentication problem. Please reload and try again.' ),
) );
}
$changeset_post_id = $this->changeset_post_id();
if ( ! $changeset_post_id ) {
wp_send_json_error( array(
'message' => __( 'No changes saved yet, so there is nothing to trash.' ),
'code' => 'non_existent_changeset',
) );
return;
}
if ( $changeset_post_id && ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->delete_post, $changeset_post_id ) ) {
wp_send_json_error( array(
'code' => 'changeset_trash_unauthorized',
'message' => __( 'Unable to trash changes.' ),
) );
}
if ( 'trash' === get_post_status( $changeset_post_id ) ) {
wp_send_json_error( array(
'message' => __( 'Changes have already been trashed.' ),
'code' => 'changeset_already_trashed',
) );
return;
}
$r = wp_trash_post( $changeset_post_id );
if ( ! ( $r instanceof WP_Post ) ) {
wp_send_json_error( array(
'code' => 'changeset_trash_failure',
'message' => __( 'Unable to trash changes.' ),
) );
}
wp_send_json_success( array(
'message' => __( 'Changes trashed successfully.' ),
) );
}
/**
* Re-map 'edit_post' meta cap for a customize_changeset post to be the same as 'customize' maps.
*
@ -3102,12 +3162,12 @@ final class WP_Customize_Manager {
*
* @since 4.9.0
*/
public function handle_dismiss_changeset_autosave_request() {
public function handle_dismiss_autosave_request() {
if ( ! $this->is_preview() ) {
wp_send_json_error( 'not_preview', 400 );
}
if ( ! check_ajax_referer( 'dismiss_customize_changeset_autosave', 'nonce', false ) ) {
if ( ! check_ajax_referer( 'customize_dismiss_autosave', 'nonce', false ) ) {
wp_send_json_error( 'invalid_nonce', 403 );
}
@ -3543,8 +3603,8 @@ final class WP_Customize_Manager {
?>
<script type="text/html" id="tmpl-customize-notification">
<li class="notice notice-{{ data.type || 'info' }} {{ data.alt ? 'notice-alt' : '' }} {{ data.dismissible ? 'is-dismissible' : '' }}" data-code="{{ data.code }}" data-type="{{ data.type }}">
{{{ data.message || data.code }}}
<li class="notice notice-{{ data.type || 'info' }} {{ data.alt ? 'notice-alt' : '' }} {{ data.dismissible ? 'is-dismissible' : '' }} {{ data.classes || '' }}" data-code="{{ data.code }}" data-type="{{ data.type }}">
<div class="notification-message">{{{ data.message || data.code }}}</div>
<# if ( data.dismissible ) { #>
<button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php _e( 'Dismiss' ); ?></span></button>
<# } #>
@ -3576,6 +3636,9 @@ final class WP_Customize_Manager {
</label>
</div>
</script>
<script type="text/html" id="tmpl-customize-trash-changeset-control">
<button type="button" class="button-link button-link-delete"><?php _e( 'Trash unpublished changes' ); ?></button>
</script>
<?php
}
@ -3901,8 +3964,9 @@ final class WP_Customize_Manager {
$nonces = array(
'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ),
'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ),
'switch-themes' => wp_create_nonce( 'switch-themes' ),
'dismiss_autosave' => wp_create_nonce( 'dismiss_customize_changeset_autosave' ),
'switch_themes' => wp_create_nonce( 'switch_themes' ),
'dismiss_autosave' => wp_create_nonce( 'customize_dismiss_autosave' ),
'trash' => wp_create_nonce( 'trash_customize_changeset' ),
);
/**
@ -4159,6 +4223,7 @@ final class WP_Customize_Manager {
$this->add_control( 'changeset_status', array(
'section' => 'publish_settings',
'priority' => 10,
'settings' => array(),
'type' => 'radio',
'label' => __( 'Action' ),
@ -4173,6 +4238,7 @@ final class WP_Customize_Manager {
}
$this->add_control( new WP_Customize_Date_Time_Control( $this, 'changeset_scheduled_date', array(
'section' => 'publish_settings',
'priority' => 20,
'settings' => array(),
'type' => 'date_time',
'min_year' => date( 'Y' ),
@ -4723,8 +4789,8 @@ final class WP_Customize_Manager {
*
* @since 4.9.0
*/
public function load_themes_ajax() {
check_ajax_referer( 'switch-themes', 'switch-themes-nonce' );
public function handle_load_themes_request() {
check_ajax_referer( 'switch_themes', 'nonce' );
if ( ! current_user_can( 'switch_themes' ) ) {
wp_die( -1 );

View File

@ -89,10 +89,6 @@ class WP_Customize_Themes_Panel extends WP_Customize_Panel {
<# } #>
<?php endif; ?>
</li>
<li id="customize-themes-loading-container">
<span class="customize-loading-text-installing-theme"><?php _e( 'Downloading your new theme&hellip;' ); ?></span>
<span class="customize-loading-text"><?php _e( 'Setting up your live preview. This may take a bit.' ); ?></span>
</li><?php // Used as a full-screen overlay transition after clicking to preview a theme. ?>
<li class="customize-themes-full-container-container">
<ul class="customize-themes-full-container">
<li class="customize-themes-notifications"></li>

View File

@ -809,6 +809,14 @@ window.wp = window.wp || {};
*/
templateId: 'customize-notification',
/**
* Additional class names to add to the notification container.
*
* @since 4.9.0
* @var {string}
*/
classes: '',
/**
* Initialize notification.
*
@ -821,6 +829,7 @@ window.wp = window.wp || {};
* @param {string} [params.setting] - Related setting ID.
* @param {Function} [params.template] - Function for rendering template. If not provided, this will come from templateId.
* @param {string} [params.templateId] - ID for template to render the notification.
* @param {string} [params.classes] - Additional class names to add to the notification container.
* @param {boolean} [params.dismissible] - Whether the notification can be dismissed.
*/
initialize: function( code, params ) {
@ -834,7 +843,8 @@ window.wp = window.wp || {};
data: null,
setting: null,
template: null,
dismissible: false
dismissible: false,
classes: ''
},
params
);

File diff suppressed because one or more lines are too long

View File

@ -571,6 +571,10 @@ function wp_default_scripts( &$scripts ) {
'expandSidebar' => _x( 'Show Controls', 'label for hide controls button without length constraints' ),
'untitledBlogName' => __( '(Untitled)' ),
'serverSaveError' => __( 'Failed connecting to the server. Please try saving again.' ),
'themeDownloading' => __( 'Downloading your new theme&hellip;' ),
'themePreviewWait' => __( 'Setting up your live preview. This may take a bit.' ),
'revertingChanges' => __( 'Reverting unpublished changes&hellip;' ),
'trashConfirm' => __( 'Are you sure you would like to discard your unpublished changes?' ),
/* translators: %s: URL to the Customizer to load the autosaved version */
'autosaveNotice' => __( 'There is a more recent autosave of your changes than the one you are previewing. <a href="%s">Restore the autosave</a>' ),
'videoHeaderNotice' => __( 'This theme doesn\'t support video headers on this page. Navigate to the front page or another page that supports video headers.' ),

View File

@ -4,7 +4,7 @@
*
* @global string $wp_version
*/
$wp_version = '4.9-alpha-41666';
$wp_version = '4.9-alpha-41667';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.