protected function renderActionEffectDescription($type, $data)
 {
     switch ($type) {
         case self::DO_PRIORITY:
             return pht('Changed task priority to "%s".', ManiphestTaskPriority::getTaskPriorityName($data));
     }
 }
 protected function execute(ConduitAPIRequest $request)
 {
     $task_id = $request->getValue('task_id');
     $task = id(new ManiphestTask())->load($task_id);
     if (!$task) {
         throw new ConduitException('ERR_BAD_TASK');
     }
     $result = array('id' => $task->getID(), 'phid' => $task->getPHID(), 'authorPHID' => $task->getAuthorPHID(), 'ownerPHID' => $task->getAuthorPHID(), 'ccPHIDs' => $task->getCCPHIDs(), 'status' => $task->getStatus(), 'priority' => ManiphestTaskPriority::getTaskPriorityName($task->getPriority()), 'title' => $task->getTitle(), 'description' => $task->getDescription(), 'projectPHIDs' => $task->getProjectPHIDs(), 'uri' => PhabricatorEnv::getProductionURI('/T' . $task->getID()));
     return $result;
 }
 public function render()
 {
     if (!$this->user) {
         throw new Exception("Call setUser() before rendering!");
     }
     $task = $this->task;
     $handles = $this->handles;
     require_celerity_resource('maniphest-task-summary-css');
     $classes = array(ManiphestTaskPriority::PRIORITY_UNBREAK_NOW => 'pri-unbreak', ManiphestTaskPriority::PRIORITY_TRIAGE => 'pri-triage', ManiphestTaskPriority::PRIORITY_HIGH => 'pri-high', ManiphestTaskPriority::PRIORITY_NORMAL => 'pri-normal', ManiphestTaskPriority::PRIORITY_LOW => 'pri-low', ManiphestTaskPriority::PRIORITY_WISH => 'pri-wish');
     $pri_class = idx($classes, $task->getPriority());
     return '<table class="maniphest-task-summary">' . '<tr>' . '<td class="maniphest-task-number ' . $pri_class . '">' . 'T' . $task->getID() . '</td>' . '<td class="maniphest-task-status">' . ($task->getStatus() == ManiphestTaskStatus::STATUS_OPEN ? 'Open' : 'Closed') . '</td>' . '<td class="maniphest-task-owner">' . ($task->getOwnerPHID() ? $handles[$task->getOwnerPHID()]->renderLink() : '<em>None</em>') . '</td>' . '<td class="maniphest-task-name">' . phutil_render_tag('a', array('href' => '/T' . $task->getID()), phutil_escape_html($task->getTitle())) . '</td>' . '<td class="maniphest-task-priority">' . ManiphestTaskPriority::getTaskPriorityName($task->getPriority()) . '</td>' . '<td class="maniphest-task-updated">' . phabricator_datetime($task->getDateModified(), $this->user) . '</td>' . '</tr>' . '</table>';
 }
 public function renderHovercard(PhabricatorHovercardView $hovercard, PhabricatorObjectHandle $handle, $task, $data)
 {
     $viewer = $this->getViewer();
     $hovercard->setTitle($task->getMonogram())->setDetail($task->getTitle());
     $owner_phid = $task->getOwnerPHID();
     if ($owner_phid) {
         $owner = $viewer->renderHandle($owner_phid);
     } else {
         $owner = phutil_tag('em', array(), pht('None'));
     }
     $hovercard->addField(pht('Assigned To'), $owner);
     $hovercard->addField(pht('Priority'), ManiphestTaskPriority::getTaskPriorityName($task->getPriority()));
     $hovercard->addTag(ManiphestView::renderTagForTask($task));
 }
 protected function buildTaskInfoDictionaries(array $tasks)
 {
     if (!$tasks) {
         return array();
     }
     $all_aux = id(new ManiphestTaskAuxiliaryStorage())->loadAllWhere('taskPHID in (%Ls)', mpull($tasks, 'getPHID'));
     $all_aux = mgroup($all_aux, 'getTaskPHID');
     $result = array();
     foreach ($tasks as $task) {
         $auxiliary = idx($all_aux, $task->getPHID(), array());
         $auxiliary = mpull($auxiliary, 'getValue', 'getName');
         $result[$task->getPHID()] = array('id' => $task->getID(), 'phid' => $task->getPHID(), 'authorPHID' => $task->getAuthorPHID(), 'ownerPHID' => $task->getOwnerPHID(), 'ccPHIDs' => $task->getCCPHIDs(), 'status' => $task->getStatus(), 'priority' => ManiphestTaskPriority::getTaskPriorityName($task->getPriority()), 'title' => $task->getTitle(), 'description' => $task->getDescription(), 'projectPHIDs' => $task->getProjectPHIDs(), 'uri' => PhabricatorEnv::getProductionURI('/T' . $task->getID()), 'auxiliary' => $auxiliary, 'objectName' => 'T' . $task->getID(), 'dateCreated' => $task->getDateCreated(), 'dateModified' => $task->getDateModified());
     }
     return $result;
 }
 private function handleHovercardEvent(PhutilEvent $event)
 {
     $viewer = $event->getUser();
     $hovercard = $event->getValue('hovercard');
     $handle = $event->getValue('handle');
     $phid = $handle->getPHID();
     $task = $event->getValue('object');
     if (!$task instanceof ManiphestTask) {
         return;
     }
     $e_project = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
     // Fun with "Unbeta Pholio", hua hua
     $e_dep_on = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
     $e_dep_by = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
     $edge_query = id(new PhabricatorEdgeQuery())->withSourcePHIDs(array($phid))->withEdgeTypes(array($e_project, $e_dep_on, $e_dep_by));
     $edges = idx($edge_query->execute(), $phid);
     $edge_phids = $edge_query->getDestinationPHIDs();
     $owner_phid = $task->getOwnerPHID();
     $hovercard->setTitle(pht('T%d', $task->getID()))->setDetail($task->getTitle());
     if ($owner_phid) {
         $owner = $viewer->renderHandle($owner_phid);
     } else {
         $owner = phutil_tag('em', array(), pht('None'));
     }
     $hovercard->addField(pht('Assigned To'), $owner);
     $hovercard->addField(pht('Priority'), ManiphestTaskPriority::getTaskPriorityName($task->getPriority()));
     if ($edge_phids) {
         $edge_types = array($e_project => pht('Projects'), $e_dep_by => pht('Blocks'), $e_dep_on => pht('Blocked By'));
         $max_count = 6;
         foreach ($edge_types as $edge_type => $edge_name) {
             if ($edges[$edge_type]) {
                 // TODO: This can be made more sophisticated. We still load all
                 // edges into memory. Only load the ones we need.
                 $edge_overflow = array();
                 if (count($edges[$edge_type]) > $max_count) {
                     $edges[$edge_type] = array_slice($edges[$edge_type], 0, 6, true);
                     $edge_overflow = ', ...';
                 }
                 $hovercard->addField($edge_name, array($viewer->renderHandleList(array_keys($edges[$edge_type])), $edge_overflow));
             }
         }
     }
     $hovercard->addTag(ManiphestView::renderTagForTask($task));
     $event->setValue('hovercard', $hovercard);
 }
 private function buildHeaderView(ManiphestTask $task)
 {
     $view = id(new PHUIHeaderView())->setHeader($task->getTitle())->setUser($this->getRequest()->getUser())->setPolicyObject($task);
     $priority_name = ManiphestTaskPriority::getTaskPriorityName($task->getPriority());
     $priority_color = ManiphestTaskPriority::getTaskPriorityColor($task->getPriority());
     $status = $task->getStatus();
     $status_name = ManiphestTaskStatus::renderFullDescription($status, $priority_name, $priority_color);
     $view->addProperty(PHUIHeaderView::PROPERTY_STATUS, $status_name);
     $view->setHeaderIcon(ManiphestTaskStatus::getStatusIcon($task->getStatus()) . ' ' . $priority_color);
     if (ManiphestTaskPoints::getIsEnabled()) {
         $points = $task->getPoints();
         if ($points !== null) {
             $points_name = pht('%s %s', $task->getPoints(), ManiphestTaskPoints::getPointsLabel());
             $tag = id(new PHUITagView())->setName($points_name)->setShade('blue')->setType(PHUITagView::TYPE_SHADE);
             $view->addTag($tag);
         }
     }
     return $view;
 }
