1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-02 18:08:26 +01:00

Allow construction of ApplicationSearch queries with GET

Summary:
Ref T3775 (discussion here). Ref T2625.

T3775 presents two problems:

  # Existing tools which linked to `/differential/active/epriestley/` (that is, put a username in the URL) can't generate search links now.
  # Humans can't edit the URL anymore, either.

I think (1) is an actual issue, and this fixes it. I think (2) is pretty fluff, and this doesn't really try to fix it, although it probably improves it.

The fix for (1) is:

  - Provide a helper to read a parameter containing either a list of user PHIDs or a list of usernames, so `/?users[]=PHID-USER-xyz` (from a tokenizer) and `/?users=alincoln,htaft` (from an external program) are equivalent inputs.
  - Rename all the form parameters to be more digestable (`authorPHIDs` -> `authors`). Almost all of them were in this form already anyway. This just gives us `?users=alincoln` instead of `userPHIDs=alincoln`.
  - Inside ApplicationSearch, if a request has no query associated with it but does have query parameters, build a query from the request instead of issuing the user's default query. Basically, this means that `/differential/` runs the default query, while `/differential/?users=x` runs a custom query.

Test Plan: {F56612}

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2625, T3775

Differential Revision: https://secure.phabricator.com/D6840
This commit is contained in:
epriestley 2013-08-29 11:52:29 -07:00
parent 5a11f08ba4
commit f1c75a6382
16 changed files with 98 additions and 27 deletions

View file

@ -168,6 +168,10 @@ final class AphrontRequest {
(idx($_FILES[$name], 'error') !== UPLOAD_ERR_NO_FILE); (idx($_FILES[$name], 'error') !== UPLOAD_ERR_NO_FILE);
} }
final public function isHTTPGet() {
return ($_SERVER['REQUEST_METHOD'] == 'GET');
}
final public function isHTTPPost() { final public function isHTTPPost() {
return ($_SERVER['REQUEST_METHOD'] == 'POST'); return ($_SERVER['REQUEST_METHOD'] == 'POST');
} }
@ -416,7 +420,7 @@ final class AphrontRequest {
// Remove magic parameters like __dialog__ and __ajax__. // Remove magic parameters like __dialog__ and __ajax__.
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
if (strncmp($key, '__', 2)) { if (!strncmp($key, '__', 2)) {
unset($data[$key]); unset($data[$key]);
} }
} }

View file

@ -7,7 +7,7 @@ final class PhabricatorCountdownSearchEngine
$saved = new PhabricatorSavedQuery(); $saved = new PhabricatorSavedQuery();
$saved->setParameter( $saved->setParameter(
'authorPHIDs', 'authorPHIDs',
array_values($request->getArr('authors'))); $this->readUsersFromRequest($request, 'authors'));
$saved->setParameter('upcoming', $request->getBool('upcoming')); $saved->setParameter('upcoming', $request->getBool('upcoming'));

View file

