1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 16:52:41 +01:00

Use delegation to generalize application search controllers

Summary:
Ref T2625. Lifts almost all of the search logic out of Paste controllers and into Search.

This uses controller delegation for generalization. We use this in a few places, but don't use it very much yet. I think it's pretty reasonable as-is, but I might be able to make even more stuff free.

There are some slightly rough edges around routes, still, but I want to hit Phame and Differential (which both have multiple application search engines) before trying to generalize that.

Test Plan: Executed, browsed and managed Paste searches.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2625

Differential Revision: https://secure.phabricator.com/D6073
This commit is contained in:
epriestley 2013-05-30 14:09:02 -07:00
parent cf5009d5fb
commit 5d94a8a338
10 changed files with 367 additions and 237 deletions

View file

@ -743,7 +743,9 @@ phutil_register_library_map(array(
'PhabricatorApplicationReleephConfigOptions' => 'applications/releeph/config/PhabricatorApplicationReleephConfigOptions.php',
'PhabricatorApplicationRepositories' => 'applications/repository/application/PhabricatorApplicationRepositories.php',
'PhabricatorApplicationSearch' => 'applications/search/application/PhabricatorApplicationSearch.php',
'PhabricatorApplicationSearchController' => 'applications/search/controller/PhabricatorApplicationSearchController.php',
'PhabricatorApplicationSearchEngine' => 'applications/search/engine/PhabricatorApplicationSearchEngine.php',
'PhabricatorApplicationSearchResultsControllerInterface' => 'applications/search/interface/PhabricatorApplicationSearchResultsControllerInterface.php',
'PhabricatorApplicationSettings' => 'applications/settings/application/PhabricatorApplicationSettings.php',
'PhabricatorApplicationSlowvote' => 'applications/slowvote/application/PhabricatorApplicationSlowvote.php',
'PhabricatorApplicationStatusView' => 'applications/meta/view/PhabricatorApplicationStatusView.php',
@ -1235,7 +1237,6 @@ phutil_register_library_map(array(
'PhabricatorPasteDAO' => 'applications/paste/storage/PhabricatorPasteDAO.php',
'PhabricatorPasteEditController' => 'applications/paste/controller/PhabricatorPasteEditController.php',
'PhabricatorPasteListController' => 'applications/paste/controller/PhabricatorPasteListController.php',
'PhabricatorPasteQueriesController' => 'applications/paste/controller/PhabricatorPasteQueriesController.php',
'PhabricatorPasteQuery' => 'applications/paste/query/PhabricatorPasteQuery.php',
'PhabricatorPasteRemarkupRule' => 'applications/paste/remarkup/PhabricatorPasteRemarkupRule.php',
'PhabricatorPasteSearchEngine' => 'applications/paste/query/PhabricatorPasteSearchEngine.php',
@ -2530,6 +2531,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationReleephConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorApplicationRepositories' => 'PhabricatorApplication',
'PhabricatorApplicationSearch' => 'PhabricatorApplication',
'PhabricatorApplicationSearchController' => 'PhabricatorSearchBaseController',
'PhabricatorApplicationSettings' => 'PhabricatorApplication',
'PhabricatorApplicationSlowvote' => 'PhabricatorApplication',
'PhabricatorApplicationStatusView' => 'AphrontView',
@ -3016,8 +3018,11 @@ phutil_register_library_map(array(
'PhabricatorPasteController' => 'PhabricatorController',
'PhabricatorPasteDAO' => 'PhabricatorLiskDAO',
'PhabricatorPasteEditController' => 'PhabricatorPasteController',
'PhabricatorPasteListController' => 'PhabricatorPasteController',
'PhabricatorPasteQueriesController' => 'PhabricatorPasteController',
'PhabricatorPasteListController' =>
array(
0 => 'PhabricatorPasteController',
1 => 'PhabricatorApplicationSearchResultsControllerInterface',
),
'PhabricatorPasteQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorPasteRemarkupRule' => 'PhabricatorRemarkupRuleObject',
'PhabricatorPasteSearchEngine' => 'PhabricatorApplicationSearchEngine',

View file

@ -7,6 +7,18 @@ abstract class AphrontController extends Phobject {
private $request;
private $currentApplication;
private $delegatingController;
public function setDelegatingController(
AphrontController $delegating_controller) {
$this->delegatingController = $delegating_controller;
return $this;
}
public function getDelegatingController() {
return $this->delegatingController;
}
public function willBeginExecution() {
return;
@ -31,6 +43,13 @@ abstract class AphrontController extends Phobject {
}
final public function delegateToController(AphrontController $controller) {
$controller->setDelegatingController($this);
$application = $this->getCurrentApplication();
if ($application) {
$controller->setCurrentApplication($application);
}
return $controller->processRequest();
}

View file

@ -32,12 +32,9 @@ final class PhabricatorApplicationPaste extends PhabricatorApplication {
return array(
'/P(?P<id>[1-9]\d*)' => 'PhabricatorPasteViewController',
'/paste/' => array(
'' => 'PhabricatorPasteListController',
'(query/(?P<queryKey>[^/]+)/)?' => 'PhabricatorPasteListController',
'create/' => 'PhabricatorPasteEditController',
'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorPasteEditController',
'filter/(?P<filter>\w+)/' => 'PhabricatorPasteListController',
'query/(?P<queryKey>[^/]+)/'=> 'PhabricatorPasteListController',
'savedqueries/' => 'PhabricatorPasteQueriesController',
),
);
}

View file

@ -12,27 +12,9 @@ abstract class PhabricatorPasteController extends PhabricatorController {
$nav->addFilter('create', pht('Create Paste'));
}
$nav->addLabel(pht('Queries'));
$engine = id(new PhabricatorPasteSearchEngine())
->setViewer($user);
$named_queries = id(new PhabricatorNamedQueryQuery())
id(new PhabricatorPasteSearchEngine())
->setViewer($user)
->withUserPHIDs(array($user->getPHID()))
->withEngineClassNames(array(get_class($engine)))
->execute();
$named_queries = $named_queries + $engine->getBuiltinQueries($user);
foreach ($named_queries as $query) {
$nav->addFilter('query/'.$query->getQueryKey(), $query->getQueryName());
}
$nav->addFilter('savedqueries', pht('Edit Queries...'));
$nav->addLabel(pht('Search'));
$nav->addFilter('query/advanced', pht('Advanced Search'));
->addNavigationItems($nav);
$nav->selectFilter(null);

View file

@ -1,6 +1,7 @@
<?php
final class PhabricatorPasteListController extends PhabricatorPasteController {
final class PhabricatorPasteListController extends PhabricatorPasteController
implements PhabricatorApplicationSearchResultsControllerInterface {
private $queryKey;
@ -14,128 +15,15 @@ final class PhabricatorPasteListController extends PhabricatorPasteController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$controller = id(new PhabricatorApplicationSearchController($request))
->setQueryKey($this->queryKey)
->setSearchEngine(new PhabricatorPasteSearchEngine())
->setNavigation($this->buildSideNavView());
$engine = id(new PhabricatorPasteSearchEngine())
->setViewer($user);
if ($request->isFormPost()) {
return id(new AphrontRedirectResponse())->setURI(
$engine->getQueryResultsPageURI(
$engine->buildSavedQueryFromRequest($request)->getQueryKey()));
return $this->delegateToController($controller);
}
$nav = $this->buildSideNavView();
$named_query = null;
$run_query = true;
$query_key = $this->queryKey;
if ($this->queryKey == 'advanced') {
$run_query = false;
$query_key = $request->getStr('query');
}
if ($engine->isBuiltinQuery($query_key)) {
$saved_query = $engine->buildSavedQueryFromBuiltin($query_key);
$named_query = $engine->getBuiltinQuery($query_key);
} else if ($query_key) {
$saved_query = id(new PhabricatorSavedQueryQuery())
->setViewer($user)
->withQueryKeys(array($query_key))
->executeOne();
if (!$saved_query) {
return new Aphront404Response();
}
$named_query = id(new PhabricatorNamedQueryQuery())
->setViewer($user)
->withQueryKeys(array($saved_query->getQueryKey()))
->withEngineClassNames(array(get_class($engine)))
->withUserPHIDs(array($user->getPHID()))
->executeOne();
} else {
$saved_query = $engine->buildSavedQueryFromRequest($request);
}
$filter = $nav->selectFilter(
'query/'.$saved_query->getQueryKey(),
'query/advanced');
$form = id(new AphrontFormView())
->setNoShading(true)
->setUser($user);
$engine->buildSearchForm($form, $saved_query);
$submit = id(new AphrontFormSubmitControl())
->setValue(pht('Execute Query'));
if ($run_query && !$named_query) {
$submit->addCancelButton(
'/search/edit/'.$saved_query->getQueryKey().'/',
pht('Save Custom Query...'));
}
$form->appendChild($submit);
$filter_view = id(new AphrontListFilterView())->appendChild($form);
if ($run_query && $named_query) {
if ($named_query->getIsBuiltin()) {
$description = pht(
'Showing results for query "%s".',
$named_query->getQueryName());
} else {
$description = pht(
'Showing results for saved query "%s".',
$named_query->getQueryName());
}
$filter_view->setCollapsed(
pht('Edit Query...'),
pht('Hide Query'),
$description,
$this->getApplicationURI('query/advanced/?query='.$query_key));
}
$nav->appendChild($filter_view);
if ($run_query) {
$query = id(new PhabricatorPasteSearchEngine())
->buildQueryFromSavedQuery($saved_query);
$pager = new AphrontCursorPagerView();
$pager->readFromRequest($request);
$pastes = $query->setViewer($request->getUser())
->needContent(true)
->executeWithCursorPager($pager);
$list = $this->buildPasteList($pastes);
$list->setPager($pager);
$list->setNoDataString(pht("No results found for this query."));
$nav->appendChild($list);
}
$crumbs = $this
->buildApplicationCrumbs($nav)
->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht("Pastes"))
->setHref($this->getApplicationURI('filter/'.$filter.'/')));
$nav->setCrumbs($crumbs);
return $this->buildApplicationPage(
$nav,
array(
'title' => pht("Pastes"),
'device' => true,
'dust' => true,
));
}
private function buildPasteList(array $pastes) {
public function renderResultsList(array $pastes) {
assert_instances_of($pastes, 'PhabricatorPaste');
$user = $this->getRequest()->getUser();

View file

@ -1,80 +0,0 @@
<?php
final class PhabricatorPasteQueriesController
extends PhabricatorPasteController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$engine = id(new PhabricatorPasteSearchEngine())
->setViewer($user);
$nav = $this->buildSideNavView();
$nav->selectFilter('savedqueries');
$named_queries = id(new PhabricatorNamedQueryQuery())
->setViewer($user)
->withUserPHIDs(array($user->getPHID()))
->withEngineClassNames(array(get_class($engine)))
->execute();
$named_queries += $engine->getBuiltinQueries();
$list = new PhabricatorObjectItemListView();
$list->setUser($user);
foreach ($named_queries as $named_query) {
$date_created = phabricator_datetime(
$named_query->getDateCreated(),
$user);
$item = id(new PhabricatorObjectItemView())
->setHeader($named_query->getQueryName())
->setHref($engine->getQueryResultsPageURI($named_query->getQueryKey()));
if ($named_query->getIsBuiltin()) {
$item->addIcon('lock-grey', pht('Builtin'));
$item->setBarColor('grey');
} else {
$item->addIcon('none', $date_created);
$item->addAction(
id(new PhabricatorMenuItemView())
->setIcon('delete')
->setHref('/search/delete/'.$named_query->getQueryKey().'/')
->setWorkflow(true));
$item->addAction(
id(new PhabricatorMenuItemView())
->setIcon('edit')
->setHref('/search/edit/'.$named_query->getQueryKey().'/'));
}
$list->addItem($item);
}
$list->setNoDataString(pht("No results found for this query."));
$nav->appendChild(
array(
$list,
));
$crumbs = $this
->buildApplicationCrumbs($nav)
->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht("Saved Queries"))
->setHref($this->getApplicationURI('/savedqueries/')));
$nav->setCrumbs($crumbs);
return $this->buildApplicationPage(
$nav,
array(
'title' => pht("Saved Queries"),
'device' => true,
'dust' => true,
));
}
}

View file

@ -70,12 +70,8 @@ final class PhabricatorPasteSearchEngine
->setValue($author_tokens));
}
public function getQueryResultsPageURI($query_key) {
return '/paste/query/'.$query_key.'/';
}
public function getQueryManagementURI() {
return '/paste/savedqueries/';
protected function getURI($path) {
return '/paste/'.$path;
}
public function getBuiltinQueryNames() {

View file

@ -0,0 +1,272 @@
<?php
final class PhabricatorApplicationSearchController
extends PhabricatorSearchBaseController {
private $searchEngine;
private $navigation;
private $queryKey;
public function setQueryKey($query_key) {
$this->queryKey = $query_key;
return $this;
}
protected function getQueryKey() {
return $this->queryKey;
}
public function setNavigation(AphrontSideNavFilterView $navigation) {
$this->navigation = $navigation;
return $this;
}
protected function getNavigation() {
return $this->navigation;
}
public function setSearchEngine(
PhabricatorApplicationSearchEngine $search_engine) {
$this->searchEngine = $search_engine;
return $this;
}
protected function getSearchEngine() {
return $this->searchEngine;
}
protected function validateDelegatingController() {
$parent = $this->getDelegatingController();
if (!$parent) {
throw new Exception(
"You must delegate to this controller, not invoke it directly.");
}
$engine = $this->getSearchEngine();
if (!$engine) {
throw new Exception(
"Call setEngine() before delegating to this controller!");
}
$nav = $this->getNavigation();
if (!$nav) {
throw new Exception(
"Call setNavigation() before delegating to this controller!");
}
$engine->setViewer($this->getRequest()->getUser());
$parent = $this->getDelegatingController();
$interface = 'PhabricatorApplicationSearchResultsControllerInterface';
if (!$parent instanceof $interface) {
throw new Exception(
"Delegating controller must implement '{$interface}'.");
}
}
public function processRequest() {
$this->validateDelegatingController();
$key = $this->getQueryKey();
if ($key == 'edit') {
return $this->processEditRequest();
} else {
return $this->processSearchRequest();
}
}
private function processSearchRequest() {
$parent = $this->getDelegatingController();
$request = $this->getRequest();
$user = $request->getUser();
$engine = $this->getSearchEngine();
$nav = $this->getNavigation();
if ($request->isFormPost()) {
return id(new AphrontRedirectResponse())->setURI(
$engine->getQueryResultsPageURI(
$engine->buildSavedQueryFromRequest($request)->getQueryKey()));
}
$named_query = null;
$run_query = true;
$query_key = $this->queryKey;
if ($this->queryKey == 'advanced') {
$run_query = false;
$query_key = $request->getStr('query');
}
if ($engine->isBuiltinQuery($query_key)) {
$saved_query = $engine->buildSavedQueryFromBuiltin($query_key);
$named_query = $engine->getBuiltinQuery($query_key);
} else if ($query_key) {
$saved_query = id(new PhabricatorSavedQueryQuery())
->setViewer($user)
->withQueryKeys(array($query_key))
->executeOne();
if (!$saved_query) {
return new Aphront404Response();
}
$named_query = id(new PhabricatorNamedQueryQuery())
->setViewer($user)
->withQueryKeys(array($saved_query->getQueryKey()))
->withEngineClassNames(array(get_class($engine)))
->withUserPHIDs(array($user->getPHID()))
->executeOne();
} else {
$saved_query = $engine->buildSavedQueryFromRequest($request);
}
$nav->selectFilter(
'query/'.$saved_query->getQueryKey(),
'query/advanced');
$form = id(new AphrontFormView())
->setNoShading(true)
->setUser($user);
$engine->buildSearchForm($form, $saved_query);
$submit = id(new AphrontFormSubmitControl())
->setValue(pht('Execute Query'));
if ($run_query && !$named_query) {
$submit->addCancelButton(
'/search/edit/'.$saved_query->getQueryKey().'/',
pht('Save Custom Query...'));
}
$form->appendChild($submit);
$filter_view = id(new AphrontListFilterView())->appendChild($form);
if ($run_query && $named_query) {
if ($named_query->getIsBuiltin()) {
$description = pht(
'Showing results for query "%s".',
$named_query->getQueryName());
} else {
$description = pht(
'Showing results for saved query "%s".',
$named_query->getQueryName());
}
$filter_view->setCollapsed(
pht('Edit Query...'),
pht('Hide Query'),
$description,
$this->getApplicationURI('query/advanced/?query='.$query_key));
}
$nav->appendChild($filter_view);
if ($run_query) {
$query = id(new PhabricatorPasteSearchEngine())
->buildQueryFromSavedQuery($saved_query);
$pager = new AphrontCursorPagerView();
$pager->readFromRequest($request);
$pastes = $query->setViewer($request->getUser())
->needContent(true)
->executeWithCursorPager($pager);
$list = $parent->renderResultsList($pastes);
$list->setPager($pager);
$list->setNoDataString(pht("No results found for this query."));
$nav->appendChild($list);
}
if ($named_query) {
$title = pht('Query: %s', $named_query->getQueryName());
} else {
$title = pht('Advanced Search');
}
$crumbs = $parent
->buildApplicationCrumbs()
->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht("Search")));
$nav->setCrumbs($crumbs);
return $this->buildApplicationPage(
$nav,
array(
'title' => $title,
'device' => true,
'dust' => true,
));
}
private function processEditRequest() {
$parent = $this->getDelegatingController();
$request = $this->getRequest();
$user = $request->getUser();
$engine = $this->getSearchEngine();
$nav = $this->getNavigation();
$named_queries = id(new PhabricatorNamedQueryQuery())
->setViewer($user)
->withUserPHIDs(array($user->getPHID()))
->withEngineClassNames(array(get_class($engine)))
->execute();
$named_queries += $engine->getBuiltinQueries();
$list = new PhabricatorObjectItemListView();
$list->setUser($user);
foreach ($named_queries as $named_query) {
$date_created = phabricator_datetime(
$named_query->getDateCreated(),
$user);
$item = id(new PhabricatorObjectItemView())
->setHeader($named_query->getQueryName())
->setHref($engine->getQueryResultsPageURI($named_query->getQueryKey()));
if ($named_query->getIsBuiltin()) {
$item->addIcon('lock-grey', pht('Builtin'));
$item->setBarColor('grey');
} else {
$item->addIcon('none', $date_created);
$item->addAction(
id(new PhabricatorMenuItemView())
->setIcon('delete')
->setHref('/search/delete/'.$named_query->getQueryKey().'/')
->setWorkflow(true));
$item->addAction(
id(new PhabricatorMenuItemView())
->setIcon('edit')
->setHref('/search/edit/'.$named_query->getQueryKey().'/'));
}
$list->addItem($item);
}
$list->setNoDataString(pht('No saved queries.'));
$crumbs = $parent
->buildApplicationCrumbs()
->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht("Saved Queries"))
->setHref($engine->getQueryManagementURI()));
$nav->selectFilter('query/edit');
$nav->setCrumbs($crumbs);
$nav->appendChild($list);
return $parent->buildApplicationPage(
$nav,
array(
'title' => pht("Saved Queries"),
'device' => true,
'dust' => true,
));
}
}

View file

@ -63,7 +63,9 @@ abstract class PhabricatorApplicationSearchEngine {
* @return string URI where the query can be executed.
* @task uri
*/
abstract public function getQueryResultsPageURI($query_key);
public function getQueryResultsPageURI($query_key) {
return $this->getURI('query/'.$query_key.'/');
}
/**
@ -73,7 +75,19 @@ abstract class PhabricatorApplicationSearchEngine {
* @return string URI where queries can be managed.
* @task uri
*/
abstract public function getQueryManagementURI();
public function getQueryManagementURI() {
return $this->getURI('query/edit/');
}
/**
* Return the URI to a path within the application. Used to construct default
* URIs for management and results.
*
* @return string URI to path.
* @task uri
*/
abstract protected function getURI($path);
public function newSavedQuery() {
@ -82,6 +96,36 @@ abstract class PhabricatorApplicationSearchEngine {
}
public function addNavigationItems(AphrontSideNavFilterView $nav) {
$viewer = $this->requireViewer();
$nav->addLabel(pht('Queries'));
$named_queries = id(new PhabricatorNamedQueryQuery())
->setViewer($viewer)
->withUserPHIDs(array($viewer->getPHID()))
->withEngineClassNames(array(get_class($this)))
->execute();
$named_queries = $named_queries + $this->getBuiltinQueries($viewer);
foreach ($named_queries as $query) {
$key = $query->getQueryKey();
$uri = $this->getQueryResultsPageURI($key);
$nav->addFilter('query/'.$key, $query->getQueryName(), $uri);
}
$manage_uri = $this->getQueryManagementURI();
$nav->addFilter('query/edit', pht('Edit Queries...'), $manage_uri);
$nav->addLabel(pht('Search'));
$advanced_uri = $this->getQueryResultsPageURI('advanced');
$nav->addFilter('query/advanced', pht('Advanced Search'), $advanced_uri);
return $this;
}
/* -( Builtin Queries )---------------------------------------------------- */

View file

@ -0,0 +1,7 @@
<?php
interface PhabricatorApplicationSearchResultsControllerInterface {
public function renderResultsList(array $items);
}