diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 76105fdda8..accf2c05cb 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2860,6 +2860,7 @@ phutil_register_library_map(array( 'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php', 'PhabricatorProjectArchiveController' => 'applications/project/controller/PhabricatorProjectArchiveController.php', 'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php', + 'PhabricatorProjectBoardDisableController' => 'applications/project/controller/PhabricatorProjectBoardDisableController.php', 'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php', 'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php', 'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php', @@ -7274,6 +7275,7 @@ phutil_register_library_map(array( 'PhabricatorProjectApplication' => 'PhabricatorApplication', 'PhabricatorProjectArchiveController' => 'PhabricatorProjectController', 'PhabricatorProjectBoardController' => 'PhabricatorProjectController', + 'PhabricatorProjectBoardDisableController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController', diff --git a/src/applications/project/application/PhabricatorProjectApplication.php b/src/applications/project/application/PhabricatorProjectApplication.php index bd321bbaa8..0f5f90a737 100644 --- a/src/applications/project/application/PhabricatorProjectApplication.php +++ b/src/applications/project/application/PhabricatorProjectApplication.php @@ -84,6 +84,8 @@ final class PhabricatorProjectApplication extends PhabricatorApplication { => 'PhabricatorProjectBoardImportController', 'reorder/' => 'PhabricatorProjectBoardReorderController', + 'disable/' + => 'PhabricatorProjectBoardDisableController', ), 'update/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/' => 'PhabricatorProjectUpdateController', diff --git a/src/applications/project/controller/PhabricatorProjectBoardDisableController.php b/src/applications/project/controller/PhabricatorProjectBoardDisableController.php new file mode 100644 index 0000000000..0440ff9eff --- /dev/null +++ b/src/applications/project/controller/PhabricatorProjectBoardDisableController.php @@ -0,0 +1,61 @@ +<?php + +final class PhabricatorProjectBoardDisableController + extends PhabricatorProjectBoardController { + + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getUser(); + $project_id = $request->getURIData('projectID'); + + $project = id(new PhabricatorProjectQuery()) + ->setViewer($viewer) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->withIDs(array($project_id)) + ->executeOne(); + if (!$project) { + return new Aphront404Response(); + } + + if (!$project->getHasWorkboard()) { + return new Aphront404Response(); + } + + $this->setProject($project); + $id = $project->getID(); + + $board_uri = $this->getApplicationURI("board/{$id}/"); + + if ($request->isFormPost()) { + $xactions = array(); + + $xactions[] = id(new PhabricatorProjectTransaction()) + ->setTransactionType(PhabricatorProjectTransaction::TYPE_HASWORKBOARD) + ->setNewValue(0); + + id(new PhabricatorProjectTransactionEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($project, $xactions); + + return id(new AphrontRedirectResponse()) + ->setURI($board_uri); + } + + return $this->newDialog() + ->setTitle(pht('Disable Workboard')) + ->appendParagraph( + pht( + 'Disabling a workboard hides the board. Objects on the board '. + 'will no longer be annotated with column names in other '. + 'applications. You can restore the workboard later.')) + ->addCancelButton($board_uri) + ->addSubmitButton(pht('Disable Workboard')); + } + +} diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index a10cdc1696..ffedb6b17f 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -121,18 +121,27 @@ final class PhabricatorProjectBoardViewController ->setViewer($viewer) ->setBoardPHIDs(array($board_phid)) ->setObjectPHIDs(array_keys($tasks)) + ->setFetchAllBoards(true) ->executeLayout(); $columns = $layout_engine->getColumns($board_phid); - if (!$columns) { + if (!$columns || !$project->getHasWorkboard()) { $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $project, PhabricatorPolicyCapability::CAN_EDIT); - if (!$can_edit) { - $content = $this->buildNoAccessContent($project); + if (!$columns) { + if (!$can_edit) { + $content = $this->buildNoAccessContent($project); + } else { + $content = $this->buildInitializeContent($project); + } } else { - $content = $this->buildInitializeContent($project); + if (!$can_edit) { + $content = $this->buildDisabledContent($project); + } else { + $content = $this->buildEnableContent($project); + } } if ($content instanceof AphrontResponse) { @@ -544,6 +553,12 @@ final class PhabricatorProjectBoardViewController $request = $this->getRequest(); $viewer = $request->getUser(); + $id = $project->getID(); + + $disable_uri = $this->getApplicationURI("board/{$id}/disable/"); + $add_uri = $this->getApplicationURI("board/{$id}/edit/"); + $reorder_uri = $this->getApplicationURI("board/{$id}/reorder/"); + $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $project, @@ -554,14 +569,14 @@ final class PhabricatorProjectBoardViewController $manage_items[] = id(new PhabricatorActionView()) ->setIcon('fa-plus') ->setName(pht('Add Column')) - ->setHref($this->getApplicationURI('board/'.$this->id.'/edit/')) + ->setHref($add_uri) ->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/')) + ->setHref($reorder_uri) ->setDisabled(!$can_edit) ->setWorkflow(true); @@ -595,6 +610,13 @@ final class PhabricatorProjectBoardViewController ->setHref($batch_edit_uri) ->setDisabled(!$can_batch_edit); + $manage_items[] = id(new PhabricatorActionView()) + ->setIcon('fa-ban') + ->setName(pht('Disable Workboard')) + ->setHref($disable_uri) + ->setWorkflow(true) + ->setDisabled(!$can_edit); + $manage_menu = id(new PhabricatorActionListView()) ->setUser($viewer); foreach ($manage_items as $item) { @@ -852,4 +874,59 @@ final class PhabricatorProjectBoardViewController ->addCancelButton($profile_uri); } + + private function buildEnableContent(PhabricatorProject $project) { + $request = $this->getRequest(); + $viewer = $this->getViewer(); + + $id = $project->getID(); + $profile_uri = $this->getApplicationURI("profile/{$id}/"); + $board_uri = $this->getApplicationURI("board/{$id}/"); + + if ($request->isFormPost()) { + $xactions = array(); + + $xactions[] = id(new PhabricatorProjectTransaction()) + ->setTransactionType(PhabricatorProjectTransaction::TYPE_HASWORKBOARD) + ->setNewValue(1); + + id(new PhabricatorProjectTransactionEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($project, $xactions); + + return id(new AphrontRedirectResponse()) + ->setURI($board_uri); + } + + return $this->newDialog() + ->setTitle(pht('Workboard Disabled')) + ->addHiddenInput('initialize', 1) + ->appendParagraph( + pht( + 'This workboard has been disabled, but can be restored to its '. + 'former glory.')) + ->addCancelButton($profile_uri) + ->addSubmitButton(pht('Enable Workboard')); + } + + private function buildDisabledContent(PhabricatorProject $project) { + $viewer = $this->getViewer(); + + $id = $project->getID(); + + $profile_uri = $this->getApplicationURI("profile/{$id}/"); + + return $this->newDialog() + ->setTitle(pht('Workboard Disabled')) + ->appendParagraph( + pht( + 'This workboard has been disabled, and you do not have permission '. + 'to enable it. Only users who can edit this project can restore '. + 'the workboard.')) + ->addCancelButton($profile_uri); + } + } diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index 20eb2c42bf..51a120000a 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -39,6 +39,7 @@ final class PhabricatorProjectTransactionEditor $types[] = PhabricatorProjectTransaction::TYPE_LOCKED; $types[] = PhabricatorProjectTransaction::TYPE_PARENT; $types[] = PhabricatorProjectTransaction::TYPE_MILESTONE; + $types[] = PhabricatorProjectTransaction::TYPE_HASWORKBOARD; return $types; } @@ -65,6 +66,8 @@ final class PhabricatorProjectTransactionEditor return $object->getColor(); case PhabricatorProjectTransaction::TYPE_LOCKED: return (int)$object->getIsMembershipLocked(); + case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: + return (int)$object->getHasWorkboard(); case PhabricatorProjectTransaction::TYPE_PARENT: case PhabricatorProjectTransaction::TYPE_MILESTONE: return null; @@ -87,6 +90,8 @@ final class PhabricatorProjectTransactionEditor case PhabricatorProjectTransaction::TYPE_PARENT: case PhabricatorProjectTransaction::TYPE_MILESTONE: return $xaction->getNewValue(); + case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: + return (int)$xaction->getNewValue(); case PhabricatorProjectTransaction::TYPE_SLUGS: return $this->normalizeSlugs($xaction->getNewValue()); } @@ -131,6 +136,9 @@ final class PhabricatorProjectTransactionEditor $object->setMilestoneNumber($number); $object->setParentProjectPHID($xaction->getNewValue()); return; + case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: + $object->setHasWorkboard($xaction->getNewValue()); + return; } return parent::applyCustomInternalTransaction($object, $xaction); @@ -172,6 +180,7 @@ final class PhabricatorProjectTransactionEditor case PhabricatorProjectTransaction::TYPE_LOCKED: case PhabricatorProjectTransaction::TYPE_PARENT: case PhabricatorProjectTransaction::TYPE_MILESTONE: + case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: return; } diff --git a/src/applications/project/engine/PhabricatorBoardLayoutEngine.php b/src/applications/project/engine/PhabricatorBoardLayoutEngine.php index cfd752abb3..caedee336c 100644 --- a/src/applications/project/engine/PhabricatorBoardLayoutEngine.php +++ b/src/applications/project/engine/PhabricatorBoardLayoutEngine.php @@ -9,6 +9,7 @@ final class PhabricatorBoardLayoutEngine extends Phobject { private $columnMap = array(); private $objectColumnMap = array(); private $boardLayout = array(); + private $fetchAllBoards; private $remQueue = array(); private $addQueue = array(); @@ -40,6 +41,18 @@ final class PhabricatorBoardLayoutEngine extends Phobject { return $this->objectPHIDs; } + /** + * Fetch all boards, even if the board is disabled. + */ + public function setFetchAllBoards($fetch_all) { + $this->fetchAllBoards = $fetch_all; + return $this; + } + + public function getFetchAllBoards() { + return $this->fetchAllBoards; + } + public function executeLayout() { $viewer = $this->getViewer(); @@ -301,9 +314,11 @@ final class PhabricatorBoardLayoutEngine extends Phobject { ->execute(); $boards = mpull($boards, null, 'getPHID'); - foreach ($boards as $key => $board) { - if (!$board->getHasWorkboard()) { - unset($boards[$key]); + if (!$this->fetchAllBoards) { + foreach ($boards as $key => $board) { + if (!$board->getHasWorkboard()) { + unset($boards[$key]); + } } } diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php index 1186e9c4f7..f668b0d2ec 100644 --- a/src/applications/project/storage/PhabricatorProjectTransaction.php +++ b/src/applications/project/storage/PhabricatorProjectTransaction.php @@ -12,6 +12,7 @@ final class PhabricatorProjectTransaction const TYPE_LOCKED = 'project:locked'; const TYPE_PARENT = 'project:parent'; const TYPE_MILESTONE = 'project:milestone'; + const TYPE_HASWORKBOARD = 'project:hasworkboard'; // NOTE: This is deprecated, members are just a normal edge now. const TYPE_MEMBERS = 'project:members'; @@ -246,6 +247,17 @@ final class PhabricatorProjectTransaction } } break; + + case self::TYPE_HASWORKBOARD: + if ($new) { + return pht( + '%s enabled the workboard for this project.', + $author_handle); + } else { + return pht( + '%s disabled the workboard for this project.', + $author_handle); + } } return parent::getTitle(); @@ -366,6 +378,20 @@ final class PhabricatorProjectTransaction $object_handle, $this->renderSlugList($rem)); } + + case self::TYPE_HASWORKBOARD: + if ($new) { + return pht( + '%s enabled the workboard for %s.', + $author_handle, + $object_handle); + } else { + return pht( + '%s disabled the workboard for %s.', + $author_handle, + $object_handle); + } + } return parent::getTitleForFeed();