diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 807ad90739..115dbafb78 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -520,6 +520,8 @@ phutil_register_library_map(array( 'DiffusionPathQuery' => 'applications/diffusion/query/DiffusionPathQuery.php', 'DiffusionPathQueryTestCase' => 'applications/diffusion/query/pathid/__tests__/DiffusionPathQueryTestCase.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', 'DiffusionQuery' => 'applications/diffusion/query/DiffusionQuery.php', 'DiffusionRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php', @@ -3070,9 +3072,11 @@ phutil_register_library_map(array( 'DiffusionPathCompleteController' => 'DiffusionController', 'DiffusionPathQueryTestCase' => 'PhabricatorTestCase', 'DiffusionPathValidateController' => 'DiffusionController', + 'DiffusionPushEventViewController' => 'DiffusionPushLogController', + 'DiffusionPushLogController' => 'DiffusionController', 'DiffusionPushLogListController' => array( - 0 => 'DiffusionController', + 0 => 'DiffusionPushLogController', 1 => 'PhabricatorApplicationSearchResultsControllerInterface', ), 'DiffusionQuery' => 'PhabricatorQuery', diff --git a/src/applications/diffusion/application/PhabricatorApplicationDiffusion.php b/src/applications/diffusion/application/PhabricatorApplicationDiffusion.php index cd6c0ee090..1d6ff40df5 100644 --- a/src/applications/diffusion/application/PhabricatorApplicationDiffusion.php +++ b/src/applications/diffusion/application/PhabricatorApplicationDiffusion.php @@ -47,9 +47,10 @@ final class PhabricatorApplicationDiffusion extends PhabricatorApplication { 'new/' => 'DiffusionRepositoryNewController', '(?Pcreate)/' => 'DiffusionRepositoryCreateController', '(?Pimport)/' => 'DiffusionRepositoryCreateController', - 'pushlog/(?:query/(?P[^/]+)/)?' - => 'DiffusionPushLogListController', - + 'pushlog/' => array( + '(?:query/(?P[^/]+)/)?' => 'DiffusionPushLogListController', + 'view/(?P\d+)/' => 'DiffusionPushEventViewController', + ), '(?P[A-Z]+)/' => array( '' => 'DiffusionRepositoryController', diff --git a/src/applications/diffusion/controller/DiffusionPushEventViewController.php b/src/applications/diffusion/controller/DiffusionPushEventViewController.php new file mode 100644 index 0000000000..88b237b136 --- /dev/null +++ b/src/applications/diffusion/controller/DiffusionPushEventViewController.php @@ -0,0 +1,184 @@ +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; + } + +} diff --git a/src/applications/diffusion/controller/DiffusionPushLogController.php b/src/applications/diffusion/controller/DiffusionPushLogController.php new file mode 100644 index 0000000000..fa827bd7fd --- /dev/null +++ b/src/applications/diffusion/controller/DiffusionPushLogController.php @@ -0,0 +1,112 @@ +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; + } + +} diff --git a/src/applications/diffusion/controller/DiffusionPushLogListController.php b/src/applications/diffusion/controller/DiffusionPushLogListController.php index 7e5d8416d0..6c8b9ef366 100644 --- a/src/applications/diffusion/controller/DiffusionPushLogListController.php +++ b/src/applications/diffusion/controller/DiffusionPushLogListController.php @@ -1,6 +1,6 @@ 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); - } - } - - $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', - )); + $table = $this->renderPushLogTable($logs); $box = id(new PHUIBoxView()) ->addMargin(PHUI::MARGIN_LARGE) diff --git a/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php index 985ab92a36..49af5a4617 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php @@ -7,6 +7,7 @@ final class PhabricatorRepositoryPushEventQuery private $phids; private $repositoryPHIDs; private $pusherPHIDs; + private $needLogs; public function withIDs(array $ids) { $this->ids = $ids; @@ -28,6 +29,11 @@ final class PhabricatorRepositoryPushEventQuery return $this; } + public function needLogs($need_logs) { + $this->needLogs = $need_logs; + return $this; + } + protected function loadPage() { $table = new PhabricatorRepositoryPushEvent(); $conn_r = $table->establishConnection('r'); @@ -63,6 +69,24 @@ final class PhabricatorRepositoryPushEventQuery 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) { $where = array(); diff --git a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php index a3c1af9a1f..cfd437242e 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php @@ -9,6 +9,7 @@ final class PhabricatorRepositoryPushLogQuery private $pusherPHIDs; private $refTypes; private $newRefs; + private $pushEventPHIDs; public function withIDs(array $ids) { $this->ids = $ids; @@ -40,6 +41,11 @@ final class PhabricatorRepositoryPushLogQuery return $this; } + public function withPushEventPHIDs(array $phids) { + $this->pushEventPHIDs = $phids; + return $this; + } + protected function loadPage() { $table = new PhabricatorRepositoryPushLog(); $conn_r = $table->establishConnection('r'); @@ -57,7 +63,8 @@ final class PhabricatorRepositoryPushLogQuery public function willFilterPage(array $logs) { $event_phids = mpull($logs, 'getPushEventPHID'); - $events = id(new PhabricatorRepositoryPushEventQuery()) + $events = id(new PhabricatorObjectQuery()) + ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($event_phids) ->execute(); @@ -107,6 +114,13 @@ final class PhabricatorRepositoryPushLogQuery $this->pusherPHIDs); } + if ($this->pushEventPHIDs) { + $where[] = qsprintf( + $conn_r, + 'pushEventPHID in (%Ls)', + $this->pushEventPHIDs); + } + if ($this->refTypes) { $where[] = qsprintf( $conn_r, diff --git a/src/applications/repository/storage/PhabricatorRepositoryPushEvent.php b/src/applications/repository/storage/PhabricatorRepositoryPushEvent.php index 3c9778e5ea..d58f5c5394 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryPushEvent.php +++ b/src/applications/repository/storage/PhabricatorRepositoryPushEvent.php @@ -17,6 +17,7 @@ final class PhabricatorRepositoryPushEvent protected $rejectDetails; private $repository = self::ATTACHABLE; + private $logs = self::ATTACHABLE; public static function initializeNewEvent(PhabricatorUser $viewer) { return id(new PhabricatorRepositoryPushEvent()) @@ -44,6 +45,15 @@ final class PhabricatorRepositoryPushEvent return $this->assertAttached($this->repository); } + public function attachLogs(array $logs) { + $this->logs = $logs; + return $this; + } + + public function getLogs() { + return $this->assertAttached($this->logs); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */