Path: blob/master/src/applications/diffusion/controller/DiffusionBlameController.php
12242 views
<?php12final class DiffusionBlameController extends DiffusionController {34public function shouldAllowPublic() {5return true;6}78public function handleRequest(AphrontRequest $request) {9$response = $this->loadDiffusionContext();10if ($response) {11return $response;12}1314$viewer = $this->getViewer();15$drequest = $this->getDiffusionRequest();16$repository = $drequest->getRepository();1718$blame = $this->loadBlame();1920$identifiers = array_fuse($blame);21if ($identifiers) {22$commits = id(new DiffusionCommitQuery())23->setViewer($viewer)24->withRepository($repository)25->withIdentifiers($identifiers)26->needIdentities(true)27// See PHI1014. If identities haven't been built yet, we may need to28// fall back to raw commit data.29->needCommitData(true)30->execute();31$commits = mpull($commits, null, 'getCommitIdentifier');32} else {33$commits = array();34}3536$commit_map = mpull($commits, 'getCommitIdentifier', 'getPHID');3738$revision_map = DiffusionCommitRevisionQuery::loadRevisionMapForCommits(39$viewer,40$commits);4142$base_href = (string)$drequest->generateURI(43array(44'action' => 'browse',45'stable' => true,46));4748$skip_text = pht('Skip Past This Commit');49$skip_icon = id(new PHUIIconView())50->setIcon('fa-backward');5152Javelin::initBehavior('phabricator-tooltips');5354$handle_phids = array();55foreach ($commits as $commit) {56$handle_phids[] = $commit->getAuthorDisplayPHID();57}5859foreach ($revision_map as $revisions) {60foreach ($revisions as $revision) {61$handle_phids[] = $revision->getAuthorPHID();62}63}6465$handles = $viewer->loadHandles($handle_phids);6667$map = array();68$epochs = array();69foreach ($identifiers as $identifier) {70$skip_href = $base_href.'?before='.$identifier;7172$skip_link = javelin_tag(73'a',74array(75'href' => $skip_href,76'sigil' => 'has-tooltip',77'meta' => array(78'tip' => $skip_text,79'align' => 'E',80'size' => 300,81),82),83$skip_icon);8485// We may not have a commit object for a given identifier if the commit86// has not imported yet.8788// At time of writing, this can also happen if a line was part of the89// initial import: blame produces a "^abc123" identifier in Git, which90// doesn't correspond to a real commit.9192$commit = idx($commits, $identifier);9394$revision = null;95if ($commit) {96$revisions = idx($revision_map, $commit->getPHID());9798// There may be multiple edges between this commit and revisions in the99// database. If there are, just pick one arbitrarily.100if ($revisions) {101$revision = head($revisions);102}103}104105$author_phid = null;106107if ($commit) {108$author_phid = $commit->getAuthorDisplayPHID();109}110111if (!$author_phid) {112// This means we couldn't identify an author for the commit or the113// revision. We just render a blank for alignment.114$author_style = null;115$author_href = null;116$author_sigil = null;117$author_meta = null;118} else {119$author_src = $handles[$author_phid]->getImageURI();120$author_style = 'background-image: url('.$author_src.');';121$author_href = $handles[$author_phid]->getURI();122$author_sigil = 'has-tooltip';123$author_meta = array(124'tip' => $handles[$author_phid]->getName(),125'align' => 'E',126'size' => 'auto',127);128}129130$author_link = javelin_tag(131$author_href ? 'a' : 'span',132array(133'class' => 'phabricator-source-blame-author',134'style' => $author_style,135'href' => $author_href,136'sigil' => $author_sigil,137'meta' => $author_meta,138));139140if ($commit) {141$commit_link = javelin_tag(142'a',143array(144'href' => $commit->getURI(),145'sigil' => 'has-tooltip',146'meta' => array(147'tip' => $this->renderCommitTooltip($commit, $handles),148'align' => 'E',149'size' => 600,150),151),152$commit->getLocalName());153} else {154$commit_link = null;155}156157$info = array(158$author_link,159$commit_link,160);161162if ($revision) {163$revision_link = javelin_tag(164'a',165array(166'href' => $revision->getURI(),167'sigil' => 'has-tooltip',168'meta' => array(169'tip' => $this->renderRevisionTooltip($revision, $handles),170'align' => 'E',171'size' => 600,172),173),174$revision->getMonogram());175176$info = array(177$info,178" \xC2\xB7 ",179$revision_link,180);181}182183if ($commit) {184$epoch = $commit->getEpoch();185} else {186$epoch = 0;187}188189$epochs[] = $epoch;190191$data = array(192'skip' => $skip_link,193'info' => hsprintf('%s', $info),194'epoch' => $epoch,195);196197$map[$identifier] = $data;198}199200$epoch_min = min($epochs);201$epoch_max = max($epochs);202203return id(new AphrontAjaxResponse())->setContent(204array(205'blame' => $blame,206'map' => $map,207'epoch' => array(208'min' => $epoch_min,209'max' => $epoch_max,210),211));212}213214private function loadBlame() {215$drequest = $this->getDiffusionRequest();216217$commit = $drequest->getCommit();218$path = $drequest->getPath();219220$blame_timeout = 15;221222$blame = $this->callConduitWithDiffusionRequest(223'diffusion.blame',224array(225'commit' => $commit,226'paths' => array($path),227'timeout' => $blame_timeout,228));229230return idx($blame, $path, array());231}232233private function renderRevisionTooltip(234DifferentialRevision $revision,235$handles) {236$viewer = $this->getViewer();237238$date = phabricator_date($revision->getDateModified(), $viewer);239$monogram = $revision->getMonogram();240$title = $revision->getTitle();241$header = "{$monogram} {$title}";242243$author = $handles[$revision->getAuthorPHID()]->getName();244245return "{$header}\n{$date} \xC2\xB7 {$author}";246}247248private function renderCommitTooltip(249PhabricatorRepositoryCommit $commit,250$handles) {251252$viewer = $this->getViewer();253254$date = phabricator_date($commit->getEpoch(), $viewer);255$summary = trim($commit->getSummary());256257$author_phid = $commit->getAuthorPHID();258if ($author_phid && isset($handles[$author_phid])) {259$author_name = $handles[$author_phid]->getName();260} else {261$author_name = null;262}263264if ($author_name) {265return "{$summary}\n{$date} \xC2\xB7 {$author_name}";266} else {267return "{$summary}\n{$date}";268}269}270271}272273274