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 @@ -1926,6 +1926,7 @@ 'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php', 'PhabricatorProjectBoardDeleteController' => 'applications/project/controller/PhabricatorProjectBoardDeleteController.php', 'PhabricatorProjectBoardEditController' => 'applications/project/controller/PhabricatorProjectBoardEditController.php', + 'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php', 'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php', 'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php', 'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php', @@ -2620,6 +2621,7 @@ 'PonderVoteSaveController' => 'applications/ponder/controller/PonderVoteSaveController.php', 'ProjectBoardTaskCard' => 'applications/project/view/ProjectBoardTaskCard.php', 'ProjectConduitAPIMethod' => 'applications/project/conduit/ProjectConduitAPIMethod.php', + 'ProjectCreateConduitAPIMethod' => 'applications/project/conduit/ProjectCreateConduitAPIMethod.php', 'ProjectCreateProjectsCapability' => 'applications/project/capability/ProjectCreateProjectsCapability.php', 'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php', 'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php', @@ -4752,6 +4754,7 @@ 'PhabricatorProjectBoardController' => 'PhabricatorProjectController', 'PhabricatorProjectBoardDeleteController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardEditController' => 'PhabricatorProjectBoardController', + 'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectColumn' => array( @@ -5561,6 +5564,7 @@ 'PonderVoteEditor' => 'PhabricatorEditor', 'PonderVoteSaveController' => 'PonderController', 'ProjectConduitAPIMethod' => 'ConduitAPIMethod', + 'ProjectCreateConduitAPIMethod' => 'ProjectConduitAPIMethod', 'ProjectCreateProjectsCapability' => 'PhabricatorPolicyCapability', 'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod', 'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule', diff --git a/src/applications/project/application/PhabricatorProjectApplication.php b/src/applications/project/application/PhabricatorProjectApplication.php --- a/src/applications/project/application/PhabricatorProjectApplication.php +++ b/src/applications/project/application/PhabricatorProjectApplication.php @@ -71,6 +71,8 @@ => 'PhabricatorProjectBoardDeleteController', 'column/(?:(?P\d+)/)?' => 'PhabricatorProjectColumnDetailController', + 'import/' + => 'PhabricatorProjectBoardImportController', 'reorder/' => 'PhabricatorProjectBoardReorderController', ), diff --git a/src/applications/project/controller/PhabricatorProjectBoardImportController.php b/src/applications/project/controller/PhabricatorProjectBoardImportController.php new file mode 100644 --- /dev/null +++ b/src/applications/project/controller/PhabricatorProjectBoardImportController.php @@ -0,0 +1,118 @@ +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); + + $columns = id(new PhabricatorProjectColumnQuery()) + ->setViewer($viewer) + ->withProjectPHIDs(array($project->getPHID())) + ->execute(); + if ($columns) { + return new Aphront404Response(); + } + + $project_id = $project->getID(); + $board_uri = $this->getApplicationURI("board/{$project_id}/"); + + if ($request->isFormPost()) { + $import_phid = $request->getStr('importProjectPHID'); + + $import_columns = id(new PhabricatorProjectColumnQuery()) + ->setViewer($viewer) + ->withProjectPHIDs(array($import_phid)) + ->execute(); + // not sure how this can happen exactly, but it is bad so 404 + if (!$import_columns) { + return new Aphront404Response(); + } + + $table = id(new PhabricatorProjectColumn()) + ->openTransaction(); + foreach ($import_columns as $import_column) { + if ($import_column->isHidden()) { + continue; + } + $new_column = PhabricatorProjectColumn::initializeNewColumn($viewer) + ->setSequence($import_column->getSequence()) + ->setProjectPHID($project->getPHID()) + ->setName($import_column->getName()) + ->save(); + } + $table->saveTransaction(); + + return id(new AphrontRedirectResponse())->setURI($board_uri); + } + + $viewer_projects = id(new PhabricatorProjectQuery()) + ->setViewer($viewer) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->withMemberPHIDs(array($viewer->getPHID())) + ->execute(); + + if (empty($viewer_projects)) { + $body = pht( + 'You are not a member of any projects that have initialized '. + 'workboards. Join such a project and try again.'); + return $this->newDialog() + ->setTitle(pht('Can Not Import Project Columns')) + ->appendChild($body) + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->addCancelButton($board_uri); + } + $viewer_projects = mpull($viewer_projects, null, 'getPHID'); + + $columns = id(new PhabricatorProjectColumnQuery()) + ->setViewer($viewer) + ->withProjectPHIDs(array_keys($viewer_projects)) + ->execute(); + $columns = mgroup($columns, 'getProjectPHID'); + $importable_projects = array_intersect_key($viewer_projects, $columns); + $project_selector = id(new AphrontFormRadioButtonControl()) + ->setLabel(pht('Import from one of the following projects:')) + ->setName('importProjectPHID') + ->setValue(reset($importable_projects)->getPHID()); + foreach ($importable_projects as $importable_project) { + $project_selector->addButton( + $importable_project->getPHID(), + $importable_project->getName(), + null); + } + + return $this->newDialog() + ->setTitle(pht('Import Project Columns')) + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->appendChild($project_selector) + ->addCancelButton($board_uri) + ->addSubmitButton(pht('Import')); + } + +} 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 @@ -54,16 +54,20 @@ $columns = $column_query->execute(); $columns = mpull($columns, null, 'getSequence'); - // If there's no default column, create one now. if (empty($columns[0])) { - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - $column = PhabricatorProjectColumn::initializeNewColumn($viewer) - ->setSequence(0) - ->setProjectPHID($project->getPHID()) - ->save(); - $column->attachProject($project); - $columns[0] = $column; - unset($unguarded); + // If there's no default column and the user wants us to, create one now. + if ($request->getExists('initialize')) { + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); + $column = PhabricatorProjectColumn::initializeNewColumn($viewer) + ->setSequence(0) + ->setProjectPHID($project->getPHID()) + ->save(); + $column->attachProject($project); + $columns[0] = $column; + unset($unguarded); + } else { + return $this->initializeWorkboardDialog(); + } } ksort($columns); @@ -406,5 +410,37 @@ return $manage_button; } + private function initializeWorkboardDialog() { + $default_column_link = phutil_tag( + 'a', + array( + 'href' => + $this->getApplicationURI('/board/'.$this->id.'/?initialize=true') + ), + pht('add a default backlog column')); + $import_panels_link = phutil_tag( + 'a', + array( + 'href' => $this->getApplicationURI('/board/'.$this->id.'/import/') + ), + pht('import columns from another project')); + + $instructions = phutil_tag( + 'p', + array(), + pht( + 'You can %s or %s to get started.', + $default_column_link, + $import_panels_link)); + $dialog = id(new AphrontDialogView()) + ->setUser($this->getRequest()->getUser()) + ->setTitle(pht('This workboard needs initial setup.')) + ->addHiddenInput('initialize', true) + ->addSubmitButton('Add Default Backlog Column') + ->appendChild($instructions); + + return id(new AphrontDialogResponse()) + ->setDialog($dialog); + } }