mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Allow board columns to be reordered
Summary: Fixes T4567. This isn't going to win design awards and we have some leaky CSS, but it works fine. Test Plan: {F176743} Reviewers: btrahan, chad Reviewed By: chad Subscribers: epriestley Maniphest Tasks: T4567 Differential Revision: https://secure.phabricator.com/D9905
This commit is contained in:
parent
2101c3b689
commit
b7a970598d
6 changed files with 247 additions and 7 deletions
|
@ -413,6 +413,7 @@ return array(
|
|||
'rsrc/js/application/projects/behavior-boards-dropdown.js' => '0ec56e1d',
|
||||
'rsrc/js/application/projects/behavior-project-boards.js' => 'c6b95cbd',
|
||||
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
|
||||
'rsrc/js/application/projects/behavior-reorder-columns.js' => '09eee344',
|
||||
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
|
||||
'rsrc/js/application/releeph/releeph-request-state-change.js' => 'ab836011',
|
||||
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f',
|
||||
|
@ -645,6 +646,7 @@ return array(
|
|||
'javelin-behavior-releeph-request-typeahead' => 'de2e896f',
|
||||
'javelin-behavior-remarkup-preview' => 'f7379f45',
|
||||
'javelin-behavior-reorder-applications' => '76b9fc3e',
|
||||
'javelin-behavior-reorder-columns' => '09eee344',
|
||||
'javelin-behavior-repository-crossreference' => 'f9539603',
|
||||
'javelin-behavior-search-reorder-queries' => 'e9581f08',
|
||||
'javelin-behavior-select-on-click' => '4e3e79a6',
|
||||
|
@ -872,6 +874,14 @@ return array(
|
|||
4 => 'javelin-util',
|
||||
5 => 'phabricator-busy',
|
||||
),
|
||||
'09eee344' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-stratcom',
|
||||
2 => 'javelin-workflow',
|
||||
3 => 'javelin-dom',
|
||||
4 => 'phabricator-draggable-list',
|
||||
),
|
||||
'0a3f3021' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
|
|
|
@ -1971,6 +1971,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php',
|
||||
'PhabricatorProjectBoardDeleteController' => 'applications/project/controller/PhabricatorProjectBoardDeleteController.php',
|
||||
'PhabricatorProjectBoardEditController' => 'applications/project/controller/PhabricatorProjectBoardEditController.php',
|
||||
'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php',
|
||||
'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
|
||||
'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php',
|
||||
'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php',
|
||||
|
@ -4842,6 +4843,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectBoardController' => 'PhabricatorProjectController',
|
||||
'PhabricatorProjectBoardDeleteController' => 'PhabricatorProjectBoardController',
|
||||
'PhabricatorProjectBoardEditController' => 'PhabricatorProjectBoardController',
|
||||
'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
|
||||
'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
|
||||
'PhabricatorProjectColumn' =>
|
||||
array(
|
||||
|
|
|
@ -64,12 +64,16 @@ final class PhabricatorApplicationProject extends PhabricatorApplication {
|
|||
'(?:query/(?P<queryKey>[^/]+)/)?' =>
|
||||
'PhabricatorProjectBoardViewController',
|
||||
'move/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectMoveController',
|
||||
'board/(?P<projectID>[1-9]\d*)/edit/(?:(?P<id>\d+)/)?'
|
||||
=> 'PhabricatorProjectBoardEditController',
|
||||
'board/(?P<projectID>[1-9]\d*)/delete/(?:(?P<id>\d+)/)?'
|
||||
=> 'PhabricatorProjectBoardDeleteController',
|
||||
'board/(?P<projectID>[1-9]\d*)/column/(?:(?P<id>\d+)/)?'
|
||||
=> 'PhabricatorProjectColumnDetailController',
|
||||
'board/(?P<projectID>[1-9]\d*)/' => array(
|
||||
'edit/(?:(?P<id>\d+)/)?'
|
||||
=> 'PhabricatorProjectBoardEditController',
|
||||
'delete/(?:(?P<id>\d+)/)?'
|
||||
=> 'PhabricatorProjectBoardDeleteController',
|
||||
'column/(?:(?P<id>\d+)/)?'
|
||||
=> 'PhabricatorProjectColumnDetailController',
|
||||
'reorder/'
|
||||
=> 'PhabricatorProjectBoardReorderController',
|
||||
),
|
||||
'update/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/'
|
||||
=> 'PhabricatorProjectUpdateController',
|
||||
'history/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectHistoryController',
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorProjectBoardReorderController
|
||||
extends PhabricatorProjectBoardController {
|
||||
|
||||
private $projectID;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->projectID = $data['projectID'];
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$project = id(new PhabricatorProjectQuery())
|
||||
->setViewer($viewer)
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->withIDs(array($this->projectID))
|
||||
->executeOne();
|
||||
if (!$project) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$this->setProject($project);
|
||||
|
||||
|
||||
$project_id = $project->getID();
|
||||
|
||||
$board_uri = $this->getApplicationURI("board/{$project_id}/");
|
||||
$reorder_uri = $this->getApplicationURI("board/{$project_id}/reorder/");
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
// User clicked "Done", make sure the page reloads to show the new
|
||||
// column order.
|
||||
return id(new AphrontRedirectResponse())->setURI($board_uri);
|
||||
}
|
||||
|
||||
$columns = id(new PhabricatorProjectColumnQuery())
|
||||
->setViewer($viewer)
|
||||
->withProjectPHIDs(array($project->getPHID()))
|
||||
->execute();
|
||||
$columns = msort($columns, 'getSequence');
|
||||
|
||||
$column_phid = $request->getStr('columnPHID');
|
||||
if ($column_phid && $request->validateCSRF()) {
|
||||
|
||||
$columns = mpull($columns, null, 'getPHID');
|
||||
if (empty($columns[$column_phid])) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
// TODO: We could let you move the backlog column around if you really
|
||||
// want, but for now we use sequence position 0 as magic.
|
||||
$target_column = $columns[$column_phid];
|
||||
$new_sequence = $request->getInt('sequence');
|
||||
if ($target_column->isDefaultColumn() || $new_sequence < 1) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
// TODO: For now, we're not recording any transactions here. We probably
|
||||
// should, but this sort of edit is extremely trivial.
|
||||
|
||||
// Resequence the columns so that the moved column has the correct
|
||||
// sequence number. Move columns after it up one place in the sequence.
|
||||
$new_map = array();
|
||||
foreach ($columns as $phid => $column) {
|
||||
$value = $column->getSequence();
|
||||
if ($column->getPHID() == $column_phid) {
|
||||
$value = $new_sequence;
|
||||
} else if ($column->getSequence() >= $new_sequence) {
|
||||
$value = $value + 1;
|
||||
}
|
||||
$new_map[$phid] = $value;
|
||||
}
|
||||
|
||||
// Sort the columns into their new ordering.
|
||||
asort($new_map);
|
||||
|
||||
// Now, compact the ordering and adjust any columns that need changes.
|
||||
$project->openTransaction();
|
||||
$sequence = 0;
|
||||
foreach ($new_map as $phid => $ignored) {
|
||||
$new_value = $sequence++;
|
||||
$cur_value = $columns[$phid]->getSequence();
|
||||
if ($new_value != $cur_value) {
|
||||
$columns[$phid]->setSequence($new_value)->save();
|
||||
}
|
||||
}
|
||||
$project->saveTransaction();
|
||||
|
||||
return id(new AphrontAjaxResponse())->setContent(
|
||||
array(
|
||||
'sequenceMap' => mpull($columns, 'getSequence', 'getPHID'),
|
||||
));
|
||||
}
|
||||
|
||||
$list_id = celerity_generate_unique_node_id();
|
||||
|
||||
$static_list = id(new PHUIObjectItemListView())
|
||||
->setUser($viewer)
|
||||
->setFlush(true)
|
||||
->setStackable(true);
|
||||
|
||||
$list = id(new PHUIObjectItemListView())
|
||||
->setUser($viewer)
|
||||
->setID($list_id)
|
||||
->setFlush(true)
|
||||
->setStackable(true);
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setHeader($column->getDisplayName());
|
||||
|
||||
if ($column->isHidden()) {
|
||||
$item->setDisabled(true);
|
||||
}
|
||||
|
||||
if ($column->isDefaultColumn()) {
|
||||
$item->setDisabled(true);
|
||||
$static_list->addItem($item);
|
||||
} else {
|
||||
$item->setGrippable(true);
|
||||
$item->addSigil('board-column');
|
||||
$item->setMetadata(
|
||||
array(
|
||||
'columnPHID' => $column->getPHID(),
|
||||
'columnSequence' => $column->getSequence(),
|
||||
));
|
||||
|
||||
$list->addItem($item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Javelin::initBehavior(
|
||||
'reorder-columns',
|
||||
array(
|
||||
'listID' => $list_id,
|
||||
'reorderURI' => $reorder_uri,
|
||||
));
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Reorder Columns'))
|
||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->appendParagraph(pht('This column can not be moved:'))
|
||||
->appendChild($static_list)
|
||||
->appendParagraph(pht('Drag and drop these columns to reorder them:'))
|
||||
->appendChild($list)
|
||||
->addSubmitButton(pht('Done'));
|
||||
}
|
||||
|
||||
}
|
|
@ -357,7 +357,16 @@ final class PhabricatorProjectBoardViewController
|
|||
$manage_items[] = id(new PhabricatorActionView())
|
||||
->setIcon('fa-plus')
|
||||
->setName(pht('Add Column'))
|
||||
->setHref($this->getApplicationURI('board/'.$this->id.'/edit/'));
|
||||
->setHref($this->getApplicationURI('board/'.$this->id.'/edit/'))
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit);
|
||||
|
||||
$manage_items[] = id(new PhabricatorActionView())
|
||||
->setIcon('fa-exchange')
|
||||
->setName(pht('Reorder Columns'))
|
||||
->setHref($this->getApplicationURI('board/'.$this->id.'/reorder/'))
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true);
|
||||
|
||||
if ($show_hidden) {
|
||||
$hidden_uri = $request->getRequestURI()
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* @provides javelin-behavior-reorder-columns
|
||||
* @requires javelin-behavior
|
||||
* javelin-stratcom
|
||||
* javelin-workflow
|
||||
* javelin-dom
|
||||
* phabricator-draggable-list
|
||||
*/
|
||||
|
||||
JX.behavior('reorder-columns', function(config) {
|
||||
|
||||
var root = JX.$(config.listID);
|
||||
|
||||
var list = new JX.DraggableList('board-column', root)
|
||||
.setFindItemsHandler(function() {
|
||||
return JX.DOM.scry(root, 'li', 'board-column');
|
||||
});
|
||||
|
||||
list.listen('didDrop', function(node) {
|
||||
var nodes = list.findItems();
|
||||
|
||||
var node_data = JX.Stratcom.getData(node);
|
||||
|
||||
// Find the column sequence of the previous node.
|
||||
var sequence = null;
|
||||
var data;
|
||||
for (var ii = 0; ii < nodes.length; ii++) {
|
||||
data = JX.Stratcom.getData(nodes[ii]);
|
||||
if (data.columnPHID === node_data.columnPHID) {
|
||||
break;
|
||||
}
|
||||
sequence = data.columnSequence;
|
||||
}
|
||||
|
||||
list.lock();
|
||||
JX.DOM.alterClass(node, 'drag-sending', true);
|
||||
|
||||
var parameters = {
|
||||
columnPHID: node_data.columnPHID,
|
||||
sequence: (sequence === null) ? 1 : (parseInt(sequence, 10) + 1)
|
||||
};
|
||||
|
||||
new JX.Workflow(config.reorderURI, parameters)
|
||||
.setHandler(function(r) {
|
||||
|
||||
// Adjust metadata for the new sequence numbers.
|
||||
for (var ii = 0; ii < nodes.length; ii++) {
|
||||
var data = JX.Stratcom.getData(nodes[ii]);
|
||||
data.columnSequence = r.sequenceMap[data.columnPHID];
|
||||
}
|
||||
|
||||
list.unlock();
|
||||
JX.DOM.alterClass(node, 'drag-sending', false);
|
||||
})
|
||||
.start();
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in a new issue