From 269e0bfc943e0d5c41150ec18373feb46d904ee7 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 18 Nov 2015 10:50:09 -0800 Subject: [PATCH] Allow EditEngine form fields to be locked and hidden Summary: Ref T9132. Allows fields to be locked (shown, but not modifiable) and hidden (not shown). In both cases, default values are still respected. This lets you do things like create a form that generates objects with specific projects, policies, etc. Test Plan: - Set defaults. - Locked and hid a bunch of fields. - Created new objects using the resulting form. {F975801} {F975802} {F975803} {F975804} Reviewers: chad Reviewed By: chad Maniphest Tasks: T9132 Differential Revision: https://secure.phabricator.com/D14509 --- resources/celerity/map.php | 6 +- src/__phutil_library_map__.php | 2 + .../PhabricatorTransactionsApplication.php | 2 + ...rEditEngineConfigurationLockController.php | 109 ++++++++++++++++++ ...rEditEngineConfigurationViewController.php | 10 ++ .../editengine/PhabricatorEditEngine.php | 16 ++- .../editfield/PhabricatorEditField.php | 48 +++++++- ...abricatorEditEngineConfigurationEditor.php | 8 ++ .../PhabricatorEditEngineConfiguration.php | 43 ++++--- ...atorEditEngineConfigurationTransaction.php | 5 + webroot/rsrc/css/phui/phui-form-view.css | 4 + 11 files changed, 235 insertions(+), 18 deletions(-) create mode 100644 src/applications/transactions/controller/PhabricatorEditEngineConfigurationLockController.php diff --git a/resources/celerity/map.php b/resources/celerity/map.php index c45f8f5346..29c5dee710 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => '0689cd7e', + 'core.pkg.css' => '9f339987', 'core.pkg.js' => '47dc9ebb', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '2de124c9', @@ -130,7 +130,7 @@ return array( 'rsrc/css/phui/phui-document.css' => '765a9dac', 'rsrc/css/phui/phui-feed-story.css' => 'b7b26d23', 'rsrc/css/phui/phui-fontkit.css' => '9cda225e', - 'rsrc/css/phui/phui-form-view.css' => '621b21c5', + 'rsrc/css/phui/phui-form-view.css' => 'c1d2ef29', 'rsrc/css/phui/phui-form.css' => 'afdb2c6e', 'rsrc/css/phui/phui-header-view.css' => '55bb32dd', 'rsrc/css/phui/phui-icon.css' => 'b0a6b1b6', @@ -795,7 +795,7 @@ return array( 'phui-font-icon-base-css' => 'ecbbb4c2', 'phui-fontkit-css' => '9cda225e', 'phui-form-css' => 'afdb2c6e', - 'phui-form-view-css' => '621b21c5', + 'phui-form-view-css' => 'c1d2ef29', 'phui-header-view-css' => '55bb32dd', 'phui-icon-view-css' => 'b0a6b1b6', 'phui-image-mask-css' => '5a8b09c8', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 02f66e7320..1e9b07657c 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2117,6 +2117,7 @@ phutil_register_library_map(array( 'PhabricatorEditEngineConfigurationEditEngine' => 'applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php', 'PhabricatorEditEngineConfigurationEditor' => 'applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php', 'PhabricatorEditEngineConfigurationListController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php', + 'PhabricatorEditEngineConfigurationLockController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationLockController.php', 'PhabricatorEditEngineConfigurationPHIDType' => 'applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php', 'PhabricatorEditEngineConfigurationQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php', 'PhabricatorEditEngineConfigurationReorderController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationReorderController.php', @@ -6211,6 +6212,7 @@ phutil_register_library_map(array( 'PhabricatorEditEngineConfigurationEditEngine' => 'PhabricatorEditEngine', 'PhabricatorEditEngineConfigurationEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorEditEngineConfigurationListController' => 'PhabricatorEditEngineController', + 'PhabricatorEditEngineConfigurationLockController' => 'PhabricatorEditEngineController', 'PhabricatorEditEngineConfigurationPHIDType' => 'PhabricatorPHIDType', 'PhabricatorEditEngineConfigurationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorEditEngineConfigurationReorderController' => 'PhabricatorEditEngineController', diff --git a/src/applications/transactions/application/PhabricatorTransactionsApplication.php b/src/applications/transactions/application/PhabricatorTransactionsApplication.php index 9006a91ce3..c0a300e327 100644 --- a/src/applications/transactions/application/PhabricatorTransactionsApplication.php +++ b/src/applications/transactions/application/PhabricatorTransactionsApplication.php @@ -49,6 +49,8 @@ final class PhabricatorTransactionsApplication extends PhabricatorApplication { 'PhabricatorEditEngineConfigurationReorderController', 'defaults/(?P[^/]+)/' => 'PhabricatorEditEngineConfigurationDefaultsController', + 'lock/(?P[^/]+)/' => + 'PhabricatorEditEngineConfigurationLockController', ), ), ), diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationLockController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationLockController.php new file mode 100644 index 0000000000..ba433786fb --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationLockController.php @@ -0,0 +1,109 @@ +getURIData('engineKey'); + $this->setEngineKey($engine_key); + + $key = $request->getURIData('key'); + $viewer = $this->getViewer(); + + $config = id(new PhabricatorEditEngineConfigurationQuery()) + ->setViewer($viewer) + ->withEngineKeys(array($engine_key)) + ->withIdentifiers(array($key)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$config) { + return id(new Aphront404Response()); + } + + $cancel_uri = "/transactions/editengine/{$engine_key}/view/{$key}/"; + + if ($request->isFormPost()) { + $xactions = array(); + + $locks = $request->getArr('locks'); + $type_locks = PhabricatorEditEngineConfigurationTransaction::TYPE_LOCKS; + + $xactions[] = id(new PhabricatorEditEngineConfigurationTransaction()) + ->setTransactionType($type_locks) + ->setNewValue($locks); + + $editor = id(new PhabricatorEditEngineConfigurationEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnMissingFields(true) + ->setContinueOnNoEffect(true); + + $editor->applyTransactions($config, $xactions); + + return id(new AphrontRedirectResponse()) + ->setURI($cancel_uri); + } + + $engine = $config->getEngine(); + $fields = $engine->getFieldsForConfig($config); + + $help = pht(<<setUser($viewer) + ->appendRemarkupInstructions($help); + + $locks = $config->getFieldLocks(); + + $lock_visible = PhabricatorEditEngineConfiguration::LOCK_VISIBLE; + $lock_locked = PhabricatorEditEngineConfiguration::LOCK_LOCKED; + $lock_hidden = PhabricatorEditEngineConfiguration::LOCK_HIDDEN; + + $map = array( + $lock_visible => pht('Visible'), + $lock_locked => pht("\xF0\x9F\x94\x92 Locked"), + $lock_hidden => pht("\xE2\x9C\x98 Hidden"), + ); + + foreach ($fields as $field) { + if (!$field->getIsLockable()) { + continue; + } + + $key = $field->getKey(); + + $label = $field->getLabel(); + if (!strlen($label)) { + $label = $key; + } + + $form->appendControl( + id(new AphrontFormSelectControl()) + ->setName('locks['.$key.']') + ->setLabel($label) + ->setValue(idx($locks, $key, $lock_visible)) + ->setOptions($map)); + } + + return $this->newDialog() + ->setTitle(pht('Lock / Hide Fields')) + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->appendForm($form) + ->addSubmitButton(pht('Save Changes')) + ->addCancelButton($cancel_uri); + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php index 0dc26c57cd..5a60b16f0d 100644 --- a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php @@ -139,6 +139,16 @@ final class PhabricatorEditEngineConfigurationViewController ->setWorkflow(true) ->setDisabled(!$can_edit)); + $lock_uri = "{$base_uri}/lock/{$form_key}/"; + + $view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Lock / Hide Fields')) + ->setIcon('fa-lock') + ->setHref($lock_uri) + ->setWorkflow(true) + ->setDisabled(!$can_edit)); + return $view; } diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index e3746f3d7c..685a634115 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -658,12 +658,22 @@ abstract class PhabricatorEditEngine $validation_exception = null; if ($request->isFormPost()) { foreach ($fields as $field) { + if ($field->getIsLocked() || $field->getIsHidden()) { + continue; + } + $field->readValueFromSubmit($request); } $xactions = array(); foreach ($fields as $field) { - $xactions[] = $field->generateTransaction(clone $template); + $xaction = $field->generateTransaction(clone $template); + + if (!$xaction) { + continue; + } + + $xactions[] = $xaction; } $editor = $object->getApplicationTransactionEditor() @@ -683,6 +693,10 @@ abstract class PhabricatorEditEngine } else { if ($this->getIsCreate()) { foreach ($fields as $field) { + if ($field->getIsLocked() || $field->getIsHidden()) { + continue; + } + $field->readValueFromRequest($request); } } else { diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php index 23a9ca7f3b..38397f945c 100644 --- a/src/applications/transactions/editfield/PhabricatorEditField.php +++ b/src/applications/transactions/editfield/PhabricatorEditField.php @@ -13,11 +13,16 @@ abstract class PhabricatorEditField extends Phobject { private $metadata = array(); private $description; private $editTypeKey; + private $isLocked; + private $isHidden; + private $isPreview; - private $isReorderable = true; private $isEditDefaults; + + private $isReorderable = true; private $isDefaultable = true; + private $isLockable = true; public function setKey($key) { $this->key = $key; @@ -118,6 +123,24 @@ abstract class PhabricatorEditField extends Phobject { return $this->isDefaultable; } + public function setIsLockable($is_lockable) { + $this->isLockable = $is_lockable; + return $this; + } + + public function getIsLockable() { + return $this->isLockable; + } + + public function setIsHidden($is_hidden) { + $this->isHidden = $is_hidden; + return $this; + } + + public function getIsHidden() { + return $this->isHidden; + } + protected function newControl() { throw new PhutilMethodNotImplementedException(); } @@ -138,10 +161,17 @@ abstract class PhabricatorEditField extends Phobject { if ($this->getIsPreview()) { $disabled = true; + $hidden = false; } else if ($this->getIsEditDefaults()) { $disabled = false; + $hidden = false; } else { $disabled = $this->getIsLocked(); + $hidden = $this->getIsHidden(); + } + + if ($hidden) { + return null; } $control->setDisabled($disabled); @@ -152,6 +182,18 @@ abstract class PhabricatorEditField extends Phobject { public function appendToForm(AphrontFormView $form) { $control = $this->renderControl(); if ($control !== null) { + + if ($this->getIsPreview()) { + if ($this->getIsHidden()) { + $control + ->addClass('aphront-form-preview-hidden') + ->setError(pht('Hidden')); + } else if ($this->getIsLocked()) { + $control + ->setError(pht('Locked')); + } + } + $form->appendControl($control); } return $this; @@ -187,6 +229,10 @@ abstract class PhabricatorEditField extends Phobject { public function generateTransaction( PhabricatorApplicationTransaction $xaction) { + if (!$this->getTransactionType()) { + return null; + } + $xaction ->setTransactionType($this->getTransactionType()) ->setNewValue($this->getValueForTransaction()); diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php index 003c1e1c09..9c115254fe 100644 --- a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php @@ -21,6 +21,7 @@ final class PhabricatorEditEngineConfigurationEditor $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_PREAMBLE; $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_ORDER; $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_DEFAULT; + $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_LOCKS; return $types; } @@ -67,6 +68,8 @@ final class PhabricatorEditEngineConfigurationEditor case PhabricatorEditEngineConfigurationTransaction::TYPE_DEFAULT: $field_key = $xaction->getMetadataValue('field.key'); return $object->getFieldDefault($field_key); + case PhabricatorEditEngineConfigurationTransaction::TYPE_LOCKS: + return $object->getFieldLocks(); } } @@ -79,6 +82,7 @@ final class PhabricatorEditEngineConfigurationEditor case PhabricatorEditEngineConfigurationTransaction::TYPE_PREAMBLE; case PhabricatorEditEngineConfigurationTransaction::TYPE_ORDER: case PhabricatorEditEngineConfigurationTransaction::TYPE_DEFAULT: + case PhabricatorEditEngineConfigurationTransaction::TYPE_LOCKS: return $xaction->getNewValue(); } } @@ -101,6 +105,9 @@ final class PhabricatorEditEngineConfigurationEditor $field_key = $xaction->getMetadataValue('field.key'); $object->setFieldDefault($field_key, $xaction->getNewValue()); return; + case PhabricatorEditEngineConfigurationTransaction::TYPE_LOCKS: + $object->setFieldLocks($xaction->getNewValue()); + return; } return parent::applyCustomInternalTransaction($object, $xaction); @@ -115,6 +122,7 @@ final class PhabricatorEditEngineConfigurationEditor case PhabricatorEditEngineConfigurationTransaction::TYPE_PREAMBLE; case PhabricatorEditEngineConfigurationTransaction::TYPE_ORDER; case PhabricatorEditEngineConfigurationTransaction::TYPE_DEFAULT: + case PhabricatorEditEngineConfigurationTransaction::TYPE_LOCKS: return; } diff --git a/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php b/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php index 33964d9fcd..afe628359b 100644 --- a/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php +++ b/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php @@ -17,6 +17,10 @@ final class PhabricatorEditEngineConfiguration private $engine = self::ATTACHABLE; + const LOCK_VISIBLE = 'visible'; + const LOCK_LOCKED = 'locked'; + const LOCK_HIDDEN = 'hidden'; + public function getTableName() { return 'search_editengineconfiguration'; } @@ -97,6 +101,22 @@ final class PhabricatorEditEngineConfiguration } } + $locks = $this->getFieldLocks(); + foreach ($fields as $field) { + $key = $field->getKey(); + switch (idx($locks, $key)) { + case self::LOCK_LOCKED: + $field->setIsLocked(true); + break; + case self::LOCK_HIDDEN: + $field->setIsHidden(true); + break; + case self::LOCK_VISIBLE: + default: + break; + } + } + $fields = $this->reorderFields($fields); $preamble = $this->getPreamble(); @@ -106,6 +126,7 @@ final class PhabricatorEditEngineConfiguration ->setKey('config.preamble') ->setIsReorderable(false) ->setIsDefaultable(false) + ->setIsLockable(false) ->setValue($preamble), ) + $fields; } @@ -116,19 +137,7 @@ final class PhabricatorEditEngineConfiguration private function reorderFields(array $fields) { $keys = $this->getFieldOrder(); $fields = array_select_keys($fields, $keys) + $fields; - - // Now, move locked fields to the bottom. - $head = array(); - $tail = array(); - foreach ($fields as $key => $field) { - if (!$field->getIsLocked()) { - $head[$key] = $field; - } else { - $tail[$key] = $field; - } - } - - return $head + $tail; + return $fields; } public function getURI() { @@ -176,6 +185,14 @@ final class PhabricatorEditEngineConfiguration return $this->getProperty('order', array()); } + public function setFieldLocks(array $field_locks) { + return $this->setProperty('locks', $field_locks); + } + + public function getFieldLocks() { + return $this->getProperty('locks', array()); + } + public function getFieldDefault($key) { $defaults = $this->getProperty('defaults', array()); return idx($defaults, $key); diff --git a/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php b/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php index 6b7784eb3d..b79cb2a648 100644 --- a/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php @@ -7,6 +7,7 @@ final class PhabricatorEditEngineConfigurationTransaction const TYPE_PREAMBLE = 'editengine.config.preamble'; const TYPE_ORDER = 'editengine.config.order'; const TYPE_DEFAULT = 'editengine.config.default'; + const TYPE_LOCKS = 'editengine.config.locks'; public function getApplicationName() { return 'search'; @@ -55,6 +56,10 @@ final class PhabricatorEditEngineConfigurationTransaction '%s changed the default value for field "%s".', $this->renderHandleLink($author_phid), $key); + case self::TYPE_LOCKS: + return pht( + '%s changed locked and hidden fields.', + $this->renderHandleLink($author_phid)); } return parent::getTitle(); diff --git a/webroot/rsrc/css/phui/phui-form-view.css b/webroot/rsrc/css/phui/phui-form-view.css index ae777a8936..8368177cad 100644 --- a/webroot/rsrc/css/phui/phui-form-view.css +++ b/webroot/rsrc/css/phui/phui-form-view.css @@ -527,3 +527,7 @@ properly, and submit values. */ .aphront-form-choose-table .aphront-form-choose-button-cell { padding: 4px 8px; } + +.aphront-form-preview-hidden { + opacity: 0.5; +}