Page MenuHomePhabricator

D9905.id23779.diff
No OneTemporary

D9905.id23779.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -413,6 +413,7 @@
'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 @@
'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 @@
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',
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
@@ -1967,6 +1967,7 @@
'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',
@@ -4833,6 +4834,7 @@
'PhabricatorProjectBoardController' => 'PhabricatorProjectController',
'PhabricatorProjectBoardDeleteController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardEditController' => 'PhabricatorProjectBoardController',
+ 'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectColumn' =>
array(
diff --git a/src/applications/project/application/PhabricatorApplicationProject.php b/src/applications/project/application/PhabricatorApplicationProject.php
--- a/src/applications/project/application/PhabricatorApplicationProject.php
+++ b/src/applications/project/application/PhabricatorApplicationProject.php
@@ -64,12 +64,16 @@
'(?: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',
diff --git a/src/applications/project/controller/PhabricatorProjectBoardReorderController.php b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php
@@ -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'));
+ }
+
+}
diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
--- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
@@ -357,7 +357,16 @@
$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()
diff --git a/webroot/rsrc/js/application/projects/behavior-reorder-columns.js b/webroot/rsrc/js/application/projects/behavior-reorder-columns.js
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/js/application/projects/behavior-reorder-columns.js
@@ -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();
+ });
+
+});

File Metadata

Mime Type
text/plain
Expires
Sun, Mar 16, 8:10 AM (1 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7381790
Default Alt Text
D9905.id23779.diff (12 KB)

Event Timeline