1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 08:52:39 +01:00

Add a "Batch Edit Tasks..." action to workboard columns

Summary:
Ref T5523. Adds a new workflow to make some kinds of bulk workboard operations easier.

New dropdown action:

{F376848}

This brings you into the existing bulk edit flow:

{F376849}

When you save an edit, you're taken back to the board:

{F376850}

If you try to edit a column with nothing in it, you get an error:

{F376851}

Note that the selected workboard filter is applied before choosing tasks, so if your filter is set to "open tasks" we only batch edit the open (i.e., currently visible) tasks in the column. I think this is more powerful (it lets you use filtering to select task subsets) but might not be completely obvious in all cases (although I do think it's more obvious than the alternative rule -- just an issue of neither rule being completely obvious).

Test Plan:
  - Batch edited tasks in a column.
  - Used "Batch Edit Tasks..." to move tasks to a different workboard by removing + adding a project.
  - Batch edited a column with filtered-out tasks, verified only visible tasks were edited.
  - Batch edited a column with no visible tasks, received error.
  - Used the batch editor normally (Maniphest -> Maniphest, no boards).

Reviewers: chad, btrahan

Reviewed By: btrahan

Subscribers: johnny-bit, cburroughs, epriestley

Projects: #prioritized

Maniphest Tasks: T5523

Differential Revision: https://secure.phabricator.com/D12475
This commit is contained in:
epriestley 2015-04-20 10:05:08 -07:00
parent 2875f029be
commit e0c95bca86
3 changed files with 125 additions and 32 deletions

View file

@ -8,7 +8,7 @@
return array( return array(
'names' => array( 'names' => array(
'core.pkg.css' => 'bf29d341', 'core.pkg.css' => 'bf29d341',
'core.pkg.js' => 'dfea788f', 'core.pkg.js' => 'a626d14c',
'darkconsole.pkg.js' => '8ab24e01', 'darkconsole.pkg.js' => '8ab24e01',
'differential.pkg.css' => '3500921f', 'differential.pkg.css' => '3500921f',
'differential.pkg.js' => 'c0506961', 'differential.pkg.js' => 'c0506961',
@ -450,7 +450,7 @@ return array(
'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f', 'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f',
'rsrc/js/core/MultirowRowManager.js' => 'b5d57730', 'rsrc/js/core/MultirowRowManager.js' => 'b5d57730',
'rsrc/js/core/Notification.js' => '0c6946e7', 'rsrc/js/core/Notification.js' => '0c6946e7',
'rsrc/js/core/Prefab.js' => '6920d200', 'rsrc/js/core/Prefab.js' => 'b972bdcd',
'rsrc/js/core/ShapedRequest.js' => '7cbe244b', 'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
'rsrc/js/core/TextAreaUtils.js' => '5c93c52c', 'rsrc/js/core/TextAreaUtils.js' => '5c93c52c',
'rsrc/js/core/Title.js' => 'df5e11d2', 'rsrc/js/core/Title.js' => 'df5e11d2',
@ -744,7 +744,7 @@ return array(
'phabricator-notification-menu-css' => '3c9d8aa1', 'phabricator-notification-menu-css' => '3c9d8aa1',
'phabricator-object-selector-css' => '029a133d', 'phabricator-object-selector-css' => '029a133d',
'phabricator-phtize' => 'd254d646', 'phabricator-phtize' => 'd254d646',
'phabricator-prefab' => '6920d200', 'phabricator-prefab' => 'b972bdcd',
'phabricator-profile-css' => '1a20dcbf', 'phabricator-profile-css' => '1a20dcbf',
'phabricator-remarkup-css' => 'e10512ff', 'phabricator-remarkup-css' => 'e10512ff',
'phabricator-search-results-css' => '15c71110', 'phabricator-search-results-css' => '15c71110',
@ -1296,18 +1296,6 @@ return array(
'6882e80a' => array( '6882e80a' => array(
'javelin-dom', 'javelin-dom',
), ),
'6920d200' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-typeahead',
'javelin-tokenizer',
'javelin-typeahead-preloaded-source',
'javelin-typeahead-ondemand-source',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
),
'69adf288' => array( '69adf288' => array(
'javelin-install', 'javelin-install',
), ),
@ -1719,6 +1707,18 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-util', 'javelin-util',
), ),
'b972bdcd' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-typeahead',
'javelin-tokenizer',
'javelin-typeahead-preloaded-source',
'javelin-typeahead-ondemand-source',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
),
'bba9eedf' => array( 'bba9eedf' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',

View file

@ -2,16 +2,31 @@
final class ManiphestBatchEditController extends ManiphestController { final class ManiphestBatchEditController extends ManiphestController {
public function processRequest() { public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$this->requireApplicationCapability( $this->requireApplicationCapability(
ManiphestBulkEditCapability::CAPABILITY); ManiphestBulkEditCapability::CAPABILITY);
$request = $this->getRequest(); $project = null;
$user = $request->getUser(); $board_id = $request->getInt('board');
if ($board_id) {
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withIDs(array($board_id))
->executeOne();
if (!$project) {
return new Aphront404Response();
}
}
$task_ids = $request->getArr('batch'); $task_ids = $request->getArr('batch');
if (!$task_ids) {
$task_ids = $request->getStrList('batch');
}
$tasks = id(new ManiphestTaskQuery()) $tasks = id(new ManiphestTaskQuery())
->setViewer($user) ->setViewer($viewer)
->withIDs($task_ids) ->withIDs($task_ids)
->requireCapabilities( ->requireCapabilities(
array( array(
@ -22,6 +37,14 @@ final class ManiphestBatchEditController extends ManiphestController {
->needProjectPHIDs(true) ->needProjectPHIDs(true)
->execute(); ->execute();
if ($project) {
$cancel_uri = '/project/board/'.$project->getID().'/';
$redirect_uri = $cancel_uri;
} else {
$cancel_uri = '/maniphest/';
$redirect_uri = '/maniphest/?ids='.implode(',', mpull($tasks, 'getID'));
}
$actions = $request->getStr('actions'); $actions = $request->getStr('actions');
if ($actions) { if ($actions) {
$actions = json_decode($actions, true); $actions = json_decode($actions, true);
@ -39,7 +62,7 @@ final class ManiphestBatchEditController extends ManiphestController {
// TODO: Set content source to "batch edit". // TODO: Set content source to "batch edit".
$editor = id(new ManiphestTransactionEditor()) $editor = id(new ManiphestTransactionEditor())
->setActor($user) ->setActor($viewer)
->setContentSourceFromRequest($request) ->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true) ->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true) ->setContinueOnMissingFields(true)
@ -47,17 +70,14 @@ final class ManiphestBatchEditController extends ManiphestController {
} }
} }
$task_ids = implode(',', mpull($tasks, 'getID')); return id(new AphrontRedirectResponse())->setURI($redirect_uri);
return id(new AphrontRedirectResponse())
->setURI('/maniphest/?ids='.$task_ids);
} }
$handles = ManiphestTaskListView::loadTaskHandles($user, $tasks); $handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks);
$list = new ManiphestTaskListView(); $list = new ManiphestTaskListView();
$list->setTasks($tasks); $list->setTasks($tasks);
$list->setUser($user); $list->setUser($viewer);
$list->setHandles($handles); $list->setHandles($handles);
$template = new AphrontTokenizerTemplateView(); $template = new AphrontTokenizerTemplateView();
@ -65,9 +85,9 @@ final class ManiphestBatchEditController extends ManiphestController {
$projects_source = new PhabricatorProjectDatasource(); $projects_source = new PhabricatorProjectDatasource();
$mailable_source = new PhabricatorMetaMTAMailableDatasource(); $mailable_source = new PhabricatorMetaMTAMailableDatasource();
$mailable_source->setViewer($user); $mailable_source->setViewer($viewer);
$owner_source = new PhabricatorTypeaheadOwnerDatasource(); $owner_source = new PhabricatorTypeaheadOwnerDatasource();
$owner_source->setViewer($user); $owner_source->setViewer($viewer);
require_celerity_resource('maniphest-batch-editor'); require_celerity_resource('maniphest-batch-editor');
Javelin::initBehavior( Javelin::initBehavior(
@ -98,9 +118,10 @@ final class ManiphestBatchEditController extends ManiphestController {
'statusMap' => ManiphestTaskStatus::getTaskStatusMap(), 'statusMap' => ManiphestTaskStatus::getTaskStatusMap(),
)); ));
$form = new AphrontFormView(); $form = id(new AphrontFormView())
$form->setUser($user); ->setUser($viewer)
$form->setID('maniphest-batch-edit-form'); ->addHiddenInput('board', $board_id)
->setID('maniphest-batch-edit-form');
foreach ($tasks as $task) { foreach ($tasks as $task) {
$form->appendChild( $form->appendChild(
@ -143,7 +164,7 @@ final class ManiphestBatchEditController extends ManiphestController {
->appendChild( ->appendChild(
id(new AphrontFormSubmitControl()) id(new AphrontFormSubmitControl())
->setValue(pht('Update Tasks')) ->setValue(pht('Update Tasks'))
->addCancelButton('/maniphest/')); ->addCancelButton($cancel_uri));
$title = pht('Batch Editor'); $title = pht('Batch Editor');

View file

@ -3,6 +3,8 @@
final class PhabricatorProjectBoardViewController final class PhabricatorProjectBoardViewController
extends PhabricatorProjectBoardController { extends PhabricatorProjectBoardController {
const BATCH_EDIT_ALL = 'all';
private $id; private $id;
private $slug; private $slug;
private $handles; private $handles;
@ -211,6 +213,50 @@ final class PhabricatorProjectBoardViewController
->requireCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT)) ->requireCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT))
->apply($tasks); ->apply($tasks);
// If this is a batch edit, select the editable tasks in the chosen column
// and ship the user into the batch editor.
$batch_edit = $request->getStr('batch');
if ($batch_edit) {
if ($batch_edit !== self::BATCH_EDIT_ALL) {
$column_id_map = mpull($columns, null, 'getID');
$batch_column = idx($column_id_map, $batch_edit);
if (!$batch_column) {
return new Aphront404Response();
}
$batch_task_phids = idx($task_map, $batch_column->getPHID(), array());
foreach ($batch_task_phids as $key => $batch_task_phid) {
if (empty($task_can_edit_map[$batch_task_phid])) {
unset($batch_task_phids[$key]);
}
}
$batch_tasks = array_select_keys($tasks, $batch_task_phids);
} else {
$batch_tasks = $task_can_edit_map;
}
if (!$batch_tasks) {
$cancel_uri = $this->getURIWithState($board_uri);
return $this->newDialog()
->setTitle(pht('No Editable Tasks'))
->appendParagraph(
pht(
'The selected column contains no visible tasks which you '.
'have permission to edit.'))
->addCancelButton($board_uri);
}
$batch_ids = mpull($batch_tasks, 'getID');
$batch_ids = implode(',', $batch_ids);
$batch_uri = new PhutilURI('/maniphest/batch/');
$batch_uri->setQueryParam('board', $this->id);
$batch_uri->setQueryParam('batch', $batch_ids);
return id(new AphrontRedirectResponse())
->setURI($batch_uri);
}
$board_id = celerity_generate_unique_node_id(); $board_id = celerity_generate_unique_node_id();
$board = id(new PHUIWorkboardView()) $board = id(new PHUIWorkboardView())
@ -518,6 +564,19 @@ final class PhabricatorProjectBoardViewController
->setName($hidden_text) ->setName($hidden_text)
->setHref($hidden_uri); ->setHref($hidden_uri);
$batch_edit_uri = $request->getRequestURI();
$batch_edit_uri->setQueryParam('batch', self::BATCH_EDIT_ALL);
$can_batch_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
PhabricatorApplication::getByClass('PhabricatorManiphestApplication'),
ManiphestBulkEditCapability::CAPABILITY);
$manage_items[] = id(new PhabricatorActionView())
->setIcon('fa-list-ul')
->setName(pht('Batch Edit Visible Tasks...'))
->setHref($batch_edit_uri)
->setDisabled(!$can_batch_edit);
$manage_menu = id(new PhabricatorActionListView()) $manage_menu = id(new PhabricatorActionListView())
->setUser($viewer); ->setUser($viewer);
foreach ($manage_items as $item) { foreach ($manage_items as $item) {
@ -563,6 +622,19 @@ final class PhabricatorProjectBoardViewController
)) ))
->setDisabled(!$can_edit); ->setDisabled(!$can_edit);
$batch_edit_uri = $request->getRequestURI();
$batch_edit_uri->setQueryParam('batch', $column->getID());
$can_batch_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
PhabricatorApplication::getByClass('PhabricatorManiphestApplication'),
ManiphestBulkEditCapability::CAPABILITY);
$column_items[] = id(new PhabricatorActionView())
->setIcon('fa-list-ul')
->setName(pht('Batch Edit Tasks...'))
->setHref($batch_edit_uri)
->setDisabled(!$can_batch_edit);
$edit_uri = $this->getApplicationURI( $edit_uri = $this->getApplicationURI(
'board/'.$this->id.'/column/'.$column->getID().'/'); 'board/'.$this->id.'/column/'.$column->getID().'/');