2011-02-08 19:53:59 +01:00
|
|
|
<?php
|
|
|
|
|
2012-03-10 00:46:25 +01:00
|
|
|
final class ManiphestTaskDetailController extends ManiphestController {
|
2011-02-08 19:53:59 +01:00
|
|
|
|
|
|
|
private $id;
|
|
|
|
|
2013-09-25 22:44:52 +02:00
|
|
|
public function shouldAllowPublic() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
public function willProcessRequest(array $data) {
|
|
|
|
$this->id = $data['id'];
|
|
|
|
}
|
|
|
|
|
|
|
|
public function processRequest() {
|
|
|
|
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
|
|
|
$e_title = null;
|
|
|
|
|
|
|
|
$priority_map = ManiphestTaskPriority::getTaskPriorityMap();
|
|
|
|
|
2013-09-25 22:44:14 +02:00
|
|
|
$task = id(new ManiphestTaskQuery())
|
|
|
|
->setViewer($user)
|
|
|
|
->withIDs(array($this->id))
|
|
|
|
->executeOne();
|
2011-03-31 06:38:24 +02:00
|
|
|
if (!$task) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
2011-02-08 19:53:59 +01:00
|
|
|
|
2011-08-03 23:20:05 +02:00
|
|
|
$workflow = $request->getStr('workflow');
|
|
|
|
$parent_task = null;
|
|
|
|
if ($workflow && is_numeric($workflow)) {
|
2013-09-25 22:44:14 +02:00
|
|
|
$parent_task = id(new ManiphestTaskQuery())
|
|
|
|
->setViewer($user)
|
|
|
|
->withIDs(array($workflow))
|
|
|
|
->executeOne();
|
2011-08-03 23:20:05 +02:00
|
|
|
}
|
|
|
|
|
2013-09-23 23:31:03 +02:00
|
|
|
$transactions = id(new ManiphestTransactionQuery())
|
|
|
|
->setViewer($user)
|
|
|
|
->withObjectPHIDs(array($task->getPHID()))
|
|
|
|
->needComments(true)
|
|
|
|
->execute();
|
2011-02-08 19:53:59 +01:00
|
|
|
|
2013-09-17 00:58:35 +02:00
|
|
|
$field_list = PhabricatorCustomField::getObjectFields(
|
|
|
|
$task,
|
|
|
|
PhabricatorCustomField::ROLE_VIEW);
|
2014-02-21 23:44:01 +01:00
|
|
|
$field_list
|
|
|
|
->setViewer($user)
|
|
|
|
->readFieldsFromStorage($task);
|
2013-03-08 02:24:58 +01:00
|
|
|
|
2012-07-19 05:41:42 +02:00
|
|
|
$e_commit = PhabricatorEdgeConfig::TYPE_TASK_HAS_COMMIT;
|
|
|
|
$e_dep_on = PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK;
|
|
|
|
$e_dep_by = PhabricatorEdgeConfig::TYPE_TASK_DEPENDED_ON_BY_TASK;
|
2012-07-20 17:59:39 +02:00
|
|
|
$e_rev = PhabricatorEdgeConfig::TYPE_TASK_HAS_RELATED_DREV;
|
2013-07-20 00:59:29 +02:00
|
|
|
$e_mock = PhabricatorEdgeConfig::TYPE_TASK_HAS_MOCK;
|
2012-07-19 05:41:42 +02:00
|
|
|
|
|
|
|
$phid = $task->getPHID();
|
|
|
|
|
|
|
|
$query = id(new PhabricatorEdgeQuery())
|
|
|
|
->withSourcePHIDs(array($phid))
|
|
|
|
->withEdgeTypes(
|
|
|
|
array(
|
|
|
|
$e_commit,
|
|
|
|
$e_dep_on,
|
|
|
|
$e_dep_by,
|
2012-07-20 17:59:39 +02:00
|
|
|
$e_rev,
|
2013-07-20 00:59:29 +02:00
|
|
|
$e_mock,
|
2012-07-19 05:41:42 +02:00
|
|
|
));
|
2012-12-11 23:03:16 +01:00
|
|
|
$edges = idx($query->execute(), $phid);
|
2012-07-19 05:41:42 +02:00
|
|
|
$phids = array_fill_keys($query->getDestinationPHIDs(), true);
|
2012-04-05 02:34:25 +02:00
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
foreach ($task->getCCPHIDs() as $phid) {
|
|
|
|
$phids[$phid] = true;
|
|
|
|
}
|
2011-02-21 05:08:16 +01:00
|
|
|
foreach ($task->getProjectPHIDs() as $phid) {
|
|
|
|
$phids[$phid] = true;
|
|
|
|
}
|
2011-02-08 19:53:59 +01:00
|
|
|
if ($task->getOwnerPHID()) {
|
|
|
|
$phids[$task->getOwnerPHID()] = true;
|
|
|
|
}
|
|
|
|
$phids[$task->getAuthorPHID()] = true;
|
|
|
|
|
2011-02-17 23:32:01 +01:00
|
|
|
$attached = $task->getAttached();
|
|
|
|
foreach ($attached as $type => $list) {
|
|
|
|
foreach ($list as $phid => $info) {
|
|
|
|
$phids[$phid] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-03 23:20:05 +02:00
|
|
|
if ($parent_task) {
|
|
|
|
$phids[$parent_task->getPHID()] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$phids = array_keys($phids);
|
2013-03-08 02:24:58 +01:00
|
|
|
|
2012-12-11 23:03:16 +01:00
|
|
|
$this->loadHandles($phids);
|
2011-02-21 05:08:16 +01:00
|
|
|
|
2013-03-08 02:24:58 +01:00
|
|
|
$handles = $this->getLoadedHandles();
|
|
|
|
|
Tweak style on "Create Another Task" button
Summary:
Not totally sure I'm in love with this but I think it's somewhat non-terrible,
despite the lack of lens flare.
Also made "Cancel" take you back to the task if you got to "Create" from "Create
Another Task".
Test Plan:
- Style:
https://secure.phabricator.com/file/view/PHID-FILE-ad37d3c1f3b2c7a7a7d1/
- Hit "Cancel" from "Create Another", got sent back to task.
- Hit "Cancel" from normal create, got sent back to list.
- Tried to save an invalid task after making changes to CC/Projects, changes
were preserved.
Reviewed By: codeblock
Reviewers: hunterbridges, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock
Differential Revision: 736
2011-07-27 19:46:22 +02:00
|
|
|
$context_bar = null;
|
2011-08-03 23:20:05 +02:00
|
|
|
|
|
|
|
if ($parent_task) {
|
|
|
|
$context_bar = new AphrontContextBarView();
|
2013-02-13 23:50:15 +01:00
|
|
|
$context_bar->addButton(phutil_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '/maniphest/task/create/?parent='.$parent_task->getID(),
|
|
|
|
'class' => 'green button',
|
|
|
|
),
|
2013-03-13 07:30:03 +01:00
|
|
|
pht('Create Another Subtask')));
|
2013-02-13 23:50:15 +01:00
|
|
|
$context_bar->appendChild(hsprintf(
|
|
|
|
'Created a subtask of <strong>%s</strong>',
|
|
|
|
$this->getHandle($parent_task->getPHID())->renderLink()));
|
2011-08-03 23:20:05 +02:00
|
|
|
} else if ($workflow == 'create') {
|
Tweak style on "Create Another Task" button
Summary:
Not totally sure I'm in love with this but I think it's somewhat non-terrible,
despite the lack of lens flare.
Also made "Cancel" take you back to the task if you got to "Create" from "Create
Another Task".
Test Plan:
- Style:
https://secure.phabricator.com/file/view/PHID-FILE-ad37d3c1f3b2c7a7a7d1/
- Hit "Cancel" from "Create Another", got sent back to task.
- Hit "Cancel" from normal create, got sent back to list.
- Tried to save an invalid task after making changes to CC/Projects, changes
were preserved.
Reviewed By: codeblock
Reviewers: hunterbridges, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock
Differential Revision: 736
2011-07-27 19:46:22 +02:00
|
|
|
$context_bar = new AphrontContextBarView();
|
2013-02-13 23:50:15 +01:00
|
|
|
$context_bar->addButton(phutil_tag('label', array(), 'Create Another'));
|
|
|
|
$context_bar->addButton(phutil_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '/maniphest/task/create/?template='.$task->getID(),
|
|
|
|
'class' => 'green button',
|
|
|
|
),
|
2013-03-13 07:30:03 +01:00
|
|
|
pht('Similar Task')));
|
2013-02-13 23:50:15 +01:00
|
|
|
$context_bar->addButton(phutil_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '/maniphest/task/create/',
|
|
|
|
'class' => 'green button',
|
|
|
|
),
|
2013-03-13 07:30:03 +01:00
|
|
|
pht('Empty Task')));
|
|
|
|
$context_bar->appendChild(pht('New task created.'));
|
2011-07-26 19:39:36 +02:00
|
|
|
}
|
2011-02-20 23:15:53 +01:00
|
|
|
|
2012-07-11 20:40:10 +02:00
|
|
|
$engine = new PhabricatorMarkupEngine();
|
2012-09-05 20:40:48 +02:00
|
|
|
$engine->setViewer($user);
|
2012-07-11 20:40:10 +02:00
|
|
|
$engine->addObject($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION);
|
2013-09-23 23:31:03 +02:00
|
|
|
foreach ($transactions as $modern_xaction) {
|
2013-09-23 23:29:40 +02:00
|
|
|
if ($modern_xaction->getComment()) {
|
|
|
|
$engine->addObject(
|
|
|
|
$modern_xaction->getComment(),
|
|
|
|
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
|
2012-07-11 20:40:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-08 02:24:33 +01:00
|
|
|
$engine->process();
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
$resolution_types = ManiphestTaskStatus::getTaskStatusMap();
|
|
|
|
|
2013-09-25 20:16:43 +02:00
|
|
|
$transaction_types = array(
|
|
|
|
PhabricatorTransactions::TYPE_COMMENT => pht('Comment'),
|
2014-03-25 21:47:42 +01:00
|
|
|
ManiphestTransaction::TYPE_STATUS => pht('Change Status'),
|
2013-09-25 20:16:43 +02:00
|
|
|
ManiphestTransaction::TYPE_OWNER => pht('Reassign / Claim'),
|
|
|
|
ManiphestTransaction::TYPE_CCS => pht('Add CCs'),
|
|
|
|
ManiphestTransaction::TYPE_PRIORITY => pht('Change Priority'),
|
|
|
|
ManiphestTransaction::TYPE_PROJECTS => pht('Associate Projects'),
|
|
|
|
);
|
|
|
|
|
Add capabilities for editing task triage details (priority, assignee, etc)
Summary:
This is primarily a client request, and a little bit use-case specific, but policies seem to be holding up well and I'm getting more comfortable about maintaining this. Much if it can run through ApplicationTransactions.
Allow the ability to edit status, policies, priorities, assignees and projects of a task to be restricted to some subset of users. Also allow bulk edit to be locked. This affects the editor itself and the edit, view and list interfaces.
Test Plan: As a restricted user, created, edited and commented on tasks. Tried to drag them around.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Differential Revision: https://secure.phabricator.com/D7357
2013-10-22 01:59:06 +02:00
|
|
|
// Remove actions the user doesn't have permission to take.
|
|
|
|
|
|
|
|
$requires = array(
|
|
|
|
ManiphestTransaction::TYPE_OWNER =>
|
|
|
|
ManiphestCapabilityEditAssign::CAPABILITY,
|
|
|
|
ManiphestTransaction::TYPE_PRIORITY =>
|
|
|
|
ManiphestCapabilityEditPriority::CAPABILITY,
|
|
|
|
ManiphestTransaction::TYPE_PROJECTS =>
|
|
|
|
ManiphestCapabilityEditProjects::CAPABILITY,
|
|
|
|
ManiphestTransaction::TYPE_STATUS =>
|
|
|
|
ManiphestCapabilityEditStatus::CAPABILITY,
|
|
|
|
);
|
|
|
|
|
|
|
|
foreach ($transaction_types as $type => $name) {
|
|
|
|
if (isset($requires[$type])) {
|
|
|
|
if (!$this->hasApplicationCapability($requires[$type])) {
|
|
|
|
unset($transaction_types[$type]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-25 21:47:42 +01:00
|
|
|
// Don't show an option to change to the current status, or to change to
|
|
|
|
// the duplicate status explicitly.
|
|
|
|
unset($resolution_types[$task->getStatus()]);
|
|
|
|
unset($resolution_types[ManiphestTaskStatus::getDuplicateStatus()]);
|
|
|
|
|
|
|
|
// Don't show owner/priority changes for closed tasks, as they don't make
|
|
|
|
// much sense.
|
|
|
|
if ($task->isClosed()) {
|
2013-09-25 20:16:43 +02:00
|
|
|
unset($transaction_types[ManiphestTransaction::TYPE_PRIORITY]);
|
|
|
|
unset($transaction_types[ManiphestTransaction::TYPE_OWNER]);
|
2011-02-08 19:53:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$default_claim = array(
|
|
|
|
$user->getPHID() => $user->getUsername().' ('.$user->getRealName().')',
|
|
|
|
);
|
|
|
|
|
2011-05-11 01:18:47 +02:00
|
|
|
$draft = id(new PhabricatorDraft())->loadOneWhere(
|
|
|
|
'authorPHID = %s AND draftKey = %s',
|
|
|
|
$user->getPHID(),
|
|
|
|
$task->getPHID());
|
|
|
|
if ($draft) {
|
|
|
|
$draft_text = $draft->getDraft();
|
|
|
|
} else {
|
|
|
|
$draft_text = null;
|
|
|
|
}
|
|
|
|
|
Provide a configuration flag to disable silliness in the UI
Summary: See comments. A few installs have remarked that their organizations
would prefer buttons labled "Submit" to buttons labeled "Clowncopterize".
Test Plan:
- In "serious" mode, verified Differential and Maniphest have serious strings,
tasks can not be closed out of spite, and reset/welcome emails are extremely
serious.
- In unserious mode, verified Differential and Maniphest have normal strings,
tasks can be closed out of spite, and reset/welcome emails are silly.
- This does not disable the "fax these changes" message in Arcanist (no
reasonable way for it to read the config value) or the rainbow syntax
highlighter (already removable though configuration).
Reviewers: moskov, jungejason, nh, tuomaspelkonen, aran
Reviewed By: moskov
CC: aran, moskov
Differential Revision: 1081
2011-11-04 23:16:34 +01:00
|
|
|
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
|
|
|
|
2014-03-25 22:20:25 +01:00
|
|
|
$submit_text = $is_serious
|
|
|
|
? pht('Submit')
|
|
|
|
: pht('Avast!');
|
|
|
|
|
|
|
|
$close_text = $is_serious
|
|
|
|
? pht('Close Task')
|
|
|
|
: pht('Scuttle Task');
|
|
|
|
|
|
|
|
$submit_control = id(new PHUIFormMultiSubmitControl());
|
|
|
|
if (!$task->isClosed()) {
|
|
|
|
$close_image = id(new PHUIIconView())
|
|
|
|
->setSpriteSheet(PHUIIconView::SPRITE_ICONS)
|
|
|
|
->setSpriteIcon('check');
|
|
|
|
$submit_control->addButtonView(
|
|
|
|
id(new PHUIButtonView())
|
|
|
|
->setColor(PHUIButtonView::GREY)
|
|
|
|
->setIcon($close_image)
|
|
|
|
->setText($close_text)
|
|
|
|
->setName('scuttle')
|
|
|
|
->addSigil('alternate-submit-button'));
|
|
|
|
}
|
|
|
|
$submit_control->addSubmitButton($submit_text);
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
$comment_form = new AphrontFormView();
|
|
|
|
$comment_form
|
|
|
|
->setUser($user)
|
2014-03-14 23:13:51 +01:00
|
|
|
->setWorkflow(true)
|
2011-02-08 19:53:59 +01:00
|
|
|
->setAction('/maniphest/transaction/save/')
|
2011-02-21 05:08:16 +01:00
|
|
|
->setEncType('multipart/form-data')
|
2011-02-08 19:53:59 +01:00
|
|
|
->addHiddenInput('taskID', $task->getID())
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSelectControl())
|
2013-03-13 07:30:03 +01:00
|
|
|
->setLabel(pht('Action'))
|
2011-02-08 19:53:59 +01:00
|
|
|
->setName('action')
|
|
|
|
->setOptions($transaction_types)
|
|
|
|
->setID('transaction-action'))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSelectControl())
|
2014-03-25 21:47:42 +01:00
|
|
|
->setLabel(pht('Status'))
|
2011-02-08 19:53:59 +01:00
|
|
|
->setName('resolution')
|
|
|
|
->setControlID('resolution')
|
|
|
|
->setControlStyle('display: none')
|
|
|
|
->setOptions($resolution_types))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
2013-03-13 07:30:03 +01:00
|
|
|
->setLabel(pht('Assign To'))
|
2011-02-08 19:53:59 +01:00
|
|
|
->setName('assign_to')
|
|
|
|
->setControlID('assign_to')
|
|
|
|
->setControlStyle('display: none')
|
|
|
|
->setID('assign-tokenizer')
|
|
|
|
->setDisableBehavior(true))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
2013-03-13 07:30:03 +01:00
|
|
|
->setLabel(pht('CCs'))
|
2011-02-08 19:53:59 +01:00
|
|
|
->setName('ccs')
|
|
|
|
->setControlID('ccs')
|
|
|
|
->setControlStyle('display: none')
|
|
|
|
->setID('cc-tokenizer')
|
|
|
|
->setDisableBehavior(true))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSelectControl())
|
2013-03-13 07:30:03 +01:00
|
|
|
->setLabel(pht('Priority'))
|
2011-02-08 19:53:59 +01:00
|
|
|
->setName('priority')
|
|
|
|
->setOptions($priority_map)
|
|
|
|
->setControlID('priority')
|
|
|
|
->setControlStyle('display: none')
|
|
|
|
->setValue($task->getPriority()))
|
2011-02-21 05:08:16 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
2013-03-13 07:30:03 +01:00
|
|
|
->setLabel(pht('Projects'))
|
2011-02-21 05:08:16 +01:00
|
|
|
->setName('projects')
|
|
|
|
->setControlID('projects')
|
|
|
|
->setControlStyle('display: none')
|
|
|
|
->setID('projects-tokenizer')
|
|
|
|
->setDisableBehavior(true))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormFileControl())
|
2013-03-13 07:30:03 +01:00
|
|
|
->setLabel(pht('File'))
|
2011-02-21 05:08:16 +01:00
|
|
|
->setName('file')
|
|
|
|
->setControlID('file')
|
|
|
|
->setControlStyle('display: none'))
|
2011-02-08 19:53:59 +01:00
|
|
|
->appendChild(
|
2012-09-19 21:27:28 +02:00
|
|
|
id(new PhabricatorRemarkupControl())
|
2013-03-13 07:30:03 +01:00
|
|
|
->setLabel(pht('Comments'))
|
2011-02-08 19:53:59 +01:00
|
|
|
->setName('comments')
|
2011-05-11 01:18:47 +02:00
|
|
|
->setValue($draft_text)
|
2012-11-27 23:06:31 +01:00
|
|
|
->setID('transaction-comments')
|
|
|
|
->setUser($user))
|
2014-03-25 22:20:25 +01:00
|
|
|
->appendChild($submit_control);
|
2011-02-08 19:53:59 +01:00
|
|
|
|
2011-05-11 13:17:48 +02:00
|
|
|
$control_map = array(
|
2013-09-25 20:16:43 +02:00
|
|
|
ManiphestTransaction::TYPE_STATUS => 'resolution',
|
|
|
|
ManiphestTransaction::TYPE_OWNER => 'assign_to',
|
|
|
|
ManiphestTransaction::TYPE_CCS => 'ccs',
|
|
|
|
ManiphestTransaction::TYPE_PRIORITY => 'priority',
|
|
|
|
ManiphestTransaction::TYPE_PROJECTS => 'projects',
|
2011-05-11 13:17:48 +02:00
|
|
|
);
|
|
|
|
|
2012-04-14 16:05:58 +02:00
|
|
|
$tokenizer_map = array(
|
2013-09-25 20:16:43 +02:00
|
|
|
ManiphestTransaction::TYPE_PROJECTS => array(
|
2012-04-14 16:05:58 +02:00
|
|
|
'id' => 'projects-tokenizer',
|
|
|
|
'src' => '/typeahead/common/projects/',
|
2013-03-13 07:30:03 +01:00
|
|
|
'placeholder' => pht('Type a project name...'),
|
2012-04-14 16:05:58 +02:00
|
|
|
),
|
2013-09-25 20:16:43 +02:00
|
|
|
ManiphestTransaction::TYPE_OWNER => array(
|
2012-04-14 16:05:58 +02:00
|
|
|
'id' => 'assign-tokenizer',
|
|
|
|
'src' => '/typeahead/common/users/',
|
|
|
|
'value' => $default_claim,
|
|
|
|
'limit' => 1,
|
2013-03-13 07:30:03 +01:00
|
|
|
'placeholder' => pht('Type a user name...'),
|
2012-04-14 16:05:58 +02:00
|
|
|
),
|
2013-09-25 20:16:43 +02:00
|
|
|
ManiphestTransaction::TYPE_CCS => array(
|
2012-04-14 16:05:58 +02:00
|
|
|
'id' => 'cc-tokenizer',
|
|
|
|
'src' => '/typeahead/common/mailable/',
|
2013-03-13 07:30:03 +01:00
|
|
|
'placeholder' => pht('Type a user or mailing list...'),
|
2012-04-14 16:05:58 +02:00
|
|
|
),
|
|
|
|
);
|
|
|
|
|
2013-09-25 22:44:52 +02:00
|
|
|
// TODO: Initializing these behaviors for logged out users fatals things.
|
|
|
|
if ($user->isLoggedIn()) {
|
|
|
|
Javelin::initBehavior('maniphest-transaction-controls', array(
|
|
|
|
'select' => 'transaction-action',
|
|
|
|
'controlMap' => $control_map,
|
|
|
|
'tokenizers' => $tokenizer_map,
|
|
|
|
));
|
|
|
|
|
|
|
|
Javelin::initBehavior('maniphest-transaction-preview', array(
|
|
|
|
'uri' => '/maniphest/transaction/preview/'.$task->getID().'/',
|
|
|
|
'preview' => 'transaction-preview',
|
|
|
|
'comments' => 'transaction-comments',
|
|
|
|
'action' => 'transaction-action',
|
|
|
|
'map' => $control_map,
|
|
|
|
'tokenizers' => $tokenizer_map,
|
|
|
|
));
|
|
|
|
}
|
2011-05-10 17:29:28 +02:00
|
|
|
|
2013-11-22 01:09:04 +01:00
|
|
|
$comment_header = $is_serious
|
|
|
|
? pht('Add Comment')
|
|
|
|
: pht('Weigh In');
|
2011-02-08 19:53:59 +01:00
|
|
|
|
2013-11-11 18:23:23 +01:00
|
|
|
$preview_panel = phutil_tag_div(
|
|
|
|
'aphront-panel-preview',
|
|
|
|
phutil_tag(
|
|
|
|
'div',
|
|
|
|
array('id' => 'transaction-preview'),
|
|
|
|
phutil_tag_div(
|
|
|
|
'aphront-panel-preview-loading-text',
|
|
|
|
pht('Loading preview...'))));
|
2011-05-10 17:29:28 +02:00
|
|
|
|
2013-09-23 23:29:40 +02:00
|
|
|
$timeline = id(new PhabricatorApplicationTransactionView())
|
|
|
|
->setUser($user)
|
|
|
|
->setObjectPHID($task->getPHID())
|
2013-09-23 23:31:03 +02:00
|
|
|
->setTransactions($transactions)
|
2013-09-23 23:29:40 +02:00
|
|
|
->setMarkupEngine($engine);
|
2011-02-08 19:53:59 +01:00
|
|
|
|
2012-12-11 23:03:16 +01:00
|
|
|
$object_name = 'T'.$task->getID();
|
2013-04-09 21:42:03 +02:00
|
|
|
$actions = $this->buildActionView($task);
|
2012-12-11 23:03:16 +01:00
|
|
|
|
2013-12-19 02:47:34 +01:00
|
|
|
$crumbs = $this->buildApplicationCrumbs()
|
|
|
|
->addTextCrumb($object_name, '/'.$object_name)
|
2013-09-13 20:29:08 +02:00
|
|
|
->setActionList($actions);
|
2012-12-11 23:03:16 +01:00
|
|
|
|
|
|
|
$header = $this->buildHeaderView($task);
|
2013-10-11 16:53:56 +02:00
|
|
|
$properties = $this->buildPropertyView(
|
|
|
|
$task, $field_list, $edges, $actions);
|
|
|
|
$description = $this->buildDescriptionView($task, $engine);
|
2012-12-11 23:03:16 +01:00
|
|
|
|
2013-09-25 22:44:52 +02:00
|
|
|
if (!$user->isLoggedIn()) {
|
|
|
|
// TODO: Eventually, everything should run through this. For now, we're
|
|
|
|
// only using it to get a consistent "Login to Comment" button.
|
2013-12-05 17:44:43 +01:00
|
|
|
$comment_box = id(new PhabricatorApplicationTransactionCommentView())
|
2013-09-25 22:44:52 +02:00
|
|
|
->setUser($user)
|
|
|
|
->setRequestURI($request->getRequestURI());
|
|
|
|
$preview_panel = null;
|
2013-12-05 17:44:43 +01:00
|
|
|
} else {
|
|
|
|
$comment_box = id(new PHUIObjectBoxView())
|
|
|
|
->setFlush(true)
|
|
|
|
->setHeaderText($comment_header)
|
|
|
|
->appendChild($comment_form);
|
2013-09-25 22:44:52 +02:00
|
|
|
}
|
|
|
|
|
2013-09-29 00:55:38 +02:00
|
|
|
$object_box = id(new PHUIObjectBoxView())
|
|
|
|
->setHeader($header)
|
2013-10-11 16:53:56 +02:00
|
|
|
->addPropertyList($properties);
|
|
|
|
|
|
|
|
if ($description) {
|
|
|
|
$object_box->addPropertyList($description);
|
|
|
|
}
|
2013-09-29 00:55:38 +02:00
|
|
|
|
2012-12-11 23:03:16 +01:00
|
|
|
return $this->buildApplicationPage(
|
2011-02-08 19:53:59 +01:00
|
|
|
array(
|
2012-12-11 23:03:16 +01:00
|
|
|
$crumbs,
|
Tweak style on "Create Another Task" button
Summary:
Not totally sure I'm in love with this but I think it's somewhat non-terrible,
despite the lack of lens flare.
Also made "Cancel" take you back to the task if you got to "Create" from "Create
Another Task".
Test Plan:
- Style:
https://secure.phabricator.com/file/view/PHID-FILE-ad37d3c1f3b2c7a7a7d1/
- Hit "Cancel" from "Create Another", got sent back to task.
- Hit "Cancel" from normal create, got sent back to list.
- Tried to save an invalid task after making changes to CC/Projects, changes
were preserved.
Reviewed By: codeblock
Reviewers: hunterbridges, jungejason, tuomaspelkonen, aran, codeblock
CC: aran, epriestley, codeblock
Differential Revision: 736
2011-07-27 19:46:22 +02:00
|
|
|
$context_bar,
|
2013-09-29 00:55:38 +02:00
|
|
|
$object_box,
|
2013-09-23 23:29:40 +02:00
|
|
|
$timeline,
|
2013-09-29 00:55:38 +02:00
|
|
|
$comment_box,
|
2011-05-10 17:29:28 +02:00
|
|
|
$preview_panel,
|
2011-02-08 19:53:59 +01:00
|
|
|
),
|
|
|
|
array(
|
2011-02-10 01:38:31 +01:00
|
|
|
'title' => 'T'.$task->getID().' '.$task->getTitle(),
|
2012-06-14 02:28:21 +02:00
|
|
|
'pageObjects' => array($task->getPHID()),
|
2013-01-25 01:29:47 +01:00
|
|
|
'device' => true,
|
2011-02-08 19:53:59 +01:00
|
|
|
));
|
|
|
|
}
|
2011-02-19 07:15:28 +01:00
|
|
|
|
2012-12-11 23:03:16 +01:00
|
|
|
private function buildHeaderView(ManiphestTask $task) {
|
2013-09-17 18:12:37 +02:00
|
|
|
$view = id(new PHUIHeaderView())
|
2013-09-25 22:44:45 +02:00
|
|
|
->setHeader($task->getTitle())
|
|
|
|
->setUser($this->getRequest()->getUser())
|
|
|
|
->setPolicyObject($task);
|
2012-12-11 23:03:16 +01:00
|
|
|
|
2013-09-24 17:42:04 +02:00
|
|
|
$status = $task->getStatus();
|
|
|
|
$status_name = ManiphestTaskStatus::renderFullDescription($status);
|
|
|
|
|
|
|
|
$view->addProperty(PHUIHeaderView::PROPERTY_STATUS, $status_name);
|
2012-12-11 23:03:16 +01:00
|
|
|
|
|
|
|
return $view;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function buildActionView(ManiphestTask $task) {
|
|
|
|
$viewer = $this->getRequest()->getUser();
|
2013-05-04 00:47:39 +02:00
|
|
|
$viewer_phid = $viewer->getPHID();
|
|
|
|
$viewer_is_cc = in_array($viewer_phid, $task->getCCPHIDs());
|
2012-12-11 23:03:16 +01:00
|
|
|
|
|
|
|
$id = $task->getID();
|
|
|
|
$phid = $task->getPHID();
|
|
|
|
|
2013-09-25 22:44:52 +02:00
|
|
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
|
|
|
$viewer,
|
|
|
|
$task,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
|
2013-07-12 20:39:47 +02:00
|
|
|
$view = id(new PhabricatorActionListView())
|
|
|
|
->setUser($viewer)
|
|
|
|
->setObject($task)
|
2013-09-25 22:44:52 +02:00
|
|
|
->setObjectURI($this->getRequest()->getRequestURI());
|
|
|
|
|
|
|
|
$view->addAction(
|
2012-12-11 23:03:16 +01:00
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setName(pht('Edit Task'))
|
|
|
|
->setIcon('edit')
|
2013-09-25 22:44:52 +02:00
|
|
|
->setHref($this->getApplicationURI("/task/edit/{$id}/"))
|
|
|
|
->setDisabled(!$can_edit)
|
|
|
|
->setWorkflow(!$can_edit));
|
2012-12-11 23:03:16 +01:00
|
|
|
|
2013-05-04 00:47:39 +02:00
|
|
|
if ($task->getOwnerPHID() === $viewer_phid) {
|
|
|
|
$view->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setName(pht('Automatically Subscribed'))
|
|
|
|
->setDisabled(true)
|
2013-06-07 00:06:08 +02:00
|
|
|
->setIcon('enable'));
|
2013-05-04 00:47:39 +02:00
|
|
|
} else {
|
|
|
|
$action = $viewer_is_cc ? 'rem' : 'add';
|
2013-06-07 00:06:08 +02:00
|
|
|
$name = $viewer_is_cc ? pht('Unsubscribe') : pht('Subscribe');
|
|
|
|
$icon = $viewer_is_cc ? 'disable' : 'check';
|
2013-05-04 00:47:39 +02:00
|
|
|
|
|
|
|
$view->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
2013-06-07 00:06:08 +02:00
|
|
|
->setName($name)
|
2013-05-04 00:47:39 +02:00
|
|
|
->setHref("/maniphest/subscribe/{$action}/{$id}/")
|
|
|
|
->setRenderAsForm(true)
|
|
|
|
->setUser($viewer)
|
|
|
|
->setIcon($icon));
|
|
|
|
}
|
|
|
|
|
2012-12-11 23:03:16 +01:00
|
|
|
$view->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
2013-07-17 19:01:13 +02:00
|
|
|
->setName(pht('Merge Duplicates In'))
|
2012-12-11 23:03:16 +01:00
|
|
|
->setHref("/search/attach/{$phid}/TASK/merge/")
|
|
|
|
->setWorkflow(true)
|
2013-09-25 22:44:52 +02:00
|
|
|
->setIcon('merge')
|
|
|
|
->setDisabled(!$can_edit)
|
2013-10-16 19:35:52 +02:00
|
|
|
->setWorkflow(true));
|
2012-12-11 23:03:16 +01:00
|
|
|
|
|
|
|
$view->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setName(pht('Create Subtask'))
|
|
|
|
->setHref($this->getApplicationURI("/task/create/?parent={$id}"))
|
|
|
|
->setIcon('fork'));
|
|
|
|
|
|
|
|
$view->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setName(pht('Edit Dependencies'))
|
|
|
|
->setHref("/search/attach/{$phid}/TASK/dependencies/")
|
|
|
|
->setWorkflow(true)
|
2013-09-25 22:44:52 +02:00
|
|
|
->setIcon('link')
|
|
|
|
->setDisabled(!$can_edit)
|
2013-10-01 21:01:55 +02:00
|
|
|
->setWorkflow(true));
|
2012-12-11 23:03:16 +01:00
|
|
|
|
|
|
|
return $view;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildPropertyView(
|
|
|
|
ManiphestTask $task,
|
2013-09-17 01:02:27 +02:00
|
|
|
PhabricatorCustomFieldList $field_list,
|
2012-12-11 23:03:16 +01:00
|
|
|
array $edges,
|
2013-10-11 16:53:56 +02:00
|
|
|
PhabricatorActionListView $actions) {
|
2012-12-11 23:03:16 +01:00
|
|
|
|
|
|
|
$viewer = $this->getRequest()->getUser();
|
|
|
|
|
2013-10-11 16:53:56 +02:00
|
|
|
$view = id(new PHUIPropertyListView())
|
2013-02-15 16:47:14 +01:00
|
|
|
->setUser($viewer)
|
2013-10-11 16:53:56 +02:00
|
|
|
->setObject($task)
|
|
|
|
->setActionList($actions);
|
2012-12-11 23:03:16 +01:00
|
|
|
|
|
|
|
$view->addProperty(
|
|
|
|
pht('Assigned To'),
|
|
|
|
$task->getOwnerPHID()
|
2013-07-20 00:59:29 +02:00
|
|
|
? $this->getHandle($task->getOwnerPHID())->renderLink()
|
|
|
|
: phutil_tag('em', array(), pht('None')));
|
2012-12-11 23:03:16 +01:00
|
|
|
|
|
|
|
$view->addProperty(
|
|
|
|
pht('Priority'),
|
2013-01-29 20:01:47 +01:00
|
|
|
ManiphestTaskPriority::getTaskPriorityName($task->getPriority()));
|
2012-12-11 23:03:16 +01:00
|
|
|
|
2014-03-14 19:22:00 +01:00
|
|
|
$handles = $this->getLoadedHandles();
|
|
|
|
$cc_handles = array_select_keys($handles, $task->getCCPHIDs());
|
|
|
|
$subscriber_html = id(new SubscriptionListStringBuilder())
|
|
|
|
->setObjectPHID($task->getPHID())
|
|
|
|
->setHandles($cc_handles)
|
|
|
|
->buildPropertyString();
|
|
|
|
$view->addProperty(pht('Subscribers'), $subscriber_html);
|
2012-12-11 23:03:16 +01:00
|
|
|
|
|
|
|
$view->addProperty(
|
|
|
|
pht('Author'),
|
|
|
|
$this->getHandle($task->getAuthorPHID())->renderLink());
|
|
|
|
|
|
|
|
$source = $task->getOriginalEmailSource();
|
|
|
|
if ($source) {
|
|
|
|
$subject = '[T'.$task->getID().'] '.$task->getTitle();
|
|
|
|
$view->addProperty(
|
|
|
|
pht('From Email'),
|
2013-01-18 03:43:35 +01:00
|
|
|
phutil_tag(
|
2012-12-11 23:03:16 +01:00
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => 'mailto:'.$source.'?subject='.$subject
|
2013-07-20 00:59:29 +02:00
|
|
|
),
|
2013-01-18 03:43:35 +01:00
|
|
|
$source));
|
2012-12-11 23:03:16 +01:00
|
|
|
}
|
|
|
|
|
2014-01-13 21:25:12 +01:00
|
|
|
$project_phids = $task->getProjectPHIDs();
|
|
|
|
if ($project_phids) {
|
2014-02-14 03:12:38 +01:00
|
|
|
require_celerity_resource('maniphest-task-summary-css');
|
|
|
|
|
2014-01-13 21:25:12 +01:00
|
|
|
// If we end up with real-world projects with many hundreds of columns, it
|
|
|
|
// might be better to just load all the edges, then load those columns and
|
|
|
|
// work backward that way, or denormalize this data more.
|
|
|
|
|
|
|
|
$columns = id(new PhabricatorProjectColumnQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withProjectPHIDs($project_phids)
|
|
|
|
->execute();
|
|
|
|
$columns = mpull($columns, null, 'getPHID');
|
|
|
|
|
|
|
|
$column_edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_COLUMN;
|
|
|
|
$all_column_phids = array_keys($columns);
|
|
|
|
|
|
|
|
$column_edge_query = id(new PhabricatorEdgeQuery())
|
|
|
|
->withSourcePHIDs(array($task->getPHID()))
|
|
|
|
->withEdgeTypes(array($column_edge_type))
|
|
|
|
->withDestinationPHIDs($all_column_phids);
|
|
|
|
$column_edge_query->execute();
|
|
|
|
$in_column_phids = array_fuse($column_edge_query->getDestinationPHIDs());
|
|
|
|
|
|
|
|
$column_groups = mgroup($columns, 'getProjectPHID');
|
|
|
|
|
|
|
|
$project_rows = array();
|
|
|
|
foreach ($project_phids as $project_phid) {
|
|
|
|
$row = array();
|
|
|
|
|
|
|
|
$handle = $this->getHandle($project_phid);
|
|
|
|
$row[] = $handle->renderLink();
|
|
|
|
|
|
|
|
$columns = idx($column_groups, $project_phid, array());
|
|
|
|
$column = head(array_intersect_key($columns, $in_column_phids));
|
|
|
|
if ($column) {
|
2014-02-14 03:12:38 +01:00
|
|
|
$column_name = pht('(%s)', $column->getDisplayName());
|
|
|
|
// TODO: This is really hacky but there's no cleaner way to do it
|
|
|
|
// right now, T4022 should give us better tools for this.
|
|
|
|
$column_href = str_replace(
|
|
|
|
'project/view',
|
|
|
|
'project/board',
|
|
|
|
$handle->getURI());
|
|
|
|
$column_link = phutil_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => $column_href,
|
|
|
|
'class' => 'maniphest-board-link',
|
|
|
|
),
|
|
|
|
$column_name);
|
|
|
|
|
|
|
|
$row[] = ' ';
|
|
|
|
$row[] = $column_link;
|
2014-01-13 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$project_rows[] = phutil_tag('div', array(), $row);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$project_rows = phutil_tag('em', array(), pht('None'));
|
|
|
|
}
|
|
|
|
|
|
|
|
$view->addProperty(pht('Projects'), $project_rows);
|
2012-12-11 23:03:16 +01:00
|
|
|
|
|
|
|
$edge_types = array(
|
|
|
|
PhabricatorEdgeConfig::TYPE_TASK_DEPENDED_ON_BY_TASK
|
2013-07-20 00:59:29 +02:00
|
|
|
=> pht('Dependent Tasks'),
|
2012-12-11 23:03:16 +01:00
|
|
|
PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK
|
2013-07-20 00:59:29 +02:00
|
|
|
=> pht('Depends On'),
|
2012-12-11 23:03:16 +01:00
|
|
|
PhabricatorEdgeConfig::TYPE_TASK_HAS_RELATED_DREV
|
2013-07-20 00:59:29 +02:00
|
|
|
=> pht('Differential Revisions'),
|
|
|
|
PhabricatorEdgeConfig::TYPE_TASK_HAS_MOCK
|
|
|
|
=> pht('Pholio Mocks'),
|
2012-12-11 23:03:16 +01:00
|
|
|
);
|
|
|
|
|
2013-04-06 20:40:43 +02:00
|
|
|
$revisions_commits = array();
|
|
|
|
$handles = $this->getLoadedHandles();
|
|
|
|
|
|
|
|
$commit_phids = array_keys(
|
|
|
|
$edges[PhabricatorEdgeConfig::TYPE_TASK_HAS_COMMIT]);
|
|
|
|
if ($commit_phids) {
|
2013-04-13 07:48:16 +02:00
|
|
|
$commit_drev = PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV;
|
|
|
|
$drev_edges = id(new PhabricatorEdgeQuery())
|
|
|
|
->withSourcePHIDs($commit_phids)
|
|
|
|
->withEdgeTypes(array($commit_drev))
|
|
|
|
->execute();
|
2013-04-06 20:40:43 +02:00
|
|
|
|
2013-04-13 07:48:16 +02:00
|
|
|
foreach ($commit_phids as $phid) {
|
2013-04-06 20:40:43 +02:00
|
|
|
$revisions_commits[$phid] = $handles[$phid]->renderLink();
|
2013-04-13 07:48:16 +02:00
|
|
|
$revision_phid = key($drev_edges[$phid][$commit_drev]);
|
2013-04-06 20:40:43 +02:00
|
|
|
$revision_handle = idx($handles, $revision_phid);
|
|
|
|
if ($revision_handle) {
|
2013-04-13 07:48:16 +02:00
|
|
|
$task_drev = PhabricatorEdgeConfig::TYPE_TASK_HAS_RELATED_DREV;
|
|
|
|
unset($edges[$task_drev][$revision_phid]);
|
2013-04-06 20:40:43 +02:00
|
|
|
$revisions_commits[$phid] = hsprintf(
|
|
|
|
'%s / %s',
|
|
|
|
$revision_handle->renderLink($revision_handle->getName()),
|
|
|
|
$revisions_commits[$phid]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-11 23:03:16 +01:00
|
|
|
foreach ($edge_types as $edge_type => $edge_name) {
|
|
|
|
if ($edges[$edge_type]) {
|
|
|
|
$view->addProperty(
|
|
|
|
$edge_name,
|
|
|
|
$this->renderHandlesForPHIDs(array_keys($edges[$edge_type])));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-06 20:40:43 +02:00
|
|
|
if ($revisions_commits) {
|
|
|
|
$view->addProperty(
|
|
|
|
pht('Commits'),
|
|
|
|
phutil_implode_html(phutil_tag('br'), $revisions_commits));
|
|
|
|
}
|
|
|
|
|
2012-12-11 23:03:16 +01:00
|
|
|
$attached = $task->getAttached();
|
2013-08-05 19:27:50 +02:00
|
|
|
if (!is_array($attached)) {
|
|
|
|
$attached = array();
|
|
|
|
}
|
|
|
|
|
2013-07-22 17:02:56 +02:00
|
|
|
$file_infos = idx($attached, PhabricatorFilePHIDTypeFile::TYPECONST);
|
2012-12-11 23:03:16 +01:00
|
|
|
if ($file_infos) {
|
|
|
|
$file_phids = array_keys($file_infos);
|
|
|
|
|
2013-09-30 18:38:13 +02:00
|
|
|
// TODO: These should probably be handles or something; clean this up
|
|
|
|
// as we sort out file attachments.
|
|
|
|
$files = id(new PhabricatorFileQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withPHIDs($file_phids)
|
|
|
|
->execute();
|
2012-12-11 23:03:16 +01:00
|
|
|
|
|
|
|
$file_view = new PhabricatorFileLinkListView();
|
|
|
|
$file_view->setFiles($files);
|
|
|
|
|
|
|
|
$view->addProperty(
|
|
|
|
pht('Files'),
|
|
|
|
$file_view->render());
|
|
|
|
}
|
|
|
|
|
2013-09-24 20:10:46 +02:00
|
|
|
$field_list->appendFieldsToPropertyList(
|
|
|
|
$task,
|
|
|
|
$viewer,
|
|
|
|
$view);
|
|
|
|
|
2013-02-15 16:47:14 +01:00
|
|
|
$view->invokeWillRenderEvent();
|
|
|
|
|
2013-10-11 16:53:56 +02:00
|
|
|
return $view;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildDescriptionView(
|
|
|
|
ManiphestTask $task,
|
|
|
|
PhabricatorMarkupEngine $engine) {
|
|
|
|
|
|
|
|
$section = null;
|
2012-12-11 23:03:16 +01:00
|
|
|
if (strlen($task->getDescription())) {
|
2013-10-11 16:53:56 +02:00
|
|
|
$section = new PHUIPropertyListView();
|
2013-11-04 20:07:51 +01:00
|
|
|
$section->addSectionHeader(
|
|
|
|
pht('Description'),
|
|
|
|
PHUIPropertyListView::ICON_SUMMARY);
|
2013-10-11 16:53:56 +02:00
|
|
|
$section->addTextContent(
|
2013-01-29 03:46:48 +01:00
|
|
|
phutil_tag(
|
2012-12-11 23:03:16 +01:00
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'phabricator-remarkup',
|
|
|
|
),
|
|
|
|
$engine->getOutput($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION)));
|
2012-07-19 05:41:42 +02:00
|
|
|
}
|
2012-12-11 23:03:16 +01:00
|
|
|
|
2013-10-11 16:53:56 +02:00
|
|
|
return $section;
|
2012-07-19 05:41:42 +02:00
|
|
|
}
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
}
|