diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -428,6 +428,7 @@ 'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08', 'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f', 'rsrc/js/application/transactions/behavior-comment-actions.js' => '6de53e91', + 'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243', 'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96', 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'dbbf48b6', 'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => 'b23b49e6', @@ -604,6 +605,7 @@ 'javelin-behavior-doorkeeper-tag' => 'e5822781', 'javelin-behavior-drydock-live-operation-status' => '901935ef', 'javelin-behavior-durable-column' => 'c72aa091', + 'javelin-behavior-editengine-reorder-configs' => 'd7a74243', 'javelin-behavior-editengine-reorder-fields' => 'b59e1e96', 'javelin-behavior-error-log' => '6882e80a', 'javelin-behavior-event-all-day' => '38dcf3c8', @@ -1867,6 +1869,13 @@ 'javelin-dom', 'phabricator-keyboard-shortcut', ), + 'd7a74243' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-workflow', + 'javelin-dom', + 'phabricator-draggable-list', + ), 'd835b03a' => array( 'javelin-behavior', 'javelin-dom', 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 @@ -2143,6 +2143,7 @@ 'PhabricatorEditEngineConfigurationReorderController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationReorderController.php', 'PhabricatorEditEngineConfigurationSaveController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php', 'PhabricatorEditEngineConfigurationSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php', + 'PhabricatorEditEngineConfigurationSortController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationSortController.php', 'PhabricatorEditEngineConfigurationTransaction' => 'applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php', 'PhabricatorEditEngineConfigurationTransactionQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php', 'PhabricatorEditEngineConfigurationViewController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php', @@ -6272,6 +6273,7 @@ 'PhabricatorEditEngineConfigurationReorderController' => 'PhabricatorEditEngineController', 'PhabricatorEditEngineConfigurationSaveController' => 'PhabricatorEditEngineController', 'PhabricatorEditEngineConfigurationSearchEngine' => 'PhabricatorApplicationSearchEngine', + 'PhabricatorEditEngineConfigurationSortController' => 'PhabricatorEditEngineController', 'PhabricatorEditEngineConfigurationTransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorEditEngineConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorEditEngineConfigurationViewController' => 'PhabricatorEditEngineController', diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -24,6 +24,7 @@ private $context; private $controller; private $namedQueries; + private $navigationItems = array(); const CONTEXT_LIST = 'list'; const CONTEXT_PANEL = 'panel'; @@ -86,6 +87,18 @@ return ($this->context == self::CONTEXT_PANEL); } + public function setNavigationItems(array $navigation_items) { + assert_instances_of($navigation_items, 'PHUIListItemView'); + $this->navigationItems = $navigation_items; + return $this; + } + + public function getNavigationItems() { + return $this->navigationItems; + } + + + public function canUseInPanelContext() { return true; } @@ -476,6 +489,10 @@ $advanced_uri = $this->getQueryResultsPageURI('advanced'); $menu->newLink(pht('Advanced Search'), $advanced_uri, 'query/advanced'); + foreach ($this->navigationItems as $extra_item) { + $menu->addMenuItem($extra_item); + } + return $this; } diff --git a/src/applications/transactions/application/PhabricatorTransactionsApplication.php b/src/applications/transactions/application/PhabricatorTransactionsApplication.php --- a/src/applications/transactions/application/PhabricatorTransactionsApplication.php +++ b/src/applications/transactions/application/PhabricatorTransactionsApplication.php @@ -41,6 +41,8 @@ 'PhabricatorEditEngineConfigurationListController', $this->getEditRoutePattern('edit/') => 'PhabricatorEditEngineConfigurationEditController', + 'sort/(?Pedit|create)/' => + 'PhabricatorEditEngineConfigurationSortController', 'view/(?P[^/]+)/' => 'PhabricatorEditEngineConfigurationViewController', 'save/(?P[^/]+)/' => diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php --- a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php @@ -8,11 +8,44 @@ } public function handleRequest(AphrontRequest $request) { - $this->setEngineKey($request->getURIData('engineKey')); + $viewer = $this->getViewer(); + + $engine_key = $request->getURIData('engineKey'); + $this->setEngineKey($engine_key); + + $engine = PhabricatorEditEngine::getByKey($viewer, $engine_key); + + $items = array(); + $items[] = id(new PHUIListItemView()) + ->setType(PHUIListItemView::TYPE_LABEL) + ->setName(pht('Form Order')); + + $sort_create_uri = "/transactions/editengine/{$engine_key}/sort/create/"; + $sort_edit_uri = "/transactions/editengine/{$engine_key}/sort/edit/"; + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $engine, + PhabricatorPolicyCapability::CAN_EDIT); + + $items[] = id(new PHUIListItemView()) + ->setType(PHUIListItemView::TYPE_LINK) + ->setName(pht('Reorder Create Forms')) + ->setHref($sort_create_uri) + ->setWorkflow(true) + ->setDisabled(!$can_edit); + + $items[] = id(new PHUIListItemView()) + ->setType(PHUIListItemView::TYPE_LINK) + ->setName(pht('Reorder Edit Forms')) + ->setHref($sort_edit_uri) + ->setWorkflow(true) + ->setDisabled(!$can_edit); return id(new PhabricatorEditEngineConfigurationSearchEngine()) ->setController($this) ->setEngineKey($this->getEngineKey()) + ->setNavigationItems($items) ->buildResponse(); } diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationSortController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationSortController.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationSortController.php @@ -0,0 +1,163 @@ +getViewer(); + $engine_key = $request->getURIData('engineKey'); + $this->setEngineKey($engine_key); + + $type = $request->getURIData('type'); + $is_create = ($type == 'create'); + + $engine = id(new PhabricatorEditEngineQuery()) + ->setViewer($viewer) + ->withEngineKeys(array($engine_key)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$engine) { + return id(new Aphront404Response()); + } + + $cancel_uri = "/transactions/editengine/{$engine_key}/"; + $reorder_uri = "/transactions/editengine/{$engine_key}/sort/{$type}/"; + + $query = id(new PhabricatorEditEngineConfigurationQuery()) + ->setViewer($viewer) + ->withEngineKeys(array($engine->getEngineKey())); + + if ($is_create) { + $query->withIsDefault(true); + } else { + $query->withIsEdit(true); + } + + $configs = $query->execute(); + + if ($is_create) { + $configs = msort($configs, 'getCreateSortKey'); + } else { + $configs = msort($configs, 'getEditSortKey'); + } + + if ($request->isFormPost()) { + $form_order = $request->getStrList('formOrder'); + + // NOTE: This has a side-effect of saving any factory-default forms + // to the database. We might want to warn the user better, but this + // shouldn't generally be very important or confusing. + + $configs = mpull($configs, null, 'getIdentifier'); + $configs = array_select_keys($configs, $form_order) + $configs; + + $order = 1; + foreach ($configs as $config) { + $xactions = array(); + + if ($is_create) { + $xaction_type = + PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER; + } else { + $xaction_type = + PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER; + } + + $xactions[] = id(new PhabricatorEditEngineConfigurationTransaction()) + ->setTransactionType($xaction_type) + ->setNewValue($order); + + $editor = id(new PhabricatorEditEngineConfigurationEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true); + + $editor->applyTransactions($config, $xactions); + + $order++; + } + + return id(new AphrontRedirectResponse()) + ->setURI($cancel_uri); + } + + $list_id = celerity_generate_unique_node_id(); + $input_id = celerity_generate_unique_node_id(); + + $list = id(new PHUIObjectItemListView()) + ->setUser($viewer) + ->setID($list_id) + ->setFlush(true); + + $form_order = array(); + foreach ($configs as $config) { + $name = $config->getName(); + $identifier = $config->getIdentifier(); + + $item = id(new PHUIObjectItemView()) + ->setHeader($name) + ->setGrippable(true) + ->addSigil('editengine-form-config') + ->setMetadata( + array( + 'formIdentifier' => $identifier, + )); + + $list->addItem($item); + + $form_order[] = $identifier; + } + + Javelin::initBehavior( + 'editengine-reorder-configs', + array( + 'listID' => $list_id, + 'inputID' => $input_id, + 'reorderURI' => $reorder_uri, + )); + + if ($is_create) { + $title = pht('Reorder Create Forms'); + $button = pht('Save Create Order'); + + $note_text = pht( + 'Drag and drop fields to change the order in which they appear in '. + 'the application "Create" menu.'); + } else { + $title = pht('Reorder Edit Forms'); + $button = pht('Save Edit Order'); + + $note_text = pht( + 'Drag and drop fields to change their priority for edits. When a '. + 'user edits an object, they will be shown the first form in this '. + 'list that they have permission to see.'); + } + + $note = id(new PHUIInfoView()) + ->appendChild($note_text) + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE); + + $input = phutil_tag( + 'input', + array( + 'type' => 'hidden', + 'name' => 'formOrder', + 'value' => implode(', ', $form_order), + 'id' => $input_id, + )); + + return $this->newDialog() + ->setTitle($title) + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->appendChild($note) + ->appendChild($list) + ->appendChild($input) + ->addSubmitButton(pht('Save Changes')) + ->addCancelButton($cancel_uri); + } + +} diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -1031,8 +1031,6 @@ $create_uri = $this->getEditURI(null, "form/{$form_key}/"); if (count($configs) > 1) { - $configs = msort($configs, 'getDisplayName'); - $menu_icon = 'fa-caret-square-o-down'; $dropdown = id(new PhabricatorActionListView()) @@ -1068,7 +1066,14 @@ } final public function buildEditEngineCommentView($object) { - $config = $this->loadDefaultConfiguration(); + $config = $this->loadDefaultEditConfiguration(); + + if (!$config) { + // TODO: This just nukes the entire comment form if you don't have access + // to any edit forms. We might want to tailor this UX a bit. + return id(new PhabricatorApplicationTransactionCommentView()) + ->setNoPermission(true); + } $viewer = $this->getViewer(); $object_phid = $object->getPHID(); @@ -1260,7 +1265,11 @@ return new Aphront400Response(); } - $config = $this->loadDefaultConfiguration(); + $config = $this->loadDefaultEditConfiguration(); + if (!$config) { + return new Aphront404Response(); + } + $fields = $this->buildEditFields($object); $is_preview = $request->isPreviewRequest(); diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php --- a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php @@ -26,6 +26,8 @@ $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_ISEDIT; $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_DISABLE; + $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER; + $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER; return $types; } @@ -80,6 +82,10 @@ return (int)$object->getIsEdit(); case PhabricatorEditEngineConfigurationTransaction::TYPE_DISABLE: return (int)$object->getIsDisabled(); + case PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER: + return (int)$object->getCreateOrder(); + case PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER: + return (int)$object->getEditOrder(); } } @@ -97,6 +103,8 @@ case PhabricatorEditEngineConfigurationTransaction::TYPE_DEFAULTCREATE: case PhabricatorEditEngineConfigurationTransaction::TYPE_ISEDIT: case PhabricatorEditEngineConfigurationTransaction::TYPE_DISABLE: + case PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER: + case PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER: return (int)$xaction->getNewValue(); } } @@ -131,6 +139,12 @@ case PhabricatorEditEngineConfigurationTransaction::TYPE_DISABLE: $object->setIsDisabled($xaction->getNewValue()); return; + case PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER: + $object->setCreateOrder($xaction->getNewValue()); + return; + case PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER: + $object->setEditOrder($xaction->getNewValue()); + return; } return parent::applyCustomInternalTransaction($object, $xaction); @@ -149,6 +163,8 @@ case PhabricatorEditEngineConfigurationTransaction::TYPE_LOCKS: case PhabricatorEditEngineConfigurationTransaction::TYPE_DEFAULTCREATE: case PhabricatorEditEngineConfigurationTransaction::TYPE_DISABLE: + case PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER: + case PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER: return; } diff --git a/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php b/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php --- a/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php @@ -11,6 +11,8 @@ const TYPE_DEFAULTCREATE = 'editengine.config.default.create'; const TYPE_ISEDIT = 'editengine.config.isedit'; const TYPE_DISABLE = 'editengine.config.disable'; + const TYPE_CREATEORDER = 'editengine.order.create'; + const TYPE_EDITORDER = 'editengine.order.edit'; public function getApplicationName() { return 'search'; diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php --- a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php @@ -18,6 +18,9 @@ private $showPreview = true; private $objectPHID; private $headerText; + private $noPermission; + + private $currentVersion; private $versionedDraft; @@ -110,16 +113,32 @@ return $this->editTypes; } + public function setNoPermission($no_permission) { + $this->noPermission = $no_permission; + return $this; + } + + public function getNoPermission() { + return $this->noPermission; + } + public function setTransactionTimeline( PhabricatorApplicationTransactionView $timeline) { $timeline->setQuoteTargetID($this->getCommentID()); + if ($this->getNoPermission()) { + $timeline->setShouldTerminate(true); + } $this->transactionTimeline = $timeline; return $this; } public function render() { + if ($this->getNoPermission()) { + return null; + } + $user = $this->getUser(); if (!$user->isLoggedIn()) { $uri = id(new PhutilURI('/login/')) diff --git a/webroot/rsrc/js/application/transactions/behavior-reorder-configs.js b/webroot/rsrc/js/application/transactions/behavior-reorder-configs.js new file mode 100644 --- /dev/null +++ b/webroot/rsrc/js/application/transactions/behavior-reorder-configs.js @@ -0,0 +1,32 @@ +/** + * @provides javelin-behavior-editengine-reorder-configs + * @requires javelin-behavior + * javelin-stratcom + * javelin-workflow + * javelin-dom + * phabricator-draggable-list + */ + +JX.behavior('editengine-reorder-configs', function(config) { + + var root = JX.$(config.listID); + + var list = new JX.DraggableList('editengine-form-config', root) + .setFindItemsHandler(function() { + return JX.DOM.scry(root, 'li', 'editengine-form-config'); + }); + + list.listen('didDrop', function() { + var nodes = list.findItems(); + + var data; + var keys = []; + for (var ii = 0; ii < nodes.length; ii++) { + data = JX.Stratcom.getData(nodes[ii]); + keys.push(data.formIdentifier); + } + + JX.$(config.inputID).value = keys.join(','); + }); + +});