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 @@ -40,16 +40,15 @@ $saved->setParameter( 'subscriberPHIDs', - $this->readUsersFromRequest($request, 'subscribers')); + $this->readSubscribersFromRequest($request, 'subscribers')); $saved->setParameter( 'repositoryPHIDs', $request->getArr('repositories')); - // TODO: Implement "readProjectsFromRequest(...)" to improve this. $saved->setParameter( 'projects', - $this->readListFromRequest($request, 'projects')); + $this->readProjectsFromRequest($request, 'projects')); $saved->setParameter( 'draft', diff --git a/src/applications/phid/PhabricatorPHIDConstants.php b/src/applications/phid/PhabricatorPHIDConstants.php --- a/src/applications/phid/PhabricatorPHIDConstants.php +++ b/src/applications/phid/PhabricatorPHIDConstants.php @@ -2,7 +2,6 @@ final class PhabricatorPHIDConstants { - const PHID_TYPE_USER = 'USER'; const PHID_TYPE_UNKNOWN = '????'; const PHID_TYPE_MAGIC = '!!!!'; const PHID_TYPE_STRY = 'STRY'; diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php --- a/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php @@ -44,6 +44,8 @@ $phids[] = head($argv); } + $phids = $this->resolvePHIDs($phids); + $projects = id(new PhabricatorProjectQuery()) ->setViewer($this->getViewer()) ->withMemberPHIDs($phids) @@ -65,6 +67,8 @@ $phids[] = head($argv); } + $phids = $this->resolvePHIDs($phids); + $tokens = $this->renderTokens($phids); foreach ($tokens as $token) { if ($token->isInvalid()) { @@ -82,4 +86,38 @@ return $tokens; } + private function resolvePHIDs(array $phids) { + // If we have a function like `projects(alincoln)`, try to resolve the + // username first. This won't happen normally, but can be passed in from + // the query string. + + // The user might also give us an invalid username. In this case, we + // preserve it and return it in-place so we get an "invalid" token rendered + // in the UI. This shows the user where the issue is and best represents + // the user's input. + + $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/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 @@ -378,8 +378,7 @@ * @param AphrontRequest Request to read user PHIDs from. * @param string Key to read in the request. * @param list Other permitted PHID types. - * @return list List of user PHIDs. - * + * @return list List of user PHIDs and selector functions. * @task read */ protected function readUsersFromRequest( @@ -392,7 +391,7 @@ $phids = array(); $names = array(); $allow_types = array_fuse($allow_types); - $user_type = PhabricatorPHIDConstants::PHID_TYPE_USER; + $user_type = PhabricatorPeopleUserPHIDType::TYPECONST; foreach ($list as $item) { $type = phid_get_type($item); if ($type == $user_type) { @@ -426,6 +425,71 @@ /** + * Read a list of project PHIDs from a request in a flexible way. + * + * @param AphrontRequest Request to read user PHIDs from. + * @param string Key to read in the request. + * @return list List of projet PHIDs and selector functions. + * @task read + */ + protected function readProjectsFromRequest(AphrontRequest $request, $key) { + $list = $this->readListFromRequest($request, $key); + + $phids = array(); + $slugs = array(); + $project_type = PhabricatorProjectProjectPHIDType::TYPECONST; + foreach ($list as $item) { + $type = phid_get_type($item); + if ($type == $project_type) { + $phids[] = $item; + } else { + if (PhabricatorTypeaheadDatasource::isFunctionToken($item)) { + // If this is a function, pass it through unchanged; we'll evaluate + // it later. + $phids[] = $item; + } else { + $slugs[] = $item; + } + } + } + + if ($slugs) { + $projects = id(new PhabricatorProjectQuery()) + ->setViewer($this->requireViewer()) + ->withSlugs($slugs) + ->execute(); + foreach ($projects as $project) { + $phids[] = $project->getPHID(); + } + $phids = array_unique($phids); + } + + return $phids; + } + + + /** + * Read a list of subscribers from a request in a flexible way. + * + * @param AphrontRequest Request to read PHIDs from. + * @param string Key to read in the request. + * @return list List of object PHIDs. + * @task read + */ + protected function readSubscribersFromRequest( + AphrontRequest $request, + $key) { + return $this->readUsersFromRequest( + $request, + $key, + array( + PhabricatorProjectProjectPHIDType::TYPECONST, + PhabricatorMailingListListPHIDType::TYPECONST, + )); + } + + + /** * Read a list of generic PHIDs from a request in a flexible way. Like * @{method:readUsersFromRequest}, this method supports either array or * comma-delimited forms. Objects can be specified either by PHID or by