From c246aa26382a32c36d46001853b903062be2fcf7 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 18 Apr 2015 09:15:09 -0700 Subject: [PATCH] Implement a projects() datasource function Summary: Ref T4100. This is like members(), but is implemented on top of the raw datasource. This is a lot simpler and involves way less code duplication. I'll go back and implement members() like this, too. Nothing actually uses this yet. Test Plan: - Used browse view to browse datasource. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T4100 Differential Revision: https://secure.phabricator.com/D12458 --- src/__phutil_library_map__.php | 2 + .../PhabricatorUserProjectsDatasource.php | 85 +++++++++++++++++++ ...habricatorTypeaheadCompositeDatasource.php | 18 +++- .../PhabricatorTypeaheadDatasource.php | 15 +++- .../view/PhabricatorTypeaheadTokenView.php | 4 + 5 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 src/applications/people/typeahead/PhabricatorUserProjectsDatasource.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 675bcad0dd..84c732b486 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2664,6 +2664,7 @@ phutil_register_library_map(array( 'PhabricatorUserPreferences' => 'applications/settings/storage/PhabricatorUserPreferences.php', 'PhabricatorUserProfile' => 'applications/people/storage/PhabricatorUserProfile.php', 'PhabricatorUserProfileEditor' => 'applications/people/editor/PhabricatorUserProfileEditor.php', + 'PhabricatorUserProjectsDatasource' => 'applications/people/typeahead/PhabricatorUserProjectsDatasource.php', 'PhabricatorUserRealNameField' => 'applications/people/customfield/PhabricatorUserRealNameField.php', 'PhabricatorUserRolesField' => 'applications/people/customfield/PhabricatorUserRolesField.php', 'PhabricatorUserSchemaSpec' => 'applications/people/storage/PhabricatorUserSchemaSpec.php', @@ -6083,6 +6084,7 @@ phutil_register_library_map(array( 'PhabricatorUserPreferences' => 'PhabricatorUserDAO', 'PhabricatorUserProfile' => 'PhabricatorUserDAO', 'PhabricatorUserProfileEditor' => 'PhabricatorApplicationTransactionEditor', + 'PhabricatorUserProjectsDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'PhabricatorUserRealNameField' => 'PhabricatorUserCustomField', 'PhabricatorUserRolesField' => 'PhabricatorUserCustomField', 'PhabricatorUserSchemaSpec' => 'PhabricatorConfigSchemaSpec', diff --git a/src/applications/people/typeahead/PhabricatorUserProjectsDatasource.php b/src/applications/people/typeahead/PhabricatorUserProjectsDatasource.php new file mode 100644 index 0000000000..02a080c58e --- /dev/null +++ b/src/applications/people/typeahead/PhabricatorUserProjectsDatasource.php @@ -0,0 +1,85 @@ +)...'); + } + + public function getDatasourceApplicationClass() { + return 'PhabricatorProjectApplication'; + } + + public function getComponentDatasources() { + return array( + new PhabricatorPeopleDatasource(), + ); + } + + public function getDatasourceFunctions() { + return array( + 'projects' => array( + 'name' => pht("Find results in any of a user's projects."), + ), + ); + } + + protected function didLoadResults(array $results) { + foreach ($results as $result) { + $result + ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) + ->setIcon('fa-briefcase') + ->setPHID('projects('.$result->getPHID().')') + ->setDisplayName(pht('Projects: %s', $result->getDisplayName())) + ->setName($result->getName().' projects'); + } + + return $results; + } + + protected function evaluateFunction($function, array $argv_list) { + $phids = array(); + foreach ($argv_list as $argv) { + $phids[] = head($argv); + } + + $projects = id(new PhabricatorPeopleQuery()) + ->setViewer($this->getViewer()) + ->needMembers(true) + ->withPHIDs($phids) + ->execute(); + + $results = array(); + foreach ($projects as $project) { + foreach ($project->getMemberPHIDs() as $phid) { + $results[$phid] = $phid; + } + } + + return array_values($results); + } + + public function renderFunctionTokens($function, array $argv_list) { + $phids = array(); + foreach ($argv_list as $argv) { + $phids[] = head($argv); + } + + $tokens = $this->renderTokens($phids); + foreach ($tokens as $token) { + if ($token->isInvalid()) { + $token + ->setValue(pht('Projects: Invalid User')); + } else { + $token + ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) + ->setKey('projects('.$token->getKey().')') + ->setValue(pht('Projects: %s', $token->getValue())); + } + } + + return $tokens; + } + +} diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php index 0c25e731bc..08424611bd 100644 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php @@ -25,10 +25,22 @@ abstract class PhabricatorTypeaheadCompositeDatasource $offset = $this->getOffset(); $limit = $this->getLimit(); + // If the input query is a function like `members(platy`, and we can + // parse the function, we strip the function off and hand the stripped + // query to child sources. This makes it easier to implement function + // sources in terms of real object sources. + $raw_query = $this->getRawQuery(); + if (self::isFunctionToken($raw_query)) { + $function = $this->parseFunction($raw_query, $allow_partial = true); + if ($function) { + $raw_query = head($function['argv']); + } + } + $results = array(); foreach ($this->getUsableDatasources() as $source) { $source - ->setRawQuery($this->getRawQuery()) + ->setRawQuery($raw_query) ->setQuery($this->getQuery()) ->setViewer($this->getViewer()); @@ -36,7 +48,9 @@ abstract class PhabricatorTypeaheadCompositeDatasource $source->setLimit($offset + $limit); } - $results[] = $source->loadResults(); + $source_results = $source->loadResults(); + $source_results = $source->didLoadResults($source_results); + $results[] = $source_results; } $results = array_mergev($results); diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php index 2742f68409..73e1c429cf 100644 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php @@ -90,6 +90,10 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject { abstract public function getDatasourceApplicationClass(); abstract public function loadResults(); + protected function didLoadResults(array $results) { + return $results; + } + public static function tokenizeString($string) { $string = phutil_utf8_strtolower($string); $string = trim($string); @@ -258,11 +262,20 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject { /* -( Token Functions )---------------------------------------------------- */ + /** + * @task functions + */ + public function getDatasourceFunctions() { + return array(); + } + + /** * @task functions */ protected function canEvaluateFunction($function) { - return false; + $functions = $this->getDatasourceFunctions(); + return isset($functions[$function]); } diff --git a/src/applications/typeahead/view/PhabricatorTypeaheadTokenView.php b/src/applications/typeahead/view/PhabricatorTypeaheadTokenView.php index 425de130fb..d08b1b406d 100644 --- a/src/applications/typeahead/view/PhabricatorTypeaheadTokenView.php +++ b/src/applications/typeahead/view/PhabricatorTypeaheadTokenView.php @@ -44,6 +44,10 @@ final class PhabricatorTypeaheadTokenView return $token; } + public function isInvalid() { + return ($this->getTokenType() == self::TYPE_INVALID); + } + public function setKey($key) { $this->key = $key; return $this;