mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 00:42:41 +01:00
Modernize "Responsible Users" tokenizer and add "exact(user)" token
Summary: Ref T10939. Fixes T9263. Ref T4144. First, this resolves users (converting users into all packages and projects they are responsible for) earlier, so bucketing can act on that data correctly. Previously, your own blocking reviews would appear in "Must Review" but your packages/projects' would not. Now, all of them will. Second, this adds `exact(username)` to mean "just me, not my packages/projects". You can use this along with "Bucket: By Required Action" to create a personal view of "Active Revisions" if you'd like, and ignore all your project/package reviews. Test Plan: Queried by "me" and "exact(me)", got reasonable looking results. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4144, T9263, T10939 Differential Revision: https://secure.phabricator.com/D15925
This commit is contained in:
parent
42d49be47b
commit
d46378df20
11 changed files with 244 additions and 29 deletions
|
@ -435,6 +435,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php',
|
||||
'DifferentialDraft' => 'applications/differential/storage/DifferentialDraft.php',
|
||||
'DifferentialEditPolicyField' => 'applications/differential/customfield/DifferentialEditPolicyField.php',
|
||||
'DifferentialExactUserFunctionDatasource' => 'applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php',
|
||||
'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php',
|
||||
'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php',
|
||||
'DifferentialFindConduitAPIMethod' => 'applications/differential/conduit/DifferentialFindConduitAPIMethod.php',
|
||||
|
@ -491,6 +492,8 @@ phutil_register_library_map(array(
|
|||
'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php',
|
||||
'DifferentialRepositoryLookup' => 'applications/differential/query/DifferentialRepositoryLookup.php',
|
||||
'DifferentialRequiredSignaturesField' => 'applications/differential/customfield/DifferentialRequiredSignaturesField.php',
|
||||
'DifferentialResponsibleDatasource' => 'applications/differential/typeahead/DifferentialResponsibleDatasource.php',
|
||||
'DifferentialResponsibleUserDatasource' => 'applications/differential/typeahead/DifferentialResponsibleUserDatasource.php',
|
||||
'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php',
|
||||
'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php',
|
||||
'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php',
|
||||
|
@ -4643,6 +4646,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher',
|
||||
'DifferentialDraft' => 'DifferentialDAO',
|
||||
'DifferentialEditPolicyField' => 'DifferentialCoreCustomField',
|
||||
'DifferentialExactUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'DifferentialFieldParseException' => 'Exception',
|
||||
'DifferentialFieldValidationException' => 'Exception',
|
||||
'DifferentialFindConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
||||
|
@ -4705,6 +4709,8 @@ phutil_register_library_map(array(
|
|||
'DifferentialRepositoryField' => 'DifferentialCoreCustomField',
|
||||
'DifferentialRepositoryLookup' => 'Phobject',
|
||||
'DifferentialRequiredSignaturesField' => 'DifferentialCoreCustomField',
|
||||
'DifferentialResponsibleDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'DifferentialResponsibleUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'DifferentialRevertPlanField' => 'DifferentialStoredCustomField',
|
||||
'DifferentialReviewedByField' => 'DifferentialCoreCustomField',
|
||||
'DifferentialReviewer' => 'Phobject',
|
||||
|
|
|
@ -501,39 +501,17 @@ final class DifferentialRevisionQuery
|
|||
$basic_authors = $this->authors;
|
||||
$basic_reviewers = $this->reviewers;
|
||||
|
||||
$authority_phids = $this->responsibles;
|
||||
|
||||
$authority_projects = id(new PhabricatorProjectQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withMemberPHIDs($this->responsibles)
|
||||
->execute();
|
||||
foreach ($authority_projects as $project) {
|
||||
$authority_phids[] = $project->getPHID();
|
||||
}
|
||||
|
||||
// NOTE: We're querying by explicit owners to make this a little faster,
|
||||
// since we've already expanded project membership so we don't need to
|
||||
// have the PackageQuery do it again.
|
||||
$authority_packages = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withOwnerPHIDs($authority_phids)
|
||||
->execute();
|
||||
foreach ($authority_packages as $package) {
|
||||
$authority_phids[] = $package->getPHID();
|
||||
}
|
||||
|
||||
try {
|
||||
// Build the query where the responsible users are authors.
|
||||
$this->authors = array_merge($basic_authors, $this->responsibles);
|
||||
|
||||
$this->reviewers = $basic_reviewers;
|
||||
$selects[] = $this->buildSelectStatement($conn_r);
|
||||
|
||||
// Build the query where the responsible users are reviewers, or
|
||||
// projects they are members of are reviewers.
|
||||
$this->authors = $basic_authors;
|
||||
$this->reviewers = array_merge(
|
||||
$basic_reviewers,
|
||||
$authority_phids);
|
||||
$this->reviewers = array_merge($basic_reviewers, $this->responsibles);
|
||||
$selects[] = $this->buildSelectStatement($conn_r);
|
||||
|
||||
// Put everything back like it was.
|
||||
|
|
|
@ -17,7 +17,7 @@ final class DifferentialRevisionRequiredActionResultBucket
|
|||
|
||||
$this->objects = $objects;
|
||||
|
||||
$phids = $query->getParameter('responsiblePHIDs', array());
|
||||
$phids = $query->getEvaluatedParameter('responsiblePHIDs', array());
|
||||
if (!$phids) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
|
|
|
@ -51,10 +51,11 @@ final class DifferentialRevisionSearchEngine
|
|||
|
||||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
id(new PhabricatorUsersSearchField())
|
||||
id(new PhabricatorSearchDatasourceField())
|
||||
->setLabel(pht('Responsible Users'))
|
||||
->setKey('responsiblePHIDs')
|
||||
->setAliases(array('responsiblePHID', 'responsibles', 'responsible'))
|
||||
->setDatasource(new DifferentialResponsibleDatasource())
|
||||
->setDescription(
|
||||
pht('Find revisions that a given user is responsible for.')),
|
||||
id(new PhabricatorUsersSearchField())
|
||||
|
@ -67,7 +68,7 @@ final class DifferentialRevisionSearchEngine
|
|||
->setLabel(pht('Reviewers'))
|
||||
->setKey('reviewerPHIDs')
|
||||
->setAliases(array('reviewer', 'reviewers', 'reviewerPHID'))
|
||||
->setDatasource(new DiffusionAuditorDatasource())
|
||||
->setDatasource(new DiffusionAuditorFunctionDatasource())
|
||||
->setDescription(
|
||||
pht('Find revisions with specific reviewers.')),
|
||||
id(new PhabricatorSearchDatasourceField())
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialExactUserFunctionDatasource
|
||||
extends PhabricatorTypeaheadCompositeDatasource {
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse Users');
|
||||
}
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type exact(<user>)...');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorDifferentialApplication';
|
||||
}
|
||||
|
||||
public function getComponentDatasources() {
|
||||
return array(
|
||||
new PhabricatorPeopleDatasource(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getDatasourceFunctions() {
|
||||
return array(
|
||||
'exact' => array(
|
||||
'name' => pht('Exact: ...'),
|
||||
'arguments' => pht('username'),
|
||||
'summary' => pht('Find results matching users exactly.'),
|
||||
'description' => pht(
|
||||
"This function allows you to find results associated only with ".
|
||||
"a user, exactly, and not any of their projects or packages. For ".
|
||||
"example, this will find results associated with only `%s`:".
|
||||
"\n\n%s\n\n",
|
||||
'alincoln',
|
||||
'> exact(alincoln)'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected function didLoadResults(array $results) {
|
||||
foreach ($results as $result) {
|
||||
$result
|
||||
->setColor(null)
|
||||
->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION)
|
||||
->setIcon('fa-asterisk')
|
||||
->setPHID('exact('.$result->getPHID().')')
|
||||
->setDisplayName(pht('Exact User: %s', $result->getDisplayName()))
|
||||
->setName($result->getName().' exact');
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
protected function evaluateFunction($function, array $argv_list) {
|
||||
$phids = array();
|
||||
foreach ($argv_list as $argv) {
|
||||
$phids[] = head($argv);
|
||||
}
|
||||
|
||||
return $this->resolvePHIDs($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('Exact User: Invalid User'));
|
||||
} else {
|
||||
$token
|
||||
->setIcon('fa-asterisk')
|
||||
->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION)
|
||||
->setKey('exact('.$token->getKey().')')
|
||||
->setValue(pht('Exact User: %s', $token->getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
private function resolvePHIDs(array $phids) {
|
||||
$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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialResponsibleDatasource
|
||||
extends PhabricatorTypeaheadCompositeDatasource {
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse Responsible Users');
|
||||
}
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type a user, project, or package name, or function...');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorDifferentialApplication';
|
||||
}
|
||||
|
||||
public function getComponentDatasources() {
|
||||
return array(
|
||||
new DifferentialResponsibleUserDatasource(),
|
||||
new DifferentialExactUserFunctionDatasource(),
|
||||
new PhabricatorProjectDatasource(),
|
||||
new PhabricatorOwnersPackageDatasource(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialResponsibleUserDatasource
|
||||
extends PhabricatorTypeaheadCompositeDatasource {
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse Users');
|
||||
}
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type a user name...');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorDifferentialApplication';
|
||||
}
|
||||
|
||||
public function getComponentDatasources() {
|
||||
return array(
|
||||
new PhabricatorPeopleDatasource(),
|
||||
);
|
||||
}
|
||||
|
||||
protected function evaluateValues(array $values) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$phids = array();
|
||||
foreach ($values as $value) {
|
||||
if (phid_get_type($value) == PhabricatorPeopleUserPHIDType::TYPECONST) {
|
||||
$phids[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$phids) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
$projects = id(new PhabricatorProjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withMemberPHIDs($phids)
|
||||
->execute();
|
||||
foreach ($projects as $project) {
|
||||
$phids[] = $project->getPHID();
|
||||
$values[] = $project->getPHID();
|
||||
}
|
||||
|
||||
$packages = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($viewer)
|
||||
->withOwnerPHIDs($phids)
|
||||
->execute();
|
||||
foreach ($packages as $package) {
|
||||
$values[] = $package->getPHID();
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
}
|
|
@ -143,8 +143,8 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
|
|||
* @param PhabricatorSavedQuery The saved query to operate on.
|
||||
* @return The result of the query.
|
||||
*/
|
||||
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
||||
$saved = clone $saved;
|
||||
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $original) {
|
||||
$saved = clone $original;
|
||||
$this->willUseSavedQuery($saved);
|
||||
|
||||
$fields = $this->buildSearchFields();
|
||||
|
@ -158,6 +158,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
|
|||
$map[$field->getKey()] = $value;
|
||||
}
|
||||
|
||||
$original->attachParameterMap($map);
|
||||
$query = $this->buildQueryFromParameters($map);
|
||||
|
||||
$object = $this->newResultObject();
|
||||
|
|
|
@ -7,6 +7,8 @@ final class PhabricatorSavedQuery extends PhabricatorSearchDAO
|
|||
protected $queryKey;
|
||||
protected $engineClassName;
|
||||
|
||||
private $parameterMap = self::ATTACHABLE;
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
|
@ -52,6 +54,15 @@ final class PhabricatorSavedQuery extends PhabricatorSearchDAO
|
|||
return newv($this->getEngineClassName(), array());
|
||||
}
|
||||
|
||||
public function attachParameterMap(array $map) {
|
||||
$this->parameterMap = $map;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEvaluatedParameter($key, $default = null) {
|
||||
return $this->assertAttachedKey($this->parameterMap, $key, $default);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
|
|
@ -142,6 +142,14 @@ abstract class PhabricatorTypeaheadCompositeDatasource
|
|||
return parent::canEvaluateFunction($function);
|
||||
}
|
||||
|
||||
protected function evaluateValues(array $values) {
|
||||
foreach ($this->getUsableDatasources() as $source) {
|
||||
$values = $source->evaluateValues($values);
|
||||
}
|
||||
|
||||
return parent::evaluateValues($values);
|
||||
}
|
||||
|
||||
protected function evaluateFunction($function, array $argv) {
|
||||
foreach ($this->getUsableDatasources() as $source) {
|
||||
if ($source->canEvaluateFunction($function)) {
|
||||
|
|
|
@ -331,6 +331,14 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task functions
|
||||
*/
|
||||
protected function evaluateValues(array $values) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task functions
|
||||
*/
|
||||
|
@ -345,6 +353,8 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
|||
}
|
||||
}
|
||||
|
||||
$results = $this->evaluateValues($results);
|
||||
|
||||
foreach ($evaluate as $function) {
|
||||
$function = self::parseFunction($function);
|
||||
if (!$function) {
|
||||
|
|
Loading…
Reference in a new issue