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

Allow profile menu items to be reordered

Summary: Ref T10054. Allows users to drag menu items to reorder them.

Test Plan: Reordered a project menu.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10054

Differential Revision: https://secure.phabricator.com/D15011
This commit is contained in:
epriestley 2016-01-13 09:40:27 -08:00
parent f24318f308
commit 1c5167dc74
7 changed files with 173 additions and 1 deletions

View file

@ -424,6 +424,7 @@ return array(
'rsrc/js/application/releeph/releeph-request-state-change.js' => 'a0b57eb8', 'rsrc/js/application/releeph/releeph-request-state-change.js' => 'a0b57eb8',
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f', 'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f',
'rsrc/js/application/repository/repository-crossreference.js' => 'e5339c43', 'rsrc/js/application/repository/repository-crossreference.js' => 'e5339c43',
'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072',
'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08', 'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f', 'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f',
'rsrc/js/application/transactions/behavior-comment-actions.js' => '1f2fcaf8', 'rsrc/js/application/transactions/behavior-comment-actions.js' => '1f2fcaf8',
@ -663,6 +664,7 @@ return array(
'javelin-behavior-remarkup-preview' => '4b700e9e', 'javelin-behavior-remarkup-preview' => '4b700e9e',
'javelin-behavior-reorder-applications' => '76b9fc3e', 'javelin-behavior-reorder-applications' => '76b9fc3e',
'javelin-behavior-reorder-columns' => 'e1d25dfb', 'javelin-behavior-reorder-columns' => 'e1d25dfb',
'javelin-behavior-reorder-profile-menu-items' => 'e2e0a072',
'javelin-behavior-repository-crossreference' => 'e5339c43', 'javelin-behavior-repository-crossreference' => 'e5339c43',
'javelin-behavior-scrollbar' => '834a1173', 'javelin-behavior-scrollbar' => '834a1173',
'javelin-behavior-search-reorder-queries' => 'e9581f08', 'javelin-behavior-search-reorder-queries' => 'e9581f08',
@ -1933,6 +1935,13 @@ return array(
'e292eaf4' => array( 'e292eaf4' => array(
'javelin-install', 'javelin-install',
), ),
'e2e0a072' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'e379b58e' => array( 'e379b58e' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',

View file

@ -642,6 +642,7 @@ abstract class PhabricatorApplication
'(?P<panelAction>view)/(?P<panelID>[^/]+)/' => $controller, '(?P<panelAction>view)/(?P<panelID>[^/]+)/' => $controller,
'(?P<panelAction>hide)/(?P<panelID>[^/]+)/' => $controller, '(?P<panelAction>hide)/(?P<panelID>[^/]+)/' => $controller,
'(?P<panelAction>configure)/' => $controller, '(?P<panelAction>configure)/' => $controller,
'(?P<panelAction>reorder)/' => $controller,
'(?P<panelAction>edit)/'.$edit_route => $controller, '(?P<panelAction>edit)/'.$edit_route => $controller,
'(?P<panelAction>new)/(?<panelKey>[^/]+)/'.$edit_route => $controller, '(?P<panelAction>new)/(?<panelKey>[^/]+)/'.$edit_route => $controller,
'(?P<panelAction>builtin)/(?<panelID>[^/]+)/'.$edit_route '(?P<panelAction>builtin)/(?<panelID>[^/]+)/'.$edit_route

View file

@ -15,6 +15,7 @@ final class PhabricatorProfilePanelEditor
$types = parent::getTransactionTypes(); $types = parent::getTransactionTypes();
$types[] = PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY; $types[] = PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY;
$types[] = PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER;
return $types; return $types;
} }
@ -27,6 +28,8 @@ final class PhabricatorProfilePanelEditor
case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY: case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY:
$key = $xaction->getMetadataValue('property.key'); $key = $xaction->getMetadataValue('property.key');
return $object->getPanelProperty($key, null); return $object->getPanelProperty($key, null);
case PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER:
return $object->getPanelOrder();
} }
} }
@ -37,6 +40,8 @@ final class PhabricatorProfilePanelEditor
switch ($xaction->getTransactionType()) { switch ($xaction->getTransactionType()) {
case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY: case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY:
return $xaction->getNewValue(); return $xaction->getNewValue();
case PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER:
return (int)$xaction->getNewValue();
} }
} }
@ -50,6 +55,9 @@ final class PhabricatorProfilePanelEditor
$value = $xaction->getNewValue(); $value = $xaction->getNewValue();
$object->setPanelProperty($key, $value); $object->setPanelProperty($key, $value);
return; return;
case PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER:
$object->setPanelOrder($xaction->getNewValue());
return;
} }
return parent::applyCustomInternalTransaction($object, $xaction); return parent::applyCustomInternalTransaction($object, $xaction);
@ -61,6 +69,7 @@ final class PhabricatorProfilePanelEditor
switch ($xaction->getTransactionType()) { switch ($xaction->getTransactionType()) {
case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY: case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY:
case PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER:
return; return;
} }

View file