Пример #8
0
 public function getTitleForFeed(PhabricatorFeedStory $story)
 {
     $author_phid = $this->getAuthorPHID();
     $object_phid = $this->getObjectPHID();
     $old = $this->getOldValue();
     $new = $this->getNewValue();
     switch ($this->getTransactionType()) {
         case self::TYPE_TITLE:
             if ($old === null) {
                 return pht('%s created %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
             }
             return pht('%s renamed %s from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old, $new);
         case self::TYPE_DESCRIPTION:
             return pht('%s edited the description of %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
         case self::TYPE_STATUS:
             $old_closed = ManiphestTaskStatus::isClosedStatus($old);
             $new_closed = ManiphestTaskStatus::isClosedStatus($new);
             $old_name = ManiphestTaskStatus::getTaskStatusName($old);
             $new_name = ManiphestTaskStatus::getTaskStatusName($new);
             if ($new_closed && !$old_closed) {
                 if ($new == ManiphestTaskStatus::getDuplicateStatus()) {
                     return pht('%s closed %s as a duplicate.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                 } else {
                     return pht('%s closed %s as "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $new_name);
                 }
             } else {
                 if (!$new_closed && $old_closed) {
                     return pht('%s reopened %s as "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $new_name);
                 } else {
                     return pht('%s changed the status of %s from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old_name, $new_name);
                 }
             }
         case self::TYPE_UNBLOCK:
             $blocker_phid = key($new);
             $old_status = head($old);
             $new_status = head($new);
             $old_closed = ManiphestTaskStatus::isClosedStatus($old_status);
             $new_closed = ManiphestTaskStatus::isClosedStatus($new_status);
             $old_name = ManiphestTaskStatus::getTaskStatusName($old_status);
             $new_name = ManiphestTaskStatus::getTaskStatusName($new_status);
             if ($old_closed && !$new_closed) {
                 return pht('%s reopened %s, a task blocking %s, as "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($blocker_phid), $this->renderHandleLink($object_phid), $new_name);
             } else {
                 if (!$old_closed && $new_closed) {
                     return pht('%s closed %s, a task blocking %s, as "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($blocker_phid), $this->renderHandleLink($object_phid), $new_name);
                 } else {
                     return pht('%s changed the status of %s, a task blocking %s, ' . 'from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($blocker_phid), $this->renderHandleLink($object_phid), $old_name, $new_name);
                 }
             }
         case self::TYPE_OWNER:
             if ($author_phid == $new) {
                 return pht('%s claimed %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
             } else {
                 if (!$new) {
                     return pht('%s placed %s up for grabs.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                 } else {
                     if (!$old) {
                         return pht('%s assigned %s to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $this->renderHandleLink($new));
                     } else {
                         return pht('%s reassigned %s from %s to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $this->renderHandleLink($old), $this->renderHandleLink($new));
                     }
                 }
             }
         case self::TYPE_PROJECTS:
             $added = array_diff($new, $old);
             $removed = array_diff($old, $new);
             if ($added && !$removed) {
                 return pht('%s added %d project(s) to %s: %s', $this->renderHandleLink($author_phid), count($added), $this->renderHandleLink($object_phid), $this->renderHandleList($added));
             } else {
                 if ($removed && !$added) {
                     return pht('%s removed %d project(s) from %s: %s', $this->renderHandleLink($author_phid), count($removed), $this->renderHandleLink($object_phid), $this->renderHandleList($removed));
                 } else {
                     if ($removed && $added) {
                         return pht('%s changed project(s) of %s, added %d: %s; removed %d: %s', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), count($added), $this->renderHandleList($added), count($removed), $this->renderHandleList($removed));
                     }
                 }
             }
         case self::TYPE_PRIORITY:
             $old_name = ManiphestTaskPriority::getTaskPriorityName($old);
             $new_name = ManiphestTaskPriority::getTaskPriorityName($new);
             if ($old == ManiphestTaskPriority::getDefaultPriority()) {
                 return pht('%s triaged %s as "%s" priority.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $new_name);
             } else {
                 if ($old > $new) {
                     return pht('%s lowered the priority of %s from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old_name, $new_name);
                 } else {
                     return pht('%s raised the priority of %s from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old_name, $new_name);
                 }
             }
         case self::TYPE_CCS:
             // TODO: Remove this when we switch to subscribers. Just reuse the
             // code in the parent.
             $clone = clone $this;
             $clone->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS);
             return $clone->getTitleForFeed($story);
         case self::TYPE_EDGE:
             // TODO: Remove this when we switch to real edges. Just reuse the
             // code in the parent;
             $clone = clone $this;
             $clone->setTransactionType(PhabricatorTransactions::TYPE_EDGE);
             return $clone->getTitleForFeed($story);
         case self::TYPE_ATTACH:
             $old = nonempty($old, array());
             $new = nonempty($new, array());
             $new = array_keys(idx($new, 'FILE', array()));
             $old = array_keys(idx($old, 'FILE', array()));
             $added = array_diff($new, $old);
             $removed = array_diff($old, $new);
             if ($added && !$removed) {
                 return pht('%s attached %d file(s) of %s: %s', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), count($added), $this->renderHandleList($added));
             } else {
                 if ($removed && !$added) {
                     return pht('%s detached %d file(s) of %s: %s', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), count($removed), $this->renderHandleList($removed));
                 } else {
                     return pht('%s changed file(s) for %s, attached %d: %s; detached %d: %s', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), count($added), $this->renderHandleList($added), count($removed), $this->renderHandleList($removed));
                 }
             }
         case self::TYPE_PROJECT_COLUMN:
             $project_phid = $new['projectPHID'];
             $column_phid = head($new['columnPHIDs']);
             return pht('%s moved %s to %s on the %s workboard.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $this->renderHandleLink($column_phid), $this->renderHandleLink($project_phid));
         case self::TYPE_MERGED_INTO:
             return pht('%s merged task %s into %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $this->renderHandleLink($new));
         case self::TYPE_MERGED_FROM:
             return pht('%s merged %d task(s) %s into %s.', $this->renderHandleLink($author_phid), count($new), $this->renderHandleList($new), $this->renderHandleLink($object_phid));
     }
     return parent::getTitleForFeed($story);
 }
 private function describeAction($transaction)
 {
     $verb = null;
     $desc = null;
     $classes = array();
     $handles = $this->handles;
     $type = $transaction->getTransactionType();
     $author_phid = $transaction->getAuthorPHID();
     $new = $transaction->getNewValue();
     $old = $transaction->getOldValue();
     switch ($type) {
         case ManiphestTransactionType::TYPE_TITLE:
             $verb = 'Retitled';
             $desc = 'changed the title from ' . $this->renderString($old) . ' to ' . $this->renderString($new);
             break;
         case ManiphestTransactionType::TYPE_DESCRIPTION:
             $verb = 'Edited';
             if ($this->forEmail || $this->getRenderFullSummary()) {
                 $desc = 'updated the task description';
             } else {
                 $desc = 'updated the task description; ' . $this->renderExpandLink($transaction);
             }
             break;
         case ManiphestTransactionType::TYPE_NONE:
             $verb = 'Commented On';
             $desc = 'added a comment';
             break;
         case ManiphestTransactionType::TYPE_OWNER:
             if ($transaction->getAuthorPHID() == $new) {
                 $verb = 'Claimed';
                 $desc = 'claimed this task';
                 $classes[] = 'claimed';
             } else {
                 if (!$new) {
                     $verb = 'Up For Grabs';
                     $desc = 'placed this task up for grabs';
                     $classes[] = 'upforgrab';
                 } else {
                     if (!$old) {
                         $verb = 'Assigned';
                         $desc = 'assigned this task to ' . $this->renderHandles(array($new));
                         $classes[] = 'assigned';
                     } else {
                         $verb = 'Reassigned';
                         $desc = 'reassigned this task from ' . $this->renderHandles(array($old)) . ' to ' . $this->renderHandles(array($new));
                         $classes[] = 'reassigned';
                     }
                 }
             }
             break;
         case ManiphestTransactionType::TYPE_CCS:
             if ($this->preview) {
                 $verb = 'Changed CC';
                 $desc = 'changed CCs..';
                 break;
             }
             $added = array_diff($new, $old);
             $removed = array_diff($old, $new);
             if ($added && !$removed) {
                 $verb = 'Added CC';
                 if (count($added) == 1) {
                     $desc = 'added ' . $this->renderHandles($added) . ' to CC';
                 } else {
                     $desc = 'added CCs: ' . $this->renderHandles($added);
                 }
             } else {
                 if ($removed && !$added) {
                     $verb = 'Removed CC';
                     if (count($removed) == 1) {
                         $desc = 'removed ' . $this->renderHandles($removed) . ' from CC';
                     } else {
                         $desc = 'removed CCs: ' . $this->renderHandles($removed);
                     }
                 } else {
                     $verb = 'Changed CC';
                     $desc = 'changed CCs, added: ' . $this->renderHandles($added) . '; ' . 'removed: ' . $this->renderHandles($removed);
                 }
             }
             break;
         case ManiphestTransactionType::TYPE_PROJECTS:
             if ($this->preview) {
                 $verb = 'Changed Projects';
                 $desc = 'changed projects..';
                 break;
             }
             $added = array_diff($new, $old);
             $removed = array_diff($old, $new);
             if ($added && !$removed) {
                 $verb = 'Added Project';
                 if (count($added) == 1) {
                     $desc = 'added project ' . $this->renderHandles($added);
                 } else {
                     $desc = 'added projects: ' . $this->renderHandles($added);
                 }
             } else {
                 if ($removed && !$added) {
                     $verb = 'Removed Project';
                     if (count($removed) == 1) {
                         $desc = 'removed project ' . $this->renderHandles($removed);
                     } else {
                         $desc = 'removed projectss: ' . $this->renderHandles($removed);
                     }
                 } else {
                     $verb = 'Changed Projects';
                     $desc = 'changed projects, added: ' . $this->renderHandles($added) . '; ' . 'removed: ' . $this->renderHandles($removed);
                 }
             }
             break;
         case ManiphestTransactionType::TYPE_STATUS:
             if ($new == ManiphestTaskStatus::STATUS_OPEN) {
                 if ($old) {
                     $verb = 'Reopened';
                     $desc = 'reopened this task';
                     $classes[] = 'reopened';
                 } else {
                     $verb = 'Created';
                     $desc = 'created this task';
                     $classes[] = 'created';
                 }
             } else {
                 if ($new == ManiphestTaskStatus::STATUS_CLOSED_SPITE) {
                     $verb = 'Spited';
                     $desc = 'closed this task out of spite';
                     $classes[] = 'spited';
                 } else {
                     if ($new == ManiphestTaskStatus::STATUS_CLOSED_DUPLICATE) {
                         $verb = 'Merged';
                         $desc = 'closed this task as a duplicate';
                         $classes[] = 'duplicate';
                     } else {
                         $verb = 'Closed';
                         $full = idx(ManiphestTaskStatus::getTaskStatusMap(), $new, '???');
                         $desc = 'closed this task as "' . $full . '"';
                         $classes[] = 'closed';
                     }
                 }
             }
             break;
         case ManiphestTransactionType::TYPE_PRIORITY:
             $old_name = ManiphestTaskPriority::getTaskPriorityName($old);
             $new_name = ManiphestTaskPriority::getTaskPriorityName($new);
             if ($old == ManiphestTaskPriority::PRIORITY_TRIAGE) {
                 $verb = 'Triaged';
                 $desc = 'triaged this task as "' . $new_name . '" priority';
             } else {
                 if ($old > $new) {
                     $verb = 'Lowered Priority';
                     $desc = 'lowered the priority of this task from "' . $old_name . '" to ' . '"' . $new_name . '"';
                 } else {
                     $verb = 'Raised Priority';
                     $desc = 'raised the priority of this task from "' . $old_name . '" to ' . '"' . $new_name . '"';
                 }
             }
             if ($new == ManiphestTaskPriority::PRIORITY_UNBREAK_NOW) {
                 $classes[] = 'unbreaknow';
             }
             break;
         case ManiphestTransactionType::TYPE_ATTACH:
             if ($this->preview) {
                 $verb = 'Changed Attached';
                 $desc = 'changed attachments..';
                 break;
             }
             $old_raw = nonempty($old, array());
             $new_raw = nonempty($new, array());
             foreach (array(PhabricatorPHIDConstants::PHID_TYPE_DREV, PhabricatorPHIDConstants::PHID_TYPE_TASK, PhabricatorPHIDConstants::PHID_TYPE_FILE) as $type) {
                 $old = array_keys(idx($old_raw, $type, array()));
                 $new = array_keys(idx($new_raw, $type, array()));
                 if ($old != $new) {
                     break;
                 }
             }
             $added = array_diff($new, $old);
             $removed = array_diff($old, $new);
             $add_desc = $this->renderHandles($added);
             $rem_desc = $this->renderHandles($removed);
             switch ($type) {
                 case PhabricatorPHIDConstants::PHID_TYPE_DREV:
                     $singular = 'Differential Revision';
                     $plural = 'Differential Revisions';
                     break;
                 case PhabricatorPHIDConstants::PHID_TYPE_FILE:
                     $singular = 'file';
                     $plural = 'files';
                     break;
                 case PhabricatorPHIDConstants::PHID_TYPE_TASK:
                     $singular = 'Maniphest Task';
                     $plural = 'Maniphest Tasks';
                     $dependency = true;
                     break;
             }
             if ($added && !$removed) {
                 $verb = 'Attached';
                 if (count($added) == 1) {
                     $desc = 'attached ' . $singular . ': ' . $add_desc;
                 } else {
                     $desc = 'attached ' . $plural . ': ' . $add_desc;
                 }
             } else {
                 if ($removed && !$added) {
                     $verb = 'Detached';
                     if (count($removed) == 1) {
                         $desc = 'detached ' . $singular . ': ' . $rem_desc;
                     } else {
                         $desc = 'detached ' . $plural . ': ' . $rem_desc;
                     }
                 } else {
                     $verb = 'Changed Attached';
                     $desc = 'changed attached ' . $plural . ', added: ' . $add_desc . '; ' . 'removed: ' . $rem_desc;
                 }
             }
             break;
         case ManiphestTransactionType::TYPE_AUXILIARY:
             // TODO: This is temporary and hacky! Allow auxiliary fields to
             // customize this.
             $old_esc = phutil_escape_html($old);
             $new_esc = phutil_escape_html($new);
             $aux_key = $transaction->getMetadataValue('aux:key');
             if ($old === null) {
                 $verb = "Set Field";
                 $desc = "set field '{$aux_key}' to '{$new_esc}'";
             } else {
                 if ($new === null) {
                     $verb = "Removed Field";
                     $desc = "removed field '{$aux_key}'";
                 } else {
                     $verb = "Updated Field";
                     $desc = "updated field '{$aux_key}' " . "from '{$old_esc}' to '{$new_esc}'";
                 }
             }
             break;
         default:
             return array($type, ' brazenly ' . $type . "'d", $classes);
     }
     return array($verb, $desc, $classes);
 }
 private function getPriorityName($task)
 {
     $priority_name = new ManiphestTaskPriority();
     return $priority_name->getTaskPriorityName($task->getPriority());
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $e_title = null;
     $priority_map = ManiphestTaskPriority::getTaskPriorityMap();
     $task = id(new ManiphestTask())->load($this->id);
     if (!$task) {
         return new Aphront404Response();
     }
     $transactions = id(new ManiphestTransaction())->loadAllWhere('taskID = %d ORDER BY id ASC', $task->getID());
     $phids = array();
     foreach ($transactions as $transaction) {
         foreach ($transaction->extractPHIDs() as $phid) {
             $phids[$phid] = 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;
     $phids = array_keys($phids);
     $attached = $task->getAttached();
     foreach ($attached as $type => $list) {
         foreach ($list as $phid => $info) {
             $phids[$phid] = true;
         }
     }
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $engine = PhabricatorMarkupEngine::newManiphestMarkupEngine();
     $dict = array();
     $dict['Status'] = '<strong>' . ManiphestTaskStatus::getTaskStatusFullName($task->getStatus()) . '</strong>';
     $dict['Assigned To'] = $task->getOwnerPHID() ? $handles[$task->getOwnerPHID()]->renderLink() : '<em>None</em>';
     $dict['Priority'] = ManiphestTaskPriority::getTaskPriorityName($task->getPriority());
     $cc = $task->getCCPHIDs();
     if ($cc) {
         $cc_links = array();
         foreach ($cc as $phid) {
             $cc_links[] = $handles[$phid]->renderLink();
         }
         $dict['CC'] = implode(', ', $cc_links);
     } else {
         $dict['CC'] = '<em>None</em>';
     }
     $dict['Author'] = $handles[$task->getAuthorPHID()]->renderLink();
     $projects = $task->getProjectPHIDs();
     if ($projects) {
         $project_links = array();
         foreach ($projects as $phid) {
             $project_links[] = $handles[$phid]->renderLink();
         }
         $dict['Projects'] = implode(', ', $project_links);
     } else {
         $dict['Projects'] = '<em>None</em>';
     }
     if (idx($attached, PhabricatorPHIDConstants::PHID_TYPE_DREV)) {
         $revs = idx($attached, PhabricatorPHIDConstants::PHID_TYPE_DREV);
         $rev_links = array();
         foreach ($revs as $rev => $info) {
             $rev_links[] = $handles[$rev]->renderLink();
         }
         $rev_links = implode('<br />', $rev_links);
         $dict['Revisions'] = $rev_links;
     }
     if (idx($attached, PhabricatorPHIDConstants::PHID_TYPE_FILE)) {
         $file_infos = idx($attached, PhabricatorPHIDConstants::PHID_TYPE_FILE);
         $file_phids = array_keys($file_infos);
         if ($file_phids) {
             $files = id(new PhabricatorFile())->loadAllWhere('phid IN (%Ls)', $file_phids);
             $views = array();
             foreach ($files as $file) {
                 $view = new AphrontFilePreviewView();
                 $view->setFile($file);
                 $views[] = $view->render();
             }
             $dict['Files'] = implode('', $views);
         }
     }
     $dict['Description'] = '<div class="maniphest-task-description">' . '<div class="phabricator-remarkup">' . $engine->markupText($task->getDescription()) . '</div>' . '</div>';
     require_celerity_resource('mainphest-task-detail-css');
     $table = array();
     foreach ($dict as $key => $value) {
         $table[] = '<tr>' . '<th>' . phutil_escape_html($key) . ':</th>' . '<td>' . $value . '</td>' . '</tr>';
     }
     $table = '<table class="maniphest-task-properties">' . implode("\n", $table) . '</table>';
     $actions = array();
     $action = new AphrontHeadsupActionView();
     $action->setName('Edit Task');
     $action->setURI('/maniphest/task/edit/' . $task->getID() . '/');
     $action->setClass('action-edit');
     $actions[] = $action;
     require_celerity_resource('phabricator-object-selector-css');
     require_celerity_resource('javelin-behavior-phabricator-object-selector');
     $action = new AphrontHeadsupActionView();
     $action->setName('Merge Duplicates');
     $action->setURI('/search/attach/' . $task->getPHID() . '/TASK/merge/');
     $action->setWorkflow(true);
     $action->setClass('action-merge');
     $actions[] = $action;
     $action = new AphrontHeadsupActionView();
     $action->setName('Edit Differential Revisions');
     $action->setURI('/search/attach/' . $task->getPHID() . '/DREV/');
     $action->setWorkflow(true);
     $action->setClass('action-attach');
     $actions[] = $action;
     $action_list = new AphrontHeadsupActionListView();
     $action_list->setActions($actions);
     $panel = '<div class="maniphest-panel">' . $action_list->render() . '<div class="maniphest-task-detail-core">' . '<h1>' . '<span class="aphront-headsup-object-name">' . phutil_escape_html('T' . $task->getID()) . '</span>' . ' ' . phutil_escape_html($task->getTitle()) . '</h1>' . $table . '</div>' . '</div>';
     $transaction_types = ManiphestTransactionType::getTransactionTypeMap();
     $resolution_types = ManiphestTaskStatus::getTaskStatusMap();
     if ($task->getStatus() == ManiphestTaskStatus::STATUS_OPEN) {
         $resolution_types = array_select_keys($resolution_types, array(ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, ManiphestTaskStatus::STATUS_CLOSED_WONTFIX, ManiphestTaskStatus::STATUS_CLOSED_INVALID, ManiphestTaskStatus::STATUS_CLOSED_SPITE));
     } else {
         $resolution_types = array(ManiphestTaskStatus::STATUS_OPEN => 'Reopened');
         $transaction_types[ManiphestTransactionType::TYPE_STATUS] = 'Reopen Task';
         unset($transaction_types[ManiphestTransactionType::TYPE_PRIORITY]);
         unset($transaction_types[ManiphestTransactionType::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;
     }
     $panel_id = celerity_generate_unique_node_id();
     $comment_form = new AphrontFormView();
     $comment_form->setUser($user)->setAction('/maniphest/transaction/save/')->setEncType('multipart/form-data')->addHiddenInput('taskID', $task->getID())->appendChild(id(new AphrontFormSelectControl())->setLabel('Action')->setName('action')->setOptions($transaction_types)->setID('transaction-action'))->appendChild(id(new AphrontFormSelectControl())->setLabel('Resolution')->setName('resolution')->setControlID('resolution')->setControlStyle('display: none')->setOptions($resolution_types))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('Assign To')->setName('assign_to')->setControlID('assign_to')->setControlStyle('display: none')->setID('assign-tokenizer')->setDisableBehavior(true))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('CCs')->setName('ccs')->setControlID('ccs')->setControlStyle('display: none')->setID('cc-tokenizer')->setDisableBehavior(true))->appendChild(id(new AphrontFormSelectControl())->setLabel('Priority')->setName('priority')->setOptions($priority_map)->setControlID('priority')->setControlStyle('display: none')->setValue($task->getPriority()))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('Projects')->setName('projects')->setControlID('projects')->setControlStyle('display: none')->setID('projects-tokenizer')->setDisableBehavior(true))->appendChild(id(new AphrontFormFileControl())->setLabel('File')->setName('file')->setControlID('file')->setControlStyle('display: none'))->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Comments')->setName('comments')->setValue($draft_text)->setID('transaction-comments'))->appendChild(id(new AphrontFormDragAndDropUploadControl())->setLabel('Attached Files')->setName('files')->setDragAndDropTarget($panel_id)->setActivatedClass('aphront-panel-view-drag-and-drop'))->appendChild(id(new AphrontFormSubmitControl())->setValue('Avast!'));
     $control_map = array(ManiphestTransactionType::TYPE_STATUS => 'resolution', ManiphestTransactionType::TYPE_OWNER => 'assign_to', ManiphestTransactionType::TYPE_CCS => 'ccs', ManiphestTransactionType::TYPE_PRIORITY => 'priority', ManiphestTransactionType::TYPE_PROJECTS => 'projects', ManiphestTransactionType::TYPE_ATTACH => 'file');
     Javelin::initBehavior('maniphest-transaction-controls', array('select' => 'transaction-action', 'controlMap' => $control_map, 'tokenizers' => array(ManiphestTransactionType::TYPE_PROJECTS => array('id' => 'projects-tokenizer', 'src' => '/typeahead/common/projects/'), ManiphestTransactionType::TYPE_OWNER => array('id' => 'assign-tokenizer', 'src' => '/typeahead/common/users/', 'value' => $default_claim, 'limit' => 1), ManiphestTransactionType::TYPE_CCS => array('id' => 'cc-tokenizer', 'src' => '/typeahead/common/mailable/'))));
     Javelin::initBehavior('maniphest-transaction-preview', array('uri' => '/maniphest/transaction/preview/' . $task->getID() . '/', 'preview' => 'transaction-preview', 'comments' => 'transaction-comments', 'action' => 'transaction-action', 'map' => $control_map));
     $comment_panel = new AphrontPanelView();
     $comment_panel->appendChild($comment_form);
     $comment_panel->setID($panel_id);
     $comment_panel->addClass('aphront-panel-accent');
     $comment_panel->setHeader('Weigh In');
     $preview_panel = '<div class="aphront-panel-preview">
     <div id="transaction-preview">
       <div class="aphront-panel-preview-loading-text">
         Loading preview...
       </div>
     </div>
   </div>';
     $transaction_view = new ManiphestTransactionListView();
     $transaction_view->setTransactions($transactions);
     $transaction_view->setHandles($handles);
     $transaction_view->setUser($user);
     $transaction_view->setMarkupEngine($engine);
     return $this->buildStandardPageResponse(array($panel, $transaction_view, $comment_panel, $preview_panel), array('title' => 'T' . $task->getID() . ' ' . $task->getTitle()));
 }
 public static function loadTasks(PhabricatorSearchQuery $search_query)
 {
     $any_project = false;
     $search_text = $search_query->getParameter('fullTextSearch');
     $user_phids = $search_query->getParameter('userPHIDs', array());
     $project_phids = $search_query->getParameter('projectPHIDs', array());
     $task_ids = $search_query->getParameter('taskIDs', array());
     $xproject_phids = $search_query->getParameter('excludeProjectPHIDs', array());
     $owner_phids = $search_query->getParameter('ownerPHIDs', array());
     $author_phids = $search_query->getParameter('authorPHIDs', array());
     $low_priority = $search_query->getParameter('lowPriority');
     $low_priority = nonempty($low_priority, ManiphestTaskPriority::getLowestPriority());
     $high_priority = $search_query->getParameter('highPriority');
     $high_priority = nonempty($high_priority, ManiphestTaskPriority::getHighestPriority());
     $query = new ManiphestTaskQuery();
     $query->withProjects($project_phids);
     $query->withTaskIDs($task_ids);
     if ($xproject_phids) {
         $query->withoutProjects($xproject_phids);
     }
     if ($owner_phids) {
         $query->withOwners($owner_phids);
     }
     if ($author_phids) {
         $query->withAuthors($author_phids);
     }
     $status = $search_query->getParameter('status', 'all');
     if (!empty($status['open']) && !empty($status['closed'])) {
         $query->withStatus(ManiphestTaskQuery::STATUS_ANY);
     } else {
         if (!empty($status['open'])) {
             $query->withStatus(ManiphestTaskQuery::STATUS_OPEN);
         } else {
             $query->withStatus(ManiphestTaskQuery::STATUS_CLOSED);
         }
     }
     switch ($search_query->getParameter('view')) {
         case 'action':
             $query->withOwners($user_phids);
             break;
         case 'created':
             $query->withAuthors($user_phids);
             break;
         case 'subscribed':
             $query->withSubscribers($user_phids);
             break;
         case 'triage':
             $query->withOwners($user_phids);
             $query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
             break;
         case 'alltriage':
             $query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
             break;
         case 'all':
             break;
         case 'projecttriage':
             $query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
             $any_project = true;
             break;
         case 'projectall':
             $any_project = true;
             break;
         case 'custom':
             $query->withPrioritiesBetween($low_priority, $high_priority);
             break;
     }
     $query->withAnyProject($any_project);
     $query->withFullTextSearch($search_text);
     $order_map = array('priority' => ManiphestTaskQuery::ORDER_PRIORITY, 'created' => ManiphestTaskQuery::ORDER_CREATED, 'title' => ManiphestTaskQuery::ORDER_TITLE);
     $query->setOrderBy(idx($order_map, $search_query->getParameter('order'), ManiphestTaskQuery::ORDER_MODIFIED));
     $group_map = array('priority' => ManiphestTaskQuery::GROUP_PRIORITY, 'owner' => ManiphestTaskQuery::GROUP_OWNER, 'status' => ManiphestTaskQuery::GROUP_STATUS, 'project' => ManiphestTaskQuery::GROUP_PROJECT);
     $query->setGroupBy(idx($group_map, $search_query->getParameter('group'), ManiphestTaskQuery::GROUP_NONE));
     $query->setCalculateRows(true);
     $query->setLimit($search_query->getParameter('limit'));
     $query->setOffset($search_query->getParameter('offset'));
     $data = $query->execute();
     $total_row_count = $query->getRowCount();
     $project_group_phids = array();
     if ($search_query->getParameter('group') == 'project') {
         foreach ($data as $task) {
             foreach ($task->getProjectPHIDs() as $phid) {
                 $project_group_phids[] = $phid;
             }
         }
     }
     $handle_phids = mpull($data, 'getOwnerPHID');
     $handle_phids = array_merge($handle_phids, $project_phids, $user_phids, $xproject_phids, $owner_phids, $author_phids, $project_group_phids, array_mergev(mpull($data, 'getProjectPHIDs')));
     $handles = id(new PhabricatorObjectHandleData($handle_phids))->loadHandles();
     switch ($search_query->getParameter('group')) {
         case 'priority':
             $data = mgroup($data, 'getPriority');
             // If we have invalid priorities, they'll all map to "???". Merge
             // arrays to prevent them from overwriting each other.
             $out = array();
             foreach ($data as $pri => $tasks) {
                 $out[ManiphestTaskPriority::getTaskPriorityName($pri)][] = $tasks;
             }
             foreach ($out as $pri => $tasks) {
                 $out[$pri] = array_mergev($tasks);
             }
             $data = $out;
             break;
         case 'status':
             $data = mgroup($data, 'getStatus');
             $out = array();
             foreach ($data as $status => $tasks) {
                 $out[ManiphestTaskStatus::getTaskStatusFullName($status)] = $tasks;
             }
             $data = $out;
             break;
         case 'owner':
             $data = mgroup($data, 'getOwnerPHID');
             $out = array();
             foreach ($data as $phid => $tasks) {
                 if ($phid) {
                     $out[$handles[$phid]->getFullName()] = $tasks;
                 } else {
                     $out['Unassigned'] = $tasks;
                 }
             }
             $data = $out;
             ksort($data);
             // Move "Unassigned" to the top of the list.
             if (isset($data['Unassigned'])) {
                 $data = array('Unassigned' => $out['Unassigned']) + $out;
             }
             break;
         case 'project':
             $grouped = array();
             foreach ($query->getGroupByProjectResults() as $project => $tasks) {
                 foreach ($tasks as $task) {
                     $group = $project ? $handles[$project]->getName() : 'No Project';
                     $grouped[$group][$task->getID()] = $task;
                 }
             }
             $data = $grouped;
             ksort($data);
             // Move "No Project" to the end of the list.
             if (isset($data['No Project'])) {
                 $noproject = $data['No Project'];
                 unset($data['No Project']);
                 $data += array('No Project' => $noproject);
             }
             break;
         default:
             $data = array('Tasks' => $data);
             break;
     }
     return array($data, $handles, $total_row_count);
 }
Пример #13
0
 public function getFieldValuesForConduit()
 {
     $status_value = $this->getStatus();
     $status_info = array('value' => $status_value, 'name' => ManiphestTaskStatus::getTaskStatusName($status_value), 'color' => ManiphestTaskStatus::getStatusColor($status_value));
     $priority_value = (int) $this->getPriority();
     $priority_info = array('value' => $priority_value, 'subpriority' => (double) $this->getSubpriority(), 'name' => ManiphestTaskPriority::getTaskPriorityName($priority_value), 'color' => ManiphestTaskPriority::getTaskPriorityColor($priority_value));
     return array('name' => $this->getTitle(), 'authorPHID' => $this->getAuthorPHID(), 'ownerPHID' => $this->getOwnerPHID(), 'status' => $status_info, 'priority' => $priority_info);
 }
 private function buildPropertyView(ManiphestTask $task, PhabricatorCustomFieldList $field_list, array $edges, PhabricatorActionListView $actions, $handles)
 {
     $viewer = $this->getRequest()->getUser();
     $view = id(new PHUIPropertyListView())->setUser($viewer)->setObject($task)->setActionList($actions);
     $owner_phid = $task->getOwnerPHID();
     if ($owner_phid) {
         $assigned_to = $handles->renderHandle($owner_phid)->setShowHovercard(true);
     } else {
         $assigned_to = phutil_tag('em', array(), pht('None'));
     }
     $view->addProperty(pht('Assigned To'), $assigned_to);
     $view->addProperty(pht('Priority'), ManiphestTaskPriority::getTaskPriorityName($task->getPriority()));
     $author = $handles->renderHandle($task->getAuthorPHID())->setShowHovercard(true);
     $view->addProperty(pht('Author'), $author);
     $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));
     }
     $edge_types = array(ManiphestTaskDependedOnByTaskEdgeType::EDGECONST => pht('Blocks'), ManiphestTaskDependsOnTaskEdgeType::EDGECONST => pht('Blocked By'), ManiphestTaskHasRevisionEdgeType::EDGECONST => pht('Differential Revisions'), ManiphestTaskHasMockEdgeType::EDGECONST => pht('Pholio Mocks'));
     $revisions_commits = array();
     $commit_phids = array_keys($edges[ManiphestTaskHasCommitEdgeType::EDGECONST]);
     if ($commit_phids) {
         $commit_drev = DiffusionCommitHasRevisionEdgeType::EDGECONST;
         $drev_edges = id(new PhabricatorEdgeQuery())->withSourcePHIDs($commit_phids)->withEdgeTypes(array($commit_drev))->execute();
         foreach ($commit_phids as $phid) {
             $revisions_commits[$phid] = $handles->renderHandle($phid)->setShowHovercard(true);
             $revision_phid = key($drev_edges[$phid][$commit_drev]);
             $revision_handle = $handles->getHandleIfExists($revision_phid);
             if ($revision_handle) {
                 $task_drev = ManiphestTaskHasRevisionEdgeType::EDGECONST;
                 unset($edges[$task_drev][$revision_phid]);
                 $revisions_commits[$phid] = hsprintf('%s / %s', $revision_handle->renderHovercardLink($revision_handle->getName()), $revisions_commits[$phid]);
             }
         }
     }
     foreach ($edge_types as $edge_type => $edge_name) {
         if ($edges[$edge_type]) {
             $edge_handles = $viewer->loadHandles(array_keys($edges[$edge_type]));
             $view->addProperty($edge_name, $edge_handles->renderList());
         }
     }
     if ($revisions_commits) {
         $view->addProperty(pht('Commits'), phutil_implode_html(phutil_tag('br'), $revisions_commits));
     }
     $view->invokeWillRenderEvent();
     $field_list->appendFieldsToPropertyList($task, $viewer, $view);
     return $view;
 }
Пример #15
0
 public function getTitleForFeed()
 {
     $author_phid = $this->getAuthorPHID();
     $object_phid = $this->getObjectPHID();
     $old = $this->getOldValue();
     $new = $this->getNewValue();
     switch ($this->getTransactionType()) {
         case self::TYPE_TITLE:
             if ($old === null) {
                 return pht('%s created %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
             }
             return pht('%s renamed %s from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old, $new);
         case self::TYPE_DESCRIPTION:
             return pht('%s edited the description of %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
         case self::TYPE_STATUS:
             $old_closed = ManiphestTaskStatus::isClosedStatus($old);
             $new_closed = ManiphestTaskStatus::isClosedStatus($new);
             $old_name = ManiphestTaskStatus::getTaskStatusName($old);
             $new_name = ManiphestTaskStatus::getTaskStatusName($new);
             $commit_phid = $this->getMetadataValue('commitPHID');
             if ($new_closed && !$old_closed) {
                 if ($new == ManiphestTaskStatus::getDuplicateStatus()) {
                     if ($commit_phid) {
                         return pht('%s closed %s as a duplicate by committing %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $this->renderHandleLink($commit_phid));
                     } else {
                         return pht('%s closed %s as a duplicate.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                     }
                 } else {
                     if ($commit_phid) {
                         return pht('%s closed %s as "%s" by committing %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $new_name, $this->renderHandleLink($commit_phid));
                     } else {
                         return pht('%s closed %s as "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $new_name);
                     }
                 }
             } else {
                 if (!$new_closed && $old_closed) {
                     if ($commit_phid) {
                         return pht('%s reopened %s as "%s" by committing %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $new_name, $this->renderHandleLink($commit_phid));
                     } else {
                         return pht('%s reopened %s as "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $new_name);
                     }
                 } else {
                     if ($commit_phid) {
                         return pht('%s changed the status of %s from "%s" to "%s" by committing %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old_name, $new_name, $this->renderHandleLink($commit_phid));
                     } else {
                         return pht('%s changed the status of %s from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old_name, $new_name);
                     }
                 }
             }
         case self::TYPE_UNBLOCK:
             $blocker_phid = key($new);
             $old_status = head($old);
             $new_status = head($new);
             $old_closed = ManiphestTaskStatus::isClosedStatus($old_status);
             $new_closed = ManiphestTaskStatus::isClosedStatus($new_status);
             $old_name = ManiphestTaskStatus::getTaskStatusName($old_status);
             $new_name = ManiphestTaskStatus::getTaskStatusName($new_status);
             if ($old_closed && !$new_closed) {
                 return pht('%s reopened %s, a subtask of %s, as "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($blocker_phid), $this->renderHandleLink($object_phid), $new_name);
             } else {
                 if (!$old_closed && $new_closed) {
                     return pht('%s closed %s, a subtask of %s, as "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($blocker_phid), $this->renderHandleLink($object_phid), $new_name);
                 } else {
                     return pht('%s changed the status of %s, a subtasktask of %s, ' . 'from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($blocker_phid), $this->renderHandleLink($object_phid), $old_name, $new_name);
                 }
             }
         case self::TYPE_OWNER:
             if ($author_phid == $new) {
                 return pht('%s claimed %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
             } else {
                 if (!$new) {
                     return pht('%s placed %s up for grabs.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                 } else {
                     if (!$old) {
                         return pht('%s assigned %s to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $this->renderHandleLink($new));
                     } else {
                         return pht('%s reassigned %s from %s to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $this->renderHandleLink($old), $this->renderHandleLink($new));
                     }
                 }
             }
         case self::TYPE_PRIORITY:
             $old_name = ManiphestTaskPriority::getTaskPriorityName($old);
             $new_name = ManiphestTaskPriority::getTaskPriorityName($new);
             if ($old == ManiphestTaskPriority::getDefaultPriority()) {
                 return pht('%s triaged %s as "%s" priority.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $new_name);
             } else {
                 if ($old > $new) {
                     return pht('%s lowered the priority of %s from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old_name, $new_name);
                 } else {
                     return pht('%s raised the priority of %s from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old_name, $new_name);
                 }
             }
         case self::TYPE_ATTACH:
             $old = nonempty($old, array());
             $new = nonempty($new, array());
             $new = array_keys(idx($new, 'FILE', array()));
             $old = array_keys(idx($old, 'FILE', array()));
             $added = array_diff($new, $old);
             $removed = array_diff($old, $new);
             if ($added && !$removed) {
                 return pht('%s attached %d file(s) of %s: %s', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), count($added), $this->renderHandleList($added));
             } else {
                 if ($removed && !$added) {
                     return pht('%s detached %d file(s) of %s: %s', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), count($removed), $this->renderHandleList($removed));
                 } else {
                     return pht('%s changed file(s) for %s, attached %d: %s; detached %d: %s', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), count($added), $this->renderHandleList($added), count($removed), $this->renderHandleList($removed));
                 }
             }
         case self::TYPE_MERGED_INTO:
             return pht('%s merged task %s into %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $this->renderHandleLink($new));
         case self::TYPE_MERGED_FROM:
             return pht('%s merged %s task(s) %s into %s.', $this->renderHandleLink($author_phid), phutil_count($new), $this->renderHandleList($new), $this->renderHandleLink($object_phid));
     }
     return parent::getTitleForFeed();
 }
 private function describeAction($transaction)
 {
     $verb = null;
     $desc = null;
     $classes = array();
     $handles = $this->handles;
     $type = $transaction->getTransactionType();
     $author_phid = $transaction->getAuthorPHID();
     $new = $transaction->getNewValue();
     $old = $transaction->getOldValue();
     switch ($type) {
         case ManiphestTransactionType::TYPE_TITLE:
             $verb = 'Retitled';
             $desc = 'changed the title from ' . $this->renderString($old) . ' to ' . $this->renderString($new);
             break;
         case ManiphestTransactionType::TYPE_DESCRIPTION:
             $verb = 'Edited';
             if ($this->forEmail || $this->getRenderFullSummary()) {
                 $desc = 'updated the task description';
             } else {
                 $desc = 'updated the task description; ' . $this->renderExpandLink($transaction);
             }
             break;
         case ManiphestTransactionType::TYPE_NONE:
             $verb = 'Commented On';
             $desc = 'added a comment';
             break;
         case ManiphestTransactionType::TYPE_OWNER:
             if ($transaction->getAuthorPHID() == $new) {
                 $verb = 'Claimed';
                 $desc = 'claimed this task';
                 $classes[] = 'claimed';
             } else {
                 if (!$new) {
                     $verb = 'Up For Grabs';
                     $desc = 'placed this task up for grabs';
                     $classes[] = 'upforgrab';
                 } else {
                     if (!$old) {
                         $verb = 'Assigned';
                         $desc = 'assigned this task to ' . $this->renderHandles(array($new));
                         $classes[] = 'assigned';
                     } else {
                         $verb = 'Reassigned';
                         $desc = 'reassigned this task from ' . $this->renderHandles(array($old)) . ' to ' . $this->renderHandles(array($new));
                         $classes[] = 'reassigned';
                     }
                 }
             }
             break;
         case ManiphestTransactionType::TYPE_CCS:
             $added = array_diff($new, $old);
             $removed = array_diff($old, $new);
             // can only add in preview so just show placeholder if nothing to add
             if ($this->preview && empty($added)) {
                 $verb = 'Changed CC';
                 $desc = 'changed CCs..';
                 break;
             }
             if ($added && !$removed) {
                 $verb = 'Added CC';
                 if (count($added) == 1) {
                     $desc = 'added ' . $this->renderHandles($added) . ' to CC';
                 } else {
                     $desc = 'added CCs: ' . $this->renderHandles($added);
                 }
             } else {
                 if ($removed && !$added) {
                     $verb = 'Removed CC';
                     if (count($removed) == 1) {
                         $desc = 'removed ' . $this->renderHandles($removed) . ' from CC';
                     } else {
                         $desc = 'removed CCs: ' . $this->renderHandles($removed);
                     }
                 } else {
                     $verb = 'Changed CC';
                     $desc = 'changed CCs, added: ' . $this->renderHandles($added) . '; ' . 'removed: ' . $this->renderHandles($removed);
                 }
             }
             break;
         case ManiphestTransactionType::TYPE_EDGE:
             $edge_type = $transaction->getMetadataValue('edge:type');
             $add = array_diff_key($new, $old);
             $rem = array_diff_key($old, $new);
             if ($add && !$rem) {
                 $verb = $this->getEdgeAddVerb($edge_type);
                 $desc = $this->getEdgeAddList($edge_type, $add);
             } else {
                 if ($rem && !$add) {
                     $verb = $this->getEdgeRemVerb($edge_type);
                     $desc = $this->getEdgeRemList($edge_type, $rem);
                 } else {
                     $verb = $this->getEdgeEditVerb($edge_type);
                     $desc = $this->getEdgeEditList($edge_type, $add, $rem);
                 }
             }
             break;
         case ManiphestTransactionType::TYPE_PROJECTS:
             $added = array_diff($new, $old);
             $removed = array_diff($old, $new);
             // can only add in preview so just show placeholder if nothing to add
             if ($this->preview && empty($added)) {
                 $verb = 'Changed Projects';
                 $desc = 'changed projects..';
                 break;
             }
             if ($added && !$removed) {
                 $verb = 'Added Project';
                 if (count($added) == 1) {
                     $desc = 'added project ' . $this->renderHandles($added);
                 } else {
                     $desc = 'added projects: ' . $this->renderHandles($added);
                 }
             } else {
                 if ($removed && !$added) {
                     $verb = 'Removed Project';
                     if (count($removed) == 1) {
                         $desc = 'removed project ' . $this->renderHandles($removed);
                     } else {
                         $desc = 'removed projects: ' . $this->renderHandles($removed);
                     }
                 } else {
                     $verb = 'Changed Projects';
                     $desc = 'changed projects, added: ' . $this->renderHandles($added) . '; ' . 'removed: ' . $this->renderHandles($removed);
                 }
             }
             break;
         case ManiphestTransactionType::TYPE_STATUS:
             if ($new == ManiphestTaskStatus::STATUS_OPEN) {
                 if ($old) {
                     $verb = 'Reopened';
                     $desc = 'reopened this task';
                     $classes[] = 'reopened';
                 } else {
                     $verb = 'Created';
                     $desc = 'created this task';
                     $classes[] = 'created';
                 }
             } else {
                 if ($new == ManiphestTaskStatus::STATUS_CLOSED_SPITE) {
                     $verb = 'Spited';
                     $desc = 'closed this task out of spite';
                     $classes[] = 'spited';
                 } else {
                     if ($new == ManiphestTaskStatus::STATUS_CLOSED_DUPLICATE) {
                         $verb = 'Merged';
                         $desc = 'closed this task as a duplicate';
                         $classes[] = 'duplicate';
                     } else {
                         $verb = 'Closed';
                         $full = idx(ManiphestTaskStatus::getTaskStatusMap(), $new, '???');
                         $desc = 'closed this task as "' . $full . '"';
                         $classes[] = 'closed';
                     }
                 }
             }
             break;
         case ManiphestTransactionType::TYPE_PRIORITY:
             $old_name = ManiphestTaskPriority::getTaskPriorityName($old);
             $new_name = ManiphestTaskPriority::getTaskPriorityName($new);
             if ($old == ManiphestTaskPriority::PRIORITY_TRIAGE) {
                 $verb = 'Triaged';
                 $desc = 'triaged this task as "' . $new_name . '" priority';
             } else {
                 if ($old > $new) {
                     $verb = 'Lowered Priority';
                     $desc = 'lowered the priority of this task from "' . $old_name . '" to ' . '"' . $new_name . '"';
                 } else {
                     $verb = 'Raised Priority';
                     $desc = 'raised the priority of this task from "' . $old_name . '" to ' . '"' . $new_name . '"';
                 }
             }
             if ($new == ManiphestTaskPriority::PRIORITY_UNBREAK_NOW) {
                 $classes[] = 'unbreaknow';
             }
             break;
         case ManiphestTransactionType::TYPE_ATTACH:
             if ($this->preview) {
                 $verb = 'Changed Attached';
                 $desc = 'changed attachments..';
                 break;
             }
             $old_raw = nonempty($old, array());
             $new_raw = nonempty($new, array());
             foreach (array(PhabricatorPHIDConstants::PHID_TYPE_DREV, PhabricatorPHIDConstants::PHID_TYPE_TASK, PhabricatorPHIDConstants::PHID_TYPE_FILE) as $attach_type) {
                 $old = array_keys(idx($old_raw, $attach_type, array()));
                 $new = array_keys(idx($new_raw, $attach_type, array()));
                 if ($old != $new) {
                     break;
                 }
             }
             $added = array_diff($new, $old);
             $removed = array_diff($old, $new);
             $add_desc = $this->renderHandles($added);
             $rem_desc = $this->renderHandles($removed);
             if ($added && !$removed) {
                 $verb = 'Attached';
                 $desc = 'attached ' . $this->getAttachName($attach_type, count($added)) . ': ' . $add_desc;
             } else {
                 if ($removed && !$added) {
                     $verb = 'Detached';
                     $desc = 'detached ' . $this->getAttachName($attach_type, count($removed)) . ': ' . $rem_desc;
                 } else {
                     $verb = 'Changed Attached';
                     $desc = 'changed attached ' . $this->getAttachName($attach_type, count($added) + count($removed)) . ', added: ' . $add_desc . '; ' . 'removed: ' . $rem_desc;
                 }
             }
             break;
         case ManiphestTransactionType::TYPE_AUXILIARY:
             $aux_key = $transaction->getMetadataValue('aux:key');
             $aux_field = $this->getAuxiliaryField($aux_key);
             $verb = null;
             if ($aux_field) {
                 $verb = $aux_field->renderTransactionEmailVerb($transaction);
             }
             if ($verb === null) {
                 if ($old === null) {
                     $verb = "Set Field";
                 } else {
                     if ($new === null) {
                         $verb = "Removed Field";
                     } else {
                         $verb = "Updated Field";
                     }
                 }
             }
             $desc = null;
             if ($aux_field) {
                 $use_field = $aux_field;
             } else {
                 $use_field = id(new ManiphestAuxiliaryFieldDefaultSpecification())->setFieldType(ManiphestAuxiliaryFieldDefaultSpecification::TYPE_STRING);
             }
             $desc = $use_field->renderTransactionDescription($transaction, $this->forEmail ? ManiphestAuxiliaryFieldSpecification::RENDER_TARGET_TEXT : ManiphestAuxiliaryFieldSpecification::RENDER_TARGET_HTML);
             break;
         default:
             return array($type, ' brazenly ' . $type . "'d", $classes);
     }
     return array($verb, $desc, $classes);
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $e_title = null;
     $priority_map = ManiphestTaskPriority::getTaskPriorityMap();
     $task = id(new ManiphestTask())->load($this->id);
     if (!$task) {
         return new Aphront404Response();
     }
     $workflow = $request->getStr('workflow');
     $parent_task = null;
     if ($workflow && is_numeric($workflow)) {
         $parent_task = id(new ManiphestTask())->load($workflow);
     }
     $transactions = id(new ManiphestTransaction())->loadAllWhere('taskID = %d ORDER BY id ASC', $task->getID());
     $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;
     $phid = $task->getPHID();
     $query = id(new PhabricatorEdgeQuery())->withSourcePHIDs(array($phid))->withEdgeTypes(array($e_commit, $e_dep_on, $e_dep_by, $e_rev));
     $edges = $query->execute();
     $commit_phids = array_keys($edges[$phid][$e_commit]);
     $dep_on_tasks = array_keys($edges[$phid][$e_dep_on]);
     $dep_by_tasks = array_keys($edges[$phid][$e_dep_by]);
     $revs = array_keys($edges[$phid][$e_rev]);
     $phids = array_fill_keys($query->getDestinationPHIDs(), true);
     foreach ($transactions as $transaction) {
         foreach ($transaction->extractPHIDs() as $phid) {
             $phids[$phid] = 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);
     $handles = $this->loadViewerHandles($phids);
     $dict = array();
     $dict['Status'] = '<strong>' . ManiphestTaskStatus::getTaskStatusFullName($task->getStatus()) . '</strong>';
     $dict['Assigned To'] = $task->getOwnerPHID() ? $handles[$task->getOwnerPHID()]->renderLink() : '<em>None</em>';
     $dict['Priority'] = ManiphestTaskPriority::getTaskPriorityName($task->getPriority());
     $cc = $task->getCCPHIDs();
     if ($cc) {
         $cc_links = array();
         foreach ($cc as $phid) {
             $cc_links[] = $handles[$phid]->renderLink();
         }
         $dict['CC'] = implode(', ', $cc_links);
     } else {
         $dict['CC'] = '<em>None</em>';
     }
     $dict['Author'] = $handles[$task->getAuthorPHID()]->renderLink();
     $source = $task->getOriginalEmailSource();
     if ($source) {
         $subject = '[T' . $task->getID() . '] ' . $task->getTitle();
         $dict['From Email'] = phutil_render_tag('a', array('href' => 'mailto:' . $source . '?subject=' . $subject), phutil_escape_html($source));
     }
     $projects = $task->getProjectPHIDs();
     if ($projects) {
         $project_links = array();
         foreach ($projects as $phid) {
             $project_links[] = $handles[$phid]->renderLink();
         }
         $dict['Projects'] = implode(', ', $project_links);
     } else {
         $dict['Projects'] = '<em>None</em>';
     }
     $extensions = ManiphestTaskExtensions::newExtensions();
     $aux_fields = $extensions->getAuxiliaryFieldSpecifications();
     if ($aux_fields) {
         $task->loadAndAttachAuxiliaryAttributes();
         foreach ($aux_fields as $aux_field) {
             $aux_key = $aux_field->getAuxiliaryKey();
             $aux_field->setValue($task->getAuxiliaryAttribute($aux_key));
             $value = $aux_field->renderForDetailView();
             if (strlen($value)) {
                 $dict[$aux_field->getLabel()] = $value;
             }
         }
     }
     if ($dep_by_tasks) {
         $dict['Dependent Tasks'] = $this->renderHandleList(array_select_keys($handles, $dep_by_tasks));
     }
     if ($dep_on_tasks) {
         $dict['Depends On'] = $this->renderHandleList(array_select_keys($handles, $dep_on_tasks));
     }
     if ($revs) {
         $dict['Revisions'] = $this->renderHandleList(array_select_keys($handles, $revs));
     }
     if ($commit_phids) {
         $dict['Commits'] = $this->renderHandleList(array_select_keys($handles, $commit_phids));
     }
     $file_infos = idx($attached, PhabricatorPHIDConstants::PHID_TYPE_FILE);
     if ($file_infos) {
         $file_phids = array_keys($file_infos);
         $files = id(new PhabricatorFile())->loadAllWhere('phid IN (%Ls)', $file_phids);
         $views = array();
         foreach ($files as $file) {
             $view = new AphrontFilePreviewView();
             $view->setFile($file);
             $views[] = $view->render();
         }
         $dict['Files'] = implode('', $views);
     }
     $context_bar = null;
     if ($parent_task) {
         $context_bar = new AphrontContextBarView();
         $context_bar->addButton(phutil_render_tag('a', array('href' => '/maniphest/task/create/?parent=' . $parent_task->getID(), 'class' => 'green button'), 'Create Another Subtask'));
         $context_bar->appendChild('Created a subtask of <strong>' . $handles[$parent_task->getPHID()]->renderLink() . '</strong>');
     } else {
         if ($workflow == 'create') {
             $context_bar = new AphrontContextBarView();
             $context_bar->addButton('<label>Create Another:</label>');
             $context_bar->addButton(phutil_render_tag('a', array('href' => '/maniphest/task/create/?template=' . $task->getID(), 'class' => 'green button'), 'Similar Task'));
             $context_bar->addButton(phutil_render_tag('a', array('href' => '/maniphest/task/create/', 'class' => 'green button'), 'Empty Task'));
             $context_bar->appendChild('New task created.');
         }
     }
     $actions = array();
     $action = new AphrontHeadsupActionView();
     $action->setName('Edit Task');
     $action->setURI('/maniphest/task/edit/' . $task->getID() . '/');
     $action->setClass('action-edit');
     $actions[] = $action;
     require_celerity_resource('phabricator-flag-css');
     $flag = PhabricatorFlagQuery::loadUserFlag($user, $task->getPHID());
     if ($flag) {
         $class = PhabricatorFlagColor::getCSSClass($flag->getColor());
         $color = PhabricatorFlagColor::getColorName($flag->getColor());
         $action = new AphrontHeadsupActionView();
         $action->setClass('flag-clear ' . $class);
         $action->setURI('/flag/delete/' . $flag->getID() . '/');
         $action->setName('Remove ' . $color . ' Flag');
         $action->setWorkflow(true);
         $actions[] = $action;
     } else {
         $action = new AphrontHeadsupActionView();
         $action->setClass('phabricator-flag-ghost');
         $action->setURI('/flag/edit/' . $task->getPHID() . '/');
         $action->setName('Flag Task');
         $action->setWorkflow(true);
         $actions[] = $action;
     }
     require_celerity_resource('phabricator-object-selector-css');
     require_celerity_resource('javelin-behavior-phabricator-object-selector');
     $action = new AphrontHeadsupActionView();
     $action->setName('Merge Duplicates');
     $action->setURI('/search/attach/' . $task->getPHID() . '/TASK/merge/');
     $action->setWorkflow(true);
     $action->setClass('action-merge');
     $actions[] = $action;
     $action = new AphrontHeadsupActionView();
     $action->setName('Create Subtask');
     $action->setURI('/maniphest/task/create/?parent=' . $task->getID());
     $action->setClass('action-branch');
     $actions[] = $action;
     $action = new AphrontHeadsupActionView();
     $action->setName('Edit Dependencies');
     $action->setURI('/search/attach/' . $task->getPHID() . '/TASK/dependencies/');
     $action->setWorkflow(true);
     $action->setClass('action-dependencies');
     $actions[] = $action;
     $action = new AphrontHeadsupActionView();
     $action->setName('Edit Differential Revisions');
     $action->setURI('/search/attach/' . $task->getPHID() . '/DREV/');
     $action->setWorkflow(true);
     $action->setClass('action-attach');
     $actions[] = $action;
     $action_list = new AphrontHeadsupActionListView();
     $action_list->setActions($actions);
     $headsup_panel = new AphrontHeadsupView();
     $headsup_panel->setObjectName('T' . $task->getID());
     $headsup_panel->setHeader($task->getTitle());
     $headsup_panel->setActionList($action_list);
     $headsup_panel->setProperties($dict);
     $engine = new PhabricatorMarkupEngine();
     $engine->setViewer($user);
     $engine->addObject($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION);
     foreach ($transactions as $xaction) {
         if ($xaction->hasComments()) {
             $engine->addObject($xaction, ManiphestTransaction::MARKUP_FIELD_BODY);
         }
     }
     $engine->process();
     $headsup_panel->appendChild('<div class="phabricator-remarkup">' . $engine->getOutput($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION) . '</div>');
     $transaction_types = ManiphestTransactionType::getTransactionTypeMap();
     $resolution_types = ManiphestTaskStatus::getTaskStatusMap();
     if ($task->getStatus() == ManiphestTaskStatus::STATUS_OPEN) {
         $resolution_types = array_select_keys($resolution_types, array(ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, ManiphestTaskStatus::STATUS_CLOSED_WONTFIX, ManiphestTaskStatus::STATUS_CLOSED_INVALID, ManiphestTaskStatus::STATUS_CLOSED_SPITE));
     } else {
         $resolution_types = array(ManiphestTaskStatus::STATUS_OPEN => 'Reopened');
         $transaction_types[ManiphestTransactionType::TYPE_STATUS] = 'Reopen Task';
         unset($transaction_types[ManiphestTransactionType::TYPE_PRIORITY]);
         unset($transaction_types[ManiphestTransactionType::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;
     }
     $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
     if ($is_serious) {
         // Prevent tasks from being closed "out of spite" in serious business
         // installs.
         unset($resolution_types[ManiphestTaskStatus::STATUS_CLOSED_SPITE]);
     }
     $comment_form = new AphrontFormView();
     $comment_form->setUser($user)->setAction('/maniphest/transaction/save/')->setEncType('multipart/form-data')->addHiddenInput('taskID', $task->getID())->appendChild(id(new AphrontFormSelectControl())->setLabel('Action')->setName('action')->setOptions($transaction_types)->setID('transaction-action'))->appendChild(id(new AphrontFormSelectControl())->setLabel('Resolution')->setName('resolution')->setControlID('resolution')->setControlStyle('display: none')->setOptions($resolution_types))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('Assign To')->setName('assign_to')->setControlID('assign_to')->setControlStyle('display: none')->setID('assign-tokenizer')->setDisableBehavior(true))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('CCs')->setName('ccs')->setControlID('ccs')->setControlStyle('display: none')->setID('cc-tokenizer')->setDisableBehavior(true))->appendChild(id(new AphrontFormSelectControl())->setLabel('Priority')->setName('priority')->setOptions($priority_map)->setControlID('priority')->setControlStyle('display: none')->setValue($task->getPriority()))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('Projects')->setName('projects')->setControlID('projects')->setControlStyle('display: none')->setID('projects-tokenizer')->setDisableBehavior(true))->appendChild(id(new AphrontFormFileControl())->setLabel('File')->setName('file')->setControlID('file')->setControlStyle('display: none'))->appendChild(id(new PhabricatorRemarkupControl())->setLabel('Comments')->setName('comments')->setValue($draft_text)->setID('transaction-comments'))->appendChild(id(new AphrontFormDragAndDropUploadControl())->setLabel('Attached Files')->setName('files')->setActivatedClass('aphront-panel-view-drag-and-drop'))->appendChild(id(new AphrontFormSubmitControl())->setValue($is_serious ? 'Submit' : 'Avast!'));
     $control_map = array(ManiphestTransactionType::TYPE_STATUS => 'resolution', ManiphestTransactionType::TYPE_OWNER => 'assign_to', ManiphestTransactionType::TYPE_CCS => 'ccs', ManiphestTransactionType::TYPE_PRIORITY => 'priority', ManiphestTransactionType::TYPE_PROJECTS => 'projects', ManiphestTransactionType::TYPE_ATTACH => 'file');
     $tokenizer_map = array(ManiphestTransactionType::TYPE_PROJECTS => array('id' => 'projects-tokenizer', 'src' => '/typeahead/common/projects/', 'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'), 'placeholder' => 'Type a project name...'), ManiphestTransactionType::TYPE_OWNER => array('id' => 'assign-tokenizer', 'src' => '/typeahead/common/users/', 'value' => $default_claim, 'limit' => 1, 'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'), 'placeholder' => 'Type a user name...'), ManiphestTransactionType::TYPE_CCS => array('id' => 'cc-tokenizer', 'src' => '/typeahead/common/mailable/', 'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'), 'placeholder' => 'Type a user or mailing list...'));
     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));
     $comment_panel = new AphrontPanelView();
     $comment_panel->appendChild($comment_form);
     $comment_panel->addClass('aphront-panel-accent');
     $comment_panel->setHeader($is_serious ? 'Add Comment' : 'Weigh In');
     $preview_panel = '<div class="aphront-panel-preview">
     <div id="transaction-preview">
       <div class="aphront-panel-preview-loading-text">
         Loading preview...
       </div>
     </div>
   </div>';
     $transaction_view = new ManiphestTransactionListView();
     $transaction_view->setTransactions($transactions);
     $transaction_view->setHandles($handles);
     $transaction_view->setUser($user);
     $transaction_view->setAuxiliaryFields($aux_fields);
     $transaction_view->setMarkupEngine($engine);
     PhabricatorFeedStoryNotification::updateObjectNotificationViews($user, $task->getPHID());
     return $this->buildStandardPageResponse(array($context_bar, $headsup_panel, $transaction_view, $comment_panel, $preview_panel), array('title' => 'T' . $task->getID() . ' ' . $task->getTitle(), 'pageObjects' => array($task->getPHID())));
 }
 protected function buildTaskInfoDictionaries(array $tasks)
 {
     assert_instances_of($tasks, 'ManiphestTask');
     if (!$tasks) {
         return array();
     }
     $task_phids = mpull($tasks, 'getPHID');
     $all_deps = id(new PhabricatorEdgeQuery())->withSourcePHIDs($task_phids)->withEdgeTypes(array(ManiphestTaskDependsOnTaskEdgeType::EDGECONST));
     $all_deps->execute();
     $result = array();
     foreach ($tasks as $task) {
         // TODO: Batch this get as CustomField gets cleaned up.
         $field_list = PhabricatorCustomField::getObjectFields($task, PhabricatorCustomField::ROLE_EDIT);
         $field_list->readFieldsFromStorage($task);
         $auxiliary = mpull($field_list->getFields(), 'getValueForStorage', 'getFieldKey');
         $task_deps = $all_deps->getDestinationPHIDs(array($task->getPHID()), array(ManiphestTaskDependsOnTaskEdgeType::EDGECONST));
         $result[$task->getPHID()] = array('id' => $task->getID(), 'phid' => $task->getPHID(), 'authorPHID' => $task->getAuthorPHID(), 'ownerPHID' => $task->getOwnerPHID(), 'ccPHIDs' => $task->getSubscriberPHIDs(), 'status' => $task->getStatus(), 'statusName' => ManiphestTaskStatus::getTaskStatusName($task->getStatus()), 'isClosed' => $task->isClosed(), 'priority' => ManiphestTaskPriority::getTaskPriorityName($task->getPriority()), 'priorityColor' => ManiphestTaskPriority::getTaskPriorityColor($task->getPriority()), 'title' => $task->getTitle(), 'description' => $task->getDescription(), 'projectPHIDs' => $task->getProjectPHIDs(), 'uri' => PhabricatorEnv::getProductionURI('/T' . $task->getID()), 'auxiliary' => $auxiliary, 'objectName' => 'T' . $task->getID(), 'dateCreated' => $task->getDateCreated(), 'dateModified' => $task->getDateModified(), 'dependsOnTaskPHIDs' => $task_deps);
     }
     return $result;
 }
 private function getTaskLabelName($group, $label_key, array $handles)
 {
     switch ($group) {
         case 'priority':
             return ManiphestTaskPriority::getTaskPriorityName($label_key);
         case 'status':
             return ManiphestTaskStatus::getTaskStatusFullName($label_key);
         case 'assigned':
             if ($label_key) {
                 return $handles[$label_key]->getFullName();
             } else {
                 return pht('(Not Assigned)');
             }
         case 'project':
             if ($label_key) {
                 return $handles[$label_key]->getFullName();
             } else {
                 // This may mean "No Projects", or it may mean the query has project
                 // constraints but the task is only in constrained projects (in this
                 // case, we don't show the group because it would always have all
                 // of the tasks). Since distinguishing between these two cases is
                 // messy and the UI is reasonably clear, label generically.
                 return pht('(Ungrouped)');
             }
         default:
             return pht('Tasks');
     }
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $e_title = null;
     $priority_map = ManiphestTaskPriority::getTaskPriorityMap();
     $task = id(new ManiphestTask())->load($this->id);
     if (!$task) {
         return new Aphront404Response();
     }
     $workflow = $request->getStr('workflow');
     $parent_task = null;
     if ($workflow && is_numeric($workflow)) {
         $parent_task = id(new ManiphestTask())->load($workflow);
     }
     $transactions = id(new ManiphestTransaction())->loadAllWhere('taskID = %d ORDER BY id ASC', $task->getID());
     $phids = array();
     foreach ($transactions as $transaction) {
         foreach ($transaction->extractPHIDs() as $phid) {
             $phids[$phid] = 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);
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $engine = PhabricatorMarkupEngine::newManiphestMarkupEngine();
     $dict = array();
     $dict['Status'] = '<strong>' . ManiphestTaskStatus::getTaskStatusFullName($task->getStatus()) . '</strong>';
     $dict['Assigned To'] = $task->getOwnerPHID() ? $handles[$task->getOwnerPHID()]->renderLink() : '<em>None</em>';
     $dict['Priority'] = ManiphestTaskPriority::getTaskPriorityName($task->getPriority());
     $cc = $task->getCCPHIDs();
     if ($cc) {
         $cc_links = array();
         foreach ($cc as $phid) {
             $cc_links[] = $handles[$phid]->renderLink();
         }
         $dict['CC'] = implode(', ', $cc_links);
     } else {
         $dict['CC'] = '<em>None</em>';
     }
     $dict['Author'] = $handles[$task->getAuthorPHID()]->renderLink();
     $source = $task->getOriginalEmailSource();
     if ($source) {
         $subject = '[T' . $task->getID() . '] ' . $task->getTitle();
         $dict['From Email'] = phutil_render_tag('a', array('href' => 'mailto:' . $source . '?subject=' . $subject), phutil_escape_html($source));
     }
     $projects = $task->getProjectPHIDs();
     if ($projects) {
         $project_links = array();
         foreach ($projects as $phid) {
             $project_links[] = $handles[$phid]->renderLink();
         }
         $dict['Projects'] = implode(', ', $project_links);
     } else {
         $dict['Projects'] = '<em>None</em>';
     }
     $extensions = ManiphestTaskExtensions::newExtensions();
     $aux_fields = $extensions->getAuxiliaryFieldSpecifications();
     if ($aux_fields) {
         $task->loadAndAttachAuxiliaryAttributes();
         foreach ($aux_fields as $aux_field) {
             $aux_key = $aux_field->getAuxiliaryKey();
             $aux_field->setValue($task->getAuxiliaryAttribute($aux_key));
             $value = $aux_field->renderForDetailView();
             if (strlen($value)) {
                 $dict[$aux_field->getLabel()] = $value;
             }
         }
     }
     $dtasks = idx($attached, PhabricatorPHIDConstants::PHID_TYPE_TASK);
     if ($dtasks) {
         $dtask_links = array();
         foreach ($dtasks as $dtask => $info) {
             $dtask_links[] = $handles[$dtask]->renderLink();
         }
         $dtask_links = implode('<br />', $dtask_links);
         $dict['Depends On'] = $dtask_links;
     }
     $revs = idx($attached, PhabricatorPHIDConstants::PHID_TYPE_DREV);
     if ($revs) {
         $rev_links = array();
         foreach ($revs as $rev => $info) {
             $rev_links[] = $handles[$rev]->renderLink();
         }
         $rev_links = implode('<br />', $rev_links);
         $dict['Revisions'] = $rev_links;
     }
     $file_infos = idx($attached, PhabricatorPHIDConstants::PHID_TYPE_FILE);
     if ($file_infos) {
         $file_phids = array_keys($file_infos);
         $files = id(new PhabricatorFile())->loadAllWhere('phid IN (%Ls)', $file_phids);
         $views = array();
         foreach ($files as $file) {
             $view = new AphrontFilePreviewView();
             $view->setFile($file);
             $views[] = $view->render();
         }
         $dict['Files'] = implode('', $views);
     }
     $dict['Description'] = '<div class="maniphest-task-description">' . '<div class="phabricator-remarkup">' . $engine->markupText($task->getDescription()) . '</div>' . '</div>';
     require_celerity_resource('mainphest-task-detail-css');
     $table = array();
     foreach ($dict as $key => $value) {
         $table[] = '<tr>' . '<th>' . phutil_escape_html($key) . ':</th>' . '<td>' . $value . '</td>' . '</tr>';
     }
     $table = '<table class="maniphest-task-properties">' . implode("\n", $table) . '</table>';
     $context_bar = null;
     if ($parent_task) {
         $context_bar = new AphrontContextBarView();
         $context_bar->addButton(phutil_render_tag('a', array('href' => '/maniphest/task/create/?parent=' . $parent_task->getID(), 'class' => 'green button'), 'Create Another Subtask'));
         $context_bar->appendChild('Created a subtask of <strong>' . $handles[$parent_task->getPHID()]->renderLink() . '</strong>');
     } else {
         if ($workflow == 'create') {
             $context_bar = new AphrontContextBarView();
             $context_bar->addButton(phutil_render_tag('a', array('href' => '/maniphest/task/create/?template=' . $task->getID(), 'class' => 'green button'), 'Create Another Task'));
             $context_bar->appendChild('New task created.');
         }
     }
     $actions = array();
     $action = new AphrontHeadsupActionView();
     $action->setName('Edit Task');
     $action->setURI('/maniphest/task/edit/' . $task->getID() . '/');
     $action->setClass('action-edit');
     $actions[] = $action;
     require_celerity_resource('phabricator-object-selector-css');
     require_celerity_resource('javelin-behavior-phabricator-object-selector');
     $action = new AphrontHeadsupActionView();
     $action->setName('Merge Duplicates');
     $action->setURI('/search/attach/' . $task->getPHID() . '/TASK/merge/');
     $action->setWorkflow(true);
     $action->setClass('action-merge');
     $actions[] = $action;
     $action = new AphrontHeadsupActionView();
     $action->setName('Create Subtask');
     $action->setURI('/maniphest/task/create/?parent=' . $task->getID());
     $action->setClass('action-branch');
     $actions[] = $action;
     $action = new AphrontHeadsupActionView();
     $action->setName('Edit Dependencies');
     $action->setURI('/search/attach/' . $task->getPHID() . '/TASK/dependencies/');
     $action->setWorkflow(true);
     $action->setClass('action-dependencies');
     $actions[] = $action;
     $action = new AphrontHeadsupActionView();
     $action->setName('Edit Differential Revisions');
     $action->setURI('/search/attach/' . $task->getPHID() . '/DREV/');
     $action->setWorkflow(true);
     $action->setClass('action-attach');
     $actions[] = $action;
     $action_list = new AphrontHeadsupActionListView();
     $action_list->setActions($actions);
     $panel = '<div class="maniphest-panel">' . $action_list->render() . '<div class="maniphest-task-detail-core">' . '<h1>' . '<span class="aphront-headsup-object-name">' . phutil_escape_html('T' . $task->getID()) . '</span>' . ' ' . phutil_escape_html($task->getTitle()) . '</h1>' . $table . '</div>' . '</div>';
     $transaction_types = ManiphestTransactionType::getTransactionTypeMap();
     $resolution_types = ManiphestTaskStatus::getTaskStatusMap();
     if ($task->getStatus() == ManiphestTaskStatus::STATUS_OPEN) {
         $resolution_types = array_select_keys($resolution_types, array(ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, ManiphestTaskStatus::STATUS_CLOSED_WONTFIX, ManiphestTaskStatus::STATUS_CLOSED_INVALID, ManiphestTaskStatus::STATUS_CLOSED_SPITE));
     } else {
         $resolution_types = array(ManiphestTaskStatus::STATUS_OPEN => 'Reopened');
         $transaction_types[ManiphestTransactionType::TYPE_STATUS] = 'Reopen Task';
         unset($transaction_types[ManiphestTransactionType::TYPE_PRIORITY]);
         unset($transaction_types[ManiphestTransactionType::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;
     }
     $panel_id = celerity_generate_unique_node_id();
     $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
     if ($is_serious) {
         // Prevent tasks from being closed "out of spite" in serious business
         // installs.
         unset($resolution_types[ManiphestTaskStatus::STATUS_CLOSED_SPITE]);
     }
     $remarkup_href = PhabricatorEnv::getDoclink('article/Remarkup_Reference.html');
     $comment_form = new AphrontFormView();
     $comment_form->setUser($user)->setAction('/maniphest/transaction/save/')->setEncType('multipart/form-data')->addHiddenInput('taskID', $task->getID())->appendChild(id(new AphrontFormSelectControl())->setLabel('Action')->setName('action')->setOptions($transaction_types)->setID('transaction-action'))->appendChild(id(new AphrontFormSelectControl())->setLabel('Resolution')->setName('resolution')->setControlID('resolution')->setControlStyle('display: none')->setOptions($resolution_types))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('Assign To')->setName('assign_to')->setControlID('assign_to')->setControlStyle('display: none')->setID('assign-tokenizer')->setDisableBehavior(true))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('CCs')->setName('ccs')->setControlID('ccs')->setControlStyle('display: none')->setID('cc-tokenizer')->setDisableBehavior(true))->appendChild(id(new AphrontFormSelectControl())->setLabel('Priority')->setName('priority')->setOptions($priority_map)->setControlID('priority')->setControlStyle('display: none')->setValue($task->getPriority()))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('Projects')->setName('projects')->setControlID('projects')->setControlStyle('display: none')->setID('projects-tokenizer')->setDisableBehavior(true))->appendChild(id(new AphrontFormFileControl())->setLabel('File')->setName('file')->setControlID('file')->setControlStyle('display: none'))->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Comments')->setName('comments')->setValue($draft_text)->setCaption(phutil_render_tag('a', array('href' => $remarkup_href, 'tabindex' => '-1', 'target' => '_blank'), 'Formatting Reference'))->setID('transaction-comments'))->appendChild(id(new AphrontFormDragAndDropUploadControl())->setLabel('Attached Files')->setName('files')->setDragAndDropTarget($panel_id)->setActivatedClass('aphront-panel-view-drag-and-drop'))->appendChild(id(new AphrontFormSubmitControl())->setValue($is_serious ? 'Submit' : 'Avast!'));
     $control_map = array(ManiphestTransactionType::TYPE_STATUS => 'resolution', ManiphestTransactionType::TYPE_OWNER => 'assign_to', ManiphestTransactionType::TYPE_CCS => 'ccs', ManiphestTransactionType::TYPE_PRIORITY => 'priority', ManiphestTransactionType::TYPE_PROJECTS => 'projects', ManiphestTransactionType::TYPE_ATTACH => 'file');
     Javelin::initBehavior('maniphest-transaction-controls', array('select' => 'transaction-action', 'controlMap' => $control_map, 'tokenizers' => array(ManiphestTransactionType::TYPE_PROJECTS => array('id' => 'projects-tokenizer', 'src' => '/typeahead/common/projects/', 'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand')), ManiphestTransactionType::TYPE_OWNER => array('id' => 'assign-tokenizer', 'src' => '/typeahead/common/users/', 'value' => $default_claim, 'limit' => 1, 'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand')), ManiphestTransactionType::TYPE_CCS => array('id' => 'cc-tokenizer', 'src' => '/typeahead/common/mailable/', 'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand')))));
     Javelin::initBehavior('maniphest-transaction-preview', array('uri' => '/maniphest/transaction/preview/' . $task->getID() . '/', 'preview' => 'transaction-preview', 'comments' => 'transaction-comments', 'action' => 'transaction-action', 'map' => $control_map));
     $comment_panel = new AphrontPanelView();
     $comment_panel->appendChild($comment_form);
     $comment_panel->setID($panel_id);
     $comment_panel->addClass('aphront-panel-accent');
     $comment_panel->setHeader($is_serious ? 'Add Comment' : 'Weigh In');
     $preview_panel = '<div class="aphront-panel-preview">
     <div id="transaction-preview">
       <div class="aphront-panel-preview-loading-text">
         Loading preview...
       </div>
     </div>
   </div>';
     $transaction_view = new ManiphestTransactionListView();
     $transaction_view->setTransactions($transactions);
     $transaction_view->setHandles($handles);
     $transaction_view->setUser($user);
     $transaction_view->setMarkupEngine($engine);
     return $this->buildStandardPageResponse(array($context_bar, $panel, $transaction_view, $comment_panel, $preview_panel), array('title' => 'T' . $task->getID() . ' ' . $task->getTitle()));
 }
 private function loadTasks(array $user_phids, array $project_phids, array $task_ids, array $dict)
 {
     $query = new ManiphestTaskQuery();
     $query->withProjects($project_phids);
     $query->withTaskIDs($task_ids);
     $status = $dict['status'];
     if (!empty($status['open']) && !empty($status['closed'])) {
         $query->withStatus(ManiphestTaskQuery::STATUS_ANY);
     } else {
         if (!empty($status['open'])) {
             $query->withStatus(ManiphestTaskQuery::STATUS_OPEN);
         } else {
             $query->withStatus(ManiphestTaskQuery::STATUS_CLOSED);
         }
     }
     switch ($this->view) {
         case 'action':
             $query->withOwners($user_phids);
             break;
         case 'created':
             $query->withAuthors($user_phids);
             break;
         case 'subscribed':
             $query->withSubscribers($user_phids);
             break;
         case 'triage':
             $query->withOwners($user_phids);
             $query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
             break;
         case 'alltriage':
             $query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
             break;
         case 'all':
             break;
     }
     $order_map = array('priority' => ManiphestTaskQuery::ORDER_PRIORITY, 'created' => ManiphestTaskQuery::ORDER_CREATED);
     $query->setOrderBy(idx($order_map, $dict['order'], ManiphestTaskQuery::ORDER_MODIFIED));
     $group_map = array('priority' => ManiphestTaskQuery::GROUP_PRIORITY, 'owner' => ManiphestTaskQuery::GROUP_OWNER, 'status' => ManiphestTaskQuery::GROUP_STATUS);
     $query->setGroupBy(idx($group_map, $dict['group'], ManiphestTaskQuery::GROUP_NONE));
     $query->setCalculateRows(true);
     $query->setLimit($dict['limit']);
     $query->setOffset($dict['offset']);
     $data = $query->execute();
     $total_row_count = $query->getRowCount();
     $handle_phids = mpull($data, 'getOwnerPHID');
     $handle_phids = array_merge($handle_phids, $project_phids, $user_phids);
     $handles = id(new PhabricatorObjectHandleData($handle_phids))->loadHandles();
     switch ($dict['group']) {
         case 'priority':
             $data = mgroup($data, 'getPriority');
             krsort($data);
             // If we have invalid priorities, they'll all map to "???". Merge
             // arrays to prevent them from overwriting each other.
             $out = array();
             foreach ($data as $pri => $tasks) {
                 $out[ManiphestTaskPriority::getTaskPriorityName($pri)][] = $tasks;
             }
             foreach ($out as $pri => $tasks) {
                 $out[$pri] = array_mergev($tasks);
             }
             $data = $out;
             break;
         case 'status':
             $data = mgroup($data, 'getStatus');
             ksort($data);
             $out = array();
             foreach ($data as $status => $tasks) {
                 $out[ManiphestTaskStatus::getTaskStatusFullName($status)] = $tasks;
             }
             $data = $out;
             break;
         case 'owner':
             $data = mgroup($data, 'getOwnerPHID');
             $out = array();
             foreach ($data as $phid => $tasks) {
                 if ($phid) {
                     $out[$handles[$phid]->getFullName()] = $tasks;
                 } else {
                     $out['Unassigned'] = $tasks;
                 }
             }
             if (isset($out['Unassigned'])) {
                 // If any tasks are unassigned, move them to the front of the list.
                 $data = array('Unassigned' => $out['Unassigned']) + $out;
             } else {
                 $data = $out;
             }
             ksort($data);
             break;
         default:
             $data = array('Tasks' => $data);
             break;
     }
     return array($data, $handles, $total_row_count);
 }
 private function buildPropertyView(ManiphestTask $task, PhabricatorCustomFieldList $field_list, array $edges, PhabricatorActionListView $actions, $handles)
 {
     $viewer = $this->getRequest()->getUser();
     $view = id(new PHUIPropertyListView())->setUser($viewer)->setObject($task)->setActionList($actions);
     $view->addProperty(pht('Assigned To'), $task->getOwnerPHID() ? $handles->renderHandle($task->getOwnerPHID()) : phutil_tag('em', array(), pht('None')));
     $view->addProperty(pht('Priority'), ManiphestTaskPriority::getTaskPriorityName($task->getPriority()));
     $view->addProperty(pht('Author'), $handles->renderHandle($task->getAuthorPHID()));
     $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));
     }
     $edge_types = array(ManiphestTaskDependedOnByTaskEdgeType::EDGECONST => pht('Blocks'), ManiphestTaskDependsOnTaskEdgeType::EDGECONST => pht('Blocked By'), ManiphestTaskHasRevisionEdgeType::EDGECONST => pht('Differential Revisions'), ManiphestTaskHasMockEdgeType::EDGECONST => pht('Pholio Mocks'));
     $revisions_commits = array();
     $commit_phids = array_keys($edges[ManiphestTaskHasCommitEdgeType::EDGECONST]);
     if ($commit_phids) {
         $commit_drev = DiffusionCommitHasRevisionEdgeType::EDGECONST;
         $drev_edges = id(new PhabricatorEdgeQuery())->withSourcePHIDs($commit_phids)->withEdgeTypes(array($commit_drev))->execute();
         foreach ($commit_phids as $phid) {
             $revisions_commits[$phid] = $handles->renderHandle($phid);
             $revision_phid = key($drev_edges[$phid][$commit_drev]);
             $revision_handle = $handles->getHandleIfExists($revision_phid);
             if ($revision_handle) {
                 $task_drev = ManiphestTaskHasRevisionEdgeType::EDGECONST;
                 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]) {
             $edge_handles = $viewer->loadHandles(array_keys($edges[$edge_type]));
             $view->addProperty($edge_name, $edge_handles->renderList());
         }
     }
     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, PhabricatorFileFilePHIDType::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());
     }
     $view->invokeWillRenderEvent();
     $field_list->appendFieldsToPropertyList($task, $viewer, $view);
     return $view;
 }