diff --git a/src/applications/diffusion/conduit/ConduitAPI_diffusion_diffquery_Method.php b/src/applications/diffusion/conduit/ConduitAPI_diffusion_diffquery_Method.php index 35cb59854e..6becde1908 100644 --- a/src/applications/diffusion/conduit/ConduitAPI_diffusion_diffquery_Method.php +++ b/src/applications/diffusion/conduit/ConduitAPI_diffusion_diffquery_Method.php @@ -1,241 +1,235 @@ 'required string', 'commit' => 'optional string', ); } protected function getResult(ConduitAPIRequest $request) { $result = parent::getResult($request); return array( 'changes' => mpull($result, 'toDictionary'), - 'effectiveCommit' => $this->getEffectiveCommit($request)); + 'effectiveCommit' => $this->getEffectiveCommit($request), + ); } protected function getGitResult(ConduitAPIRequest $request) { return $this->getGitOrMercurialResult($request); } protected function getMercurialResult(ConduitAPIRequest $request) { return $this->getGitOrMercurialResult($request); } /** * NOTE: We have to work particularly hard for SVN as compared to other VCS. * That's okay but means this shares little code with the other VCS. */ protected function getSVNResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $effective_commit = $this->getEffectiveCommit($request); if (!$effective_commit) { return $this->getEmptyResult(); } $drequest = clone $drequest; $drequest->setCommit($effective_commit); $path_change_query = DiffusionPathChangeQuery::newFromDiffusionRequest( $drequest); $path_changes = $path_change_query->loadChanges(); $path = null; foreach ($path_changes as $change) { if ($change->getPath() == $drequest->getPath()) { $path = $change; } } if (!$path) { return $this->getEmptyResult(); } $change_type = $path->getChangeType(); switch ($change_type) { case DifferentialChangeType::TYPE_MULTICOPY: case DifferentialChangeType::TYPE_DELETE: if ($path->getTargetPath()) { $old = array( $path->getTargetPath(), $path->getTargetCommitIdentifier()); } else { $old = array($path->getPath(), $path->getCommitIdentifier() - 1); } $old_name = $path->getPath(); $new_name = ''; $new = null; break; case DifferentialChangeType::TYPE_ADD: $old = null; $new = array($path->getPath(), $path->getCommitIdentifier()); $old_name = ''; $new_name = $path->getPath(); break; case DifferentialChangeType::TYPE_MOVE_HERE: case DifferentialChangeType::TYPE_COPY_HERE: $old = array( $path->getTargetPath(), $path->getTargetCommitIdentifier()); $new = array($path->getPath(), $path->getCommitIdentifier()); $old_name = $path->getTargetPath(); $new_name = $path->getPath(); break; case DifferentialChangeType::TYPE_MOVE_AWAY: $old = array( $path->getPath(), $path->getCommitIdentifier() - 1); $old_name = $path->getPath(); $new_name = null; $new = null; break; default: $old = array($path->getPath(), $path->getCommitIdentifier() - 1); $new = array($path->getPath(), $path->getCommitIdentifier()); $old_name = $path->getPath(); $new_name = $path->getPath(); break; } $futures = array( 'old' => $this->buildSVNContentFuture($old), 'new' => $this->buildSVNContentFuture($new), ); $futures = array_filter($futures); foreach (Futures($futures) as $key => $future) { $stdout = ''; try { list($stdout) = $future->resolvex(); } catch (CommandException $e) { if ($path->getFileType() != DifferentialChangeType::FILE_DIRECTORY) { throw $e; } } $futures[$key] = $stdout; } $old_data = idx($futures, 'old', ''); $new_data = idx($futures, 'new', ''); $engine = new PhabricatorDifferenceEngine(); $engine->setOldName($old_name); $engine->setNewName($new_name); $raw_diff = $engine->generateRawDiffFromFileContent($old_data, $new_data); $arcanist_changes = DiffusionPathChange::convertToArcanistChanges( $path_changes); $parser = $this->getDefaultParser(); $parser->setChanges($arcanist_changes); $parser->forcePath($path->getPath()); $changes = $parser->parseDiff($raw_diff); $change = $changes[$path->getPath()]; return array($change); } private function getEffectiveCommit(ConduitAPIRequest $request) { if ($this->effectiveCommit === null) { $drequest = $this->getDiffusionRequest(); - $user = $request->getUser(); - $commit = null; - $conduit_result = DiffusionQuery::callConduitWithDiffusionRequest( - $user, + $path = $drequest->getPath(); + $result = DiffusionQuery::callConduitWithDiffusionRequest( + $request->getUser(), $drequest, 'diffusion.lastmodifiedquery', array( - 'commit' => $drequest->getCommit(), - 'path' => $drequest->getPath())); - $c_dict = $conduit_result['commit']; - if ($c_dict) { - $commit = PhabricatorRepositoryCommit::newFromDictionary($c_dict); - } - if (!$commit) { - // TODO: Improve error messages here. - return null; - } - $this->effectiveCommit = $commit->getCommitIdentifier(); + 'paths' => array($path => $drequest->getCommit()), + )); + + $this->effectiveCommit = idx($result, $path); } + return $this->effectiveCommit; } private function buildSVNContentFuture($spec) { if (!$spec) { return null; } $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); list($ref, $rev) = $spec; return $repository->getRemoteCommandFuture( 'cat %s', $repository->getSubversionPathURI($ref, $rev)); } private function getGitOrMercurialResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $effective_commit = $this->getEffectiveCommit($request); if (!$effective_commit) { return $this->getEmptyResult(1); } // TODO: This side effect is kind of sketchy. $drequest->setCommit($effective_commit); $raw_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest); $raw_diff = $raw_query->loadRawDiff(); if (!$raw_diff) { return $this->getEmptyResult(2); } $parser = $this->getDefaultParser(); $changes = $parser->parseDiff($raw_diff); return $changes; } private function getDefaultParser() { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $parser = new ArcanistDiffParser(); $try_encoding = $repository->getDetail('encoding'); if ($try_encoding) { $parser->setTryEncoding($try_encoding); } $parser->setDetectBinaryFiles(true); return $parser; } private function getEmptyResult() { return array(); } } diff --git a/src/applications/diffusion/conduit/ConduitAPI_diffusion_lastmodifiedquery_Method.php b/src/applications/diffusion/conduit/ConduitAPI_diffusion_lastmodifiedquery_Method.php index 8cfa1f6f55..d0101c4b15 100644 --- a/src/applications/diffusion/conduit/ConduitAPI_diffusion_lastmodifiedquery_Method.php +++ b/src/applications/diffusion/conduit/ConduitAPI_diffusion_lastmodifiedquery_Method.php @@ -1,115 +1,84 @@ '; } protected function defineCustomParamTypes() { return array( - 'commit' => 'required string', - 'path' => 'required string', + 'paths' => 'required map', ); } - protected function getResult(ConduitAPIRequest $request) { - list($commit, $commit_data) = parent::getResult($request); - if ($commit) { - $commit = $commit->toDictionary(); - } - if ($commit_data) { - $commit_data = $commit_data->toDictionary(); - } - return array( - 'commit' => $commit, - 'commitData' => $commit_data); - } - protected function getGitResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); - list($hash) = $repository->execxLocalCommand( - 'log -n1 --format=%%H %s -- %s', - $drequest->getCommit(), - $drequest->getPath()); - $hash = trim($hash); + $result = array(); + foreach ($request->getValue('paths') as $path => $commit) { + list($hash) = $repository->execxLocalCommand( + 'log -n1 --format=%%H %s -- %s', + $commit, + $path); + $result[$path] = trim($hash); + } - return $this->loadDataFromHash($hash); + return $result; } protected function getSVNResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); - $path = $drequest->getPath(); - - $history_result = DiffusionQuery::callConduitWithDiffusionRequest( - $request->getUser(), - $drequest, - 'diffusion.historyquery', - array( - 'commit' => $drequest->getCommit(), - 'path' => $path, - 'limit' => 1, - 'offset' => 0, - 'needDirectChanges' => true, - 'needChildChanges' => true)); - $history_array = DiffusionPathChange::newFromConduit( - $history_result['pathChanges']); - - if (!$history_array) { - return array(array(), array()); + $result = array(); + foreach ($request->getValue('paths') as $path => $commit) { + $history_result = DiffusionQuery::callConduitWithDiffusionRequest( + $request->getUser(), + $drequest, + 'diffusion.historyquery', + array( + 'commit' => $commit, + 'path' => $path, + 'limit' => 1, + 'offset' => 0, + 'needDirectChanges' => true, + 'needChildChanges' => true, + )); + + $history_array = DiffusionPathChange::newFromConduit( + $history_result['pathChanges']); + if ($history_array) { + $result[$path] = head($history_array) + ->getCommit() + ->getCommitIdentifier(); + } } - $history = reset($history_array); - - return array($history->getCommit(), $history->getCommitData()); + return $result; } protected function getMercurialResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); - $path = $drequest->getPath(); - - list($hash) = $repository->execxLocalCommand( - 'log --template %s --limit 1 --removed --rev %s -- %s', - '{node}', - hgsprintf('reverse(ancestors(%s))', $drequest->getCommit()), - nonempty(ltrim($path, '/'), '.')); - - return $this->loadDataFromHash($hash); - } - - private function loadDataFromHash($hash) { - $drequest = $this->getDiffusionRequest(); - $repository = $drequest->getRepository(); - - $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere( - 'repositoryID = %d AND commitIdentifier = %s', - $repository->getID(), - $hash); - - if ($commit) { - $commit_data = $commit->loadCommitData(); - } else { - $commit = array(); - $commit_data = array(); + $result = array(); + foreach ($request->getValue('paths') as $path => $commit) { + list($hash) = $repository->execxLocalCommand( + 'log --template %s --limit 1 --removed --rev %s -- %s', + '{node}', + hgsprintf('reverse(ancestors(%s))', $commit), + nonempty(ltrim($path, '/'), '.')); + $result[$path] = trim($hash); } - return array($commit, $commit_data); + return $result; } } diff --git a/src/applications/diffusion/controller/DiffusionLastModifiedController.php b/src/applications/diffusion/controller/DiffusionLastModifiedController.php index fd10082af6..7b96f6ab7f 100644 --- a/src/applications/diffusion/controller/DiffusionLastModifiedController.php +++ b/src/applications/diffusion/controller/DiffusionLastModifiedController.php @@ -1,155 +1,158 @@ getDiffusionRequest(); $request = $this->getRequest(); + $viewer = $request->getUser(); $paths = $request->getStr('paths'); $paths = json_decode($paths, true); if (!is_array($paths)) { return new Aphront400Response(); } + $modified_map = $this->callConduitWithDiffusionRequest( + 'diffusion.lastmodifiedquery', + array( + 'paths' => array_fill_keys($paths, $drequest->getCommit()), + )); + + if ($modified_map) { + $commit_map = id(new DiffusionCommitQuery()) + ->setViewer($viewer) + ->withRepository($drequest->getRepository()) + ->withIdentifiers(array_values($modified_map)) + ->needCommitData(true) + ->execute(); + $commit_map = mpull($commit_map, null, 'getCommitIdentifier'); + } else { + $commit_map = array(); + } + $commits = array(); foreach ($paths as $path) { - $prequest = clone $drequest; - $prequest->setPath($path); - - $conduit_result = $this->callConduitWithDiffusionRequest( - 'diffusion.lastmodifiedquery', - array( - 'commit' => $prequest->getCommit(), - 'path' => $prequest->getPath(), - )); - - $commit = PhabricatorRepositoryCommit::newFromDictionary( - $conduit_result['commit']); - - $commit_data = PhabricatorRepositoryCommitData::newFromDictionary( - $conduit_result['commitData']); - - $commit->attachCommitData($commit_data); - - $phids = array(); - if ($commit_data) { - if ($commit_data->getCommitDetail('authorPHID')) { - $phids[$commit_data->getCommitDetail('authorPHID')] = true; - } - if ($commit_data->getCommitDetail('committerPHID')) { - $phids[$commit_data->getCommitDetail('committerPHID')] = true; + $modified_at = idx($modified_map, $path); + if ($modified_at) { + $commit = idx($commit_map, $modified_at); + if ($commit) { + $commits[$path] = $commit; } } - - $commits[$path] = $commit; } - $phids = array_keys($phids); + $phids = array(); + foreach ($commits as $commit) { + $data = $commit->getCommitData(); + $phids[] = $data->getCommitDetail('authorPHID'); + $phids[] = $data->getCommitDetail('committerPHID'); + } + $phids = array_filter($phids); $handles = $this->loadViewerHandles($phids); $branch = $drequest->loadBranch(); - if ($branch) { + if ($branch && $commits) { $lint_query = id(new DiffusionLintCountQuery()) ->withBranchIDs(array($branch->getID())) ->withPaths(array_keys($commits)); if ($drequest->getLint()) { $lint_query->withCodes(array($drequest->getLint())); } $lint = $lint_query->execute(); } else { $lint = array(); } $output = array(); foreach ($commits as $path => $commit) { $prequest = clone $drequest; $prequest->setPath($path); $output[$path] = $this->renderColumns( $prequest, $handles, $commit, idx($lint, $path)); } return id(new AphrontAjaxResponse())->setContent($output); } private function renderColumns( DiffusionRequest $drequest, array $handles, PhabricatorRepositoryCommit $commit = null, $lint = null) { assert_instances_of($handles, 'PhabricatorObjectHandle'); $viewer = $this->getRequest()->getUser(); if ($commit) { $epoch = $commit->getEpoch(); $modified = DiffusionView::linkCommit( $drequest->getRepository(), $commit->getCommitIdentifier()); $date = phabricator_date($epoch, $viewer); $time = phabricator_time($epoch, $viewer); } else { $modified = ''; $date = ''; $time = ''; } $data = $commit->getCommitData(); if ($data) { $author_phid = $data->getCommitDetail('authorPHID'); if ($author_phid && isset($handles[$author_phid])) { $author = $handles[$author_phid]->renderLink(); } else { $author = DiffusionView::renderName($data->getAuthorName()); } $committer = $data->getCommitDetail('committer'); if ($committer) { $committer_phid = $data->getCommitDetail('committerPHID'); if ($committer_phid && isset($handles[$committer_phid])) { $committer = $handles[$committer_phid]->renderLink(); } else { $committer = DiffusionView::renderName($committer); } if ($author != $committer) { $author = hsprintf('%s/%s', $author, $committer); } } $details = AphrontTableView::renderSingleDisplayLine($data->getSummary()); } else { $author = ''; $details = ''; } $return = array( 'commit' => $modified, 'date' => $date, 'time' => $time, 'author' => $author, 'details' => $details, ); if ($lint !== null) { $return['lint'] = phutil_tag( 'a', array('href' => $drequest->generateURI(array( 'action' => 'lint', 'lint' => null, ))), number_format($lint)); } return $return; } } diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php index 0b8d9cc523..1b7b90d706 100644 --- a/src/applications/diffusion/query/DiffusionCommitQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitQuery.php @@ -1,512 +1,515 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } /** * Load commits by partial or full identifiers, e.g. "rXab82393", "rX1234", * or "a9caf12". When an identifier matches multiple commits, they will all * be returned; callers should be prepared to deal with more results than * they queried for. */ public function withIdentifiers(array $identifiers) { $this->identifiers = $identifiers; return $this; } /** * Look up commits in a specific repository. This is a shorthand for calling * @{method:withDefaultRepository} and @{method:withRepositoryIDs}. */ public function withRepository(PhabricatorRepository $repository) { $this->withDefaultRepository($repository); $this->withRepositoryIDs(array($repository->getID())); return $this; } /** * If a default repository is provided, ambiguous commit identifiers will * be assumed to belong to the default repository. * * For example, "r123" appearing in a commit message in repository X is * likely to be unambiguously "rX123". Normally the reference would be * considered ambiguous, but if you provide a default repository it will * be correctly resolved. */ public function withDefaultRepository(PhabricatorRepository $repository) { $this->defaultRepository = $repository; return $this; } public function withRepositoryIDs(array $repository_ids) { $this->repositoryIDs = $repository_ids; return $this; } public function needCommitData($need) { $this->needCommitData = $need; return $this; } public function needAuditRequests($need) { $this->needAuditRequests = $need; return $this; } /** * Retuns true if we should join the audit table, either because we're * interested in the information if it's available or because matching * rows must always have it. */ private function shouldJoinAudits() { return $this->needAuditRequests || $this->auditStatus || $this->rowsMustHaveAudits(); } /** * Return true if we should `JOIN` (vs `LEFT JOIN`) the audit table, because * matching commits will always have audit rows. */ private function rowsMustHaveAudits() { return $this->auditIDs || $this->auditorPHIDs || $this->auditAwaitingUser; } public function withAuditIDs(array $ids) { $this->auditIDs = $ids; return $this; } public function withAuditorPHIDs(array $auditor_phids) { $this->auditorPHIDs = $auditor_phids; return $this; } public function withAuditAwaitingUser(PhabricatorUser $user) { $this->auditAwaitingUser = $user; return $this; } public function withAuditStatus($status) { $this->auditStatus = $status; return $this; } public function getIdentifierMap() { if ($this->identifierMap === null) { throw new Exception( "You must execute() the query before accessing the identifier map."); } return $this->identifierMap; } protected function getPagingColumn() { return 'commit.id'; } protected function willExecute() { if ($this->identifierMap === null) { $this->identifierMap = array(); } } protected function loadPage() { $table = new PhabricatorRepositoryCommit(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT commit.* %Q FROM %T commit %Q %Q %Q %Q', $this->buildAuditSelect($conn_r), $table->getTableName(), $this->buildJoinClause($conn_r), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); if ($this->shouldJoinAudits()) { $this->loadAuditIds = ipull($data, 'audit_id'); } return $table->loadAllFromArray($data); } private function buildAuditSelect($conn_r) { if ($this->shouldJoinAudits()) { return qsprintf( $conn_r, ', audit.id as audit_id'); } return ''; } protected function willFilterPage(array $commits) { $repository_ids = mpull($commits, 'getRepositoryID', 'getRepositoryID'); $repos = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withIDs($repository_ids) ->execute(); foreach ($commits as $key => $commit) { $repo = idx($repos, $commit->getRepositoryID()); if ($repo) { $commit->attachRepository($repo); } else { unset($commits[$key]); } } if ($this->identifiers !== null) { $ids = array_fuse($this->identifiers); $min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH; $result = array(); foreach ($commits as $commit) { $prefix = 'r'.$commit->getRepository()->getCallsign(); $suffix = $commit->getCommitIdentifier(); if ($commit->getRepository()->isSVN()) { if (isset($ids[$prefix.$suffix])) { $result[$prefix.$suffix][] = $commit; } } else { // This awkward contruction is so we can link the commits up in O(N) // time instead of O(N^2). for ($ii = $min_qualified; $ii <= strlen($suffix); $ii++) { $part = substr($suffix, 0, $ii); if (isset($ids[$prefix.$part])) { $result[$prefix.$part][] = $commit; } if (isset($ids[$part])) { $result[$part][] = $commit; } } } } foreach ($result as $identifier => $matching_commits) { if (count($matching_commits) == 1) { $result[$identifier] = head($matching_commits); } else { // This reference is ambiguous (it matches more than one commit) so // don't link it unset($result[$identifier]); } } $this->identifierMap += $result; } return $commits; } protected function didFilterPage(array $commits) { if ($this->needCommitData) { $data = id(new PhabricatorRepositoryCommitData())->loadAllWhere( 'commitID in (%Ld)', mpull($commits, 'getID')); $data = mpull($data, null, 'getCommitID'); foreach ($commits as $commit) { $commit_data = idx($data, $commit->getID()); + if (!$commit_data) { + $commit_data = new PhabricatorRepositoryCommitData(); + } $commit->attachCommitData($commit_data); } } if ($this->shouldJoinAudits()) { $load_ids = array_filter($this->loadAuditIds); if ($load_ids) { $requests = id(new PhabricatorRepositoryAuditRequest()) ->loadAllWhere('id IN (%Ld)', $this->loadAuditIds); } else { $requests = array(); } $requests = mgroup($requests, 'getCommitPHID'); foreach ($commits as $commit) { $audit_requests = idx($requests, $commit->getPHID(), array()); $commit->attachAudits($audit_requests); foreach ($audit_requests as $audit_request) { $audit_request->attachCommit($commit); } } } return $commits; } private function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'commit.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'commit.phid IN (%Ls)', $this->phids); } if ($this->repositoryIDs !== null) { $where[] = qsprintf( $conn_r, 'commit.repositoryID IN (%Ld)', $this->repositoryIDs); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn_r, 'commit.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->identifiers !== null) { $min_unqualified = PhabricatorRepository::MINIMUM_UNQUALIFIED_HASH; $min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH; $refs = array(); $bare = array(); foreach ($this->identifiers as $identifier) { $matches = null; preg_match('/^(?:r([A-Z]+))?(.*)$/', $identifier, $matches); $repo = nonempty($matches[1], null); $identifier = nonempty($matches[2], null); if ($repo === null) { if ($this->defaultRepository) { $repo = $this->defaultRepository->getCallsign(); } } if ($repo === null) { if (strlen($identifier) < $min_unqualified) { continue; } $bare[] = $identifier; } else { $refs[] = array( 'callsign' => $repo, 'identifier' => $identifier, ); } } $sql = array(); foreach ($bare as $identifier) { $sql[] = qsprintf( $conn_r, '(commit.commitIdentifier LIKE %> AND '. 'LENGTH(commit.commitIdentifier) = 40)', $identifier); } if ($refs) { $callsigns = ipull($refs, 'callsign'); $repos = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withCallsigns($callsigns) ->execute(); $repos = mpull($repos, null, 'getCallsign'); foreach ($refs as $key => $ref) { $repo = idx($repos, $ref['callsign']); if (!$repo) { continue; } if ($repo->isSVN()) { if (!ctype_digit($ref['identifier'])) { continue; } $sql[] = qsprintf( $conn_r, '(commit.repositoryID = %d AND commit.commitIdentifier = %s)', $repo->getID(), // NOTE: Because the 'commitIdentifier' column is a string, MySQL // ignores the index if we hand it an integer. Hand it a string. // See T3377. (int)$ref['identifier']); } else { if (strlen($ref['identifier']) < $min_qualified) { continue; } $sql[] = qsprintf( $conn_r, '(commit.repositoryID = %d AND commit.commitIdentifier LIKE %>)', $repo->getID(), $ref['identifier']); } } } if (!$sql) { // If we discarded all possible identifiers (e.g., they all referenced // bogus repositories or were all too short), make sure the query finds // nothing. throw new PhabricatorEmptyQueryException( pht('No commit identifiers.')); } $where[] = '('.implode(' OR ', $sql).')'; } if ($this->auditIDs !== null) { $where[] = qsprintf( $conn_r, 'audit.id IN (%Ld)', $this->auditIDs); } if ($this->auditorPHIDs !== null) { $where[] = qsprintf( $conn_r, 'audit.auditorPHID IN (%Ls)', $this->auditorPHIDs); } if ($this->auditAwaitingUser) { $awaiting_user_phid = $this->auditAwaitingUser->getPHID(); // Exclude package and project audits associated with commits where // the user is the author. $where[] = qsprintf( $conn_r, '(commit.authorPHID IS NULL OR commit.authorPHID != %s) OR (audit.auditorPHID = %s)', $awaiting_user_phid, $awaiting_user_phid); } $status = $this->auditStatus; if ($status !== null) { switch ($status) { case self::AUDIT_STATUS_CONCERN: $where[] = qsprintf( $conn_r, 'audit.auditStatus = %s', PhabricatorAuditStatusConstants::CONCERNED); break; case self::AUDIT_STATUS_OPEN: $where[] = qsprintf( $conn_r, 'audit.auditStatus in (%Ls)', PhabricatorAuditStatusConstants::getOpenStatusConstants()); if ($this->auditAwaitingUser) { $where[] = qsprintf( $conn_r, 'awaiting.auditStatus IS NULL OR awaiting.auditStatus != %s', PhabricatorAuditStatusConstants::RESIGNED); } break; case self::AUDIT_STATUS_ANY: break; default: $valid = array( self::AUDIT_STATUS_ANY, self::AUDIT_STATUS_OPEN, self::AUDIT_STATUS_CONCERN, ); throw new Exception( "Unknown audit status '{$status}'! Valid statuses are: ". implode(', ', $valid)); } } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function didFilterResults(array $filtered) { if ($this->identifierMap) { foreach ($this->identifierMap as $name => $commit) { if (isset($filtered[$commit->getPHID()])) { unset($this->identifierMap[$name]); } } } } private function buildJoinClause($conn_r) { $joins = array(); $audit_request = new PhabricatorRepositoryAuditRequest(); if ($this->shouldJoinAudits()) { $joins[] = qsprintf( $conn_r, '%Q %T audit ON commit.phid = audit.commitPHID', ($this->rowsMustHaveAudits() ? 'JOIN' : 'LEFT JOIN'), $audit_request->getTableName()); } if ($this->auditAwaitingUser) { // Join the request table on the awaiting user's requests, so we can // filter out package and project requests which the user has resigned // from. $joins[] = qsprintf( $conn_r, 'LEFT JOIN %T awaiting ON audit.commitPHID = awaiting.commitPHID AND awaiting.auditorPHID = %s', $audit_request->getTableName(), $this->auditAwaitingUser->getPHID()); } if ($joins) { return implode(' ', $joins); } else { return ''; } } public function getQueryApplicationClass() { return 'PhabricatorApplicationDiffusion'; } }