Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15427867
D16005.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
28 KB
Referenced Files
None
Subscribers
None
D16005.diff
View Options
diff --git a/resources/sql/autopatches/20160601.user.02.copyprefs.php b/resources/sql/autopatches/20160601.user.02.copyprefs.php
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160601.user.02.copyprefs.php
@@ -0,0 +1,59 @@
+<?php
+
+// Move timezone, translation and pronoun from the user object to preferences
+// so they can be defaulted and edited like other settings.
+
+$table = new PhabricatorUser();
+$conn_w = $table->establishConnection('w');
+$table_name = $table->getTableName();
+$prefs_table = new PhabricatorUserPreferences();
+
+foreach (new LiskRawMigrationIterator($conn_w, $table_name) as $row) {
+ $phid = $row['phid'];
+
+ $pref_row = queryfx_one(
+ $conn_w,
+ 'SELECT preferences FROM %T WHERE userPHID = %s',
+ $prefs_table->getTableName(),
+ $phid);
+
+ if ($pref_row) {
+ try {
+ $prefs = phutil_json_decode($pref_row['preferences']);
+ } catch (Exception $ex) {
+ $prefs = array();
+ }
+ } else {
+ $prefs = array();
+ }
+
+ $zone = $row['timezoneIdentifier'];
+ if (strlen($zone)) {
+ $prefs[PhabricatorTimezoneSetting::SETTINGKEY] = $zone;
+ }
+
+ $pronoun = $row['sex'];
+ if (strlen($pronoun)) {
+ $prefs[PhabricatorPronounSetting::SETTINGKEY] = $pronoun;
+ }
+
+ $translation = $row['translation'];
+ if (strlen($translation)) {
+ $prefs[PhabricatorTranslationSetting::SETTINGKEY] = $translation;
+ }
+
+ if ($prefs) {
+ queryfx(
+ $conn_w,
+ 'INSERT INTO %T (phid, userPHID, preferences, dateModified, dateCreated)
+ VALUES (%s, %s, %s, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())
+ ON DUPLICATE KEY UPDATE preferences = VALUES(preferences)',
+ $prefs_table->getTableName(),
+ $prefs_table->generatePHID(),
+ $phid,
+ phutil_json_encode($prefs));
+ }
+}
+
+$prefs_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES;
+PhabricatorUserCache::clearCacheForAllUsers($prefs_key);
diff --git a/resources/sql/autopatches/20160601.user.03.removetime.sql b/resources/sql/autopatches/20160601.user.03.removetime.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160601.user.03.removetime.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_user.user
+ DROP COLUMN timezoneIdentifier;
diff --git a/resources/sql/autopatches/20160601.user.04.removetranslation.sql b/resources/sql/autopatches/20160601.user.04.removetranslation.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160601.user.04.removetranslation.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_user.user
+ DROP COLUMN translation;
diff --git a/resources/sql/autopatches/20160601.user.05.removesex.sql b/resources/sql/autopatches/20160601.user.05.removesex.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160601.user.05.removesex.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_user.user
+ DROP COLUMN sex;
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
@@ -2837,6 +2837,7 @@
'PhabricatorOlderInlinesSetting' => 'applications/settings/setting/PhabricatorOlderInlinesSetting.php',
'PhabricatorOneTimeTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorOneTimeTriggerClock.php',
'PhabricatorOpcodeCacheSpec' => 'applications/cache/spec/PhabricatorOpcodeCacheSpec.php',
+ 'PhabricatorOptionGroupSetting' => 'applications/settings/setting/PhabricatorOptionGroupSetting.php',
'PhabricatorOwnerPathQuery' => 'applications/owners/query/PhabricatorOwnerPathQuery.php',
'PhabricatorOwnersApplication' => 'applications/owners/application/PhabricatorOwnersApplication.php',
'PhabricatorOwnersArchiveController' => 'applications/owners/controller/PhabricatorOwnersArchiveController.php',
@@ -3168,6 +3169,7 @@
'PhabricatorProjectsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsSearchEngineAttachment.php',
'PhabricatorProjectsSearchEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsSearchEngineExtension.php',
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsWatchersSearchEngineAttachment.php',
+ 'PhabricatorPronounSetting' => 'applications/settings/setting/PhabricatorPronounSetting.php',
'PhabricatorProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorProtocolAdapter.php',
'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php',
'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php',
@@ -3535,6 +3537,7 @@
'PhabricatorTimeFormatSetting' => 'applications/settings/setting/PhabricatorTimeFormatSetting.php',
'PhabricatorTimeGuard' => 'infrastructure/time/PhabricatorTimeGuard.php',
'PhabricatorTimeTestCase' => 'infrastructure/time/__tests__/PhabricatorTimeTestCase.php',
+ 'PhabricatorTimezoneSetting' => 'applications/settings/setting/PhabricatorTimezoneSetting.php',
'PhabricatorTimezoneSetupCheck' => 'applications/config/check/PhabricatorTimezoneSetupCheck.php',
'PhabricatorTitleGlyphsSetting' => 'applications/settings/setting/PhabricatorTitleGlyphsSetting.php',
'PhabricatorToken' => 'applications/tokens/storage/PhabricatorToken.php',
@@ -3565,6 +3568,7 @@
'PhabricatorTransactionsDestructionEngineExtension' => 'applications/transactions/engineextension/PhabricatorTransactionsDestructionEngineExtension.php',
'PhabricatorTransactionsFulltextEngineExtension' => 'applications/transactions/engineextension/PhabricatorTransactionsFulltextEngineExtension.php',
'PhabricatorTransformedFile' => 'applications/files/storage/PhabricatorTransformedFile.php',
+ 'PhabricatorTranslationSetting' => 'applications/settings/setting/PhabricatorTranslationSetting.php',
'PhabricatorTranslationsConfigOptions' => 'applications/config/option/PhabricatorTranslationsConfigOptions.php',
'PhabricatorTriggerAction' => 'infrastructure/daemon/workers/action/PhabricatorTriggerAction.php',
'PhabricatorTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorTriggerClock.php',
@@ -7455,6 +7459,7 @@
'PhabricatorOlderInlinesSetting' => 'PhabricatorSelectSetting',
'PhabricatorOneTimeTriggerClock' => 'PhabricatorTriggerClock',
'PhabricatorOpcodeCacheSpec' => 'PhabricatorCacheSpec',
+ 'PhabricatorOptionGroupSetting' => 'PhabricatorSetting',
'PhabricatorOwnerPathQuery' => 'Phobject',
'PhabricatorOwnersApplication' => 'PhabricatorApplication',
'PhabricatorOwnersArchiveController' => 'PhabricatorOwnersController',
@@ -7858,6 +7863,7 @@
'PhabricatorProjectsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
+ 'PhabricatorPronounSetting' => 'PhabricatorSelectSetting',
'PhabricatorProtocolAdapter' => 'Phobject',
'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorQuery' => 'Phobject',
@@ -8294,6 +8300,7 @@
'PhabricatorTimeFormatSetting' => 'PhabricatorSelectSetting',
'PhabricatorTimeGuard' => 'Phobject',
'PhabricatorTimeTestCase' => 'PhabricatorTestCase',
+ 'PhabricatorTimezoneSetting' => 'PhabricatorOptionGroupSetting',
'PhabricatorTimezoneSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorTitleGlyphsSetting' => 'PhabricatorSelectSetting',
'PhabricatorToken' => array(
@@ -8329,6 +8336,7 @@
'PhabricatorTransactionsDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
'PhabricatorTransactionsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
'PhabricatorTransformedFile' => 'PhabricatorFileDAO',
+ 'PhabricatorTranslationSetting' => 'PhabricatorOptionGroupSetting',
'PhabricatorTranslationsConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorTriggerAction' => 'Phobject',
'PhabricatorTriggerClock' => 'Phobject',
diff --git a/src/applications/calendar/__tests__/CalendarTimeUtilTestCase.php b/src/applications/calendar/__tests__/CalendarTimeUtilTestCase.php
--- a/src/applications/calendar/__tests__/CalendarTimeUtilTestCase.php
+++ b/src/applications/calendar/__tests__/CalendarTimeUtilTestCase.php
@@ -4,7 +4,7 @@
public function testTimestampsAtMidnight() {
$u = new PhabricatorUser();
- $u->setTimezoneIdentifier('America/Los_Angeles');
+ $u->overrideTimezoneIdentifier('America/Los_Angeles');
$days = $this->getAllDays();
foreach ($days as $day) {
$data = CalendarTimeUtil::getCalendarWidgetTimestamps(
@@ -19,7 +19,7 @@
public function testTimestampsStartDay() {
$u = new PhabricatorUser();
- $u->setTimezoneIdentifier('America/Los_Angeles');
+ $u->overrideTimezoneIdentifier('America/Los_Angeles');
$days = $this->getAllDays();
foreach ($days as $day) {
$data = CalendarTimeUtil::getTimestamps(
diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php
--- a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php
+++ b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php
@@ -164,33 +164,6 @@
return $this->getParam('herald-force-recipients', array());
}
- public function getTranslation(array $objects) {
- $default_translation = PhabricatorEnv::getEnvConfig('translation.provider');
- $return = null;
- $recipients = array_merge(
- idx($this->parameters, 'to', array()),
- idx($this->parameters, 'cc', array()));
- foreach (array_select_keys($objects, $recipients) as $object) {
- $translation = null;
- if ($object instanceof PhabricatorUser) {
- $translation = $object->getTranslation();
- }
- if (!$translation) {
- $translation = $default_translation;
- }
- if ($return && $translation != $return) {
- return $default_translation;
- }
- $return = $translation;
- }
-
- if (!$return) {
- $return = $default_translation;
- }
-
- return $return;
- }
-
public function addPHIDHeaders($name, array $phids) {
$phids = array_unique($phids);
foreach ($phids as $phid) {
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
@@ -5,6 +5,7 @@
* @task image-cache Profile Image Cache
* @task factors Multi-Factor Authentication
* @task handles Managing Handles
+ * @task settings Settings
* @task cache User Cache
*/
final class PhabricatorUser
@@ -26,15 +27,12 @@
protected $userName;
protected $realName;
- protected $sex;
- protected $translation;
protected $passwordSalt;
protected $passwordHash;
protected $profileImagePHID;
protected $profileImageCache;
protected $availabilityCache;
protected $availabilityCacheTTL;
- protected $timezoneIdentifier = '';
protected $consoleEnabled = 0;
protected $consoleVisible = 0;
@@ -68,14 +66,10 @@
private $authorities = array();
private $handlePool;
private $csrfSalt;
+ private $timezoneOverride;
protected function readField($field) {
switch ($field) {
- case 'timezoneIdentifier':
- // If the user hasn't set one, guess the server's time.
- return nonempty(
- $this->timezoneIdentifier,
- date_default_timezone_get());
// Make sure these return booleans.
case 'isAdmin':
return (bool)$this->isAdmin;
@@ -191,8 +185,6 @@
self::CONFIG_COLUMN_SCHEMA => array(
'userName' => 'sort64',
'realName' => 'text128',
- 'sex' => 'text4?',
- 'translation' => 'text64?',
'passwordSalt' => 'text32?',
'passwordHash' => 'text128?',
'profileImagePHID' => 'phid?',
@@ -204,7 +196,6 @@
'isMailingList' => 'bool',
'isDisabled' => 'bool',
'isAdmin' => 'bool',
- 'timezoneIdentifier' => 'text255',
'isEmailVerified' => 'uint32',
'isApproved' => 'uint32',
'accountSecret' => 'bytes64',
@@ -261,11 +252,6 @@
return $this;
}
- // To satisfy PhutilPerson.
- public function getSex() {
- return $this->sex;
- }
-
public function getMonogram() {
return '@'.$this->getUsername();
}
@@ -490,6 +476,10 @@
'(isPrimary = 1)');
}
+
+/* -( Settings )----------------------------------------------------------- */
+
+
public function getUserSetting($key) {
$settings_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES;
$settings = $this->requireCacheData($settings_key);
@@ -506,11 +496,51 @@
return null;
}
+
+ /**
+ * Test if a given setting is set to a particular value.
+ *
+ * @param const Setting key.
+ * @param wild Value to compare.
+ * @return bool True if the setting has the specified value.
+ * @task settings
+ */
public function compareUserSetting($key, $value) {
$actual = $this->getUserSetting($key);
return ($actual == $value);
}
+ public function getTranslation() {
+ return $this->getUserSetting(PhabricatorTranslationSetting::SETTINGKEY);
+ }
+
+ public function getTimezoneIdentifier() {
+ if ($this->timezoneOverride) {
+ return $this->timezoneOverride;
+ }
+
+ return $this->getUserSetting(PhabricatorTimezoneSetting::SETTINGKEY);
+ }
+
+
+ /**
+ * Override the user's timezone identifier.
+ *
+ * This is primarily useful for unit tests.
+ *
+ * @param string New timezone identifier.
+ * @return this
+ * @task settings
+ */
+ public function overrideTimezoneIdentifier($identifier) {
+ $this->timezoneOverride = $identifier;
+ return $this;
+ }
+
+ public function getSex() {
+ return $this->getUserSetting(PhabricatorPronounSetting::SETTINGKEY);
+ }
+
public function loadPreferences() {
if ($this->preferences) {
return $this->preferences;
@@ -1539,4 +1569,14 @@
return $usable_value;
}
+
+ /**
+ * @task cache
+ */
+ public function clearCacheData($key) {
+ unset($this->rawCacheData[$key]);
+ unset($this->usableCacheData[$key]);
+ return $this;
+ }
+
}
diff --git a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php
--- a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php
+++ b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php
@@ -31,6 +31,7 @@
$timezone = $request->getStr('timezone');
$pref_ignore = PhabricatorUserPreferences::PREFERENCE_IGNORE_OFFSET;
+ $pref_timezone = PhabricatorTimezoneSetting::SETTINGKEY;
$preferences = $viewer->loadPreferences();
@@ -52,11 +53,11 @@
if (isset($options[$timezone])) {
$preferences
->setPreference($pref_ignore, null)
+ ->setPreference($pref_timezone, $timezone)
->save();
- $viewer
- ->setTimezoneIdentifier($timezone)
- ->save();
+ $viewer->clearCacheData(
+ PhabricatorUserPreferencesCacheType::KEY_PREFERENCES);
}
}
@@ -115,9 +116,9 @@
$offset = $offset / 60;
if ($offset >= 0) {
- return pht('GMT-%d', $offset);
+ return pht('UTC-%d', $offset);
} else {
- return pht('GMT+%d', -$offset);
+ return pht('UTC+%d', -$offset);
}
}
diff --git a/src/applications/settings/editor/PhabricatorSettingsEditEngine.php b/src/applications/settings/editor/PhabricatorSettingsEditEngine.php
--- a/src/applications/settings/editor/PhabricatorSettingsEditEngine.php
+++ b/src/applications/settings/editor/PhabricatorSettingsEditEngine.php
@@ -78,6 +78,12 @@
$viewer = $this->getViewer();
$settings = PhabricatorSetting::getAllEnabledSettings($viewer);
+ foreach ($settings as $key => $setting) {
+ $setting = clone $setting;
+ $setting->setViewer($viewer);
+ $settings[$key] = $setting;
+ }
+
$fields = array();
foreach ($settings as $setting) {
foreach ($setting->newCustomEditFields($object) as $field) {
diff --git a/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php b/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php
--- a/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php
@@ -23,21 +23,29 @@
$user = $this->getUser();
$username = $user->getUsername();
+ $preferences = $user->loadPreferences();
+
$errors = array();
if ($request->isFormPost()) {
$sex = $request->getStr('sex');
$sexes = array(PhutilPerson::SEX_MALE, PhutilPerson::SEX_FEMALE);
if (in_array($sex, $sexes)) {
- $user->setSex($sex);
+ $new_value = $sex;
} else {
- $user->setSex(null);
+ $new_value = null;
}
- // Checked in runtime.
- $user->setTranslation($request->getStr('translation'));
+ $preferences->setPreference(
+ PhabricatorPronounSetting::SETTINGKEY,
+ $new_value);
+
+ $preferences->setPreference(
+ PhabricatorTranslationSetting::SETTINGKEY,
+ $request->getStr('translation'));
if (!$errors) {
- $user->save();
+ $preferences->save();
+
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI('?saved=true'));
}
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
@@ -18,6 +18,7 @@
$user = $request->getUser();
$username = $user->getUsername();
+ $pref_timezone = PhabricatorTimezoneSetting::SETTINGKEY;
$pref_time = PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT;
$pref_date = PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT;
$pref_week_start = PhabricatorUserPreferences::PREFERENCE_WEEK_START_DAY;
@@ -27,13 +28,12 @@
$errors = array();
if ($request->isFormPost()) {
$new_timezone = $request->getStr('timezone');
- if (in_array($new_timezone, DateTimeZone::listIdentifiers(), true)) {
- $user->setTimezoneIdentifier($new_timezone);
- } else {
+ if (!in_array($new_timezone, DateTimeZone::listIdentifiers(), true)) {
$errors[] = pht('The selected timezone is not a valid timezone.');
}
$preferences
+ ->setPreference($pref_timezone, $new_timezone)
->setPreference(
$pref_time,
$request->getStr($pref_time))
@@ -47,7 +47,7 @@
if (!$errors) {
$preferences->save();
- $user->save();
+
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI('?saved=true'));
}
diff --git a/src/applications/settings/setting/PhabricatorOptionGroupSetting.php b/src/applications/settings/setting/PhabricatorOptionGroupSetting.php
new file mode 100644
--- /dev/null
+++ b/src/applications/settings/setting/PhabricatorOptionGroupSetting.php
@@ -0,0 +1,73 @@
+<?php
+
+abstract class PhabricatorOptionGroupSetting
+ extends PhabricatorSetting {
+
+ abstract protected function getSelectOptionGroups();
+
+ final protected function getSelectOptionMap() {
+ $groups = $this->getSelectOptionGroups();
+
+ $map = array();
+ foreach ($groups as $group) {
+ $map += $group['options'];
+ }
+
+ return $map;
+ }
+
+ final protected function newCustomEditField($object) {
+ $setting_key = $this->getSettingKey();
+ $default_value = $object->getDefaultValue($setting_key);
+
+ $options = $this->getSelectOptionGroups();
+
+ $map = $this->getSelectOptionMap();
+ if (isset($map[$default_value])) {
+ $default_label = pht('Default (%s)', $map[$default_value]);
+ } else {
+ $default_label = pht('Default (Unknown, "%s")', $default_value);
+ }
+
+ $head_key = head_key($options);
+ $options[$head_key]['options'] = array(
+ '' => $default_label,
+ ) + $options[$head_key]['options'];
+
+ $flat_options = array();
+ foreach ($options as $group) {
+ $flat_options[$group['label']] = $group['options'];
+ }
+
+ return $this->newEditField($object, new PhabricatorSelectEditField())
+ ->setOptions($flat_options);
+ }
+
+ final public function validateTransactionValue($value) {
+ if (!strlen($value)) {
+ return;
+ }
+
+ $map = $this->getSelectOptionMap();
+
+ if (!isset($map[$value])) {
+ throw new Exception(
+ pht(
+ 'Value "%s" is not valid for setting "%s": valid values are %s.',
+ $value,
+ $this->getSettingName(),
+ implode(', ', array_keys($map))));
+ }
+
+ return;
+ }
+
+ public function getTransactionNewValue($value) {
+ if (!strlen($value)) {
+ return null;
+ }
+
+ return (string)$value;
+ }
+
+}
diff --git a/src/applications/settings/setting/PhabricatorPronounSetting.php b/src/applications/settings/setting/PhabricatorPronounSetting.php
new file mode 100644
--- /dev/null
+++ b/src/applications/settings/setting/PhabricatorPronounSetting.php
@@ -0,0 +1,35 @@
+<?php
+
+final class PhabricatorPronounSetting
+ extends PhabricatorSelectSetting {
+
+ const SETTINGKEY = 'pronoun';
+
+ public function getSettingName() {
+ return pht('Pronoun');
+ }
+
+ protected function getControlInstructions() {
+ return pht('Choose the pronoun you prefer.');
+ }
+
+ public function getSettingDefaultValue() {
+ return PhutilPerson::SEX_UNKNOWN;
+ }
+
+ protected function getSelectOptions() {
+ $viewer = $this->getViewer();
+ $username = $viewer->getUsername();
+
+ $label_unknown = pht('%s updated their profile', $username);
+ $label_her = pht('%s updated her profile', $username);
+ $label_his = pht('%s updated his profile', $username);
+
+ return array(
+ PhutilPerson::SEX_UNKNOWN => $label_unknown,
+ PhutilPerson::SEX_MALE => $label_his,
+ PhutilPerson::SEX_FEMALE => $label_her,
+ );
+ }
+
+}
diff --git a/src/applications/settings/setting/PhabricatorSelectSetting.php b/src/applications/settings/setting/PhabricatorSelectSetting.php
--- a/src/applications/settings/setting/PhabricatorSelectSetting.php
+++ b/src/applications/settings/setting/PhabricatorSelectSetting.php
@@ -17,9 +17,11 @@
$default_label = pht('Default (Unknown, "%s")', $default_value);
}
- $options = array(
- '' => $default_label,
- ) + $options;
+ if (empty($options[''])) {
+ $options = array(
+ '' => $default_label,
+ ) + $options;
+ }
return $this->newEditField($object, new PhabricatorSelectEditField())
->setOptions($options);
diff --git a/src/applications/settings/setting/PhabricatorTimezoneSetting.php b/src/applications/settings/setting/PhabricatorTimezoneSetting.php
new file mode 100644
--- /dev/null
+++ b/src/applications/settings/setting/PhabricatorTimezoneSetting.php
@@ -0,0 +1,53 @@
+<?php
+
+final class PhabricatorTimezoneSetting
+ extends PhabricatorOptionGroupSetting {
+
+ const SETTINGKEY = 'timezone';
+
+ public function getSettingName() {
+ return pht('Timezone');
+ }
+
+ public function getSettingDefaultValue() {
+ return date_default_timezone_get();
+ }
+
+ protected function getSelectOptionGroups() {
+ $timezones = DateTimeZone::listIdentifiers();
+ $now = new DateTime('@'.PhabricatorTime::getNow());
+
+ $groups = array();
+ foreach ($timezones as $timezone) {
+ $zone = new DateTimeZone($timezone);
+ $offset = -($zone->getOffset($now) / (60 * 60));
+ $groups[$offset][] = $timezone;
+ }
+
+ krsort($groups);
+
+ $option_groups = array(
+ array(
+ 'label' => pht('Default'),
+ 'options' => array(),
+ ),
+ );
+
+ foreach ($groups as $offset => $group) {
+ if ($offset >= 0) {
+ $label = pht('UTC-%d', $offset);
+ } else {
+ $label = pht('UTC+%d', -$offset);
+ }
+
+ sort($group);
+ $option_groups[] = array(
+ 'label' => $label,
+ 'options' => array_fuse($group),
+ );
+ }
+
+ return $option_groups;
+ }
+
+}
diff --git a/src/applications/settings/setting/PhabricatorTranslationSetting.php b/src/applications/settings/setting/PhabricatorTranslationSetting.php
new file mode 100644
--- /dev/null
+++ b/src/applications/settings/setting/PhabricatorTranslationSetting.php
@@ -0,0 +1,92 @@
+<?php
+
+final class PhabricatorTranslationSetting
+ extends PhabricatorOptionGroupSetting {
+
+ const SETTINGKEY = 'translation';
+
+ public function getSettingName() {
+ return pht('Translation');
+ }
+
+ public function getSettingDefaultValue() {
+ return 'en_US';
+ }
+
+ protected function getSelectOptionGroups() {
+ $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
+ $locales = PhutilLocale::loadAllLocales();
+
+ $group_labels = array(
+ 'normal' => pht('Translations'),
+ 'limited' => pht('Limited Translations'),
+ 'silly' => pht('Silly Translations'),
+ 'test' => pht('Developer/Test Translations'),
+ );
+
+ $groups = array_fill_keys(array_keys($group_labels), array());
+
+ $translations = array();
+ foreach ($locales as $locale) {
+ $code = $locale->getLocaleCode();
+
+ // Get the locale's localized name if it's available. For example,
+ // "Deutsch" instead of "German". This helps users who do not speak the
+ // current language to find the correct setting.
+ $raw_scope = PhabricatorEnv::beginScopedLocale($code);
+ $name = $locale->getLocaleName();
+ unset($raw_scope);
+
+ if ($locale->isSillyLocale()) {
+ if ($is_serious) {
+ // Omit silly locales on serious business installs.
+ continue;
+ }
+ $groups['silly'][$code] = $name;
+ continue;
+ }
+
+ if ($locale->isTestLocale()) {
+ $groups['test'][$code] = $name;
+ continue;
+ }
+
+ $strings = PhutilTranslation::getTranslationMapForLocale($code);
+ $size = count($strings);
+
+ // If a translation is English, assume it can fall back to the default
+ // strings and don't caveat its completeness.
+ $is_english = (substr($code, 0, 3) == 'en_');
+
+ // Arbitrarily pick some number of available strings to promote a
+ // translation out of the "limited" group. The major goal is just to
+ // keep locales with very few strings out of the main group, so users
+ // aren't surprised if a locale has no upstream translations available.
+ if ($size > 512 || $is_english) {
+ $type = 'normal';
+ } else {
+ $type = 'limited';
+ }
+
+ $groups[$type][$code] = $name;
+ }
+
+ $results = array();
+ foreach ($groups as $key => $group) {
+ $label = $group_labels[$key];
+ if (!$group) {
+ continue;
+ }
+
+ asort($group);
+
+ $results[] = array(
+ 'label' => $label,
+ 'options' => $group,
+ );
+ }
+
+ return $results;
+ }
+
+}
diff --git a/src/infrastructure/time/__tests__/PhabricatorTimeTestCase.php b/src/infrastructure/time/__tests__/PhabricatorTimeTestCase.php
--- a/src/infrastructure/time/__tests__/PhabricatorTimeTestCase.php
+++ b/src/infrastructure/time/__tests__/PhabricatorTimeTestCase.php
@@ -15,10 +15,10 @@
public function testParseLocalTime() {
$u = new PhabricatorUser();
- $u->setTimezoneIdentifier('UTC');
+ $u->overrideTimezoneIdentifier('UTC');
$v = new PhabricatorUser();
- $v->setTimezoneIdentifier('America/Los_Angeles');
+ $v->overrideTimezoneIdentifier('America/Los_Angeles');
$t = 1370202281; // 2013-06-02 12:44:41 -0700
$time = PhabricatorTime::pushTime($t, 'America/Los_Angeles');
diff --git a/src/view/__tests__/PhabricatorLocalTimeTestCase.php b/src/view/__tests__/PhabricatorLocalTimeTestCase.php
--- a/src/view/__tests__/PhabricatorLocalTimeTestCase.php
+++ b/src/view/__tests__/PhabricatorLocalTimeTestCase.php
@@ -4,10 +4,10 @@
public function testLocalTimeFormatting() {
$user = new PhabricatorUser();
- $user->setTimezoneIdentifier('America/Los_Angeles');
+ $user->overrideTimezoneIdentifier('America/Los_Angeles');
$utc = new PhabricatorUser();
- $utc->setTimezoneIdentifier('UTC');
+ $utc->overrideTimezoneIdentifier('UTC');
$this->assertEqual(
'Jan 1 2000, 12:00 AM',
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Mar 24, 4:30 PM (3 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7705484
Default Alt Text
D16005.diff (28 KB)
Attached To
Mode
D16005: Make translation, timezone and pronoun into real settings
Attached
Detach File
Event Timeline
Log In to Comment