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
|
|
|
|
2014-07-18 00:42:06 +02:00
|
|
|
$e_commit = ManiphestTaskHasCommitEdgeType::EDGECONST;
|
2012-07-19 05:41:42 +02:00
|
|
|
$e_dep_on = PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK;
|
|
|
|
$e_dep_by = PhabricatorEdgeConfig::TYPE_TASK_DEPENDED_ON_BY_TASK;
|
2014-07-18 00:41:08 +02:00
|
|
|
$e_rev = ManiphestTaskHasRevisionEdgeType::EDGECONST;
|
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;
|
|
|
|
}
|
|
|
|
|
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-05-21 21:44:58 +02:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSubmitControl())
|
|
|
|
->setValue(pht('Submit')));
|
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
|
|
|
);
|
|
|
|
|
2014-07-11 02:28:29 +02:00
|
|
|
$projects_source = new PhabricatorProjectDatasource();
|
2014-07-18 00:44:18 +02:00
|
|
|
$users_source = new PhabricatorPeopleDatasource();
|
2014-07-18 00:44:29 +02:00
|
|
|
$mailable_source = new PhabricatorMetaMTAMailableDatasource();
|
2014-07-11 02:28:29 +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',
|
2014-07-11 02:28:29 +02:00
|
|
|
'src' => $projects_source->getDatasourceURI(),
|
|
|
|
'placeholder' => $projects_source->getPlaceholderText(),
|
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',
|
2014-07-18 00:44:18 +02:00
|
|
|
'src' => $users_source->getDatasourceURI(),
|
2012-04-14 16:05:58 +02:00
|
|
|
'value' => $default_claim,
|
|
|
|
'limit' => 1,
|
2014-07-18 00:44:18 +02:00
|
|
|
'placeholder' => $users_source->getPlaceholderText(),
|
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',
|
2014-07-18 00:44:29 +02:00
|
|
|
'src' => $mailable_source->getDatasourceURI(),
|
|
|
|
'placeholder' => $mailable_source->getPlaceholderText(),
|
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
|
|
|
|
2014-04-19 02:51:46 +02:00
|
|
|
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
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);
|
2014-05-05 19:55:58 +02:00
|
|
|
$timeline->setQuoteTargetID('transaction-comments');
|
|
|
|
$timeline->setQuoteRef($object_name);
|
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()),
|
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'))
|
2014-05-12 19:08:32 +02:00
|
|
|
->setIcon('fa-pencil')
|
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)
|
2014-05-12 19:08:32 +02:00
|
|
|
->setIcon('fa-check-circle'));
|
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');
|
2014-05-12 19:08:32 +02:00
|
|
|
$icon = $viewer_is_cc ? 'fa-minus-circle' : 'fa-plus-circle';
|
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)
|
2014-05-12 19:08:32 +02:00
|
|
|
->setIcon('fa-compress')
|
2013-09-25 22:44:52 +02:00
|
|
|
->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}"))
|
2014-05-12 19:08:32 +02:00
|
|
|
->setIcon('fa-level-down'));
|
2012-12-11 23:03:16 +01:00
|
|
|
|
|
|
|
$view->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
"Blocks" instead of "Dependent Tasks"
Summary: Fixes T5021, UI labels for the fields, "Edit Dependencies" in the action list, transaction strings ("added dependent tasks", etc), UI strings in the dependencies dialog (title/submit/etc)
Test Plan: Open task, edit blocks, dialog should have new term, task history should show "blocks" instead of "dependencies"
Reviewers: epriestley, #blessed_reviewers
Reviewed By: epriestley, #blessed_reviewers
Subscribers: epriestley, Korvin
Maniphest Tasks: T5021
Differential Revision: https://secure.phabricator.com/D9270
2014-05-23 22:50:20 +02:00
|
|
|
->setName(pht('Edit Blocking Tasks'))
|
|
|
|
->setHref("/search/attach/{$phid}/TASK/blocks/")
|
2012-12-11 23:03:16 +01:00
|
|
|
->setWorkflow(true)
|
2014-05-12 19:08:32 +02:00
|
|
|
->setIcon('fa-link')
|
2013-09-25 22:44:52 +02:00
|
|
|
->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');
|
|
|
|
|
2014-06-26 17:49:44 +02:00
|
|
|
$project_handles = array();
|
|
|
|
$project_annotations = array();
|
2014-01-13 21:25:12 +01:00
|
|
|
foreach ($project_phids as $project_phid) {
|
|
|
|
$handle = $this->getHandle($project_phid);
|
2014-06-26 17:49:44 +02:00
|
|
|
$project_handles[] = $handle;
|
2014-01-13 21:25:12 +01:00
|
|
|
|
|
|
|
$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());
|
|
|
|
$column_link = phutil_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
2014-06-13 23:09:21 +02:00
|
|
|
'href' => $handle->getURI().'board/',
|
2014-02-14 03:12:38 +01:00
|
|
|
'class' => 'maniphest-board-link',
|
|
|
|
),
|
|
|
|
$column_name);
|
|
|
|
|
2014-06-26 17:49:44 +02:00
|
|
|
$project_annotations[$project_phid] = array(
|
|
|
|
' ',
|
|
|
|
$column_link);
|
2014-01-13 21:25:12 +01:00
|
|
|
}
|
|
|
|
}
|
2014-06-26 17:49:44 +02:00
|
|
|
|
|
|
|
$project_rows = id(new PHUIHandleTagListView())
|
|
|
|
->setHandles($project_handles)
|
|
|
|
->setAnnotations($project_annotations);
|
2014-01-13 21:25:12 +01:00
|
|
|
} 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
|
2014-07-18 00:41:08 +02:00
|
|
|
=> pht('Blocks'),
|
2012-12-11 23:03:16 +01:00
|
|
|
PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK
|
2014-07-18 00:41:08 +02:00
|
|
|
=> pht('Blocked By'),
|
|
|
|
ManiphestTaskHasRevisionEdgeType::EDGECONST
|
|
|
|
=> pht('Differential Revisions'),
|
2013-07-20 00:59:29 +02:00
|
|
|
PhabricatorEdgeConfig::TYPE_TASK_HAS_MOCK
|
2014-07-18 00:41:08 +02:00
|
|
|
=> 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(
|
2014-07-18 00:42:06 +02:00
|
|
|
$edges[ManiphestTaskHasCommitEdgeType::EDGECONST]);
|
2013-04-06 20:40:43 +02:00
|
|
|
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) {
|
2014-07-18 00:41:08 +02:00
|
|
|
$task_drev = ManiphestTaskHasRevisionEdgeType::EDGECONST;
|
2013-04-13 07:48:16 +02:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2014-07-24 00:05:46 +02:00
|
|
|
$file_infos = idx($attached, PhabricatorFileFilePHIDType::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
|
|
|
}
|