Customize: Add global notifications area.
* Displays an error notification in the global area when a save attempt is rejected due to invalid settings. An error notification is also displayed when saving fails due to a network error or server error. * Introduces `wp.customize.Notifications` subclass of `wp.customize.Values` to contain instances of `wp.customize.Notification` and manage their rendering into a container. * Exposes the global notification area as `wp.customize.notifications` collection instance. * Updates the `notifications` object on `Control` to use `Notifications` rather than `Values` and to re-use the rendering logic from the former. The old `Control#renderNotifications` method is deprecated. * Allows notifications to be dismissed by instantiating them with a `dismissible` property. * Allows `wp.customize.Notification` to be extended with custom templates and `render` functions. * Triggers a `removed` event on `wp.customize.Values` instances _after_ a value has been removed from the collection. Props delawski, westonruter, karmatosed, celloexpressions, Fab1en, melchoyce, Kelderic, afercia, adamsilverstein. See #34893, #39896. Fixes #35210, #31582, #37727, #37269. Built from https://develop.svn.wordpress.org/trunk@41374 git-svn-id: http://core.svn.wordpress.org/trunk@41207 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
560d705b00
commit
d8f445bf0f
|
@ -766,7 +766,6 @@ p.customize-section-description {
|
|||
#customize-controls .customize-control-notifications-container { /* Scoped to #customize-controls for specificity over notification styles in common.css. */
|
||||
margin: 4px 0 8px 0;
|
||||
padding: 0;
|
||||
display: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
|
@ -798,6 +797,33 @@ p.customize-section-description {
|
|||
outline: 2px solid #dc3232;
|
||||
}
|
||||
|
||||
#customize-controls #customize-notifications-area {
|
||||
position: absolute;
|
||||
top: 46px;
|
||||
width: 100%;
|
||||
max-height: 210px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
border-bottom: 1px solid #ddd;
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#customize-controls #customize-notifications-area > ul,
|
||||
#customize-controls #customize-notifications-area .notice {
|
||||
margin: 0;
|
||||
}
|
||||
#customize-controls #customize-notifications-area .notice {
|
||||
padding: 9px 14px;
|
||||
}
|
||||
#customize-controls #customize-notifications-area .notice.is-dismissible {
|
||||
padding-left: 38px;
|
||||
}
|
||||
#customize-controls #customize-notifications-area .notice + .notice {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
/* Style for custom settings */
|
||||
|
||||
/**
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -766,7 +766,6 @@ p.customize-section-description {
|
|||
#customize-controls .customize-control-notifications-container { /* Scoped to #customize-controls for specificity over notification styles in common.css. */
|
||||
margin: 4px 0 8px 0;
|
||||
padding: 0;
|
||||
display: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
|
@ -798,6 +797,33 @@ p.customize-section-description {
|
|||
outline: 2px solid #dc3232;
|
||||
}
|
||||
|
||||
#customize-controls #customize-notifications-area {
|
||||
position: absolute;
|
||||
top: 46px;
|
||||
width: 100%;
|
||||
max-height: 210px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
border-bottom: 1px solid #ddd;
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#customize-controls #customize-notifications-area > ul,
|
||||
#customize-controls #customize-notifications-area .notice {
|
||||
margin: 0;
|
||||
}
|
||||
#customize-controls #customize-notifications-area .notice {
|
||||
padding: 9px 14px;
|
||||
}
|
||||
#customize-controls #customize-notifications-area .notice.is-dismissible {
|
||||
padding-right: 38px;
|
||||
}
|
||||
#customize-controls #customize-notifications-area .notice + .notice {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
/* Style for custom settings */
|
||||
|
||||
/**
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -151,6 +151,9 @@ do_action( 'customize_controls_print_scripts' );
|
|||
</div>
|
||||
|
||||
<div id="widgets-right" class="wp-clearfix"><!-- For Widget Customizer, many widgets try to look for instances under div#widgets-right, so we have to add that ID to a container div in the Customizer for compat -->
|
||||
<div id="customize-notifications-area" class="customize-control-notifications-container">
|
||||
<ul></ul>
|
||||
</div>
|
||||
<div class="wp-full-overlay-sidebar-content" tabindex="-1">
|
||||
<div id="customize-info" class="accordion-section customize-info">
|
||||
<div class="accordion-section-title">
|
||||
|
|
|
@ -1,7 +1,213 @@
|
|||
/* global _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer */
|
||||
/* global _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer, console */
|
||||
(function( exports, $ ){
|
||||
var Container, focus, normalizedTransitionendEventName, api = wp.customize;
|
||||
|
||||
/**
|
||||
* A collection of observable notifications.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @class
|
||||
* @augments wp.customize.Values
|
||||
*/
|
||||
api.Notifications = api.Values.extend({
|
||||
|
||||
/**
|
||||
* Whether the alternative style should be used.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @type {boolean}
|
||||
*/
|
||||
alt: false,
|
||||
|
||||
/**
|
||||
* The default constructor for items of the collection.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @type {object}
|
||||
*/
|
||||
defaultConstructor: api.Notification,
|
||||
|
||||
/**
|
||||
* Initialize notifications area.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @constructor
|
||||
* @param {object} options - Options.
|
||||
* @param {jQuery} [options.container] - Container element for notifications. This can be injected later.
|
||||
* @param {boolean} [options.alt] - Whether alternative style should be used when rendering notifications.
|
||||
* @returns {void}
|
||||
* @this {wp.customize.Notifications}
|
||||
*/
|
||||
initialize: function( options ) {
|
||||
var collection = this;
|
||||
|
||||
api.Values.prototype.initialize.call( collection, options );
|
||||
|
||||
// Keep track of the order in which the notifications were added for sorting purposes.
|
||||
collection._addedIncrement = 0;
|
||||
collection._addedOrder = {};
|
||||
|
||||
// Trigger change event when notification is added or removed.
|
||||
collection.bind( 'add', function( notification ) {
|
||||
collection.trigger( 'change', notification );
|
||||
});
|
||||
collection.bind( 'removed', function( notification ) {
|
||||
collection.trigger( 'change', notification );
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the number of notifications added.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @return {number} Count of notifications.
|
||||
*/
|
||||
count: function() {
|
||||
return _.size( this._value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Add notification to the collection.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @param {string} code - Notification code.
|
||||
* @param {object} params - Notification params.
|
||||
* @return {api.Notification} Added instance (or existing instance if it was already added).
|
||||
*/
|
||||
add: function( code, params ) {
|
||||
var collection = this;
|
||||
if ( ! collection.has( code ) ) {
|
||||
collection._addedIncrement += 1;
|
||||
collection._addedOrder[ code ] = collection._addedIncrement;
|
||||
}
|
||||
return api.Values.prototype.add.call( this, code, params );
|
||||
},
|
||||
|
||||
/**
|
||||
* Add notification to the collection.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @param {string} code - Notification code to remove.
|
||||
* @return {api.Notification} Added instance (or existing instance if it was already added).
|
||||
*/
|
||||
remove: function( code ) {
|
||||
var collection = this;
|
||||
delete collection._addedOrder[ code ];
|
||||
return api.Values.prototype.remove.call( this, code );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get list of notifications.
|
||||
*
|
||||
* Notifications may be sorted by type followed by added time.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @param {object} args - Args.
|
||||
* @param {boolean} [args.sort=false] - Whether to return the notifications sorted.
|
||||
* @return {Array.<wp.customize.Notification>} Notifications.
|
||||
* @this {wp.customize.Notifications}
|
||||
*/
|
||||
get: function( args ) {
|
||||
var collection = this, notifications, errorTypePriorities, params;
|
||||
notifications = _.values( collection._value );
|
||||
|
||||
params = _.extend(
|
||||
{ sort: false },
|
||||
args
|
||||
);
|
||||
|
||||
if ( params.sort ) {
|
||||
errorTypePriorities = { error: 4, warning: 3, success: 2, info: 1 };
|
||||
notifications.sort( function( a, b ) {
|
||||
var aPriority = 0, bPriority = 0;
|
||||
if ( ! _.isUndefined( errorTypePriorities[ a.type ] ) ) {
|
||||
aPriority = errorTypePriorities[ a.type ];
|
||||
}
|
||||
if ( ! _.isUndefined( errorTypePriorities[ b.type ] ) ) {
|
||||
bPriority = errorTypePriorities[ b.type ];
|
||||
}
|
||||
if ( aPriority !== bPriority ) {
|
||||
return bPriority - aPriority; // Show errors first.
|
||||
}
|
||||
return collection._addedOrder[ b.code ] - collection._addedOrder[ a.code ]; // Show newer notifications higher.
|
||||
});
|
||||
}
|
||||
|
||||
return notifications;
|
||||
},
|
||||
|
||||
/**
|
||||
* Render notifications area.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @returns {void}
|
||||
* @this {wp.customize.Notifications}
|
||||
*/
|
||||
render: function() {
|
||||
var collection = this,
|
||||
notifications,
|
||||
renderedNotificationContainers,
|
||||
prevRenderedCodes,
|
||||
nextRenderedCodes,
|
||||
addedCodes,
|
||||
removedCodes,
|
||||
listElement;
|
||||
|
||||
// Short-circuit if there are no container to render into.
|
||||
if ( ! collection.container || ! collection.container.length ) {
|
||||
return;
|
||||
}
|
||||
listElement = collection.container.children( 'ul' ).first();
|
||||
if ( ! listElement.length ) {
|
||||
listElement = $( '<ul></ul>' );
|
||||
collection.container.append( listElement );
|
||||
}
|
||||
|
||||
notifications = collection.get( { sort: true } );
|
||||
|
||||
renderedNotificationContainers = {};
|
||||
listElement.find( '> [data-code]' ).each( function() {
|
||||
renderedNotificationContainers[ $( this ).data( 'code' ) ] = $( this );
|
||||
});
|
||||
|
||||
collection.container.toggle( 0 !== notifications.length );
|
||||
|
||||
nextRenderedCodes = _.pluck( notifications, 'code' );
|
||||
prevRenderedCodes = _.keys( renderedNotificationContainers );
|
||||
|
||||
// Short-circuit if there are no notifications added.
|
||||
if ( _.isEqual( nextRenderedCodes, prevRenderedCodes ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
addedCodes = _.difference( nextRenderedCodes, prevRenderedCodes );
|
||||
removedCodes = _.difference( prevRenderedCodes, nextRenderedCodes );
|
||||
|
||||
// Remove notifications that have been removed.
|
||||
_.each( renderedNotificationContainers, function( renderedContainer, code ) {
|
||||
if ( -1 !== _.indexOf( removedCodes, code ) ) {
|
||||
renderedContainer.remove(); // @todo Consider slideUp as enhancement.
|
||||
}
|
||||
});
|
||||
|
||||
// Add all notifications in the sorted order.
|
||||
_.each( notifications, function( notification ) {
|
||||
var notificationContainer = renderedNotificationContainers[ notification.code ];
|
||||
if ( notificationContainer ) {
|
||||
listElement.append( notificationContainer );
|
||||
} else {
|
||||
notificationContainer = $( notification.render() );
|
||||
listElement.append( notificationContainer ); // @todo Consider slideDown() as enhancement.
|
||||
if ( wp.a11y ) {
|
||||
wp.a11y.speak( notification.message, 'assertive' );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
collection.trigger( 'rendered' );
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* A Customizer Setting.
|
||||
*
|
||||
|
@ -1883,7 +2089,9 @@
|
|||
control.priority = new api.Value();
|
||||
control.active = new api.Value();
|
||||
control.activeArgumentsQueue = [];
|
||||
control.notifications = new api.Values({ defaultConstructor: api.Notification });
|
||||
control.notifications = new api.Notifications({
|
||||
alt: control.altNotice
|
||||
});
|
||||
|
||||
control.elements = [];
|
||||
|
||||
|
@ -1973,21 +2181,17 @@
|
|||
|
||||
// After the control is embedded on the page, invoke the "ready" method.
|
||||
control.deferred.embedded.done( function () {
|
||||
/*
|
||||
* Note that this debounced/deferred rendering is needed for two reasons:
|
||||
* 1) The 'remove' event is triggered just _before_ the notification is actually removed.
|
||||
* 2) Improve performance when adding/removing multiple notifications at a time.
|
||||
*/
|
||||
var debouncedRenderNotifications = _.debounce( function renderNotifications() {
|
||||
control.renderNotifications();
|
||||
var renderNotifications = function() {
|
||||
control.notifications.render();
|
||||
};
|
||||
control.notifications.container = control.getNotificationsContainerElement();
|
||||
control.notifications.bind( 'rendered', function() {
|
||||
var notifications = control.notifications.get();
|
||||
control.container.toggleClass( 'has-notifications', 0 !== notifications.length );
|
||||
control.container.toggleClass( 'has-error', 0 !== _.where( notifications, { type: 'error' } ).length );
|
||||
} );
|
||||
control.notifications.bind( 'add', function( notification ) {
|
||||
wp.a11y.speak( notification.message, 'assertive' );
|
||||
debouncedRenderNotifications();
|
||||
} );
|
||||
control.notifications.bind( 'remove', debouncedRenderNotifications );
|
||||
control.renderNotifications();
|
||||
|
||||
renderNotifications();
|
||||
control.notifications.bind( 'change', _.debounce( renderNotifications ) );
|
||||
control.ready();
|
||||
});
|
||||
},
|
||||
|
@ -2091,11 +2295,17 @@
|
|||
* Control subclasses may override this method to do their own handling
|
||||
* of rendering notifications.
|
||||
*
|
||||
* @deprecated in favor of `control.notifications.render()`
|
||||
* @since 4.6.0
|
||||
* @this {wp.customize.Control}
|
||||
*/
|
||||
renderNotifications: function() {
|
||||
var control = this, container, notifications, hasError = false;
|
||||
|
||||
if ( 'undefined' !== typeof console && console.warn ) {
|
||||
console.warn( '[DEPRECATED] wp.customize.Control.prototype.renderNotifications() is deprecated in favor of instantating a wp.customize.Notifications and calling its render() method.' );
|
||||
}
|
||||
|
||||
container = control.getNotificationsContainerElement();
|
||||
if ( ! container || ! container.length ) {
|
||||
return;
|
||||
|
@ -3427,6 +3637,9 @@
|
|||
api.section = new api.Values({ defaultConstructor: api.Section });
|
||||
api.panel = new api.Values({ defaultConstructor: api.Panel });
|
||||
|
||||
// Create the collection for global Notifications.
|
||||
api.notifications = new api.Notifications();
|
||||
|
||||
/**
|
||||
* An object that fetches a preview in the background of the document, which
|
||||
* allows for seamless replacement of an existing preview.
|
||||
|
@ -4501,6 +4714,13 @@
|
|||
api.unbind( 'change', captureSettingModifiedDuringSave );
|
||||
} );
|
||||
|
||||
// Remove notifications that were added due to save failures.
|
||||
api.notifications.each( function( notification ) {
|
||||
if ( notification.saveFailure ) {
|
||||
api.notifications.remove( notification.code );
|
||||
}
|
||||
});
|
||||
|
||||
request.fail( function ( response ) {
|
||||
|
||||
if ( '0' === response ) {
|
||||
|
@ -4518,6 +4738,22 @@
|
|||
previewer.save();
|
||||
previewer.preview.iframe.show();
|
||||
} );
|
||||
} else if ( response.code ) {
|
||||
api.notifications.add( response.code, new api.Notification( response.code, {
|
||||
message: response.message,
|
||||
type: 'error',
|
||||
dismissible: true,
|
||||
fromServer: true,
|
||||
saveFailure: true
|
||||
} ) );
|
||||
} else {
|
||||
api.notifications.add( 'unknown_error', new api.Notification( 'unknown_error', {
|
||||
message: api.l10n.serverSaveError,
|
||||
type: 'error',
|
||||
dismissible: true,
|
||||
fromServer: true,
|
||||
saveFailure: true
|
||||
} ) );
|
||||
}
|
||||
|
||||
if ( response.setting_validities ) {
|
||||
|
@ -4688,6 +4924,29 @@
|
|||
values.bind( 'remove', debouncedReflowPaneContents );
|
||||
} );
|
||||
|
||||
// Set up global notifications area.
|
||||
api.bind( 'ready', function setUpGlobalNotificationsArea() {
|
||||
var sidebar, containerHeight, containerInitialTop;
|
||||
api.notifications.container = $( '#customize-notifications-area' );
|
||||
|
||||
api.notifications.bind( 'change', _.debounce( function() {
|
||||
api.notifications.render();
|
||||
} ) );
|
||||
|
||||
sidebar = $( '.wp-full-overlay-sidebar-content' );
|
||||
api.notifications.bind( 'rendered', function updateSidebarTop() {
|
||||
sidebar.css( 'top', '' );
|
||||
if ( 0 !== api.notifications.count() ) {
|
||||
containerHeight = api.notifications.container.outerHeight() + 1;
|
||||
containerInitialTop = parseInt( sidebar.css( 'top' ), 10 );
|
||||
sidebar.css( 'top', containerInitialTop + containerHeight + 'px' );
|
||||
}
|
||||
api.notifications.trigger( 'sidebarTopUpdated' );
|
||||
});
|
||||
|
||||
api.notifications.render();
|
||||
});
|
||||
|
||||
// Save and activated states
|
||||
(function() {
|
||||
var state = new api.Values(),
|
||||
|
@ -4971,12 +5230,32 @@
|
|||
}
|
||||
|
||||
var scrollTop = parentContainer.scrollTop(),
|
||||
isScrollingUp = ( lastScrollTop ) ? scrollTop <= lastScrollTop : true;
|
||||
scrollDirection;
|
||||
|
||||
if ( ! lastScrollTop ) {
|
||||
scrollDirection = 1;
|
||||
} else {
|
||||
if ( scrollTop === lastScrollTop ) {
|
||||
scrollDirection = 0;
|
||||
} else if ( scrollTop > lastScrollTop ) {
|
||||
scrollDirection = 1;
|
||||
} else {
|
||||
scrollDirection = -1;
|
||||
}
|
||||
}
|
||||
lastScrollTop = scrollTop;
|
||||
positionStickyHeader( activeHeader, scrollTop, isScrollingUp );
|
||||
if ( 0 !== scrollDirection ) {
|
||||
positionStickyHeader( activeHeader, scrollTop, scrollDirection );
|
||||
}
|
||||
}, 8 ) );
|
||||
|
||||
// Update header position on sidebar layout change.
|
||||
api.notifications.bind( 'sidebarTopUpdated', function() {
|
||||
if ( activeHeader && activeHeader.element.hasClass( 'is-sticky' ) ) {
|
||||
activeHeader.element.css( 'top', parentContainer.css( 'top' ) );
|
||||
}
|
||||
});
|
||||
|
||||
// Release header element if it is sticky.
|
||||
releaseStickyHeader = function( headerElement ) {
|
||||
if ( ! headerElement.hasClass( 'is-sticky' ) ) {
|
||||
|
@ -4990,6 +5269,7 @@
|
|||
|
||||
// Reset position of the sticky header.
|
||||
resetStickyHeader = function( headerElement, headerParent ) {
|
||||
if ( headerElement.hasClass( 'is-in-view' ) ) {
|
||||
headerElement
|
||||
.removeClass( 'maybe-sticky is-in-view' )
|
||||
.css( {
|
||||
|
@ -4997,6 +5277,7 @@
|
|||
top: ''
|
||||
} );
|
||||
headerParent.css( 'padding-top', '' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -5023,19 +5304,20 @@
|
|||
* @since 4.7.0
|
||||
* @access private
|
||||
*
|
||||
* @param {object} header Header.
|
||||
* @param {number} scrollTop Scroll top.
|
||||
* @param {boolean} isScrollingUp Is scrolling up?
|
||||
* @param {object} header - Header.
|
||||
* @param {number} scrollTop - Scroll top.
|
||||
* @param {number} scrollDirection - Scroll direction, negative number being up and positive being down.
|
||||
* @returns {void}
|
||||
*/
|
||||
positionStickyHeader = function( header, scrollTop, isScrollingUp ) {
|
||||
positionStickyHeader = function( header, scrollTop, scrollDirection ) {
|
||||
var headerElement = header.element,
|
||||
headerParent = header.parent,
|
||||
headerHeight = header.height,
|
||||
headerTop = parseInt( headerElement.css( 'top' ), 10 ),
|
||||
maybeSticky = headerElement.hasClass( 'maybe-sticky' ),
|
||||
isSticky = headerElement.hasClass( 'is-sticky' ),
|
||||
isInView = headerElement.hasClass( 'is-in-view' );
|
||||
isInView = headerElement.hasClass( 'is-in-view' ),
|
||||
isScrollingUp = ( -1 === scrollDirection );
|
||||
|
||||
// When scrolling down, gradually hide sticky header.
|
||||
if ( ! isScrollingUp ) {
|
||||
|
@ -5078,7 +5360,7 @@
|
|||
headerElement
|
||||
.addClass( 'is-sticky' )
|
||||
.css( {
|
||||
top: '',
|
||||
top: parentContainer.css( 'top' ),
|
||||
width: headerParent.outerWidth() + 'px'
|
||||
} );
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -550,6 +550,10 @@
|
|||
}
|
||||
control.widgetContentEmbedded = true;
|
||||
|
||||
// Update the notification container element now that the widget content has been embedded.
|
||||
control.notifications.container = control.getNotificationsContainerElement();
|
||||
control.notifications.render();
|
||||
|
||||
widgetContent = $( control.params.widget_content );
|
||||
control.container.find( '.widget-content:first' ).append( widgetContent );
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -348,7 +348,7 @@ final class WP_Customize_Manager {
|
|||
add_action( 'customize_controls_init', array( $this, 'prepare_controls' ) );
|
||||
add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) );
|
||||
|
||||
// Render Panel, Section, and Control templates.
|
||||
// Render Common, Panel, Section, and Control templates.
|
||||
add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_panel_templates' ), 1 );
|
||||
add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_section_templates' ), 1 );
|
||||
add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_control_templates' ), 1 );
|
||||
|
@ -2355,7 +2355,8 @@ final class WP_Customize_Manager {
|
|||
if ( $update_transactionally && $invalid_setting_count > 0 ) {
|
||||
$response = array(
|
||||
'setting_validities' => $setting_validities,
|
||||
'message' => sprintf( _n( 'There is %s invalid setting.', 'There are %s invalid settings.', $invalid_setting_count ), number_format_i18n( $invalid_setting_count ) ),
|
||||
/* translators: placeholder is number of invalid settings */
|
||||
'message' => sprintf( _n( 'Unable to save due to %s invalid setting.', 'Unable to save due to %s invalid settings.', $invalid_setting_count ), number_format_i18n( $invalid_setting_count ) ),
|
||||
);
|
||||
return new WP_Error( 'transaction_fail', '', $response );
|
||||
}
|
||||
|
@ -3183,6 +3184,19 @@ final class WP_Customize_Manager {
|
|||
) );
|
||||
$control->print_template();
|
||||
}
|
||||
|
||||
?>
|
||||
<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 }}}
|
||||
<# if ( data.dismissible ) { #>
|
||||
<button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php _e( 'Dismiss' ); ?></span></button>
|
||||
<# } #>
|
||||
</li>
|
||||
</script>
|
||||
|
||||
<?php
|
||||
/* The following template is obsolete in core but retained for plugins. */
|
||||
?>
|
||||
<script type="text/html" id="tmpl-customize-control-notifications">
|
||||
<ul>
|
||||
|
|
|
@ -433,18 +433,26 @@ window.wp = window.wp || {};
|
|||
* @param {string} id The ID of the item to remove.
|
||||
*/
|
||||
remove: function( id ) {
|
||||
var value;
|
||||
var value = this.value( id );
|
||||
|
||||
if ( this.has( id ) ) {
|
||||
value = this.value( id );
|
||||
if ( value ) {
|
||||
|
||||
// Trigger event right before the element is removed from the collection.
|
||||
this.trigger( 'remove', value );
|
||||
if ( value.extended( api.Value ) )
|
||||
|
||||
if ( value.extended( api.Value ) ) {
|
||||
value.unbind( this._change );
|
||||
}
|
||||
delete value.parent;
|
||||
}
|
||||
|
||||
delete this._value[ id ];
|
||||
delete this._deferreds[ id ];
|
||||
|
||||
// Trigger removed event after the item has been eliminated from the collection.
|
||||
if ( value ) {
|
||||
this.trigger( 'removed', value );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -790,6 +798,39 @@ window.wp = window.wp || {};
|
|||
* @param {*} [params.data=null] - Any additional data.
|
||||
*/
|
||||
api.Notification = api.Class.extend(/** @lends wp.customize.Notification.prototype */{
|
||||
|
||||
/**
|
||||
* Template function for rendering the notification.
|
||||
*
|
||||
* This will be populated with template option or else it will be populated with template from the ID.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @var {Function}
|
||||
*/
|
||||
template: null,
|
||||
|
||||
/**
|
||||
* ID for the template to render the notification.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @var {string}
|
||||
*/
|
||||
templateId: 'customize-notification',
|
||||
|
||||
/**
|
||||
* Initialize notification.
|
||||
*
|
||||
* @since 4.9.0
|
||||
*
|
||||
* @param {string} code - Notification code.
|
||||
* @param {object} params - Notification parameters.
|
||||
* @param {string} params.message - Message.
|
||||
* @param {string} [params.type=error] - Type.
|
||||
* @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 {boolean} [params.dismissible] - Whether the notification can be dismissed.
|
||||
*/
|
||||
initialize: function( code, params ) {
|
||||
var _params;
|
||||
this.code = code;
|
||||
|
@ -799,12 +840,44 @@ window.wp = window.wp || {};
|
|||
type: 'error',
|
||||
fromServer: false,
|
||||
data: null,
|
||||
setting: null
|
||||
setting: null,
|
||||
template: null,
|
||||
dismissible: false
|
||||
},
|
||||
params
|
||||
);
|
||||
delete _params.code;
|
||||
_.extend( this, _params );
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the notification.
|
||||
*
|
||||
* @since 4.9.0
|
||||
*
|
||||
* @returns {jQuery} Notification container element.
|
||||
*/
|
||||
render: function() {
|
||||
var notification = this, container, data;
|
||||
if ( ! notification.template ) {
|
||||
notification.template = wp.template( notification.templateId );
|
||||
}
|
||||
data = _.extend( {}, notification, {
|
||||
alt: notification.parent && notification.parent.alt
|
||||
} );
|
||||
container = $( notification.template( data ) );
|
||||
|
||||
if ( notification.dismissible ) {
|
||||
container.find( '.notice-dismiss' ).on( 'click', function() {
|
||||
if ( notification.parent ) {
|
||||
notification.parent.remove( notification.code );
|
||||
} else {
|
||||
container.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -546,6 +546,7 @@ function wp_default_scripts( &$scripts ) {
|
|||
'collapseSidebar' => _x( 'Hide Controls', 'label for hide controls button without length constraints' ),
|
||||
'expandSidebar' => _x( 'Show Controls', 'label for hide controls button without length constraints' ),
|
||||
'untitledBlogName' => __( '(Untitled)' ),
|
||||
'serverSaveError' => __( 'Failed connecting to the server. Please try saving again.' ),
|
||||
// Used for overriding the file types allowed in plupload.
|
||||
'allowedFiles' => __( 'Allowed Files' ),
|
||||
) );
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* @global string $wp_version
|
||||
*/
|
||||
$wp_version = '4.9-alpha-41373';
|
||||
$wp_version = '4.9-alpha-41374';
|
||||
|
||||
/**
|
||||
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
||||
|
|
Loading…
Reference in New Issue