1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-27 01:02:42 +01:00

Modernize Audit search engine

Summary:
Fixes T9279. Modernizes the SearchEngine and Query classes. User-facing changes:

  - Added order by commit date, default to order by commit date with newest commits first.
  - Added explicit "Needs Audit by".
  - Added new `packages(...)` typeahead function.
  - Picked up automatic subscribers, projects, and order fields.

This changes behavior a little bit: we previously attempted to exclude, e.g., commits which a package you own needs to audit, but which you have resigned from. This is difficult in general and I think it needs a more comprehensive solution. This shouldn't impact users much, anyway.

Test Plan: {F767628}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9279

Differential Revision: https://secure.phabricator.com/D14013
This commit is contained in:
epriestley 2015-08-31 10:17:54 -07:00
parent e67c438943
commit bce0698a0f
11 changed files with 449 additions and 206 deletions

View file

@ -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',

View file

@ -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();

View file

@ -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()));

View file

@ -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;
}
}

View file

@ -0,0 +1,25 @@
<?php
final class DiffusionAuditorFunctionDatasource
extends PhabricatorTypeaheadCompositeDatasource {
public function getBrowseTitle() {
return pht('Browse Auditors');
}
public function getPlaceholderText() {
return pht('Type a user, project, package name or function...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorDiffusionApplication';
}
public function getComponentDatasources() {
return array(
new PhabricatorProjectOrUserFunctionDatasource(),
new PhabricatorOwnersPackageFunctionDatasource(),
);
}
}

View file

@ -349,9 +349,8 @@ final class PhabricatorHomeMainController extends PhabricatorHomeController {
$query = id(new DiffusionCommitQuery())
->setViewer($user)
->withAuditorPHIDs($phids)
->withNeedsAuditByPHIDs($phids)
->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_OPEN)
->withAuditAwaitingUser($user)
->needAuditRequests(true)
->needCommitData(true)
->setLimit(10);

View file

@ -0,0 +1,25 @@
<?php
final class PhabricatorOwnersPackageFunctionDatasource
extends PhabricatorTypeaheadCompositeDatasource {
public function getBrowseTitle() {
return pht('Browse Packages');
}
public function getPlaceholderText() {
return pht('Type a package name or function...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorOwnersApplication';
}
public function getComponentDatasources() {
return array(
new PhabricatorOwnersPackageDatasource(),
new PhabricatorOwnersPackageOwnerDatasource(),
);
}
}

View file

@ -0,0 +1,140 @@
<?php
final class PhabricatorOwnersPackageOwnerDatasource
extends PhabricatorTypeaheadCompositeDatasource {
public function getBrowseTitle() {
return pht('Browse Packages by Owner');
}
public function getPlaceholderText() {
return pht('Type packages(<user>) or packages(<project>)...');
}
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;
}
}

View file

@ -0,0 +1,25 @@
<?php
final class PhabricatorProjectOrUserFunctionDatasource
extends PhabricatorTypeaheadCompositeDatasource {
public function getBrowseTitle() {
return pht('Browse Users and Projects');
}
public function getPlaceholderText() {
return pht('Type a user, project name, or function...');
}
public function getComponentDatasources() {
return array(
new PhabricatorViewerDatasource(),
new PhabricatorPeopleDatasource(),
new PhabricatorProjectDatasource(),
new PhabricatorProjectMembersDatasource(),
new PhabricatorProjectUserFunctionDatasource(),
);
}
}

View file

@ -0,0 +1,36 @@
<?php
final class PhabricatorProjectUserFunctionDatasource
extends PhabricatorTypeaheadCompositeDatasource {
public function getBrowseTitle() {
return pht('Browse User Projects');
}
public function getPlaceholderText() {
return pht('Type projects(<user>)...');
}
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;
}
}

View file

@ -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',