1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 23:02:42 +01:00

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
This commit is contained in:
epriestley 2015-12-08 06:14:47 -08:00
parent 2f8e409876
commit 59ae0d6fff
11 changed files with 309 additions and 5 deletions

View file

@ -428,6 +428,7 @@ return array(
'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 @@ return array(
'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 @@ return array(
'javelin-dom',
'phabricator-keyboard-shortcut',
),
'd7a74243' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'd835b03a' => array(
'javelin-behavior',
'javelin-dom',

View file

@ -2143,6 +2143,7 @@ phutil_register_library_map(array(
'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 @@ phutil_register_library_map(array(
'PhabricatorEditEngineConfigurationReorderController' => 'PhabricatorEditEngineController',
'PhabricatorEditEngineConfigurationSaveController' => 'PhabricatorEditEngineController',
'PhabricatorEditEngineConfigurationSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorEditEngineConfigurationSortController' => 'PhabricatorEditEngineController',
'PhabricatorEditEngineConfigurationTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorEditEngineConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorEditEngineConfigurationViewController' => 'PhabricatorEditEngineController',

View file

@ -24,6 +24,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
private $context;
private $controller;
private $namedQueries;
private $navigationItems = array();
const CONTEXT_LIST = 'list';
const CONTEXT_PANEL = 'panel';
@ -86,6 +87,18 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
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 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
$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;
}

View file

@ -41,6 +41,8 @@ final class PhabricatorTransactionsApplication extends PhabricatorApplication {
'PhabricatorEditEngineConfigurationListController',
$this->getEditRoutePattern('edit/') =>
'PhabricatorEditEngineConfigurationEditController',
'sort/(?P<type>edit|create)/' =>
'PhabricatorEditEngineConfigurationSortController',
'view/(?P<key>[^/]+)/' =>
'PhabricatorEditEngineConfigurationViewController',
'save/(?P<key>[^/]+)/' =>

View file

@ -8,11 +8,44 @@ final class PhabricatorEditEngineConfigurationListController
}
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();
}

View file

@ -0,0 +1,163 @@
<?php
final class PhabricatorEditEngineConfigurationSortController
extends PhabricatorEditEngineController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->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);
}
}

View file

@ -1031,8 +1031,6 @@ abstract class PhabricatorEditEngine
$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 @@ abstract class PhabricatorEditEngine
}
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 @@ abstract class PhabricatorEditEngine
return new Aphront400Response();
}
$config = $this->loadDefaultConfiguration();
$config = $this->loadDefaultEditConfiguration();
if (!$config) {
return new Aphront404Response();
}
$fields = $this->buildEditFields($object);
$is_preview = $request->isPreviewRequest();

View file

@ -26,6 +26,8 @@ final class PhabricatorEditEngineConfigurationEditor
$types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_ISEDIT;
$types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_DISABLE;
$types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER;
$types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER;
return $types;
}
@ -80,6 +82,10 @@ final class PhabricatorEditEngineConfigurationEditor
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 @@ final class PhabricatorEditEngineConfigurationEditor
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 @@ final class PhabricatorEditEngineConfigurationEditor
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 @@ final class PhabricatorEditEngineConfigurationEditor
case PhabricatorEditEngineConfigurationTransaction::TYPE_LOCKS:
case PhabricatorEditEngineConfigurationTransaction::TYPE_DEFAULTCREATE:
case PhabricatorEditEngineConfigurationTransaction::TYPE_DISABLE:
case PhabricatorEditEngineConfigurationTransaction::TYPE_CREATEORDER:
case PhabricatorEditEngineConfigurationTransaction::TYPE_EDITORDER:
return;
}

View file

@ -11,6 +11,8 @@ final class PhabricatorEditEngineConfigurationTransaction
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';

View file

@ -18,6 +18,9 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
private $showPreview = true;
private $objectPHID;
private $headerText;
private $noPermission;
private $currentVersion;
private $versionedDraft;
@ -110,16 +113,32 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
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/'))

View file

@ -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(',');
});
});