@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Allow EditEngine create and edit forms to be reordered

Summary:
Ref T9132. Ref T9908. Puts reordering UI in place:

- For create forms, this just lets you pick a UI display order other than alphabetical. Seems nice to have.
- For edit forms, this lets you create a hierarchy of advanced-to-basic forms and give them different visibility policies, if you want.

Test Plan:
{F1017842}

- Verified that "Edit Thing" now takes me to the highest-ranked edit form.
- Verified that create menu and quick create menu reflect application order.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9132, T9908

Differential Revision: https://secure.phabricator.com/D14704

+309 -5
+9
resources/celerity/map.php
··· 428 428 'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08', 429 429 'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f', 430 430 'rsrc/js/application/transactions/behavior-comment-actions.js' => '6de53e91', 431 + 'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243', 431 432 'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96', 432 433 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'dbbf48b6', 433 434 'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => 'b23b49e6', ··· 604 605 'javelin-behavior-doorkeeper-tag' => 'e5822781', 605 606 'javelin-behavior-drydock-live-operation-status' => '901935ef', 606 607 'javelin-behavior-durable-column' => 'c72aa091', 608 + 'javelin-behavior-editengine-reorder-configs' => 'd7a74243', 607 609 'javelin-behavior-editengine-reorder-fields' => 'b59e1e96', 608 610 'javelin-behavior-error-log' => '6882e80a', 609 611 'javelin-behavior-event-all-day' => '38dcf3c8', ··· 1866 1868 'javelin-json', 1867 1869 'javelin-dom', 1868 1870 'phabricator-keyboard-shortcut', 1871 + ), 1872 + 'd7a74243' => array( 1873 + 'javelin-behavior', 1874 + 'javelin-stratcom', 1875 + 'javelin-workflow', 1876 + 'javelin-dom', 1877 + 'phabricator-draggable-list', 1869 1878 ), 1870 1879 'd835b03a' => array( 1871 1880 'javelin-behavior',
+2
src/__phutil_library_map__.php
··· 2143 2143 'PhabricatorEditEngineConfigurationReorderController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationReorderController.php', 2144 2144 'PhabricatorEditEngineConfigurationSaveController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php', 2145 2145 'PhabricatorEditEngineConfigurationSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php', 2146 + 'PhabricatorEditEngineConfigurationSortController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationSortController.php', 2146 2147 'PhabricatorEditEngineConfigurationTransaction' => 'applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php', 2147 2148 'PhabricatorEditEngineConfigurationTransactionQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php', 2148 2149 'PhabricatorEditEngineConfigurationViewController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php', ··· 6272 6273 'PhabricatorEditEngineConfigurationReorderController' => 'PhabricatorEditEngineController', 6273 6274 'PhabricatorEditEngineConfigurationSaveController' => 'PhabricatorEditEngineController', 6274 6275 'PhabricatorEditEngineConfigurationSearchEngine' => 'PhabricatorApplicationSearchEngine', 6276 + 'PhabricatorEditEngineConfigurationSortController' => 'PhabricatorEditEngineController', 6275 6277 'PhabricatorEditEngineConfigurationTransaction' => 'PhabricatorApplicationTransaction', 6276 6278 'PhabricatorEditEngineConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 6277 6279 'PhabricatorEditEngineConfigurationViewController' => 'PhabricatorEditEngineController',
+17
src/applications/search/engine/PhabricatorApplicationSearchEngine.php
··· 24 24 private $context; 25 25 private $controller; 26 26 private $namedQueries; 27 + private $navigationItems = array(); 27 28 28 29 const CONTEXT_LIST = 'list'; 29 30 const CONTEXT_PANEL = 'panel'; ··· 85 86 public function isPanelContext() { 86 87 return ($this->context == self::CONTEXT_PANEL); 87 88 } 89 + 90 + public function setNavigationItems(array $navigation_items) { 91 + assert_instances_of($navigation_items, 'PHUIListItemView'); 92 + $this->navigationItems = $navigation_items; 93 + return $this; 94 + } 95 + 96 + public function getNavigationItems() { 97 + return $this->navigationItems; 98 + } 99 + 100 + 88 101 89 102 public function canUseInPanelContext() { 90 103 return true; ··· 475 488 $menu->newLabel(pht('Search')); 476 489 $advanced_uri = $this->getQueryResultsPageURI('advanced'); 477 490 $menu->newLink(pht('Advanced Search'), $advanced_uri, 'query/advanced'); 491 + 492 + foreach ($this->navigationItems as $extra_item) { 493 + $menu->addMenuItem($extra_item); 494 + } 478 495 479 496 return $this; 480 497 }
+2
src/applications/transactions/application/PhabricatorTransactionsApplication.php
··· 41 41 'PhabricatorEditEngineConfigurationListController', 42 42 $this->getEditRoutePattern('edit/') => 43 43 'PhabricatorEditEngineConfigurationEditController', 44 + 'sort/(?P<type>edit|create)/' => 45 + 'PhabricatorEditEngineConfigurationSortController', 44 46 'view/(?P<key>[^/]+)/' => 45 47 'PhabricatorEditEngineConfigurationViewController', 46 48 'save/(?P<key>[^/]+)/' =>
+34 -1
src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php
··· 8 8 } 9 9 10 10 public function handleRequest(AphrontRequest $request) { 11 - $this->setEngineKey($request->getURIData('engineKey')); 11 + $viewer = $this->getViewer(); 12 + 13 + $engine_key = $request->getURIData('engineKey'); 14 + $this->setEngineKey($engine_key); 15 + 16 + $engine = PhabricatorEditEngine::getByKey($viewer, $engine_key); 17 + 18 + $items = array(); 19 + $items[] = id(new PHUIListItemView()) 20 + ->setType(PHUIListItemView::TYPE_LABEL) 21 + ->setName(pht('Form Order')); 22 + 23 + $sort_create_uri = "/transactions/editengine/{$engine_key}/sort/create/"; 24 + $sort_edit_uri = "/transactions/editengine/{$engine_key}/sort/edit/"; 25 + 26 + $can_edit = PhabricatorPolicyFilter::hasCapability( 27 + $viewer, 28 + $engine, 29 + PhabricatorPolicyCapability::CAN_EDIT); 30 + 31 + $items[] = id(new PHUIListItemView()) 32 + ->setType(PHUIListItemView::TYPE_LINK) 33 + ->setName(pht('Reorder Create Forms')) 34 + ->setHref($sort_create_uri) 35 + ->setWorkflow(true) 36 + ->setDisabled(!$can_edit); 37 + 38 + $items[] = id(new PHUIListItemView()) 39 + ->setType(PHUIListItemView::TYPE_LINK) 40 + ->setName(pht('Reorder Edit Forms')) 41 + ->setHref($sort_edit_uri) 42 + ->setWorkflow(true) 43 + ->setDisabled(!$can_edit); 12 44 13 45 return id(new PhabricatorEditEngineConfigurationSearchEngine()) 14 46 ->setController($this) 15 47 ->setEngineKey($this->getEngineKey()) 48 + ->setNavigationItems($items) 16 49 ->buildResponse(); 17 50 } 18 51
+163
src/applications/transactions/controller/PhabricatorEditEngineConfigurationSortController.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfigurationSortController 4 + extends PhabricatorEditEngineController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $this->getViewer(); 8 + $engine_key = $request->getURIData('engineKey'); 9 + $this->setEngineKey($engine_key); 10 + 11 + $type = $request->getURIData('type'); 12 + $is_create = ($type == 'create'); 13 + 14 + $engine = id(new PhabricatorEditEngineQuery()) 15 + ->setViewer($viewer) 16 + ->withEngineKeys(array($engine_key)) 17 + ->requireCapabilities( 18 + array( 19 + PhabricatorPolicyCapability::CAN_VIEW, 20 + PhabricatorPolicyCapability::CAN_EDIT, 21 + )) 22 + ->executeOne(); 23 + if (!$engine) { 24 + return id(new Aphront404Response()); 25 + } 26 + 27 + $cancel_uri = "/transactions/editengine/{$engine_key}/"; 28 + $reorder_uri = "/transactions/editengine/{$engine_key}/sort/{$type}/"; 29 + 30 + $query = id(new PhabricatorEditEngineConfigurationQuery()) 31 + ->setViewer($viewer) 32 + ->withEngineKeys(array($engine->getEngineKey())); 33 + 34 + if ($is_create) { 35 + $query->withIsDefault(true); 36 + } else { 37 + $query->withIsEdit(true); 38 + } 39 + 40 + $configs = $query->execute(); 41 + 42 + if ($is_create) { 43 + $configs = msort($configs, 'getCreateSortKey'); 44 + } else { 45 + $configs = msort($configs, 'getEditSortKey'); 46 + } 47 + 48 + if ($request->isFormPost()) { 49 + $form_order = $request->getStrList('formOrder'); 50 + 51 + // NOTE: This has a side-effect of saving any factory-default forms 52 + // to the database. We might want to warn the user better, but this 53 + // shouldn't generally be very important or confusing. 54 + 55 + $configs = mpull($configs, null, 'getIdentifier'); 56 + $configs = array_select_keys($configs, $form_order) + $configs; 57 + 58 + $order = 1; 59 + foreach ($configs as $config) { 60 + $xactions = array(); 61 + 62 + if ($is_create) { 63 + $xaction_type = 64 + PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER; 65 + } else { 66 + $xaction_type = 67 + PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER; 68 + } 69 + 70 + $xactions[] = id(new PhabricatorEditEngineConfigurationTransaction()) 71 + ->setTransactionType($xaction_type) 72 + ->setNewValue($order); 73 + 74 + $editor = id(new PhabricatorEditEngineConfigurationEditor()) 75 + ->setActor($viewer) 76 + ->setContentSourceFromRequest($request) 77 + ->setContinueOnNoEffect(true); 78 + 79 + $editor->applyTransactions($config, $xactions); 80 + 81 + $order++; 82 + } 83 + 84 + return id(new AphrontRedirectResponse()) 85 + ->setURI($cancel_uri); 86 + } 87 + 88 + $list_id = celerity_generate_unique_node_id(); 89 + $input_id = celerity_generate_unique_node_id(); 90 + 91 + $list = id(new PHUIObjectItemListView()) 92 + ->setUser($viewer) 93 + ->setID($list_id) 94 + ->setFlush(true); 95 + 96 + $form_order = array(); 97 + foreach ($configs as $config) { 98 + $name = $config->getName(); 99 + $identifier = $config->getIdentifier(); 100 + 101 + $item = id(new PHUIObjectItemView()) 102 + ->setHeader($name) 103 + ->setGrippable(true) 104 + ->addSigil('editengine-form-config') 105 + ->setMetadata( 106 + array( 107 + 'formIdentifier' => $identifier, 108 + )); 109 + 110 + $list->addItem($item); 111 + 112 + $form_order[] = $identifier; 113 + } 114 + 115 + Javelin::initBehavior( 116 + 'editengine-reorder-configs', 117 + array( 118 + 'listID' => $list_id, 119 + 'inputID' => $input_id, 120 + 'reorderURI' => $reorder_uri, 121 + )); 122 + 123 + if ($is_create) { 124 + $title = pht('Reorder Create Forms'); 125 + $button = pht('Save Create Order'); 126 + 127 + $note_text = pht( 128 + 'Drag and drop fields to change the order in which they appear in '. 129 + 'the application "Create" menu.'); 130 + } else { 131 + $title = pht('Reorder Edit Forms'); 132 + $button = pht('Save Edit Order'); 133 + 134 + $note_text = pht( 135 + 'Drag and drop fields to change their priority for edits. When a '. 136 + 'user edits an object, they will be shown the first form in this '. 137 + 'list that they have permission to see.'); 138 + } 139 + 140 + $note = id(new PHUIInfoView()) 141 + ->appendChild($note_text) 142 + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE); 143 + 144 + $input = phutil_tag( 145 + 'input', 146 + array( 147 + 'type' => 'hidden', 148 + 'name' => 'formOrder', 149 + 'value' => implode(', ', $form_order), 150 + 'id' => $input_id, 151 + )); 152 + 153 + return $this->newDialog() 154 + ->setTitle($title) 155 + ->setWidth(AphrontDialogView::WIDTH_FORM) 156 + ->appendChild($note) 157 + ->appendChild($list) 158 + ->appendChild($input) 159 + ->addSubmitButton(pht('Save Changes')) 160 + ->addCancelButton($cancel_uri); 161 + } 162 + 163 + }
+13 -4
src/applications/transactions/editengine/PhabricatorEditEngine.php
··· 1031 1031 $create_uri = $this->getEditURI(null, "form/{$form_key}/"); 1032 1032 1033 1033 if (count($configs) > 1) { 1034 - $configs = msort($configs, 'getDisplayName'); 1035 - 1036 1034 $menu_icon = 'fa-caret-square-o-down'; 1037 1035 1038 1036 $dropdown = id(new PhabricatorActionListView()) ··· 1068 1066 } 1069 1067 1070 1068 final public function buildEditEngineCommentView($object) { 1071 - $config = $this->loadDefaultConfiguration(); 1069 + $config = $this->loadDefaultEditConfiguration(); 1070 + 1071 + if (!$config) { 1072 + // TODO: This just nukes the entire comment form if you don't have access 1073 + // to any edit forms. We might want to tailor this UX a bit. 1074 + return id(new PhabricatorApplicationTransactionCommentView()) 1075 + ->setNoPermission(true); 1076 + } 1072 1077 1073 1078 $viewer = $this->getViewer(); 1074 1079 $object_phid = $object->getPHID(); ··· 1260 1265 return new Aphront400Response(); 1261 1266 } 1262 1267 1263 - $config = $this->loadDefaultConfiguration(); 1268 + $config = $this->loadDefaultEditConfiguration(); 1269 + if (!$config) { 1270 + return new Aphront404Response(); 1271 + } 1272 + 1264 1273 $fields = $this->buildEditFields($object); 1265 1274 1266 1275 $is_preview = $request->isPreviewRequest();
+16
src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php
··· 26 26 $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_ISEDIT; 27 27 $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_DISABLE; 28 28 29 + $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER; 30 + $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER; 29 31 30 32 return $types; 31 33 } ··· 80 82 return (int)$object->getIsEdit(); 81 83 case PhabricatorEditEngineConfigurationTransaction::TYPE_DISABLE: 82 84 return (int)$object->getIsDisabled(); 85 + case PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER: 86 + return (int)$object->getCreateOrder(); 87 + case PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER: 88 + return (int)$object->getEditOrder(); 83 89 } 84 90 } 85 91 ··· 97 103 case PhabricatorEditEngineConfigurationTransaction::TYPE_DEFAULTCREATE: 98 104 case PhabricatorEditEngineConfigurationTransaction::TYPE_ISEDIT: 99 105 case PhabricatorEditEngineConfigurationTransaction::TYPE_DISABLE: 106 + case PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER: 107 + case PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER: 100 108 return (int)$xaction->getNewValue(); 101 109 } 102 110 } ··· 131 139 case PhabricatorEditEngineConfigurationTransaction::TYPE_DISABLE: 132 140 $object->setIsDisabled($xaction->getNewValue()); 133 141 return; 142 + case PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER: 143 + $object->setCreateOrder($xaction->getNewValue()); 144 + return; 145 + case PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER: 146 + $object->setEditOrder($xaction->getNewValue()); 147 + return; 134 148 } 135 149 136 150 return parent::applyCustomInternalTransaction($object, $xaction); ··· 149 163 case PhabricatorEditEngineConfigurationTransaction::TYPE_LOCKS: 150 164 case PhabricatorEditEngineConfigurationTransaction::TYPE_DEFAULTCREATE: 151 165 case PhabricatorEditEngineConfigurationTransaction::TYPE_DISABLE: 166 + case PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER: 167 + case PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER: 152 168 return; 153 169 } 154 170
+2
src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php
··· 11 11 const TYPE_DEFAULTCREATE = 'editengine.config.default.create'; 12 12 const TYPE_ISEDIT = 'editengine.config.isedit'; 13 13 const TYPE_DISABLE = 'editengine.config.disable'; 14 + const TYPE_CREATEORDER = 'editengine.order.create'; 15 + const TYPE_EDITORDER = 'editengine.order.edit'; 14 16 15 17 public function getApplicationName() { 16 18 return 'search';
+19
src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
··· 18 18 private $showPreview = true; 19 19 private $objectPHID; 20 20 private $headerText; 21 + private $noPermission; 22 + 23 + 21 24 22 25 private $currentVersion; 23 26 private $versionedDraft; ··· 110 113 return $this->editTypes; 111 114 } 112 115 116 + public function setNoPermission($no_permission) { 117 + $this->noPermission = $no_permission; 118 + return $this; 119 + } 120 + 121 + public function getNoPermission() { 122 + return $this->noPermission; 123 + } 124 + 113 125 public function setTransactionTimeline( 114 126 PhabricatorApplicationTransactionView $timeline) { 115 127 116 128 $timeline->setQuoteTargetID($this->getCommentID()); 129 + if ($this->getNoPermission()) { 130 + $timeline->setShouldTerminate(true); 131 + } 117 132 118 133 $this->transactionTimeline = $timeline; 119 134 return $this; 120 135 } 121 136 122 137 public function render() { 138 + if ($this->getNoPermission()) { 139 + return null; 140 + } 141 + 123 142 $user = $this->getUser(); 124 143 if (!$user->isLoggedIn()) { 125 144 $uri = id(new PhutilURI('/login/'))
+32
webroot/rsrc/js/application/transactions/behavior-reorder-configs.js
··· 1 + /** 2 + * @provides javelin-behavior-editengine-reorder-configs 3 + * @requires javelin-behavior 4 + * javelin-stratcom 5 + * javelin-workflow 6 + * javelin-dom 7 + * phabricator-draggable-list 8 + */ 9 + 10 + JX.behavior('editengine-reorder-configs', function(config) { 11 + 12 + var root = JX.$(config.listID); 13 + 14 + var list = new JX.DraggableList('editengine-form-config', root) 15 + .setFindItemsHandler(function() { 16 + return JX.DOM.scry(root, 'li', 'editengine-form-config'); 17 + }); 18 + 19 + list.listen('didDrop', function() { 20 + var nodes = list.findItems(); 21 + 22 + var data; 23 + var keys = []; 24 + for (var ii = 0; ii < nodes.length; ii++) { 25 + data = JX.Stratcom.getData(nodes[ii]); 26 + keys.push(data.formIdentifier); 27 + } 28 + 29 + JX.$(config.inputID).value = keys.join(','); 30 + }); 31 + 32 + });