Page MenuHomePhabricator

D16005.diff
No OneTemporary

D16005.diff

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

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)

Event Timeline