@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
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+356 -100
+5 -1
src/__phutil_library_map__.php
··· 520 520 'DiffusionPathQuery' => 'applications/diffusion/query/DiffusionPathQuery.php', 521 521 'DiffusionPathQueryTestCase' => 'applications/diffusion/query/pathid/__tests__/DiffusionPathQueryTestCase.php', 522 522 'DiffusionPathValidateController' => 'applications/diffusion/controller/DiffusionPathValidateController.php', 523 + 'DiffusionPushEventViewController' => 'applications/diffusion/controller/DiffusionPushEventViewController.php', 524 + 'DiffusionPushLogController' => 'applications/diffusion/controller/DiffusionPushLogController.php', 523 525 'DiffusionPushLogListController' => 'applications/diffusion/controller/DiffusionPushLogListController.php', 524 526 'DiffusionQuery' => 'applications/diffusion/query/DiffusionQuery.php', 525 527 'DiffusionRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php', ··· 3070 3072 'DiffusionPathCompleteController' => 'DiffusionController', 3071 3073 'DiffusionPathQueryTestCase' => 'PhabricatorTestCase', 3072 3074 'DiffusionPathValidateController' => 'DiffusionController', 3075 + 'DiffusionPushEventViewController' => 'DiffusionPushLogController', 3076 + 'DiffusionPushLogController' => 'DiffusionController', 3073 3077 'DiffusionPushLogListController' => 3074 3078 array( 3075 - 0 => 'DiffusionController', 3079 + 0 => 'DiffusionPushLogController', 3076 3080 1 => 'PhabricatorApplicationSearchResultsControllerInterface', 3077 3081 ), 3078 3082 'DiffusionQuery' => 'PhabricatorQuery',
+4 -3
src/applications/diffusion/application/PhabricatorApplicationDiffusion.php
··· 47 47 'new/' => 'DiffusionRepositoryNewController', 48 48 '(?P<edit>create)/' => 'DiffusionRepositoryCreateController', 49 49 '(?P<edit>import)/' => 'DiffusionRepositoryCreateController', 50 - 'pushlog/(?:query/(?P<queryKey>[^/]+)/)?' 51 - => 'DiffusionPushLogListController', 52 - 50 + 'pushlog/' => array( 51 + '(?:query/(?P<queryKey>[^/]+)/)?' => 'DiffusionPushLogListController', 52 + 'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController', 53 + ), 53 54 '(?P<callsign>[A-Z]+)/' => array( 54 55 '' => 'DiffusionRepositoryController', 55 56
+184
src/applications/diffusion/controller/DiffusionPushEventViewController.php
··· 1 + <?php 2 + 3 + final class DiffusionPushEventViewController 4 + extends DiffusionPushLogController { 5 + 6 + private $id; 7 + 8 + public function shouldAllowPublic() { 9 + return true; 10 + } 11 + 12 + public function willProcessRequest(array $data) { 13 + $this->id = idx($data, 'id'); 14 + } 15 + 16 + public function processRequest() { 17 + $request = $this->getRequest(); 18 + $viewer = $request->getUser(); 19 + 20 + $event = id(new PhabricatorRepositoryPushEventQuery()) 21 + ->setViewer($viewer) 22 + ->withIDs(array($this->id)) 23 + ->needLogs(true) 24 + ->executeOne(); 25 + if (!$event) { 26 + return new Aphront404Response(); 27 + } 28 + 29 + $repository = $event->getRepository(); 30 + $title = pht('Push %d', $event->getID()); 31 + 32 + $crumbs = $this->buildApplicationCrumbs(); 33 + $crumbs->addTextCrumb( 34 + $repository->getName(), 35 + $this->getApplicationURI($repository->getCallsign().'/')); 36 + $crumbs->addTextCrumb( 37 + pht('Push Logs'), 38 + $this->getApplicationURI( 39 + 'pushlog/?repositories='.$repository->getMonogram())); 40 + $crumbs->addTextCrumb($title); 41 + 42 + $event_properties = $this->buildPropertyList($event); 43 + 44 + $detail_box = id(new PHUIObjectBoxView()) 45 + ->setHeaderText($title) 46 + ->addPropertyList($event_properties); 47 + 48 + $commits = $this->loadCommits($event); 49 + $commits_table = $this->renderCommitsTable($event, $commits); 50 + 51 + $commits_box = id(new PHUIObjectBoxView()) 52 + ->setHeaderText(pht('Pushed Commits')) 53 + ->appendChild($commits_table); 54 + 55 + $updates_table = $this->renderPushLogTable($event->getLogs()); 56 + 57 + $update_box = id(new PHUIObjectBoxView()) 58 + ->setHeaderText(pht('All Pushed Updates')) 59 + ->appendChild($updates_table); 60 + 61 + return $this->buildApplicationPage( 62 + array( 63 + $crumbs, 64 + $detail_box, 65 + $commits_box, 66 + $update_box, 67 + ), 68 + array( 69 + 'title' => $title, 70 + 'device' => true, 71 + )); 72 + } 73 + 74 + private function buildPropertyList(PhabricatorRepositoryPushEvent $event) { 75 + $viewer = $this->getRequest()->getUser(); 76 + 77 + $this->loadHandles(array($event->getPusherPHID())); 78 + 79 + $view = new PHUIPropertyListView(); 80 + 81 + $view->addProperty( 82 + pht('Pushed At'), 83 + phabricator_datetime($event->getEpoch(), $viewer)); 84 + 85 + $view->addProperty( 86 + pht('Pushed By'), 87 + $this->getHandle($event->getPusherPHID())->renderLink()); 88 + 89 + $view->addProperty( 90 + pht('Pushed Via'), 91 + $event->getRemoteProtocol()); 92 + 93 + return $view; 94 + } 95 + 96 + private function loadCommits(PhabricatorRepositoryPushEvent $event) { 97 + $viewer = $this->getRequest()->getUser(); 98 + 99 + $identifiers = array(); 100 + foreach ($event->getLogs() as $log) { 101 + if ($log->getRefType() == PhabricatorRepositoryPushLog::REFTYPE_COMMIT) { 102 + $identifiers[] = $log->getRefNew(); 103 + } 104 + } 105 + 106 + if (!$identifiers) { 107 + return array(); 108 + } 109 + 110 + // NOTE: Commits may not have been parsed/discovered yet. We need to return 111 + // the identifiers no matter what. If possible, we'll also return the 112 + // corresponding commits. 113 + 114 + $commits = id(new DiffusionCommitQuery()) 115 + ->setViewer($viewer) 116 + ->withRepository($event->getRepository()) 117 + ->withIdentifiers($identifiers) 118 + ->execute(); 119 + 120 + $commits = mpull($commits, null, 'getCommitIdentifier'); 121 + 122 + $results = array(); 123 + foreach ($identifiers as $identifier) { 124 + $results[$identifier] = idx($commits, $identifier); 125 + } 126 + 127 + return $results; 128 + } 129 + 130 + private function renderCommitsTable( 131 + PhabricatorRepositoryPushEvent $event, 132 + array $commits) { 133 + 134 + $viewer = $this->getRequest()->getUser(); 135 + $repository = $event->getRepository(); 136 + 137 + $rows = array(); 138 + foreach ($commits as $identifier => $commit) { 139 + if ($commit) { 140 + $partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE | 141 + PhabricatorRepositoryCommit::IMPORTED_CHANGE; 142 + if ($commit->isPartiallyImported($partial_import)) { 143 + $summary = AphrontTableView::renderSingleDisplayLine( 144 + $commit->getSummary()); 145 + } else { 146 + $summary = phutil_tag('em', array(), pht('Importing...')); 147 + } 148 + } else { 149 + $summary = phutil_tag('em', array(), pht('Discovering...')); 150 + } 151 + 152 + $commit_name = $repository->formatCommitName($identifier); 153 + if ($commit) { 154 + $commit_name = phutil_tag( 155 + 'a', 156 + array( 157 + 'href' => '/'.$commit_name, 158 + ), 159 + $commit_name); 160 + } 161 + 162 + $rows[] = array( 163 + $commit_name, 164 + $summary, 165 + ); 166 + } 167 + 168 + $table = id(new AphrontTableView($rows)) 169 + ->setNoDataString(pht("This push didn't push any new commits.")) 170 + ->setHeaders( 171 + array( 172 + pht('Commit'), 173 + pht('Summary'), 174 + )) 175 + ->setColumnClasses( 176 + array( 177 + 'n', 178 + 'wide', 179 + )); 180 + 181 + return $table; 182 + } 183 + 184 + }
+112
src/applications/diffusion/controller/DiffusionPushLogController.php
··· 1 + <?php 2 + 3 + abstract class DiffusionPushLogController extends DiffusionController { 4 + 5 + public function renderPushLogTable(array $logs) { 6 + $viewer = $this->getRequest()->getUser(); 7 + 8 + $this->loadHandles(mpull($logs, 'getPusherPHID')); 9 + 10 + // Figure out which repositories are editable. We only let you see remote 11 + // IPs if you have edit capability on a repository. 12 + $editable_repos = array(); 13 + if ($logs) { 14 + $editable_repos = id(new PhabricatorRepositoryQuery()) 15 + ->setViewer($viewer) 16 + ->requireCapabilities( 17 + array( 18 + PhabricatorPolicyCapability::CAN_VIEW, 19 + PhabricatorPolicyCapability::CAN_EDIT, 20 + )) 21 + ->withPHIDs(mpull($logs, 'getRepositoryPHID')) 22 + ->execute(); 23 + $editable_repos = mpull($editable_repos, null, 'getPHID'); 24 + } 25 + 26 + $rows = array(); 27 + foreach ($logs as $log) { 28 + 29 + // Reveal this if it's valid and the user can edit the repository. 30 + $remote_addr = '-'; 31 + if (isset($editable_repos[$log->getRepositoryPHID()])) { 32 + $remote_long = $log->getPushEvent()->getRemoteAddress(); 33 + if ($remote_long) { 34 + $remote_addr = long2ip($remote_long); 35 + } 36 + } 37 + 38 + $event_id = $log->getPushEvent()->getID(); 39 + 40 + $callsign = $log->getRepository()->getCallsign(); 41 + $rows[] = array( 42 + phutil_tag( 43 + 'a', 44 + array( 45 + 'href' => $this->getApplicationURI('pushlog/view/'.$event_id.'/'), 46 + ), 47 + $event_id), 48 + phutil_tag( 49 + 'a', 50 + array( 51 + 'href' => $this->getApplicationURI($callsign.'/'), 52 + ), 53 + $callsign), 54 + $this->getHandle($log->getPusherPHID())->renderLink(), 55 + $remote_addr, 56 + $log->getPushEvent()->getRemoteProtocol(), 57 + $log->getRefType(), 58 + $log->getRefName(), 59 + phutil_tag( 60 + 'a', 61 + array( 62 + 'href' => '/r'.$callsign.$log->getRefOld(), 63 + ), 64 + $log->getRefOldShort()), 65 + phutil_tag( 66 + 'a', 67 + array( 68 + 'href' => '/r'.$callsign.$log->getRefNew(), 69 + ), 70 + $log->getRefNewShort()), 71 + 72 + // TODO: Make these human-readable. 73 + $log->getChangeFlags(), 74 + $log->getPushEvent()->getRejectCode(), 75 + phabricator_datetime($log->getEpoch(), $viewer), 76 + ); 77 + } 78 + 79 + $table = id(new AphrontTableView($rows)) 80 + ->setHeaders( 81 + array( 82 + pht('Push'), 83 + pht('Repository'), 84 + pht('Pusher'), 85 + pht('From'), 86 + pht('Via'), 87 + pht('Type'), 88 + pht('Name'), 89 + pht('Old'), 90 + pht('New'), 91 + pht('Flags'), 92 + pht('Code'), 93 + pht('Date'), 94 + )) 95 + ->setColumnClasses( 96 + array( 97 + '', 98 + '', 99 + '', 100 + '', 101 + '', 102 + '', 103 + 'wide', 104 + 'n', 105 + 'n', 106 + 'date', 107 + )); 108 + 109 + return $table; 110 + } 111 + 112 + }
+2 -95
src/applications/diffusion/controller/DiffusionPushLogListController.php
··· 1 1 <?php 2 2 3 - final class DiffusionPushLogListController extends DiffusionController 3 + final class DiffusionPushLogListController extends DiffusionPushLogController 4 4 implements PhabricatorApplicationSearchResultsControllerInterface { 5 5 6 6 private $queryKey; ··· 26 26 public function renderResultsList( 27 27 array $logs, 28 28 PhabricatorSavedQuery $query) { 29 - $viewer = $this->getRequest()->getUser(); 30 29 31 - $this->loadHandles(mpull($logs, 'getPusherPHID')); 32 - 33 - // Figure out which repositories are editable. We only let you see remote 34 - // IPs if you have edit capability on a repository. 35 - $editable_repos = array(); 36 - if ($logs) { 37 - $editable_repos = id(new PhabricatorRepositoryQuery()) 38 - ->setViewer($viewer) 39 - ->requireCapabilities( 40 - array( 41 - PhabricatorPolicyCapability::CAN_VIEW, 42 - PhabricatorPolicyCapability::CAN_EDIT, 43 - )) 44 - ->withPHIDs(mpull($logs, 'getRepositoryPHID')) 45 - ->execute(); 46 - $editable_repos = mpull($editable_repos, null, 'getPHID'); 47 - } 48 - 49 - $rows = array(); 50 - foreach ($logs as $log) { 51 - 52 - // Reveal this if it's valid and the user can edit the repository. 53 - $remote_addr = '-'; 54 - if (isset($editable_repos[$log->getRepositoryPHID()])) { 55 - $remote_long = $log->getPushEvent()->getRemoteAddress(); 56 - if ($remote_long) { 57 - $remote_addr = long2ip($remote_long); 58 - } 59 - } 60 - 61 - $callsign = $log->getRepository()->getCallsign(); 62 - $rows[] = array( 63 - $log->getPushEvent()->getID(), 64 - phutil_tag( 65 - 'a', 66 - array( 67 - 'href' => $this->getApplicationURI($callsign.'/'), 68 - ), 69 - $callsign), 70 - $this->getHandle($log->getPusherPHID())->renderLink(), 71 - $remote_addr, 72 - $log->getPushEvent()->getRemoteProtocol(), 73 - $log->getRefType(), 74 - $log->getRefName(), 75 - phutil_tag( 76 - 'a', 77 - array( 78 - 'href' => '/r'.$callsign.$log->getRefOld(), 79 - ), 80 - $log->getRefOldShort()), 81 - phutil_tag( 82 - 'a', 83 - array( 84 - 'href' => '/r'.$callsign.$log->getRefNew(), 85 - ), 86 - $log->getRefNewShort()), 87 - 88 - // TODO: Make these human-readable. 89 - $log->getChangeFlags(), 90 - $log->getPushEvent()->getRejectCode(), 91 - phabricator_datetime($log->getEpoch(), $viewer), 92 - ); 93 - } 94 - 95 - $table = id(new AphrontTableView($rows)) 96 - ->setHeaders( 97 - array( 98 - pht('Push'), 99 - pht('Repository'), 100 - pht('Pusher'), 101 - pht('From'), 102 - pht('Via'), 103 - pht('Type'), 104 - pht('Name'), 105 - pht('Old'), 106 - pht('New'), 107 - pht('Flags'), 108 - pht('Code'), 109 - pht('Date'), 110 - )) 111 - ->setColumnClasses( 112 - array( 113 - '', 114 - '', 115 - '', 116 - '', 117 - '', 118 - '', 119 - 'wide', 120 - 'n', 121 - 'n', 122 - 'date', 123 - )); 30 + $table = $this->renderPushLogTable($logs); 124 31 125 32 $box = id(new PHUIBoxView()) 126 33 ->addMargin(PHUI::MARGIN_LARGE)
+24
src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php
··· 7 7 private $phids; 8 8 private $repositoryPHIDs; 9 9 private $pusherPHIDs; 10 + private $needLogs; 10 11 11 12 public function withIDs(array $ids) { 12 13 $this->ids = $ids; ··· 25 26 26 27 public function withPusherPHIDs(array $pusher_phids) { 27 28 $this->pusherPHIDs = $pusher_phids; 29 + return $this; 30 + } 31 + 32 + public function needLogs($need_logs) { 33 + $this->needLogs = $need_logs; 28 34 return $this; 29 35 } 30 36 ··· 63 69 return $events; 64 70 } 65 71 72 + public function didFilterPage(array $events) { 73 + $phids = mpull($events, 'getPHID'); 74 + 75 + if ($this->needLogs) { 76 + $logs = id(new PhabricatorRepositoryPushLogQuery()) 77 + ->setParentQuery($this) 78 + ->setViewer($this->getViewer()) 79 + ->withPushEventPHIDs($phids) 80 + ->execute(); 81 + $logs = mgroup($logs, 'getPushEventPHID'); 82 + foreach ($events as $key => $event) { 83 + $event_logs = idx($logs, $event->getPHID(), array()); 84 + $event->attachLogs($event_logs); 85 + } 86 + } 87 + 88 + return $events; 89 + } 66 90 67 91 private function buildWhereClause(AphrontDatabaseConnection $conn_r) { 68 92 $where = array();
+15 -1
src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php
··· 9 9 private $pusherPHIDs; 10 10 private $refTypes; 11 11 private $newRefs; 12 + private $pushEventPHIDs; 12 13 13 14 public function withIDs(array $ids) { 14 15 $this->ids = $ids; ··· 40 41 return $this; 41 42 } 42 43 44 + public function withPushEventPHIDs(array $phids) { 45 + $this->pushEventPHIDs = $phids; 46 + return $this; 47 + } 48 + 43 49 protected function loadPage() { 44 50 $table = new PhabricatorRepositoryPushLog(); 45 51 $conn_r = $table->establishConnection('r'); ··· 57 63 58 64 public function willFilterPage(array $logs) { 59 65 $event_phids = mpull($logs, 'getPushEventPHID'); 60 - $events = id(new PhabricatorRepositoryPushEventQuery()) 66 + $events = id(new PhabricatorObjectQuery()) 67 + ->setParentQuery($this) 61 68 ->setViewer($this->getViewer()) 62 69 ->withPHIDs($event_phids) 63 70 ->execute(); ··· 105 112 $conn_r, 106 113 'pusherPHID in (%Ls)', 107 114 $this->pusherPHIDs); 115 + } 116 + 117 + if ($this->pushEventPHIDs) { 118 + $where[] = qsprintf( 119 + $conn_r, 120 + 'pushEventPHID in (%Ls)', 121 + $this->pushEventPHIDs); 108 122 } 109 123 110 124 if ($this->refTypes) {
+10
src/applications/repository/storage/PhabricatorRepositoryPushEvent.php
··· 17 17 protected $rejectDetails; 18 18 19 19 private $repository = self::ATTACHABLE; 20 + private $logs = self::ATTACHABLE; 20 21 21 22 public static function initializeNewEvent(PhabricatorUser $viewer) { 22 23 return id(new PhabricatorRepositoryPushEvent()) ··· 42 43 43 44 public function getRepository() { 44 45 return $this->assertAttached($this->repository); 46 + } 47 + 48 + public function attachLogs(array $logs) { 49 + $this->logs = $logs; 50 + return $this; 51 + } 52 + 53 + public function getLogs() { 54 + return $this->assertAttached($this->logs); 45 55 } 46 56 47 57