mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-09 16:32:39 +01:00
Make translation, timezone and pronoun into real settings
Summary: Ref T4103. These are currently stored on the user, for historic/performance reasons. Since I want administrators to be able to set defaults for translations and timezones at a minimum and there's no longer a meaningful performance penalty for moving them off the user record, turn them into real preferences and then nuke the columns. Test Plan: - Set settings to unusual values. - Ran migrations. - Verified my unusual settings survived. - Created a new user. - Edited all settings with old and new UIs. - Reconciled client/server timezone disagreement. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16005
This commit is contained in:
parent
edfc6a6934
commit
ebd8f3c987
19 changed files with 422 additions and 66 deletions
59
resources/sql/autopatches/20160601.user.02.copyprefs.php
Normal file
59
resources/sql/autopatches/20160601.user.02.copyprefs.php
Normal file
|
@ -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);
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_user.user
|
||||
DROP COLUMN timezoneIdentifier;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_user.user
|
||||
DROP COLUMN translation;
|
2
resources/sql/autopatches/20160601.user.05.removesex.sql
Normal file
2
resources/sql/autopatches/20160601.user.05.removesex.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_user.user
|
||||
DROP COLUMN sex;
|
|
@ -2837,6 +2837,7 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'PhabricatorOlderInlinesSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorOneTimeTriggerClock' => 'PhabricatorTriggerClock',
|
||||
'PhabricatorOpcodeCacheSpec' => 'PhabricatorCacheSpec',
|
||||
'PhabricatorOptionGroupSetting' => 'PhabricatorSetting',
|
||||
'PhabricatorOwnerPathQuery' => 'Phobject',
|
||||
'PhabricatorOwnersApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorOwnersArchiveController' => 'PhabricatorOwnersController',
|
||||
|
@ -7858,6 +7863,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||
'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
|
||||
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||
'PhabricatorPronounSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorProtocolAdapter' => 'Phobject',
|
||||
'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorQuery' => 'Phobject',
|
||||
|
@ -8294,6 +8300,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorTimeFormatSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorTimeGuard' => 'Phobject',
|
||||
'PhabricatorTimeTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorTimezoneSetting' => 'PhabricatorOptionGroupSetting',
|
||||
'PhabricatorTimezoneSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorTitleGlyphsSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorToken' => array(
|
||||
|
@ -8329,6 +8336,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorTransactionsDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
|
||||
'PhabricatorTransactionsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
|
||||
'PhabricatorTransformedFile' => 'PhabricatorFileDAO',
|
||||
'PhabricatorTranslationSetting' => 'PhabricatorOptionGroupSetting',
|
||||
'PhabricatorTranslationsConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorTriggerAction' => 'Phobject',
|
||||
'PhabricatorTriggerClock' => 'Phobject',
|
||||
|
|
|
@ -4,7 +4,7 @@ final class CalendarTimeUtilTestCase extends PhabricatorTestCase {
|
|||
|
||||
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 @@ final class CalendarTimeUtilTestCase extends PhabricatorTestCase {
|
|||
|
||||
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(
|
||||
|
|
|
@ -164,33 +164,6 @@ final class PhabricatorMetaMTAMail
|
|||
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) {
|
||||
|
|
|
@ -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 @@ final class PhabricatorUser
|
|||
|
||||
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 @@ final class PhabricatorUser
|
|||
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 @@ final class PhabricatorUser
|
|||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'userName' => 'sort64',
|
||||
'realName' => 'text128',
|
||||
'sex' => 'text4?',
|
||||
'translation' => 'text64?',
|
||||
'passwordSalt' => 'text32?',
|
||||
'passwordHash' => 'text128?',
|
||||
'profileImagePHID' => 'phid?',
|
||||
|
@ -204,7 +196,6 @@ final class PhabricatorUser
|
|||
'isMailingList' => 'bool',
|
||||
'isDisabled' => 'bool',
|
||||
'isAdmin' => 'bool',
|
||||
'timezoneIdentifier' => 'text255',
|
||||
'isEmailVerified' => 'uint32',
|
||||
'isApproved' => 'uint32',
|
||||
'accountSecret' => 'bytes64',
|
||||
|
@ -261,11 +252,6 @@ final class PhabricatorUser
|
|||
return $this;
|
||||
}
|
||||
|
||||
// To satisfy PhutilPerson.
|
||||
public function getSex() {
|
||||
return $this->sex;
|
||||
}
|
||||
|
||||
public function getMonogram() {
|
||||
return '@'.$this->getUsername();
|
||||
}
|
||||
|
@ -490,6 +476,10 @@ final class PhabricatorUser
|
|||
'(isPrimary = 1)');
|
||||
}
|
||||
|
||||
|
||||
/* -( Settings )----------------------------------------------------------- */
|
||||
|
||||
|
||||
public function getUserSetting($key) {
|
||||
$settings_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES;
|
||||
$settings = $this->requireCacheData($settings_key);
|
||||
|
@ -506,11 +496,51 @@ final class PhabricatorUser
|
|||
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 @@ final class PhabricatorUser
|
|||
return $usable_value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task cache
|
||||
*/
|
||||
public function clearCacheData($key) {
|
||||
unset($this->rawCacheData[$key]);
|
||||
unset($this->usableCacheData[$key]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ final class PhabricatorSettingsTimezoneController
|
|||
$timezone = $request->getStr('timezone');
|
||||
|
||||
$pref_ignore = PhabricatorUserPreferences::PREFERENCE_IGNORE_OFFSET;
|
||||
$pref_timezone = PhabricatorTimezoneSetting::SETTINGKEY;
|
||||
|
||||
$preferences = $viewer->loadPreferences();
|
||||
|
||||
|
@ -52,11 +53,11 @@ final class PhabricatorSettingsTimezoneController
|
|||
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 @@ final class PhabricatorSettingsTimezoneController
|
|||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,6 +78,12 @@ final class PhabricatorSettingsEditEngine
|
|||
$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) {
|
||||
|
|
|
@ -23,21 +23,29 @@ final class PhabricatorAccountSettingsPanel extends PhabricatorSettingsPanel {
|
|||
$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'));
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ final class PhabricatorDateTimeSettingsPanel extends PhabricatorSettingsPanel {
|
|||
$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 @@ final class PhabricatorDateTimeSettingsPanel extends PhabricatorSettingsPanel {
|
|||
$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 @@ final class PhabricatorDateTimeSettingsPanel extends PhabricatorSettingsPanel {
|
|||
|
||||
if (!$errors) {
|
||||
$preferences->save();
|
||||
$user->save();
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($this->getPanelURI('?saved=true'));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -17,9 +17,11 @@ abstract class PhabricatorSelectSetting
|
|||
$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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -15,10 +15,10 @@ final class PhabricatorTimeTestCase extends PhabricatorTestCase {
|
|||
|
||||
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');
|
||||
|
|
|
@ -4,10 +4,10 @@ final class PhabricatorLocalTimeTestCase extends PhabricatorTestCase {
|
|||
|
||||
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',
|
||||
|
|
Loading…
Reference in a new issue