<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[49146] trunk: Community Events: Display dates and times in the user's time zone.</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/49146">49146</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/49146","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>iandunn</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2020-10-14 18:19:43 +0000 (Wed, 14 Oct 2020)</dd>
</dl>
<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Community Events: Display dates and times in the user's time zone.
Fixes <a href="https://core.trac.wordpress.org/ticket/51130">#51130</a>
Props sippis, hlashbrooke, audrasjb, Rarst, iandunn</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcjs_enqueueswpdashboardjs">trunk/src/js/_enqueues/wp/dashboard.js</a></li>
<li><a href="#trunksrcwpadminincludesclasswpcommunityeventsphp">trunk/src/wp-admin/includes/class-wp-community-events.php</a></li>
<li><a href="#trunksrcwpadminincludesdashboardphp">trunk/src/wp-admin/includes/dashboard.php</a></li>
<li><a href="#trunksrcwpincludesscriptloaderphp">trunk/src/wp-includes/script-loader.php</a></li>
<li><a href="#trunktestsqunitindexhtml">trunk/tests/qunit/index.html</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunktestsqunitwpadminjsdashboardjs">trunk/tests/qunit/wp-admin/js/dashboard.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcjs_enqueueswpdashboardjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/js/_enqueues/wp/dashboard.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/js/_enqueues/wp/dashboard.js 2020-10-14 18:17:55 UTC (rev 49145)
+++ trunk/src/js/_enqueues/wp/dashboard.js 2020-10-14 18:19:43 UTC (rev 49146)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -266,6 +266,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 'use strict';
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> var communityEventsData = window.communityEventsData || {},
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ dateI18n = wp.date.dateI18n,
+ format = wp.date.format,
+ sprintf = wp.i18n.sprintf,
+ __ = wp.i18n.__,
+ _x = wp.i18n._x,
</ins><span class="cx" style="display: block; padding: 0 10px"> app;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -441,6 +446,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> .fail( function() {
</span><span class="cx" style="display: block; padding: 0 10px"> app.renderEventsTemplate({
</span><span class="cx" style="display: block; padding: 0 10px"> 'location' : false,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'events' : [],
</ins><span class="cx" style="display: block; padding: 0 10px"> 'error' : true
</span><span class="cx" style="display: block; padding: 0 10px"> }, initiatedBy );
</span><span class="cx" style="display: block; padding: 0 10px"> });
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -465,6 +471,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $locationMessage = $( '#community-events-location-message' ),
</span><span class="cx" style="display: block; padding: 0 10px"> $results = $( '.community-events-results' );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ templateParams.events = app.populateDynamicEventFields(
+ templateParams.events,
+ communityEventsData.time_format
+ );
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /*
</span><span class="cx" style="display: block; padding: 0 10px"> * Hide all toggleable elements by default, to keep the logic simple.
</span><span class="cx" style="display: block; padding: 0 10px"> * Otherwise, each block below would have to turn hide everything that
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -576,6 +587,195 @@
</span><span class="cx" style="display: block; padding: 0 10px"> } else {
</span><span class="cx" style="display: block; padding: 0 10px"> app.toggleLocationForm( 'show' );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ },
+
+ /**
+ * Populate event fields that have to be calculated on the fly.
+ *
+ * These can't be stored in the database, because they're dependent on
+ * the user's current time zone, locale, etc.
+ *
+ * @since 5.6.0
+ *
+ * @param {Array} rawEvents The events that should have dynamic fields added to them.
+ * @param {string} timeFormat A time format acceptable by `wp.date.dateI18n()`.
+ *
+ * @returns {Array}
+ */
+ populateDynamicEventFields: function( rawEvents, timeFormat ) {
+ // Clone the parameter to avoid mutating it, so that this can remain a pure function.
+ var populatedEvents = JSON.parse( JSON.stringify( rawEvents ) );
+
+ $.each( populatedEvents, function( index, event ) {
+ var timeZone = app.getTimeZone( event.start_unix_timestamp * 1000 );
+
+ event.user_formatted_date = app.getFormattedDate(
+ event.start_unix_timestamp * 1000,
+ event.end_unix_timestamp * 1000,
+ timeZone
+ );
+
+ event.user_formatted_time = dateI18n(
+ timeFormat,
+ event.start_unix_timestamp * 1000,
+ timeZone
+ );
+
+ event.timeZoneAbbreviation = app.getTimeZoneAbbreviation( event.start_unix_timestamp * 1000 );
+ } );
+
+ return populatedEvents;
+ },
+
+ /**
+ * Returns the user's local/browser time zone, in a form suitable for `wp.date.i18n()`.
+ *
+ * @since 5.6.0
+ *
+ * @param startTimestamp
+ *
+ * @returns {string|number}
+ */
+ getTimeZone: function( startTimestamp ) {
+ /*
+ * Prefer a name like `Europe/Helsinki`, since that automatically tracks daylight savings. This
+ * doesn't need to take `startTimestamp` into account for that reason.
+ */
+ var timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
+
+ /*
+ * Fall back to an offset for IE11, which declares the property but doesn't assign a value.
+ */
+ if ( 'undefined' === typeof timeZone ) {
+ /*
+ * It's important to use the _event_ time, not the _current_
+ * time, so that daylight savings time is accounted for.
+ */
+ timeZone = app.getFlippedTimeZoneOffset( startTimestamp );
+ }
+
+ return timeZone;
+ },
+
+ /**
+ * Get intuitive time zone offset.
+ *
+ * `Data.prototype.getTimezoneOffset()` returns a positive value for time zones
+ * that are _behind_ UTC, and a _negative_ value for ones that are ahead.
+ *
+ * See https://stackoverflow.com/questions/21102435/why-does-javascript-date-gettimezoneoffset-consider-0500-as-a-positive-off.
+ *
+ * @since 5.6.0
+ *
+ * @param {number} startTimestamp
+ *
+ * @returns {number}
+ */
+ getFlippedTimeZoneOffset: function( startTimestamp ) {
+ return new Date( startTimestamp ).getTimezoneOffset() * -1;
+ },
+
+ /**
+ * Get a short time zone name, like `PST`.
+ *
+ * @since 5.6.0
+ *
+ * @param {number} startTimestamp
+ *
+ * @returns {string}
+ */
+ getTimeZoneAbbreviation: function( startTimestamp ) {
+ var timeZoneAbbreviation,
+ eventDateTime = new Date( startTimestamp );
+
+ /*
+ * Leaving the `locales` argument undefined is important, so that the browser
+ * displays the abbreviation that's most appropriate for the current locale. For
+ * some that will be `UTC{+|-}{n}`, and for others it will be a code like `PST`.
+ *
+ * This doesn't need to take `startTimestamp` into account, because a name like
+ * `America/Chicago` automatically tracks daylight savings.
+ */
+ var shortTimeStringParts = eventDateTime.toLocaleTimeString( undefined, { timeZoneName : 'short' } ).split( ' ' );
+
+ if ( 3 === shortTimeStringParts.length ) {
+ timeZoneAbbreviation = shortTimeStringParts[2];
+ }
+
+ if ( 'undefined' === typeof timeZoneAbbreviation ) {
+ /*
+ * It's important to use the _event_ time, not the _current_
+ * time, so that daylight savings time is accounted for.
+ */
+ var timeZoneOffset = app.getFlippedTimeZoneOffset( startTimestamp ),
+ sign = -1 === Math.sign( timeZoneOffset ) ? '' : '+';
+
+ // translators: Used as part of a string like `GMT+5` in the Events Widget.
+ timeZoneAbbreviation = _x( 'GMT', 'Events widget offset prefix' ) + sign + ( timeZoneOffset / 60 );
+ }
+
+ return timeZoneAbbreviation;
+ },
+
+ /**
+ * Format a start/end date in the user's local time zone and locale.
+ *
+ * @since 5.6.0
+ *
+ * @param {int} startDate The Unix timestamp in milliseconds when the the event starts.
+ * @param {int} endDate The Unix timestamp in milliseconds when the the event ends.
+ * @param {string} timeZone A time zone string or offset which is parsable by `wp.date.i18n()`.
+ *
+ * @returns {string}
+ */
+ getFormattedDate: function( startDate, endDate, timeZone ) {
+ var formattedDate;
+
+ /*
+ * The `date_format` option is not used because it's important
+ * in this context to keep the day of the week in the displayed date,
+ * so that users can tell at a glance if the event is on a day they
+ * are available, without having to open the link.
+ *
+ * The case of crossing a year boundary is intentionally not handled.
+ * It's so rare in practice that it's not worth the complexity
+ * tradeoff. The _ending_ year should be passed to
+ * `multiple_month_event`, though, just in case.
+ */
+ /* translators: Date format for upcoming events on the dashboard. Include the day of the week. See https://www.php.net/manual/datetime.format.php */
+ var singleDayEvent = __( 'l, M j, Y' ),
+ /* translators: Date string for upcoming events. 1: Month, 2: Starting day, 3: Ending day, 4: Year. */
+ multipleDayEvent = __( '%1$s %2$d–%3$d, %4$d' ),
+ /* translators: Date string for upcoming events. 1: Starting month, 2: Starting day, 3: Ending month, 4: Ending day, 5: Ending year. */
+ multipleMonthEvent = __( '%1$s %2$d – %3$s %4$d, %5$d' );
+
+ // Detect single-day events.
+ if ( ! endDate || format( 'Y-m-d', startDate ) === format( 'Y-m-d', endDate ) ) {
+ formattedDate = dateI18n( singleDayEvent, startDate, timeZone );
+
+ // Multiple day events.
+ } else if ( format( 'Y-m', startDate ) === format( 'Y-m', endDate ) ) {
+ formattedDate = sprintf(
+ multipleDayEvent,
+ dateI18n( _x( 'F', 'upcoming events month format' ), startDate, timeZone ),
+ dateI18n( _x( 'j', 'upcoming events day format' ), startDate, timeZone ),
+ dateI18n( _x( 'j', 'upcoming events day format' ), endDate, timeZone ),
+ dateI18n( _x( 'Y', 'upcoming events year format' ), endDate, timeZone )
+ );
+
+ // Multi-day events that cross a month boundary.
+ } else {
+ formattedDate = sprintf(
+ multipleMonthEvent,
+ dateI18n( _x( 'F', 'upcoming events month format' ), startDate, timeZone ),
+ dateI18n( _x( 'j', 'upcoming events day format' ), startDate, timeZone ),
+ dateI18n( _x( 'F', 'upcoming events month format' ), endDate, timeZone ),
+ dateI18n( _x( 'j', 'upcoming events day format' ), endDate, timeZone ),
+ dateI18n( _x( 'Y', 'upcoming events year format' ), endDate, timeZone )
+ );
+ }
+
+ return formattedDate;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><span class="cx" style="display: block; padding: 0 10px">
</span></span></pre></div>
<a id="trunksrcwpadminincludesclasswpcommunityeventsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/class-wp-community-events.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/class-wp-community-events.php 2020-10-14 18:17:55 UTC (rev 49145)
+++ trunk/src/wp-admin/includes/class-wp-community-events.php 2020-10-14 18:19:43 UTC (rev 49146)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -77,6 +77,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * mitigates possible privacy concerns.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 4.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 5.6.0 Response no longer contains formatted date field. They're added
+ * in `wp.communityEvents.populateDynamicEventFields()` now.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @param string $location_search Optional. City name to help determine the location.
</span><span class="cx" style="display: block; padding: 0 10px"> * e.g., "Seattle". Default empty string.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -165,7 +167,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $this->cache_events( $response_body, $expiration );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $response_body['events'] = $this->trim_events( $response_body['events'] );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $response_body = $this->format_event_data_time( $response_body );
</del><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> return $response_body;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -344,6 +345,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * Gets cached events.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 4.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 5.6.0 Response no longer contains formatted date field. They're added
+ * in `wp.communityEvents.populateDynamicEventFields()` now.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @return array|false An array containing `location` and `events` items
</span><span class="cx" style="display: block; padding: 0 10px"> * on success, false on failure.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -355,7 +358,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $cached_response['events'] = $this->trim_events( $cached_response['events'] );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return $this->format_event_data_time( $cached_response );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return $cached_response;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -372,6 +375,12 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * @return array The response with dates and times formatted.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> protected function format_event_data_time( $response_body ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ _deprecated_function(
+ __METHOD__,
+ '5.6.0',
+ 'This is no longer used by Core, and only kept for backwards-compatibility.'
+ );
+
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( isset( $response_body['events'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px"> foreach ( $response_body['events'] as $key => $event ) {
</span><span class="cx" style="display: block; padding: 0 10px"> $timestamp = strtotime( $event['date'] );
</span></span></pre></div>
<a id="trunksrcwpadminincludesdashboardphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/dashboard.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/dashboard.php 2020-10-14 18:17:55 UTC (rev 49145)
+++ trunk/src/wp-admin/includes/dashboard.php 2020-10-14 18:19:43 UTC (rev 49146)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1389,9 +1389,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> <div class="event-date-time">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- <span class="event-date">{{ event.formatted_date }}</span>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <span class="event-date">{{ event.user_formatted_date }}</span>
</ins><span class="cx" style="display: block; padding: 0 10px"> <# if ( 'meetup' === event.type ) { #>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- <span class="event-time">{{ event.formatted_time }}</span>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <span class="event-time">
+ {{ event.user_formatted_time }} {{ event.timeZoneAbbreviation }}
+ </span>
</ins><span class="cx" style="display: block; padding: 0 10px"> <# } #>
</span><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><span class="cx" style="display: block; padding: 0 10px"> </li>
</span></span></pre></div>
<a id="trunksrcwpincludesscriptloaderphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/script-loader.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/script-loader.php 2020-10-14 18:17:55 UTC (rev 49145)
+++ trunk/src/wp-includes/script-loader.php 2020-10-14 18:19:43 UTC (rev 49146)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1308,7 +1308,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $scripts->add( 'wp-color-picker', "/wp-admin/js/color-picker$suffix.js", array( 'iris' ), false, 1 );
</span><span class="cx" style="display: block; padding: 0 10px"> $scripts->set_translations( 'wp-color-picker' );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'jquery', 'admin-comments', 'postbox', 'wp-util', 'wp-a11y' ), false, 1 );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'jquery', 'admin-comments', 'postbox', 'wp-util', 'wp-a11y', 'wp-date' ), false, 1 );
+ $scripts->set_translations( 'dashboard' );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $scripts->add( 'list-revisions', "/wp-includes/js/wp-list-revisions$suffix.js" );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1755,6 +1756,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> array(
</span><span class="cx" style="display: block; padding: 0 10px"> 'nonce' => wp_create_nonce( 'community_events' ),
</span><span class="cx" style="display: block; padding: 0 10px"> 'cache' => $events_client->get_cached_events(),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'time_format' => get_option( 'time_format' ),
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> 'l10n' => array(
</span><span class="cx" style="display: block; padding: 0 10px"> 'enter_closest_city' => __( 'Enter your closest city to find nearby events.' ),
</span></span></pre></div>
<a id="trunktestsqunitindexhtml"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/qunit/index.html</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/index.html 2020-10-14 18:17:55 UTC (rev 49145)
+++ trunk/tests/qunit/index.html 2020-10-14 18:19:43 UTC (rev 49146)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -85,7 +85,12 @@
</span><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> <!-- Tested files -->
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <script src="../../build/wp-admin/js/dashboard.js"></script>
</ins><span class="cx" style="display: block; padding: 0 10px"> <script src="../../build/wp-admin/js/password-strength-meter.js"></script>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <script src="../../build/wp-admin/js/postbox.js"></script>
+ <script src="../../build/wp-includes/js/dist/vendor/moment.js"></script>
+ <script src="../../build/wp-includes/js/dist/date.js"></script>
+ <script src="../../build/wp-includes/js/dist/i18n.js"></script>
</ins><span class="cx" style="display: block; padding: 0 10px"> <script src="../../build/wp-includes/js/customize-base.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px"> <script src="../../build/wp-includes/js/customize-models.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px"> <script src="../../build/wp-includes/js/shortcode.js"></script>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -142,6 +147,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> <script src="wp-admin/js/password-strength-meter.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px"> <script src="wp-admin/js/customize-base.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px"> <script src="wp-admin/js/customize-header.js"></script>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <script src="wp-admin/js/dashboard.js"></script>
</ins><span class="cx" style="display: block; padding: 0 10px"> <script src="wp-includes/js/shortcode.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px"> <script src="wp-includes/js/api-request.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px"> <script src="wp-includes/js/wp-api.js"></script>
</span></span></pre></div>
<a id="trunktestsqunitwpadminjsdashboardjs"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/qunit/wp-admin/js/dashboard.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/wp-admin/js/dashboard.js (rev 0)
+++ trunk/tests/qunit/wp-admin/js/dashboard.js 2020-10-14 18:19:43 UTC (rev 49146)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,219 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/* global wp, sinon, JSON */
+var communityEventsData, dateI18n, pagenow;
+
+jQuery( document ).ready( function () {
+ var getFormattedDate = wp.communityEvents.getFormattedDate,
+ getTimeZone = wp.communityEvents.getTimeZone,
+ getTimeZoneAbbreviation = wp.communityEvents.getTimeZoneAbbreviation,
+ populateDynamicEventFields = wp.communityEvents.populateDynamicEventFields,
+ startDate = 1600185600 * 1000, // Tue Sep 15 9:00:00 AM PDT 2020
+ HOUR_IN_MS = 60 * 60 * 1000,
+ DAY_IN_MS = HOUR_IN_MS * 24,
+ WEEK_IN_MS = DAY_IN_MS * 7;
+
+ QUnit.module( 'dashboard', function( hooks ) {
+ hooks.beforeEach( function() {
+ this.oldDateI18n = dateI18n;
+ this.oldPagenow = pagenow;
+
+ dateI18n = wp.date.dateI18n;
+ pagenow = 'dashboard';
+
+ communityEventsData = {
+ time_format: 'g:i a',
+ l10n: {
+ date_formats: {
+ single_day_event: 'l, M j, Y',
+ multiple_day_event: '%1$s %2$d–%3$d, %4$d',
+ multiple_month_event: '%1$s %2$d – %3$s %4$d, %5$d'
+ }
+ }
+ };
+ } );
+
+ hooks.afterEach( function() {
+ dateI18n = this.oldDateI18n;
+ pagenow = this.oldPagenow;
+ } );
+
+ QUnit.module( 'communityEvents.populateDynamicEventFields', function() {
+ QUnit.test( 'dynamic fields should be added', function( assert ) {
+ var timeFormat = communityEventsData.time_format;
+
+ var getFormattedDateStub = sinon.stub( wp.communityEvents, 'getFormattedDate' ),
+ getTimeZoneStub = sinon.stub( wp.communityEvents, 'getTimeZone' ),
+ getTimeZoneAbbreviationStub = sinon.stub( wp.communityEvents, 'getTimeZoneAbbreviation' );
+
+ getFormattedDateStub.returns( 'Tuesday, Sep 15, 2020' );
+ getTimeZoneStub.returns( 'America/Chicago' );
+ getTimeZoneAbbreviationStub.returns( 'CDT' );
+
+ var rawEvents = [
+ {
+ start_unix_timestamp: 1600185600,
+ end_unix_timestamp: 1600189200
+ },
+
+ {
+ start_unix_timestamp: 1602232400,
+ end_unix_timestamp: 1602236000
+ }
+ ];
+
+ var expected = JSON.parse( JSON.stringify( rawEvents ) );
+ expected[0].user_formatted_date = 'Tuesday, Sep 15, 2020';
+ expected[0].user_formatted_time = '11:00 am';
+ expected[0].timeZoneAbbreviation = 'CDT';
+
+ expected[1].user_formatted_date = 'Tuesday, Sep 15, 2020'; // This is expected to be the same as item 0, because of the stub.
+ expected[1].user_formatted_time = '3:33 am';
+ expected[1].timeZoneAbbreviation = 'CDT';
+
+ var actual = populateDynamicEventFields( rawEvents, timeFormat );
+
+ assert.strictEqual(
+ JSON.stringify( actual ),
+ JSON.stringify( expected )
+ );
+
+ getFormattedDateStub.restore();
+ getTimeZoneStub.restore();
+ getTimeZoneAbbreviationStub.restore();
+ } );
+ } );
+
+
+ QUnit.module( 'communityEvents.getFormattedDate', function() {
+ QUnit.test( 'single month event should use corresponding format', function( assert ) {
+ var actual = getFormattedDate(
+ startDate,
+ startDate + HOUR_IN_MS,
+ 'America/Vancouver',
+ communityEventsData.l10n.date_formats
+ );
+
+ assert.strictEqual( actual, 'Tuesday, Sep 15, 2020' );
+ } );
+
+ QUnit.test( 'multiple day event should use corresponding format', function( assert ) {
+ var actual = getFormattedDate(
+ startDate,
+ startDate + ( 2 * DAY_IN_MS ),
+ 'America/Vancouver',
+ communityEventsData.l10n.date_formats
+ );
+
+ assert.strictEqual( actual, 'September 15–17, 2020' );
+ } );
+
+ QUnit.test( 'multiple month event should use corresponding format', function( assert ) {
+ var actual = getFormattedDate(
+ startDate,
+ startDate + ( 3 * WEEK_IN_MS ),
+ 'America/Vancouver',
+ communityEventsData.l10n.date_formats
+ );
+
+ assert.strictEqual( actual, 'September 15 – October 6, 2020' );
+ } );
+
+ QUnit.test( 'undefined end date should be treated as a single-day event', function( assert ) {
+ var actual = getFormattedDate(
+ startDate,
+ undefined,
+ 'America/Vancouver',
+ communityEventsData.l10n.date_formats
+ );
+
+ assert.strictEqual( actual, 'Tuesday, Sep 15, 2020' );
+ } );
+
+ QUnit.test( 'empty end date should be treated as a single-day event', function( assert ) {
+ var actual = getFormattedDate(
+ startDate,
+ '',
+ 'America/Vancouver',
+ communityEventsData.l10n.date_formats
+ );
+
+ assert.strictEqual( actual, 'Tuesday, Sep 15, 2020' );
+ } );
+ } );
+
+
+ QUnit.module( 'communityEvents.getTimeZone', function() {
+ QUnit.test( 'modern browsers should return a time zone name', function( assert ) {
+ // Simulate a modern browser.
+ var stub = sinon.stub( Intl.DateTimeFormat.prototype, 'resolvedOptions' );
+ stub.returns( { timeZone: 'America/Chicago' } );
+
+ var actual = getTimeZone( startDate );
+
+ stub.restore();
+
+ assert.strictEqual( actual, 'America/Chicago' );
+ } );
+
+ QUnit.test( 'older browsers should fallback to a raw UTC offset', function( assert ) {
+ // Simulate IE11.
+ var resolvedOptionsStub = sinon.stub( Intl.DateTimeFormat.prototype, 'resolvedOptions' );
+ var getTimezoneOffsetStub = sinon.stub( Date.prototype, 'getTimezoneOffset' );
+
+ resolvedOptionsStub.returns( { timeZone: undefined } );
+
+ getTimezoneOffsetStub.returns( 300 );
+ var actual = getTimeZone( startDate );
+ assert.strictEqual( actual, -300, 'negative offset' ); // Intentionally opposite, see `getTimeZone()`.
+
+ getTimezoneOffsetStub.returns( 0 );
+ actual = getTimeZone( startDate );
+ assert.strictEqual( actual, 0, 'no offset' );
+
+ getTimezoneOffsetStub.returns( -300 );
+ actual = getTimeZone( startDate );
+ assert.strictEqual( actual, 300, 'positive offset' ); // Intentionally opposite, see `getTimeZone()`.
+
+ resolvedOptionsStub.restore();
+ getTimezoneOffsetStub.restore();
+ } );
+ } );
+
+
+ QUnit.module( 'communityEvents.getTimeZoneAbbreviation', function() {
+ QUnit.test( 'modern browsers should return a time zone abbreviation', function( assert ) {
+ // Modern browsers append a short time zone code to the time string.
+ var stub = sinon.stub( Date.prototype, 'toLocaleTimeString' );
+ stub.returns( '4:00:00 PM CDT' );
+
+ var actual = getTimeZoneAbbreviation( startDate );
+
+ stub.restore();
+
+ assert.strictEqual( actual, 'CDT' );
+ } );
+
+ QUnit.test( 'older browsers should fallback to a formatted UTC offset', function( assert ) {
+ var toLocaleTimeStringStub = sinon.stub( Date.prototype, 'toLocaleTimeString' );
+ var getTimezoneOffsetStub = sinon.stub( Date.prototype, 'getTimezoneOffset' );
+
+ // IE 11 doesn't add the abbreviation like modern browsers do.
+ toLocaleTimeStringStub.returns( '4:00:00 PM' );
+
+ getTimezoneOffsetStub.returns( 300 );
+ var actual = getTimeZoneAbbreviation( startDate );
+ assert.strictEqual( actual, 'GMT-5', 'negative offset' ); // Intentionally opposite, see `getTimeZone()`.
+
+ getTimezoneOffsetStub.returns( 0 );
+ actual = getTimeZoneAbbreviation( startDate );
+ assert.strictEqual( actual, 'GMT+0', 'no offset' );
+
+ getTimezoneOffsetStub.returns( -300 );
+ actual = getTimeZoneAbbreviation( startDate );
+ assert.strictEqual( actual, 'GMT+5', 'positive offset' ); // Intentionally opposite, see `getTimeZone()`.
+
+ toLocaleTimeStringStub.restore();
+ getTimezoneOffsetStub.restore();
+ } );
+ } );
+ } );
+} );
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/qunit/wp-admin/js/dashboard.js
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span></div>
</body>
</html>