Index: resources/sql/patches/20131204.pushlog.sql =================================================================== --- /dev/null +++ resources/sql/patches/20131204.pushlog.sql @@ -0,0 +1,25 @@ +CREATE TABLE {$NAMESPACE}_repository.repository_pushlog ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + epoch INT UNSIGNED NOT NULL, + repositoryPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, + pusherPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, + remoteAddress INT UNSIGNED, + remoteProtocol VARCHAR(32), + transactionKey CHAR(12) NOT NULL COLLATE latin1_bin, + refType VARCHAR(12) NOT NULL COLLATE utf8_bin, + refNameHash VARCHAR(12) COLLATE latin1_bin, + refNameRaw LONGTEXT COLLATE latin1_bin, + refNameEncoding VARCHAR(16) COLLATE utf8_bin, + refOld VARCHAR(40) COLLATE latin1_bin, + refNew VARCHAR(40) NOT NULL COLLATE latin1_bin, + mergeBase VARCHAR(40) COLLATE latin1_bin, + changeFlags INT UNSIGNED NOT NULL, + rejectCode INT UNSIGNED NOT NULL, + rejectDetails VARCHAR(64) COLLATE utf8_bin, + + KEY `key_repository` (repositoryPHID), + KEY `key_ref` (repositoryPHID, refNew), + KEY `key_pusher` (pusherPHID), + KEY `key_name` (repositoryPHID, refNameHash) + +) ENGINE=InnoDB, COLLATE=utf8_general_ci; Index: src/__phutil_library_map__.php =================================================================== --- src/__phutil_library_map__.php +++ src/__phutil_library_map__.php @@ -521,6 +521,7 @@ 'DiffusionPathQuery' => 'applications/diffusion/query/DiffusionPathQuery.php', 'DiffusionPathQueryTestCase' => 'applications/diffusion/query/pathid/__tests__/DiffusionPathQueryTestCase.php', 'DiffusionPathValidateController' => 'applications/diffusion/controller/DiffusionPathValidateController.php', + 'DiffusionPushLogListController' => 'applications/diffusion/controller/DiffusionPushLogListController.php', 'DiffusionQuery' => 'applications/diffusion/query/DiffusionQuery.php', 'DiffusionRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php', 'DiffusionRemarkupRule' => 'applications/diffusion/remarkup/DiffusionRemarkupRule.php', @@ -1791,6 +1792,9 @@ 'PhabricatorRepositoryPHIDTypeRepository' => 'applications/repository/phid/PhabricatorRepositoryPHIDTypeRepository.php', 'PhabricatorRepositoryPullEngine' => 'applications/repository/engine/PhabricatorRepositoryPullEngine.php', 'PhabricatorRepositoryPullLocalDaemon' => 'applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php', + 'PhabricatorRepositoryPushLog' => 'applications/repository/storage/PhabricatorRepositoryPushLog.php', + 'PhabricatorRepositoryPushLogQuery' => 'applications/repository/query/PhabricatorRepositoryPushLogQuery.php', + 'PhabricatorRepositoryPushLogSearchEngine' => 'applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php', 'PhabricatorRepositoryQuery' => 'applications/repository/query/PhabricatorRepositoryQuery.php', 'PhabricatorRepositorySearchEngine' => 'applications/repository/query/PhabricatorRepositorySearchEngine.php', 'PhabricatorRepositoryShortcut' => 'applications/repository/storage/PhabricatorRepositoryShortcut.php', @@ -2846,6 +2850,11 @@ 'DiffusionPathCompleteController' => 'DiffusionController', 'DiffusionPathQueryTestCase' => 'PhabricatorTestCase', 'DiffusionPathValidateController' => 'DiffusionController', + 'DiffusionPushLogListController' => + array( + 0 => 'DiffusionController', + 1 => 'PhabricatorApplicationSearchResultsControllerInterface', + ), 'DiffusionQuery' => 'PhabricatorQuery', 'DiffusionRawDiffQuery' => 'DiffusionQuery', 'DiffusionRemarkupRule' => 'PhabricatorRemarkupRuleObject', @@ -4302,6 +4311,13 @@ 'PhabricatorRepositoryPHIDTypeRepository' => 'PhabricatorPHIDType', 'PhabricatorRepositoryPullEngine' => 'PhabricatorRepositoryEngine', 'PhabricatorRepositoryPullLocalDaemon' => 'PhabricatorDaemon', + 'PhabricatorRepositoryPushLog' => + array( + 0 => 'PhabricatorRepositoryDAO', + 1 => 'PhabricatorPolicyInterface', + ), + 'PhabricatorRepositoryPushLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorRepositoryPushLogSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorRepositoryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorRepositorySearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorRepositoryShortcut' => 'PhabricatorRepositoryDAO', Index: src/applications/diffusion/application/PhabricatorApplicationDiffusion.php =================================================================== --- src/applications/diffusion/application/PhabricatorApplicationDiffusion.php +++ src/applications/diffusion/application/PhabricatorApplicationDiffusion.php @@ -46,6 +46,9 @@ 'new/' => 'DiffusionRepositoryNewController', '(?Pcreate)/' => 'DiffusionRepositoryCreateController', '(?Pimport)/' => 'DiffusionRepositoryCreateController', + 'pushlog/(?:query/(?P[^/]+)/)?' + => 'DiffusionPushLogListController', + '(?P[A-Z]+)/' => array( '' => 'DiffusionRepositoryController', @@ -58,7 +61,6 @@ 'tags/(?P.*)' => 'DiffusionTagListController', 'branches/(?P.*)' => 'DiffusionBranchTableController', 'lint/(?P.*)' => 'DiffusionLintController', - 'commit/(?P[a-z0-9]+)/branches/' => 'DiffusionCommitBranchesController', 'commit/(?P[a-z0-9]+)/tags/' Index: src/applications/diffusion/controller/DiffusionPushLogListController.php =================================================================== --- /dev/null +++ src/applications/diffusion/controller/DiffusionPushLogListController.php @@ -0,0 +1,96 @@ +queryKey = idx($data, 'queryKey'); + } + + public function processRequest() { + $request = $this->getRequest(); + $controller = id(new PhabricatorApplicationSearchController($request)) + ->setQueryKey($this->queryKey) + ->setSearchEngine(new PhabricatorRepositoryPushLogSearchEngine()) + ->setNavigation($this->buildSideNavView()); + + return $this->delegateToController($controller); + } + + public function renderResultsList( + array $logs, + PhabricatorSavedQuery $query) { + $viewer = $this->getRequest()->getUser(); + + $this->loadHandles(mpull($logs, 'getPusherPHID')); + + $rows = array(); + foreach ($logs as $log) { + $callsign = $log->getRepository()->getCallsign(); + $rows[] = array( + phutil_tag( + 'a', + array( + 'href' => $this->getApplicationURI($callsign.'/'), + ), + $callsign), + $this->getHandle($log->getPusherPHID())->renderLink(), + $log->getRefType(), + $log->getRefName(), + $log->getRefOldShort(), + $log->getRefNewShort(), + phabricator_datetime($log->getEpoch(), $viewer), + ); + } + + $table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + pht('Repository'), + pht('Pusher'), + pht('Type'), + pht('Name'), + pht('Old'), + pht('New'), + pht('Date'), + )) + ->setColumnClasses( + array( + '', + '', + '', + 'wide', + 'n', + 'n', + 'date', + )); + + $box = id(new PHUIBoxView()) + ->addMargin(PHUI::MARGIN_LARGE) + ->appendChild($table); + + return $box; + } + + public function buildSideNavView($for_app = false) { + $viewer = $this->getRequest()->getUser(); + + $nav = new AphrontSideNavFilterView(); + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); + + id(new PhabricatorRepositoryPushLogSearchEngine()) + ->setViewer($viewer) + ->addNavigationItems($nav->getMenu()); + + $nav->selectFilter(null); + + return $nav; + } + +} Index: src/applications/diffusion/engine/DiffusionCommitHookEngine.php =================================================================== --- src/applications/diffusion/engine/DiffusionCommitHookEngine.php +++ src/applications/diffusion/engine/DiffusionCommitHookEngine.php @@ -1,5 +1,10 @@ getViewer()) + ->setRepositoryPHID($this->getRepository()->getPHID()) + ->setEpoch(time()) + ->setRemoteAddress(null) // TODO: Populate this where possible. + ->setRemoteProtocol(null) // TODO: Populate this where possible. + ->setTransactionKey($transaction_key) + ->setRefType($update['type']) + ->setRefNameHash(PhabricatorHash::digestForIndex($update['ref'])) + ->setRefNameRaw($update['ref']) + ->setRefNameEncoding(phutil_is_utf8($update['ref']) ? 'utf8' : null) + ->setRefOld($update['old']) + ->setRefNew($update['new']) + ->setMergeBase(idx($update, 'merge-base')) + ->setRejectCode(PhabricatorRepositoryPushLog::REJECT_ACCEPT) + ->setRejectDetails(null); + + $flags = 0; + if ($update['operation'] == 'create') { + $flags = $flags | PhabricatorRepositoryPushLog::CHANGEFLAG_ADD; + } else if ($update['operation'] == 'delete') { + $flags = $flags | PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE; + } else { + // TODO: This isn't correct; these might be APPEND or REWRITE, and + // if they're REWRITE they might be DANGEROUS. Fix this when this + // gets generalized. + $flags = $flags | PhabricatorRepositoryPushLog::CHANGEFLAG_APPEND; + } + + $log->setChangeFlags($flags); + $logs[] = $log; + } + + foreach ($logs as $log) { + $log->save(); + } + return 0; } Index: src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php =================================================================== --- /dev/null +++ src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php @@ -0,0 +1,86 @@ +ids = $ids; + return $this; + } + + public function withRepositoryPHIDs(array $repository_phids) { + $this->repositoryPHIDs = $repository_phids; + return $this; + } + + protected function loadPage() { + $table = new PhabricatorRepositoryPushLog(); + $conn_r = $table->establishConnection('r'); + + $data = queryfx_all( + $conn_r, + 'SELECT * FROM %T %Q %Q %Q', + $table->getTableName(), + $this->buildWhereClause($conn_r), + $this->buildOrderClause($conn_r), + $this->buildLimitClause($conn_r)); + + return $table->loadAllFromArray($data); + } + + public function willFilterPage(array $logs) { + $repository_phids = mpull($logs, 'getRepositoryPHID'); + if ($repository_phids) { + $repositories = id(new PhabricatorRepositoryQuery()) + ->setViewer($this->getViewer()) + ->withPHIDs($repository_phids) + ->execute(); + $repositories = mpull($repositories, null, 'getPHID'); + } else { + $repositories = array(); + } + + foreach ($logs as $key => $log) { + $phid = $log->getRepositoryPHID(); + if (empty($repositories[$phid])) { + unset($logs[$key]); + continue; + } + $log->attachRepository($repositories[$phid]); + } + + return $logs; + } + + + private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + $where = array(); + + if ($this->ids) { + $where[] = qsprintf( + $conn_r, + 'id IN (%Ld)', + $this->ids); + } + + if ($this->repositoryPHIDs) { + $where[] = qsprintf( + $conn_r, + 'repositoryPHID IN (%Ls)', + $this->repositoryPHIDs); + } + + $where[] = $this->buildPagingClause($conn_r); + + return $this->formatWhereClause($where); + } + + + public function getQueryApplicationClass() { + return 'PhabricatorApplicationDiffusion'; + } + +} Index: src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php =================================================================== --- /dev/null +++ src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php @@ -0,0 +1,49 @@ + pht('All Push Logs'), + ); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + +} Index: src/applications/repository/storage/PhabricatorRepositoryPushLog.php =================================================================== --- /dev/null +++ src/applications/repository/storage/PhabricatorRepositoryPushLog.php @@ -0,0 +1,117 @@ +setPusherPHID($viewer->getPHID()); + } + + public function getConfiguration() { + return array( + self::CONFIG_TIMESTAMPS => false, + ) + parent::getConfiguration(); + } + + public function attachRepository(PhabricatorRepository $repository) { + $this->repository = $repository; + return $this; + } + + public function getRepository() { + return $this->assertAttached($this->repository); + } + + public function getRefName() { + if ($this->getRefNameEncoding() == 'utf8') { + return $this->getRefNameRaw(); + } + return phutil_utf8ize($this->getRefNameRaw()); + } + + public function getRefOldShort() { + if ($this->getRepository()->isSVN()) { + return $this->getRefOld(); + } + return substr($this->getRefOld(), 0, 12); + } + + public function getRefNewShort() { + if ($this->getRepository()->isSVN()) { + return $this->getRefNew(); + } + return substr($this->getRefNew(), 0, 12); + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + ); + } + + public function getPolicy($capability) { + return $this->getRepository()->getPolicy($capability); + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return $this->getRepository()->hasAutomaticCapability($capability, $viewer); + } + + public function describeAutomaticCapability($capability) { + return pht( + "A repository's push logs are visible to users who can see the ". + "repository."); + } + +} Index: src/docs/user/userguide/diffusion_hosting.diviner =================================================================== --- src/docs/user/userguide/diffusion_hosting.diviner +++ src/docs/user/userguide/diffusion_hosting.diviner @@ -31,8 +31,10 @@ | Reads | Yes | Yes | | Writes | Yes | Yes | | Authenticated Access | Yes | Yes | +| Push Logs | Yes | Yes | +| Commit Hooks | Yes | Yes | | Anonymous Access | No | Yes | -| Security | Better (Asymetric Key) | Okay (Password) | +| Security | Better (Asymmetric Key) | Okay (Password) | | Performance | Better | Okay | | Setup | Hard | Easy | Index: src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php =================================================================== --- src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -1796,6 +1796,10 @@ 'type' => 'sql', 'name' => $this->getPatchPath('20131129.drydockresourceblueprint.sql'), ), + '20131204.pushlog.sql' => array( + 'type' => 'sql', + 'name' => $this->getPatchPath('20131204.pushlog.sql'), + ), ); } }