Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2891407
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
13 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/maniphest/controller/ManiphestBatchEditController.php b/src/applications/maniphest/controller/ManiphestBatchEditController.php
index 5fb1938f84..3d69493ae1 100644
--- a/src/applications/maniphest/controller/ManiphestBatchEditController.php
+++ b/src/applications/maniphest/controller/ManiphestBatchEditController.php
@@ -1,295 +1,296 @@
<?php
/**
* @group maniphest
*/
final class ManiphestBatchEditController extends ManiphestController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$task_ids = $request->getArr('batch');
$tasks = id(new ManiphestTask())->loadAllWhere(
'id IN (%Ld)',
$task_ids);
$actions = $request->getStr('actions');
if ($actions) {
$actions = json_decode($actions, true);
}
if ($request->isFormPost() && is_array($actions)) {
foreach ($tasks as $task) {
$xactions = $this->buildTransactions($actions, $task);
if ($xactions) {
$editor = new ManiphestTransactionEditor();
$editor->setActor($user);
$editor->applyTransactions($task, $xactions);
}
}
$task_ids = implode(',', mpull($tasks, 'getID'));
return id(new AphrontRedirectResponse())
->setURI('/maniphest/view/custom/?s=oc&tasks='.$task_ids);
}
$panel = new AphrontPanelView();
- $panel->setHeader('Maniphest Batch Editor');
+ $panel->setHeader(pht('Maniphest Batch Editor'));
+ $panel->setNoBackground();
$handle_phids = mpull($tasks, 'getOwnerPHID');
$handles = $this->loadViewerHandles($handle_phids);
$list = new ManiphestTaskListView();
$list->setTasks($tasks);
$list->setUser($user);
$list->setHandles($handles);
$template = new AphrontTokenizerTemplateView();
$template = $template->render();
require_celerity_resource('maniphest-batch-editor');
Javelin::initBehavior(
'maniphest-batch-editor',
array(
'root' => 'maniphest-batch-edit-form',
'tokenizerTemplate' => $template,
'sources' => array(
'project' => array(
'src' => '/typeahead/common/projects/',
'placeholder' => 'Type a project name...',
),
'owner' => array(
'src' => '/typeahead/common/searchowner/',
'placeholder' => 'Type a user name...',
'limit' => 1,
),
),
'input' => 'batch-form-actions',
'priorityMap' => ManiphestTaskPriority::getTaskPriorityMap(),
'statusMap' => ManiphestTaskStatus::getTaskStatusMap(),
));
$form = new AphrontFormView();
$form->setUser($user);
$form->setID('maniphest-batch-edit-form');
foreach ($tasks as $task) {
$form->appendChild(
phutil_render_tag(
'input',
array(
'type' => 'hidden',
'name' => 'batch[]',
'value' => $task->getID(),
),
null));
}
$form->appendChild(
phutil_render_tag(
'input',
array(
'type' => 'hidden',
'name' => 'actions',
'id' => 'batch-form-actions',
),
null));
$form->appendChild('<p>These tasks will be edited:</p>');
$form->appendChild($list);
$form->appendChild(
id(new AphrontFormInsetView())
->setTitle('Actions')
->setRightButton(javelin_render_tag(
'a',
array(
'href' => '#',
'class' => 'button green',
'sigil' => 'add-action',
'mustcapture' => true,
),
'Add Another Action'))
->setContent(javelin_render_tag(
'table',
array(
'sigil' => 'maniphest-batch-actions',
'class' => 'maniphest-batch-actions-table',
),
'')))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Update Tasks')
->addCancelButton('/maniphest/', 'Done'));
$panel->appendChild($form);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => 'Batch Editor',
));
}
private function buildTransactions($actions, ManiphestTask $task) {
$value_map = array();
$type_map = array(
'add_comment' => ManiphestTransactionType::TYPE_NONE,
'assign' => ManiphestTransactionType::TYPE_OWNER,
'status' => ManiphestTransactionType::TYPE_STATUS,
'priority' => ManiphestTransactionType::TYPE_PRIORITY,
'add_project' => ManiphestTransactionType::TYPE_PROJECTS,
'remove_project' => ManiphestTransactionType::TYPE_PROJECTS,
);
$edge_edit_types = array(
'add_project' => true,
'remove_project' => true,
);
$xactions = array();
foreach ($actions as $action) {
if (empty($type_map[$action['action']])) {
throw new Exception("Unknown batch edit action '{$action}'!");
}
$type = $type_map[$action['action']];
// Figure out the current value, possibly after modifications by other
// batch actions of the same type. For example, if the user chooses to
// "Add Comment" twice, we should add both comments. More notably, if the
// user chooses "Remove Project..." and also "Add Project...", we should
// avoid restoring the removed project in the second transaction.
if (array_key_exists($type, $value_map)) {
$current = $value_map[$type];
} else {
switch ($type) {
case ManiphestTransactionType::TYPE_NONE:
$current = null;
break;
case ManiphestTransactionType::TYPE_OWNER:
$current = $task->getOwnerPHID();
break;
case ManiphestTransactionType::TYPE_STATUS:
$current = $task->getStatus();
break;
case ManiphestTransactionType::TYPE_PRIORITY:
$current = $task->getPriority();
break;
case ManiphestTransactionType::TYPE_PROJECTS:
$current = $task->getProjectPHIDs();
break;
}
}
// Check if the value is meaningful / provided, and normalize it if
// necessary. This discards, e.g., empty comments and empty owner
// changes.
$value = $action['value'];
switch ($type) {
case ManiphestTransactionType::TYPE_NONE:
if (!strlen($value)) {
continue 2;
}
break;
case ManiphestTransactionType::TYPE_OWNER:
if (empty($value)) {
continue 2;
}
$value = head($value);
if ($value === ManiphestTaskOwner::OWNER_UP_FOR_GRABS) {
$value = null;
}
break;
case ManiphestTransactionType::TYPE_PROJECTS:
if (empty($value)) {
continue 2;
}
break;
}
// If the edit doesn't change anything, go to the next action. This
// check is only valid for changes like "owner", "status", etc, not
// for edge edits, because we should still apply an edit like
// "Remove Projects: A, B" to a task with projects "A, B".
if (empty($edge_edit_types[$action['action']])) {
if ($value == $current) {
continue;
}
}
// Apply the value change; for most edits this is just replacement, but
// some need to merge the current and edited values (add/remove project).
switch ($type) {
case ManiphestTransactionType::TYPE_NONE:
if (strlen($current)) {
$value = $current."\n\n".$value;
}
break;
case ManiphestTransactionType::TYPE_PROJECTS:
$is_remove = ($action['action'] == 'remove_project');
$current = array_fill_keys($current, true);
$value = array_fill_keys($value, true);
$new = $current;
$did_something = false;
if ($is_remove) {
foreach ($value as $phid => $ignored) {
if (isset($new[$phid])) {
unset($new[$phid]);
$did_something = true;
}
}
} else {
foreach ($value as $phid => $ignored) {
if (empty($new[$phid])) {
$new[$phid] = true;
$did_something = true;
}
}
}
if (!$did_something) {
continue 2;
}
$value = array_keys($new);
break;
}
$value_map[$type] = $value;
}
$template = new ManiphestTransaction();
$template->setAuthorPHID($this->getRequest()->getUser()->getPHID());
// TODO: Set content source to "batch edit".
foreach ($value_map as $type => $value) {
$xaction = clone $template;
$xaction->setTransactionType($type);
switch ($type) {
case ManiphestTransactionType::TYPE_NONE:
$xaction->setComments($value);
break;
default:
$xaction->setNewValue($value);
break;
}
$xactions[] = $xaction;
}
return $xactions;
}
}
diff --git a/webroot/rsrc/css/application/maniphest/task-summary.css b/webroot/rsrc/css/application/maniphest/task-summary.css
index 89c32a0d7f..34050b4fc1 100644
--- a/webroot/rsrc/css/application/maniphest/task-summary.css
+++ b/webroot/rsrc/css/application/maniphest/task-summary.css
@@ -1,200 +1,201 @@
/**
* @provides maniphest-task-summary-css
*/
.maniphest-task-summary {
width: 100%;
margin: 4px 0;
border-collapse: separate;
font-size: 12px;
color: #333;
}
.maniphest-task-summary td {
padding: 6px 4px;
background: #fff;
white-space: nowrap;
border-style: solid;
border-top-color: #d5dadf;
border-bottom-color: #d5dadf;
border-width: 1px 0;
}
.maniphest-task-summary td em {
color: #888888;
}
.maniphest-batch-selected td {
background: #fff;
}
.maniphest-task-summary td.maniphest-task-handle {
padding: 0 4px;
width: 1px;
border-right-width: 1px;
border-right-color: #d5dadf;
}
.maniphest-task-summary td.maniphest-task-batch {
padding-right: 0px;
width: 16px;
text-align: center;
overflow: hidden;
}
.maniphest-task-summary td.maniphest-task-batch,
.maniphest-task-summary td.maniphest-task-batch input {
cursor: pointer;
}
.maniphest-task-summary td.maniphest-task-batch input {
margin: 0;
}
.maniphest-task-summary td.maniphest-task-number {
font-weight: bold;
color: #333;
width: 50px;
text-align: right;
}
.maniphest-task-summary td.maniphest-task-status {
width: 60px;
text-align: center;
}
.maniphest-task-summary td.maniphest-task-owner {
width: 100px;
}
.maniphest-task-summary td.maniphest-task-name {
font-weight: bold;
white-space: normal;
overflow: hidden;
}
.maniphest-task-summary td.maniphest-task-projects {
width: 180px;
text-align: right;
white-space: normal;
}
.maniphest-task-summary td.maniphest-task-updated {
text-align: right;
width: 85px;
padding-right: 8px;
border-right-width: 1px;
border-right-style: solid;
border-right-color: #d5dadf;
}
.maniphest-task-summary .pri-bullet {
}
.maniphest-task-summary .pri-unbreak {
border-color: #ff0000;
background-color: #ff0000;
}
.maniphest-task-summary .pri-triage {
border-color: #ee00ee;
background-color: #ee00ee;
}
.maniphest-task-summary .pri-high {
border-color: #ff6622;
background-color: #ff6622;
}
.maniphest-task-summary .pri-normal {
border-color: #ffaa66;
background-color: #ffaa66;
}
.maniphest-task-summary .pri-low {
border-color: #eecc66;
background-color: #eecc66;
}
.maniphest-task-summary .pri-wish {
border-color: #0099ff;
background-color: #0099ff;
}
.maniphest-task-group-header {
font-size: 16px;
font-weight: bold;
color: #555;
padding: 5px 0;
+ margin-left: 20px;
}
.maniphest-total-result-count {
text-align: right;
padding-right: 2em;
font-size: 11px;
color: #666666;
}
.batch-editor-header {
font-size: 16px;
color: #555;
padding: 8px 0px;
font-weight: bold;
}
.maniphest-batch-editor {
margin: 20px;
}
.maniphest-batch-editor-layout {
width: 100%;
border-top: 1px solid #d5dadf;
background: #f4f5f8;
}
.maniphest-batch-editor-layout td {
padding: 10px 8px;
white-space: nowrap;
}
.maniphest-batch-editor-layout a.button,
.maniphest-batch-editor-layout button {
margin: 0px 4px;
}
.maniphest-batch-editor-layout .batch-select-submit-cell {
text-align: right;
}
#batch-select-status-cell {
text-align: right;
color: #666666;
font-size: 11px;
vertical-align: middle;
width: 100%;
}
td.maniphest-active-handle {
cursor: move;
background-image: url('/rsrc/image/grippy_texture.png');
background-position: 3px 0px;
background-repeat: repeat-y;
}
.maniphest-subpriority-target {
position: relative;
border: 1px dashed #aaaaaa;
background: #f9f9f9;
}
.maniphest-task-loading {
opacity: 0.5;
}
.maniphest-task-dragging {
position: relative;
opacity: 0.5;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 15:09 (3 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1125827
Default Alt Text
(13 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment