Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15487650
D15961.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
10 KB
Referenced Files
None
Subscribers
None
D15961.diff
View Options
diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php
--- a/resources/celerity/packages.php
+++ b/resources/celerity/packages.php
@@ -81,6 +81,7 @@
'javelin-behavior-scrollbar',
'javelin-behavior-durable-column',
'conpherence-thread-manager',
+ 'javelin-behavior-detect-timezone',
),
'core.pkg.css' => array(
'phabricator-core-css',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -3355,6 +3355,7 @@
'PhabricatorSettingsMainController' => 'applications/settings/controller/PhabricatorSettingsMainController.php',
'PhabricatorSettingsMainMenuBarExtension' => 'applications/settings/extension/PhabricatorSettingsMainMenuBarExtension.php',
'PhabricatorSettingsPanel' => 'applications/settings/panel/PhabricatorSettingsPanel.php',
+ 'PhabricatorSettingsTimezoneController' => 'applications/settings/controller/PhabricatorSettingsTimezoneController.php',
'PhabricatorSetupCheck' => 'applications/config/check/PhabricatorSetupCheck.php',
'PhabricatorSetupCheckTestCase' => 'applications/config/check/__tests__/PhabricatorSetupCheckTestCase.php',
'PhabricatorSetupIssue' => 'applications/config/issue/PhabricatorSetupIssue.php',
@@ -8065,6 +8066,7 @@
'PhabricatorSettingsMainController' => 'PhabricatorController',
'PhabricatorSettingsMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension',
'PhabricatorSettingsPanel' => 'Phobject',
+ 'PhabricatorSettingsTimezoneController' => 'PhabricatorController',
'PhabricatorSetupCheck' => 'Phobject',
'PhabricatorSetupCheckTestCase' => 'PhabricatorTestCase',
'PhabricatorSetupIssue' => 'Phobject',
diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php
--- a/src/applications/people/storage/PhabricatorUser.php
+++ b/src/applications/people/storage/PhabricatorUser.php
@@ -755,6 +755,17 @@
return new DateTimeZone($this->getTimezoneIdentifier());
}
+ public function getTimeZoneOffset() {
+ $timezone = $this->getTimeZone();
+ $now = new DateTime('@'.PhabricatorTime::getNow());
+ $offset = $timezone->getOffset($now);
+
+ // Javascript offsets are in minutes and have the opposite sign.
+ $offset = -(int)($offset / 60);
+
+ return $offset;
+ }
+
public function formatShortDateTime($when, $now = null) {
if ($now === null) {
$now = PhabricatorTime::getNow();
diff --git a/src/applications/settings/application/PhabricatorSettingsApplication.php b/src/applications/settings/application/PhabricatorSettingsApplication.php
--- a/src/applications/settings/application/PhabricatorSettingsApplication.php
+++ b/src/applications/settings/application/PhabricatorSettingsApplication.php
@@ -32,6 +32,8 @@
'(?:(?P<id>\d+)/)?(?:panel/(?P<key>[^/]+)/)?'
=> 'PhabricatorSettingsMainController',
'adjust/' => 'PhabricatorSettingsAdjustController',
+ 'timezone/(?P<offset>[^/]+)/'
+ => 'PhabricatorSettingsTimezoneController',
),
);
}
diff --git a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php
@@ -0,0 +1,108 @@
+<?php
+
+final class PhabricatorSettingsTimezoneController
+ extends PhabricatorController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
+
+ $client_offset = $request->getURIData('offset');
+ $client_offset = (int)$client_offset;
+
+ $timezones = DateTimeZone::listIdentifiers();
+ $now = new DateTime('@'.PhabricatorTime::getNow());
+
+ $options = array(
+ 'ignore' => pht('Ignore Conflict'),
+ );
+
+ foreach ($timezones as $identifier) {
+ $zone = new DateTimeZone($identifier);
+ $offset = -($zone->getOffset($now) / 60);
+ if ($offset == $client_offset) {
+ $options[$identifier] = $identifier;
+ }
+ }
+
+ $settings_help = pht(
+ 'You can change your date and time preferences in Settings.');
+
+ if ($request->isFormPost()) {
+ $timezone = $request->getStr('timezone');
+
+ $pref_ignore = PhabricatorUserPreferences::PREFERENCE_IGNORE_OFFSET;
+
+ $preferences = $viewer->loadPreferences();
+
+ if ($timezone == 'ignore') {
+ $preferences
+ ->setPreference($pref_ignore, $client_offset)
+ ->save();
+
+ return $this->newDialog()
+ ->setTitle(pht('Conflict Ignored'))
+ ->appendParagraph(
+ pht(
+ 'The conflict between your browser and profile timezone '.
+ 'settings will be ignored.'))
+ ->appendParagraph($settings_help)
+ ->addCancelButton('/', pht('Done'));
+ }
+
+ if (isset($options[$timezone])) {
+ $preferences
+ ->setPreference($pref_ignore, null)
+ ->save();
+
+ $viewer
+ ->setTimezoneIdentifier($timezone)
+ ->save();
+ }
+ }
+
+ $server_offset = $viewer->getTimeZoneOffset();
+
+ if ($client_offset == $server_offset) {
+ return $this->newDialog()
+ ->setTitle(pht('Timezone Calibrated'))
+ ->appendParagraph(
+ pht(
+ 'Your browser timezone and profile timezone are now '.
+ 'in agreement (%s).',
+ $this->formatOffset($client_offset)))
+ ->appendParagraph($settings_help)
+ ->addCancelButton('/', pht('Done'));
+ }
+
+ $form = id(new AphrontFormView())
+ ->appendChild(
+ id(new AphrontFormSelectControl())
+ ->setName('timezone')
+ ->setLabel(pht('Timezone'))
+ ->setOptions($options));
+
+ return $this->newDialog()
+ ->setTitle(pht('Adjust Timezone'))
+ ->appendParagraph(
+ pht(
+ 'Your browser timezone (%s) differs from your profile timezone '.
+ '(%s). You can ignore this conflict or adjust your profile setting '.
+ 'to match your client.',
+ $this->formatOffset($client_offset),
+ $this->formatOffset($server_offset)))
+ ->appendForm($form)
+ ->addCancelButton(pht('Cancel'))
+ ->addSubmitButton(pht('Submit'));
+ }
+
+ private function formatOffset($offset) {
+ $offset = $offset / 60;
+
+ if ($offset >= 0) {
+ return pht('GMT-%d', $offset);
+ } else {
+ return pht('GMT+%d', -$offset);
+ }
+ }
+
+}
diff --git a/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php b/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php
--- a/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php
@@ -21,6 +21,7 @@
$pref_time = PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT;
$pref_date = PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT;
$pref_week_start = PhabricatorUserPreferences::PREFERENCE_WEEK_START_DAY;
+ $pref_ignore = PhabricatorUserPreferences::PREFERENCE_IGNORE_OFFSET;
$preferences = $user->loadPreferences();
$errors = array();
@@ -41,7 +42,8 @@
$request->getStr($pref_date))
->setPreference(
$pref_week_start,
- $request->getStr($pref_week_start));
+ $request->getStr($pref_week_start))
+ ->setPreference($pref_ignore, null);
if (!$errors) {
$preferences->save();
diff --git a/src/applications/settings/storage/PhabricatorUserPreferences.php b/src/applications/settings/storage/PhabricatorUserPreferences.php
--- a/src/applications/settings/storage/PhabricatorUserPreferences.php
+++ b/src/applications/settings/storage/PhabricatorUserPreferences.php
@@ -43,6 +43,7 @@
const PREFERENCE_PROFILE_MENU_COLLAPSED = 'profile-menu.collapsed';
const PREFERENCE_FAVORITE_POLICIES = 'policy.favorites';
+ const PREFERENCE_IGNORE_OFFSET = 'time.offset.ignore';
// These are in an unusual order for historic reasons.
const MAILTAG_PREFERENCE_NOTIFY = 0;
diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php
--- a/src/view/page/PhabricatorStandardPageView.php
+++ b/src/view/page/PhabricatorStandardPageView.php
@@ -223,6 +223,30 @@
}
if ($user) {
+ if ($user->isLoggedIn()) {
+ $offset = $user->getTimeZoneOffset();
+
+ $preferences = $user->loadPreferences();
+ $ignore_key = PhabricatorUserPreferences::PREFERENCE_IGNORE_OFFSET;
+
+ $ignore = $preferences->getPreference($ignore_key);
+ if (!strlen($ignore)) {
+ $ignore = null;
+ }
+
+ Javelin::initBehavior(
+ 'detect-timezone',
+ array(
+ 'offset' => $offset,
+ 'uri' => '/settings/timezone/',
+ 'message' => pht(
+ 'Your browser timezone setting differs from the timezone '.
+ 'setting in your profile.'),
+ 'ignoreKey' => $ignore_key,
+ 'ignore' => $ignore,
+ ));
+ }
+
$default_img_uri =
celerity_get_resource_uri(
'rsrc/image/icon/fatcow/document_black.png');
diff --git a/webroot/rsrc/js/core/behavior-detect-timezone.js b/webroot/rsrc/js/core/behavior-detect-timezone.js
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/js/core/behavior-detect-timezone.js
@@ -0,0 +1,53 @@
+/**
+ * @provides javelin-behavior-detect-timezone
+ * @requires javelin-behavior
+ * javelin-uri
+ * phabricator-notification
+ */
+
+JX.behavior('detect-timezone', function(config) {
+
+ var offset = new Date().getTimezoneOffset();
+ var ignore = config.ignore;
+
+ if (ignore !== null) {
+ // If we're ignoring a client offset and it's the current offset, just
+ // bail. This means the user has chosen to ignore the clock difference
+ // between the current client setting and their server setting.
+ if (offset == ignore) {
+ return;
+ }
+
+ // If we're ignoring a client offset but the current offset is different,
+ // wipe the offset. If you go from SF to NY, ignore the difference, return
+ // to SF, then travel back to NY a few months later, we want to prompt you
+ // again. This code will clear the ignored setting upon your return to SF.
+ new JX.Request('/settings/adjust/', JX.bag)
+ .setData({key: config.ignoreKey, value: ''})
+ .send();
+
+ ignore = null;
+ }
+
+ // If the client and server clocks are in sync, we're all set.
+ if (offset == config.offset) {
+ return;
+ }
+
+ var notification = new JX.Notification()
+ .alterClassName('jx-notification-alert', true)
+ .setContent(config.message)
+ .setDuration(0);
+
+ notification.listen('activate', function() {
+ JX.Stratcom.context().kill();
+ notification.hide();
+
+ var uri = config.uri + offset + '/';
+
+ new JX.Workflow(uri)
+ .start();
+ });
+
+ notification.show();
+});
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Apr 11, 3:53 PM (3 d, 17 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7705826
Default Alt Text
D15961.diff (10 KB)
Attached To
Mode
D15961: Detect timezone discrepancies and prompt users to reconcile them
Attached
Detach File
Event Timeline
Log In to Comment