mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-24 14:30:56 +01:00
eace05e4a5
Summary: Ref T5021. This specific label is a little more clear as "Blocks". See also IRC. Test Plan: eyeballed it Reviewers: btrahan, lpriestley, hach-que Reviewed By: hach-que Subscribers: epriestley Maniphest Tasks: T5021 Differential Revision: https://secure.phabricator.com/D9279
702 lines
22 KiB
PHP
702 lines
22 KiB
PHP
<?php
|
|
|
|
final class ManiphestTaskDetailController extends ManiphestController {
|
|
|
|
private $id;
|
|
|
|
public function shouldAllowPublic() {
|
|
return true;
|
|
}
|
|
|
|
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();
|
|
|
|
$task = id(new ManiphestTaskQuery())
|
|
->setViewer($user)
|
|
->withIDs(array($this->id))
|
|
->executeOne();
|
|
if (!$task) {
|
|
return new Aphront404Response();
|
|
}
|
|
|
|
$workflow = $request->getStr('workflow');
|
|
$parent_task = null;
|
|
if ($workflow && is_numeric($workflow)) {
|
|
$parent_task = id(new ManiphestTaskQuery())
|
|
->setViewer($user)
|
|
->withIDs(array($workflow))
|
|
->executeOne();
|
|
}
|
|
|
|
$transactions = id(new ManiphestTransactionQuery())
|
|
->setViewer($user)
|
|
->withObjectPHIDs(array($task->getPHID()))
|
|
->needComments(true)
|
|
->execute();
|
|
|
|
$field_list = PhabricatorCustomField::getObjectFields(
|
|
$task,
|
|
PhabricatorCustomField::ROLE_VIEW);
|
|
$field_list
|
|
->setViewer($user)
|
|
->readFieldsFromStorage($task);
|
|
|
|
$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;
|
|
$e_rev = PhabricatorEdgeConfig::TYPE_TASK_HAS_RELATED_DREV;
|
|
$e_mock = PhabricatorEdgeConfig::TYPE_TASK_HAS_MOCK;
|
|
|
|
$phid = $task->getPHID();
|
|
|
|
$query = id(new PhabricatorEdgeQuery())
|
|
->withSourcePHIDs(array($phid))
|
|
->withEdgeTypes(
|
|
array(
|
|
$e_commit,
|
|
$e_dep_on,
|
|
$e_dep_by,
|
|
$e_rev,
|
|
$e_mock,
|
|
));
|
|
$edges = idx($query->execute(), $phid);
|
|
$phids = array_fill_keys($query->getDestinationPHIDs(), true);
|
|
|
|
foreach ($task->getCCPHIDs() as $phid) {
|
|
$phids[$phid] = true;
|
|
}
|
|
foreach ($task->getProjectPHIDs() as $phid) {
|
|
$phids[$phid] = true;
|
|
}
|
|
if ($task->getOwnerPHID()) {
|
|
$phids[$task->getOwnerPHID()] = true;
|
|
}
|
|
$phids[$task->getAuthorPHID()] = true;
|
|
|
|
$attached = $task->getAttached();
|
|
foreach ($attached as $type => $list) {
|
|
foreach ($list as $phid => $info) {
|
|
$phids[$phid] = true;
|
|
}
|
|
}
|
|
|
|
if ($parent_task) {
|
|
$phids[$parent_task->getPHID()] = true;
|
|
}
|
|
|
|
$phids = array_keys($phids);
|
|
|
|
$this->loadHandles($phids);
|
|
|
|
$handles = $this->getLoadedHandles();
|
|
|
|
$context_bar = null;
|
|
|
|
if ($parent_task) {
|
|
$context_bar = new AphrontContextBarView();
|
|
$context_bar->addButton(phutil_tag(
|
|
'a',
|
|
array(
|
|
'href' => '/maniphest/task/create/?parent='.$parent_task->getID(),
|
|
'class' => 'green button',
|
|
),
|
|
pht('Create Another Subtask')));
|
|
$context_bar->appendChild(hsprintf(
|
|
'Created a subtask of <strong>%s</strong>',
|
|
$this->getHandle($parent_task->getPHID())->renderLink()));
|
|
} else if ($workflow == 'create') {
|
|
$context_bar = new AphrontContextBarView();
|
|
$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',
|
|
),
|
|
pht('Similar Task')));
|
|
$context_bar->addButton(phutil_tag(
|
|
'a',
|
|
array(
|
|
'href' => '/maniphest/task/create/',
|
|
'class' => 'green button',
|
|
),
|
|
pht('Empty Task')));
|
|
$context_bar->appendChild(pht('New task created.'));
|
|
}
|
|
|
|
$engine = new PhabricatorMarkupEngine();
|
|
$engine->setViewer($user);
|
|
$engine->addObject($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION);
|
|
foreach ($transactions as $modern_xaction) {
|
|
if ($modern_xaction->getComment()) {
|
|
$engine->addObject(
|
|
$modern_xaction->getComment(),
|
|
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
|
|
}
|
|
}
|
|
|
|
$engine->process();
|
|
|
|
$resolution_types = ManiphestTaskStatus::getTaskStatusMap();
|
|
|
|
$transaction_types = array(
|
|
PhabricatorTransactions::TYPE_COMMENT => pht('Comment'),
|
|
ManiphestTransaction::TYPE_STATUS => pht('Change Status'),
|
|
ManiphestTransaction::TYPE_OWNER => pht('Reassign / Claim'),
|
|
ManiphestTransaction::TYPE_CCS => pht('Add CCs'),
|
|
ManiphestTransaction::TYPE_PRIORITY => pht('Change Priority'),
|
|
ManiphestTransaction::TYPE_PROJECTS => pht('Associate Projects'),
|
|
);
|
|
|
|
// 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]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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()) {
|
|
unset($transaction_types[ManiphestTransaction::TYPE_PRIORITY]);
|
|
unset($transaction_types[ManiphestTransaction::TYPE_OWNER]);
|
|
}
|
|
|
|
$default_claim = array(
|
|
$user->getPHID() => $user->getUsername().' ('.$user->getRealName().')',
|
|
);
|
|
|
|
$draft = id(new PhabricatorDraft())->loadOneWhere(
|
|
'authorPHID = %s AND draftKey = %s',
|
|
$user->getPHID(),
|
|
$task->getPHID());
|
|
if ($draft) {
|
|
$draft_text = $draft->getDraft();
|
|
} else {
|
|
$draft_text = null;
|
|
}
|
|
|
|
$comment_form = new AphrontFormView();
|
|
$comment_form
|
|
->setUser($user)
|
|
->setWorkflow(true)
|
|
->setAction('/maniphest/transaction/save/')
|
|
->setEncType('multipart/form-data')
|
|
->addHiddenInput('taskID', $task->getID())
|
|
->appendChild(
|
|
id(new AphrontFormSelectControl())
|
|
->setLabel(pht('Action'))
|
|
->setName('action')
|
|
->setOptions($transaction_types)
|
|
->setID('transaction-action'))
|
|
->appendChild(
|
|
id(new AphrontFormSelectControl())
|
|
->setLabel(pht('Status'))
|
|
->setName('resolution')
|
|
->setControlID('resolution')
|
|
->setControlStyle('display: none')
|
|
->setOptions($resolution_types))
|
|
->appendChild(
|
|
id(new AphrontFormTokenizerControl())
|
|
->setLabel(pht('Assign To'))
|
|
->setName('assign_to')
|
|
->setControlID('assign_to')
|
|
->setControlStyle('display: none')
|
|
->setID('assign-tokenizer')
|
|
->setDisableBehavior(true))
|
|
->appendChild(
|
|
id(new AphrontFormTokenizerControl())
|
|
->setLabel(pht('CCs'))
|
|
->setName('ccs')
|
|
->setControlID('ccs')
|
|
->setControlStyle('display: none')
|
|
->setID('cc-tokenizer')
|
|
->setDisableBehavior(true))
|
|
->appendChild(
|
|
id(new AphrontFormSelectControl())
|
|
->setLabel(pht('Priority'))
|
|
->setName('priority')
|
|
->setOptions($priority_map)
|
|
->setControlID('priority')
|
|
->setControlStyle('display: none')
|
|
->setValue($task->getPriority()))
|
|
->appendChild(
|
|
id(new AphrontFormTokenizerControl())
|
|
->setLabel(pht('Projects'))
|
|
->setName('projects')
|
|
->setControlID('projects')
|
|
->setControlStyle('display: none')
|
|
->setID('projects-tokenizer')
|
|
->setDisableBehavior(true))
|
|
->appendChild(
|
|
id(new AphrontFormFileControl())
|
|
->setLabel(pht('File'))
|
|
->setName('file')
|
|
->setControlID('file')
|
|
->setControlStyle('display: none'))
|
|
->appendChild(
|
|
id(new PhabricatorRemarkupControl())
|
|
->setLabel(pht('Comments'))
|
|
->setName('comments')
|
|
->setValue($draft_text)
|
|
->setID('transaction-comments')
|
|
->setUser($user))
|
|
->appendChild(
|
|
id(new AphrontFormSubmitControl())
|
|
->setValue(pht('Submit')));
|
|
|
|
$control_map = array(
|
|
ManiphestTransaction::TYPE_STATUS => 'resolution',
|
|
ManiphestTransaction::TYPE_OWNER => 'assign_to',
|
|
ManiphestTransaction::TYPE_CCS => 'ccs',
|
|
ManiphestTransaction::TYPE_PRIORITY => 'priority',
|
|
ManiphestTransaction::TYPE_PROJECTS => 'projects',
|
|
);
|
|
|
|
$tokenizer_map = array(
|
|
ManiphestTransaction::TYPE_PROJECTS => array(
|
|
'id' => 'projects-tokenizer',
|
|
'src' => '/typeahead/common/projects/',
|
|
'placeholder' => pht('Type a project name...'),
|
|
),
|
|
ManiphestTransaction::TYPE_OWNER => array(
|
|
'id' => 'assign-tokenizer',
|
|
'src' => '/typeahead/common/users/',
|
|
'value' => $default_claim,
|
|
'limit' => 1,
|
|
'placeholder' => pht('Type a user name...'),
|
|
),
|
|
ManiphestTransaction::TYPE_CCS => array(
|
|
'id' => 'cc-tokenizer',
|
|
'src' => '/typeahead/common/mailable/',
|
|
'placeholder' => pht('Type a user or mailing list...'),
|
|
),
|
|
);
|
|
|
|
// 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,
|
|
));
|
|
}
|
|
|
|
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
|
$comment_header = $is_serious
|
|
? pht('Add Comment')
|
|
: pht('Weigh In');
|
|
|
|
$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...'))));
|
|
|
|
$timeline = id(new PhabricatorApplicationTransactionView())
|
|
->setUser($user)
|
|
->setObjectPHID($task->getPHID())
|
|
->setTransactions($transactions)
|
|
->setMarkupEngine($engine);
|
|
|
|
$object_name = 'T'.$task->getID();
|
|
$actions = $this->buildActionView($task);
|
|
|
|
$crumbs = $this->buildApplicationCrumbs()
|
|
->addTextCrumb($object_name, '/'.$object_name)
|
|
->setActionList($actions);
|
|
|
|
$header = $this->buildHeaderView($task);
|
|
$properties = $this->buildPropertyView(
|
|
$task, $field_list, $edges, $actions);
|
|
$description = $this->buildDescriptionView($task, $engine);
|
|
|
|
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.
|
|
$comment_box = id(new PhabricatorApplicationTransactionCommentView())
|
|
->setUser($user)
|
|
->setRequestURI($request->getRequestURI());
|
|
$preview_panel = null;
|
|
} else {
|
|
$comment_box = id(new PHUIObjectBoxView())
|
|
->setFlush(true)
|
|
->setHeaderText($comment_header)
|
|
->appendChild($comment_form);
|
|
$timeline->setQuoteTargetID('transaction-comments');
|
|
$timeline->setQuoteRef($object_name);
|
|
}
|
|
|
|
$object_box = id(new PHUIObjectBoxView())
|
|
->setHeader($header)
|
|
->addPropertyList($properties);
|
|
|
|
if ($description) {
|
|
$object_box->addPropertyList($description);
|
|
}
|
|
|
|
return $this->buildApplicationPage(
|
|
array(
|
|
$crumbs,
|
|
$context_bar,
|
|
$object_box,
|
|
$timeline,
|
|
$comment_box,
|
|
$preview_panel,
|
|
),
|
|
array(
|
|
'title' => 'T'.$task->getID().' '.$task->getTitle(),
|
|
'pageObjects' => array($task->getPHID()),
|
|
'device' => true,
|
|
));
|
|
}
|
|
|
|
private function buildHeaderView(ManiphestTask $task) {
|
|
$view = id(new PHUIHeaderView())
|
|
->setHeader($task->getTitle())
|
|
->setUser($this->getRequest()->getUser())
|
|
->setPolicyObject($task);
|
|
|
|
$status = $task->getStatus();
|
|
$status_name = ManiphestTaskStatus::renderFullDescription($status);
|
|
|
|
$view->addProperty(PHUIHeaderView::PROPERTY_STATUS, $status_name);
|
|
|
|
return $view;
|
|
}
|
|
|
|
|
|
private function buildActionView(ManiphestTask $task) {
|
|
$viewer = $this->getRequest()->getUser();
|
|
$viewer_phid = $viewer->getPHID();
|
|
$viewer_is_cc = in_array($viewer_phid, $task->getCCPHIDs());
|
|
|
|
$id = $task->getID();
|
|
$phid = $task->getPHID();
|
|
|
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
|
$viewer,
|
|
$task,
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
$view = id(new PhabricatorActionListView())
|
|
->setUser($viewer)
|
|
->setObject($task)
|
|
->setObjectURI($this->getRequest()->getRequestURI());
|
|
|
|
$view->addAction(
|
|
id(new PhabricatorActionView())
|
|
->setName(pht('Edit Task'))
|
|
->setIcon('fa-pencil')
|
|
->setHref($this->getApplicationURI("/task/edit/{$id}/"))
|
|
->setDisabled(!$can_edit)
|
|
->setWorkflow(!$can_edit));
|
|
|
|
if ($task->getOwnerPHID() === $viewer_phid) {
|
|
$view->addAction(
|
|
id(new PhabricatorActionView())
|
|
->setName(pht('Automatically Subscribed'))
|
|
->setDisabled(true)
|
|
->setIcon('fa-check-circle'));
|
|
} else {
|
|
$action = $viewer_is_cc ? 'rem' : 'add';
|
|
$name = $viewer_is_cc ? pht('Unsubscribe') : pht('Subscribe');
|
|
$icon = $viewer_is_cc ? 'fa-minus-circle' : 'fa-plus-circle';
|
|
|
|
$view->addAction(
|
|
id(new PhabricatorActionView())
|
|
->setName($name)
|
|
->setHref("/maniphest/subscribe/{$action}/{$id}/")
|
|
->setRenderAsForm(true)
|
|
->setUser($viewer)
|
|
->setIcon($icon));
|
|
}
|
|
|
|
$view->addAction(
|
|
id(new PhabricatorActionView())
|
|
->setName(pht('Merge Duplicates In'))
|
|
->setHref("/search/attach/{$phid}/TASK/merge/")
|
|
->setWorkflow(true)
|
|
->setIcon('fa-compress')
|
|
->setDisabled(!$can_edit)
|
|
->setWorkflow(true));
|
|
|
|
$view->addAction(
|
|
id(new PhabricatorActionView())
|
|
->setName(pht('Create Subtask'))
|
|
->setHref($this->getApplicationURI("/task/create/?parent={$id}"))
|
|
->setIcon('fa-level-down'));
|
|
|
|
$view->addAction(
|
|
id(new PhabricatorActionView())
|
|
->setName(pht('Edit Blocking Tasks'))
|
|
->setHref("/search/attach/{$phid}/TASK/blocks/")
|
|
->setWorkflow(true)
|
|
->setIcon('fa-link')
|
|
->setDisabled(!$can_edit)
|
|
->setWorkflow(true));
|
|
|
|
return $view;
|
|
}
|
|
|
|
private function buildPropertyView(
|
|
ManiphestTask $task,
|
|
PhabricatorCustomFieldList $field_list,
|
|
array $edges,
|
|
PhabricatorActionListView $actions) {
|
|
|
|
$viewer = $this->getRequest()->getUser();
|
|
|
|
$view = id(new PHUIPropertyListView())
|
|
->setUser($viewer)
|
|
->setObject($task)
|
|
->setActionList($actions);
|
|
|
|
$view->addProperty(
|
|
pht('Assigned To'),
|
|
$task->getOwnerPHID()
|
|
? $this->getHandle($task->getOwnerPHID())->renderLink()
|
|
: phutil_tag('em', array(), pht('None')));
|
|
|
|
$view->addProperty(
|
|
pht('Priority'),
|
|
ManiphestTaskPriority::getTaskPriorityName($task->getPriority()));
|
|
|
|
$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);
|
|
|
|
$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'),
|
|
phutil_tag(
|
|
'a',
|
|
array(
|
|
'href' => 'mailto:'.$source.'?subject='.$subject
|
|
),
|
|
$source));
|
|
}
|
|
|
|
$project_phids = $task->getProjectPHIDs();
|
|
if ($project_phids) {
|
|
require_celerity_resource('maniphest-task-summary-css');
|
|
|
|
// 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) {
|
|
$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;
|
|
}
|
|
|
|
$project_rows[] = phutil_tag('div', array(), $row);
|
|
}
|
|
} else {
|
|
$project_rows = phutil_tag('em', array(), pht('None'));
|
|
}
|
|
|
|
$view->addProperty(pht('Projects'), $project_rows);
|
|
|
|
$edge_types = array(
|
|
PhabricatorEdgeConfig::TYPE_TASK_DEPENDED_ON_BY_TASK
|
|
=> pht('Blocks'),
|
|
PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK
|
|
=> pht('Blocked By'),
|
|
PhabricatorEdgeConfig::TYPE_TASK_HAS_RELATED_DREV
|
|
=> pht('Differential Revisions'),
|
|
PhabricatorEdgeConfig::TYPE_TASK_HAS_MOCK
|
|
=> pht('Pholio Mocks'),
|
|
);
|
|
|
|
$revisions_commits = array();
|
|
$handles = $this->getLoadedHandles();
|
|
|
|
$commit_phids = array_keys(
|
|
$edges[PhabricatorEdgeConfig::TYPE_TASK_HAS_COMMIT]);
|
|
if ($commit_phids) {
|
|
$commit_drev = PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV;
|
|
$drev_edges = id(new PhabricatorEdgeQuery())
|
|
->withSourcePHIDs($commit_phids)
|
|
->withEdgeTypes(array($commit_drev))
|
|
->execute();
|
|
|
|
foreach ($commit_phids as $phid) {
|
|
$revisions_commits[$phid] = $handles[$phid]->renderLink();
|
|
$revision_phid = key($drev_edges[$phid][$commit_drev]);
|
|
$revision_handle = idx($handles, $revision_phid);
|
|
if ($revision_handle) {
|
|
$task_drev = PhabricatorEdgeConfig::TYPE_TASK_HAS_RELATED_DREV;
|
|
unset($edges[$task_drev][$revision_phid]);
|
|
$revisions_commits[$phid] = hsprintf(
|
|
'%s / %s',
|
|
$revision_handle->renderLink($revision_handle->getName()),
|
|
$revisions_commits[$phid]);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($edge_types as $edge_type => $edge_name) {
|
|
if ($edges[$edge_type]) {
|
|
$view->addProperty(
|
|
$edge_name,
|
|
$this->renderHandlesForPHIDs(array_keys($edges[$edge_type])));
|
|
}
|
|
}
|
|
|
|
if ($revisions_commits) {
|
|
$view->addProperty(
|
|
pht('Commits'),
|
|
phutil_implode_html(phutil_tag('br'), $revisions_commits));
|
|
}
|
|
|
|
$attached = $task->getAttached();
|
|
if (!is_array($attached)) {
|
|
$attached = array();
|
|
}
|
|
|
|
$file_infos = idx($attached, PhabricatorFilePHIDTypeFile::TYPECONST);
|
|
if ($file_infos) {
|
|
$file_phids = array_keys($file_infos);
|
|
|
|
// 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();
|
|
|
|
$file_view = new PhabricatorFileLinkListView();
|
|
$file_view->setFiles($files);
|
|
|
|
$view->addProperty(
|
|
pht('Files'),
|
|
$file_view->render());
|
|
}
|
|
|
|
$field_list->appendFieldsToPropertyList(
|
|
$task,
|
|
$viewer,
|
|
$view);
|
|
|
|
$view->invokeWillRenderEvent();
|
|
|
|
return $view;
|
|
}
|
|
|
|
private function buildDescriptionView(
|
|
ManiphestTask $task,
|
|
PhabricatorMarkupEngine $engine) {
|
|
|
|
$section = null;
|
|
if (strlen($task->getDescription())) {
|
|
$section = new PHUIPropertyListView();
|
|
$section->addSectionHeader(
|
|
pht('Description'),
|
|
PHUIPropertyListView::ICON_SUMMARY);
|
|
$section->addTextContent(
|
|
phutil_tag(
|
|
'div',
|
|
array(
|
|
'class' => 'phabricator-remarkup',
|
|
),
|
|
$engine->getOutput($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION)));
|
|
}
|
|
|
|
return $section;
|
|
}
|
|
|
|
}
|