2015-12-03 23:32:40 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class ManiphestEditEngine
|
|
|
|
extends PhabricatorEditEngine {
|
|
|
|
|
|
|
|
const ENGINECONST = 'maniphest.task';
|
|
|
|
|
|
|
|
public function getEngineName() {
|
|
|
|
return pht('Maniphest Tasks');
|
|
|
|
}
|
|
|
|
|
2015-12-17 16:25:42 +01:00
|
|
|
public function getSummaryHeader() {
|
|
|
|
return pht('Configure Maniphest Task Forms');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getSummaryText() {
|
|
|
|
return pht('Configure how users create and edit tasks.');
|
|
|
|
}
|
|
|
|
|
2015-12-03 23:32:40 +01:00
|
|
|
public function getEngineApplicationClass() {
|
|
|
|
return 'PhabricatorManiphestApplication';
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function newEditableObject() {
|
|
|
|
return ManiphestTask::initializeNewTask($this->getViewer());
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function newObjectQuery() {
|
|
|
|
return id(new ManiphestTaskQuery());
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getObjectCreateTitleText($object) {
|
|
|
|
return pht('Create New Task');
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getObjectEditTitleText($object) {
|
2016-03-28 18:05:22 +02:00
|
|
|
return pht('Edit Task: %s', $object->getTitle());
|
2015-12-03 23:32:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getObjectEditShortText($object) {
|
|
|
|
return $object->getMonogram();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getObjectCreateShortText() {
|
|
|
|
return pht('Create Task');
|
|
|
|
}
|
|
|
|
|
2016-03-28 18:05:22 +02:00
|
|
|
protected function getObjectName() {
|
|
|
|
return pht('Task');
|
|
|
|
}
|
|
|
|
|
2015-12-09 11:13:36 +01:00
|
|
|
protected function getEditorURI() {
|
|
|
|
return $this->getApplication()->getApplicationURI('task/edit/');
|
|
|
|
}
|
|
|
|
|
2015-12-03 23:32:40 +01:00
|
|
|
protected function getCommentViewHeaderText($object) {
|
2015-12-12 02:03:10 +01:00
|
|
|
return pht('Weigh In');
|
|
|
|
}
|
2015-12-03 23:32:40 +01:00
|
|
|
|
2015-12-12 02:03:10 +01:00
|
|
|
protected function getCommentViewButtonText($object) {
|
|
|
|
return pht('Set Sail for Adventure');
|
2015-12-03 23:32:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getObjectViewURI($object) {
|
|
|
|
return '/'.$object->getMonogram();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildCustomEditFields($object) {
|
2015-12-05 19:40:56 +01:00
|
|
|
$status_map = $this->getTaskStatusMap($object);
|
|
|
|
$priority_map = $this->getTaskPriorityMap($object);
|
2015-12-03 23:32:40 +01:00
|
|
|
|
2015-12-04 16:56:03 +01:00
|
|
|
if ($object->isClosed()) {
|
|
|
|
$default_status = ManiphestTaskStatus::getDefaultStatus();
|
|
|
|
} else {
|
|
|
|
$default_status = ManiphestTaskStatus::getDefaultClosedStatus();
|
|
|
|
}
|
|
|
|
|
2015-12-04 18:30:53 +01:00
|
|
|
if ($object->getOwnerPHID()) {
|
|
|
|
$owner_value = array($object->getOwnerPHID());
|
|
|
|
} else {
|
|
|
|
$owner_value = array($this->getViewer()->getPHID());
|
|
|
|
}
|
|
|
|
|
2016-04-06 13:37:01 +02:00
|
|
|
$column_documentation = pht(<<<EODOCS
|
|
|
|
You can use this transaction type to create a task into a particular workboard
|
|
|
|
column, or move an existing task between columns.
|
|
|
|
|
|
|
|
The transaction value can be specified in several forms. Some are simpler but
|
|
|
|
less powerful, while others are more complex and more powerful.
|
|
|
|
|
|
|
|
The simplest valid value is a single column PHID:
|
|
|
|
|
|
|
|
```lang=json
|
|
|
|
"PHID-PCOL-1111"
|
|
|
|
```
|
|
|
|
|
|
|
|
This will move the task into that column, or create the task into that column
|
|
|
|
if you are creating a new task. If the task is currently on the board, it will
|
|
|
|
be moved out of any exclusive columns. If the task is not currently on the
|
|
|
|
board, it will be added to the board.
|
|
|
|
|
|
|
|
You can also perform multiple moves at the same time by passing a list of
|
|
|
|
PHIDs:
|
|
|
|
|
|
|
|
```lang=json
|
|
|
|
["PHID-PCOL-2222", "PHID-PCOL-3333"]
|
|
|
|
```
|
|
|
|
|
|
|
|
This is equivalent to performing each move individually.
|
|
|
|
|
|
|
|
The most complex and most powerful form uses a dictionary to provide additional
|
|
|
|
information about the move, including an optional specific position within the
|
|
|
|
column.
|
|
|
|
|
|
|
|
The target column should be identified as `columnPHID`, and you may select a
|
|
|
|
position by passing either `beforePHID` or `afterPHID`, specifying the PHID of
|
|
|
|
a task currently in the column that you want to move this task before or after:
|
|
|
|
|
|
|
|
```lang=json
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"columnPHID": "PHID-PCOL-4444",
|
|
|
|
"beforePHID": "PHID-TASK-5555"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that this affects only the "natural" position of the task. The task
|
|
|
|
position when the board is sorted by some other attribute (like priority)
|
|
|
|
depends on that attribute value: change a task's priority to move it on
|
|
|
|
priority-sorted boards.
|
|
|
|
EODOCS
|
|
|
|
);
|
|
|
|
|
2016-04-06 14:59:09 +02:00
|
|
|
$column_map = $this->getColumnMap($object);
|
|
|
|
|
2016-02-09 00:24:52 +01:00
|
|
|
$fields = array(
|
2015-12-08 16:54:01 +01:00
|
|
|
id(new PhabricatorHandlesEditField())
|
|
|
|
->setKey('parent')
|
|
|
|
->setLabel(pht('Parent Task'))
|
|
|
|
->setDescription(pht('Task to make this a subtask of.'))
|
2015-12-16 14:09:21 +01:00
|
|
|
->setConduitDescription(pht('Create as a subtask of another task.'))
|
|
|
|
->setConduitTypeDescription(pht('PHID of the parent task.'))
|
2015-12-08 16:54:01 +01:00
|
|
|
->setAliases(array('parentPHID'))
|
|
|
|
->setTransactionType(ManiphestTransaction::TYPE_PARENT)
|
2015-12-09 01:30:49 +01:00
|
|
|
->setHandleParameterType(new ManiphestTaskListHTTPParameterType())
|
|
|
|
->setSingleValue(null)
|
|
|
|
->setIsReorderable(false)
|
|
|
|
->setIsDefaultable(false)
|
|
|
|
->setIsLockable(false),
|
2016-04-06 13:37:01 +02:00
|
|
|
id(new PhabricatorColumnsEditField())
|
2015-12-09 01:30:49 +01:00
|
|
|
->setKey('column')
|
|
|
|
->setLabel(pht('Column'))
|
2016-04-06 11:52:17 +02:00
|
|
|
->setDescription(pht('Create a task in a workboard column.'))
|
|
|
|
->setConduitDescription(
|
|
|
|
pht('Move a task to one or more workboard columns.'))
|
|
|
|
->setConduitTypeDescription(
|
2016-04-06 13:37:01 +02:00
|
|
|
pht('List of columns to move the task to.'))
|
|
|
|
->setConduitDocumentation($column_documentation)
|
2016-04-06 11:52:17 +02:00
|
|
|
->setAliases(array('columnPHID', 'columns', 'columnPHIDs'))
|
|
|
|
->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS)
|
2015-12-09 01:30:49 +01:00
|
|
|
->setIsReorderable(false)
|
|
|
|
->setIsDefaultable(false)
|
2016-04-06 14:59:09 +02:00
|
|
|
->setIsLockable(false)
|
|
|
|
->setCommentActionLabel(pht('Move on Workboard'))
|
|
|
|
->setColumnMap($column_map),
|
2015-12-03 23:32:40 +01:00
|
|
|
id(new PhabricatorTextEditField())
|
|
|
|
->setKey('title')
|
|
|
|
->setLabel(pht('Title'))
|
2015-12-04 15:37:36 +01:00
|
|
|
->setDescription(pht('Name of the task.'))
|
2015-12-16 14:09:21 +01:00
|
|
|
->setConduitDescription(pht('Rename the task.'))
|
|
|
|
->setConduitTypeDescription(pht('New task name.'))
|
2015-12-03 23:32:40 +01:00
|
|
|
->setTransactionType(ManiphestTransaction::TYPE_TITLE)
|
|
|
|
->setIsRequired(true)
|
|
|
|
->setValue($object->getTitle()),
|
|
|
|
id(new PhabricatorUsersEditField())
|
2015-12-04 16:21:50 +01:00
|
|
|
->setKey('owner')
|
|
|
|
->setAliases(array('ownerPHID', 'assign', 'assigned'))
|
2015-12-03 23:32:40 +01:00
|
|
|
->setLabel(pht('Assigned To'))
|
|
|
|
->setDescription(pht('User who is responsible for the task.'))
|
2015-12-16 14:09:21 +01:00
|
|
|
->setConduitDescription(pht('Reassign the task.'))
|
|
|
|
->setConduitTypeDescription(
|
|
|
|
pht('New task owner, or `null` to unassign.'))
|
2015-12-03 23:32:40 +01:00
|
|
|
->setTransactionType(ManiphestTransaction::TYPE_OWNER)
|
2015-12-07 20:37:51 +01:00
|
|
|
->setIsCopyable(true)
|
2015-12-04 18:30:53 +01:00
|
|
|
->setSingleValue($object->getOwnerPHID())
|
2015-12-07 19:38:32 +01:00
|
|
|
->setCommentActionLabel(pht('Assign / Claim'))
|
2015-12-15 15:57:32 +01:00
|
|
|
->setCommentActionValue($owner_value),
|
2015-12-08 23:29:53 +01:00
|
|
|
id(new PhabricatorSelectEditField())
|
|
|
|
->setKey('status')
|
|
|
|
->setLabel(pht('Status'))
|
|
|
|
->setDescription(pht('Status of the task.'))
|
2015-12-16 14:09:21 +01:00
|
|
|
->setConduitDescription(pht('Change the task status.'))
|
|
|
|
->setConduitTypeDescription(pht('New task status constant.'))
|
2015-12-08 23:29:53 +01:00
|
|
|
->setTransactionType(ManiphestTransaction::TYPE_STATUS)
|
|
|
|
->setIsCopyable(true)
|
|
|
|
->setValue($object->getStatus())
|
|
|
|
->setOptions($status_map)
|
|
|
|
->setCommentActionLabel(pht('Change Status'))
|
2015-12-15 15:57:32 +01:00
|
|
|
->setCommentActionValue($default_status),
|
2015-12-03 23:32:40 +01:00
|
|
|
id(new PhabricatorSelectEditField())
|
|
|
|
->setKey('priority')
|
|
|
|
->setLabel(pht('Priority'))
|
|
|
|
->setDescription(pht('Priority of the task.'))
|
2015-12-16 14:09:21 +01:00
|
|
|
->setConduitDescription(pht('Change the priority of the task.'))
|
|
|
|
->setConduitTypeDescription(pht('New task priority constant.'))
|
2015-12-03 23:32:40 +01:00
|
|
|
->setTransactionType(ManiphestTransaction::TYPE_PRIORITY)
|
2015-12-07 20:37:51 +01:00
|
|
|
->setIsCopyable(true)
|
2015-12-03 23:32:40 +01:00
|
|
|
->setValue($object->getPriority())
|
2015-12-04 16:56:03 +01:00
|
|
|
->setOptions($priority_map)
|
2015-12-23 22:18:32 +01:00
|
|
|
->setCommentActionLabel(pht('Change Priority')),
|
2015-12-03 23:32:40 +01:00
|
|
|
);
|
2016-02-09 00:24:52 +01:00
|
|
|
|
|
|
|
if (ManiphestTaskPoints::getIsEnabled()) {
|
|
|
|
$points_label = ManiphestTaskPoints::getPointsLabel();
|
|
|
|
$action_label = ManiphestTaskPoints::getPointsActionLabel();
|
|
|
|
|
|
|
|
$fields[] = id(new PhabricatorPointsEditField())
|
|
|
|
->setKey('points')
|
|
|
|
->setLabel($points_label)
|
|
|
|
->setDescription(pht('Point value of the task.'))
|
|
|
|
->setConduitDescription(pht('Change the task point value.'))
|
|
|
|
->setConduitTypeDescription(pht('New task point value.'))
|
|
|
|
->setTransactionType(ManiphestTransaction::TYPE_POINTS)
|
|
|
|
->setIsCopyable(true)
|
|
|
|
->setValue($object->getPoints())
|
|
|
|
->setCommentActionLabel($action_label);
|
|
|
|
}
|
|
|
|
|
|
|
|
$fields[] = id(new PhabricatorRemarkupEditField())
|
|
|
|
->setKey('description')
|
|
|
|
->setLabel(pht('Description'))
|
|
|
|
->setDescription(pht('Task description.'))
|
|
|
|
->setConduitDescription(pht('Update the task description.'))
|
|
|
|
->setConduitTypeDescription(pht('New task description.'))
|
|
|
|
->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION)
|
|
|
|
->setValue($object->getDescription())
|
|
|
|
->setPreviewPanel(
|
|
|
|
id(new PHUIRemarkupPreviewPanel())
|
|
|
|
->setHeader(pht('Description Preview')));
|
|
|
|
|
|
|
|
return $fields;
|
2015-12-03 23:32:40 +01:00
|
|
|
}
|
|
|
|
|
2015-12-05 19:40:56 +01:00
|
|
|
private function getTaskStatusMap(ManiphestTask $task) {
|
|
|
|
$status_map = ManiphestTaskStatus::getTaskStatusMap();
|
|
|
|
|
|
|
|
$current_status = $task->getStatus();
|
|
|
|
|
|
|
|
// If the current status is something we don't recognize (maybe an older
|
|
|
|
// status which was deleted), put a dummy entry in the status map so that
|
|
|
|
// saving the form doesn't destroy any data by accident.
|
|
|
|
if (idx($status_map, $current_status) === null) {
|
|
|
|
$status_map[$current_status] = pht('<Unknown: %s>', $current_status);
|
|
|
|
}
|
|
|
|
|
|
|
|
$dup_status = ManiphestTaskStatus::getDuplicateStatus();
|
|
|
|
foreach ($status_map as $status => $status_name) {
|
|
|
|
// Always keep the task's current status.
|
|
|
|
if ($status == $current_status) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't allow tasks to be changed directly into "Closed, Duplicate"
|
|
|
|
// status. Instead, you have to merge them. See T4819.
|
|
|
|
if ($status == $dup_status) {
|
|
|
|
unset($status_map[$status]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't let new or existing tasks be moved into a disabled status.
|
|
|
|
if (ManiphestTaskStatus::isDisabledStatus($status)) {
|
|
|
|
unset($status_map[$status]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $status_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getTaskPriorityMap(ManiphestTask $task) {
|
|
|
|
$priority_map = ManiphestTaskPriority::getTaskPriorityMap();
|
|
|
|
$current_priority = $task->getPriority();
|
|
|
|
|
|
|
|
// If the current value isn't a legitimate one, put it in the dropdown
|
|
|
|
// anyway so saving the form doesn't cause a side effects.
|
|
|
|
if (idx($priority_map, $current_priority) === null) {
|
|
|
|
$priority_map[$current_priority] = pht(
|
|
|
|
'<Unknown: %s>',
|
|
|
|
$current_priority);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($priority_map as $priority => $priority_name) {
|
|
|
|
// Always keep the current priority.
|
|
|
|
if ($priority == $current_priority) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ManiphestTaskPriority::isDisabledPriority($priority)) {
|
|
|
|
unset($priority_map[$priority]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $priority_map;
|
|
|
|
}
|
|
|
|
|
2015-12-08 23:29:53 +01:00
|
|
|
protected function newEditResponse(
|
|
|
|
AphrontRequest $request,
|
|
|
|
$object,
|
|
|
|
array $xactions) {
|
|
|
|
|
|
|
|
if ($request->isAjax()) {
|
|
|
|
// Reload the task to make sure we pick up the final task state.
|
2015-12-08 23:46:16 +01:00
|
|
|
$viewer = $this->getViewer();
|
2015-12-08 23:29:53 +01:00
|
|
|
$task = id(new ManiphestTaskQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withIDs(array($object->getID()))
|
|
|
|
->needSubscriberPHIDs(true)
|
|
|
|
->needProjectPHIDs(true)
|
|
|
|
->executeOne();
|
|
|
|
|
2015-12-08 23:46:16 +01:00
|
|
|
switch ($request->getStr('responseType')) {
|
|
|
|
case 'card':
|
|
|
|
return $this->buildCardResponse($task);
|
|
|
|
default:
|
|
|
|
return $this->buildListResponse($task);
|
|
|
|
}
|
2015-12-08 23:29:53 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-12-09 01:30:49 +01:00
|
|
|
return parent::newEditResponse($request, $object, $xactions);
|
2015-12-08 23:29:53 +01:00
|
|
|
}
|
|
|
|
|
2015-12-08 23:46:16 +01:00
|
|
|
private function buildListResponse(ManiphestTask $task) {
|
|
|
|
$controller = $this->getController();
|
|
|
|
|
|
|
|
$payload = array(
|
|
|
|
'tasks' => $controller->renderSingleTask($task),
|
|
|
|
'data' => array(),
|
|
|
|
);
|
|
|
|
|
|
|
|
return id(new AphrontAjaxResponse())->setContent($payload);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildCardResponse(ManiphestTask $task) {
|
|
|
|
$controller = $this->getController();
|
|
|
|
$request = $controller->getRequest();
|
|
|
|
$viewer = $request->getViewer();
|
|
|
|
|
|
|
|
$column_phid = $request->getStr('columnPHID');
|
2016-02-10 14:59:46 +01:00
|
|
|
|
|
|
|
$visible_phids = $request->getStrList('visiblePHIDs');
|
|
|
|
if (!$visible_phids) {
|
|
|
|
$visible_phids = array();
|
|
|
|
}
|
2015-12-08 23:46:16 +01:00
|
|
|
|
|
|
|
$column = id(new PhabricatorProjectColumnQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withPHIDs(array($column_phid))
|
|
|
|
->executeOne();
|
|
|
|
if (!$column) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
2016-02-09 02:31:21 +01:00
|
|
|
$board_phid = $column->getProjectPHID();
|
2016-02-10 14:59:46 +01:00
|
|
|
$object_phid = $task->getPHID();
|
2016-02-09 02:31:21 +01:00
|
|
|
|
2016-02-10 14:59:46 +01:00
|
|
|
return id(new PhabricatorBoardResponseEngine())
|
2015-12-08 23:46:16 +01:00
|
|
|
->setViewer($viewer)
|
2016-02-10 14:59:46 +01:00
|
|
|
->setBoardPHID($board_phid)
|
|
|
|
->setObjectPHID($object_phid)
|
|
|
|
->setVisiblePHIDs($visible_phids)
|
|
|
|
->buildResponse();
|
2015-12-08 23:46:16 +01:00
|
|
|
}
|
|
|
|
|
2016-04-06 14:59:09 +02:00
|
|
|
private function getColumnMap(ManiphestTask $task) {
|
|
|
|
$phid = $task->getPHID();
|
|
|
|
if (!$phid) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
|
|
|
$board_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
|
|
|
$phid,
|
|
|
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
|
|
|
if (!$board_phids) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
|
|
|
|
$layout_engine = id(new PhabricatorBoardLayoutEngine())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->setBoardPHIDs($board_phids)
|
|
|
|
->setObjectPHIDs(array($task->getPHID()))
|
|
|
|
->setFetchAllBoards(true)
|
|
|
|
->executeLayout();
|
|
|
|
|
|
|
|
$map = array();
|
|
|
|
foreach ($board_phids as $board_phid) {
|
|
|
|
$in_columns = $layout_engine->getObjectColumns($board_phid, $phid);
|
|
|
|
$in_columns = mpull($in_columns, null, 'getPHID');
|
|
|
|
|
|
|
|
$all_columns = $layout_engine->getColumns($board_phid);
|
|
|
|
$options = array();
|
|
|
|
foreach ($all_columns as $column) {
|
|
|
|
$name = $column->getDisplayName();
|
|
|
|
|
|
|
|
$is_hidden = $column->isHidden();
|
|
|
|
$is_selected = isset($in_columns[$column->getPHID()]);
|
|
|
|
|
|
|
|
// Don't show hidden, subproject or milestone columns in this map
|
|
|
|
// unless the object is currently in the column.
|
|
|
|
$skip_column = ($is_hidden || $column->getProxyPHID());
|
|
|
|
if ($skip_column) {
|
|
|
|
if (!$is_selected) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($is_hidden) {
|
|
|
|
$name = pht('(%s)', $name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($is_selected) {
|
|
|
|
$name = pht("\xE2\x97\x8F %s", $name);
|
|
|
|
} else {
|
|
|
|
$name = pht("\xE2\x97\x8B %s", $name);
|
|
|
|
}
|
|
|
|
|
|
|
|
$option = array(
|
|
|
|
'key' => $column->getPHID(),
|
|
|
|
'label' => $name,
|
|
|
|
'selected' => (bool)$is_selected,
|
|
|
|
);
|
|
|
|
|
|
|
|
$options[] = $option;
|
|
|
|
}
|
|
|
|
|
|
|
|
$map[] = array(
|
|
|
|
'label' => head($all_columns)->getProject()->getDisplayName(),
|
|
|
|
'options' => $options,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$map = isort($map, 'label');
|
|
|
|
$map = array_values($map);
|
|
|
|
|
|
|
|
return $map;
|
|
|
|
}
|
|
|
|
|
2015-12-08 23:46:16 +01:00
|
|
|
|
2015-12-03 23:32:40 +01:00
|
|
|
}
|