diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index a80ca41d9a..9388482854 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -496,6 +496,7 @@ phutil_register_library_map(array( 'DifferentialUpdateRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php', 'DifferentialViewPolicyField' => 'applications/differential/customfield/DifferentialViewPolicyField.php', 'DiffusionAuditorDatasource' => 'applications/diffusion/typeahead/DiffusionAuditorDatasource.php', + 'DiffusionAuditorFunctionDatasource' => 'applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php', 'DiffusionAuditorsAddAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php', 'DiffusionAuditorsAddSelfHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php', 'DiffusionAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsHeraldAction.php', @@ -2436,6 +2437,8 @@ phutil_register_library_map(array( 'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php', 'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php', 'PhabricatorOwnersPackageDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php', + 'PhabricatorOwnersPackageFunctionDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php', + 'PhabricatorOwnersPackageOwnerDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php', 'PhabricatorOwnersPackagePHIDType' => 'applications/owners/phid/PhabricatorOwnersPackagePHIDType.php', 'PhabricatorOwnersPackageQuery' => 'applications/owners/query/PhabricatorOwnersPackageQuery.php', 'PhabricatorOwnersPackageSearchEngine' => 'applications/owners/query/PhabricatorOwnersPackageSearchEngine.php', @@ -2632,6 +2635,7 @@ phutil_register_library_map(array( 'PhabricatorProjectNoProjectsDatasource' => 'applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php', 'PhabricatorProjectObjectHasProjectEdgeType' => 'applications/project/edge/PhabricatorProjectObjectHasProjectEdgeType.php', 'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php', + 'PhabricatorProjectOrUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php', 'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php', 'PhabricatorProjectProjectHasMemberEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasMemberEdgeType.php', 'PhabricatorProjectProjectHasObjectEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasObjectEdgeType.php', @@ -2651,6 +2655,7 @@ phutil_register_library_map(array( 'PhabricatorProjectTransactionQuery' => 'applications/project/query/PhabricatorProjectTransactionQuery.php', 'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php', 'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php', + 'PhabricatorProjectUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php', 'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php', 'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php', 'PhabricatorProjectsPolicyRule' => 'applications/policy/rule/PhabricatorProjectsPolicyRule.php', @@ -4158,6 +4163,7 @@ phutil_register_library_map(array( 'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialViewPolicyField' => 'DifferentialCoreCustomField', 'DiffusionAuditorDatasource' => 'PhabricatorTypeaheadCompositeDatasource', + 'DiffusionAuditorFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'DiffusionAuditorsAddAuditorsHeraldAction' => 'DiffusionAuditorsHeraldAction', 'DiffusionAuditorsAddSelfHeraldAction' => 'DiffusionAuditorsHeraldAction', 'DiffusionAuditorsHeraldAction' => 'HeraldAction', @@ -6407,6 +6413,8 @@ phutil_register_library_map(array( 'PhabricatorApplicationTransactionInterface', ), 'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource', + 'PhabricatorOwnersPackageFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', + 'PhabricatorOwnersPackageOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'PhabricatorOwnersPackagePHIDType' => 'PhabricatorPHIDType', 'PhabricatorOwnersPackageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorOwnersPackageSearchEngine' => 'PhabricatorApplicationSearchEngine', @@ -6650,6 +6658,7 @@ phutil_register_library_map(array( 'PhabricatorProjectNoProjectsDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorProjectObjectHasProjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource', + 'PhabricatorProjectOrUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'PhabricatorProjectProfileController' => 'PhabricatorProjectController', 'PhabricatorProjectProjectHasMemberEdgeType' => 'PhabricatorEdgeType', 'PhabricatorProjectProjectHasObjectEdgeType' => 'PhabricatorEdgeType', @@ -6672,6 +6681,7 @@ phutil_register_library_map(array( 'PhabricatorProjectTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener', 'PhabricatorProjectUpdateController' => 'PhabricatorProjectController', + 'PhabricatorProjectUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'PhabricatorProjectViewController' => 'PhabricatorProjectController', 'PhabricatorProjectWatchController' => 'PhabricatorProjectController', 'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule', diff --git a/src/applications/audit/application/PhabricatorAuditApplication.php b/src/applications/audit/application/PhabricatorAuditApplication.php index e186215c96..858993e5aa 100644 --- a/src/applications/audit/application/PhabricatorAuditApplication.php +++ b/src/applications/audit/application/PhabricatorAuditApplication.php @@ -70,9 +70,8 @@ final class PhabricatorAuditApplication extends PhabricatorApplication { $query = id(new DiffusionCommitQuery()) ->setViewer($user) - ->withAuditorPHIDs($phids) + ->withNeedsAuditByPHIDs($phids) ->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_OPEN) - ->withAuditAwaitingUser($user) ->setLimit(self::MAX_STATUS_ITEMS); $commits = $query->execute(); diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php index 97b3a942e4..af0194bc79 100644 --- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php +++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php @@ -11,103 +11,65 @@ final class PhabricatorCommitSearchEngine return 'PhabricatorDiffusionApplication'; } - public function buildSavedQueryFromRequest(AphrontRequest $request) { - $saved = new PhabricatorSavedQuery(); - - $saved->setParameter( - 'auditorPHIDs', - $this->readPHIDsFromRequest($request, 'auditorPHIDs')); - - $saved->setParameter( - 'commitAuthorPHIDs', - $this->readUsersFromRequest($request, 'authors')); - - $saved->setParameter( - 'auditStatus', - $request->getStr('auditStatus')); - - $saved->setParameter( - 'repositoryPHIDs', - $this->readPHIDsFromRequest($request, 'repositoryPHIDs')); - - // -- TODO - T4173 - file location - - return $saved; - } - - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { - $query = id(new DiffusionCommitQuery()) + public function newQuery() { + return id(new DiffusionCommitQuery()) ->needAuditRequests(true) ->needCommitData(true); + } - $auditor_phids = $saved->getParameter('auditorPHIDs', array()); - if ($auditor_phids) { - $query->withAuditorPHIDs($auditor_phids); + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + + if ($map['needsAuditByPHIDs']) { + $query->withNeedsAuditByPHIDs($map['needsAuditByPHIDs']); } - $commit_author_phids = $saved->getParameter('commitAuthorPHIDs', array()); - if ($commit_author_phids) { - $query->withAuthorPHIDs($commit_author_phids); + if ($map['auditorPHIDs']) { + $query->withAuditorPHIDs($map['auditorPHIDs']); } - $audit_status = $saved->getParameter('auditStatus', null); - if ($audit_status) { - $query->withAuditStatus($audit_status); + if ($map['commitAuthorPHIDs']) { + $query->withAuthorPHIDs($map['commitAuthorPHIDs']); } - $awaiting_user_phid = $saved->getParameter('awaitingUserPHID', null); - if ($awaiting_user_phid) { - // This is used only for the built-in "needs attention" filter, - // so cheat and just use the already-loaded viewer rather than reloading - // it. - $query->withAuditAwaitingUser($this->requireViewer()); + if ($map['auditStatus']) { + $query->withAuditStatus($map['auditStatus']); } - $repository_phids = $saved->getParameter('repositoryPHIDs', array()); - if ($repository_phids) { - $query->withRepositoryPHIDs($repository_phids); + if ($map['repositoryPHIDs']) { + $query->withRepositoryPHIDs($map['repositoryPHIDs']); } return $query; } - public function buildSearchForm( - AphrontFormView $form, - PhabricatorSavedQuery $saved) { - - $auditor_phids = $saved->getParameter('auditorPHIDs', array()); - $commit_author_phids = $saved->getParameter( - 'commitAuthorPHIDs', - array()); - $audit_status = $saved->getParameter('auditStatus', null); - $repository_phids = $saved->getParameter('repositoryPHIDs', array()); - - $form - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setDatasource(new DiffusionAuditorDatasource()) - ->setName('auditorPHIDs') - ->setLabel(pht('Auditors')) - ->setValue($auditor_phids)) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setDatasource(new PhabricatorPeopleDatasource()) - ->setName('authors') - ->setLabel(pht('Commit Authors')) - ->setValue($commit_author_phids)) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setName('auditStatus') - ->setLabel(pht('Audit Status')) - ->setOptions($this->getAuditStatusOptions()) - ->setValue($audit_status)) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setLabel(pht('Repositories')) - ->setName('repositoryPHIDs') - ->setDatasource(new DiffusionRepositoryDatasource()) - ->setValue($repository_phids)); - + protected function buildCustomSearchFields() { + return array( + id(new PhabricatorSearchDatasourceField()) + ->setLabel(pht('Needs Audit By')) + ->setKey('needsAuditByPHIDs') + ->setAliases(array('needs', 'need')) + ->setDatasource(new DiffusionAuditorFunctionDatasource()), + id(new PhabricatorSearchDatasourceField()) + ->setLabel(pht('Auditors')) + ->setKey('auditorPHIDs') + ->setAliases(array('auditor', 'auditors')) + ->setDatasource(new DiffusionAuditorFunctionDatasource()), + id(new PhabricatorUsersSearchField()) + ->setLabel(pht('Authors')) + ->setKey('commitAuthorPHIDs') + ->setAliases(array('author', 'authors')), + id(new PhabricatorSearchSelectField()) + ->setLabel(pht('Audit Status')) + ->setKey('auditStatus') + ->setAliases(array('status')) + ->setOptions($this->getAuditStatusOptions()), + id(new PhabricatorSearchDatasourceField()) + ->setLabel(pht('Repositories')) + ->setKey('repositoryPHIDs') + ->setAliases(array('repository', 'repositories')) + ->setDatasource(new DiffusionRepositoryDatasource()), + ); } protected function getURI($path) { @@ -118,7 +80,7 @@ final class PhabricatorCommitSearchEngine $names = array(); if ($this->requireViewer()->isLoggedIn()) { - $names['need'] = pht('Need Attention'); + $names['need'] = pht('Needs Audit'); $names['problem'] = pht('Problem Commits'); } @@ -138,22 +100,24 @@ final class PhabricatorCommitSearchEngine $query->setQueryKey($query_key); $viewer = $this->requireViewer(); + $viewer_phid = $viewer->getPHID(); + $status_open = DiffusionCommitQuery::AUDIT_STATUS_OPEN; + switch ($query_key) { case 'all': return $query; case 'open': - $query->setParameter( - 'auditStatus', - DiffusionCommitQuery::AUDIT_STATUS_OPEN); + $query->setParameter('auditStatus', $status_open); return $query; case 'need': - $query->setParameter('awaitingUserPHID', $viewer->getPHID()); - $query->setParameter( - 'auditStatus', - DiffusionCommitQuery::AUDIT_STATUS_OPEN); - $query->setParameter( - 'auditorPHIDs', - PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($viewer)); + $needs_tokens = array( + $viewer_phid, + 'projects('.$viewer_phid.')', + 'packages('.$viewer_phid.')', + ); + + $query->setParameter('needsAuditByPHIDs', $needs_tokens); + $query->setParameter('auditStatus', $status_open); return $query; case 'authored': $query->setParameter('commitAuthorPHIDs', array($viewer->getPHID())); diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php index 45f8b063af..5d6b2623b5 100644 --- a/src/applications/diffusion/query/DiffusionCommitQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitQuery.php @@ -15,7 +15,7 @@ final class DiffusionCommitQuery private $needAuditRequests; private $auditIDs; private $auditorPHIDs; - private $auditAwaitingUser; + private $needsAuditByPHIDs; private $auditStatus; private $epochMin; private $epochMax; @@ -103,27 +103,6 @@ final class DiffusionCommitQuery return $this; } - /** - * Returns 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->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; @@ -134,8 +113,8 @@ final class DiffusionCommitQuery return $this; } - public function withAuditAwaitingUser(PhabricatorUser $user) { - $this->auditAwaitingUser = $user; + public function withNeedsAuditByPHIDs(array $needs_phids) { + $this->needsAuditByPHIDs = $needs_phids; return $this; } @@ -175,21 +154,12 @@ final class DiffusionCommitQuery } } + public function newResultObject() { + return new PhabricatorRepositoryCommit(); + } + protected function loadPage() { - $table = new PhabricatorRepositoryCommit(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT commit.* FROM %T commit %Q %Q %Q %Q %Q', - $table->getTableName(), - $this->buildJoinClause($conn_r), - $this->buildWhereClause($conn_r), - $this->buildGroupClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); + return $this->loadStandardPage($this->newResultObject()); } protected function willFilterPage(array $commits) { @@ -296,8 +266,8 @@ final class DiffusionCommitQuery return $commits; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); if ($this->repositoryPHIDs !== null) { $map_repositories = id(new PhabricatorRepositoryQuery()) @@ -317,42 +287,42 @@ final class DiffusionCommitQuery if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'commit.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'commit.phid IN (%Ls)', $this->phids); } if ($this->repositoryIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'commit.repositoryID IN (%Ld)', $this->repositoryIDs); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'commit.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->epochMin !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'commit.epoch >= %d', $this->epochMin); } if ($this->epochMax !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'commit.epoch <= %d', $this->epochMax); } @@ -360,13 +330,13 @@ final class DiffusionCommitQuery if ($this->importing !== null) { if ($this->importing) { $where[] = qsprintf( - $conn_r, + $conn, '(commit.importStatus & %d) != %d', PhabricatorRepositoryCommit::IMPORTED_ALL, PhabricatorRepositoryCommit::IMPORTED_ALL); } else { $where[] = qsprintf( - $conn_r, + $conn, '(commit.importStatus & %d) = %d', PhabricatorRepositoryCommit::IMPORTED_ALL, PhabricatorRepositoryCommit::IMPORTED_ALL); @@ -409,7 +379,7 @@ final class DiffusionCommitQuery foreach ($bare as $identifier) { $sql[] = qsprintf( - $conn_r, + $conn, '(commit.commitIdentifier LIKE %> AND '. 'LENGTH(commit.commitIdentifier) = 40)', $identifier); @@ -437,7 +407,7 @@ final class DiffusionCommitQuery continue; } $sql[] = qsprintf( - $conn_r, + $conn, '(commit.repositoryID = %d AND commit.commitIdentifier = %s)', $repo->getID(), // NOTE: Because the 'commitIdentifier' column is a string, MySQL @@ -449,7 +419,7 @@ final class DiffusionCommitQuery continue; } $sql[] = qsprintf( - $conn_r, + $conn, '(commit.repositoryID = %d AND commit.commitIdentifier LIKE %>)', $repo->getID(), $ref['identifier']); @@ -470,28 +440,23 @@ final class DiffusionCommitQuery if ($this->auditIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'audit.id IN (%Ld)', $this->auditIDs); } if ($this->auditorPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, '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. + if ($this->needsAuditByPHIDs !== null) { $where[] = qsprintf( - $conn_r, - '(commit.authorPHID IS NULL OR commit.authorPHID != %s) - OR (audit.auditorPHID = %s)', - $awaiting_user_phid, - $awaiting_user_phid); + $conn, + 'needs.auditorPHID IN (%Ls)', + $this->needsAuditByPHIDs); } $status = $this->auditStatus; @@ -499,33 +464,27 @@ final class DiffusionCommitQuery switch ($status) { case self::AUDIT_STATUS_PARTIAL: $where[] = qsprintf( - $conn_r, + $conn, 'commit.auditStatus = %d', PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED); break; case self::AUDIT_STATUS_ACCEPTED: $where[] = qsprintf( - $conn_r, + $conn, 'commit.auditStatus = %d', PhabricatorAuditCommitStatusConstants::FULLY_AUDITED); break; case self::AUDIT_STATUS_CONCERN: $where[] = qsprintf( - $conn_r, - 'audit.auditStatus = %s', + $conn, + 'status.auditStatus = %s', PhabricatorAuditStatusConstants::CONCERNED); break; case self::AUDIT_STATUS_OPEN: $where[] = qsprintf( - $conn_r, - 'audit.auditStatus in (%Ls)', + $conn, + 'status.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; @@ -545,9 +504,7 @@ final class DiffusionCommitQuery } } - $where[] = $this->buildPagingClause($conn_r); - - return $this->formatWhereClause($where); + return $where; } protected function didFilterResults(array $filtered) { @@ -560,56 +517,113 @@ final class DiffusionCommitQuery } } - protected function buildJoinClause(AphrontDatabaseConnection $conn_r) { - $joins = array(); + private function shouldJoinStatus() { + return $this->auditStatus; + } + + private function shouldJoinAudits() { + return $this->auditIDs || $this->auditorPHIDs; + } + + private function shouldJoinNeeds() { + return $this->needsAuditByPHIDs; + } + + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { + $join = parent::buildJoinClauseParts($conn); $audit_request = new PhabricatorRepositoryAuditRequest(); - if ($this->shouldJoinAudits()) { - $joins[] = qsprintf( - $conn_r, - '%Q %T audit ON commit.phid = audit.commitPHID', - ($this->rowsMustHaveAudits() ? 'JOIN' : 'LEFT JOIN'), + if ($this->shouldJoinStatus()) { + $join[] = qsprintf( + $conn, + 'LEFT JOIN %T status ON commit.phid = status.commitPHID', $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 ($this->shouldJoinAudits()) { + $join[] = qsprintf( + $conn, + 'JOIN %T audit ON commit.phid = audit.commitPHID', + $audit_request->getTableName()); } - if ($joins) { - return implode(' ', $joins); - } else { - return ''; + if ($this->shouldJoinNeeds()) { + $join[] = qsprintf( + $conn, + 'JOIN %T needs ON commit.phid = needs.commitPHID + AND needs.auditStatus IN (%Ls)', + $audit_request->getTableName(), + array( + PhabricatorAuditStatusConstants::AUDIT_REQUESTED, + PhabricatorAuditStatusConstants::AUDIT_REQUIRED, + )); } + + return $join; } - protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { - $should_group = $this->shouldJoinAudits(); - - // TODO: Currently, the audit table is missing a unique key, so we may - // require a GROUP BY if we perform this join. See T1768. This can be - // removed once the table has the key. - if ($this->auditAwaitingUser) { - $should_group = true; + protected function shouldGroupQueryResultRows() { + if ($this->shouldJoinStatus()) { + return true; } - if ($should_group) { - return 'GROUP BY commit.id'; - } else { - return ''; + if ($this->shouldJoinAudits()) { + return true; } + + if ($this->shouldJoinNeeds()) { + return true; + } + + return parent::shouldGroupQueryResultRows(); } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } + public function getOrderableColumns() { + return parent::getOrderableColumns() + array( + 'epoch' => array( + 'table' => $this->getPrimaryTableAlias(), + 'column' => 'epoch', + 'type' => 'int', + 'reverse' => false, + ), + ); + } + + protected function getPagingValueMap($cursor, array $keys) { + $commit = $this->loadCursorObject($cursor); + return array( + 'id' => $commit->getID(), + 'epoch' => $commit->getEpoch(), + ); + } + + public function getBuiltinOrders() { + $parent = parent::getBuiltinOrders(); + + // Rename the default ID-based orders. + $parent['importnew'] = array( + 'name' => pht('Import Date (Newest First)'), + ) + $parent['newest']; + + $parent['importold'] = array( + 'name' => pht('Import Date (Oldest First)'), + ) + $parent['oldest']; + + return array( + 'newest' => array( + 'vector' => array('epoch', 'id'), + 'name' => pht('Commit Date (Newest First)'), + ), + 'oldest' => array( + 'vector' => array('-epoch', '-id'), + 'name' => pht('Commit Date (Oldest First)'), + ), + ) + $parent; + } + + } diff --git a/src/applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php b/src/applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php new file mode 100644 index 0000000000..2bfc1ee037 --- /dev/null +++ b/src/applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php @@ -0,0 +1,25 @@ +setViewer($user) - ->withAuditorPHIDs($phids) + ->withNeedsAuditByPHIDs($phids) ->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_OPEN) - ->withAuditAwaitingUser($user) ->needAuditRequests(true) ->needCommitData(true) ->setLimit(10); diff --git a/src/applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php b/src/applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php new file mode 100644 index 0000000000..2ad0512ee2 --- /dev/null +++ b/src/applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php @@ -0,0 +1,25 @@ +) or packages()...'); + } + + public function getDatasourceApplicationClass() { + return 'PhabricatorOwnersApplication'; + } + + public function getComponentDatasources() { + return array( + new PhabricatorPeopleDatasource(), + new PhabricatorProjectDatasource(), + ); + } + + public function getDatasourceFunctions() { + return array( + 'packages' => array( + 'name' => pht('Packages: ...'), + 'arguments' => pht('owner'), + 'summary' => pht("Find results in any of an owner's projects."), + 'description' => pht( + "This function allows you to find results associated with any ". + "of the packages a specified user or project is an owner of. ". + "For example, this will find results associated with all of ". + "the projects `%s` owns:\n\n%s\n\n", + 'alincoln', + '> packages(alincoln)'), + ), + ); + } + + protected function didLoadResults(array $results) { + foreach ($results as $result) { + $result + ->setColor(null) + ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) + ->setIcon('fa-asterisk') + ->setPHID('packages('.$result->getPHID().')') + ->setDisplayName(pht('Packages: %s', $result->getDisplayName())) + ->setName($result->getName().' packages'); + } + + return $results; + } + + protected function evaluateFunction($function, array $argv_list) { + $phids = array(); + foreach ($argv_list as $argv) { + $phids[] = head($argv); + } + + $phids = $this->resolvePHIDs($phids); + + $user_phids = array(); + foreach ($phids as $phid) { + if (phid_get_type($phid) == PhabricatorPeopleUserPHIDType::TYPECONST) { + $user_phids[] = $phid; + } + } + + if ($user_phids) { + $projects = id(new PhabricatorProjectQuery()) + ->setViewer($this->getViewer()) + ->withMemberPHIDs($user_phids) + ->execute(); + foreach ($projects as $project) { + $phids[] = $project->getPHID(); + } + } + + return $phids; + } + + public function renderFunctionTokens($function, array $argv_list) { + $phids = array(); + foreach ($argv_list as $argv) { + $phids[] = head($argv); + } + + $phids = $this->resolvePHIDs($phids); + + $tokens = $this->renderTokens($phids); + foreach ($tokens as $token) { + $token->setColor(null); + if ($token->isInvalid()) { + $token + ->setValue(pht('Packages: Invalid Owner')); + } else { + $token + ->setIcon('fa-asterisk') + ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) + ->setKey('packages('.$token->getKey().')') + ->setValue(pht('Packages: %s', $token->getValue())); + } + } + + return $tokens; + } + + private function resolvePHIDs(array $phids) { + + // TODO: It would be nice for this to handle `packages(#project)` from a + // query string or eventually via Conduit. This could also share code with + // PhabricatorProjectLogicalUserDatasource. + + $usernames = array(); + foreach ($phids as $key => $phid) { + if (phid_get_type($phid) != PhabricatorPeopleUserPHIDType::TYPECONST) { + $usernames[$key] = $phid; + } + } + + if ($usernames) { + $users = id(new PhabricatorPeopleQuery()) + ->setViewer($this->getViewer()) + ->withUsernames($usernames) + ->execute(); + $users = mpull($users, null, 'getUsername'); + foreach ($usernames as $key => $username) { + $user = idx($users, $username); + if ($user) { + $phids[$key] = $user->getPHID(); + } + } + } + + return $phids; + } + +} diff --git a/src/applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php b/src/applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php new file mode 100644 index 0000000000..6a6bc25ee0 --- /dev/null +++ b/src/applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php @@ -0,0 +1,25 @@ +)...'); + } + + public function getDatasourceApplicationClass() { + return 'PhabricatorProjectApplication'; + } + + public function getComponentDatasources() { + return array( + new PhabricatorProjectLogicalUserDatasource(), + ); + } + + protected function evaluateFunction($function, array $argv_list) { + $result = parent::evaluateFunction($function, $argv_list); + + foreach ($result as $k => $v) { + if ($v instanceof PhabricatorQueryConstraint) { + $result[$k] = $v->getValue(); + } + } + + return $result; + } + +} diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php index 18328758ba..984e6325f7 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php +++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php @@ -98,6 +98,12 @@ final class PhabricatorRepositoryCommit 'columns' => array('commitIdentifier', 'repositoryID'), 'unique' => true, ), + 'key_epoch' => array( + 'columns' => array('epoch'), + ), + 'key_author' => array( + 'columns' => array('authorPHID', 'epoch'), + ), ), self::CONFIG_NO_MUTATE => array( 'importStatus',