From 5c73c169fd31d029df764086cab24d19a6317d76 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 29 Sep 2017 16:20:42 -0700 Subject: [PATCH] Rough cut of "Move tasks to column..." Summary: Ref T5523. See PHI50. See PHI40. This isn't perfect, but should improve things. Add a "Move tasks to column..." action to workboards which moves all visible tasks in a column to another column, either on the same board or on a different board. This is a two-step process so that I don't have to write Javascript, and because I'm not 100% sure this is what users actually want/need. If it sticks, the UI could be refined later. - The first dialog asks you to choose a project, defaulting ot the current project. - The second dialog asks you to choose a column on that project's board. Test Plan: - Moved tasks on the same board. - Moved tasks to a different board. - Tried to move tasks to the starting column, got a sensible error. - Tried to move tasks to no project, got a sensible error. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T5523 Differential Revision: https://secure.phabricator.com/D18665 --- .../PhabricatorProjectBoardViewController.php | 205 +++++++++++++++++- 1 file changed, 204 insertions(+), 1 deletion(-) diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index 8405ec677f..87cb5bc417 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -34,7 +34,9 @@ final class PhabricatorProjectBoardViewController ->setBaseURI($board_uri) ->setIsBoardView(true); - if ($request->isFormPost() && !$request->getBool('initialize')) { + if ($request->isFormPost() + && !$request->getBool('initialize') + && !$request->getStr('move')) { $saved = $search_engine->buildSavedQueryFromRequest($request); $search_engine->saveQuery($saved); $filter_form = id(new AphrontFormView()) @@ -238,6 +240,199 @@ final class PhabricatorProjectBoardViewController ->setURI($batch_uri); } + $move_id = $request->getStr('move'); + if (strlen($move_id)) { + $column_id_map = mpull($columns, null, 'getID'); + $move_column = idx($column_id_map, $move_id); + if (!$move_column) { + return new Aphront404Response(); + } + + $move_task_phids = $layout_engine->getColumnObjectPHIDs( + $board_phid, + $move_column->getPHID()); + + foreach ($move_task_phids as $key => $move_task_phid) { + if (empty($task_can_edit_map[$move_task_phid])) { + unset($move_task_phids[$key]); + } + } + + $move_tasks = array_select_keys($tasks, $move_task_phids); + $cancel_uri = $this->getURIWithState($board_uri); + + if (!$move_tasks) { + return $this->newDialog() + ->setTitle(pht('No Movable Tasks')) + ->appendParagraph( + pht( + 'The selected column contains no visible tasks which you '. + 'have permission to move.')) + ->addCancelButton($cancel_uri); + } + + $move_project_phid = $project->getPHID(); + $move_column_phid = null; + $move_project = null; + $move_column = null; + $columns = null; + $errors = array(); + + if ($request->isFormPost()) { + $move_project_phid = head($request->getArr('moveProjectPHID')); + if (!$move_project_phid) { + $move_project_phid = $request->getStr('moveProjectPHID'); + } + + if (!$move_project_phid) { + if ($request->getBool('hasProject')) { + $errors[] = pht('Choose a project to move tasks to.'); + } + } else { + $target_project = id(new PhabricatorProjectQuery()) + ->setViewer($viewer) + ->withPHIDs(array($move_project_phid)) + ->executeOne(); + if (!$target_project) { + $errors[] = pht('You must choose a valid project.'); + } else if (!$project->getHasWorkboard()) { + $errors[] = pht( + 'You must choose a project with a workboard.'); + } else { + $move_project = $target_project; + } + } + + if ($move_project) { + $move_engine = id(new PhabricatorBoardLayoutEngine()) + ->setViewer($viewer) + ->setBoardPHIDs(array($move_project->getPHID())) + ->setFetchAllBoards(true) + ->executeLayout(); + + $columns = $move_engine->getColumns($move_project->getPHID()); + $columns = mpull($columns, null, 'getPHID'); + + $move_column_phid = $request->getStr('moveColumnPHID'); + if (!$move_column_phid) { + if ($request->getBool('hasColumn')) { + $errors[] = pht('Choose a column to move tasks to.'); + } + } else { + if (empty($columns[$move_column_phid])) { + $errors[] = pht( + 'Choose a valid column on the target workboard to move '. + 'tasks to.'); + } else if ($columns[$move_column_phid]->getID() == $move_id) { + $errors[] = pht( + 'You can not move tasks from a column to itself.'); + } else { + $move_column = $columns[$move_column_phid]; + } + } + } + } + + if ($move_column && $move_project) { + foreach ($move_tasks as $move_task) { + $xactions = array(); + + // If we're switching projects, get out of the old project first + // and move to the new project. + if ($move_project->getID() != $project->getID()) { + $xactions[] = id(new ManiphestTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) + ->setMetadataValue( + 'edge:type', + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST) + ->setNewValue( + array( + '-' => array( + $project->getPHID() => $project->getPHID(), + ), + '+' => array( + $move_project->getPHID() => $move_project->getPHID(), + ), + )); + } + + $xactions[] = id(new ManiphestTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS) + ->setNewValue( + array( + array( + 'columnPHID' => $move_column->getPHID(), + ), + )); + + $editor = id(new ManiphestTransactionEditor()) + ->setActor($viewer) + ->setContinueOnMissingFields(true) + ->setContinueOnNoEffect(true) + ->setContentSourceFromRequest($request); + + $editor->applyTransactions($move_task, $xactions); + } + + return id(new AphrontRedirectResponse()) + ->setURI($cancel_uri); + } + + if ($move_project) { + $column_form = id(new AphrontFormView()) + ->setViewer($viewer) + ->appendControl( + id(new AphrontFormSelectControl()) + ->setName('moveColumnPHID') + ->setLabel(pht('Move to Column')) + ->setValue($move_column_phid) + ->setOptions(mpull($columns, 'getDisplayName', 'getPHID'))); + + return $this->newDialog() + ->setTitle(pht('Move Tasks')) + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->setErrors($errors) + ->addHiddenInput('move', $move_id) + ->addHiddenInput('moveProjectPHID', $move_project->getPHID()) + ->addHiddenInput('hasColumn', true) + ->addHiddenInput('hasProject', true) + ->appendParagraph( + pht( + 'Choose a column on the %s workboard to move tasks to:', + $viewer->renderHandle($move_project->getPHID()))) + ->appendForm($column_form) + ->addSubmitButton(pht('Move Tasks')) + ->addCancelButton($cancel_uri); + } + + if ($move_project_phid) { + $move_project_phid_value = array($move_project_phid); + } else { + $move_project_phid_value = array(); + } + + $project_form = id(new AphrontFormView()) + ->setViewer($viewer) + ->appendControl( + id(new AphrontFormTokenizerControl()) + ->setName('moveProjectPHID') + ->setLimit(1) + ->setLabel(pht('Move to Project')) + ->setValue($move_project_phid_value) + ->setDatasource(new PhabricatorProjectDatasource())); + + return $this->newDialog() + ->setTitle(pht('Move Tasks')) + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->setErrors($errors) + ->addHiddenInput('move', $move_id) + ->addHiddenInput('hasProject', true) + ->appendForm($project_form) + ->addSubmitButton(pht('Continue')) + ->addCancelButton($cancel_uri); + } + + $board_id = celerity_generate_unique_node_id(); $board = id(new PHUIWorkboardView()) @@ -851,6 +1046,14 @@ final class PhabricatorProjectBoardViewController ->setHref($batch_edit_uri) ->setDisabled(!$can_batch_edit); + $batch_move_uri = $request->getRequestURI(); + $batch_move_uri->setQueryParam('move', $column->getID()); + $column_items[] = id(new PhabricatorActionView()) + ->setIcon('fa-arrow-right') + ->setName(pht('Move Tasks to Column...')) + ->setHref($batch_move_uri) + ->setWorkflow(true); + // Column Related Actions Below // $edit_uri = 'board/'.$this->id.'/edit/'.$column->getID().'/';