1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 14:00:56 +01:00

Make Settings modular and allow them to be EditEngine'd

Summary: Ref T4103. This starts breaking out settings in a modern way to prepare for global defaults.

Test Plan:
  - Edited diff settings.
  - Saw them take effect in primary settings pane.
  - Set stuff to new automatic defaults.
  - Tried to edit another user's settings.
  - Edited a bot's settings as an administrator.

{F1669077}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T4103

Differential Revision: https://secure.phabricator.com/D15995
This commit is contained in:
epriestley 2016-05-27 05:53:30 -07:00
parent 2dc4096ea1
commit 5e6716399c
15 changed files with 566 additions and 4 deletions

View file

@ -2822,6 +2822,7 @@ phutil_register_library_map(array(
'PhabricatorObjectSelectorDialog' => 'view/control/PhabricatorObjectSelectorDialog.php',
'PhabricatorOffsetPagedQuery' => 'infrastructure/query/PhabricatorOffsetPagedQuery.php',
'PhabricatorOldWorldContentSource' => 'infrastructure/contentsource/PhabricatorOldWorldContentSource.php',
'PhabricatorOlderInlinesSetting' => 'applications/settings/setting/PhabricatorOlderInlinesSetting.php',
'PhabricatorOneTimeTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorOneTimeTriggerClock.php',
'PhabricatorOpcodeCacheSpec' => 'applications/cache/spec/PhabricatorOpcodeCacheSpec.php',
'PhabricatorOwnerPathQuery' => 'applications/owners/query/PhabricatorOwnerPathQuery.php',
@ -3348,11 +3349,15 @@ phutil_register_library_map(array(
'PhabricatorSecurityConfigOptions' => 'applications/config/option/PhabricatorSecurityConfigOptions.php',
'PhabricatorSecuritySetupCheck' => 'applications/config/check/PhabricatorSecuritySetupCheck.php',
'PhabricatorSelectEditField' => 'applications/transactions/editfield/PhabricatorSelectEditField.php',
'PhabricatorSelectSetting' => 'applications/settings/setting/PhabricatorSelectSetting.php',
'PhabricatorSendGridConfigOptions' => 'applications/config/option/PhabricatorSendGridConfigOptions.php',
'PhabricatorSessionsSettingsPanel' => 'applications/settings/panel/PhabricatorSessionsSettingsPanel.php',
'PhabricatorSetting' => 'applications/settings/setting/PhabricatorSetting.php',
'PhabricatorSettingsAddEmailAction' => 'applications/settings/action/PhabricatorSettingsAddEmailAction.php',
'PhabricatorSettingsAdjustController' => 'applications/settings/controller/PhabricatorSettingsAdjustController.php',
'PhabricatorSettingsApplication' => 'applications/settings/application/PhabricatorSettingsApplication.php',
'PhabricatorSettingsEditController' => 'applications/settings/controller/PhabricatorSettingsEditController.php',
'PhabricatorSettingsEditEngine' => 'applications/settings/editor/PhabricatorSettingsEditEngine.php',
'PhabricatorSettingsMainController' => 'applications/settings/controller/PhabricatorSettingsMainController.php',
'PhabricatorSettingsMainMenuBarExtension' => 'applications/settings/extension/PhabricatorSettingsMainMenuBarExtension.php',
'PhabricatorSettingsPanel' => 'applications/settings/panel/PhabricatorSettingsPanel.php',
@ -3363,6 +3368,7 @@ phutil_register_library_map(array(
'PhabricatorSetupIssueUIExample' => 'applications/uiexample/examples/PhabricatorSetupIssueUIExample.php',
'PhabricatorSetupIssueView' => 'applications/config/view/PhabricatorSetupIssueView.php',
'PhabricatorShortSite' => 'aphront/site/PhabricatorShortSite.php',
'PhabricatorShowFiletreeSetting' => 'applications/settings/setting/PhabricatorShowFiletreeSetting.php',
'PhabricatorSimpleEditType' => 'applications/transactions/edittype/PhabricatorSimpleEditType.php',
'PhabricatorSite' => 'aphront/site/PhabricatorSite.php',
'PhabricatorSlowvoteApplication' => 'applications/slowvote/application/PhabricatorSlowvoteApplication.php',
@ -3568,6 +3574,7 @@ phutil_register_library_map(array(
'PhabricatorUIExampleRenderController' => 'applications/uiexample/controller/PhabricatorUIExampleRenderController.php',
'PhabricatorUIExamplesApplication' => 'applications/uiexample/application/PhabricatorUIExamplesApplication.php',
'PhabricatorUSEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php',
'PhabricatorUnifiedDiffsSetting' => 'applications/settings/setting/PhabricatorUnifiedDiffsSetting.php',
'PhabricatorUnitTestContentSource' => 'infrastructure/contentsource/PhabricatorUnitTestContentSource.php',
'PhabricatorUnitsTestCase' => 'view/__tests__/PhabricatorUnitsTestCase.php',
'PhabricatorUnknownContentSource' => 'infrastructure/contentsource/PhabricatorUnknownContentSource.php',
@ -3592,6 +3599,7 @@ phutil_register_library_map(array(
'PhabricatorUserLogView' => 'applications/people/view/PhabricatorUserLogView.php',
'PhabricatorUserPHIDResolver' => 'applications/phid/resolver/PhabricatorUserPHIDResolver.php',
'PhabricatorUserPreferences' => 'applications/settings/storage/PhabricatorUserPreferences.php',
'PhabricatorUserPreferencesEditor' => 'applications/settings/editor/PhabricatorUserPreferencesEditor.php',
'PhabricatorUserPreferencesPHIDType' => 'applications/settings/phid/PhabricatorUserPreferencesPHIDType.php',
'PhabricatorUserPreferencesQuery' => 'applications/settings/query/PhabricatorUserPreferencesQuery.php',
'PhabricatorUserPreferencesTransaction' => 'applications/settings/storage/PhabricatorUserPreferencesTransaction.php',
@ -7413,6 +7421,7 @@ phutil_register_library_map(array(
'PhabricatorObjectSelectorDialog' => 'Phobject',
'PhabricatorOffsetPagedQuery' => 'PhabricatorQuery',
'PhabricatorOldWorldContentSource' => 'PhabricatorContentSource',
'PhabricatorOlderInlinesSetting' => 'PhabricatorSelectSetting',
'PhabricatorOneTimeTriggerClock' => 'PhabricatorTriggerClock',
'PhabricatorOpcodeCacheSpec' => 'PhabricatorCacheSpec',
'PhabricatorOwnerPathQuery' => 'Phobject',
@ -8064,11 +8073,15 @@ phutil_register_library_map(array(
'PhabricatorSecurityConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorSecuritySetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorSelectEditField' => 'PhabricatorEditField',
'PhabricatorSelectSetting' => 'PhabricatorSetting',
'PhabricatorSendGridConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorSessionsSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorSetting' => 'Phobject',
'PhabricatorSettingsAddEmailAction' => 'PhabricatorSystemAction',
'PhabricatorSettingsAdjustController' => 'PhabricatorController',
'PhabricatorSettingsApplication' => 'PhabricatorApplication',
'PhabricatorSettingsEditController' => 'PhabricatorController',
'PhabricatorSettingsEditEngine' => 'PhabricatorEditEngine',
'PhabricatorSettingsMainController' => 'PhabricatorController',
'PhabricatorSettingsMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension',
'PhabricatorSettingsPanel' => 'Phobject',
@ -8079,6 +8092,7 @@ phutil_register_library_map(array(
'PhabricatorSetupIssueUIExample' => 'PhabricatorUIExample',
'PhabricatorSetupIssueView' => 'AphrontView',
'PhabricatorShortSite' => 'PhabricatorSite',
'PhabricatorShowFiletreeSetting' => 'PhabricatorSelectSetting',
'PhabricatorSimpleEditType' => 'PhabricatorEditType',
'PhabricatorSite' => 'AphrontSite',
'PhabricatorSlowvoteApplication' => 'PhabricatorApplication',
@ -8305,6 +8319,7 @@ phutil_register_library_map(array(
'PhabricatorUIExampleRenderController' => 'PhabricatorController',
'PhabricatorUIExamplesApplication' => 'PhabricatorApplication',
'PhabricatorUSEnglishTranslation' => 'PhutilTranslation',
'PhabricatorUnifiedDiffsSetting' => 'PhabricatorSelectSetting',
'PhabricatorUnitTestContentSource' => 'PhabricatorContentSource',
'PhabricatorUnitsTestCase' => 'PhabricatorTestCase',
'PhabricatorUnknownContentSource' => 'PhabricatorContentSource',
@ -8351,6 +8366,7 @@ phutil_register_library_map(array(
'PhabricatorDestructibleInterface',
'PhabricatorApplicationTransactionInterface',
),
'PhabricatorUserPreferencesEditor' => 'AlmanacEditor',
'PhabricatorUserPreferencesPHIDType' => 'PhabricatorPHIDType',
'PhabricatorUserPreferencesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorUserPreferencesTransaction' => 'PhabricatorApplicationTransaction',

View file

@ -503,6 +503,7 @@ final class PhabricatorUser
if (!$preferences) {
$preferences = new PhabricatorUserPreferences();
$preferences->setUserPHID($this->getPHID());
$preferences->attachUser($this);
$default_dict = array(
PhabricatorUserPreferences::PREFERENCE_TITLES => 'glyph',

View file

@ -34,6 +34,8 @@ final class PhabricatorSettingsApplication extends PhabricatorApplication {
'adjust/' => 'PhabricatorSettingsAdjustController',
'timezone/(?P<offset>[^/]+)/'
=> 'PhabricatorSettingsTimezoneController',
'(?P<type>user)/(?P<username>[^/]+)/(?:panel/(?P<key>[^/]+)/)?'
=> 'PhabricatorSettingsEditController',
),
);
}

View file

@ -0,0 +1,35 @@
<?php
final class PhabricatorSettingsEditController
extends PhabricatorController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$engine = id(new PhabricatorSettingsEditEngine())
->setController($this);
switch ($request->getURIData('type')) {
case 'user':
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withUsernames(array($request->getURIData('username')))
->executeOne();
$preferences = $user->loadPreferences();
PhabricatorPolicyFilter::requireCapability(
$viewer,
$preferences,
PhabricatorPolicyCapability::CAN_EDIT);
$engine->setTargetObject($preferences);
break;
default:
return new Aphront404Response();
}
return $engine->buildResponse();
}
}

View file

@ -0,0 +1,91 @@
<?php
final class PhabricatorSettingsEditEngine
extends PhabricatorEditEngine {
const ENGINECONST = 'settings.settings';
public function isEngineConfigurable() {
return false;
}
public function getEngineName() {
return pht('Settings');
}
public function getSummaryHeader() {
return pht('Edit Settings Configurations');
}
public function getSummaryText() {
return pht('This engine is used to edit settings.');
}
public function getEngineApplicationClass() {
return 'PhabricatorSettingsApplication';
}
protected function newEditableObject() {
return new PhabricatorUserPreferences();
}
protected function newObjectQuery() {
return new PhabricatorUserPreferencesQuery();
}
protected function getObjectCreateTitleText($object) {
return pht('Create Settings');
}
protected function getObjectCreateButtonText($object) {
return pht('Create Settings');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Settings');
}
protected function getObjectEditShortText($object) {
return pht('Edit Settings');
}
protected function getObjectCreateShortText() {
return pht('Create Settings');
}
protected function getObjectName() {
return pht('Settings');
}
protected function getEditorURI() {
return '/settings/edit/';
}
protected function getObjectCreateCancelURI($object) {
return '/settings/';
}
protected function getObjectViewURI($object) {
// TODO: This isn't correct...
return '/settings/user/'.$this->getViewer()->getUsername().'/';
}
protected function getCreateNewObjectPolicy() {
return PhabricatorPolicies::POLICY_ADMIN;
}
protected function buildCustomEditFields($object) {
$viewer = $this->getViewer();
$settings = PhabricatorSetting::getAllEnabledSettings($viewer);
$fields = array();
foreach ($settings as $setting) {
foreach ($setting->newCustomEditFields($object) as $field) {
$fields[] = $field;
}
}
return $fields;
}
}

View file

@ -0,0 +1,132 @@
<?php
final class PhabricatorUserPreferencesEditor
extends AlmanacEditor {
public function getEditorObjectsDescription() {
return pht('Settings');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorUserPreferencesTransaction::TYPE_SETTING;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$setting_key = $xaction->getMetadataValue(
PhabricatorUserPreferencesTransaction::PROPERTY_SETTING);
switch ($xaction->getTransactionType()) {
case PhabricatorUserPreferencesTransaction::TYPE_SETTING:
return $object->getPreference($setting_key);
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$actor = $this->getActor();
$setting_key = $xaction->getMetadataValue(
PhabricatorUserPreferencesTransaction::PROPERTY_SETTING);
$settings = PhabricatorSetting::getAllEnabledSettings($actor);
$setting = $settings[$setting_key];
switch ($xaction->getTransactionType()) {
case PhabricatorUserPreferencesTransaction::TYPE_SETTING:
$value = $xaction->getNewValue();
$value = $setting->getTransactionNewValue($value);
return $value;
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$setting_key = $xaction->getMetadataValue(
PhabricatorUserPreferencesTransaction::PROPERTY_SETTING);
switch ($xaction->getTransactionType()) {
case PhabricatorUserPreferencesTransaction::TYPE_SETTING:
$new_value = $xaction->getNewValue();
if ($new_value === null) {
$object->unsetPreference($setting_key);
} else {
$object->setPreference($setting_key, $new_value);
}
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorUserPreferencesTransaction::TYPE_SETTING:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
$actor = $this->getActor();
$settings = PhabricatorSetting::getAllEnabledSettings($actor);
switch ($type) {
case PhabricatorUserPreferencesTransaction::TYPE_SETTING:
foreach ($xactions as $xaction) {
$setting_key = $xaction->getMetadataValue(
PhabricatorUserPreferencesTransaction::PROPERTY_SETTING);
$setting = idx($settings, $setting_key);
if (!$setting) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'There is no known application setting with key "%s".',
$setting_key),
$xaction);
continue;
}
try {
$setting->validateTransactionValue($xaction->getNewValue());
} catch (Exception $ex) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
$ex->getMessage(),
$xaction);
}
}
break;
}
return $errors;
}
}

View file

@ -32,7 +32,6 @@ final class PhabricatorUserPreferencesPHIDType extends PhabricatorPHIDType {
$viewer = $query->getViewer();
foreach ($handles as $phid => $handle) {
$preferences = $objects[$phid];
$handle->setName(pht('Settings %d', $preferences->getID()));
}
}

View file

@ -0,0 +1,35 @@
<?php
final class PhabricatorOlderInlinesSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'diff-ghosts';
const VALUE_GHOST_INLINES_ENABLED = 'default';
const VALUE_GHOST_INLINES_DISABLED = 'disabled';
public function getSettingName() {
return pht('Show Older Inlines');
}
protected function getControlInstructions() {
return pht(
'When a revision is updated, Phabricator attempts to bring inline '.
'comments on the older version forward to the new changes. You can '.
'disable this behavior if you prefer comments stay anchored in one '.
'place.');
}
public function getSettingDefaultValue() {
return self::VALUE_GHOST_INLINES_ENABLED;
}
protected function getSelectOptions() {
return array(
self::VALUE_GHOST_INLINES_ENABLED => pht('Enabled'),
self::VALUE_GHOST_INLINES_DISABLED => pht('Disabled'),
);
}
}

View file

@ -0,0 +1,55 @@
<?php
abstract class PhabricatorSelectSetting
extends PhabricatorSetting {
abstract protected function getSelectOptions();
final protected function newCustomEditField($object) {
$setting_key = $this->getSettingKey();
$default_value = $object->getDefaultValue($setting_key);
$options = $this->getSelectOptions();
if (isset($options[$default_value])) {
$default_label = pht('Default (%s)', $options[$default_value]);
} else {
$default_label = pht('Default (Unknown, "%s")', $default_value);
}
$options = array(
'' => $default_label,
) + $options;
return $this->newEditField($object, new PhabricatorSelectEditField())
->setOptions($options);
}
final public function validateTransactionValue($value) {
if (!strlen($value)) {
return;
}
$options = $this->getSelectOptions();
if (!isset($options[$value])) {
throw new Exception(
pht(
'Value "%s" is not valid for setting "%s": valid values are %s.',
$value,
$this->getSettingName(),
implode(', ', array_keys($options))));
}
return;
}
public function getTransactionNewValue($value) {
if (!strlen($value)) {
return null;
}
return (string)$value;
}
}

View file

@ -0,0 +1,96 @@
<?php
abstract class PhabricatorSetting extends Phobject {
private $viewer;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
abstract public function getSettingName();
protected function getControlInstructions() {
return null;
}
protected function isEnabledForViewer(PhabricatorUser $viewer) {
return true;
}
public function getSettingDefaultValue() {
return null;
}
final public function getSettingKey() {
return $this->getPhobjectClassConstant('SETTINGKEY');
}
public static function getAllSettings() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getSettingKey')
->execute();
}
public static function getAllEnabledSettings(PhabricatorUser $viewer) {
$settings = self::getAllSettings();
foreach ($settings as $key => $setting) {
if (!$setting->isEnabledForViewer($viewer)) {
unset($settings[$key]);
}
}
return $settings;
}
final public function newCustomEditFields($object) {
$fields = array();
$field = $this->newCustomEditField($object);
if ($field) {
$fields[] = $field;
}
return $fields;
}
protected function newCustomEditField($object) {
return null;
}
protected function newEditField($object, PhabricatorEditField $template) {
$setting_property = PhabricatorUserPreferencesTransaction::PROPERTY_SETTING;
$setting_key = $this->getSettingKey();
$value = $object->getPreference($setting_key);
$xaction_type = PhabricatorUserPreferencesTransaction::TYPE_SETTING;
$label = $this->getSettingName();
$template
->setKey($setting_key)
->setLabel($label)
->setValue($value)
->setTransactionType($xaction_type)
->setMetadataValue($setting_property, $setting_key);
$instructions = $this->getControlInstructions();
if (strlen($instructions)) {
$template->setControlInstructions($instructions);
}
return $template;
}
public function validateTransactionValue($value) {
return;
}
public function getTransactionNewValue($value) {
return $value;
}
}

View file

@ -0,0 +1,34 @@
<?php
final class PhabricatorShowFiletreeSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'diff-filetree';
const VALUE_DISABLE_FILETREE = 0;
const VALUE_ENABLE_FILETREE = 1;
public function getSettingName() {
return pht('Show Filetree');
}
protected function getControlInstructions() {
return pht(
'When viewing a revision or commit, you can enable a sidebar showing '.
'affected files. When this option is enabled, press {nav %s} to show '.
'or hide the sidebar.',
'f');
}
public function getSettingDefaultValue() {
return self::VALUE_DISABLE_FILETREE;
}
protected function getSelectOptions() {
return array(
self::VALUE_DISABLE_FILETREE => pht('Disable Filetree'),
self::VALUE_ENABLE_FILETREE => pht('Enable Filetree'),
);
}
}

View file

@ -0,0 +1,35 @@
<?php
final class PhabricatorUnifiedDiffsSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'diff-unified';
const VALUE_ON_SMALL_SCREENS = 'default';
const VALUE_ALWAYS_UNIFIED = 'unified';
public function getSettingName() {
return pht('Show Unified Diffs');
}
protected function getControlInstructions() {
return pht(
'Phabricator normally shows diffs in a side-by-side layout on large '.
'screens and automatically switches to a unified view on small '.
'screens (like mobile phones). If you prefer unified diffs even on '.
'large screens, you can select them for use on all displays.');
}
public function getSettingDefaultValue() {
return self::VALUE_ON_SMALL_SCREENS;
}
protected function getSelectOptions() {
return array(
self::VALUE_ON_SMALL_SCREENS => pht('On Small Screens'),
self::VALUE_ALWAYS_UNIFIED => pht('Always'),
);
}
}

View file

@ -92,6 +92,21 @@ final class PhabricatorUserPreferences
return $this;
}
public function getDefaultValue($key) {
$setting = self::getSettingObject($key);
if (!$setting) {
return null;
}
return $setting->getSettingDefaultValue();
}
private static function getSettingObject($key) {
$settings = PhabricatorSetting::getAllSettings();
return idx($settings, $key);
}
public function getPinnedApplications(array $apps, PhabricatorUser $viewer) {
$pref_pinned = self::PREFERENCE_APP_PINNED;
$pinned = $this->getPreference($pref_pinned);
@ -212,8 +227,7 @@ final class PhabricatorUserPreferences
public function getApplicationTransactionEditor() {
// TODO: Implement.
throw new PhutilMethodNotImplementedException();
return new PhabricatorUserPreferencesEditor();
}
public function getApplicationTransactionObject() {

View file

@ -3,6 +3,10 @@
final class PhabricatorUserPreferencesTransaction
extends PhabricatorApplicationTransaction {
const TYPE_SETTING = 'setting';
const PROPERTY_SETTING = 'setting.key';
public function getApplicationName() {
return 'user';
}

View file

@ -56,6 +56,7 @@ final class AphrontFormSelectControl extends AphrontFormControl {
$disabled = array_fuse($disabled);
$tags = array();
$already_selected = false;
foreach ($options as $value => $thing) {
if (is_array($thing)) {
$tags[] = phutil_tag(
@ -65,10 +66,22 @@ final class AphrontFormSelectControl extends AphrontFormControl {
),
self::renderOptions($selected, $thing));
} else {
// When there are a list of options including similar values like
// "0" and "" (the empty string), only select the first matching
// value. Ideally this should be more precise about matching, but we
// have 2,000 of these controls at this point so hold that for a
// broader rewrite.
if (!$already_selected && ($value == $selected)) {
$is_selected = 'selected';
$already_selected = true;
} else {
$is_selected = null;
}
$tags[] = phutil_tag(
'option',
array(
'selected' => ($value == $selected) ? 'selected' : null,
'selected' => $is_selected,
'value' => $value,
'disabled' => isset($disabled[$value]) ? 'disabled' : null,
),