@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 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

+247 -7
+10
resources/celerity/map.php
··· 413 413 'rsrc/js/application/projects/behavior-boards-dropdown.js' => '0ec56e1d', 414 414 'rsrc/js/application/projects/behavior-project-boards.js' => 'c6b95cbd', 415 415 'rsrc/js/application/projects/behavior-project-create.js' => '065227cc', 416 + 'rsrc/js/application/projects/behavior-reorder-columns.js' => '09eee344', 416 417 'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf', 417 418 'rsrc/js/application/releeph/releeph-request-state-change.js' => 'ab836011', 418 419 'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f', ··· 645 646 'javelin-behavior-releeph-request-typeahead' => 'de2e896f', 646 647 'javelin-behavior-remarkup-preview' => 'f7379f45', 647 648 'javelin-behavior-reorder-applications' => '76b9fc3e', 649 + 'javelin-behavior-reorder-columns' => '09eee344', 648 650 'javelin-behavior-repository-crossreference' => 'f9539603', 649 651 'javelin-behavior-search-reorder-queries' => 'e9581f08', 650 652 'javelin-behavior-select-on-click' => '4e3e79a6', ··· 871 873 3 => 'javelin-mask', 872 874 4 => 'javelin-util', 873 875 5 => 'phabricator-busy', 876 + ), 877 + '09eee344' => 878 + array( 879 + 0 => 'javelin-behavior', 880 + 1 => 'javelin-stratcom', 881 + 2 => 'javelin-workflow', 882 + 3 => 'javelin-dom', 883 + 4 => 'phabricator-draggable-list', 874 884 ), 875 885 '0a3f3021' => 876 886 array(
+2
src/__phutil_library_map__.php
··· 1971 1971 'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php', 1972 1972 'PhabricatorProjectBoardDeleteController' => 'applications/project/controller/PhabricatorProjectBoardDeleteController.php', 1973 1973 'PhabricatorProjectBoardEditController' => 'applications/project/controller/PhabricatorProjectBoardEditController.php', 1974 + 'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php', 1974 1975 'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php', 1975 1976 'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php', 1976 1977 'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php', ··· 4842 4843 'PhabricatorProjectBoardController' => 'PhabricatorProjectController', 4843 4844 'PhabricatorProjectBoardDeleteController' => 'PhabricatorProjectBoardController', 4844 4845 'PhabricatorProjectBoardEditController' => 'PhabricatorProjectBoardController', 4846 + 'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController', 4845 4847 'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController', 4846 4848 'PhabricatorProjectColumn' => 4847 4849 array(
+10 -6
src/applications/project/application/PhabricatorApplicationProject.php
··· 64 64 '(?:query/(?P<queryKey>[^/]+)/)?' => 65 65 'PhabricatorProjectBoardViewController', 66 66 'move/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectMoveController', 67 - 'board/(?P<projectID>[1-9]\d*)/edit/(?:(?P<id>\d+)/)?' 68 - => 'PhabricatorProjectBoardEditController', 69 - 'board/(?P<projectID>[1-9]\d*)/delete/(?:(?P<id>\d+)/)?' 70 - => 'PhabricatorProjectBoardDeleteController', 71 - 'board/(?P<projectID>[1-9]\d*)/column/(?:(?P<id>\d+)/)?' 72 - => 'PhabricatorProjectColumnDetailController', 67 + 'board/(?P<projectID>[1-9]\d*)/' => array( 68 + 'edit/(?:(?P<id>\d+)/)?' 69 + => 'PhabricatorProjectBoardEditController', 70 + 'delete/(?:(?P<id>\d+)/)?' 71 + => 'PhabricatorProjectBoardDeleteController', 72 + 'column/(?:(?P<id>\d+)/)?' 73 + => 'PhabricatorProjectColumnDetailController', 74 + 'reorder/' 75 + => 'PhabricatorProjectBoardReorderController', 76 + ), 73 77 'update/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/' 74 78 => 'PhabricatorProjectUpdateController', 75 79 'history/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectHistoryController',
+157
src/applications/project/controller/PhabricatorProjectBoardReorderController.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectBoardReorderController 4 + extends PhabricatorProjectBoardController { 5 + 6 + private $projectID; 7 + 8 + public function willProcessRequest(array $data) { 9 + $this->projectID = $data['projectID']; 10 + } 11 + 12 + public function processRequest() { 13 + $request = $this->getRequest(); 14 + $viewer = $request->getUser(); 15 + 16 + $project = id(new PhabricatorProjectQuery()) 17 + ->setViewer($viewer) 18 + ->requireCapabilities( 19 + array( 20 + PhabricatorPolicyCapability::CAN_VIEW, 21 + PhabricatorPolicyCapability::CAN_EDIT, 22 + )) 23 + ->withIDs(array($this->projectID)) 24 + ->executeOne(); 25 + if (!$project) { 26 + return new Aphront404Response(); 27 + } 28 + 29 + $this->setProject($project); 30 + 31 + 32 + $project_id = $project->getID(); 33 + 34 + $board_uri = $this->getApplicationURI("board/{$project_id}/"); 35 + $reorder_uri = $this->getApplicationURI("board/{$project_id}/reorder/"); 36 + 37 + if ($request->isFormPost()) { 38 + // User clicked "Done", make sure the page reloads to show the new 39 + // column order. 40 + return id(new AphrontRedirectResponse())->setURI($board_uri); 41 + } 42 + 43 + $columns = id(new PhabricatorProjectColumnQuery()) 44 + ->setViewer($viewer) 45 + ->withProjectPHIDs(array($project->getPHID())) 46 + ->execute(); 47 + $columns = msort($columns, 'getSequence'); 48 + 49 + $column_phid = $request->getStr('columnPHID'); 50 + if ($column_phid && $request->validateCSRF()) { 51 + 52 + $columns = mpull($columns, null, 'getPHID'); 53 + if (empty($columns[$column_phid])) { 54 + return new Aphront404Response(); 55 + } 56 + 57 + // TODO: We could let you move the backlog column around if you really 58 + // want, but for now we use sequence position 0 as magic. 59 + $target_column = $columns[$column_phid]; 60 + $new_sequence = $request->getInt('sequence'); 61 + if ($target_column->isDefaultColumn() || $new_sequence < 1) { 62 + return new Aphront404Response(); 63 + } 64 + 65 + // TODO: For now, we're not recording any transactions here. We probably 66 + // should, but this sort of edit is extremely trivial. 67 + 68 + // Resequence the columns so that the moved column has the correct 69 + // sequence number. Move columns after it up one place in the sequence. 70 + $new_map = array(); 71 + foreach ($columns as $phid => $column) { 72 + $value = $column->getSequence(); 73 + if ($column->getPHID() == $column_phid) { 74 + $value = $new_sequence; 75 + } else if ($column->getSequence() >= $new_sequence) { 76 + $value = $value + 1; 77 + } 78 + $new_map[$phid] = $value; 79 + } 80 + 81 + // Sort the columns into their new ordering. 82 + asort($new_map); 83 + 84 + // Now, compact the ordering and adjust any columns that need changes. 85 + $project->openTransaction(); 86 + $sequence = 0; 87 + foreach ($new_map as $phid => $ignored) { 88 + $new_value = $sequence++; 89 + $cur_value = $columns[$phid]->getSequence(); 90 + if ($new_value != $cur_value) { 91 + $columns[$phid]->setSequence($new_value)->save(); 92 + } 93 + } 94 + $project->saveTransaction(); 95 + 96 + return id(new AphrontAjaxResponse())->setContent( 97 + array( 98 + 'sequenceMap' => mpull($columns, 'getSequence', 'getPHID'), 99 + )); 100 + } 101 + 102 + $list_id = celerity_generate_unique_node_id(); 103 + 104 + $static_list = id(new PHUIObjectItemListView()) 105 + ->setUser($viewer) 106 + ->setFlush(true) 107 + ->setStackable(true); 108 + 109 + $list = id(new PHUIObjectItemListView()) 110 + ->setUser($viewer) 111 + ->setID($list_id) 112 + ->setFlush(true) 113 + ->setStackable(true); 114 + 115 + foreach ($columns as $column) { 116 + $item = id(new PHUIObjectItemView()) 117 + ->setHeader($column->getDisplayName()); 118 + 119 + if ($column->isHidden()) { 120 + $item->setDisabled(true); 121 + } 122 + 123 + if ($column->isDefaultColumn()) { 124 + $item->setDisabled(true); 125 + $static_list->addItem($item); 126 + } else { 127 + $item->setGrippable(true); 128 + $item->addSigil('board-column'); 129 + $item->setMetadata( 130 + array( 131 + 'columnPHID' => $column->getPHID(), 132 + 'columnSequence' => $column->getSequence(), 133 + )); 134 + 135 + $list->addItem($item); 136 + } 137 + 138 + } 139 + 140 + Javelin::initBehavior( 141 + 'reorder-columns', 142 + array( 143 + 'listID' => $list_id, 144 + 'reorderURI' => $reorder_uri, 145 + )); 146 + 147 + return $this->newDialog() 148 + ->setTitle(pht('Reorder Columns')) 149 + ->setWidth(AphrontDialogView::WIDTH_FORM) 150 + ->appendParagraph(pht('This column can not be moved:')) 151 + ->appendChild($static_list) 152 + ->appendParagraph(pht('Drag and drop these columns to reorder them:')) 153 + ->appendChild($list) 154 + ->addSubmitButton(pht('Done')); 155 + } 156 + 157 + }
+10 -1
src/applications/project/controller/PhabricatorProjectBoardViewController.php
··· 357 357 $manage_items[] = id(new PhabricatorActionView()) 358 358 ->setIcon('fa-plus') 359 359 ->setName(pht('Add Column')) 360 - ->setHref($this->getApplicationURI('board/'.$this->id.'/edit/')); 360 + ->setHref($this->getApplicationURI('board/'.$this->id.'/edit/')) 361 + ->setDisabled(!$can_edit) 362 + ->setWorkflow(!$can_edit); 363 + 364 + $manage_items[] = id(new PhabricatorActionView()) 365 + ->setIcon('fa-exchange') 366 + ->setName(pht('Reorder Columns')) 367 + ->setHref($this->getApplicationURI('board/'.$this->id.'/reorder/')) 368 + ->setDisabled(!$can_edit) 369 + ->setWorkflow(true); 361 370 362 371 if ($show_hidden) { 363 372 $hidden_uri = $request->getRequestURI()
+58
webroot/rsrc/js/application/projects/behavior-reorder-columns.js
··· 1 + /** 2 + * @provides javelin-behavior-reorder-columns 3 + * @requires javelin-behavior 4 + * javelin-stratcom 5 + * javelin-workflow 6 + * javelin-dom 7 + * phabricator-draggable-list 8 + */ 9 + 10 + JX.behavior('reorder-columns', function(config) { 11 + 12 + var root = JX.$(config.listID); 13 + 14 + var list = new JX.DraggableList('board-column', root) 15 + .setFindItemsHandler(function() { 16 + return JX.DOM.scry(root, 'li', 'board-column'); 17 + }); 18 + 19 + list.listen('didDrop', function(node) { 20 + var nodes = list.findItems(); 21 + 22 + var node_data = JX.Stratcom.getData(node); 23 + 24 + // Find the column sequence of the previous node. 25 + var sequence = null; 26 + var data; 27 + for (var ii = 0; ii < nodes.length; ii++) { 28 + data = JX.Stratcom.getData(nodes[ii]); 29 + if (data.columnPHID === node_data.columnPHID) { 30 + break; 31 + } 32 + sequence = data.columnSequence; 33 + } 34 + 35 + list.lock(); 36 + JX.DOM.alterClass(node, 'drag-sending', true); 37 + 38 + var parameters = { 39 + columnPHID: node_data.columnPHID, 40 + sequence: (sequence === null) ? 1 : (parseInt(sequence, 10) + 1) 41 + }; 42 + 43 + new JX.Workflow(config.reorderURI, parameters) 44 + .setHandler(function(r) { 45 + 46 + // Adjust metadata for the new sequence numbers. 47 + for (var ii = 0; ii < nodes.length; ii++) { 48 + var data = JX.Stratcom.getData(nodes[ii]); 49 + data.columnSequence = r.sequenceMap[data.columnPHID]; 50 + } 51 + 52 + list.unlock(); 53 + JX.DOM.alterClass(node, 'drag-sending', false); 54 + }) 55 + .start(); 56 + }); 57 + 58 + });