From 64182dc94d2b53f178103996dade323e998cf53f Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 15 Apr 2015 06:23:12 -0700 Subject: [PATCH] Implement an extremely primitive typeahead browse view with offset paging Summary: Ref T5750. Give up and use offset paging. Each page will become progressively slower, but the amount of time I was sinking into T7803 without making real headway was not justified by fixing this. In comparison, this took 10 minutes. It isn't really useful to browse through tens of thousands of results anyway, and no one is realistically going to try to do that (later in this sequence, I'll explicitly prevent it). Test Plan: This is completely primitive right now: {F372224} Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T5750 Differential Revision: https://secure.phabricator.com/D12423 --- .../PhabricatorTypeaheadApplication.php | 2 +- ...orTypeaheadModularDatasourceController.php | 52 ++++++++++++++++--- ...habricatorTypeaheadCompositeDatasource.php | 23 ++++++-- .../PhabricatorTypeaheadDatasource.php | 10 ++++ .../storage/PhabricatorTypeaheadResult.php | 4 ++ 5 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/applications/typeahead/application/PhabricatorTypeaheadApplication.php b/src/applications/typeahead/application/PhabricatorTypeaheadApplication.php index b69d4f4223..fdae374f90 100644 --- a/src/applications/typeahead/application/PhabricatorTypeaheadApplication.php +++ b/src/applications/typeahead/application/PhabricatorTypeaheadApplication.php @@ -9,7 +9,7 @@ final class PhabricatorTypeaheadApplication extends PhabricatorApplication { public function getRoutes() { return array( '/typeahead/' => array( - 'class/(?:(?P\w+)/)?' + '(?Pbrowse|class)/(?:(?P\w+)/)?' => 'PhabricatorTypeaheadModularDatasourceController', ), ); diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php index a754d1362a..ffc8ce2af4 100644 --- a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php @@ -11,6 +11,7 @@ final class PhabricatorTypeaheadModularDatasourceController $request = $this->getRequest(); $viewer = $request->getUser(); $query = $request->getStr('q'); + $is_browse = ($request->getURIData('action') == 'browse'); // Default this to the query string to make debugging a little bit easier. $raw_query = nonempty($request->getStr('raw'), $query); @@ -23,16 +24,11 @@ final class PhabricatorTypeaheadModularDatasourceController ->loadObjects(); if (isset($sources[$class])) { $source = $sources[$class]; - if ($source->getDatasourceApplicationClass()) { - if (!PhabricatorApplication::isClassInstalledForViewer( - $source->getDatasourceApplicationClass(), - $viewer)) { - return id(new Aphront404Response()); - } - } - $source->setParameters($request->getRequestData()); + // NOTE: Wrapping the source in a Composite datasource ensures we perform + // application visibility checks for the viewer, so we do not need to do + // those separately. $composite = new PhabricatorTypeaheadRuntimeCompositeDatasource(); $composite->addDatasource($source); @@ -41,7 +37,47 @@ final class PhabricatorTypeaheadModularDatasourceController ->setQuery($query) ->setRawQuery($raw_query); + if ($is_browse) { + $limit = 3; + $offset = $request->getInt('offset'); + $composite + ->setLimit($limit + 1) + ->setOffset($offset); + } + $results = $composite->loadResults(); + + if ($is_browse) { + $next_link = null; + if (count($results) > $limit) { + $results = array_slice($results, 0, $limit, $preserve_keys = true); + $next_link = phutil_tag( + 'a', + array( + 'href' => id(new PhutilURI($request->getRequestURI())) + ->setQueryParam('offset', $offset + $limit), + ), + pht('Next Page')); + } + + $rows = array(); + foreach ($results as $result) { + // TODO: Render nicely. + $rows[] = array_slice($result->getWireFormat(), 0, 3, true); + } + + $table = id(new AphrontTableView($rows)); + + return $this->newDialog() + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->setTitle(get_class($source)) // TODO: Provide nice names. + ->appendChild($table) + ->appendChild($next_link) + ->addCancelButton('/', pht('Close')); + } + + } else if ($is_browse) { + return new Aphront404Response(); } else { $results = array(); } diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php index 9c34af71c7..8990460505 100644 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php @@ -10,17 +10,34 @@ abstract class PhabricatorTypeaheadCompositeDatasource } public function loadResults() { + $offset = $this->getOffset(); + $limit = $this->getLimit(); + $results = array(); foreach ($this->getUsableDatasources() as $source) { $source ->setRawQuery($this->getRawQuery()) ->setQuery($this->getQuery()) - ->setViewer($this->getViewer()) - ->setLimit($this->getLimit()); + ->setViewer($this->getViewer()); + + if ($limit) { + $source->setLimit($offset + $limit); + } $results[] = $source->loadResults(); } - return array_mergev($results); + + $results = array_mergev($results); + $results = msort($results, 'getName'); + + if ($offset || $limit) { + if (!$limit) { + $limit = count($results); + } + $results = array_slice($results, $offset, $limit, $preserve_keys = true); + } + + return $results; } private function getUsableDatasources() { diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php index d08ea87685..ac29954fff 100644 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php @@ -5,6 +5,7 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject { private $viewer; private $query; private $rawQuery; + private $offset; private $limit; private $parameters = array(); @@ -17,6 +18,15 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject { return $this->limit; } + public function setOffset($offset) { + $this->offset = $offset; + return $this; + } + + public function getOffset() { + return $this->offset; + } + public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; return $this; diff --git a/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php b/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php index 37aa314ce9..4820a23e8c 100644 --- a/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php +++ b/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php @@ -69,6 +69,10 @@ final class PhabricatorTypeaheadResult { return $this; } + public function getName() { + return $this->name; + } + public function getWireFormat() { $data = array( $this->name,