@ -15,19 +15,19 @@ final class DifferentialRevisionSearchEngine
$saved->setParameter( $saved->setParameter(
'responsiblePHIDs', 'responsiblePHIDs',
$request->getArr('responsiblePHIDs')); $this->readUsersFromRequest($request, 'responsibles'));
$saved->setParameter( $saved->setParameter(
'authorPHIDs', 'authorPHIDs',
$request->getArr('authorPHIDs')); $this->readUsersFromRequest($request, 'authors'));
$saved->setParameter( $saved->setParameter(
'reviewerPHIDs', 'reviewerPHIDs',
$request->getArr('reviewerPHIDs')); $this->readUsersFromRequest($request, 'reviewers'));
$saved->setParameter( $saved->setParameter(
'subscriberPHIDs', 'subscriberPHIDs',
$request->getArr('subscriberPHIDs')); $this->readUsersFromRequest($request, 'subscribers'));
$saved->setParameter( $saved->setParameter(
'draft', 'draft',
@ -115,25 +115,25 @@ final class DifferentialRevisionSearchEngine
->appendChild( ->appendChild(
id(new AphrontFormTokenizerControl()) id(new AphrontFormTokenizerControl())
->setLabel(pht('Responsible Users')) ->setLabel(pht('Responsible Users'))
->setName('responsiblePHIDs') ->setName('responsibles')
->setDatasource('/typeahead/common/accounts/') ->setDatasource('/typeahead/common/accounts/')
->setValue(array_select_keys($tokens, $responsible_phids))) ->setValue(array_select_keys($tokens, $responsible_phids)))
->appendChild( ->appendChild(
id(new AphrontFormTokenizerControl()) id(new AphrontFormTokenizerControl())
->setLabel(pht('Authors')) ->setLabel(pht('Authors'))
->setName('authorPHIDs') ->setName('authors')
->setDatasource('/typeahead/common/accounts/') ->setDatasource('/typeahead/common/accounts/')
->setValue(array_select_keys($tokens, $author_phids))) ->setValue(array_select_keys($tokens, $author_phids)))
->appendChild( ->appendChild(
id(new AphrontFormTokenizerControl()) id(new AphrontFormTokenizerControl())
->setLabel(pht('Reviewers')) ->setLabel(pht('Reviewers'))
->setName('reviewerPHIDs') ->setName('reviewers')
->setDatasource('/typeahead/common/accounts/') ->setDatasource('/typeahead/common/accounts/')
->setValue(array_select_keys($tokens, $reviewer_phids))) ->setValue(array_select_keys($tokens, $reviewer_phids)))
->appendChild( ->appendChild(
id(new AphrontFormTokenizerControl()) id(new AphrontFormTokenizerControl())
->setLabel(pht('Subscribers')) ->setLabel(pht('Subscribers'))
->setName('subscriberPHIDs') ->setName('subscribers')
->setDatasource('/typeahead/common/allmailable/') ->setDatasource('/typeahead/common/allmailable/')
->setValue(array_select_keys($tokens, $subscriber_phids))) ->setValue(array_select_keys($tokens, $subscriber_phids)))
->appendChild( ->appendChild(

View file

@ -8,7 +8,7 @@ final class PhabricatorFeedSearchEngine
$saved->setParameter( $saved->setParameter(
'userPHIDs', 'userPHIDs',
array_values($request->getArr('userPHIDs'))); $this->readUsersFromRequest($request, 'users'));
$saved->setParameter( $saved->setParameter(
'projectPHIDs', 'projectPHIDs',
@ -76,7 +76,7 @@ final class PhabricatorFeedSearchEngine
->appendChild( ->appendChild(
id(new AphrontFormTokenizerControl()) id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/') ->setDatasource('/typeahead/common/users/')
->setName('userPHIDs') ->setName('users')
->setLabel(pht('Include Users')) ->setLabel(pht('Include Users'))
->setValue($user_tokens)) ->setValue($user_tokens))
->appendChild( ->appendChild(

View file

@ -7,7 +7,7 @@ final class PhabricatorFileSearchEngine
$saved = new PhabricatorSavedQuery(); $saved = new PhabricatorSavedQuery();
$saved->setParameter( $saved->setParameter(
'authorPHIDs', 'authorPHIDs',
array_values($request->getArr('authors'))); $this->readUsersFromRequest($request, 'authors'));
$saved->setParameter('explicit', $request->getBool('explicit')); $saved->setParameter('explicit', $request->getBool('explicit'));
$saved->setParameter('createdStart', $request->getStr('createdStart')); $saved->setParameter('createdStart', $request->getStr('createdStart'));

View file

@ -8,7 +8,7 @@ final class HeraldRuleSearchEngine
$saved->setParameter( $saved->setParameter(
'authorPHIDs', 'authorPHIDs',
array_values($request->getArr('authors'))); $this->readUsersFromRequest($request, 'authors'));
$saved->setParameter('contentType', $request->getStr('contentType')); $saved->setParameter('contentType', $request->getStr('contentType'));
$saved->setParameter('ruleType', $request->getStr('ruleType')); $saved->setParameter('ruleType', $request->getStr('ruleType'));

View file

@ -10,11 +10,11 @@ final class LegalpadDocumentSearchEngine
$saved = new PhabricatorSavedQuery(); $saved = new PhabricatorSavedQuery();
$saved->setParameter( $saved->setParameter(
'creatorPHIDs', 'creatorPHIDs',
array_values($request->getArr('creators'))); $this->readUsersFromRequest($request, 'creators'));
$saved->setParameter( $saved->setParameter(
'contributorPHIDs', 'contributorPHIDs',
array_values($request->getArr('contributors'))); $this->readUsersFromRequest($request, 'contributors'));
$saved->setParameter('createdStart', $request->getStr('createdStart')); $saved->setParameter('createdStart', $request->getStr('createdStart'));
$saved->setParameter('createdEnd', $request->getStr('createdEnd')); $saved->setParameter('createdEnd', $request->getStr('createdEnd'));

View file

@ -7,7 +7,7 @@ final class PhabricatorMacroSearchEngine
$saved = new PhabricatorSavedQuery(); $saved = new PhabricatorSavedQuery();
$saved->setParameter( $saved->setParameter(
'authorPHIDs', 'authorPHIDs',
array_values($request->getArr('authors'))); $this->readUsersFromRequest($request, 'authors'));
$saved->setParameter('status', $request->getStr('status')); $saved->setParameter('status', $request->getStr('status'));
$saved->setParameter('names', $request->getStrList('names')); $saved->setParameter('names', $request->getStrList('names'));

View file

@ -10,7 +10,7 @@ final class PhabricatorPasteSearchEngine
$saved = new PhabricatorSavedQuery(); $saved = new PhabricatorSavedQuery();
$saved->setParameter( $saved->setParameter(
'authorPHIDs', 'authorPHIDs',
array_values($request->getArr('authors'))); $this->readUsersFromRequest($request, 'authors'));
$languages = $request->getStrList('languages'); $languages = $request->getStrList('languages');
if ($request->getBool('noLanguage')) { if ($request->getBool('noLanguage')) {

View file

@ -7,7 +7,7 @@ final class PholioMockSearchEngine
$saved = new PhabricatorSavedQuery(); $saved = new PhabricatorSavedQuery();
$saved->setParameter( $saved->setParameter(
'authorPHIDs', 'authorPHIDs',
array_values($request->getArr('authors'))); $this->readUsersFromRequest($request, 'authors'));
return $saved; return $saved;
} }

View file

@ -8,11 +8,11 @@ final class PonderQuestionSearchEngine
$saved->setParameter( $saved->setParameter(
'authorPHIDs', 'authorPHIDs',
array_values($request->getArr('authors'))); $this->readUsersFromRequest($request, 'authors'));
$saved->setParameter( $saved->setParameter(
'answererPHIDs', 'answererPHIDs',
array_values($request->getArr('answerers'))); $this->readUsersFromRequest($request, 'answerers'));
$saved->setParameter('status', $request->getStr('status')); $saved->setParameter('status', $request->getStr('status'));

View file

@ -6,7 +6,9 @@ final class PhabricatorProjectSearchEngine
public function buildSavedQueryFromRequest(AphrontRequest $request) { public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery(); $saved = new PhabricatorSavedQuery();
$saved->setParameter('memberPHIDs', $request->getArr('memberPHIDs')); $saved->setParameter(
'memberPHIDs',
$this->readUsersFromRequest($request, 'members'));
$saved->setParameter('status', $request->getStr('status')); $saved->setParameter('status', $request->getStr('status'));
return $saved; return $saved;
@ -45,7 +47,7 @@ final class PhabricatorProjectSearchEngine
->appendChild( ->appendChild(
id(new AphrontFormTokenizerControl()) id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/') ->setDatasource('/typeahead/common/users/')
->setName('memberPHIDs') ->setName('members')
->setLabel(pht('Members')) ->setLabel(pht('Members'))
->setValue($member_tokens)) ->setValue($member_tokens))
->appendChild( ->appendChild(

View file

@ -25,7 +25,9 @@ final class ReleephRequestSearchEngine
$saved->setParameter('status', $request->getStr('status')); $saved->setParameter('status', $request->getStr('status'));
$saved->setParameter('severity', $request->getStr('severity')); $saved->setParameter('severity', $request->getStr('severity'));
$saved->setParameter('requestorPHIDs', $request->getArr('requestorPHIDs')); $saved->setParameter(
'requestorPHIDs',
$this->readUsersFromRequest($request, 'requestors'));
return $saved; return $saved;
} }
@ -79,7 +81,7 @@ final class ReleephRequestSearchEngine
->appendChild( ->appendChild(
id(new AphrontFormTokenizerControl()) id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/') ->setDatasource('/typeahead/common/users/')
->setName('requestorPHIDs') ->setName('requestors')
->setLabel(pht('Requestors')) ->setLabel(pht('Requestors'))
->setValue($requestor_tokens)); ->setValue($requestor_tokens));
} }

View file

@ -107,7 +107,15 @@ final class PhabricatorApplicationSearchController
$run_query = false; $run_query = false;
$query_key = $request->getStr('query'); $query_key = $request->getStr('query');
} else if (!strlen($this->queryKey)) { } else if (!strlen($this->queryKey)) {
$query_key = head_key($engine->loadEnabledNamedQueries()); if ($request->isHTTPGet() && $request->getPassthroughRequestData()) {
// If this is a GET request and it has some query data, don't
// do anything. We'll build and execute a query from it below.
// This allows external tools to build URIs like "/query/?users=a,b".
} else {
// Otherwise, there's no query data so just run the user's default
// query for this application.
$query_key = head_key($engine->loadEnabledNamedQueries());
}
} }
if ($engine->isBuiltinQuery($query_key)) { if ($engine->isBuiltinQuery($query_key)) {

View file

@ -7,6 +7,7 @@
* @task builtin Builtin Queries * @task builtin Builtin Queries
* @task uri Query URIs * @task uri Query URIs
* @task dates Date Filters * @task dates Date Filters
* @task read Reading Utilities
* *
* @group search * @group search
*/ */
@ -234,6 +235,60 @@ abstract class PhabricatorApplicationSearchEngine {
} }
/* -( Reading Utilities )--------------------------------------------------- */
/**
* Read a list of user PHIDs from a request in a flexible way. This method
* supports either of these forms:
*
* users[]=alincoln&users[]=htaft
* users=alincoln,htaft
*
* Additionally, users can be specified either by PHID or by name.
*
* The main goal of this flexibility is to allow external programs to generate
* links to pages (like "alincoln's open revisions") without needing to make
* API calls.
*
* @param AphrontRequest Request to read user PHIDs from.
* @param string Key to read in the request.
* @return list<phid> List of user PHIDs.
*
* @task read
*/
protected function readUsersFromRequest(AphrontRequest $request, $key) {
$list = $request->getArr($key, null);
if ($list === null) {
$list = $request->getStrList($key);
}
$phids = array();
$names = array();
$user_type = PhabricatorPHIDConstants::PHID_TYPE_USER;
foreach ($list as $item) {
if (phid_get_type($item) == $user_type) {
$phids[] = $item;
} else {
$names[] = $item;
}
}
if ($names) {
$users = id(new PhabricatorPeopleQuery())
->setViewer($this->requireViewer())
->withUsernames($names)
->execute();
foreach ($users as $user) {
$phids[] = $user->getPHID();
}
$phids = array_unique($phids);
}
return $phids;
}
/* -( Dates )-------------------------------------------------------------- */ /* -( Dates )-------------------------------------------------------------- */

View file

@ -7,7 +7,7 @@ final class PhabricatorSlowvoteSearchEngine
$saved = new PhabricatorSavedQuery(); $saved = new PhabricatorSavedQuery();
$saved->setParameter( $saved->setParameter(
'authorPHIDs', 'authorPHIDs',
array_values($request->getArr('authors'))); $this->readUsersFromRequest($request, 'authors'));
$saved->setParameter('voted', $request->getBool('voted')); $saved->setParameter('voted', $request->getBool('voted'));