1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-27 09:12: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:
epriestley 2016-05-16 08:31:31 -07:00
parent 42d49be47b
commit d46378df20
11 changed files with 244 additions and 29 deletions

View file

@ -435,6 +435,7 @@ phutil_register_library_map(array(
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php', 'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php',
'DifferentialDraft' => 'applications/differential/storage/DifferentialDraft.php', 'DifferentialDraft' => 'applications/differential/storage/DifferentialDraft.php',
'DifferentialEditPolicyField' => 'applications/differential/customfield/DifferentialEditPolicyField.php', 'DifferentialEditPolicyField' => 'applications/differential/customfield/DifferentialEditPolicyField.php',
'DifferentialExactUserFunctionDatasource' => 'applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php',
'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php', 'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php',
'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php', 'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php',
'DifferentialFindConduitAPIMethod' => 'applications/differential/conduit/DifferentialFindConduitAPIMethod.php', 'DifferentialFindConduitAPIMethod' => 'applications/differential/conduit/DifferentialFindConduitAPIMethod.php',
@ -491,6 +492,8 @@ phutil_register_library_map(array(
'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php', 'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php',
'DifferentialRepositoryLookup' => 'applications/differential/query/DifferentialRepositoryLookup.php', 'DifferentialRepositoryLookup' => 'applications/differential/query/DifferentialRepositoryLookup.php',
'DifferentialRequiredSignaturesField' => 'applications/differential/customfield/DifferentialRequiredSignaturesField.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', 'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php',
'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php', 'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php',
'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php', 'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php',
@ -4643,6 +4646,7 @@ phutil_register_library_map(array(
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher', 'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher',
'DifferentialDraft' => 'DifferentialDAO', 'DifferentialDraft' => 'DifferentialDAO',
'DifferentialEditPolicyField' => 'DifferentialCoreCustomField', 'DifferentialEditPolicyField' => 'DifferentialCoreCustomField',
'DifferentialExactUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DifferentialFieldParseException' => 'Exception', 'DifferentialFieldParseException' => 'Exception',
'DifferentialFieldValidationException' => 'Exception', 'DifferentialFieldValidationException' => 'Exception',
'DifferentialFindConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialFindConduitAPIMethod' => 'DifferentialConduitAPIMethod',
@ -4705,6 +4709,8 @@ phutil_register_library_map(array(
'DifferentialRepositoryField' => 'DifferentialCoreCustomField', 'DifferentialRepositoryField' => 'DifferentialCoreCustomField',
'DifferentialRepositoryLookup' => 'Phobject', 'DifferentialRepositoryLookup' => 'Phobject',
'DifferentialRequiredSignaturesField' => 'DifferentialCoreCustomField', 'DifferentialRequiredSignaturesField' => 'DifferentialCoreCustomField',
'DifferentialResponsibleDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DifferentialResponsibleUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DifferentialRevertPlanField' => 'DifferentialStoredCustomField', 'DifferentialRevertPlanField' => 'DifferentialStoredCustomField',
'DifferentialReviewedByField' => 'DifferentialCoreCustomField', 'DifferentialReviewedByField' => 'DifferentialCoreCustomField',
'DifferentialReviewer' => 'Phobject', 'DifferentialReviewer' => 'Phobject',

View file

@ -501,39 +501,17 @@ final class DifferentialRevisionQuery
$basic_authors = $this->authors; $basic_authors = $this->authors;
$basic_reviewers = $this->reviewers; $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 { try {
// Build the query where the responsible users are authors. // Build the query where the responsible users are authors.
$this->authors = array_merge($basic_authors, $this->responsibles); $this->authors = array_merge($basic_authors, $this->responsibles);
$this->reviewers = $basic_reviewers; $this->reviewers = $basic_reviewers;
$selects[] = $this->buildSelectStatement($conn_r); $selects[] = $this->buildSelectStatement($conn_r);
// Build the query where the responsible users are reviewers, or // Build the query where the responsible users are reviewers, or
// projects they are members of are reviewers. // projects they are members of are reviewers.
$this->authors = $basic_authors; $this->authors = $basic_authors;
$this->reviewers = array_merge( $this->reviewers = array_merge($basic_reviewers, $this->responsibles);
$basic_reviewers,
$authority_phids);
$selects[] = $this->buildSelectStatement($conn_r); $selects[] = $this->buildSelectStatement($conn_r);
// Put everything back like it was. // Put everything back like it was.

View file

@ -17,7 +17,7 @@ final class DifferentialRevisionRequiredActionResultBucket
$this->objects = $objects; $this->objects = $objects;
$phids = $query->getParameter('responsiblePHIDs', array()); $phids = $query->getEvaluatedParameter('responsiblePHIDs', array());
if (!$phids) { if (!$phids) {
throw new Exception( throw new Exception(
pht( pht(

View file

@ -51,10 +51,11 @@ final class DifferentialRevisionSearchEngine
protected function buildCustomSearchFields() { protected function buildCustomSearchFields() {
return array( return array(
id(new PhabricatorUsersSearchField()) id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Responsible Users')) ->setLabel(pht('Responsible Users'))
->setKey('responsiblePHIDs') ->setKey('responsiblePHIDs')
->setAliases(array('responsiblePHID', 'responsibles', 'responsible')) ->setAliases(array('responsiblePHID', 'responsibles', 'responsible'))
->setDatasource(new DifferentialResponsibleDatasource())
->setDescription( ->setDescription(
pht('Find revisions that a given user is responsible for.')), pht('Find revisions that a given user is responsible for.')),
id(new PhabricatorUsersSearchField()) id(new PhabricatorUsersSearchField())
@ -67,7 +68,7 @@ final class DifferentialRevisionSearchEngine
->setLabel(pht('Reviewers')) ->setLabel(pht('Reviewers'))
->setKey('reviewerPHIDs') ->setKey('reviewerPHIDs')
->setAliases(array('reviewer', 'reviewers', 'reviewerPHID')) ->setAliases(array('reviewer', 'reviewers', 'reviewerPHID'))
->setDatasource(new DiffusionAuditorDatasource()) ->setDatasource(new DiffusionAuditorFunctionDatasource())
->setDescription( ->setDescription(
pht('Find revisions with specific reviewers.')), pht('Find revisions with specific reviewers.')),
id(new PhabricatorSearchDatasourceField()) id(new PhabricatorSearchDatasourceField())

View file

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

View file

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

View file

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

View file

@ -143,8 +143,8 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
* @param PhabricatorSavedQuery The saved query to operate on. * @param PhabricatorSavedQuery The saved query to operate on.
* @return The result of the query. * @return The result of the query.
*/ */
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { public function buildQueryFromSavedQuery(PhabricatorSavedQuery $original) {
$saved = clone $saved; $saved = clone $original;
$this->willUseSavedQuery($saved); $this->willUseSavedQuery($saved);
$fields = $this->buildSearchFields(); $fields = $this->buildSearchFields();
@ -158,6 +158,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
$map[$field->getKey()] = $value; $map[$field->getKey()] = $value;
} }
$original->attachParameterMap($map);
$query = $this->buildQueryFromParameters($map); $query = $this->buildQueryFromParameters($map);
$object = $this->newResultObject(); $object = $this->newResultObject();

View file

@ -7,6 +7,8 @@ final class PhabricatorSavedQuery extends PhabricatorSearchDAO
protected $queryKey; protected $queryKey;
protected $engineClassName; protected $engineClassName;
private $parameterMap = self::ATTACHABLE;
protected function getConfiguration() { protected function getConfiguration() {
return array( return array(
self::CONFIG_SERIALIZATION => array( self::CONFIG_SERIALIZATION => array(
@ -52,6 +54,15 @@ final class PhabricatorSavedQuery extends PhabricatorSearchDAO
return newv($this->getEngineClassName(), array()); 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 )----------------------------------------- */ /* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -142,6 +142,14 @@ abstract class PhabricatorTypeaheadCompositeDatasource
return parent::canEvaluateFunction($function); 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) { protected function evaluateFunction($function, array $argv) {
foreach ($this->getUsableDatasources() as $source) { foreach ($this->getUsableDatasources() as $source) {
if ($source->canEvaluateFunction($function)) { if ($source->canEvaluateFunction($function)) {

View file

@ -331,6 +331,14 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
} }
/**
* @task functions
*/
protected function evaluateValues(array $values) {
return $values;
}
/** /**
* @task functions * @task functions
*/ */
@ -345,6 +353,8 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
} }
} }
$results = $this->evaluateValues($results);
foreach ($evaluate as $function) { foreach ($evaluate as $function) {
$function = self::parseFunction($function); $function = self::parseFunction($function);
if (!$function) { if (!$function) {