Changeset 41667
- Timestamp:
- 10/02/2017 03:36:18 AM (7 years ago)
- Location:
- trunk
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/css/customize-controls.css
r41649 r41667 30 30 } 31 31 32 body:not(.ready) #customize-save-button-wrapper .save { 33 visibility: hidden; 34 } 32 35 #customize-save-button-wrapper .save { 33 36 float: left; 34 37 border-radius: 3px; 35 38 box-shadow: none; /* @todo Adjust box shadow based on the disable states of paired button. */ 36 display: none; /* Shown when ready. */37 39 margin-top: 0; 38 40 } … … 107 109 width: 30px; 108 110 float: left; 109 display: none; /* Shown when ready. */110 111 -webkit-transform: none; 111 112 transform: none; … … 113 114 } 114 115 116 body:not(.ready) #publish-settings, 117 body.trashing #customize-save-button-wrapper .save, 118 body.trashing #publish-settings { 119 display: none; 120 } 121 115 122 #customize-header-actions .spinner { 116 123 margin-top: 13px; … … 118 125 } 119 126 120 .saving #customize-header-actions .spinner { 127 .saving #customize-header-actions .spinner, 128 .trashing #customize-header-actions .spinner { 121 129 visibility: visible; 122 130 } … … 146 154 padding-left: 12px; 147 155 padding-right: 12px; 156 } 157 158 #customize-control-trash_changeset { 159 margin-top: 20px; 160 } 161 #customize-control-trash_changeset button { 162 position: relative; 163 padding-left: 24px; 164 display: inline-block; 165 } 166 #customize-control-trash_changeset button:before { 167 content: "\f182"; 168 font: normal 22px dashicons; 169 text-decoration: none; 170 position: absolute; 171 left: 0; 172 top: -2px; 148 173 } 149 174 … … 1127 1152 } 1128 1153 1154 @-webkit-keyframes customize-fade-in { 1155 0% { opacity: 0; } 1156 100% { opacity: 1; } 1157 } 1158 1159 @keyframes customize-fade-in { 1160 0% { opacity: 0; } 1161 100% { opacity: 1; } 1162 } 1163 1164 #customize-controls .notice.notification-overlay, 1165 #customize-controls #customize-notifications-area .notice.notification-overlay { 1166 margin: 0; 1167 border-left: 0; /* @todo Appropriate styles could be added for notice-error, notice-warning, notice-success, etc */ 1168 } 1169 1170 #customize-controls .customize-control-notifications-container.has-overlay-notifications { 1171 -webkit-animation: customize-fade-in 0.5s; 1172 animation: customize-fade-in 0.5s; 1173 z-index: 30; 1174 } 1175 1176 /* Note: Styles for this are also defined in themes.css */ 1177 #customize-controls #customize-notifications-area .notice.notification-overlay .notification-message { 1178 clear: both; 1179 color: #191e23; 1180 font-size: 18px; 1181 font-style: normal; 1182 margin: 0; 1183 padding: 2em 0; 1184 text-align: center; 1185 width: 100%; 1186 display: block; 1187 top: 50%; 1188 position: relative; 1189 } 1190 1129 1191 /* Style for custom settings */ 1130 1192 … … 1551 1613 */ 1552 1614 1553 @-webkit-keyframes customize-reload {1554 0% { opacity: 0; }1555 100% { opacity: 1; }1556 }1557 1558 @keyframes customize-reload {1559 0% { opacity: 0; }1560 100% { opacity: 1; }1561 }1562 1563 .wp-customizer .customize-loading #customize-themes-loading-container {1564 display: block;1565 -webkit-animation: customize-reload .5s; /* Can't use `transition` because `display` changes here. */1566 animation: customize-reload .5s;1567 }1568 1569 .customize-loading #customize-themes-loading-container span {1570 clear: both;1571 color: #191e23;1572 font-size: 18px;1573 font-style: normal;1574 margin: 0;1575 padding: 2em 0;1576 text-align: center;1577 width: 100%;1578 display: block;1579 top: 50%;1580 position: relative;1581 }1582 1583 .customize-loading #customize-themes-loading-container .customize-loading-text {1584 display: none;1585 }1586 1587 1615 #customize-theme-controls .control-panel-themes { 1588 1616 border-bottom: none; … … 1674 1702 } 1675 1703 1676 .in-themes-panel:not(.animating) #customize-header-actions .save,1677 .in-themes-panel:not(.animating) #customize-header-actions #publish-settings,1678 1704 .in-themes-panel:not(.animating) #customize-header-actions .spinner, 1679 1705 .in-themes-panel:not(.animating) #customize-header-actions .customize-controls-preview-toggle, -
trunk/src/wp-admin/css/themes.css
r41648 r41667 1708 1708 1709 1709 #customize-container, 1710 #customize-themes-loading-container { 1711 display: none; 1710 #customize-controls .notice.notification-overlay { 1712 1711 background: #eee; 1713 1712 z-index: 500000; … … 1720 1719 height: 100%; 1721 1720 } 1721 #customize-container { 1722 display: none; 1723 } 1722 1724 1723 1725 /* Make the Customizer and Theme installer overlays the only available content. */ 1724 1726 #customize-container, 1725 #customize-themes-loading-container,1726 1727 .theme-install-overlay { 1727 1728 visibility: visible; … … 1828 1829 #customize-preview.wp-full-overlay-main:before, 1829 1830 .customize-loading #customize-container:before, 1830 .customize-loading #customize-themes-loading-container:before,1831 #customize-controls .notice.notification-overlay.notification-loading:before, 1831 1832 .theme-install-overlay .wp-full-overlay-main:before { 1832 1833 content: ""; … … 1866 1867 #customize-preview.wp-full-overlay-main:before, 1867 1868 .customize-loading #customize-container:before, 1868 .customize-loading #customize-themes-loading-container:before,1869 #customize-controls .notice.notification-overlay.notification-loading:before, 1869 1870 .theme-install-overlay .wp-full-overlay-main:before { 1870 1871 background-image: url(../images/spinner-2x.gif); -
trunk/src/wp-admin/js/customize-controls.js
r41650 r41667 2 2 (function( exports, $ ){ 3 3 var Container, focus, normalizedTransitionendEventName, api = wp.customize; 4 5 /** 6 * A notification that is displayed in a full-screen overlay. 7 * 8 * @since 4.9.0 9 * @class 10 * @augments wp.customize.Notification 11 */ 12 api.OverlayNotification = api.Notification.extend({ 13 14 /** 15 * Whether the notification should show a loading spinner. 16 * 17 * @since 4.9.0 18 * @var {boolean} 19 */ 20 loading: false, 21 22 /** 23 * Initialize. 24 * 25 * @since 4.9.0 26 * 27 * @param {string} code - Code. 28 * @param {object} params - Params. 29 */ 30 initialize: function( code, params ) { 31 var notification = this; 32 api.Notification.prototype.initialize.call( notification, code, params ); 33 notification.classes += ' notification-overlay'; 34 if ( notification.loading ) { 35 notification.classes += ' notification-loading'; 36 } 37 } 38 }); 4 39 5 40 /** … … 146 181 render: function() { 147 182 var collection = this, 148 notifications, 183 notifications, hadOverlayNotification = false, hasOverlayNotification, 149 184 previousNotificationsByCode = {}, 150 185 listElement; … … 179 214 // Add all notifications in the sorted order. 180 215 _.each( notifications, function( notification ) { 216 var notificationContainer; 181 217 if ( wp.a11y && ( ! previousNotificationsByCode[ notification.code ] || ! _.isEqual( notification.message, previousNotificationsByCode[ notification.code ].message ) ) ) { 182 218 wp.a11y.speak( notification.message, 'assertive' ); 183 219 } 184 listElement.append( $( notification.render() ) ); // @todo Consider slideDown() as enhancement. 185 }); 220 notificationContainer = $( notification.render() ); 221 listElement.append( notificationContainer ); // @todo Consider slideDown() as enhancement. 222 223 // @todo Constraing focus in notificationContainer if notification.extended( api.OverlayNotification ). 224 }); 225 226 hasOverlayNotification = Boolean( _.find( notifications, function( notification ) { 227 return notification.extended( api.OverlayNotification ); 228 } ) ); 229 if ( collection.previousNotifications ) { 230 hadOverlayNotification = Boolean( _.find( collection.previousNotifications, function( notification ) { 231 return notification.extended( api.OverlayNotification ); 232 } ) ); 233 } 234 235 if ( hasOverlayNotification !== hadOverlayNotification ) { 236 $( document.body ).toggleClass( 'customize-loading', hasOverlayNotification ); 237 collection.container.toggleClass( 'has-overlay-notifications', hasOverlayNotification ); 238 } 186 239 187 240 collection.previousNotifications = notifications; … … 368 421 var deferred, request, submittedChanges = {}, data, submittedArgs; 369 422 deferred = new $.Deferred(); 423 424 // Prevent attempting changeset update while request is being made. 425 if ( 0 !== api.state( 'processing' ).get() ) { 426 deferred.reject( 'already_processing' ); 427 return deferred.promise(); 428 } 370 429 371 430 submittedArgs = _.extend( { … … 1573 1632 // Preview installed themes. 1574 1633 section.container.on( 'click', '.theme-actions .preview-theme', function() { 1575 var themeId = $( this ).data( 'slug' ); 1576 1577 $( '.wp-full-overlay' ).addClass( 'customize-loading' ); 1578 api.panel( 'themes' ).loadThemePreview( themeId ).fail( function() { 1579 $( '.wp-full-overlay' ).removeClass( 'customize-loading' ); 1580 } ); 1634 api.panel( 'themes' ).loadThemePreview( $( this ).data( 'slug' ) ); 1581 1635 }); 1582 1636 … … 1764 1818 page = Math.ceil( section.loaded / 100 ) + 1; 1765 1819 params = { 1766 ' switch-themes-nonce': api.settings.nonce['switch-themes'],1820 'nonce': api.settings.nonce.switch_themes, 1767 1821 'wp_customize': 'on', 1768 1822 'theme_action': section.params.action, … … 1781 1835 section.loading = true; 1782 1836 section.container.find( '.no-themes' ).hide(); 1783 request = wp.ajax.post( 'customize -load-themes', params );1837 request = wp.ajax.post( 'customize_load_themes', params ); 1784 1838 request.done(function( data ) { 1785 1839 var themes = data.themes, themeControl, newThemeControls; … … 2214 2268 * @access public 2215 2269 * 2270 * @deprecated 2216 2271 * @param {string} themeId Theme ID. 2217 2272 * @returns {jQuery.promise} Promise. 2218 2273 */ 2219 2274 loadThemePreview: function( themeId ) { 2220 var deferred = $.Deferred(), onceProcessingComplete, overlay, urlParser; 2221 2222 urlParser = document.createElement( 'a' ); 2223 urlParser.href = location.href; 2224 urlParser.search = $.param( _.extend( 2225 api.utils.parseQueryString( urlParser.search.substr( 1 ) ), 2226 { 2227 theme: themeId, 2228 changeset_uuid: api.settings.changeset.uuid 2229 } 2230 ) ); 2231 2232 overlay = $( '.wp-full-overlay' ); 2233 overlay.addClass( 'customize-loading' ); 2234 2235 onceProcessingComplete = function() { 2236 var request; 2237 if ( api.state( 'processing' ).get() > 0 ) { 2238 return; 2239 } 2240 2241 api.state( 'processing' ).unbind( onceProcessingComplete ); 2242 2243 request = api.requestChangesetUpdate( {}, { autosave: true } ); 2244 request.done( function() { 2245 $( window ).off( 'beforeunload.customize-confirm' ); 2246 2247 // Include autosaved param to load autosave revision without prompting user to restore it. 2248 if ( ! api.state( 'saved' ).get() ) { 2249 urlParser.search += '&customize_autosaved=on'; 2250 } 2251 2252 top.location.href = urlParser.href; 2253 deferred.resolve(); 2254 } ); 2255 request.fail( function() { 2256 overlay.removeClass( 'customize-loading' ); 2257 deferred.reject(); 2258 } ); 2259 }; 2260 2261 if ( 0 === api.state( 'processing' ).get() ) { 2262 onceProcessingComplete(); 2263 } else { 2264 api.state( 'processing' ).bind( onceProcessingComplete ); 2265 } 2266 2267 return deferred.promise(); 2275 return api.ThemesPanel.prototype.loadThemePreview.call( this, themeId ); 2268 2276 }, 2269 2277 … … 2845 2853 var theme = false, customizeId, themeControl; 2846 2854 if ( preview ) { 2847 2848 panel.loadThemePreview( slug ).fail( function() { 2849 $( '.wp-full-overlay' ).removeClass( 'customize-loading' ); 2850 } ); 2855 api.notifications.remove( 'theme_installing' ); 2856 2857 panel.loadThemePreview( slug ); 2851 2858 2852 2859 } else { … … 2900 2907 if ( $( event.target ).hasClass( 'preview' ) ) { 2901 2908 preview = true; 2902 $( '.wp-full-overlay' ).addClass( 'customize-loading' ); 2903 wp.a11y.speak( $( '#customize-themes-loading-container .customize-loading-text-installing-theme' ).text() ); 2909 2910 api.notifications.add( 'theme_installing', new api.OverlayNotification( 'theme_installing', { 2911 message: api.l10n.themeDownloading, 2912 type: 'info', 2913 loading: true 2914 } ) ); 2904 2915 } 2905 2916 }, … … 2914 2925 */ 2915 2926 loadThemePreview: function( themeId ) { 2916 var deferred = $.Deferred(), onceProcessingComplete, overlay, urlParser;2927 var deferred = $.Deferred(), onceProcessingComplete, urlParser, queryParams; 2917 2928 2918 2929 urlParser = document.createElement( 'a' ); 2919 2930 urlParser.href = location.href; 2920 urlParser.search = $.param(_.extend(2931 queryParams = _.extend( 2921 2932 api.utils.parseQueryString( urlParser.search.substr( 1 ) ), 2922 2933 { … … 2924 2935 changeset_uuid: api.settings.changeset.uuid 2925 2936 } 2926 ) ); 2937 ); 2938 2939 // Include autosaved param to load autosave revision without prompting user to restore it. 2940 if ( ! api.state( 'saved' ).get() ) { 2941 queryParams.customize_autosaved = 'on'; 2942 } 2943 2944 urlParser.search = $.param( queryParams ); 2927 2945 2928 2946 // Update loading message. Everything else is handled by reloading the page. 2929 $( '#customize-themes-loading-container span' ).hide();2930 $( '#customize-themes-loading-container .customize-loading-text' ).css( 'display', 'block' );2931 wp.a11y.speak( $( '#customize-themes-loading-container .customize-loading-text' ).text() );2932 overlay = $( '.wp-full-overlay' );2933 overlay.addClass( 'customize-loading');2947 api.notifications.add( 'theme_previewing', new api.OverlayNotification( 'theme_previewing', { 2948 message: api.l10n.themePreviewWait, 2949 type: 'info', 2950 loading: true 2951 } ) ); 2934 2952 2935 2953 onceProcessingComplete = function() { … … 2941 2959 api.state( 'processing' ).unbind( onceProcessingComplete ); 2942 2960 2943 request = api.requestChangesetUpdate( );2961 request = api.requestChangesetUpdate( {}, { autosave: true } ); 2944 2962 request.done( function() { 2945 2963 deferred.resolve(); 2946 2964 $( window ).off( 'beforeunload.customize-confirm' ); 2947 window.location.href = urlParser.href; 2965 window.location.href = urlParser.href; // @todo Use location.replace()? 2948 2966 } ); 2949 2967 request.fail( function() { 2950 overlay.removeClass( 'customize-loading' ); 2968 2969 // @todo Show notification regarding failure. 2970 api.notifications.remove( 'theme_previewing' ); 2971 2951 2972 deferred.reject(); 2952 2973 } ); … … 6231 6252 _.each( [ 6232 6253 'saved', 6233 'autosaved',6234 6254 'saving', 6255 'trashing', 6235 6256 'activated', 6236 6257 'processing', … … 6280 6301 footerActions = $( '#customize-footer-actions' ); 6281 6302 6282 saveBtn.show();6283 6284 6303 api.section( 'publish_settings', function( section ) { 6285 6304 var updateButtonsState, previewLinkControl, previewLinkControlId = 'changeset_preview_link', updateSectionActive, isSectionActive; … … 6302 6321 */ 6303 6322 isSectionActive = function() { 6304 if ( ! api.state( 'activated' ) ) { 6323 if ( ! api.state( 'activated' ).get() ) { 6324 return false; 6325 } 6326 if ( api.state( 'trashing' ).get() || 'trash' === api.state( 'changesetStatus' ).get() ) { 6305 6327 return false; 6306 6328 } … … 6317 6339 }; 6318 6340 api.state( 'activated' ).bind( updateSectionActive ); 6341 api.state( 'trashing' ).bind( updateSectionActive ); 6319 6342 api.state( 'saved' ).bind( updateSectionActive ); 6320 6343 api.state( 'changesetStatus' ).bind( updateSectionActive ); … … 6552 6575 */ 6553 6576 request = wp.ajax.post( 'customize_save', query ); 6554 6555 // Disable save button during the save request. 6556 saveBtn.prop( 'disabled', true ); 6577 api.state( 'processing' ).set( api.state( 'processing' ).get() + 1 ); 6557 6578 6558 6579 api.trigger( 'save', request ); 6559 6580 6560 6581 request.always( function () { 6582 api.state( 'processing' ).set( api.state( 'processing' ).get() - 1 ); 6561 6583 api.state( 'saving' ).set( false ); 6562 saveBtn.prop( 'disabled', false );6563 6584 api.unbind( 'change', captureSettingModifiedDuringSave ); 6564 6585 } ); … … 6637 6658 6638 6659 api.state( 'changesetStatus' ).set( response.changeset_status ); 6639 api.state( 'changesetDate' ).set( response.changeset_date ); 6660 if ( response.changeset_date ) { 6661 api.state( 'changesetDate' ).set( response.changeset_date ); 6662 } 6640 6663 6641 6664 if ( 'publish' === response.changeset_status ) { … … 6695 6718 6696 6719 /** 6720 * Trash the current changes. 6721 * 6722 * Revert the Customizer to it's previously-published state. 6723 * 6724 * @since 4.9.0 6725 * 6726 * @returns {jQuery.promise} Promise. 6727 */ 6728 trash: function trash() { 6729 var request, success, fail; 6730 6731 api.state( 'trashing' ).set( true ); 6732 api.state( 'processing' ).set( api.state( 'processing' ).get() + 1 ); 6733 6734 request = wp.ajax.post( 'customize_trash', { 6735 customize_changeset_uuid: api.settings.changeset.uuid, 6736 nonce: api.settings.nonce.trash 6737 } ); 6738 api.notifications.add( 'changeset_trashing', new api.OverlayNotification( 'changeset_trashing', { 6739 type: 'info', 6740 message: api.l10n.revertingChanges, 6741 loading: true 6742 } ) ); 6743 6744 success = function() { 6745 var urlParser = document.createElement( 'a' ), queryParams; 6746 6747 api.state( 'changesetStatus' ).set( 'trash' ); 6748 api.each( function( setting ) { 6749 setting._dirty = false; 6750 } ); 6751 api.state( 'saved' ).set( true ); 6752 6753 // Go back to Customizer without changeset. 6754 urlParser.href = location.href; 6755 queryParams = api.utils.parseQueryString( urlParser.search.substr( 1 ) ); 6756 delete queryParams.changeset_uuid; 6757 urlParser.search = $.param( queryParams ); 6758 location.replace( urlParser.href ); 6759 }; 6760 6761 fail = function( code, message ) { 6762 var notificationCode = code || 'unknown_error'; 6763 api.state( 'processing' ).set( api.state( 'processing' ).get() - 1 ); 6764 api.state( 'trashing' ).set( false ); 6765 api.notifications.remove( 'changeset_trashing' ); 6766 api.notifications.add( notificationCode, new api.Notification( notificationCode, { 6767 message: message || api.l10n.unknownError, 6768 dismissible: true, 6769 type: 'error' 6770 } ) ); 6771 }; 6772 6773 request.done( function( response ) { 6774 success( response.message ); 6775 } ); 6776 6777 request.fail( function( response ) { 6778 var code = response.code || 'trashing_failed'; 6779 if ( response.success || 'non_existent_changeset' === code || 'changeset_already_trashed' === code ) { 6780 success( response.message ); 6781 } else { 6782 fail( code, response.message ); 6783 } 6784 } ); 6785 }, 6786 6787 /** 6697 6788 * Builds the front preview url with the current state of customizer. 6698 6789 * … … 6843 6934 var saved = state.instance( 'saved' ), 6844 6935 saving = state.instance( 'saving' ), 6936 trashing = state.instance( 'trashing' ), 6845 6937 activated = state.instance( 'activated' ), 6846 6938 processing = state.instance( 'processing' ), … … 6864 6956 saveBtn.val( api.l10n.activate ); 6865 6957 closeBtn.find( '.screen-reader-text' ).text( api.l10n.cancel ); 6866 publishSettingsBtn.prop( 'disabled', false );6867 6958 6868 6959 } else if ( '' === changesetStatus.get() && saved() ) { … … 6872 6963 saveBtn.val( api.l10n.saved ); 6873 6964 } 6874 publishSettingsBtn.prop( 'disabled', true );6875 6965 closeBtn.find( '.screen-reader-text' ).text( api.l10n.close ); 6876 6966 … … 6900 6990 } 6901 6991 closeBtn.find( '.screen-reader-text' ).text( api.l10n.cancel ); 6902 publishSettingsBtn.prop( 'disabled', false );6903 6992 } 6904 6993 … … 6907 6996 * and if the theme is not active or the changeset exists but is not published. 6908 6997 */ 6909 canSave = ! saving() && ( ! activated() || ! saved() || ( changesetStatus() !== selectedChangesetStatus() && '' !== changesetStatus() ) || ( 'future' === selectedChangesetStatus() && changesetDate.get() !== selectedChangesetDate.get() ) );6998 canSave = ! saving() && ! trashing() && ( ! activated() || ! saved() || ( changesetStatus() !== selectedChangesetStatus() && '' !== changesetStatus() ) || ( 'future' === selectedChangesetStatus() && changesetDate.get() !== selectedChangesetDate.get() ) ); 6910 6999 6911 7000 saveBtn.prop( 'disabled', ! canSave ); … … 6958 7047 saving.bind( function( isSaving ) { 6959 7048 body.toggleClass( 'saving', isSaving ); 7049 } ); 7050 trashing.bind( function( isTrashing ) { 7051 body.toggleClass( 'trashing', isTrashing ); 6960 7052 } ); 6961 7053 … … 7029 7121 if ( api.settings.changeset.branching ) { 7030 7122 changesetStatus.bind( function( newStatus ) { 7031 populateChangesetUuidParam( '' !== newStatus && 'publish' !== newStatus );7123 populateChangesetUuidParam( '' !== newStatus && 'publish' !== newStatus && 'trash' !== newStatus ); 7032 7124 } ); 7033 7125 } … … 7106 7198 // Handle dismissal of notice. 7107 7199 li.find( '.notice-dismiss' ).on( 'click', function() { 7108 wp.ajax.post( ' dismiss_customize_changeset_autosave', {7200 wp.ajax.post( 'customize_dismiss_autosave', { 7109 7201 wp_customize: 'on', 7110 7202 customize_theme: api.settings.theme.stylesheet, … … 7533 7625 }); 7534 7626 7535 // Prompt user with AYS dialog if leaving the Customizer with unsaved changes 7536 $( window ).on( 'beforeunload.customize-confirm', function() { 7537 if ( ! isCleanState() ) { 7538 setTimeout( function() { 7539 overlay.removeClass( 'customize-loading' ); 7540 }, 1 ); 7541 return api.l10n.saveAlert; 7542 } 7543 }); 7627 function startPromptingBeforeUnload() { 7628 api.unbind( 'change', startPromptingBeforeUnload ); 7629 7630 // Prompt user with AYS dialog if leaving the Customizer with unsaved changes 7631 $( window ).on( 'beforeunload.customize-confirm', function() { 7632 if ( ! isCleanState() ) { 7633 setTimeout( function() { 7634 overlay.removeClass( 'customize-loading' ); 7635 }, 1 ); 7636 return api.l10n.saveAlert; 7637 } 7638 }); 7639 } 7640 api.bind( 'change', startPromptingBeforeUnload ); 7544 7641 7545 7642 closeBtn.on( 'click.customize-controls-close', function( event ) { … … 7567 7664 clearedToClose.resolve(); 7568 7665 } else { 7569 wp.ajax.send( ' dismiss_customize_changeset_autosave', {7666 wp.ajax.send( 'customize_dismiss_autosave', { 7570 7667 timeout: 500, // Don't wait too long. 7571 7668 data: { … … 7988 8085 7989 8086 // Autosave changeset. 7990 ( function() {8087 function startAutosaving() { 7991 8088 var timeoutId, updateChangesetWithReschedule, scheduleChangesetUpdate, updatePending = false; 8089 8090 api.unbind( 'change', startAutosaving ); // Ensure startAutosaving only fires once. 7992 8091 7993 8092 api.state( 'saved' ).bind( function( isSaved ) { … … 8041 8140 updateChangesetWithReschedule(); 8042 8141 } ); 8043 } ()); 8142 } 8143 api.bind( 'change', startAutosaving ); 8044 8144 8045 8145 // Make sure TinyMCE dialogs appear above Customizer UI. … … 8050 8150 } ); 8051 8151 8152 body.addClass( 'ready' ); 8052 8153 api.trigger( 'ready' ); 8053 8154 }); -
trunk/src/wp-includes/class-wp-customize-manager.php
r41648 r41667 375 375 remove_action( 'admin_init', '_maybe_update_themes' ); 376 376 377 add_action( 'wp_ajax_customize_save', array( $this, 'save' ) ); 378 add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) ); 379 add_action( 'wp_ajax_customize-load-themes', array( $this, 'load_themes_ajax' ) ); 380 add_action( 'wp_ajax_dismiss_customize_changeset_autosave', array( $this, 'handle_dismiss_changeset_autosave_request' ) ); 377 add_action( 'wp_ajax_customize_save', array( $this, 'save' ) ); 378 add_action( 'wp_ajax_customize_trash', array( $this, 'handle_changeset_trash_request' ) ); 379 add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) ); 380 add_action( 'wp_ajax_customize_load_themes', array( $this, 'handle_load_themes_request' ) ); 381 add_action( 'wp_ajax_customize_dismiss_autosave', array( $this, 'handle_dismiss_autosave_request' ) ); 381 382 382 383 add_action( 'customize_register', array( $this, 'register_controls' ) ); … … 2831 2832 2832 2833 /** 2834 * Handle request to trash a changeset. 2835 * 2836 * @since 4.9.0 2837 */ 2838 public function handle_changeset_trash_request() { 2839 if ( ! is_user_logged_in() ) { 2840 wp_send_json_error( 'unauthenticated' ); 2841 } 2842 2843 if ( ! $this->is_preview() ) { 2844 wp_send_json_error( 'not_preview' ); 2845 } 2846 2847 if ( ! check_ajax_referer( 'trash_customize_changeset', 'nonce', false ) ) { 2848 wp_send_json_error( array( 2849 'code' => 'invalid_nonce', 2850 'message' => __( 'There was an authentication problem. Please reload and try again.' ), 2851 ) ); 2852 } 2853 2854 $changeset_post_id = $this->changeset_post_id(); 2855 2856 if ( ! $changeset_post_id ) { 2857 wp_send_json_error( array( 2858 'message' => __( 'No changes saved yet, so there is nothing to trash.' ), 2859 'code' => 'non_existent_changeset', 2860 ) ); 2861 return; 2862 } 2863 2864 if ( $changeset_post_id && ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->delete_post, $changeset_post_id ) ) { 2865 wp_send_json_error( array( 2866 'code' => 'changeset_trash_unauthorized', 2867 'message' => __( 'Unable to trash changes.' ), 2868 ) ); 2869 } 2870 2871 if ( 'trash' === get_post_status( $changeset_post_id ) ) { 2872 wp_send_json_error( array( 2873 'message' => __( 'Changes have already been trashed.' ), 2874 'code' => 'changeset_already_trashed', 2875 ) ); 2876 return; 2877 } 2878 2879 $r = wp_trash_post( $changeset_post_id ); 2880 if ( ! ( $r instanceof WP_Post ) ) { 2881 wp_send_json_error( array( 2882 'code' => 'changeset_trash_failure', 2883 'message' => __( 'Unable to trash changes.' ), 2884 ) ); 2885 } 2886 2887 wp_send_json_success( array( 2888 'message' => __( 'Changes trashed successfully.' ), 2889 ) ); 2890 } 2891 2892 /** 2833 2893 * Re-map 'edit_post' meta cap for a customize_changeset post to be the same as 'customize' maps. 2834 2894 * … … 3103 3163 * @since 4.9.0 3104 3164 */ 3105 public function handle_dismiss_ changeset_autosave_request() {3165 public function handle_dismiss_autosave_request() { 3106 3166 if ( ! $this->is_preview() ) { 3107 3167 wp_send_json_error( 'not_preview', 400 ); 3108 3168 } 3109 3169 3110 if ( ! check_ajax_referer( ' dismiss_customize_changeset_autosave', 'nonce', false ) ) {3170 if ( ! check_ajax_referer( 'customize_dismiss_autosave', 'nonce', false ) ) { 3111 3171 wp_send_json_error( 'invalid_nonce', 403 ); 3112 3172 } … … 3544 3604 ?> 3545 3605 <script type="text/html" id="tmpl-customize-notification"> 3546 <li class="notice notice-{{ data.type || 'info' }} {{ data.alt ? 'notice-alt' : '' }} {{ data.dismissible ? 'is-dismissible' : '' }} " data-code="{{ data.code }}" data-type="{{ data.type }}">3547 {{{ data.message || data.code }}}3606 <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 }}"> 3607 <div class="notification-message">{{{ data.message || data.code }}}</div> 3548 3608 <# if ( data.dismissible ) { #> 3549 3609 <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php _e( 'Dismiss' ); ?></span></button> … … 3577 3637 </div> 3578 3638 </script> 3639 <script type="text/html" id="tmpl-customize-trash-changeset-control"> 3640 <button type="button" class="button-link button-link-delete"><?php _e( 'Trash unpublished changes' ); ?></button> 3641 </script> 3579 3642 <?php 3580 3643 } … … 3902 3965 'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ), 3903 3966 'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ), 3904 'switch-themes' => wp_create_nonce( 'switch-themes' ), 3905 'dismiss_autosave' => wp_create_nonce( 'dismiss_customize_changeset_autosave' ), 3967 'switch_themes' => wp_create_nonce( 'switch_themes' ), 3968 'dismiss_autosave' => wp_create_nonce( 'customize_dismiss_autosave' ), 3969 'trash' => wp_create_nonce( 'trash_customize_changeset' ), 3906 3970 ); 3907 3971 … … 4160 4224 $this->add_control( 'changeset_status', array( 4161 4225 'section' => 'publish_settings', 4226 'priority' => 10, 4162 4227 'settings' => array(), 4163 4228 'type' => 'radio', … … 4174 4239 $this->add_control( new WP_Customize_Date_Time_Control( $this, 'changeset_scheduled_date', array( 4175 4240 'section' => 'publish_settings', 4241 'priority' => 20, 4176 4242 'settings' => array(), 4177 4243 'type' => 'date_time', … … 4724 4790 * @since 4.9.0 4725 4791 */ 4726 public function load_themes_ajax() {4727 check_ajax_referer( 'switch -themes', 'switch-themes-nonce' );4792 public function handle_load_themes_request() { 4793 check_ajax_referer( 'switch_themes', 'nonce' ); 4728 4794 4729 4795 if ( ! current_user_can( 'switch_themes' ) ) { -
trunk/src/wp-includes/customize/class-wp-customize-themes-panel.php
r41648 r41667 90 90 <?php endif; ?> 91 91 </li> 92 <li id="customize-themes-loading-container">93 <span class="customize-loading-text-installing-theme"><?php _e( 'Downloading your new theme…' ); ?></span>94 <span class="customize-loading-text"><?php _e( 'Setting up your live preview. This may take a bit.' ); ?></span>95 </li><?php // Used as a full-screen overlay transition after clicking to preview a theme. ?>96 92 <li class="customize-themes-full-container-container"> 97 93 <ul class="customize-themes-full-container"> -
trunk/src/wp-includes/js/customize-base.js
r41387 r41667 811 811 812 812 /** 813 * Additional class names to add to the notification container. 814 * 815 * @since 4.9.0 816 * @var {string} 817 */ 818 classes: '', 819 820 /** 813 821 * Initialize notification. 814 822 * … … 822 830 * @param {Function} [params.template] - Function for rendering template. If not provided, this will come from templateId. 823 831 * @param {string} [params.templateId] - ID for template to render the notification. 832 * @param {string} [params.classes] - Additional class names to add to the notification container. 824 833 * @param {boolean} [params.dismissible] - Whether the notification can be dismissed. 825 834 */ … … 835 844 setting: null, 836 845 template: null, 837 dismissible: false 846 dismissible: false, 847 classes: '' 838 848 }, 839 849 params -
trunk/src/wp-includes/script-loader.php
r41640 r41667 572 572 'untitledBlogName' => __( '(Untitled)' ), 573 573 'serverSaveError' => __( 'Failed connecting to the server. Please try saving again.' ), 574 'themeDownloading' => __( 'Downloading your new theme…' ), 575 'themePreviewWait' => __( 'Setting up your live preview. This may take a bit.' ), 576 'revertingChanges' => __( 'Reverting unpublished changes…' ), 577 'trashConfirm' => __( 'Are you sure you would like to discard your unpublished changes?' ), 574 578 /* translators: %s: URL to the Customizer to load the autosaved version */ 575 579 'autosaveNotice' => __( 'There is a more recent autosave of your changes than the one you are previewing. <a href="%s">Restore the autosave</a>' ), -
trunk/tests/phpunit/tests/ajax/CustomizeManager.php
r41626 r41667 444 444 445 445 /** 446 * Test request for dismissing autosave changesets.446 * Test request for trashing a changeset. 447 447 * 448 448 * @ticket 39896 449 * @covers WP_Customize_Manager::handle_dismiss_changeset_autosave_request() 450 * @covers WP_Customize_Manager::dismiss_user_auto_draft_changesets() 451 */ 452 public function test_handle_dismiss_changeset_autosave_request() { 449 * @covers WP_Customize_Manager::handle_changeset_trash_request() 450 */ 451 public function test_handle_changeset_trash_request() { 453 452 $uuid = wp_generate_uuid4(); 454 453 $wp_customize = $this->set_up_valid_state( $uuid ); 455 454 456 $this->make_ajax_call( 'dismiss_customize_changeset_autosave' ); 455 $this->make_ajax_call( 'customize_trash' ); 456 $this->assertFalse( $this->_last_response_parsed['success'] ); 457 $this->assertEquals( 'invalid_nonce', $this->_last_response_parsed['data']['code'] ); 458 459 $nonce = wp_create_nonce( 'trash_customize_changeset' ); 460 $_POST['nonce'] = $_GET['nonce'] = $_REQUEST['nonce'] = $nonce; 461 $this->make_ajax_call( 'customize_trash' ); 462 $this->assertFalse( $this->_last_response_parsed['success'] ); 463 $this->assertEquals( 'non_existent_changeset', $this->_last_response_parsed['data']['code'] ); 464 465 $wp_customize->register_controls(); // And settings too. 466 $wp_customize->set_post_value( 'blogname', 'HELLO' ); 467 $wp_customize->save_changeset_post( array( 468 'status' => 'save', 469 ) ); 470 471 add_filter( 'map_meta_cap', array( $this, 'return_do_not_allow' ) ); 472 $this->make_ajax_call( 'customize_trash' ); 473 $this->assertFalse( $this->_last_response_parsed['success'] ); 474 $this->assertEquals( 'changeset_trash_unauthorized', $this->_last_response_parsed['data']['code'] ); 475 remove_filter( 'map_meta_cap', array( $this, 'return_do_not_allow' ) ); 476 477 wp_update_post( array( 478 'ID' => $wp_customize->changeset_post_id(), 479 'post_status' => 'trash', 480 ) ); 481 $this->make_ajax_call( 'customize_trash' ); 482 $this->assertFalse( $this->_last_response_parsed['success'] ); 483 $this->assertEquals( 'changeset_already_trashed', $this->_last_response_parsed['data']['code'] ); 484 485 wp_update_post( array( 486 'ID' => $wp_customize->changeset_post_id(), 487 'post_status' => 'draft', 488 ) ); 489 490 $wp_trash_post_count = did_action( 'wp_trash_post' ); 491 add_filter( 'pre_trash_post', '__return_false' ); 492 $this->make_ajax_call( 'customize_trash' ); 493 $this->assertFalse( $this->_last_response_parsed['success'] ); 494 $this->assertEquals( 'changeset_trash_failure', $this->_last_response_parsed['data']['code'] ); 495 remove_filter( 'pre_trash_post', '__return_false' ); 496 $this->assertEquals( $wp_trash_post_count, did_action( 'wp_trash_post' ) ); 497 498 $wp_trash_post_count = did_action( 'wp_trash_post' ); 499 $this->assertEquals( 'draft', get_post_status( $wp_customize->changeset_post_id() ) ); 500 $this->make_ajax_call( 'customize_trash' ); 501 $this->assertTrue( $this->_last_response_parsed['success'] ); 502 $this->assertEquals( 'trash', get_post_status( $wp_customize->changeset_post_id() ) ); 503 $this->assertEquals( $wp_trash_post_count + 1, did_action( 'wp_trash_post' ) ); 504 } 505 506 /** 507 * Return caps array containing 'do_not_allow'. 508 * 509 * @return array Caps. 510 */ 511 public function return_do_not_allow() { 512 return array( 'do_not_allow' ); 513 } 514 515 /** 516 * Test request for dismissing autosave changesets. 517 * 518 * @ticket 39896 519 * @covers WP_Customize_Manager::handle_dismiss_autosave_request() 520 * @covers WP_Customize_Manager::dismiss_user_auto_draft_changesets() 521 */ 522 public function test_handle_dismiss_autosave_request() { 523 $uuid = wp_generate_uuid4(); 524 $wp_customize = $this->set_up_valid_state( $uuid ); 525 526 $this->make_ajax_call( 'customize_dismiss_autosave' ); 457 527 $this->assertFalse( $this->_last_response_parsed['success'] ); 458 528 $this->assertEquals( 'invalid_nonce', $this->_last_response_parsed['data'] ); 459 529 460 $nonce = wp_create_nonce( ' dismiss_customize_changeset_autosave' );530 $nonce = wp_create_nonce( 'customize_dismiss_autosave' ); 461 531 $_POST['nonce'] = $_GET['nonce'] = $_REQUEST['nonce'] = $nonce; 462 $this->make_ajax_call( ' dismiss_customize_changeset_autosave' );532 $this->make_ajax_call( 'customize_dismiss_autosave' ); 463 533 $this->assertFalse( $this->_last_response_parsed['success'] ); 464 534 $this->assertEquals( 'no_auto_draft_to_delete', $this->_last_response_parsed['data'] ); … … 490 560 $this->assertFalse( (bool) get_post_meta( $post_id, '_customize_restore_dismissed', true ) ); 491 561 } 492 $this->make_ajax_call( ' dismiss_customize_changeset_autosave' );562 $this->make_ajax_call( 'customize_dismiss_autosave' ); 493 563 $this->assertTrue( $this->_last_response_parsed['success'] ); 494 564 $this->assertEquals( 'auto_draft_dismissed', $this->_last_response_parsed['data'] ); … … 503 573 504 574 // Subsequent test results in none dismissed. 505 $this->make_ajax_call( ' dismiss_customize_changeset_autosave' );575 $this->make_ajax_call( 'customize_dismiss_autosave' ); 506 576 $this->assertFalse( $this->_last_response_parsed['success'] ); 507 577 $this->assertEquals( 'no_auto_draft_to_delete', $this->_last_response_parsed['data'] ); … … 521 591 522 592 // Since no autosave yet, confirm no action. 523 $this->make_ajax_call( ' dismiss_customize_changeset_autosave' );593 $this->make_ajax_call( 'customize_dismiss_autosave' ); 524 594 $this->assertFalse( $this->_last_response_parsed['success'] ); 525 595 $this->assertEquals( 'no_autosave_revision_to_delete', $this->_last_response_parsed['data'] ); … … 541 611 542 612 // Confirm autosave gets deleted. 543 $this->make_ajax_call( ' dismiss_customize_changeset_autosave' );613 $this->make_ajax_call( 'customize_dismiss_autosave' ); 544 614 $this->assertTrue( $this->_last_response_parsed['success'] ); 545 615 $this->assertEquals( 'autosave_revision_deleted', $this->_last_response_parsed['data'] ); … … 547 617 548 618 // Since no autosave yet, confirm no action. 549 $this->make_ajax_call( ' dismiss_customize_changeset_autosave' );619 $this->make_ajax_call( 'customize_dismiss_autosave' ); 550 620 $this->assertFalse( $this->_last_response_parsed['success'] ); 551 621 $this->assertEquals( 'no_autosave_revision_to_delete', $this->_last_response_parsed['data'] );
Note: See TracChangeset
for help on using the changeset viewer.