1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-25 23:10:57 +01:00

Provide an "event" page for push logs, which shows details on all events in a given push

Summary:
Ref T4677. This shows a more detailed view of an entire "git push", "hg push", or "svn commit".

This is mostly to give push summary emails a reasonable, stable URI to link to for T4677.

Test Plan:
  - Pushed into SVN, Git and Mercurial.
  - Viewed partial and imported event records.

{F134864}

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T4677

Differential Revision: https://secure.phabricator.com/D8616
This commit is contained in:
epriestley 2014-03-26 07:42:30 -07:00
parent a5f55d506f
commit 75c47c6ae0
8 changed files with 356 additions and 100 deletions

View file

@ -520,6 +520,8 @@ phutil_register_library_map(array(
'DiffusionPathQuery' => 'applications/diffusion/query/DiffusionPathQuery.php', 'DiffusionPathQuery' => 'applications/diffusion/query/DiffusionPathQuery.php',
'DiffusionPathQueryTestCase' => 'applications/diffusion/query/pathid/__tests__/DiffusionPathQueryTestCase.php', 'DiffusionPathQueryTestCase' => 'applications/diffusion/query/pathid/__tests__/DiffusionPathQueryTestCase.php',
'DiffusionPathValidateController' => 'applications/diffusion/controller/DiffusionPathValidateController.php', 'DiffusionPathValidateController' => 'applications/diffusion/controller/DiffusionPathValidateController.php',
'DiffusionPushEventViewController' => 'applications/diffusion/controller/DiffusionPushEventViewController.php',
'DiffusionPushLogController' => 'applications/diffusion/controller/DiffusionPushLogController.php',
'DiffusionPushLogListController' => 'applications/diffusion/controller/DiffusionPushLogListController.php', 'DiffusionPushLogListController' => 'applications/diffusion/controller/DiffusionPushLogListController.php',
'DiffusionQuery' => 'applications/diffusion/query/DiffusionQuery.php', 'DiffusionQuery' => 'applications/diffusion/query/DiffusionQuery.php',
'DiffusionRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php', 'DiffusionRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php',
@ -3070,9 +3072,11 @@ phutil_register_library_map(array(
'DiffusionPathCompleteController' => 'DiffusionController', 'DiffusionPathCompleteController' => 'DiffusionController',
'DiffusionPathQueryTestCase' => 'PhabricatorTestCase', 'DiffusionPathQueryTestCase' => 'PhabricatorTestCase',
'DiffusionPathValidateController' => 'DiffusionController', 'DiffusionPathValidateController' => 'DiffusionController',
'DiffusionPushEventViewController' => 'DiffusionPushLogController',
'DiffusionPushLogController' => 'DiffusionController',
'DiffusionPushLogListController' => 'DiffusionPushLogListController' =>
array( array(
0 => 'DiffusionController', 0 => 'DiffusionPushLogController',
1 => 'PhabricatorApplicationSearchResultsControllerInterface', 1 => 'PhabricatorApplicationSearchResultsControllerInterface',
), ),
'DiffusionQuery' => 'PhabricatorQuery', 'DiffusionQuery' => 'PhabricatorQuery',

View file

@ -47,9 +47,10 @@ final class PhabricatorApplicationDiffusion extends PhabricatorApplication {
'new/' => 'DiffusionRepositoryNewController', 'new/' => 'DiffusionRepositoryNewController',
'(?P<edit>create)/' => 'DiffusionRepositoryCreateController', '(?P<edit>create)/' => 'DiffusionRepositoryCreateController',
'(?P<edit>import)/' => 'DiffusionRepositoryCreateController', '(?P<edit>import)/' => 'DiffusionRepositoryCreateController',
'pushlog/(?:query/(?P<queryKey>[^/]+)/)?' 'pushlog/' => array(
=> 'DiffusionPushLogListController', '(?:query/(?P<queryKey>[^/]+)/)?' => 'DiffusionPushLogListController',
'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
),
'(?P<callsign>[A-Z]+)/' => array( '(?P<callsign>[A-Z]+)/' => array(
'' => 'DiffusionRepositoryController', '' => 'DiffusionRepositoryController',

View file

@ -0,0 +1,184 @@
<?php
final class DiffusionPushEventViewController
extends DiffusionPushLogController {
private $id;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$event = id(new PhabricatorRepositoryPushEventQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->needLogs(true)
->executeOne();
if (!$event) {
return new Aphront404Response();
}
$repository = $event->getRepository();
$title = pht('Push %d', $event->getID());
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
$repository->getName(),
$this->getApplicationURI($repository->getCallsign().'/'));
$crumbs->addTextCrumb(
pht('Push Logs'),
$this->getApplicationURI(
'pushlog/?repositories='.$repository->getMonogram()));
$crumbs->addTextCrumb($title);
$event_properties = $this->buildPropertyList($event);
$detail_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->addPropertyList($event_properties);
$commits = $this->loadCommits($event);
$commits_table = $this->renderCommitsTable($event, $commits);
$commits_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Pushed Commits'))
->appendChild($commits_table);
$updates_table = $this->renderPushLogTable($event->getLogs());
$update_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('All Pushed Updates'))
->appendChild($updates_table);
return $this->buildApplicationPage(
array(
$crumbs,
$detail_box,
$commits_box,
$update_box,
),
array(
'title' => $title,
'device' => true,
));
}
private function buildPropertyList(PhabricatorRepositoryPushEvent $event) {
$viewer = $this->getRequest()->getUser();
$this->loadHandles(array($event->getPusherPHID()));
$view = new PHUIPropertyListView();
$view->addProperty(
pht('Pushed At'),
phabricator_datetime($event->getEpoch(), $viewer));
$view->addProperty(
pht('Pushed By'),
$this->getHandle($event->getPusherPHID())->renderLink());
$view->addProperty(
pht('Pushed Via'),
$event->getRemoteProtocol());
return $view;
}
private function loadCommits(PhabricatorRepositoryPushEvent $event) {
$viewer = $this->getRequest()->getUser();
$identifiers = array();
foreach ($event->getLogs() as $log) {
if ($log->getRefType() == PhabricatorRepositoryPushLog::REFTYPE_COMMIT) {
$identifiers[] = $log->getRefNew();
}
}
if (!$identifiers) {
return array();
}
// NOTE: Commits may not have been parsed/discovered yet. We need to return
// the identifiers no matter what. If possible, we'll also return the
// corresponding commits.
$commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepository($event->getRepository())
->withIdentifiers($identifiers)
->execute();
$commits = mpull($commits, null, 'getCommitIdentifier');
$results = array();
foreach ($identifiers as $identifier) {
$results[$identifier] = idx($commits, $identifier);
}
return $results;
}
private function renderCommitsTable(
PhabricatorRepositoryPushEvent $event,
array $commits) {
$viewer = $this->getRequest()->getUser();
$repository = $event->getRepository();
$rows = array();
foreach ($commits as $identifier => $commit) {
if ($commit) {
$partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE |
PhabricatorRepositoryCommit::IMPORTED_CHANGE;
if ($commit->isPartiallyImported($partial_import)) {
$summary = AphrontTableView::renderSingleDisplayLine(
$commit->getSummary());
} else {
$summary = phutil_tag('em', array(), pht('Importing...'));
}
} else {
$summary = phutil_tag('em', array(), pht('Discovering...'));
}
$commit_name = $repository->formatCommitName($identifier);
if ($commit) {
$commit_name = phutil_tag(
'a',
array(
'href' => '/'.$commit_name,
),
$commit_name);
}
$rows[] = array(
$commit_name,
$summary,
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(pht("This push didn't push any new commits."))
->setHeaders(
array(
pht('Commit'),
pht('Summary'),
))
->setColumnClasses(
array(
'n',
'wide',
));
return $table;
}
}

View file

@ -0,0 +1,112 @@
<?php
abstract class DiffusionPushLogController extends DiffusionController {
public function renderPushLogTable(array $logs) {
$viewer = $this->getRequest()->getUser();
$this->loadHandles(mpull($logs, 'getPusherPHID'));
// Figure out which repositories are editable. We only let you see remote
// IPs if you have edit capability on a repository.
$editable_repos = array();
if ($logs) {
$editable_repos = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withPHIDs(mpull($logs, 'getRepositoryPHID'))
->execute();
$editable_repos = mpull($editable_repos, null, 'getPHID');
}
$rows = array();
foreach ($logs as $log) {
// Reveal this if it's valid and the user can edit the repository.
$remote_addr = '-';
if (isset($editable_repos[$log->getRepositoryPHID()])) {
$remote_long = $log->getPushEvent()->getRemoteAddress();
if ($remote_long) {
$remote_addr = long2ip($remote_long);
}
}
$event_id = $log->getPushEvent()->getID();
$callsign = $log->getRepository()->getCallsign();
$rows[] = array(
phutil_tag(
'a',
array(
'href' => $this->getApplicationURI('pushlog/view/'.$event_id.'/'),
),
$event_id),
phutil_tag(
'a',
array(
'href' => $this->getApplicationURI($callsign.'/'),
),
$callsign),
$this->getHandle($log->getPusherPHID())->renderLink(),
$remote_addr,
$log->getPushEvent()->getRemoteProtocol(),
$log->getRefType(),
$log->getRefName(),
phutil_tag(
'a',
array(
'href' => '/r'.$callsign.$log->getRefOld(),
),
$log->getRefOldShort()),
phutil_tag(
'a',
array(
'href' => '/r'.$callsign.$log->getRefNew(),
),
$log->getRefNewShort()),
// TODO: Make these human-readable.
$log->getChangeFlags(),
$log->getPushEvent()->getRejectCode(),
phabricator_datetime($log->getEpoch(), $viewer),
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Push'),
pht('Repository'),
pht('Pusher'),
pht('From'),
pht('Via'),
pht('Type'),
pht('Name'),
pht('Old'),
pht('New'),
pht('Flags'),
pht('Code'),
pht('Date'),
))
->setColumnClasses(
array(
'',
'',
'',
'',
'',
'',
'wide',
'n',
'n',
'date',
));
return $table;
}
}

View file

@ -1,6 +1,6 @@
<?php <?php
final class DiffusionPushLogListController extends DiffusionController final class DiffusionPushLogListController extends DiffusionPushLogController
implements PhabricatorApplicationSearchResultsControllerInterface { implements PhabricatorApplicationSearchResultsControllerInterface {
private $queryKey; private $queryKey;
@ -26,101 +26,8 @@ final class DiffusionPushLogListController extends DiffusionController
public function renderResultsList( public function renderResultsList(
array $logs, array $logs,
PhabricatorSavedQuery $query) { PhabricatorSavedQuery $query) {
$viewer = $this->getRequest()->getUser();
$this->loadHandles(mpull($logs, 'getPusherPHID')); $table = $this->renderPushLogTable($logs);
// Figure out which repositories are editable. We only let you see remote
// IPs if you have edit capability on a repository.
$editable_repos = array();
if ($logs) {
$editable_repos = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withPHIDs(mpull($logs, 'getRepositoryPHID'))
->execute();
$editable_repos = mpull($editable_repos, null, 'getPHID');
}
$rows = array();
foreach ($logs as $log) {
// Reveal this if it's valid and the user can edit the repository.
$remote_addr = '-';
if (isset($editable_repos[$log->getRepositoryPHID()])) {
$remote_long = $log->getPushEvent()->getRemoteAddress();
if ($remote_long) {
$remote_addr = long2ip($remote_long);
}
}
$callsign = $log->getRepository()->getCallsign();
$rows[] = array(
$log->getPushEvent()->getID(),
phutil_tag(
'a',
array(
'href' => $this->getApplicationURI($callsign.'/'),
),
$callsign),
$this->getHandle($log->getPusherPHID())->renderLink(),
$remote_addr,
$log->getPushEvent()->getRemoteProtocol(),
$log->getRefType(),
$log->getRefName(),
phutil_tag(
'a',
array(
'href' => '/r'.$callsign.$log->getRefOld(),
),
$log->getRefOldShort()),
phutil_tag(
'a',
array(
'href' => '/r'.$callsign.$log->getRefNew(),
),
$log->getRefNewShort()),
// TODO: Make these human-readable.
$log->getChangeFlags(),
$log->getPushEvent()->getRejectCode(),
phabricator_datetime($log->getEpoch(), $viewer),
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Push'),
pht('Repository'),
pht('Pusher'),
pht('From'),
pht('Via'),
pht('Type'),
pht('Name'),
pht('Old'),
pht('New'),
pht('Flags'),
pht('Code'),
pht('Date'),
))
->setColumnClasses(
array(
'',
'',
'',
'',
'',
'',
'wide',
'n',
'n',
'date',
));
$box = id(new PHUIBoxView()) $box = id(new PHUIBoxView())
->addMargin(PHUI::MARGIN_LARGE) ->addMargin(PHUI::MARGIN_LARGE)

View file

@ -7,6 +7,7 @@ final class PhabricatorRepositoryPushEventQuery
private $phids; private $phids;
private $repositoryPHIDs; private $repositoryPHIDs;
private $pusherPHIDs; private $pusherPHIDs;
private $needLogs;
public function withIDs(array $ids) { public function withIDs(array $ids) {
$this->ids = $ids; $this->ids = $ids;
@ -28,6 +29,11 @@ final class PhabricatorRepositoryPushEventQuery
return $this; return $this;
} }
public function needLogs($need_logs) {
$this->needLogs = $need_logs;
return $this;
}
protected function loadPage() { protected function loadPage() {
$table = new PhabricatorRepositoryPushEvent(); $table = new PhabricatorRepositoryPushEvent();
$conn_r = $table->establishConnection('r'); $conn_r = $table->establishConnection('r');
@ -63,6 +69,24 @@ final class PhabricatorRepositoryPushEventQuery
return $events; return $events;
} }
public function didFilterPage(array $events) {
$phids = mpull($events, 'getPHID');
if ($this->needLogs) {
$logs = id(new PhabricatorRepositoryPushLogQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->withPushEventPHIDs($phids)
->execute();
$logs = mgroup($logs, 'getPushEventPHID');
foreach ($events as $key => $event) {
$event_logs = idx($logs, $event->getPHID(), array());
$event->attachLogs($event_logs);
}
}
return $events;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) { private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array(); $where = array();

View file

@ -9,6 +9,7 @@ final class PhabricatorRepositoryPushLogQuery
private $pusherPHIDs; private $pusherPHIDs;
private $refTypes; private $refTypes;
private $newRefs; private $newRefs;
private $pushEventPHIDs;
public function withIDs(array $ids) { public function withIDs(array $ids) {
$this->ids = $ids; $this->ids = $ids;
@ -40,6 +41,11 @@ final class PhabricatorRepositoryPushLogQuery
return $this; return $this;
} }
public function withPushEventPHIDs(array $phids) {
$this->pushEventPHIDs = $phids;
return $this;
}
protected function loadPage() { protected function loadPage() {
$table = new PhabricatorRepositoryPushLog(); $table = new PhabricatorRepositoryPushLog();
$conn_r = $table->establishConnection('r'); $conn_r = $table->establishConnection('r');
@ -57,7 +63,8 @@ final class PhabricatorRepositoryPushLogQuery
public function willFilterPage(array $logs) { public function willFilterPage(array $logs) {
$event_phids = mpull($logs, 'getPushEventPHID'); $event_phids = mpull($logs, 'getPushEventPHID');
$events = id(new PhabricatorRepositoryPushEventQuery()) $events = id(new PhabricatorObjectQuery())
->setParentQuery($this)
->setViewer($this->getViewer()) ->setViewer($this->getViewer())
->withPHIDs($event_phids) ->withPHIDs($event_phids)
->execute(); ->execute();
@ -107,6 +114,13 @@ final class PhabricatorRepositoryPushLogQuery
$this->pusherPHIDs); $this->pusherPHIDs);
} }
if ($this->pushEventPHIDs) {
$where[] = qsprintf(
$conn_r,
'pushEventPHID in (%Ls)',
$this->pushEventPHIDs);
}
if ($this->refTypes) { if ($this->refTypes) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn_r,

View file

@ -17,6 +17,7 @@ final class PhabricatorRepositoryPushEvent
protected $rejectDetails; protected $rejectDetails;
private $repository = self::ATTACHABLE; private $repository = self::ATTACHABLE;
private $logs = self::ATTACHABLE;
public static function initializeNewEvent(PhabricatorUser $viewer) { public static function initializeNewEvent(PhabricatorUser $viewer) {
return id(new PhabricatorRepositoryPushEvent()) return id(new PhabricatorRepositoryPushEvent())
@ -44,6 +45,15 @@ final class PhabricatorRepositoryPushEvent
return $this->assertAttached($this->repository); return $this->assertAttached($this->repository);
} }
public function attachLogs(array $logs) {
$this->logs = $logs;
return $this;
}
public function getLogs() {
return $this->assertAttached($this->logs);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */ /* -( PhabricatorPolicyInterface )----------------------------------------- */