diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -435,6 +435,7 @@ '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 @@ '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 @@ 'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher', 'DifferentialDraft' => 'DifferentialDAO', 'DifferentialEditPolicyField' => 'DifferentialCoreCustomField', + 'DifferentialExactUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'DifferentialFieldParseException' => 'Exception', 'DifferentialFieldValidationException' => 'Exception', 'DifferentialFindConduitAPIMethod' => 'DifferentialConduitAPIMethod', @@ -4705,6 +4709,8 @@ 'DifferentialRepositoryField' => 'DifferentialCoreCustomField', 'DifferentialRepositoryLookup' => 'Phobject', 'DifferentialRequiredSignaturesField' => 'DifferentialCoreCustomField', + 'DifferentialResponsibleDatasource' => 'PhabricatorTypeaheadCompositeDatasource', + 'DifferentialResponsibleUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'DifferentialRevertPlanField' => 'DifferentialStoredCustomField', 'DifferentialReviewedByField' => 'DifferentialCoreCustomField', 'DifferentialReviewer' => 'Phobject', diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php --- a/src/applications/differential/query/DifferentialRevisionQuery.php +++ b/src/applications/differential/query/DifferentialRevisionQuery.php @@ -501,39 +501,17 @@ $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. diff --git a/src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php b/src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php --- a/src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php +++ b/src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php @@ -17,7 +17,7 @@ $this->objects = $objects; - $phids = $query->getParameter('responsiblePHIDs', array()); + $phids = $query->getEvaluatedParameter('responsiblePHIDs', array()); if (!$phids) { throw new Exception( pht( diff --git a/src/applications/differential/query/DifferentialRevisionSearchEngine.php b/src/applications/differential/query/DifferentialRevisionSearchEngine.php --- a/src/applications/differential/query/DifferentialRevisionSearchEngine.php +++ b/src/applications/differential/query/DifferentialRevisionSearchEngine.php @@ -51,10 +51,11 @@ 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 @@ ->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()) diff --git a/src/applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php b/src/applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php new file mode 100644 --- /dev/null +++ b/src/applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php @@ -0,0 +1,115 @@ +)...'); + } + + 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; + } + +} diff --git a/src/applications/differential/typeahead/DifferentialResponsibleDatasource.php b/src/applications/differential/typeahead/DifferentialResponsibleDatasource.php new file mode 100644 --- /dev/null +++ b/src/applications/differential/typeahead/DifferentialResponsibleDatasource.php @@ -0,0 +1,27 @@ +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; + } + +} diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -143,8 +143,8 @@ * @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 @@ $map[$field->getKey()] = $value; } + $original->attachParameterMap($map); $query = $this->buildQueryFromParameters($map); $object = $this->newResultObject(); diff --git a/src/applications/search/storage/PhabricatorSavedQuery.php b/src/applications/search/storage/PhabricatorSavedQuery.php --- a/src/applications/search/storage/PhabricatorSavedQuery.php +++ b/src/applications/search/storage/PhabricatorSavedQuery.php @@ -7,6 +7,8 @@ protected $queryKey; protected $engineClassName; + private $parameterMap = self::ATTACHABLE; + protected function getConfiguration() { return array( self::CONFIG_SERIALIZATION => array( @@ -52,6 +54,15 @@ 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 )----------------------------------------- */ diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php @@ -142,6 +142,14 @@ 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)) { diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php @@ -334,6 +334,14 @@ /** * @task functions */ + protected function evaluateValues(array $values) { + return $values; + } + + + /** + * @task functions + */ public function evaluateTokens(array $tokens) { $results = array(); $evaluate = array(); @@ -345,6 +353,8 @@ } } + $results = $this->evaluateValues($results); + foreach ($evaluate as $function) { $function = self::parseFunction($function); if (!$function) {