Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / src / applications / diffusion / controller / DiffusionBlameController.php
blob5de464b33503e6cca68d4d89150f3144846d49ee
1 <?php
3 final class DiffusionBlameController extends DiffusionController {
5 public function shouldAllowPublic() {
6 return true;
9 public function handleRequest(AphrontRequest $request) {
10 $response = $this->loadDiffusionContext();
11 if ($response) {
12 return $response;
15 $viewer = $this->getViewer();
16 $drequest = $this->getDiffusionRequest();
17 $repository = $drequest->getRepository();
19 $blame = $this->loadBlame();
21 $identifiers = array_fuse($blame);
22 if ($identifiers) {
23 $commits = id(new DiffusionCommitQuery())
24 ->setViewer($viewer)
25 ->withRepository($repository)
26 ->withIdentifiers($identifiers)
27 ->needIdentities(true)
28 // See PHI1014. If identities haven't been built yet, we may need to
29 // fall back to raw commit data.
30 ->needCommitData(true)
31 ->execute();
32 $commits = mpull($commits, null, 'getCommitIdentifier');
33 } else {
34 $commits = array();
37 $commit_map = mpull($commits, 'getCommitIdentifier', 'getPHID');
39 $revision_map = DiffusionCommitRevisionQuery::loadRevisionMapForCommits(
40 $viewer,
41 $commits);
43 $base_href = (string)$drequest->generateURI(
44 array(
45 'action' => 'browse',
46 'stable' => true,
47 ));
49 $skip_text = pht('Skip Past This Commit');
50 $skip_icon = id(new PHUIIconView())
51 ->setIcon('fa-backward');
53 Javelin::initBehavior('phabricator-tooltips');
55 $handle_phids = array();
56 foreach ($commits as $commit) {
57 $handle_phids[] = $commit->getAuthorDisplayPHID();
60 foreach ($revision_map as $revisions) {
61 foreach ($revisions as $revision) {
62 $handle_phids[] = $revision->getAuthorPHID();
66 $handles = $viewer->loadHandles($handle_phids);
68 $map = array();
69 $epochs = array();
70 foreach ($identifiers as $identifier) {
71 $skip_href = $base_href.'?before='.$identifier;
73 $skip_link = javelin_tag(
74 'a',
75 array(
76 'href' => $skip_href,
77 'sigil' => 'has-tooltip',
78 'meta' => array(
79 'tip' => $skip_text,
80 'align' => 'E',
81 'size' => 300,
84 $skip_icon);
86 // We may not have a commit object for a given identifier if the commit
87 // has not imported yet.
89 // At time of writing, this can also happen if a line was part of the
90 // initial import: blame produces a "^abc123" identifier in Git, which
91 // doesn't correspond to a real commit.
93 $commit = idx($commits, $identifier);
95 $revision = null;
96 if ($commit) {
97 $revisions = idx($revision_map, $commit->getPHID());
99 // There may be multiple edges between this commit and revisions in the
100 // database. If there are, just pick one arbitrarily.
101 if ($revisions) {
102 $revision = head($revisions);
106 $author_phid = null;
108 if ($commit) {
109 $author_phid = $commit->getAuthorDisplayPHID();
112 if (!$author_phid) {
113 // This means we couldn't identify an author for the commit or the
114 // revision. We just render a blank for alignment.
115 $author_style = null;
116 $author_href = null;
117 $author_sigil = null;
118 $author_meta = null;
119 } else {
120 $author_src = $handles[$author_phid]->getImageURI();
121 $author_style = 'background-image: url('.$author_src.');';
122 $author_href = $handles[$author_phid]->getURI();
123 $author_sigil = 'has-tooltip';
124 $author_meta = array(
125 'tip' => $handles[$author_phid]->getName(),
126 'align' => 'E',
127 'size' => 'auto',
131 $author_link = javelin_tag(
132 $author_href ? 'a' : 'span',
133 array(
134 'class' => 'phabricator-source-blame-author',
135 'style' => $author_style,
136 'href' => $author_href,
137 'sigil' => $author_sigil,
138 'meta' => $author_meta,
141 if ($commit) {
142 $commit_link = javelin_tag(
143 'a',
144 array(
145 'href' => $commit->getURI(),
146 'sigil' => 'has-tooltip',
147 'meta' => array(
148 'tip' => $this->renderCommitTooltip($commit, $handles),
149 'align' => 'E',
150 'size' => 600,
153 $commit->getLocalName());
154 } else {
155 $commit_link = null;
158 $info = array(
159 $author_link,
160 $commit_link,
163 if ($revision) {
164 $revision_link = javelin_tag(
165 'a',
166 array(
167 'href' => $revision->getURI(),
168 'sigil' => 'has-tooltip',
169 'meta' => array(
170 'tip' => $this->renderRevisionTooltip($revision, $handles),
171 'align' => 'E',
172 'size' => 600,
175 $revision->getMonogram());
177 $info = array(
178 $info,
179 " \xC2\xB7 ",
180 $revision_link,
184 if ($commit) {
185 $epoch = $commit->getEpoch();
186 } else {
187 $epoch = 0;
190 $epochs[] = $epoch;
192 $data = array(
193 'skip' => $skip_link,
194 'info' => hsprintf('%s', $info),
195 'epoch' => $epoch,
198 $map[$identifier] = $data;
201 $epoch_min = min($epochs);
202 $epoch_max = max($epochs);
204 return id(new AphrontAjaxResponse())->setContent(
205 array(
206 'blame' => $blame,
207 'map' => $map,
208 'epoch' => array(
209 'min' => $epoch_min,
210 'max' => $epoch_max,
215 private function loadBlame() {
216 $drequest = $this->getDiffusionRequest();
218 $commit = $drequest->getCommit();
219 $path = $drequest->getPath();
221 $blame_timeout = 15;
223 $blame = $this->callConduitWithDiffusionRequest(
224 'diffusion.blame',
225 array(
226 'commit' => $commit,
227 'paths' => array($path),
228 'timeout' => $blame_timeout,
231 return idx($blame, $path, array());
234 private function renderRevisionTooltip(
235 DifferentialRevision $revision,
236 $handles) {
237 $viewer = $this->getViewer();
239 $date = phabricator_date($revision->getDateModified(), $viewer);
240 $monogram = $revision->getMonogram();
241 $title = $revision->getTitle();
242 $header = "{$monogram} {$title}";
244 $author = $handles[$revision->getAuthorPHID()]->getName();
246 return "{$header}\n{$date} \xC2\xB7 {$author}";
249 private function renderCommitTooltip(
250 PhabricatorRepositoryCommit $commit,
251 $handles) {
253 $viewer = $this->getViewer();
255 $date = phabricator_date($commit->getEpoch(), $viewer);
256 $summary = trim($commit->getSummary());
258 $author_phid = $commit->getAuthorPHID();
259 if ($author_phid && isset($handles[$author_phid])) {
260 $author_name = $handles[$author_phid]->getName();
261 } else {
262 $author_name = null;
265 if ($author_name) {
266 return "{$summary}\n{$date} \xC2\xB7 {$author_name}";
267 } else {
268 return "{$summary}\n{$date}";