@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 276 lines 7.1 kB view raw
1<?php 2 3final class DiffusionBlameController extends DiffusionController { 4 5 public function shouldAllowPublic() { 6 return true; 7 } 8 9 public function handleRequest(AphrontRequest $request) { 10 $response = $this->loadDiffusionContext(); 11 if ($response) { 12 return $response; 13 } 14 15 $viewer = $this->getViewer(); 16 $drequest = $this->getDiffusionRequest(); 17 $repository = $drequest->getRepository(); 18 19 $blame = $this->loadBlame(); 20 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(); 35 } 36 37 $commit_map = mpull($commits, 'getCommitIdentifier', 'getPHID'); 38 39 $revision_map = DiffusionCommitRevisionQuery::loadRevisionMapForCommits( 40 $viewer, 41 $commits); 42 43 $base_href = (string)$drequest->generateURI( 44 array( 45 'action' => 'browse', 46 'stable' => true, 47 )); 48 49 $skip_text = pht('Skip Past This Commit'); 50 $skip_icon = id(new PHUIIconView()) 51 ->setIcon('fa-backward'); 52 53 Javelin::initBehavior('phabricator-tooltips'); 54 55 $handle_phids = array(); 56 foreach ($commits as $commit) { 57 $handle_phids[] = $commit->getAuthorDisplayPHID(); 58 } 59 60 foreach ($revision_map as $revisions) { 61 foreach ($revisions as $revision) { 62 $handle_phids[] = $revision->getAuthorPHID(); 63 } 64 } 65 66 $handles = $viewer->loadHandles($handle_phids); 67 68 $map = array(); 69 $epochs = array(); 70 foreach ($identifiers as $identifier) { 71 $skip_href = $base_href.'?before='.$identifier; 72 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, 82 ), 83 ), 84 $skip_icon); 85 86 // We may not have a commit object for a given identifier if the commit 87 // has not imported yet. 88 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. 92 93 $commit = idx($commits, $identifier); 94 95 $revision = null; 96 if ($commit) { 97 $revisions = idx($revision_map, $commit->getPHID()); 98 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); 103 } 104 } 105 106 $author_phid = null; 107 108 if ($commit) { 109 $author_phid = $commit->getAuthorDisplayPHID(); 110 } 111 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', 128 ); 129 } 130 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, 139 )); 140 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, 151 ), 152 ), 153 $commit->getLocalName()); 154 } else { 155 $commit_link = null; 156 } 157 158 $info = array( 159 $author_link, 160 $commit_link, 161 ); 162 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, 173 ), 174 ), 175 $revision->getMonogram()); 176 177 $info = array( 178 $info, 179 " \xC2\xB7 ", 180 $revision_link, 181 ); 182 } 183 184 if ($commit) { 185 $epoch = $commit->getEpoch(); 186 } else { 187 $epoch = 0; 188 } 189 190 $epochs[] = $epoch; 191 192 $data = array( 193 'skip' => $skip_link, 194 'info' => hsprintf('%s', $info), 195 'epoch' => $epoch, 196 ); 197 198 $map[$identifier] = $data; 199 } 200 201 if (empty($epochs)) { 202 $epochs[] = 0; 203 } 204 205 $epoch_min = min($epochs); 206 $epoch_max = max($epochs); 207 208 return id(new AphrontAjaxResponse())->setContent( 209 array( 210 'blame' => $blame, 211 'map' => $map, 212 'epoch' => array( 213 'min' => $epoch_min, 214 'max' => $epoch_max, 215 ), 216 )); 217 } 218 219 private function loadBlame() { 220 $drequest = $this->getDiffusionRequest(); 221 222 $commit = $drequest->getCommit(); 223 $path = $drequest->getPath(); 224 225 $blame_timeout = 15; 226 227 $blame = $this->callConduitWithDiffusionRequest( 228 'diffusion.blame', 229 array( 230 'commit' => $commit, 231 'paths' => array($path), 232 'timeout' => $blame_timeout, 233 )); 234 235 return idx($blame, $path, array()); 236 } 237 238 private function renderRevisionTooltip( 239 DifferentialRevision $revision, 240 $handles) { 241 $viewer = $this->getViewer(); 242 243 $date = phabricator_date($revision->getDateModified(), $viewer); 244 $monogram = $revision->getMonogram(); 245 $title = $revision->getTitle(); 246 $header = "{$monogram} {$title}"; 247 248 $author = $handles[$revision->getAuthorPHID()]->getName(); 249 250 return "{$header}\n{$date} \xC2\xB7 {$author}"; 251 } 252 253 private function renderCommitTooltip( 254 PhabricatorRepositoryCommit $commit, 255 $handles) { 256 257 $viewer = $this->getViewer(); 258 259 $date = phabricator_date($commit->getEpoch(), $viewer); 260 $summary = trim($commit->getSummary()); 261 262 $author_phid = $commit->getAuthorPHID(); 263 if ($author_phid && isset($handles[$author_phid])) { 264 $author_name = $handles[$author_phid]->getName(); 265 } else { 266 $author_name = null; 267 } 268 269 if ($author_name) { 270 return "{$summary}\n{$date} \xC2\xB7 {$author_name}"; 271 } else { 272 return "{$summary}\n{$date}"; 273 } 274 } 275 276}