diff --git a/src/applications/typeahead/application/PhabricatorTypeaheadApplication.php b/src/applications/typeahead/application/PhabricatorTypeaheadApplication.php --- a/src/applications/typeahead/application/PhabricatorTypeaheadApplication.php +++ b/src/applications/typeahead/application/PhabricatorTypeaheadApplication.php @@ -9,7 +9,7 @@ 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 --- a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php @@ -11,6 +11,7 @@ $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 @@ ->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 @@ ->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 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php @@ -10,17 +10,34 @@ } 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 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php @@ -5,6 +5,7 @@ private $viewer; private $query; private $rawQuery; + private $offset; private $limit; private $parameters = array(); @@ -17,6 +18,15 @@ 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 --- a/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php +++ b/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php @@ -69,6 +69,10 @@ return $this; } + public function getName() { + return $this->name; + } + public function getWireFormat() { $data = array( $this->name,