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:
		@@ -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',
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,12 +32,9 @@ final class PhabricatorApplicationPaste extends PhabricatorApplication {
 | 
			
		||||
    return array(
 | 
			
		||||
      '/P(?P<id>[1-9]\d*)' => 'PhabricatorPasteViewController',
 | 
			
		||||
      '/paste/' => array(
 | 
			
		||||
        ''                        => 'PhabricatorPasteListController',
 | 
			
		||||
        'create/'                 => 'PhabricatorPasteEditController',
 | 
			
		||||
        'edit/(?P<id>[1-9]\d*)/'  => 'PhabricatorPasteEditController',
 | 
			
		||||
        'filter/(?P<filter>\w+)/' => 'PhabricatorPasteListController',
 | 
			
		||||
        'query/(?P<queryKey>[^/]+)/'=> 'PhabricatorPasteListController',
 | 
			
		||||
        'savedqueries/'           => 'PhabricatorPasteQueriesController',
 | 
			
		||||
        '(query/(?P<queryKey>[^/]+)/)?' => 'PhabricatorPasteListController',
 | 
			
		||||
        'create/'                       => 'PhabricatorPasteEditController',
 | 
			
		||||
        'edit/(?P<id>[1-9]\d*)/'        => 'PhabricatorPasteEditController',
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $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,
 | 
			
		||||
      ));
 | 
			
		||||
    return $this->delegateToController($controller);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private function buildPasteList(array $pastes) {
 | 
			
		||||
  public function renderResultsList(array $pastes) {
 | 
			
		||||
    assert_instances_of($pastes, 'PhabricatorPaste');
 | 
			
		||||
 | 
			
		||||
    $user = $this->getRequest()->getUser();
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
      ));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
      ));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -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  )---------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,7 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
interface PhabricatorApplicationSearchResultsControllerInterface {
 | 
			
		||||
 | 
			
		||||
  public function renderResultsList(array $items);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user