@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 recaptime-dev/main 665 lines 16 kB view raw
1<?php 2 3final class DiffusionCommitGraphView 4 extends DiffusionView { 5 6 private $history; 7 private $commits; 8 private $isHead; 9 private $isTail; 10 private $parents; 11 private $filterParents; 12 private $noDataString; 13 14 private $commitMap; 15 private $buildableMap; 16 private $revisionMap; 17 18 private $showAuditors; 19 20 /** 21 * @param array<DiffusionPathChange> $history 22 */ 23 public function setHistory(array $history) { 24 assert_instances_of($history, DiffusionPathChange::class); 25 $this->history = $history; 26 return $this; 27 } 28 29 public function getHistory() { 30 return $this->history; 31 } 32 33 /** 34 * @param array<PhabricatorRepositoryCommit> $commits 35 */ 36 public function setCommits(array $commits) { 37 assert_instances_of($commits, PhabricatorRepositoryCommit::class); 38 $this->commits = $commits; 39 return $this; 40 } 41 42 public function getCommits() { 43 return $this->commits; 44 } 45 46 public function setShowAuditors($show_auditors) { 47 $this->showAuditors = $show_auditors; 48 return $this; 49 } 50 51 public function getShowAuditors() { 52 return $this->showAuditors; 53 } 54 55 public function setParents(array $parents) { 56 $this->parents = $parents; 57 return $this; 58 } 59 60 public function getParents() { 61 return $this->parents; 62 } 63 64 public function setIsHead($is_head) { 65 $this->isHead = $is_head; 66 return $this; 67 } 68 69 public function getIsHead() { 70 return $this->isHead; 71 } 72 73 public function setIsTail($is_tail) { 74 $this->isTail = $is_tail; 75 return $this; 76 } 77 78 public function getIsTail() { 79 return $this->isTail; 80 } 81 82 public function setFilterParents($filter_parents) { 83 $this->filterParents = $filter_parents; 84 return $this; 85 } 86 87 public function getFilterParents() { 88 return $this->filterParents; 89 } 90 91 public function setNoDataString($no_data_string) { 92 $this->noDataString = $no_data_string; 93 return $this; 94 } 95 96 private function getRepository() { 97 $drequest = $this->getDiffusionRequest(); 98 99 if (!$drequest) { 100 return null; 101 } 102 103 return $drequest->getRepository(); 104 } 105 106 public function newObjectItemListView() { 107 $list_view = new PHUIObjectItemListView(); 108 109 $item_views = $this->newObjectItemViews(); 110 foreach ($item_views as $item_view) { 111 $list_view->addItem($item_view); 112 } 113 114 return $list_view; 115 } 116 117 private function newObjectItemViews() { 118 $viewer = $this->getViewer(); 119 120 require_celerity_resource('diffusion-css'); 121 122 $show_builds = $this->shouldShowBuilds(); 123 $show_revisions = $this->shouldShowRevisions(); 124 $show_auditors = $this->shouldShowAuditors(); 125 126 $phids = array(); 127 128 if ($show_revisions) { 129 $revision_map = $this->getRevisionMap(); 130 foreach ($revision_map as $revisions) { 131 foreach ($revisions as $revision) { 132 $phids[] = $revision->getPHID(); 133 } 134 } 135 } 136 137 $commits = $this->getCommitMap(); 138 139 foreach ($commits as $commit) { 140 $author_phid = $commit->getAuthorDisplayPHID(); 141 if ($author_phid !== null) { 142 $phids[] = $author_phid; 143 } 144 } 145 146 if ($show_auditors) { 147 foreach ($commits as $commit) { 148 $audits = $commit->getAudits(); 149 foreach ($audits as $auditor) { 150 $phids[] = $auditor->getAuditorPHID(); 151 } 152 } 153 } 154 155 $handles = $viewer->loadHandles($phids); 156 157 $views = array(); 158 159 $items = $this->newHistoryItems(); 160 foreach ($items as $hash => $item) { 161 $content = array(); 162 163 $commit = $item['commit']; 164 165 $commit_description = $this->getCommitDescription($commit); 166 $commit_link = $this->getCommitURI($hash); 167 168 $short_hash = $this->getCommitObjectName($hash); 169 $is_disabled = $this->getCommitIsDisabled($commit); 170 171 $item_view = id(new PHUIObjectItemView()) 172 ->setViewer($viewer) 173 ->setHeader($commit_description) 174 ->setObjectName($short_hash) 175 ->setHref($commit_link) 176 ->setDisabled($is_disabled); 177 178 $this->addBrowseAction($item_view, $hash); 179 180 if ($show_builds) { 181 $this->addBuildAction($item_view, $hash); 182 } 183 184 // hide Audit entry on /diffusion/commit/query/all if Audit is disabled 185 if (id(new PhabricatorAuditApplication())->isInstalled()) { 186 $this->addAuditAction($item_view, $hash); 187 } 188 189 if ($show_auditors) { 190 $auditor_list = $item_view->newMapView(); 191 if ($commit) { 192 $auditors = $this->newAuditorList($commit, $handles); 193 $auditor_list->newItem() 194 ->setName(pht('Auditors')) 195 ->setValue($auditors); 196 } 197 } 198 199 $property_list = $item_view->newMapView(); 200 201 if ($commit) { 202 $author_view = $this->getCommitAuthorView($commit); 203 if ($author_view) { 204 $property_list->newItem() 205 ->setName(pht('Author')) 206 ->setValue($author_view); 207 } 208 } 209 210 if ($show_revisions) { 211 if ($commit) { 212 $revisions = $this->getRevisions($commit); 213 if ($revisions) { 214 $list_view = $handles->newSublist(mpull($revisions, 'getPHID')) 215 ->newListView(); 216 217 $property_list->newItem() 218 ->setName(pht('Revisions')) 219 ->setValue($list_view); 220 } 221 } 222 } 223 224 $views[$hash] = $item_view; 225 } 226 227 return $views; 228 } 229 230 /** 231 * @return array<PHUIObjectItemListView> 232 */ 233 private function newObjectItemRows() { 234 $viewer = $this->getViewer(); 235 236 $items = $this->newHistoryItems(); 237 $views = $this->newObjectItemViews(); 238 239 $last_date = null; 240 $rows = array(); 241 foreach ($items as $hash => $item) { 242 $item_epoch = $item['epoch']; 243 $item_date = phabricator_date($item_epoch, $viewer); 244 if ($item_date !== $last_date) { 245 $last_date = $item_date; 246 $header = $item_date; 247 } else { 248 $header = null; 249 } 250 251 $item_view = $views[$hash]; 252 253 $list_view = id(new PHUIObjectItemListView()) 254 ->setViewer($viewer) 255 ->setFlush(true) 256 ->addItem($item_view); 257 258 if ($header !== null) { 259 $list_view->setHeader($header); 260 } 261 262 $rows[] = $list_view; 263 } 264 265 return $rows; 266 } 267 268 public function render() { 269 $rows = $this->newObjectItemRows(); 270 271 $graph = $this->newGraphView(); 272 foreach ($rows as $idx => $row) { 273 $cells = array(); 274 275 if ($graph) { 276 $cells[] = phutil_tag( 277 'td', 278 array( 279 'class' => 'diffusion-commit-graph-path-cell', 280 ), 281 $graph[$idx]); 282 } 283 284 $cells[] = phutil_tag( 285 'td', 286 array( 287 'class' => 'diffusion-commit-graph-content-cell', 288 ), 289 $row); 290 291 $rows[$idx] = phutil_tag('tr', array(), $cells); 292 } 293 294 if ($rows) { 295 $table = phutil_tag( 296 'table', 297 array( 298 'class' => 'diffusion-commit-graph-table', 299 ), 300 $rows); 301 } else { 302 $table = id(new PHUIObjectItemListView()) 303 ->setNoDataString($this->noDataString); 304 } 305 306 return $table; 307 } 308 309 private function newGraphView() { 310 if (!$this->getParents()) { 311 return null; 312 } 313 314 $parents = $this->getParents(); 315 316 // If we're filtering parents, remove relationships which point to 317 // commits that are not part of the visible graph. Otherwise, we get 318 // a big tree of nonsense when viewing release branches like "stable" 319 // versus "master". 320 if ($this->getFilterParents()) { 321 foreach ($parents as $key => $nodes) { 322 foreach ($nodes as $nkey => $node) { 323 if (empty($parents[$node])) { 324 unset($parents[$key][$nkey]); 325 } 326 } 327 } 328 } 329 330 return id(new PHUIDiffGraphView()) 331 ->setIsHead($this->getIsHead()) 332 ->setIsTail($this->getIsTail()) 333 ->renderGraph($parents); 334 } 335 336 private function shouldShowBuilds() { 337 $viewer = $this->getViewer(); 338 339 $show_builds = PhabricatorApplication::isClassInstalledForViewer( 340 PhabricatorHarbormasterApplication::class, 341 $this->getUser()); 342 343 return $show_builds; 344 } 345 346 private function shouldShowRevisions() { 347 $viewer = $this->getViewer(); 348 349 $show_revisions = PhabricatorApplication::isClassInstalledForViewer( 350 PhabricatorDifferentialApplication::class, 351 $viewer); 352 353 return $show_revisions; 354 } 355 356 private function shouldShowAuditors() { 357 return $this->getShowAuditors(); 358 } 359 360 private function newHistoryItems() { 361 $items = array(); 362 363 $history = $this->getHistory(); 364 if ($history !== null) { 365 foreach ($history as $history_item) { 366 $commit_hash = $history_item->getCommitIdentifier(); 367 368 $items[$commit_hash] = array( 369 'epoch' => $history_item->getEpoch(), 370 'hash' => $commit_hash, 371 'commit' => $this->getCommit($commit_hash), 372 ); 373 } 374 } else { 375 $commits = $this->getCommitMap(); 376 foreach ($commits as $commit) { 377 $commit_hash = $commit->getCommitIdentifier(); 378 379 $items[$commit_hash] = array( 380 'epoch' => $commit->getEpoch(), 381 'hash' => $commit_hash, 382 'commit' => $commit, 383 ); 384 } 385 } 386 387 return $items; 388 } 389 390 private function getCommitDescription($commit) { 391 if (!$commit) { 392 return phutil_tag('em', array(), pht("Discovering\xE2\x80\xA6")); 393 } 394 395 // We can show details once the message and change have been imported. 396 $partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE | 397 PhabricatorRepositoryCommit::IMPORTED_CHANGE; 398 if (!$commit->isPartiallyImported($partial_import)) { 399 return phutil_tag('em', array(), pht("Importing\xE2\x80\xA6")); 400 } 401 402 return $commit->getCommitData()->getSummary(); 403 } 404 405 private function getCommitURI($hash) { 406 $repository = $this->getRepository(); 407 408 if ($repository) { 409 return $repository->getCommitURI($hash); 410 } 411 412 $commit = $this->getCommit($hash); 413 if ($commit) { 414 return $commit->getURI(); 415 } 416 417 return null; 418 } 419 420 private function getCommitObjectName($hash) { 421 $repository = $this->getRepository(); 422 423 if ($repository) { 424 return $repository->formatCommitName( 425 $hash, 426 $is_local = true); 427 } 428 429 $commit = $this->getCommit($hash); 430 if ($commit) { 431 return $commit->getDisplayName(); 432 } 433 434 return null; 435 } 436 437 private function getCommitIsDisabled($commit) { 438 if (!$commit) { 439 return true; 440 } 441 442 if ($commit->isUnreachable()) { 443 return true; 444 } 445 446 return false; 447 } 448 449 private function getCommitAuthorView($commit) { 450 if (!$commit) { 451 return null; 452 } 453 454 $viewer = $this->getViewer(); 455 456 $author_phid = $commit->getAuthorDisplayPHID(); 457 if ($author_phid) { 458 return $viewer->loadHandles(array($author_phid)) 459 ->newListView(); 460 } 461 462 return $commit->newCommitAuthorView($viewer); 463 } 464 465 private function getCommit($hash) { 466 $commit_map = $this->getCommitMap(); 467 return idx($commit_map, $hash); 468 } 469 470 private function getCommitMap() { 471 if ($this->commitMap === null) { 472 $commit_list = $this->newCommitList(); 473 $this->commitMap = mpull($commit_list, null, 'getCommitIdentifier'); 474 } 475 476 return $this->commitMap; 477 } 478 479 private function addBrowseAction(PHUIObjectItemView $item, $hash) { 480 $repository = $this->getRepository(); 481 482 if (!$repository) { 483 return; 484 } 485 486 $drequest = $this->getDiffusionRequest(); 487 $path = $drequest->getPath(); 488 489 $uri = $drequest->generateURI( 490 array( 491 'action' => 'browse', 492 'path' => $path, 493 'commit' => $hash, 494 )); 495 496 $menu_item = $item->newMenuItem() 497 ->setName(pht('Browse Repository')) 498 ->setURI($uri); 499 500 $menu_item->newIcon() 501 ->setIcon('fa-folder-open-o') 502 ->setColor('bluegrey'); 503 } 504 505 private function addBuildAction(PHUIObjectItemView $item, $hash) { 506 $is_disabled = true; 507 508 $buildable = null; 509 510 $commit = $this->getCommit($hash); 511 if ($commit) { 512 $buildable = $this->getBuildable($commit); 513 } 514 515 if ($buildable) { 516 $icon = $buildable->getStatusIcon(); 517 $color = $buildable->getStatusColor(); 518 $name = $buildable->getStatusDisplayName(); 519 $uri = $buildable->getURI(); 520 } else { 521 $icon = 'fa-times'; 522 $color = 'grey'; 523 $name = pht('No Builds'); 524 $uri = null; 525 } 526 527 $menu_item = $item->newMenuItem() 528 ->setName($name) 529 ->setURI($uri) 530 ->setDisabled(($uri === null)); 531 532 $menu_item->newIcon() 533 ->setIcon($icon) 534 ->setColor($color); 535 } 536 537 private function addAuditAction(PHUIObjectItemView $item_view, $hash) { 538 $commit = $this->getCommit($hash); 539 540 if ($commit) { 541 $status = $commit->getAuditStatusObject(); 542 543 $text = $status->getName(); 544 $icon = $status->getIcon(); 545 546 $is_disabled = $status->isNoAudit(); 547 if ($is_disabled) { 548 $uri = null; 549 $color = 'grey'; 550 } else { 551 $color = $status->getColor(); 552 $uri = $commit->getURI(); 553 } 554 } else { 555 $text = pht('No Audit'); 556 $color = 'grey'; 557 $icon = 'fa-times'; 558 $uri = null; 559 560 $is_disabled = true; 561 } 562 563 $menu_item = $item_view->newMenuItem() 564 ->setName($text) 565 ->setURI($uri) 566 ->setBackgroundColor($color) 567 ->setDisabled($is_disabled); 568 569 $menu_item->newIcon() 570 ->setIcon($icon) 571 ->setColor($color); 572 } 573 574 private function getBuildable(PhabricatorRepositoryCommit $commit) { 575 $buildable_map = $this->getBuildableMap(); 576 return idx($buildable_map, $commit->getPHID()); 577 } 578 579 private function getBuildableMap() { 580 if ($this->buildableMap === null) { 581 $commits = $this->getCommitMap(); 582 $buildables = $this->loadBuildables($commits); 583 $this->buildableMap = $buildables; 584 } 585 586 return $this->buildableMap; 587 } 588 589 private function getRevisions(PhabricatorRepositoryCommit $commit) { 590 $revision_map = $this->getRevisionMap(); 591 return idx($revision_map, $commit->getPHID(), array()); 592 } 593 594 private function getRevisionMap() { 595 if ($this->revisionMap === null) { 596 $this->revisionMap = $this->newRevisionMap(); 597 } 598 599 return $this->revisionMap; 600 } 601 602 private function newRevisionMap() { 603 $viewer = $this->getViewer(); 604 $commits = $this->getCommitMap(); 605 606 return DiffusionCommitRevisionQuery::loadRevisionMapForCommits( 607 $viewer, 608 $commits); 609 } 610 611 private function newCommitList() { 612 $commits = $this->getCommits(); 613 if ($commits !== null) { 614 return $commits; 615 } 616 617 $repository = $this->getRepository(); 618 if (!$repository) { 619 return array(); 620 } 621 622 $history = $this->getHistory(); 623 if ($history === null) { 624 return array(); 625 } 626 627 $identifiers = array(); 628 foreach ($history as $item) { 629 $identifiers[] = $item->getCommitIdentifier(); 630 } 631 632 if (!$identifiers) { 633 return array(); 634 } 635 636 $viewer = $this->getViewer(); 637 638 $commits = id(new DiffusionCommitQuery()) 639 ->setViewer($viewer) 640 ->withRepository($repository) 641 ->withIdentifiers($identifiers) 642 ->needCommitData(true) 643 ->needIdentities(true) 644 ->execute(); 645 646 return $commits; 647 } 648 649 private function newAuditorList( 650 PhabricatorRepositoryCommit $commit, 651 $handles) { 652 653 $auditors = $commit->getAudits(); 654 if (!$auditors) { 655 return phutil_tag('em', array(), pht('None')); 656 } 657 658 $auditor_phids = mpull($auditors, 'getAuditorPHID'); 659 $auditor_list = $handles->newSublist($auditor_phids) 660 ->newListView(); 661 662 return $auditor_list; 663 } 664 665}