1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-25 08:12:40 +01:00

Introduce "DiffusionCommitGraphView", which unifies "HistoryListView" and "HistoryTableView"

Summary:
Ref T13552. Currently, commit lists are sometimes rendered as an object list and sometimes rendered as a table. There are two separate views for table rendering.

Add a fourth view ("list, with a graph") with the eventual intent of unifying all the other views. For now, this only replaces "HistoryListView" -- and needs some more work to really be a convincing replacement.

Test Plan:
  - Looked at "History" in Diffusion, saw an ugly view with all the information we want.
  - Grepped for "HistoryListView", no hits.

Maniphest Tasks: T13552

Differential Revision: https://secure.phabricator.com/D21410
This commit is contained in:
epriestley 2020-07-10 11:15:41 -07:00
parent c6de7c66a3
commit 46695c76eb
4 changed files with 440 additions and 176 deletions

View file

@ -770,6 +770,7 @@ phutil_register_library_map(array(
'DiffusionCommitEditEngine' => 'applications/diffusion/editor/DiffusionCommitEditEngine.php',
'DiffusionCommitFerretEngine' => 'applications/repository/search/DiffusionCommitFerretEngine.php',
'DiffusionCommitFulltextEngine' => 'applications/repository/search/DiffusionCommitFulltextEngine.php',
'DiffusionCommitGraphView' => 'applications/diffusion/view/DiffusionCommitGraphView.php',
'DiffusionCommitHasPackageEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasPackageEdgeType.php',
'DiffusionCommitHasRevisionEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php',
'DiffusionCommitHasRevisionRelationship' => 'applications/diffusion/relationships/DiffusionCommitHasRevisionRelationship.php',
@ -862,7 +863,6 @@ phutil_register_library_map(array(
'DiffusionGitWireProtocolRef' => 'applications/diffusion/protocol/DiffusionGitWireProtocolRef.php',
'DiffusionGitWireProtocolRefList' => 'applications/diffusion/protocol/DiffusionGitWireProtocolRefList.php',
'DiffusionHistoryController' => 'applications/diffusion/controller/DiffusionHistoryController.php',
'DiffusionHistoryListView' => 'applications/diffusion/view/DiffusionHistoryListView.php',
'DiffusionHistoryQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php',
'DiffusionHistoryTableView' => 'applications/diffusion/view/DiffusionHistoryTableView.php',
'DiffusionHistoryView' => 'applications/diffusion/view/DiffusionHistoryView.php',
@ -6860,6 +6860,7 @@ phutil_register_library_map(array(
'DiffusionCommitEditEngine' => 'PhabricatorEditEngine',
'DiffusionCommitFerretEngine' => 'PhabricatorFerretEngine',
'DiffusionCommitFulltextEngine' => 'PhabricatorFulltextEngine',
'DiffusionCommitGraphView' => 'DiffusionView',
'DiffusionCommitHasPackageEdgeType' => 'PhabricatorEdgeType',
'DiffusionCommitHasRevisionEdgeType' => 'PhabricatorEdgeType',
'DiffusionCommitHasRevisionRelationship' => 'DiffusionCommitRelationship',
@ -6955,7 +6956,6 @@ phutil_register_library_map(array(
'DiffusionGitWireProtocolRef' => 'Phobject',
'DiffusionGitWireProtocolRefList' => 'Phobject',
'DiffusionHistoryController' => 'DiffusionController',
'DiffusionHistoryListView' => 'DiffusionHistoryView',
'DiffusionHistoryQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionHistoryTableView' => 'DiffusionHistoryView',
'DiffusionHistoryView' => 'DiffusionView',

View file

@ -35,10 +35,31 @@ final class DiffusionHistoryController extends DiffusionController {
$history = $pager->sliceResults($history);
$history_list = id(new DiffusionHistoryListView())
$identifiers = array();
foreach ($history as $item) {
$identifiers[] = $item->getCommitIdentifier();
}
if ($identifiers) {
$commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepositoryPHIDs(array($repository->getPHID()))
->withIdentifiers($identifiers)
->needCommitData(true)
->needIdentities(true)
->execute();
} else {
$commits = array();
}
$history_list = id(new DiffusionCommitGraphView())
->setViewer($viewer)
->setParents($history_results['parents'])
->setIsHead(!$pager->getOffset())
->setIsTail(!$pager->getHasMorePages())
->setDiffusionRequest($drequest)
->setHistory($history);
->setHistory($history)
->setCommits($commits);
$header = $this->buildHeader($drequest);

View file

@ -0,0 +1,415 @@
<?php
final class DiffusionCommitGraphView
extends DiffusionView {
private $history;
private $commits = array();
private $isHead;
private $isTail;
private $parents;
private $filterParents;
private $commitMap = array();
private $buildableMap;
private $revisionMap;
public function setHistory(array $history) {
assert_instances_of($history, 'DiffusionPathChange');
$this->history = $history;
return $this;
}
public function getHistory() {
return $this->history;
}
public function setCommits(array $commits) {
assert_instances_of($commits, 'PhabricatorRepositoryCommit');
$this->commits = $commits;
$this->commitMap = mpull($commits, null, 'getCommitIdentifier');
return $this;
}
public function getCommits() {
return $this->commits;
}
public function setParents(array $parents) {
$this->parents = $parents;
return $this;
}
public function getParents() {
return $this->parents;
}
public function setIsHead($is_head) {
$this->isHead = $is_head;
return $this;
}
public function getIsHead() {
return $this->isHead;
}
public function setIsTail($is_tail) {
$this->isTail = $is_tail;
return $this;
}
public function getIsTail() {
return $this->isTail;
}
public function setFilterParents($filter_parents) {
$this->filterParents = $filter_parents;
return $this;
}
public function getFilterParents() {
return $this->filterParents;
}
private function getRepository() {
$drequest = $this->getDiffusionRequest();
if (!$drequest) {
return null;
}
return $drequest->getRepository();
}
public function render() {
$viewer = $this->getUser();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
require_celerity_resource('diffusion-css');
Javelin::initBehavior('phabricator-tooltips');
$show_builds = $this->shouldShowBuilds();
$show_revisions = $this->shouldShowRevisions();
$items = $this->newHistoryItems();
$rows = array();
$last_date = null;
foreach ($items as $item) {
$content = array();
$item_epoch = $item['epoch'];
$item_hash = $item['hash'];
$commit = $item['commit'];
$item_date = phabricator_date($item_epoch, $viewer);
if ($item_date !== $last_date) {
$last_date = $item_date;
$content[] = $item_date;
}
$commit_description = $this->getCommitDescription($commit);
$commit_link = $this->getCommitURI($commit, $item_hash);
$short_hash = $this->getCommitObjectName($commit, $item_hash);
$is_disabled = $this->getCommitIsDisabled($commit);
$author_view = $this->getCommitAuthorView($commit);
$item_view = id(new PHUIObjectItemView())
->setHeader($commit_description)
->setObjectName($short_hash)
->setHref($commit_link)
->setDisabled($is_disabled);
if ($author_view !== null) {
$item_view->addAttribute($author_view);
}
$browse_button = $this->newBrowseButton($item_hash);
$build_view = null;
if ($show_builds) {
$build_view = $this->newBuildView($item_hash);
}
$item_view->setSideColumn(
array(
$build_view,
$browse_button,
));
$revision_view = null;
if ($show_revisions) {
$revision_view = $this->newRevisionView($item_hash);
}
if ($revision_view !== null) {
$item_view->addAttribute($revision_view);
}
$view = id(new PHUIObjectItemListView())
->setFlush(true)
->addItem($item_view);
$content[] = $view;
$rows[] = array(
$content,
);
}
$graph = $this->newGraphView();
if ($graph) {
$idx = 0;
foreach ($rows as $key => $row) {
array_unshift($row, $graph[$idx++]);
$rows[$key] = $row;
}
}
foreach ($rows as $key => $row) {
$cells = array();
foreach ($row as $cell) {
$cells[] = phutil_tag('td', array(), $cell);
}
$rows[$key] = phutil_tag('tr', array(), $cells);
}
$table = phutil_tag('table', array(), $rows);
return $table;
}
private function newGraphView() {
if (!$this->getParents()) {
return null;
}
$parents = $this->getParents();
// If we're filtering parents, remove relationships which point to
// commits that are not part of the visible graph. Otherwise, we get
// a big tree of nonsense when viewing release branches like "stable"
// versus "master".
if ($this->getFilterParents()) {
foreach ($parents as $key => $nodes) {
foreach ($nodes as $nkey => $node) {
if (empty($parents[$node])) {
unset($parents[$key][$nkey]);
}
}
}
}
return id(new PHUIDiffGraphView())
->setIsHead($this->getIsHead())
->setIsTail($this->getIsTail())
->renderGraph($parents);
}
private function shouldShowBuilds() {
$viewer = $this->getViewer();
$show_builds = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorHarbormasterApplication',
$this->getUser());
return $show_builds;
}
private function shouldShowRevisions() {
$viewer = $this->getViewer();
$show_revisions = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorDifferentialApplication',
$viewer);
return $show_revisions;
}
private function newHistoryItems() {
$items = array();
$commits = $this->getCommits();
$commit_map = mpull($commits, null, 'getCommitIdentifier');
$history = $this->getHistory();
if ($history !== null) {
foreach ($history as $history_item) {
$commit_hash = $history_item->getCommitIdentifier();
$items[] = array(
'epoch' => $history_item->getEpoch(),
'hash' => $commit_hash,
'commit' => idx($commit_map, $commit_hash),
);
}
} else {
foreach ($commits as $commit) {
$items[] = array(
'epoch' => $commit->getEpoch(),
'hash' => $commit->getCommitIdentifier(),
'commit' => $commit,
);
}
}
return $items;
}
private function getCommitDescription($commit) {
if (!$commit) {
return phutil_tag('em', array(), pht("Discovering\xE2\x80\xA6"));
}
// We can show details once the message and change have been imported.
$partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE |
PhabricatorRepositoryCommit::IMPORTED_CHANGE;
if (!$commit->isPartiallyImported($partial_import)) {
return phutil_tag('em', array(), pht("Importing\xE2\x80\xA6"));
}
return $commit->getCommitData()->getSummary();
}
private function getCommitURI($commit, $hash) {
$repository = $this->getRepository();
if ($repository) {
return $repository->getCommitURI($hash);
}
return $commit->getURI();
}
private function getCommitObjectName($commit, $hash) {
$repository = $this->getRepository();
if ($repository) {
return $repository->formatCommitName(
$hash,
$is_local = true);
}
return $commit->getDisplayName();
}
private function getCommitIsDisabled($commit) {
if (!$commit) {
return true;
}
if ($commit->isUnreachable()) {
return true;
}
return false;
}
private function getCommitAuthorView($commit) {
if (!$commit) {
return null;
}
$viewer = $this->getViewer();
return $commit->newCommitAuthorView($viewer);
}
private function newBrowseButton($hash) {
$commit = $this->getCommit($hash);
return $this->linkBrowse(
'/',
array(
'commit' => $hash,
),
$as_button = true);
}
private function getCommit($hash) {
$commit_map = $this->getCommitMap();
return idx($commit_map, $hash);
}
private function getCommitMap() {
return $this->commitMap;
}
private function newBuildView($hash) {
$commit = $this->getCommit($hash);
if (!$commit) {
return null;
}
$buildable = $this->getBuildable($commit);
if (!$buildable) {
return null;
}
return $this->renderBuildable($buildable, 'button');
}
private function getBuildable(PhabricatorRepositoryCommit $commit) {
$buildable_map = $this->getBuildableMap();
return idx($buildable_map, $commit->getPHID());
}
private function getBuildableMap() {
if ($this->buildableMap === null) {
$commits = $this->getCommits();
$buildables = $this->loadBuildables($commits);
$this->buildableMap = $buildables;
}
return $this->buildableMap;
}
private function newRevisionView($hash) {
$commit = $this->getCommit($hash);
if (!$commit) {
return null;
}
$revisions = $this->getRevisions($commit);
if (!$revisions) {
return null;
}
$revision = head($revisions);
return id(new PHUITagView())
->setName($revision->getMonogram())
->setType(PHUITagView::TYPE_SHADE)
->setColor(PHUITagView::COLOR_BLUE)
->setHref($revision->getURI())
->setBorder(PHUITagView::BORDER_NONE)
->setSlimShady(true);
}
private function getRevisions(PhabricatorRepositoryCommit $commit) {
$revision_map = $this->getRevisionMap();
return idx($revision_map, $commit->getPHID(), array());
}
private function getRevisionMap() {
if ($this->revisionMap === null) {
$this->revisionMap = $this->newRevisionMap();
}
return $this->revisionMap;
}
private function newRevisionMap() {
$viewer = $this->getViewer();
$commits = $this->getCommits();
return DiffusionCommitRevisionQuery::loadRevisionMapForCommits(
$viewer,
$commits);
}
}

View file

@ -1,172 +0,0 @@
<?php
final class DiffusionHistoryListView extends DiffusionHistoryView {
public function render() {
$drequest = $this->getDiffusionRequest();
$viewer = $this->getUser();
$repository = $drequest->getRepository();
require_celerity_resource('diffusion-css');
Javelin::initBehavior('phabricator-tooltips');
$buildables = $this->loadBuildables(
mpull($this->getHistory(), 'getCommit'));
$show_revisions = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorDifferentialApplication',
$viewer);
$handles = $viewer->loadHandles($this->getRequiredHandlePHIDs());
$show_builds = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorHarbormasterApplication',
$this->getUser());
$cur_date = null;
$view = array();
foreach ($this->getHistory() as $history) {
$epoch = $history->getEpoch();
$new_date = phabricator_date($history->getEpoch(), $viewer);
if ($cur_date !== $new_date) {
$date = ucfirst(
phabricator_relative_date($history->getEpoch(), $viewer));
$header = id(new PHUIHeaderView())
->setHeader($date);
$list = id(new PHUIObjectItemListView())
->setFlush(true)
->addClass('diffusion-history-list');
$view[] = id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addClass('diffusion-mobile-view')
->setObjectList($list);
}
if ($epoch) {
$committed = $viewer->formatShortDateTime($epoch);
} else {
$committed = null;
}
$data = $history->getCommitData();
$author_phid = $committer = $committer_phid = null;
if ($data) {
$author_phid = $data->getCommitDetail('authorPHID');
$committer_phid = $data->getCommitDetail('committerPHID');
$committer = $data->getCommitDetail('committer');
}
if ($author_phid && isset($handles[$author_phid])) {
$author_name = $handles[$author_phid]->renderLink();
$author_image = $handles[$author_phid]->getImageURI();
} else {
$author_name = self::renderName($history->getAuthorName());
$author_image =
celerity_get_resource_uri('/rsrc/image/people/user0.png');
}
$different_committer = false;
if ($committer_phid) {
$different_committer = ($committer_phid != $author_phid);
} else if ($committer != '') {
$different_committer = ($committer != $history->getAuthorName());
}
if ($different_committer) {
if ($committer_phid && isset($handles[$committer_phid])) {
$committer = $handles[$committer_phid]->renderLink();
} else {
$committer = self::renderName($committer);
}
$author_name = hsprintf('%s / %s', $author_name, $committer);
}
// We can show details once the message and change have been imported.
$partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE |
PhabricatorRepositoryCommit::IMPORTED_CHANGE;
$commit = $history->getCommit();
if ($commit && $commit->isPartiallyImported($partial_import) && $data) {
$commit_desc = $history->getSummary();
} else {
$commit_desc = phutil_tag('em', array(), pht("Importing\xE2\x80\xA6"));
}
$browse_button = $this->linkBrowse(
$history->getPath(),
array(
'commit' => $history->getCommitIdentifier(),
'branch' => $drequest->getBranch(),
'type' => $history->getFileType(),
),
true);
$diff_tag = null;
if ($show_revisions && $commit) {
$revisions = $this->getRevisionsForCommit($commit);
if ($revisions) {
$revision = head($revisions);
$diff_tag = id(new PHUITagView())
->setName($revision->getMonogram())
->setType(PHUITagView::TYPE_SHADE)
->setColor(PHUITagView::COLOR_BLUE)
->setHref($revision->getURI())
->setBorder(PHUITagView::BORDER_NONE)
->setSlimShady(true);
}
}
$build_view = null;
if ($show_builds) {
$buildable = idx($buildables, $commit->getPHID());
if ($buildable !== null) {
$build_view = $this->renderBuildable($buildable, 'button');
}
}
$message = null;
$commit_link = $repository->getCommitURI(
$history->getCommitIdentifier());
$commit_name = $repository->formatCommitName(
$history->getCommitIdentifier(), $local = true);
$committed = phabricator_datetime($commit->getEpoch(), $viewer);
$author_name = phutil_tag(
'strong',
array(
'class' => 'diffusion-history-author-name',
),
$author_name);
$authored = pht('%s on %s.', $author_name, $committed);
$commit_tag = id(new PHUITagView())
->setName($commit_name)
->setType(PHUITagView::TYPE_SHADE)
->setColor(PHUITagView::COLOR_INDIGO)
->setBorder(PHUITagView::BORDER_NONE)
->setSlimShady(true);
$item = id(new PHUIObjectItemView())
->setHeader($commit_desc)
->setHref($commit_link)
->setDisabled($commit->isUnreachable())
->setDescription($message)
->setImageURI($author_image)
->addAttribute(array($commit_tag, ' ', $diff_tag)) // For Copy Pasta
->addAttribute($authored)
->setSideColumn(array(
$build_view,
$browse_button,
));
$list->addItem($item);
$cur_date = $new_date;
}
return $view;
}
}