Page MenuHomePhabricator

D8123.id18371.diff
No OneTemporary

D8123.id18371.diff

diff --git a/src/applications/search/application/PhabricatorApplicationSearch.php b/src/applications/search/application/PhabricatorApplicationSearch.php
--- a/src/applications/search/application/PhabricatorApplicationSearch.php
+++ b/src/applications/search/application/PhabricatorApplicationSearch.php
@@ -29,8 +29,7 @@
public function getRoutes() {
return array(
'/search/' => array(
- '' => 'PhabricatorSearchController',
- '(?P<key>[^/]+)/' => 'PhabricatorSearchController',
+ '(?:query/(?P<queryKey>[^/]+)/)?' => 'PhabricatorSearchController',
'attach/(?P<phid>[^/]+)/(?P<type>\w+)/(?:(?P<action>\w+)/)?'
=> 'PhabricatorSearchAttachController',
'select/(?P<type>\w+)/'
diff --git a/src/applications/search/controller/PhabricatorSearchController.php b/src/applications/search/controller/PhabricatorSearchController.php
--- a/src/applications/search/controller/PhabricatorSearchController.php
+++ b/src/applications/search/controller/PhabricatorSearchController.php
@@ -1,298 +1,98 @@
<?php
-/**
- * @group search
- */
final class PhabricatorSearchController
- extends PhabricatorSearchBaseController {
+ extends PhabricatorSearchBaseController
+ implements PhabricatorApplicationSearchResultsControllerInterface {
- private $key;
+ private $queryKey;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
- $this->key = idx($data, 'key');
+ $this->queryKey = idx($data, 'queryKey');
}
public function processRequest() {
$request = $this->getRequest();
- $user = $request->getUser();
-
- if ($this->key) {
- $query = id(new PhabricatorSavedQuery())->loadOneWhere(
- 'queryKey = %s',
- $this->key);
- if (!$query) {
- return new Aphront404Response();
- }
- } else {
- $query = id(new PhabricatorSavedQuery())
- ->setEngineClassName('PhabricatorSearchApplicationSearchEngine');
-
- if ($request->isFormPost()) {
- $query_str = $request->getStr('query');
-
- $pref_jump = PhabricatorUserPreferences::PREFERENCE_SEARCHBAR_JUMP;
- if ($request->getStr('jump') != 'no' &&
- $user && $user->loadPreferences()->getPreference($pref_jump, 1)) {
- $response = PhabricatorJumpNavHandler::getJumpResponse(
- $user,
- $query_str);
- } else {
- $response = null;
- }
+ $viewer = $request->getUser();
+
+ if ($request->getStr('jump') != 'no') {
+ $pref_jump = PhabricatorUserPreferences::PREFERENCE_SEARCHBAR_JUMP;
+ if ($viewer->loadPreferences($pref_jump, 1)) {
+ $response = PhabricatorJumpNavHandler::getJumpResponse(
+ $viewer,
+ $request->getStr('query'));
if ($response) {
return $response;
- } else {
- $query->setParameter('query', $query_str);
-
- if ($request->getStr('scope')) {
- switch ($request->getStr('scope')) {
- case PhabricatorSearchScope::SCOPE_OPEN_REVISIONS:
- $query->setParameter('open', 1);
- $query->setParameter(
- 'type',
- DifferentialPHIDTypeRevision::TYPECONST);
- break;
- case PhabricatorSearchScope::SCOPE_OPEN_TASKS:
- $query->setParameter('open', 1);
- $query->setParameter(
- 'type',
- ManiphestPHIDTypeTask::TYPECONST);
- break;
- case PhabricatorSearchScope::SCOPE_WIKI:
- $query->setParameter(
- 'type',
- PhrictionPHIDTypeDocument::TYPECONST);
- break;
- case PhabricatorSearchScope::SCOPE_COMMITS:
- $query->setParameter(
- 'type',
- PhabricatorRepositoryPHIDTypeCommit::TYPECONST);
- 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'));
- }
-
- if ($request->getArr('subscribers')) {
- $query->setParameter('subscribers',
- $request->getArr('subscribers'));
- }
-
- if ($request->getInt('open')) {
- $query->setParameter('open', $request->getInt('open'));
- }
-
- if ($request->getArr('project')) {
- $query->setParameter('project', $request->getArr('project'));
- }
- }
-
- try {
- $query->save();
- } catch (AphrontQueryDuplicateKeyException $ex) {
- // Someone has already executed this query.
- }
- return id(new AphrontRedirectResponse())
- ->setURI('/search/'.$query->getQueryKey().'/');
}
}
}
- $options = array(
- '' => 'All Documents',
- ) + PhabricatorSearchAbstractDocument::getSupportedTypes();
-
- $status_options = array(
- 0 => 'Open and Closed Documents',
- 1 => 'Open Documents',
- );
-
- $phids = array_merge(
- $query->getParameter('author', array()),
- $query->getParameter('owner', array()),
- $query->getParameter('subscribers', array()),
- $query->getParameter('project', array()));
-
- $handles = $this->loadViewerHandles($phids);
+ $controller = id(new PhabricatorApplicationSearchController($request))
+ ->setQueryKey($this->queryKey)
+ ->setSearchEngine(new PhabricatorSearchApplicationSearchEngine())
+ ->setUseOffsetPaging(true)
+ ->setNavigation($this->buildSideNavView());
- $author_value = array_select_keys(
- $handles,
- $query->getParameter('author', array()));
-
- $owner_value = array_select_keys(
- $handles,
- $query->getParameter('owner', array()));
-
- $subscribers_value = array_select_keys(
- $handles,
- $query->getParameter('subscribers', array()));
-
- $project_value = array_select_keys(
- $handles,
- $query->getParameter('project', array()));
-
- $search_form = new AphrontFormView();
- $search_form
- ->setUser($user)
- ->setAction('/search/')
- ->appendChild(
- phutil_tag(
- 'input',
- array(
- 'type' => 'hidden',
- 'name' => 'jump',
- 'value' => 'no',
- )))
- ->appendChild(
- id(new AphrontFormTextControl())
- ->setLabel('Search')
- ->setName('query')
- ->setValue($query->getParameter('query')))
- ->appendChild(
- id(new AphrontFormSelectControl())
- ->setLabel('Document Type')
- ->setName('type')
- ->setOptions($options)
- ->setValue($query->getParameter('type')))
- ->appendChild(
- id(new AphrontFormSelectControl())
- ->setLabel('Document Status')
- ->setName('open')
- ->setOptions($status_options)
- ->setValue($query->getParameter('open')))
- ->appendChild(
- id(new AphrontFormTokenizerControl())
- ->setName('author')
- ->setLabel('Author')
- ->setDatasource('/typeahead/common/users/')
- ->setValue($author_value))
- ->appendChild(
- id(new AphrontFormTokenizerControl())
- ->setName('owner')
- ->setLabel('Owner')
- ->setDatasource('/typeahead/common/searchowner/')
- ->setValue($owner_value)
- ->setCaption(
- 'Tip: search for "Up For Grabs" to find unowned documents.'))
- ->appendChild(
- id(new AphrontFormTokenizerControl())
- ->setName('subscribers')
- ->setLabel('Subscribers')
- ->setDatasource('/typeahead/common/users/')
- ->setValue($subscribers_value))
- ->appendChild(
- id(new AphrontFormTokenizerControl())
- ->setName('project')
- ->setLabel('Project')
- ->setDatasource('/typeahead/common/projects/')
- ->setValue($project_value))
- ->appendChild(
- id(new AphrontFormSubmitControl())
- ->setValue('Search'));
-
- $search_panel = new AphrontListFilterView();
- $search_panel->appendChild($search_form);
-
- require_celerity_resource('phabricator-search-results-css');
+ return $this->delegateToController($controller);
+ }
- if ($query->getID()) {
+ public function buildSideNavView($for_app = false) {
+ $viewer = $this->getRequest()->getUser();
- $limit = 20;
+ $nav = new AphrontSideNavFilterView();
+ $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
- $pager = new AphrontPagerView();
- $pager->setURI($request->getRequestURI(), 'page');
- $pager->setPageSize($limit);
- $pager->setOffset($request->getInt('page'));
+ id(new PhabricatorSearchApplicationSearchEngine())
+ ->setViewer($viewer)
+ ->addNavigationItems($nav->getMenu());
- $query->setParameter('limit', $limit + 1);
- $query->setParameter('offset', $pager->getOffset());
+ $nav->selectFilter(null);
- $engine = PhabricatorSearchEngineSelector::newSelector()->newEngine();
- $results = $engine->executeSearch($query);
- $results = $pager->sliceResults($results);
+ return $nav;
+ }
- // If there are any objects which match the query by name, and we're
- // not paging through the results, prefix the results with the named
- // objects.
- if (!$request->getInt('page')) {
- $named = id(new PhabricatorObjectQuery())
- ->setViewer($user)
- ->withNames(array($query->getParameter('queyr')))
- ->execute();
- if ($named) {
- $results = array_merge(array_keys($named), $results);
- }
+ public function renderResultsList(
+ array $results,
+ PhabricatorSavedQuery $query) {
+
+ $viewer = $this->getRequest()->getUser();
+
+ if ($results) {
+ $objects = id(new PhabricatorObjectQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(mpull($results, 'getPHID'))
+ ->execute();
+
+ $output = array();
+ foreach ($results as $phid => $handle) {
+ $view = id(new PhabricatorSearchResultView())
+ ->setHandle($handle)
+ ->setQuery($query)
+ ->setObject(idx($objects, $phid));
+ $output[] = $view->render();
}
- if ($results) {
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($user)
- ->withPHIDs($results)
- ->execute();
- $objects = id(new PhabricatorObjectQuery())
- ->setViewer($user)
- ->withPHIDs($results)
- ->execute();
- $results = array();
- foreach ($handles as $phid => $handle) {
- $view = id(new PhabricatorSearchResultView())
- ->setHandle($handle)
- ->setQuery($query)
- ->setObject(idx($objects, $phid));
- $results[] = $view->render();
- }
-
- $results = phutil_tag_div('phabricator-search-result-list', array(
- phutil_implode_html("\n", $results),
- phutil_tag_div('search-results-pager', $pager->render()),
- ));
- } else {
- $results = phutil_tag_div(
- 'phabricator-search-result-list',
- phutil_tag(
- 'p',
- array('class' => 'phabricator-search-no-results'),
- pht('No search results.')));
- }
- $results = id(new PHUIBoxView())
- ->addMargin(PHUI::MARGIN_LARGE)
- ->addPadding(PHUI::PADDING_LARGE)
- ->setShadow(true)
- ->appendChild($results)
- ->addClass('phabricator-search-result-box');
+ $results = phutil_tag_div(
+ 'phabricator-search-result-list',
+ $output);
} else {
- $results = null;
+ $results = phutil_tag_div(
+ 'phabricator-search-result-list',
+ phutil_tag(
+ 'p',
+ array('class' => 'phabricator-search-no-results'),
+ pht('No search results.')));
}
- $crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb(pht('Search'));
-
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $search_panel,
- $results,
- ),
- array(
- 'title' => pht('Search Results'),
- 'device' => true,
- ));
+ return id(new PHUIBoxView())
+ ->addMargin(PHUI::MARGIN_LARGE)
+ ->addPadding(PHUI::PADDING_LARGE)
+ ->setShadow(true)
+ ->appendChild($results)
+ ->addClass('phabricator-search-result-box');
}
-
}
diff --git a/src/applications/search/engine/PhabricatorSearchEngineElastic.php b/src/applications/search/engine/PhabricatorSearchEngineElastic.php
--- a/src/applications/search/engine/PhabricatorSearchEngineElastic.php
+++ b/src/applications/search/engine/PhabricatorSearchEngineElastic.php
@@ -114,17 +114,41 @@
);
}
- $rel_mapping = array(
- 'author' => PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
- 'open' => PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
- 'owner' => PhabricatorSearchRelationship::RELATIONSHIP_OWNER,
- 'subscribers' => PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER,
- 'project' => PhabricatorSearchRelationship::RELATIONSHIP_PROJECT,
- 'repository' => PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY,
+ $relationship_map = array(
+ PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR =>
+ $query->getParameter('authorPHIDs', array()),
+ PhabricatorSearchRelationship::RELATIONSHIP_OWNER =>
+ $query->getParameter('ownerPHIDs', array()),
+ PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER =>
+ $query->getParameter('subscriberPHIDs', array()),
+ PhabricatorSearchRelationship::RELATIONSHIP_PROJECT =>
+ $query->getParameter('projectPHIDs', array()),
+ PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY =>
+ $query->getParameter('repositoryPHIDs', array()),
);
- foreach ($rel_mapping as $name => $field) {
- $param = $query->getParameter($name);
- if (is_array($param)) {
+
+ $statuses = $query->getParameter('statuses', array());
+ $statuses = array_fuse($statuses);
+
+ $rel_open = PhabricatorSearchRelationship::RELATIONSHIP_OPEN;
+ $rel_closed = PhabricatorSearchRelationship::RELATIONSHIP_CLOSED;
+ $rel_unowned = PhabricatorSearchRelationship::RELATIONSHIP_UNOWNED;
+
+ $include_open = !empty($statuses[$rel_open]);
+ $include_closed = !empty($statuses[$rel_closed]);
+
+ if ($include_open && !$include_closed) {
+ $relationship_map[$rel_open] = true;
+ } else if (!$include_open && $include_closed) {
+ $relationship_map[$rel_closed] = true;
+ }
+
+ if ($query->getParameter('withUnowned')) {
+ $relationship_map[$rel_unowned] = true;
+ }
+
+ foreach ($relationship_map as $field => $param) {
+ if (is_array($param) && $param) {
$should = array();
foreach ($param as $val) {
$should[] = array(
@@ -177,24 +201,24 @@
}
public function executeSearch(PhabricatorSavedQuery $query) {
- $type = $query->getParameter('type');
- if ($type) {
- $uri = "/phabricator/{$type}/_search";
- } else {
- // Don't use '/phabricator/_search' for the case that there is something
- // else in the index (for example if 'phabricator' is only an alias to
- // some bigger index).
- $types = PhabricatorSearchAbstractDocument::getSupportedTypes();
- $uri = '/phabricator/' . implode(',', array_keys($types)) . '/_search';
+ $types = $query->getParameter('types');
+ if (!$types) {
+ $types = array_keys(
+ PhabricatorSearchApplicationSearchEngine::getIndexableDocumentTypes());
}
+ // Don't use '/phabricator/_search' for the case that there is something
+ // else in the index (for example if 'phabricator' is only an alias to
+ // some bigger index).
+ $uri = '/phabricator/'.implode(',', array_keys($types)).'/_search';
+
try {
$response = $this->executeRequest($uri, $this->buildSpec($query));
} catch (HTTPFutureResponseStatusHTTP $ex) {
// elasticsearch probably uses Lucene query syntax:
// http://lucene.apache.org/core/3_6_1/queryparsersyntax.html
// Try literal search if operator search fails.
- if (!$query->getParameter('query')) {
+ if (!strlen($query->getParameter('query'))) {
throw $ex;
}
$query = clone $query;
diff --git a/src/applications/search/engine/PhabricatorSearchEngineMySQL.php b/src/applications/search/engine/PhabricatorSearchEngineMySQL.php
--- a/src/applications/search/engine/PhabricatorSearchEngineMySQL.php
+++ b/src/applications/search/engine/PhabricatorSearchEngineMySQL.php
@@ -179,7 +179,7 @@
$q);
$field = $query->getParameter('field');
- if ($field/* && $field != AdjutantQuery::FIELD_ALL*/) {
+ if ($field) {
$where[] = qsprintf(
$conn_r,
'field.field = %s',
@@ -192,49 +192,74 @@
$where[] = qsprintf($conn_r, 'document.phid != %s', $exclude);
}
- if ($query->getParameter('type')) {
+ $types = $query->getParameter('types');
+ if ($types) {
if (strlen($q)) {
- // TODO: verify that this column actually does something useful in query
- // plans once we have nontrivial amounts of data.
$where[] = qsprintf(
$conn_r,
- 'field.phidType = %s',
- $query->getParameter('type'));
+ 'field.phidType IN (%Ls)',
+ $types);
}
$where[] = qsprintf(
$conn_r,
- 'document.documentType = %s',
- $query->getParameter('type'));
+ 'document.documentType IN (%Ls)',
+ $types);
}
$join[] = $this->joinRelationship(
$conn_r,
$query,
- 'author',
+ 'authorPHIDs',
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR);
- $join[] = $this->joinRelationship(
- $conn_r,
- $query,
- 'open',
- PhabricatorSearchRelationship::RELATIONSHIP_OPEN);
+ $statuses = $query->getParameter('statuses', array());
+ $statuses = array_fuse($statuses);
+ $open_rel = PhabricatorSearchRelationship::RELATIONSHIP_OPEN;
+ $closed_rel = PhabricatorSearchRelationship::RELATIONSHIP_CLOSED;
+ $include_open = !empty($statuses[$open_rel]);
+ $include_closed = !empty($statuses[$closed_rel]);
- $join[] = $this->joinRelationship(
- $conn_r,
- $query,
- 'owner',
- PhabricatorSearchRelationship::RELATIONSHIP_OWNER);
+ if ($include_open && !$include_closed) {
+ $join[] = $this->joinRelationship(
+ $conn_r,
+ $query,
+ 'statuses',
+ $open_rel,
+ true);
+ } else if ($include_closed && !$include_open) {
+ $join[] = $this->joinRelationship(
+ $conn_r,
+ $query,
+ 'statuses',
+ $closed_rel,
+ true);
+ }
+
+ if ($query->getParameter('withUnowned')) {
+ $join[] = $this->joinRelationship(
+ $conn_r,
+ $query,
+ 'withUnowned',
+ PhabricatorSearchRelationship::RELATIONSHIP_UNOWNED,
+ true);
+ } else {
+ $join[] = $this->joinRelationship(
+ $conn_r,
+ $query,
+ 'ownerPHIDs',
+ PhabricatorSearchRelationship::RELATIONSHIP_OWNER);
+ }
$join[] = $this->joinRelationship(
$conn_r,
$query,
- 'subscribers',
+ 'subscriberPHIDs',
PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER);
$join[] = $this->joinRelationship(
$conn_r,
$query,
- 'project',
+ 'projectPHIDs',
PhabricatorSearchRelationship::RELATIONSHIP_PROJECT);
$join[] = $this->joinRelationship(
@@ -283,19 +308,8 @@
AphrontDatabaseConnection $conn,
PhabricatorSavedQuery $query,
$field,
- $type) {
-
- $phids = $query->getParameter($field, array());
- if (!$phids) {
- return null;
- }
-
- $is_existence = false;
- switch ($type) {
- case PhabricatorSearchRelationship::RELATIONSHIP_OPEN:
- $is_existence = true;
- break;
- }
+ $type,
+ $is_existence = false) {
$sql = qsprintf(
$conn,
@@ -307,6 +321,10 @@
$type);
if (!$is_existence) {
+ $phids = $query->getParameter($field, array());
+ if (!$phids) {
+ return null;
+ }
$sql .= qsprintf(
$conn,
' AND %C.relatedPHID in (%Ls)',
diff --git a/src/applications/search/index/PhabricatorSearchAbstractDocument.php b/src/applications/search/index/PhabricatorSearchAbstractDocument.php
--- a/src/applications/search/index/PhabricatorSearchAbstractDocument.php
+++ b/src/applications/search/index/PhabricatorSearchAbstractDocument.php
@@ -1,8 +1,5 @@
<?php
-/**
- * @group search
- */
final class PhabricatorSearchAbstractDocument {
private $phid;
@@ -13,17 +10,6 @@
private $fields = array();
private $relationships = array();
- public static function getSupportedTypes() {
- return array(
- DifferentialPHIDTypeRevision::TYPECONST => 'Differential Revisions',
- PhabricatorRepositoryPHIDTypeCommit::TYPECONST => 'Repository Commits',
- ManiphestPHIDTypeTask::TYPECONST => 'Maniphest Tasks',
- PhrictionPHIDTypeDocument::TYPECONST => 'Phriction Documents',
- PhabricatorPeoplePHIDTypeUser::TYPECONST => 'Phabricator Users',
- PonderPHIDTypeQuestion::TYPECONST => 'Ponder Questions',
- );
- }
-
public function setPHID($phid) {
$this->phid = $phid;
return $this;
diff --git a/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php b/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php
--- a/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php
+++ b/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php
@@ -6,19 +6,155 @@
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
+ $saved->setParameter('query', $request->getStr('query'));
+ $saved->setParameter(
+ 'statuses',
+ $this->readListFromRequest($request, 'statuses'));
+ $saved->setParameter(
+ 'types',
+ $this->readListFromRequest($request, 'types'));
+
+ $saved->setParameter(
+ 'authorPHIDs',
+ $this->readUsersFromRequest($request, 'authorPHIDs'));
+
+ $saved->setParameter(
+ 'ownerPHIDs',
+ $this->readUsersFromRequest($request, 'ownerPHIDs'));
+
+ $saved->setParameter(
+ 'withUnowned',
+ $this->readBoolFromRequest($request, 'withUnowned'));
+
+ $saved->setParameter(
+ 'subscriberPHIDs',
+ $this->readPHIDsFromRequest($request, 'subscriberPHIDs'));
+
+ $saved->setParameter(
+ 'projectPHIDs',
+ $this->readPHIDsFromRequest($request, 'projectPHIDs'));
+
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
- $query = id(new PhabricatorSearchDocumentQuery());
-
+ $query = id(new PhabricatorSearchDocumentQuery())
+ ->withSavedQuery($saved);
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
- PhabricatorSavedQuery $saved_query) {
- return;
+ PhabricatorSavedQuery $saved) {
+
+ $options = array();
+ $author_value = null;
+ $owner_value = null;
+ $subscribers_value = null;
+ $project_value = null;
+
+ $author_phids = $saved->getParameter('authorPHIDs', array());
+ $owner_phids = $saved->getParameter('ownerPHIDs', array());
+ $subscriber_phids = $saved->getParameter('subscriberPHIDs', array());
+ $project_phids = $saved->getParameter('projectPHIDs', array());
+
+ $all_phids = array_merge(
+ $author_phids,
+ $owner_phids,
+ $subscriber_phids,
+ $project_phids);
+
+ $all_handles = id(new PhabricatorHandleQuery())
+ ->setViewer($this->requireViewer())
+ ->withPHIDs($all_phids)
+ ->execute();
+
+ $author_handles = array_select_keys($all_handles, $author_phids);
+ $owner_handles = array_select_keys($all_handles, $owner_phids);
+ $subscriber_handles = array_select_keys($all_handles, $subscriber_phids);
+ $project_handles = array_select_keys($all_handles, $project_phids);
+
+ $with_unowned = $saved->getParameter('withUnowned', array());
+
+ $status_values = $saved->getParameter('statuses', array());
+ $status_values = array_fuse($status_values);
+
+ $statuses = array(
+ PhabricatorSearchRelationship::RELATIONSHIP_OPEN => pht('Open'),
+ PhabricatorSearchRelationship::RELATIONSHIP_CLOSED => pht('Closed'),
+ );
+ $status_control = id(new AphrontFormCheckboxControl())
+ ->setLabel(pht('Document Status'));
+ foreach ($statuses as $status => $name) {
+ $status_control->addCheckbox(
+ 'statuses[]',
+ $status,
+ $name,
+ isset($status_values[$status]));
+ }
+
+ $type_values = $saved->getParameter('types', array());
+ $type_values = array_fuse($type_values);
+
+ $types = self::getIndexableDocumentTypes();
+
+ $types_control = id(new AphrontFormCheckboxControl())
+ ->setLabel(pht('Document Types'));
+ foreach ($types as $type => $name) {
+ $types_control->addCheckbox(
+ 'types[]',
+ $type,
+ $name,
+ isset($type_values[$type]));
+ }
+
+ $form
+ ->appendChild(
+ phutil_tag(
+ 'input',
+ array(
+ 'type' => 'hidden',
+ 'name' => 'jump',
+ 'value' => 'no',
+ )))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel('Query')
+ ->setName('query')
+ ->setValue($saved->getParameter('query')))
+ ->appendChild($status_control)
+ ->appendChild($types_control)
+ ->appendChild(
+ id(new AphrontFormTokenizerControl())
+ ->setName('authorPHIDs')
+ ->setLabel('Authors')
+ ->setDatasource('/typeahead/common/users/')
+ ->setValue($author_handles))
+ ->appendChild(
+ id(new AphrontFormTokenizerControl())
+ ->setName('ownerPHIDs')
+ ->setLabel('Owners')
+ ->setDatasource('/typeahead/common/searchowner/')
+ ->setValue($owner_handles))
+ ->appendChild(
+ id(new AphrontFormCheckboxControl())
+ ->addCheckbox(
+ 'withUnowned',
+ 1,
+ pht('Show only unowned documents.'),
+ $with_unowned))
+ ->appendChild(
+ id(new AphrontFormTokenizerControl())
+ ->setName('subscriberPHIDs')
+ ->setLabel('Subscribers')
+ ->setDatasource('/typeahead/common/users/')
+ ->setValue($subscriber_handles))
+ ->appendChild(
+ id(new AphrontFormTokenizerControl())
+ ->setName('projectPHIDs')
+ ->setLabel('In Any Project')
+ ->setDatasource('/typeahead/common/projects/')
+ ->setValue($project_handles));
}
protected function getURI($path) {
@@ -28,6 +164,8 @@
public function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Documents'),
+ 'open' => pht('Open Documents'),
+ 'open-tasks' => pht('Open Tasks'),
);
return $names;
@@ -41,9 +179,45 @@
switch ($query_key) {
case 'all':
return $query;
+ case 'open':
+ return $query->setParameter('statuses', array('open'));
+ case 'open-tasks':
+ return $query
+ ->setParameter('statuses', array('open'))
+ ->setParameter('types', array(ManiphestPHIDTypeTask::TYPECONST));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
+ public static function getIndexableDocumentTypes() {
+ // TODO: This is inelegant and not very efficient, but gets us reasonable
+ // results. It would be nice to do this more elegantly.
+
+ // TODO: We should hide types associated with applications the user can
+ // not access. There's no reasonable way to do this right now.
+
+ $indexers = id(new PhutilSymbolLoader())
+ ->setAncestorClass('PhabricatorSearchDocumentIndexer')
+ ->loadObjects();
+
+ $types = PhabricatorPHIDType::getAllTypes();
+
+ $results = array();
+ foreach ($types as $type) {
+ $typeconst = $type->getTypeConstant();
+ foreach ($indexers as $indexer) {
+ $fake_phid = 'PHID-'.$typeconst.'-fake';
+ if ($indexer->shouldIndexDocumentByPHID($fake_phid)) {
+ $results[$typeconst] = $type->getTypeName();
+ }
+ }
+ }
+
+ asort($results);
+
+ return $results;
+ }
+
+
}
diff --git a/src/applications/search/query/PhabricatorSearchDocumentQuery.php b/src/applications/search/query/PhabricatorSearchDocumentQuery.php
--- a/src/applications/search/query/PhabricatorSearchDocumentQuery.php
+++ b/src/applications/search/query/PhabricatorSearchDocumentQuery.php
@@ -3,12 +3,66 @@
final class PhabricatorSearchDocumentQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
+ private $savedQuery;
+
+ public function withSavedQuery(PhabricatorSavedQuery $query) {
+ $this->savedQuery = $query;
+ return $this;
+ }
+
protected function loadPage() {
- return array();
+ $phids = $this->loadDocumentPHIDsWithoutPolicyChecks();
+
+ $handles = id(new PhabricatorHandleQuery())
+ ->setViewer($this->getViewer())
+ ->withPHIDs($phids)
+ ->execute();
+
+ // Retain engine order.
+ $handles = array_select_keys($handles, $phids);
+
+ return $handles;
+ }
+
+ protected function willFilterPage(array $handles) {
+ foreach ($handles as $key => $handle) {
+ if (!$handle->isComplete()) {
+ unset($handles[$key]);
+ continue;
+ }
+ if ($handle->getPolicyFiltered()) {
+ unset($handles[$key]);
+ continue;
+ }
+ }
+
+ return $handles;
+ }
+
+ public function loadDocumentPHIDsWithoutPolicyChecks() {
+ $query = id(clone($this->savedQuery))
+ ->setParameter('offset', $this->getOffset())
+ ->setParameter('limit', $this->getRawResultLimit());
+
+ $engine = PhabricatorSearchEngineSelector::newSelector()->newEngine();
+
+ return $engine->executeSearch($query);
}
public function getQueryApplicationClass() {
return 'PhabricatorApplicationSearch';
}
+ protected function getPagingValue($result) {
+ throw new Exception(
+ pht(
+ 'This query does not support cursor paging; it must be offset '.
+ 'paged.'));
+ }
+
+ protected function nextPage(array $page) {
+ $this->setOffset($this->getOffset() + count($page));
+ return $this;
+ }
+
}

File Metadata

Mime Type
text/plain
Expires
Wed, Mar 19, 7:18 PM (2 d, 18 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7711855
Default Alt Text
D8123.id18371.diff (30 KB)

Event Timeline