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

Move workboard "Bulk Edit Tasks" workflow to a separate controller

Summary: Depends on D20633. Ref T4900. Separate the "Bulk Edit Tasks..." flow out of the main workboard controller.

Test Plan:
- Used "Bulk Edit Tasks" on a column with some tasks, got an appropraite edit operation.
- Used "Bulk Edit Tasks" on an empty column, got an error.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T4900

Differential Revision: https://secure.phabricator.com/D20634

+166 -105
+2
src/__phutil_library_map__.php
··· 4172 4172 'PhabricatorProjectColorsConfigType' => 'applications/project/config/PhabricatorProjectColorsConfigType.php', 4173 4173 'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php', 4174 4174 'PhabricatorProjectColumnAuthorOrder' => 'applications/project/order/PhabricatorProjectColumnAuthorOrder.php', 4175 + 'PhabricatorProjectColumnBulkEditController' => 'applications/project/controller/PhabricatorProjectColumnBulkEditController.php', 4175 4176 'PhabricatorProjectColumnCreatedOrder' => 'applications/project/order/PhabricatorProjectColumnCreatedOrder.php', 4176 4177 'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php', 4177 4178 'PhabricatorProjectColumnEditController' => 'applications/project/controller/PhabricatorProjectColumnEditController.php', ··· 10445 10446 'PhabricatorConduitResultInterface', 10446 10447 ), 10447 10448 'PhabricatorProjectColumnAuthorOrder' => 'PhabricatorProjectColumnOrder', 10449 + 'PhabricatorProjectColumnBulkEditController' => 'PhabricatorProjectBoardController', 10448 10450 'PhabricatorProjectColumnCreatedOrder' => 'PhabricatorProjectColumnOrder', 10449 10451 'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController', 10450 10452 'PhabricatorProjectColumnEditController' => 'PhabricatorProjectBoardController',
+2
src/applications/project/application/PhabricatorProjectApplication.php
··· 81 81 => 'PhabricatorProjectColumnDetailController', 82 82 'viewquery/(?P<columnID>\d+)/' 83 83 => 'PhabricatorProjectColumnViewQueryController', 84 + 'bulk/(?P<columnID>\d+)/' 85 + => 'PhabricatorProjectColumnBulkEditController', 84 86 'import/' 85 87 => 'PhabricatorProjectBoardImportController', 86 88 'reorder/'
+14 -105
src/applications/project/controller/PhabricatorProjectBoardViewController.php
··· 3 3 final class PhabricatorProjectBoardViewController 4 4 extends PhabricatorProjectBoardController { 5 5 6 - const BATCH_EDIT_ALL = 'all'; 7 - 8 6 public function shouldAllowPublic() { 9 7 return true; 10 8 } ··· 34 32 $custom_query = null; 35 33 } 36 34 37 - $task_query = $search_engine->buildQueryFromSavedQuery($saved); 38 - 39 - $select_phids = array($project->getPHID()); 40 - if ($project->getHasSubprojects() || $project->getHasMilestones()) { 41 - $descendants = id(new PhabricatorProjectQuery()) 42 - ->setViewer($viewer) 43 - ->withAncestorProjectPHIDs($select_phids) 44 - ->execute(); 45 - foreach ($descendants as $descendant) { 46 - $select_phids[] = $descendant->getPHID(); 47 - } 48 - } 49 - 50 - $tasks = $task_query 51 - ->withEdgeLogicPHIDs( 52 - PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, 53 - PhabricatorQueryConstraint::OPERATOR_ANCESTOR, 54 - array($select_phids)) 55 - ->setOrder(ManiphestTaskQuery::ORDER_PRIORITY) 56 - ->setViewer($viewer) 57 - ->execute(); 58 - $tasks = mpull($tasks, null, 'getPHID'); 35 + $layout_engine = $state->getLayoutEngine(); 59 36 60 37 $board_phid = $project->getPHID(); 61 - 62 - // Regardless of display order, pass tasks to the layout engine in ID order 63 - // so layout is consistent. 64 - $board_tasks = msort($tasks, 'getID'); 65 - 66 - $layout_engine = id(new PhabricatorBoardLayoutEngine()) 67 - ->setViewer($viewer) 68 - ->setBoardPHIDs(array($board_phid)) 69 - ->setObjectPHIDs(array_keys($board_tasks)) 70 - ->setFetchAllBoards(true) 71 - ->executeLayout(); 72 - 73 38 $columns = $layout_engine->getColumns($board_phid); 74 39 if (!$columns || !$project->getHasWorkboard()) { 75 40 $has_normal_columns = false; ··· 122 87 ->appendChild($content); 123 88 } 124 89 90 + $tasks = $state->getObjects(); 91 + 125 92 $task_can_edit_map = id(new PhabricatorPolicyFilter()) 126 93 ->setViewer($viewer) 127 94 ->requireCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT)) 128 95 ->apply($tasks); 129 96 130 - // If this is a batch edit, select the editable tasks in the chosen column 131 - // and ship the user into the batch editor. 132 - $batch_edit = $request->getStr('batch'); 133 - if ($batch_edit) { 134 - if ($batch_edit !== self::BATCH_EDIT_ALL) { 135 - $column_id_map = mpull($columns, null, 'getID'); 136 - $batch_column = idx($column_id_map, $batch_edit); 137 - if (!$batch_column) { 138 - return new Aphront404Response(); 139 - } 140 - 141 - $batch_task_phids = $layout_engine->getColumnObjectPHIDs( 142 - $board_phid, 143 - $batch_column->getPHID()); 144 - 145 - foreach ($batch_task_phids as $key => $batch_task_phid) { 146 - if (empty($task_can_edit_map[$batch_task_phid])) { 147 - unset($batch_task_phids[$key]); 148 - } 149 - } 150 - 151 - $batch_tasks = array_select_keys($tasks, $batch_task_phids); 152 - } else { 153 - $batch_tasks = $task_can_edit_map; 154 - } 155 - 156 - if (!$batch_tasks) { 157 - $cancel_uri = $state->newWorkboardURI(); 158 - return $this->newDialog() 159 - ->setTitle(pht('No Editable Tasks')) 160 - ->appendParagraph( 161 - pht( 162 - 'The selected column contains no visible tasks which you '. 163 - 'have permission to edit.')) 164 - ->addCancelButton($board_uri); 165 - } 166 - 167 - // Create a saved query to hold the working set. This allows us to get 168 - // around URI length limitations with a long "?ids=..." query string. 169 - // For details, see T10268. 170 - $search_engine = id(new ManiphestTaskSearchEngine()) 171 - ->setViewer($viewer); 172 - 173 - $saved_query = $search_engine->newSavedQuery(); 174 - $saved_query->setParameter('ids', mpull($batch_tasks, 'getID')); 175 - $search_engine->saveQuery($saved_query); 176 - 177 - $query_key = $saved_query->getQueryKey(); 178 - 179 - $bulk_uri = new PhutilURI("/maniphest/bulk/query/{$query_key}/"); 180 - $bulk_uri->replaceQueryParam('board', $project->getID()); 181 - 182 - return id(new AphrontRedirectResponse()) 183 - ->setURI($bulk_uri); 184 - } 185 - 186 97 $move_id = $request->getStr('move'); 187 98 if (strlen($move_id)) { 188 99 $column_id_map = mpull($columns, null, 'getID'); ··· 426 337 } 427 338 } 428 339 340 + $container_phids = $state->getBoardContainerPHIDs(); 341 + 429 342 $rendering_engine = id(new PhabricatorBoardRenderingEngine()) 430 343 ->setViewer($viewer) 431 344 ->setObjects(array_select_keys($tasks, $visible_phids)) 432 345 ->setEditMap($task_can_edit_map) 433 - ->setExcludedProjectPHIDs($select_phids); 346 + ->setExcludedProjectPHIDs($container_phids); 434 347 435 348 $templates = array(); 436 349 $all_tasks = array(); ··· 912 825 ->setName(pht('Manage Workboard')) 913 826 ->setHref($manage_uri); 914 827 915 - $batch_edit_uri = $request->getRequestURI(); 916 - $batch_edit_uri->replaceQueryParam('batch', self::BATCH_EDIT_ALL); 917 - $can_batch_edit = PhabricatorPolicyFilter::hasCapability( 918 - $viewer, 919 - PhabricatorApplication::getByClass('PhabricatorManiphestApplication'), 920 - ManiphestBulkEditCapability::CAPABILITY); 921 - 922 828 $manage_menu = id(new PhabricatorActionListView()) 923 829 ->setUser($viewer); 924 830 foreach ($manage_items as $item) { ··· 1002 908 $column_items[] = id(new PhabricatorActionView()) 1003 909 ->setType(PhabricatorActionView::TYPE_DIVIDER); 1004 910 1005 - $batch_edit_uri = $request->getRequestURI(); 1006 - $batch_edit_uri->replaceQueryParam('batch', $column->getID()); 1007 - $can_batch_edit = PhabricatorPolicyFilter::hasCapability( 911 + $bulk_edit_uri = $state->newWorkboardURI( 912 + urisprintf( 913 + 'bulk/%d/', 914 + $column->getID())); 915 + 916 + $can_bulk_edit = PhabricatorPolicyFilter::hasCapability( 1008 917 $viewer, 1009 918 PhabricatorApplication::getByClass('PhabricatorManiphestApplication'), 1010 919 ManiphestBulkEditCapability::CAPABILITY); ··· 1012 921 $column_items[] = id(new PhabricatorActionView()) 1013 922 ->setIcon('fa-list-ul') 1014 923 ->setName(pht('Bulk Edit Tasks...')) 1015 - ->setHref($batch_edit_uri) 1016 - ->setDisabled(!$can_batch_edit); 924 + ->setHref($bulk_edit_uri) 925 + ->setDisabled(!$can_bulk_edit); 1017 926 1018 927 $batch_move_uri = $request->getRequestURI(); 1019 928 $batch_move_uri->replaceQueryParam('move', $column->getID());
+72
src/applications/project/controller/PhabricatorProjectColumnBulkEditController.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectColumnBulkEditController 4 + extends PhabricatorProjectBoardController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $request->getViewer(); 8 + 9 + $response = $this->loadProject(); 10 + if ($response) { 11 + return $response; 12 + } 13 + 14 + $project = $this->getProject(); 15 + $state = $this->getViewState(); 16 + $board_uri = $state->newWorkboardURI(); 17 + 18 + $layout_engine = $state->getLayoutEngine(); 19 + 20 + $board_phid = $project->getPHID(); 21 + $columns = $layout_engine->getColumns($board_phid); 22 + $columns = mpull($columns, null, 'getID'); 23 + 24 + $column_id = $request->getURIData('columnID'); 25 + $bulk_column = idx($columns, $column_id); 26 + if (!$bulk_column) { 27 + return new Aphront404Response(); 28 + } 29 + 30 + $bulk_task_phids = $layout_engine->getColumnObjectPHIDs( 31 + $board_phid, 32 + $bulk_column->getPHID()); 33 + 34 + $tasks = $state->getObjects(); 35 + 36 + $bulk_tasks = array_select_keys($tasks, $bulk_task_phids); 37 + 38 + $bulk_tasks = id(new PhabricatorPolicyFilter()) 39 + ->setViewer($viewer) 40 + ->requireCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT)) 41 + ->apply($bulk_tasks); 42 + 43 + if (!$bulk_tasks) { 44 + return $this->newDialog() 45 + ->setTitle(pht('No Editable Tasks')) 46 + ->appendParagraph( 47 + pht( 48 + 'The selected column contains no visible tasks which you '. 49 + 'have permission to edit.')) 50 + ->addCancelButton($board_uri); 51 + } 52 + 53 + // Create a saved query to hold the working set. This allows us to get 54 + // around URI length limitations with a long "?ids=..." query string. 55 + // For details, see T10268. 56 + $search_engine = id(new ManiphestTaskSearchEngine()) 57 + ->setViewer($viewer); 58 + 59 + $saved_query = $search_engine->newSavedQuery(); 60 + $saved_query->setParameter('ids', mpull($bulk_tasks, 'getID')); 61 + $search_engine->saveQuery($saved_query); 62 + 63 + $query_key = $saved_query->getQueryKey(); 64 + 65 + $bulk_uri = new PhutilURI("/maniphest/bulk/query/{$query_key}/"); 66 + $bulk_uri->replaceQueryParam('board', $project->getID()); 67 + 68 + return id(new AphrontRedirectResponse()) 69 + ->setURI($bulk_uri); 70 + } 71 + 72 + }
+76
src/applications/project/state/PhabricatorWorkboardViewState.php
··· 8 8 private $requestState = array(); 9 9 private $savedQuery; 10 10 private $searchEngine; 11 + private $layoutEngine; 12 + private $objects; 11 13 12 14 public function setProject(PhabricatorProject $project) { 13 15 $this->project = $project; ··· 210 212 211 213 public function getQueryParameters() { 212 214 return $this->requestState; 215 + } 216 + 217 + public function getLayoutEngine() { 218 + if ($this->layoutEngine === null) { 219 + $this->layoutEngine = $this->newLayoutEngine(); 220 + } 221 + return $this->layoutEngine; 222 + } 223 + 224 + private function newLayoutEngine() { 225 + $project = $this->getProject(); 226 + $viewer = $this->getViewer(); 227 + 228 + $board_phid = $project->getPHID(); 229 + $objects = $this->getObjects(); 230 + 231 + // Regardless of display order, pass tasks to the layout engine in ID order 232 + // so layout is consistent. 233 + $objects = msort($objects, 'getID'); 234 + 235 + $layout_engine = id(new PhabricatorBoardLayoutEngine()) 236 + ->setViewer($viewer) 237 + ->setObjectPHIDs(array_keys($objects)) 238 + ->setBoardPHIDs(array($board_phid)) 239 + ->setFetchAllBoards(true) 240 + ->executeLayout(); 241 + 242 + return $layout_engine; 243 + } 244 + 245 + public function getBoardContainerPHIDs() { 246 + $project = $this->getProject(); 247 + $viewer = $this->getViewer(); 248 + 249 + $container_phids = array($project->getPHID()); 250 + if ($project->getHasSubprojects() || $project->getHasMilestones()) { 251 + $descendants = id(new PhabricatorProjectQuery()) 252 + ->setViewer($viewer) 253 + ->withAncestorProjectPHIDs($container_phids) 254 + ->execute(); 255 + foreach ($descendants as $descendant) { 256 + $container_phids[] = $descendant->getPHID(); 257 + } 258 + } 259 + 260 + return $container_phids; 261 + } 262 + 263 + public function getObjects() { 264 + if ($this->objects === null) { 265 + $this->objects = $this->newObjects(); 266 + } 267 + 268 + return $this->objects; 269 + } 270 + 271 + private function newObjects() { 272 + $viewer = $this->getViewer(); 273 + $saved_query = $this->getSavedQuery(); 274 + $search_engine = $this->getSearchEngine(); 275 + 276 + $container_phids = $this->getBoardContainerPHIDs(); 277 + 278 + $task_query = $search_engine->buildQueryFromSavedQuery($saved_query) 279 + ->setViewer($viewer) 280 + ->withEdgeLogicPHIDs( 281 + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, 282 + PhabricatorQueryConstraint::OPERATOR_ANCESTOR, 283 + array($container_phids)); 284 + 285 + $tasks = $task_query->execute(); 286 + $tasks = mpull($tasks, null, 'getPHID'); 287 + 288 + return $tasks; 213 289 } 214 290 215 291 }