2011-02-14 15:34:20 -08:00
|
|
|
<?php
|
|
|
|
|
2011-09-14 08:02:31 -07:00
|
|
|
/**
|
|
|
|
* @group search
|
|
|
|
*/
|
2012-03-09 15:46:25 -08:00
|
|
|
final class PhabricatorSearchController
|
|
|
|
extends PhabricatorSearchBaseController {
|
|
|
|
|
Use a unique random key to identify queries, not a sequential ID
Summary:
We save search information and then redirect to a "/search/<query_id>/" URI in
order to make search URIs short and bookmarkable, and save query data for
analysis/improvement of search results.
Currently, there's a vague object enumeration security issue with using
sequential IDs to identify searches, where non-admins can see searches other
users have performed. This isn't really too concerning but we lose nothing by
using random keys from a large ID space instead.
- Drop 'authorPHID', which was unused anyway, so searches can not be
personally identified, even by admins.
- Identify searches by random hash keys, not sequential IDs.
- Map old queries' keys to their IDs so we don't break any existing bookmarked
URIs.
Test Plan: Ran several searches, got redirected to URIs with random hashes from
a large ID space rather than sequential integers.
Reviewers: arice, btrahan
Reviewed By: arice
CC: aran, epriestley
Differential Revision: https://secure.phabricator.com/D1587
2012-02-07 14:58:46 -08:00
|
|
|
private $key;
|
2011-02-14 15:34:20 -08:00
|
|
|
|
|
|
|
public function willProcessRequest(array $data) {
|
Use a unique random key to identify queries, not a sequential ID
Summary:
We save search information and then redirect to a "/search/<query_id>/" URI in
order to make search URIs short and bookmarkable, and save query data for
analysis/improvement of search results.
Currently, there's a vague object enumeration security issue with using
sequential IDs to identify searches, where non-admins can see searches other
users have performed. This isn't really too concerning but we lose nothing by
using random keys from a large ID space instead.
- Drop 'authorPHID', which was unused anyway, so searches can not be
personally identified, even by admins.
- Identify searches by random hash keys, not sequential IDs.
- Map old queries' keys to their IDs so we don't break any existing bookmarked
URIs.
Test Plan: Ran several searches, got redirected to URIs with random hashes from
a large ID space rather than sequential integers.
Reviewers: arice, btrahan
Reviewed By: arice
CC: aran, epriestley
Differential Revision: https://secure.phabricator.com/D1587
2012-02-07 14:58:46 -08:00
|
|
|
$this->key = idx($data, 'key');
|
2011-02-14 15:34:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public function processRequest() {
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
Use a unique random key to identify queries, not a sequential ID
Summary:
We save search information and then redirect to a "/search/<query_id>/" URI in
order to make search URIs short and bookmarkable, and save query data for
analysis/improvement of search results.
Currently, there's a vague object enumeration security issue with using
sequential IDs to identify searches, where non-admins can see searches other
users have performed. This isn't really too concerning but we lose nothing by
using random keys from a large ID space instead.
- Drop 'authorPHID', which was unused anyway, so searches can not be
personally identified, even by admins.
- Identify searches by random hash keys, not sequential IDs.
- Map old queries' keys to their IDs so we don't break any existing bookmarked
URIs.
Test Plan: Ran several searches, got redirected to URIs with random hashes from
a large ID space rather than sequential integers.
Reviewers: arice, btrahan
Reviewed By: arice
CC: aran, epriestley
Differential Revision: https://secure.phabricator.com/D1587
2012-02-07 14:58:46 -08:00
|
|
|
if ($this->key) {
|
|
|
|
$query = id(new PhabricatorSearchQuery())->loadOneWhere(
|
|
|
|
'queryKey = %s',
|
|
|
|
$this->key);
|
2011-02-14 15:34:20 -08:00
|
|
|
if (!$query) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$query = new PhabricatorSearchQuery();
|
|
|
|
|
|
|
|
if ($request->isFormPost()) {
|
2012-03-05 19:51:16 -08:00
|
|
|
$query_str = $request->getStr('query');
|
2012-03-14 20:47:17 -07:00
|
|
|
|
|
|
|
$pref_jump = PhabricatorUserPreferences::PREFERENCE_SEARCHBAR_JUMP;
|
2012-03-14 19:07:24 -07:00
|
|
|
if ($request->getStr('jump') != 'no' &&
|
|
|
|
$user && $user->loadPreferences()->getPreference($pref_jump, 1)) {
|
2012-03-14 20:47:17 -07:00
|
|
|
$response = PhabricatorJumpNavHandler::jumpPostResponse($query_str);
|
|
|
|
} else {
|
|
|
|
$response = null;
|
|
|
|
}
|
2012-03-05 19:51:16 -08:00
|
|
|
if ($response) {
|
|
|
|
return $response;
|
2012-02-14 17:00:12 -08:00
|
|
|
} else {
|
2012-03-05 19:51:16 -08:00
|
|
|
$query->setQuery($query_str);
|
|
|
|
|
|
|
|
if ($request->getStr('scope')) {
|
|
|
|
switch ($request->getStr('scope')) {
|
|
|
|
case PhabricatorSearchScope::SCOPE_OPEN_REVISIONS:
|
|
|
|
$query->setParameter('open', 1);
|
|
|
|
$query->setParameter(
|
|
|
|
'type',
|
|
|
|
PhabricatorPHIDConstants::PHID_TYPE_DREV);
|
|
|
|
break;
|
|
|
|
case PhabricatorSearchScope::SCOPE_OPEN_TASKS:
|
|
|
|
$query->setParameter('open', 1);
|
|
|
|
$query->setParameter(
|
|
|
|
'type',
|
|
|
|
PhabricatorPHIDConstants::PHID_TYPE_TASK);
|
|
|
|
break;
|
|
|
|
case PhabricatorSearchScope::SCOPE_WIKI:
|
|
|
|
$query->setParameter(
|
|
|
|
'type',
|
|
|
|
PhabricatorPHIDConstants::PHID_TYPE_WIKI);
|
|
|
|
break;
|
|
|
|
case PhabricatorSearchScope::SCOPE_COMMITS:
|
|
|
|
$query->setParameter(
|
|
|
|
'type',
|
|
|
|
PhabricatorPHIDConstants::PHID_TYPE_CMIT);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (strlen($request->getStr('type'))) {
|
|
|
|
$query->setParameter('type', $request->getStr('type'));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($request->getArr('author')) {
|
|
|
|
$query->setParameter('author', $request->getArr('author'));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($request->getArr('owner')) {
|
|
|
|
$query->setParameter('owner', $request->getArr('owner'));
|
|
|
|
}
|
|
|
|
|
2012-11-06 15:35:26 -08:00
|
|
|
if ($request->getArr('subscribers')) {
|
|
|
|
$query->setParameter('subscribers',
|
|
|
|
$request->getArr('subscribers'));
|
|
|
|
}
|
|
|
|
|
2012-03-05 19:51:16 -08:00
|
|
|
if ($request->getInt('open')) {
|
|
|
|
$query->setParameter('open', $request->getInt('open'));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($request->getArr('project')) {
|
|
|
|
$query->setParameter('project', $request->getArr('project'));
|
|
|
|
}
|
2012-02-14 17:00:12 -08:00
|
|
|
}
|
|
|
|
|
2012-03-05 19:51:16 -08:00
|
|
|
$query->save();
|
|
|
|
return id(new AphrontRedirectResponse())
|
|
|
|
->setURI('/search/'.$query->getQueryKey().'/');
|
2011-02-20 20:37:50 -08:00
|
|
|
}
|
2011-02-14 15:34:20 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-18 17:16:00 -08:00
|
|
|
$options = array(
|
|
|
|
'' => 'All Documents',
|
Improve elasticsearch
Summary: I thought that this will be fun but the elasticsearch API is horrible and the documentation is poor.
Test Plan:
Search for:
- string
- author
- author, owner
- string, author
- open
- string, open, author
- string, exclude
- several authors, several owners
- nothing
- probably all other combinations
Normally, such an exhaustive test plan wouldn't be required but each combination requires a completely different query.
Reviewers: epriestley, jungejason
Reviewed By: epriestley
CC: aran, Koolvin, btrahan
Differential Revision: https://secure.phabricator.com/D2298
2012-04-20 17:09:30 -07:00
|
|
|
) + PhabricatorSearchAbstractDocument::getSupportedTypes();
|
2011-02-18 17:16:00 -08:00
|
|
|
|
|
|
|
$status_options = array(
|
|
|
|
0 => 'Open and Closed Documents',
|
|
|
|
1 => 'Open Documents',
|
|
|
|
);
|
|
|
|
|
|
|
|
$phids = array_merge(
|
2011-02-18 17:36:25 -08:00
|
|
|
$query->getParameter('author', array()),
|
2011-02-20 20:37:50 -08:00
|
|
|
$query->getParameter('owner', array()),
|
2012-11-06 15:35:26 -08:00
|
|
|
$query->getParameter('subscribers', array()),
|
2011-02-20 20:37:50 -08:00
|
|
|
$query->getParameter('project', array())
|
2011-02-18 17:16:00 -08:00
|
|
|
);
|
|
|
|
|
2012-09-04 19:02:56 -07:00
|
|
|
$handles = $this->loadViewerHandles($phids);
|
2011-02-18 17:16:00 -08:00
|
|
|
|
|
|
|
$author_value = array_select_keys(
|
|
|
|
$handles,
|
|
|
|
$query->getParameter('author', array()));
|
|
|
|
$author_value = mpull($author_value, 'getFullName', 'getPHID');
|
2011-02-18 21:48:20 -08:00
|
|
|
|
2011-02-18 17:36:25 -08:00
|
|
|
$owner_value = array_select_keys(
|
|
|
|
$handles,
|
|
|
|
$query->getParameter('owner', array()));
|
|
|
|
$owner_value = mpull($owner_value, 'getFullName', 'getPHID');
|
2011-02-14 15:34:20 -08:00
|
|
|
|
2012-11-06 15:35:26 -08:00
|
|
|
$subscribers_value = array_select_keys(
|
|
|
|
$handles,
|
|
|
|
$query->getParameter('subscribers', array()));
|
|
|
|
$subscribers_value = mpull($subscribers_value, 'getFullName', 'getPHID');
|
|
|
|
|
2011-02-20 20:37:50 -08:00
|
|
|
$project_value = array_select_keys(
|
|
|
|
$handles,
|
|
|
|
$query->getParameter('project', array()));
|
|
|
|
$project_value = mpull($project_value, 'getFullName', 'getPHID');
|
|
|
|
|
2011-02-14 15:34:20 -08:00
|
|
|
$search_form = new AphrontFormView();
|
|
|
|
$search_form
|
|
|
|
->setUser($user)
|
|
|
|
->setAction('/search/')
|
2012-03-14 19:07:24 -07:00
|
|
|
->appendChild(
|
|
|
|
phutil_render_tag(
|
|
|
|
'input',
|
|
|
|
array(
|
|
|
|
'type' => 'hidden',
|
|
|
|
'name' => 'jump',
|
|
|
|
'value' => 'no',
|
|
|
|
)))
|
2011-02-14 15:34:20 -08:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextControl())
|
|
|
|
->setLabel('Search')
|
|
|
|
->setName('query')
|
|
|
|
->setValue($query->getQuery()))
|
2011-02-18 17:16:00 -08:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSelectControl())
|
|
|
|
->setLabel('Document Type')
|
|
|
|
->setName('type')
|
|
|
|
->setOptions($options)
|
|
|
|
->setValue($query->getParameter('type')))
|
2011-02-18 17:36:25 -08:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSelectControl())
|
|
|
|
->setLabel('Document Status')
|
|
|
|
->setName('open')
|
|
|
|
->setOptions($status_options)
|
|
|
|
->setValue($query->getParameter('open')))
|
2011-02-18 17:16:00 -08:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
|
|
|
->setName('author')
|
|
|
|
->setLabel('Author')
|
|
|
|
->setDatasource('/typeahead/common/users/')
|
|
|
|
->setValue($author_value))
|
|
|
|
->appendChild(
|
2011-02-18 17:36:25 -08:00
|
|
|
id(new AphrontFormTokenizerControl())
|
|
|
|
->setName('owner')
|
|
|
|
->setLabel('Owner')
|
2011-02-20 20:37:50 -08:00
|
|
|
->setDatasource('/typeahead/common/searchowner/')
|
|
|
|
->setValue($owner_value)
|
|
|
|
->setCaption(
|
|
|
|
'Tip: search for "Up For Grabs" to find unowned documents.'))
|
2012-11-06 15:35:26 -08:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
|
|
|
->setName('subscribers')
|
|
|
|
->setLabel('Subscribers')
|
|
|
|
->setDatasource('/typeahead/common/users/')
|
|
|
|
->setValue($subscribers_value))
|
2011-02-20 20:37:50 -08:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
|
|
|
->setName('project')
|
|
|
|
->setLabel('Project')
|
|
|
|
->setDatasource('/typeahead/common/projects/')
|
|
|
|
->setValue($project_value))
|
2011-02-14 15:34:20 -08:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSubmitControl())
|
|
|
|
->setValue('Search'));
|
|
|
|
|
|
|
|
$search_panel = new AphrontPanelView();
|
|
|
|
$search_panel->setHeader('Search Phabricator');
|
|
|
|
$search_panel->appendChild($search_form);
|
|
|
|
|
Improve search result listing
Summary:
Make it prettier, paginate, add user pictures, show document types, clean some
stuff up a little. Plenty of room for improvement but this should make it a lot
more useful.
Test Plan:
Here's what the new one looks like:
https://secure.phabricator.com/file/view/PHID-FILE-edce2b83c2e3a121c2b7/
Reviewed By: jungejason
Reviewers: tomo, jungejason, aran, tuomaspelkonen, mroch
Commenters: tomo
CC: aran, tomo, jungejason, epriestley
Differential Revision: 545
2011-06-28 14:35:02 -07:00
|
|
|
require_celerity_resource('phabricator-search-results-css');
|
|
|
|
|
2011-02-14 15:34:20 -08:00
|
|
|
if ($query->getID()) {
|
Improve search result listing
Summary:
Make it prettier, paginate, add user pictures, show document types, clean some
stuff up a little. Plenty of room for improvement but this should make it a lot
more useful.
Test Plan:
Here's what the new one looks like:
https://secure.phabricator.com/file/view/PHID-FILE-edce2b83c2e3a121c2b7/
Reviewed By: jungejason
Reviewers: tomo, jungejason, aran, tuomaspelkonen, mroch
Commenters: tomo
CC: aran, tomo, jungejason, epriestley
Differential Revision: 545
2011-06-28 14:35:02 -07:00
|
|
|
|
|
|
|
$limit = 20;
|
|
|
|
|
|
|
|
$pager = new AphrontPagerView();
|
|
|
|
$pager->setURI($request->getRequestURI(), 'page');
|
|
|
|
$pager->setPageSize($limit);
|
|
|
|
$pager->setOffset($request->getInt('page'));
|
|
|
|
|
|
|
|
$query->setParameter('limit', $limit + 1);
|
|
|
|
$query->setParameter('offset', $pager->getOffset());
|
|
|
|
|
2011-08-07 15:14:23 -07:00
|
|
|
$engine = PhabricatorSearchEngineSelector::newSelector()->newEngine();
|
|
|
|
$results = $engine->executeSearch($query);
|
Improve search result listing
Summary:
Make it prettier, paginate, add user pictures, show document types, clean some
stuff up a little. Plenty of room for improvement but this should make it a lot
more useful.
Test Plan:
Here's what the new one looks like:
https://secure.phabricator.com/file/view/PHID-FILE-edce2b83c2e3a121c2b7/
Reviewed By: jungejason
Reviewers: tomo, jungejason, aran, tuomaspelkonen, mroch
Commenters: tomo
CC: aran, tomo, jungejason, epriestley
Differential Revision: 545
2011-06-28 14:35:02 -07:00
|
|
|
$results = $pager->sliceResults($results);
|
|
|
|
|
2012-04-02 17:29:52 -07:00
|
|
|
if (!$request->getInt('page')) {
|
2012-08-03 11:41:33 -07:00
|
|
|
$jump = PhabricatorPHID::fromObjectName($query->getQuery());
|
2012-04-02 17:29:52 -07:00
|
|
|
if ($jump) {
|
2012-08-03 11:41:33 -07:00
|
|
|
array_unshift($results, $jump);
|
2012-04-02 17:29:52 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Improve search result listing
Summary:
Make it prettier, paginate, add user pictures, show document types, clean some
stuff up a little. Plenty of room for improvement but this should make it a lot
more useful.
Test Plan:
Here's what the new one looks like:
https://secure.phabricator.com/file/view/PHID-FILE-edce2b83c2e3a121c2b7/
Reviewed By: jungejason
Reviewers: tomo, jungejason, aran, tuomaspelkonen, mroch
Commenters: tomo
CC: aran, tomo, jungejason, epriestley
Differential Revision: 545
2011-06-28 14:35:02 -07:00
|
|
|
if ($results) {
|
|
|
|
|
2012-11-06 15:35:26 -08:00
|
|
|
$loader = id(new PhabricatorObjectHandleData($results))
|
|
|
|
->setViewer($user);
|
Improve search result listing
Summary:
Make it prettier, paginate, add user pictures, show document types, clean some
stuff up a little. Plenty of room for improvement but this should make it a lot
more useful.
Test Plan:
Here's what the new one looks like:
https://secure.phabricator.com/file/view/PHID-FILE-edce2b83c2e3a121c2b7/
Reviewed By: jungejason
Reviewers: tomo, jungejason, aran, tuomaspelkonen, mroch
Commenters: tomo
CC: aran, tomo, jungejason, epriestley
Differential Revision: 545
2011-06-28 14:35:02 -07:00
|
|
|
$handles = $loader->loadHandles();
|
|
|
|
$objects = $loader->loadObjects();
|
|
|
|
$results = array();
|
|
|
|
foreach ($handles as $phid => $handle) {
|
2012-11-06 15:35:26 -08:00
|
|
|
$view = id(new PhabricatorSearchResultView())
|
|
|
|
->setHandle($handle)
|
|
|
|
->setQuery($query)
|
|
|
|
->setObject(idx($objects, $phid));
|
Improve search result listing
Summary:
Make it prettier, paginate, add user pictures, show document types, clean some
stuff up a little. Plenty of room for improvement but this should make it a lot
more useful.
Test Plan:
Here's what the new one looks like:
https://secure.phabricator.com/file/view/PHID-FILE-edce2b83c2e3a121c2b7/
Reviewed By: jungejason
Reviewers: tomo, jungejason, aran, tuomaspelkonen, mroch
Commenters: tomo
CC: aran, tomo, jungejason, epriestley
Differential Revision: 545
2011-06-28 14:35:02 -07:00
|
|
|
$results[] = $view->render();
|
|
|
|
}
|
|
|
|
$results =
|
|
|
|
'<div class="phabricator-search-result-list">'.
|
|
|
|
implode("\n", $results).
|
|
|
|
'<div class="search-results-pager">'.
|
|
|
|
$pager->render().
|
|
|
|
'</div>'.
|
|
|
|
'</div>';
|
|
|
|
} else {
|
|
|
|
$results =
|
|
|
|
'<div class="phabricator-search-result-list">'.
|
|
|
|
'<p class="phabricator-search-no-results">No search results.</p>'.
|
|
|
|
'</div>';
|
2011-02-14 15:34:20 -08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$results = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->buildStandardPageResponse(
|
|
|
|
array(
|
|
|
|
$search_panel,
|
|
|
|
$results,
|
|
|
|
),
|
|
|
|
array(
|
2012-02-14 17:00:12 -08:00
|
|
|
'title' => 'Search Results',
|
2011-02-14 15:34:20 -08:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2011-03-22 17:41:55 -07:00
|
|
|
|
2011-02-14 15:34:20 -08:00
|
|
|
}
|