diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php --- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php +++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php @@ -30,20 +30,19 @@ ->setViewer($viewer) ->setTargetObject($task); - $e_commit = ManiphestTaskHasCommitEdgeType::EDGECONST; - $e_rev = ManiphestTaskHasRevisionEdgeType::EDGECONST; - $e_mock = ManiphestTaskHasMockEdgeType::EDGECONST; + $edge_types = array( + ManiphestTaskHasCommitEdgeType::EDGECONST, + ManiphestTaskHasRevisionEdgeType::EDGECONST, + ManiphestTaskHasMockEdgeType::EDGECONST, + PhabricatorObjectMentionedByObjectEdgeType::EDGECONST, + PhabricatorObjectMentionsObjectEdgeType::EDGECONST, + ); $phid = $task->getPHID(); $query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(array($phid)) - ->withEdgeTypes( - array( - $e_commit, - $e_rev, - $e_mock, - )); + ->withEdgeTypes($edge_types); $edges = idx($query->execute(), $phid); $phids = array_fill_keys($query->getDestinationPHIDs(), true); @@ -77,15 +76,8 @@ $timeline->setQuoteRef($monogram); $comment_view->setTransactionTimeline($timeline); - $view = id(new PHUITwoColumnView()) - ->setHeader($header) - ->setCurtain($curtain) - ->setMainColumn(array( - $timeline, - $comment_view, - )) - ->addPropertySection(pht('Description'), $description) - ->addPropertySection(pht('Details'), $details); + $related_tabs = array(); + $graph_menu = null; $graph_limit = 100; $task_graph = id(new ManiphestTaskGraph()) @@ -159,13 +151,50 @@ ->setText($search_text) ->setDropdownMenu($dropdown_menu); - $graph_header = id(new PHUIHeaderView()) - ->setHeader(pht('Task Graph')) - ->addActionLink($graph_menu); + $related_tabs[] = id(new PHUITabView()) + ->setName(pht('Task Graph')) + ->setKey('graph') + ->appendChild($graph_table); + } + + $related_tabs[] = $this->newMocksTab($task, $query); + $related_tabs[] = $this->newMentionsTab($task, $query); + + $tab_view = null; + + $related_tabs = array_filter($related_tabs); + if ($related_tabs) { + $tab_group = new PHUITabGroupView(); + foreach ($related_tabs as $tab) { + $tab_group->addTab($tab); + } + + $related_header = id(new PHUIHeaderView()) + ->setHeader(pht('Related Objects')); - $view->addPropertySection($graph_header, $graph_table); + if ($graph_menu) { + $related_header->addActionLink($graph_menu); + } + + $tab_view = id(new PHUIObjectBoxView()) + ->setHeader($related_header) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addTabGroup($tab_group); } + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setCurtain($curtain) + ->setMainColumn( + array( + $tab_view, + $timeline, + $comment_view, + )) + ->addPropertySection(pht('Description'), $description) + ->addPropertySection(pht('Details'), $details); + + return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) @@ -173,10 +202,7 @@ array( $task->getPHID(), )) - ->appendChild( - array( - $view, - )); + ->appendChild($view); } @@ -356,8 +382,6 @@ $edge_types = array( ManiphestTaskHasRevisionEdgeType::EDGECONST => pht('Differential Revisions'), - ManiphestTaskHasMockEdgeType::EDGECONST - => pht('Pholio Mocks'), ); $revisions_commits = array(); @@ -435,4 +459,73 @@ return $section; } + private function newMocksTab( + ManiphestTask $task, + PhabricatorEdgeQuery $edge_query) { + + $mock_type = ManiphestTaskHasMockEdgeType::EDGECONST; + $mock_phids = $edge_query->getDestinationPHIDs(array(), array($mock_type)); + if (!$mock_phids) { + return null; + } + + $viewer = $this->getViewer(); + $handles = $viewer->loadHandles($mock_phids); + + // TODO: It would be nice to render this as pinboard-style thumbnails, + // similar to "{M123}", instead of a list of links. + + $view = id(new PHUIPropertyListView()) + ->addProperty(pht('Mocks'), $handles->renderList()); + + return id(new PHUITabView()) + ->setName(pht('Mocks')) + ->setKey('mocks') + ->appendChild($view); + } + + private function newMentionsTab( + ManiphestTask $task, + PhabricatorEdgeQuery $edge_query) { + + $in_type = PhabricatorObjectMentionedByObjectEdgeType::EDGECONST; + $out_type = PhabricatorObjectMentionsObjectEdgeType::EDGECONST; + + $in_phids = $edge_query->getDestinationPHIDs(array(), array($in_type)); + $out_phids = $edge_query->getDestinationPHIDs(array(), array($out_type)); + + // Filter out any mentioned users from the list. These are not generally + // very interesting to show in a relationship summary since they usually + // end up as subscribers anyway. + + $user_type = PhabricatorPeopleUserPHIDType::TYPECONST; + foreach ($out_phids as $key => $out_phid) { + if (phid_get_type($out_phid) == $user_type) { + unset($out_phids[$key]); + } + } + + if (!$in_phids && !$out_phids) { + return null; + } + + $viewer = $this->getViewer(); + $view = new PHUIPropertyListView(); + + if ($in_phids) { + $in_handles = $viewer->loadHandles($in_phids); + $view->addProperty(pht('Mentioned In'), $in_handles->renderList()); + } + + if ($out_phids) { + $out_handles = $viewer->loadHandles($out_phids); + $view->addProperty(pht('Mentioned Here'), $out_handles->renderList()); + } + + return id(new PHUITabView()) + ->setName(pht('Mentions')) + ->setKey('mentions') + ->appendChild($view); + } + }