@ -91,6 +91,9 @@ final class PhabricatorProfilePanelEngine extends Phobject {
$content = $this->buildPanelConfigureContent($panel_list); $content = $this->buildPanelConfigureContent($panel_list);
$crumbs->addTextCrumb(pht('Configure Menu')); $crumbs->addTextCrumb(pht('Configure Menu'));
break; break;
case 'reorder':
$content = $this->buildPanelReorderContent($panel_list);
break;
case 'new': case 'new':
$panel_key = $request->getURIData('panelKey'); $panel_key = $request->getURIData('panelKey');
$content = $this->buildPanelNewContent($panel_key); $content = $this->buildPanelNewContent($panel_key);
@ -204,6 +207,8 @@ final class PhabricatorProfilePanelEngine extends Phobject {
$impl->setViewer($viewer); $impl->setViewer($viewer);
} }
$panels = msort($panels, 'getSortKey');
// Normalize keys since callers shouldn't rely on this array being // Normalize keys since callers shouldn't rely on this array being
// partially keyed. // partially keyed.
$panels = array_values($panels); $panels = array_values($panels);
@ -305,6 +310,79 @@ final class PhabricatorProfilePanelEngine extends Phobject {
return "/project/{$id}/panel/{$path}"; return "/project/{$id}/panel/{$path}";
} }
private function buildPanelReorderContent(array $panels) {
$viewer = $this->getViewer();
$object = $this->getProfileObject();
PhabricatorPolicyFilter::requireCapability(
$viewer,
$object,
PhabricatorPolicyCapability::CAN_EDIT);
$controller = $this->getController();
$request = $controller->getRequest();
$request->validateCSRF();
$order = $request->getStrList('order');
$by_builtin = array();
$by_id = array();
foreach ($panels as $key => $panel) {
$id = $panel->getID();
if ($id) {
$by_id[$id] = $key;
continue;
}
$builtin_key = $panel->getBuiltinKey();
if ($builtin_key) {
$by_builtin[$builtin_key] = $key;
continue;
}
}
$key_order = array();
foreach ($order as $order_item) {
if (isset($by_id[$order_item])) {
$key_order[] = $by_id[$order_item];
continue;
}
if (isset($by_builtin[$order_item])) {
$key_order[] = $by_builtin[$order_item];
continue;
}
}
$panels = array_select_keys($panels, $key_order) + $panels;
$type_order =
PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER;
$order = 1;
foreach ($panels as $panel) {
$xactions = array();
$xactions[] = id(new PhabricatorProfilePanelConfigurationTransaction())
->setTransactionType($type_order)
->setNewValue($order);
$editor = id(new PhabricatorProfilePanelEditor())
->setContentSourceFromRequest($request)
->setActor($viewer)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->applyTransactions($panel, $xactions);
$order++;
}
return id(new AphrontRedirectResponse())
->setURI($this->getConfigureURI());
}
private function buildPanelConfigureContent(array $panels) { private function buildPanelConfigureContent(array $panels) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$object = $this->getProfileObject(); $object = $this->getProfileObject();
@ -314,7 +392,18 @@ final class PhabricatorProfilePanelEngine extends Phobject {
$object, $object,
PhabricatorPolicyCapability::CAN_EDIT); PhabricatorPolicyCapability::CAN_EDIT);
$list = new PHUIObjectItemListView(); $list_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'reorder-profile-menu-items',
array(
'listID' => $list_id,
'orderURI' => $this->getPanelURI('reorder/'),
));
$list = id(new PHUIObjectItemListView())
->setID($list_id);
foreach ($panels as $panel) { foreach ($panels as $panel) {
$id = $panel->getID(); $id = $panel->getID();
$builtin_key = $panel->getBuiltinKey(); $builtin_key = $panel->getBuiltinKey();
@ -336,6 +425,14 @@ final class PhabricatorProfilePanelEngine extends Phobject {
$item->addAttribute($type); $item->addAttribute($type);
if ($can_edit) { if ($can_edit) {
$item
->setGrippable(true)
->addSigil('profile-menu-item')
->setMetadata(
array(
'key' => nonempty($id, $builtin_key),
));
if ($id) { if ($id) {
$item->setHref($this->getPanelURI("edit/{$id}/")); $item->setHref($this->getPanelURI("edit/{$id}/"));
} else { } else {

View file

@ -101,6 +101,20 @@ final class PhabricatorProfilePanelConfiguration
return $this->getPanel()->getDisplayName($this); return $this->getPanel()->getDisplayName($this);
} }
public function getSortKey() {
$order = $this->getPanelOrder();
if ($order === null) {
$order = 'Z';
} else {
$order = sprintf('%020d', $order);
}
return sprintf(
'~%s%020d',
$order,
$this->getID());
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */ /* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -4,6 +4,7 @@ final class PhabricatorProfilePanelConfigurationTransaction
extends PhabricatorApplicationTransaction { extends PhabricatorApplicationTransaction {
const TYPE_PROPERTY = 'profilepanel.property'; const TYPE_PROPERTY = 'profilepanel.property';
const TYPE_ORDER = 'profilepanel.order';
public function getApplicationName() { public function getApplicationName() {
return 'search'; return 'search';

View file

@ -0,0 +1,41 @@
/**
* @provides javelin-behavior-reorder-profile-menu-items
* @requires javelin-behavior
* javelin-stratcom
* javelin-workflow
* javelin-dom
* phabricator-draggable-list
*/
JX.behavior('reorder-profile-menu-items', function(config) {
var root = JX.$(config.listID);
var list = new JX.DraggableList('profile-menu-item', root)
.setFindItemsHandler(function() {
return JX.DOM.scry(root, 'li', 'profile-menu-item');
});
list.listen('didDrop', function(node) {
var nodes = list.findItems();
var order = [];
var key;
for (var ii = 0; ii < nodes.length; ii++) {
key = JX.Stratcom.getData(nodes[ii]).key;
if (key) {
order.push(key);
}
}
list.lock();
JX.DOM.alterClass(node, 'drag-sending', true);
new JX.Workflow(config.orderURI, {order: order.join()})
.setHandler(function() {
JX.DOM.alterClass(node, 'drag-sending', false);
list.unlock();
})
.start();
});
});