From 0cc238c7c795d81dfab91f119f12d46caca6815e Mon Sep 17 00:00:00 2001 From: westonruter Date: Wed, 4 Oct 2017 20:01:12 +0000 Subject: [PATCH] Customize: Allow controls to be created with pre-instantiated `Setting` object(s), or even with plain `Value` object(s). * Allow passing settings in keyed object (e.g. `settings: { default: 'id' } ), or as an array (e.g. `settings: [ 'id' ]`) with first being default; again, `Setting`/`Value` objects may be supplied instead of IDs. * Allow a single setting to be supplied with just a single `setting` param, either a string or a `Setting`/`Value` object. * Update `changeset_status` and `scheduled_changeset_date` to be added dynamically with JS and simply passing of `api.state()` instances as `setting`. * Introduce a `data-customize-setting-key-link` attribute which, unlike `data-customize-setting-link`, allows passing the setting key (e.g. `default`) as opposed to the setting ID. * Allow `WP_Customize_Control::get_link()` to return `data-customize-setting-key-link` when setting is not registered. * Eliminate `default_value` from `WP_Customize_Date_Time_Control` since now comes from supplied `Value`. * Export status choices as `wp.customize.settings.changeset.statusChoices`. * Export date and time formats as `wp.customize.settings.dateFormat` and `wp.customize.settings.timeFormat` respectively. Props westonruter, sayedwp. See #39896, #30738, #30741, #42083. Fixes #37964, #36167. git-svn-id: https://develop.svn.wordpress.org/trunk@41750 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/css/customize-controls.css | 8 +- src/wp-admin/js/customize-controls.js | 349 ++++++++++-------- .../class-wp-customize-control.php | 13 +- .../class-wp-customize-manager.php | 93 ++--- .../class-wp-customize-date-time-control.php | 9 - src/wp-includes/script-loader.php | 2 + tests/phpunit/tests/customize/manager.php | 3 +- tests/qunit/wp-admin/js/customize-controls.js | 4 +- 8 files changed, 270 insertions(+), 211 deletions(-) diff --git a/src/wp-admin/css/customize-controls.css b/src/wp-admin/css/customize-controls.css index 96fc72b22a..aa43287616 100644 --- a/src/wp-admin/css/customize-controls.css +++ b/src/wp-admin/css/customize-controls.css @@ -138,7 +138,7 @@ body.trashing #publish-settings { margin-bottom: 15px; } -#customize-control-changeset_status label, +#customize-control-changeset_status .customize-inside-control-row, #customize-control-changeset_preview_link input { background-color: #ffffff; border-bottom: 1px solid #ddd; @@ -170,13 +170,13 @@ body.trashing #publish-settings { border-color: #dc3232; } -#customize-control-changeset_status label { +#customize-control-changeset_status .customize-inside-control-row { padding-top: 10px; padding-bottom: 10px; font-weight: 500; } -#customize-control-changeset_status label:first-of-type { +#customize-control-changeset_status .customize-inside-control-row:first-of-type { border-top: 1px solid #ddd; } @@ -2809,7 +2809,7 @@ body.adding-widget .add-new-widget:before, height: 31px; } - #customize-control-changeset_status label { + #customize-control-changeset_status .customize-inside-control-row { padding-top: 15px; } diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js index d94dba91f8..010848d257 100644 --- a/src/wp-admin/js/customize-controls.js +++ b/src/wp-admin/js/customize-controls.js @@ -3094,8 +3094,10 @@ * @param {string} [options.priority=10] - Order of priority to show the control within the section. * @param {string} [options.active=true] - Whether the control is active. * @param {string} options.section - The ID of the section the control belongs to. - * @param {string} options.settings.default - The ID of the setting the control relates to. - * @param {string} options.settings.data + * @param {mixed} [options.setting] - The ID of the main setting or an instance of this setting. + * @param {mixed} options.settings - An object with keys (e.g. default) that maps to setting IDs or Setting/Value objects, or an array of setting IDs or Setting/Value objects. + * @param {mixed} options.settings.default - The ID of the setting the control relates to. + * @param {string} options.settings.data - @todo Is this used? * @param {string} options.label - Label. * @param {string} options.description - Description. * @param {number} [options.instanceNumber] - Order in which this instance was created in relation to other instances. @@ -3110,8 +3112,7 @@ }, initialize: function( id, options ) { - var control = this, - nodes, radios, settings; + var control = this, deferredSettingIds = [], settings, gatherSettings; control.params = _.extend( {}, control.defaults ); @@ -3123,6 +3124,17 @@ control.params.instanceNumber = api.Control.instanceCounter; } + // Look up the type if one was not supplied. + if ( ! control.params.type ) { + _.find( api.controlConstructor, function( Constructor, type ) { + if ( Constructor === control.constructor ) { + control.params.type = type; + return true; + } + return false; + } ); + } + _.extend( control.params, options.params || options ); if ( ! control.params.content ) { control.params.content = $( '
  • ', { @@ -3153,31 +3165,6 @@ control.elements = []; - nodes = control.container.find('[data-customize-setting-link]'); - radios = {}; - - nodes.each( function() { - var node = $( this ), - name; - - if ( node.is( ':radio' ) ) { - name = node.prop( 'name' ); - if ( radios[ name ] ) { - return; - } - - radios[ name ] = true; - node = nodes.filter( '[name="' + name + '"]' ); - } - - api( node.data( 'customizeSettingLink' ), function( setting ) { - var element = new api.Element( node ); - control.elements.push( element ); - element.sync( setting ); - element.set( setting() ); - }); - }); - control.active.bind( function ( active ) { var args = control.activeArgumentsQueue.shift(); args = $.extend( {}, control.defaultActiveArguments, args ); @@ -3190,57 +3177,105 @@ api.utils.bubbleChildValueChanges( control, [ 'section', 'priority', 'active' ] ); - /* - * After all settings related to the control are available, - * make them available on the control and embed the control into the page. - */ - settings = $.map( control.params.settings, function( value ) { - return value; - }); + control.settings = {}; - if ( 0 === settings.length ) { - control.setting = null; - control.settings = {}; - control.embed(); - } else { - api.apply( api, settings.concat( function() { - var key; + settings = {}; + if ( control.params.setting ) { + settings['default'] = control.params.setting; + } + _.extend( settings, control.params.settings ); - control.settings = {}; - for ( key in control.params.settings ) { - control.settings[ key ] = api( control.params.settings[ key ] ); + // Note: Settings can be an array or an object. + _.each( settings, function( setting, key ) { + if ( _.isObject( setting ) ) { // @todo Or check if instance of api.Setting? + control.settings[ key ] = setting; + } else { + deferredSettingIds.push( setting ); + } + } ); + + gatherSettings = function() { + + // Fill-in all resolved settings. + _.each( settings, function ( settingId, key ) { + if ( ! control.settings[ key ] && _.isString( settingId ) ) { + control.settings[ key ] = api( settingId ); } + } ); - control.setting = control.settings['default'] || null; + // Make sure settings passed as array gets associated with default. + if ( control.settings[0] && ! control.settings['default'] ) { + control.settings['default'] = control.settings[0]; + } - // Add setting notifications to the control notification. - _.each( control.settings, function( setting ) { - setting.notifications.bind( 'add', function( settingNotification ) { - var params = _.extend( - {}, - settingNotification, - { - setting: setting.id - } - ); - control.notifications.add( new api.Notification( setting.id + ':' + settingNotification.code, params ) ); - } ); - setting.notifications.bind( 'remove', function( settingNotification ) { - control.notifications.remove( setting.id + ':' + settingNotification.code ); - } ); - } ); + // Identify the main setting. + control.setting = control.settings['default'] || null; - control.embed(); - }) ); + control.embed(); + }; + + if ( 0 === deferredSettingIds.length ) { + gatherSettings(); + } else { + api.apply( api, deferredSettingIds.concat( gatherSettings ) ); } // After the control is embedded on the page, invoke the "ready" method. control.deferred.embedded.done( function () { + control.linkElements(); control.setupNotifications(); control.ready(); }); }, + /** + * Link elements between settings and inputs. + * + * @since 4.7.0 + * @access public + * + * @returns {void} + */ + linkElements: function () { + var control = this, nodes, radios, element; + + nodes = control.container.find( '[data-customize-setting-link], [data-customize-setting-key-link]' ); + radios = {}; + + nodes.each( function () { + var node = $( this ), name, setting; + + if ( node.data( 'customizeSettingLinked' ) ) { + return; + } + node.data( 'customizeSettingLinked', true ); // Prevent re-linking element. + + if ( node.is( ':radio' ) ) { + name = node.prop( 'name' ); + if ( radios[name] ) { + return; + } + + radios[name] = true; + node = nodes.filter( '[name="' + name + '"]' ); + } + + // Let link by default refer to setting ID. If it doesn't exist, fallback to looking up by setting key. + if ( node.data( 'customizeSettingLink' ) ) { + setting = api( node.data( 'customizeSettingLink' ) ); + } else if ( node.data( 'customizeSettingKeyLink' ) ) { + setting = control.settings[ node.data( 'customizeSettingKeyLink' ) ]; + } + + if ( setting ) { + element = new api.Element( node ); + control.elements.push( element ); + element.sync( setting ); + element.set( setting() ); + } + } ); + }, + /** * Embed the control into the page. */ @@ -3342,6 +3377,26 @@ setupNotifications: function() { var control = this, renderNotificationsIfVisible, onSectionAssigned; + // Add setting notifications to the control notification. + _.each( control.settings, function( setting ) { + if ( ! setting.notifications ) { + return; + } + setting.notifications.bind( 'add', function( settingNotification ) { + var params = _.extend( + {}, + settingNotification, + { + setting: setting.id + } + ); + control.notifications.add( new api.Notification( setting.id + ':' + settingNotification.code, params ) ); + } ); + setting.notifications.bind( 'remove', function( settingNotification ) { + control.notifications.remove( setting.id + ':' + settingNotification.code ); + } ); + } ); + control.notifications.container = control.getNotificationsContainerElement(); renderNotificationsIfVisible = function() { @@ -4986,14 +5041,8 @@ _.bindAll( control, 'populateSetting', 'updateDaysForMonth', 'updateMinutesForHour', 'populateDateInputs' ); - // @todo This needs https://core.trac.wordpress.org/ticket/37964 if ( ! control.setting ) { - control.setting = new api.Value(); - } - - // @todo Should this be? Default should be on client. The default value should be in the setting itself. - if ( ! control.setting.get() && control.params.defaultValue ) { - control.setting.set( control.params.defaultValue ); + throw new Error( 'Missing setting' ); } control.container.find( '.date-input' ).each( function() { @@ -6360,8 +6409,9 @@ publishSettingsBtn = $( '#publish-settings' ), footerActions = $( '#customize-footer-actions' ); + // Set up publish settings section and its controls. api.section( 'publish_settings', function( section ) { - var updateButtonsState, trashControl, updateSectionActive, isSectionActive; + var updateButtonsState, trashControl, updateSectionActive, isSectionActive, statusControl, dateControl, toggleDateControl, publishWhenTime, pollInterval, updateTimeArrivedPoller, timeArrivedPollingInterval = 1000; trashControl = new api.Control( 'trash_changeset', { type: 'button', @@ -6433,6 +6483,84 @@ publishSettingsBtn.attr( 'aria-expanded', String( isExpanded ) ); publishSettingsBtn.toggleClass( 'active', isExpanded ); } ); + + statusControl = new api.Control( 'changeset_status', { + priority: 10, + type: 'radio', + section: 'publish_settings', + setting: api.state( 'selectedChangesetStatus' ), + templateId: 'customize-selected-changeset-status-control', + label: api.l10n.action, + choices: api.settings.changeset.statusChoices + } ); + api.control.add( statusControl ); + + dateControl = new api.DateTimeControl( 'changeset_scheduled_date', { + priority: 20, + section: 'publish_settings', + setting: api.state( 'selectedChangesetDate' ), + minYear: ( new Date() ).getFullYear(), + allowPastDate: false, + includeTime: true, + twelveHourFormat: /a/i.test( api.settings.timeFormat ), + description: api.l10n.scheduleDescription + } ); + dateControl.notifications.alt = true; + api.control.add( dateControl ); + + publishWhenTime = function() { + api.state( 'selectedChangesetStatus' ).set( 'publish' ); + api.previewer.save(); + }; + + // Start countdown for when the dateTime arrives, or clear interval when it is . + updateTimeArrivedPoller = function() { + var shouldPoll = ( + 'future' === api.state( 'changesetStatus' ).get() && + 'future' === api.state( 'selectedChangesetStatus' ).get() && + api.state( 'changesetDate' ).get() && + api.state( 'selectedChangesetDate' ).get() === api.state( 'changesetDate' ).get() && + api.utils.getRemainingTime( api.state( 'changesetDate' ).get() ) >= 0 + ); + + if ( shouldPoll && ! pollInterval ) { + pollInterval = setInterval( function() { + var remainingTime = api.utils.getRemainingTime( api.state( 'changesetDate' ).get() ); + api.state( 'remainingTimeToPublish' ).set( remainingTime ); + if ( remainingTime <= 0 ) { + clearInterval( pollInterval ); + pollInterval = 0; + publishWhenTime(); + } + }, timeArrivedPollingInterval ); + } else if ( ! shouldPoll && pollInterval ) { + clearInterval( pollInterval ); + pollInterval = 0; + } + }; + + api.state( 'changesetDate' ).bind( updateTimeArrivedPoller ); + api.state( 'selectedChangesetDate' ).bind( updateTimeArrivedPoller ); + api.state( 'changesetStatus' ).bind( updateTimeArrivedPoller ); + api.state( 'selectedChangesetStatus' ).bind( updateTimeArrivedPoller ); + updateTimeArrivedPoller(); + + // Ensure dateControl only appears when selected status is future. + dateControl.active.validate = function() { + return 'future' === api.state( 'selectedChangesetStatus' ).get(); + }; + toggleDateControl = function( value ) { + dateControl.active.set( 'future' === value ); + }; + toggleDateControl( api.state( 'selectedChangesetStatus' ).get() ); + api.state( 'selectedChangesetStatus' ).bind( toggleDateControl ); + + // Show notification on date control when status is future but it isn't a future date. + api.state( 'saving' ).bind( function( isSaving ) { + if ( isSaving && 'future' === api.state( 'selectedChangesetStatus' ).get() ) { + dateControl.toggleFutureDateNotification( ! dateControl.isFutureDate() ); + } + } ); } ); // Prevent the form from saving when enter is pressed on an input or select element. @@ -7062,6 +7190,7 @@ // Set default states. changesetStatus( api.settings.changeset.status ); changesetDate( api.settings.changeset.publishDate ); + selectedChangesetDate( api.settings.changeset.publishDate ); selectedChangesetStatus( '' === api.settings.changeset.status || 'auto-draft' === api.settings.changeset.status ? 'publish' : api.settings.changeset.status ); selectedChangesetStatus.link( changesetStatus ); // Ensure that direct updates to status on server via wp.customizer.previewer.save() will update selection. saved( true ); @@ -7985,80 +8114,6 @@ }); })(); - // Publish settings section and controls. - api.control( 'changeset_status', 'changeset_scheduled_date', function( statusControl, dateControl ) { - $.when( statusControl.deferred.embedded, dateControl.deferred.embedded ).done( function() { - var radioNodes, statusElement, toggleDateControl, publishWhenTime, pollInterval, updateTimeArrivedPoller, timeArrivedPollingInterval = 1000; - - radioNodes = statusControl.container.find( 'input[type=radio][name]' ); - statusElement = new api.Element( radioNodes ); - statusControl.elements.push( statusElement ); - - statusElement.sync( api.state( 'selectedChangesetStatus' ) ); - statusElement.set( api.state( 'selectedChangesetStatus' ).get() ); - - dateControl.notifications.alt = true; - dateControl.deferred.embedded.done( function() { - api.state( 'selectedChangesetDate' ).sync( dateControl.setting ); - api.state( 'selectedChangesetDate' ).set( dateControl.setting() ); - } ); - - publishWhenTime = function() { - api.state( 'selectedChangesetStatus' ).set( 'publish' ); - api.previewer.save(); - }; - - // Start countdown for when the dateTime arrives, or clear interval when it is . - updateTimeArrivedPoller = function() { - var shouldPoll = ( - 'future' === api.state( 'changesetStatus' ).get() && - 'future' === api.state( 'selectedChangesetStatus' ).get() && - api.state( 'changesetDate' ).get() && - api.state( 'selectedChangesetDate' ).get() === api.state( 'changesetDate' ).get() && - api.utils.getRemainingTime( api.state( 'changesetDate' ).get() ) >= 0 - ); - - if ( shouldPoll && ! pollInterval ) { - pollInterval = setInterval( function() { - var remainingTime = api.utils.getRemainingTime( api.state( 'changesetDate' ).get() ); - api.state( 'remainingTimeToPublish' ).set( remainingTime ); - if ( remainingTime <= 0 ) { - clearInterval( pollInterval ); - pollInterval = 0; - publishWhenTime(); - } - }, timeArrivedPollingInterval ); - } else if ( ! shouldPoll && pollInterval ) { - clearInterval( pollInterval ); - pollInterval = 0; - } - }; - - api.state( 'changesetDate' ).bind( updateTimeArrivedPoller ); - api.state( 'selectedChangesetDate' ).bind( updateTimeArrivedPoller ); - api.state( 'changesetStatus' ).bind( updateTimeArrivedPoller ); - api.state( 'selectedChangesetStatus' ).bind( updateTimeArrivedPoller ); - updateTimeArrivedPoller(); - - // Ensure dateControl only appears when selected status is future. - dateControl.active.validate = function() { - return 'future' === statusElement.get(); - }; - toggleDateControl = function( value ) { - dateControl.active.set( 'future' === value ); - }; - toggleDateControl( statusElement.get() ); - statusElement.bind( toggleDateControl ); - - // Show notification on date control when status is future but it isn't a future date. - api.state( 'saving' ).bind( function( isSaving ) { - if ( isSaving && 'future' === api.state( 'selectedChangesetStatus' ).get() ) { - dateControl.toggleFutureDateNotification( ! dateControl.isFutureDate() ); - } - } ); - } ); - } ); - // Toggle visibility of Header Video notice when active state change. api.control( 'header_video', function( headerVideoControl ) { headerVideoControl.deferred.embedded.done( function() { diff --git a/src/wp-includes/class-wp-customize-control.php b/src/wp-includes/class-wp-customize-control.php index 15e0262fe3..e856f71ad2 100644 --- a/src/wp-includes/class-wp-customize-control.php +++ b/src/wp-includes/class-wp-customize-control.php @@ -430,15 +430,18 @@ class WP_Customize_Control { * Get the data link attribute for a setting. * * @since 3.4.0 + * @since 4.9.0 Return a `data-customize-setting-key-link` attribute if a setting is not registered for the supplied setting key. * * @param string $setting_key - * @return string Data link parameter, if $setting_key is a valid setting, empty string otherwise. + * @return string Data link parameter, a `data-customize-setting-link` attribute if the `$setting_key` refers to a pre-registered setting, + * and a `data-customize-setting-key-link` attribute if the setting is not yet registered. */ public function get_link( $setting_key = 'default' ) { - if ( ! isset( $this->settings[ $setting_key ] ) ) - return ''; - - return 'data-customize-setting-link="' . esc_attr( $this->settings[ $setting_key ]->id ) . '"'; + if ( isset( $this->settings[ $setting_key ] ) && $this->settings[ $setting_key ] instanceof WP_Customize_Setting ) { + return 'data-customize-setting-link="' . esc_attr( $this->settings[ $setting_key ]->id ) . '"'; + } else { + return 'data-customize-setting-key-link="' . esc_attr( $setting_key ) . '"'; + } } /** diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php index 9b920d6a2a..6dd4fae3e7 100644 --- a/src/wp-includes/class-wp-customize-manager.php +++ b/src/wp-includes/class-wp-customize-manager.php @@ -3642,6 +3642,23 @@ final class WP_Customize_Manager { + cap->publish_posts ); + + // @todo Include all of the status labels here from script-loader.php, and then allow it to be filtered. + $status_choices = array(); + if ( $current_user_can_publish ) { + $status_choices[] = array( + 'status' => 'publish', + 'label' => __( 'Publish' ), + ); + } + $status_choices[] = array( + 'status' => 'draft', + 'label' => __( 'Save Draft' ), + ); + if ( $current_user_can_publish ) { + $status_choices[] = array( + 'status' => 'future', + 'label' => __( 'Schedule' ), + ); + } + // Prepare Customizer settings to pass to JavaScript. $changeset_post = null; if ( $changeset_post_id ) { $changeset_post = get_post( $changeset_post_id ); } + if ( $this->changeset_post_id() && 'future' === get_post_status( $this->changeset_post_id() ) ) { + $initial_date = get_the_time( 'Y-m-d H:i:s', $this->changeset_post_id() ); + } else { + $initial_date = current_time( 'mysql', false ); + } + $settings = array( 'changeset' => array( 'uuid' => $this->changeset_uuid(), @@ -4038,10 +4082,13 @@ final class WP_Customize_Manager { 'hasAutosaveRevision' => ! empty( $autosave_revision_post ), 'latestAutoDraftUuid' => $autosave_autodraft_post ? $autosave_autodraft_post->post_name : null, 'status' => $changeset_post ? $changeset_post->post_status : '', - 'currentUserCanPublish' => current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ), - 'publishDate' => $changeset_post ? $changeset_post->post_date : '', // @todo Only if future status? Rename to just date? + 'currentUserCanPublish' => $current_user_can_publish, + 'publishDate' => $initial_date, + 'statusChoices' => $status_choices, ), 'initialServerDate' => current_time( 'mysql', false ), + 'dateFormat' => get_option( 'date_format' ), + 'timeFormat' => get_option( 'time_format' ), 'initialServerTimestamp' => floor( microtime( true ) * 1000 ), 'initialClientTimestamp' => -1, // To be set with JS below. 'timeouts' => array( @@ -4205,6 +4252,7 @@ final class WP_Customize_Manager { /* Publish Settings */ + // Note the controls for this section are added via JS. $this->add_section( 'publish_settings', array( 'title' => __( 'Publish Settings' ), 'priority' => 0, @@ -4213,47 +4261,6 @@ final class WP_Customize_Manager { 'active_callback' => array( $this, 'is_theme_active' ), ) ); - /* Publish Settings Controls */ - $status_choices = array( - 'publish' => __( 'Publish' ), - 'draft' => __( 'Save Draft' ), - 'future' => __( 'Schedule' ), - ); - - if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) { - unset( $status_choices['publish'] ); - } - - $this->add_control( 'changeset_status', array( - 'section' => 'publish_settings', - 'priority' => 10, - 'settings' => array(), - 'type' => 'radio', - 'label' => __( 'Action' ), - 'choices' => $status_choices, - 'capability' => 'customize', - ) ); - - if ( $this->changeset_post_id() && 'future' === get_post_status( $this->changeset_post_id() ) ) { - $initial_date = get_the_time( 'Y-m-d H:i:s', $this->changeset_post_id() ); - } else { - $initial_date = current_time( 'mysql', false ); - } - - $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' ), - 'allow_past_date' => false, - 'include_time' => true, - 'twelve_hour_format' => false !== stripos( get_option( 'time_format' ), 'a' ), - 'description' => __( 'Schedule your customization changes to publish ("go live") at a future date.' ), - 'capability' => 'customize', - 'default_value' => $initial_date, - ) ) ); - /* Themes (controls are loaded via ajax) */ $this->add_panel( new WP_Customize_Themes_Panel( $this, 'themes', array( diff --git a/src/wp-includes/customize/class-wp-customize-date-time-control.php b/src/wp-includes/customize/class-wp-customize-date-time-control.php index 11fc66971e..0506910b61 100644 --- a/src/wp-includes/customize/class-wp-customize-date-time-control.php +++ b/src/wp-includes/customize/class-wp-customize-date-time-control.php @@ -65,14 +65,6 @@ class WP_Customize_Date_Time_Control extends WP_Customize_Control { */ public $twelve_hour_format = true; - /** - * Default date/time to be displayed in the control. - * - * @since 4.9.0 - * @var string - */ - public $default_value; - /** * Don't render the control's content - it's rendered with a JS template. * @@ -94,7 +86,6 @@ class WP_Customize_Date_Time_Control extends WP_Customize_Control { $data['allowPastDate'] = (bool) $this->allow_past_date; $data['twelveHourFormat'] = (bool) $this->twelve_hour_format; $data['includeTime'] = (bool) $this->include_time; - $data['defaultValue'] = $this->default_value; return $data; } diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 7f0e75dfe5..16493a67a4 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -566,6 +566,7 @@ function wp_default_scripts( &$scripts ) { 'saved' => __( 'Saved' ), 'cancel' => __( 'Cancel' ), 'close' => __( 'Close' ), + 'action' => __( 'Action' ), 'cheatin' => __( 'Cheatin’ uh?' ), 'notAllowed' => __( 'Sorry, you are not allowed to customize this site.' ), 'previewIframeTitle' => __( 'Site Preview' ), @@ -594,6 +595,7 @@ function wp_default_scripts( &$scripts ) { _n_noop( 'Unable to save due to %s invalid setting.', 'Unable to save due to %s invalid settings.' ), array( 'singular', 'plural' ) ), + 'scheduleDescription' => __( 'Schedule your customization changes to publish ("go live") at a future date.' ), ) ); $scripts->add( 'customize-selective-refresh', "/wp-includes/js/customize-selective-refresh$suffix.js", array( 'jquery', 'wp-util', 'customize-preview' ), false, 1 ); diff --git a/tests/phpunit/tests/customize/manager.php b/tests/phpunit/tests/customize/manager.php index 97a03f569c..1c1814b6a2 100644 --- a/tests/phpunit/tests/customize/manager.php +++ b/tests/phpunit/tests/customize/manager.php @@ -2581,7 +2581,7 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase { $data = json_decode( $json, true ); $this->assertNotEmpty( $data ); - $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'changeset', 'timeouts', 'initialClientTimestamp', 'initialServerDate', 'initialServerTimestamp', 'l10n' ), array_keys( $data ) ); + $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'changeset', 'timeouts', 'dateFormat', 'timeFormat', 'initialClientTimestamp', 'initialServerDate', 'initialServerTimestamp', 'l10n' ), array_keys( $data ) ); $this->assertEquals( $autofocus, $data['autofocus'] ); $this->assertArrayHasKey( 'save', $data['nonce'] ); $this->assertArrayHasKey( 'preview', $data['nonce'] ); @@ -2596,6 +2596,7 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase { 'uuid', 'currentUserCanPublish', 'publishDate', + 'statusChoices', ), array_keys( $data['changeset'] ) ); diff --git a/tests/qunit/wp-admin/js/customize-controls.js b/tests/qunit/wp-admin/js/customize-controls.js index dc3e391121..ec28ad3612 100644 --- a/tests/qunit/wp-admin/js/customize-controls.js +++ b/tests/qunit/wp-admin/js/customize-controls.js @@ -710,9 +710,9 @@ jQuery( window ).load( function (){ params: { section: section.id, type: 'date_time', + setting: new wp.customize.Value( datetime ), includeTime: true, - content: '
  • ', - defaultValue: datetime + content: '
  • ' } } );