@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 workboards to be disabled, hiding "(Backlog)" column annotations

Summary:
Fixes T7410.

- Adds a "Disable Workboard" action to the "Manage Backlog" menu.
- We'll probably move this somewhere else if/when that column gets too messy.
- Disabling a board hides it, prevents it from being recreated by non-editors, and hides the "Project (Backlog)" annotations.
- Resotring a board puts it back in pristine condition.

Test Plan:
- Disabled a board.
- Verified "(Backlog)" annotations vanished.
- Enabled a board.

Reviewers: chad

Reviewed By: chad

Subscribers: mbishopim3

Maniphest Tasks: T7410

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

+201 -9
+2
src/__phutil_library_map__.php
··· 2860 2860 'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php', 2861 2861 'PhabricatorProjectArchiveController' => 'applications/project/controller/PhabricatorProjectArchiveController.php', 2862 2862 'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php', 2863 + 'PhabricatorProjectBoardDisableController' => 'applications/project/controller/PhabricatorProjectBoardDisableController.php', 2863 2864 'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php', 2864 2865 'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php', 2865 2866 'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php', ··· 7274 7275 'PhabricatorProjectApplication' => 'PhabricatorApplication', 7275 7276 'PhabricatorProjectArchiveController' => 'PhabricatorProjectController', 7276 7277 'PhabricatorProjectBoardController' => 'PhabricatorProjectController', 7278 + 'PhabricatorProjectBoardDisableController' => 'PhabricatorProjectBoardController', 7277 7279 'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController', 7278 7280 'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController', 7279 7281 'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
+2
src/applications/project/application/PhabricatorProjectApplication.php
··· 84 84 => 'PhabricatorProjectBoardImportController', 85 85 'reorder/' 86 86 => 'PhabricatorProjectBoardReorderController', 87 + 'disable/' 88 + => 'PhabricatorProjectBoardDisableController', 87 89 ), 88 90 'update/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/' 89 91 => 'PhabricatorProjectUpdateController',
+61
src/applications/project/controller/PhabricatorProjectBoardDisableController.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectBoardDisableController 4 + extends PhabricatorProjectBoardController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $request->getUser(); 8 + $project_id = $request->getURIData('projectID'); 9 + 10 + $project = id(new PhabricatorProjectQuery()) 11 + ->setViewer($viewer) 12 + ->requireCapabilities( 13 + array( 14 + PhabricatorPolicyCapability::CAN_VIEW, 15 + PhabricatorPolicyCapability::CAN_EDIT, 16 + )) 17 + ->withIDs(array($project_id)) 18 + ->executeOne(); 19 + if (!$project) { 20 + return new Aphront404Response(); 21 + } 22 + 23 + if (!$project->getHasWorkboard()) { 24 + return new Aphront404Response(); 25 + } 26 + 27 + $this->setProject($project); 28 + $id = $project->getID(); 29 + 30 + $board_uri = $this->getApplicationURI("board/{$id}/"); 31 + 32 + if ($request->isFormPost()) { 33 + $xactions = array(); 34 + 35 + $xactions[] = id(new PhabricatorProjectTransaction()) 36 + ->setTransactionType(PhabricatorProjectTransaction::TYPE_HASWORKBOARD) 37 + ->setNewValue(0); 38 + 39 + id(new PhabricatorProjectTransactionEditor()) 40 + ->setActor($viewer) 41 + ->setContentSourceFromRequest($request) 42 + ->setContinueOnNoEffect(true) 43 + ->setContinueOnMissingFields(true) 44 + ->applyTransactions($project, $xactions); 45 + 46 + return id(new AphrontRedirectResponse()) 47 + ->setURI($board_uri); 48 + } 49 + 50 + return $this->newDialog() 51 + ->setTitle(pht('Disable Workboard')) 52 + ->appendParagraph( 53 + pht( 54 + 'Disabling a workboard hides the board. Objects on the board '. 55 + 'will no longer be annotated with column names in other '. 56 + 'applications. You can restore the workboard later.')) 57 + ->addCancelButton($board_uri) 58 + ->addSubmitButton(pht('Disable Workboard')); 59 + } 60 + 61 + }
+83 -6
src/applications/project/controller/PhabricatorProjectBoardViewController.php
··· 121 121 ->setViewer($viewer) 122 122 ->setBoardPHIDs(array($board_phid)) 123 123 ->setObjectPHIDs(array_keys($tasks)) 124 + ->setFetchAllBoards(true) 124 125 ->executeLayout(); 125 126 126 127 $columns = $layout_engine->getColumns($board_phid); 127 - if (!$columns) { 128 + if (!$columns || !$project->getHasWorkboard()) { 128 129 $can_edit = PhabricatorPolicyFilter::hasCapability( 129 130 $viewer, 130 131 $project, 131 132 PhabricatorPolicyCapability::CAN_EDIT); 132 - if (!$can_edit) { 133 - $content = $this->buildNoAccessContent($project); 133 + if (!$columns) { 134 + if (!$can_edit) { 135 + $content = $this->buildNoAccessContent($project); 136 + } else { 137 + $content = $this->buildInitializeContent($project); 138 + } 134 139 } else { 135 - $content = $this->buildInitializeContent($project); 140 + if (!$can_edit) { 141 + $content = $this->buildDisabledContent($project); 142 + } else { 143 + $content = $this->buildEnableContent($project); 144 + } 136 145 } 137 146 138 147 if ($content instanceof AphrontResponse) { ··· 544 553 $request = $this->getRequest(); 545 554 $viewer = $request->getUser(); 546 555 556 + $id = $project->getID(); 557 + 558 + $disable_uri = $this->getApplicationURI("board/{$id}/disable/"); 559 + $add_uri = $this->getApplicationURI("board/{$id}/edit/"); 560 + $reorder_uri = $this->getApplicationURI("board/{$id}/reorder/"); 561 + 547 562 $can_edit = PhabricatorPolicyFilter::hasCapability( 548 563 $viewer, 549 564 $project, ··· 554 569 $manage_items[] = id(new PhabricatorActionView()) 555 570 ->setIcon('fa-plus') 556 571 ->setName(pht('Add Column')) 557 - ->setHref($this->getApplicationURI('board/'.$this->id.'/edit/')) 572 + ->setHref($add_uri) 558 573 ->setDisabled(!$can_edit) 559 574 ->setWorkflow(!$can_edit); 560 575 561 576 $manage_items[] = id(new PhabricatorActionView()) 562 577 ->setIcon('fa-exchange') 563 578 ->setName(pht('Reorder Columns')) 564 - ->setHref($this->getApplicationURI('board/'.$this->id.'/reorder/')) 579 + ->setHref($reorder_uri) 565 580 ->setDisabled(!$can_edit) 566 581 ->setWorkflow(true); 567 582 ··· 594 609 ->setName(pht('Batch Edit Visible Tasks...')) 595 610 ->setHref($batch_edit_uri) 596 611 ->setDisabled(!$can_batch_edit); 612 + 613 + $manage_items[] = id(new PhabricatorActionView()) 614 + ->setIcon('fa-ban') 615 + ->setName(pht('Disable Workboard')) 616 + ->setHref($disable_uri) 617 + ->setWorkflow(true) 618 + ->setDisabled(!$can_edit); 597 619 598 620 $manage_menu = id(new PhabricatorActionListView()) 599 621 ->setUser($viewer); ··· 849 871 'The workboard for this project has not been created yet, '. 850 872 'but you do not have permission to create it. Only users '. 851 873 'who can edit this project can create a workboard for it.')) 874 + ->addCancelButton($profile_uri); 875 + } 876 + 877 + 878 + private function buildEnableContent(PhabricatorProject $project) { 879 + $request = $this->getRequest(); 880 + $viewer = $this->getViewer(); 881 + 882 + $id = $project->getID(); 883 + $profile_uri = $this->getApplicationURI("profile/{$id}/"); 884 + $board_uri = $this->getApplicationURI("board/{$id}/"); 885 + 886 + if ($request->isFormPost()) { 887 + $xactions = array(); 888 + 889 + $xactions[] = id(new PhabricatorProjectTransaction()) 890 + ->setTransactionType(PhabricatorProjectTransaction::TYPE_HASWORKBOARD) 891 + ->setNewValue(1); 892 + 893 + id(new PhabricatorProjectTransactionEditor()) 894 + ->setActor($viewer) 895 + ->setContentSourceFromRequest($request) 896 + ->setContinueOnNoEffect(true) 897 + ->setContinueOnMissingFields(true) 898 + ->applyTransactions($project, $xactions); 899 + 900 + return id(new AphrontRedirectResponse()) 901 + ->setURI($board_uri); 902 + } 903 + 904 + return $this->newDialog() 905 + ->setTitle(pht('Workboard Disabled')) 906 + ->addHiddenInput('initialize', 1) 907 + ->appendParagraph( 908 + pht( 909 + 'This workboard has been disabled, but can be restored to its '. 910 + 'former glory.')) 911 + ->addCancelButton($profile_uri) 912 + ->addSubmitButton(pht('Enable Workboard')); 913 + } 914 + 915 + private function buildDisabledContent(PhabricatorProject $project) { 916 + $viewer = $this->getViewer(); 917 + 918 + $id = $project->getID(); 919 + 920 + $profile_uri = $this->getApplicationURI("profile/{$id}/"); 921 + 922 + return $this->newDialog() 923 + ->setTitle(pht('Workboard Disabled')) 924 + ->appendParagraph( 925 + pht( 926 + 'This workboard has been disabled, and you do not have permission '. 927 + 'to enable it. Only users who can edit this project can restore '. 928 + 'the workboard.')) 852 929 ->addCancelButton($profile_uri); 853 930 } 854 931
+9
src/applications/project/editor/PhabricatorProjectTransactionEditor.php
··· 39 39 $types[] = PhabricatorProjectTransaction::TYPE_LOCKED; 40 40 $types[] = PhabricatorProjectTransaction::TYPE_PARENT; 41 41 $types[] = PhabricatorProjectTransaction::TYPE_MILESTONE; 42 + $types[] = PhabricatorProjectTransaction::TYPE_HASWORKBOARD; 42 43 43 44 return $types; 44 45 } ··· 65 66 return $object->getColor(); 66 67 case PhabricatorProjectTransaction::TYPE_LOCKED: 67 68 return (int)$object->getIsMembershipLocked(); 69 + case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: 70 + return (int)$object->getHasWorkboard(); 68 71 case PhabricatorProjectTransaction::TYPE_PARENT: 69 72 case PhabricatorProjectTransaction::TYPE_MILESTONE: 70 73 return null; ··· 87 90 case PhabricatorProjectTransaction::TYPE_PARENT: 88 91 case PhabricatorProjectTransaction::TYPE_MILESTONE: 89 92 return $xaction->getNewValue(); 93 + case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: 94 + return (int)$xaction->getNewValue(); 90 95 case PhabricatorProjectTransaction::TYPE_SLUGS: 91 96 return $this->normalizeSlugs($xaction->getNewValue()); 92 97 } ··· 131 136 $object->setMilestoneNumber($number); 132 137 $object->setParentProjectPHID($xaction->getNewValue()); 133 138 return; 139 + case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: 140 + $object->setHasWorkboard($xaction->getNewValue()); 141 + return; 134 142 } 135 143 136 144 return parent::applyCustomInternalTransaction($object, $xaction); ··· 172 180 case PhabricatorProjectTransaction::TYPE_LOCKED: 173 181 case PhabricatorProjectTransaction::TYPE_PARENT: 174 182 case PhabricatorProjectTransaction::TYPE_MILESTONE: 183 + case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: 175 184 return; 176 185 } 177 186
+18 -3
src/applications/project/engine/PhabricatorBoardLayoutEngine.php
··· 9 9 private $columnMap = array(); 10 10 private $objectColumnMap = array(); 11 11 private $boardLayout = array(); 12 + private $fetchAllBoards; 12 13 13 14 private $remQueue = array(); 14 15 private $addQueue = array(); ··· 38 39 39 40 public function getObjectPHIDs() { 40 41 return $this->objectPHIDs; 42 + } 43 + 44 + /** 45 + * Fetch all boards, even if the board is disabled. 46 + */ 47 + public function setFetchAllBoards($fetch_all) { 48 + $this->fetchAllBoards = $fetch_all; 49 + return $this; 50 + } 51 + 52 + public function getFetchAllBoards() { 53 + return $this->fetchAllBoards; 41 54 } 42 55 43 56 public function executeLayout() { ··· 301 314 ->execute(); 302 315 $boards = mpull($boards, null, 'getPHID'); 303 316 304 - foreach ($boards as $key => $board) { 305 - if (!$board->getHasWorkboard()) { 306 - unset($boards[$key]); 317 + if (!$this->fetchAllBoards) { 318 + foreach ($boards as $key => $board) { 319 + if (!$board->getHasWorkboard()) { 320 + unset($boards[$key]); 321 + } 307 322 } 308 323 } 309 324
+26
src/applications/project/storage/PhabricatorProjectTransaction.php
··· 12 12 const TYPE_LOCKED = 'project:locked'; 13 13 const TYPE_PARENT = 'project:parent'; 14 14 const TYPE_MILESTONE = 'project:milestone'; 15 + const TYPE_HASWORKBOARD = 'project:hasworkboard'; 15 16 16 17 // NOTE: This is deprecated, members are just a normal edge now. 17 18 const TYPE_MEMBERS = 'project:members'; ··· 246 247 } 247 248 } 248 249 break; 250 + 251 + case self::TYPE_HASWORKBOARD: 252 + if ($new) { 253 + return pht( 254 + '%s enabled the workboard for this project.', 255 + $author_handle); 256 + } else { 257 + return pht( 258 + '%s disabled the workboard for this project.', 259 + $author_handle); 260 + } 249 261 } 250 262 251 263 return parent::getTitle(); ··· 366 378 $object_handle, 367 379 $this->renderSlugList($rem)); 368 380 } 381 + 382 + case self::TYPE_HASWORKBOARD: 383 + if ($new) { 384 + return pht( 385 + '%s enabled the workboard for %s.', 386 + $author_handle, 387 + $object_handle); 388 + } else { 389 + return pht( 390 + '%s disabled the workboard for %s.', 391 + $author_handle, 392 + $object_handle); 393 + } 394 + 369 395 } 370 396 371 397 return parent::getTitleForFeed();