mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-30 01:10:58 +01:00
Begin modularizing typeahead sources
Summary: Ref T4420. This sets up the basics for modular typeahead sources. Basically, the huge `switch()` is just replaced with class-based runtime dispatch. The only clever bit I'm doing here is with `CompositeDatasource`, which pretty much just combines the results from several other datasources. We can use this to implement some of the weird cases where we need multiple types of results, although I think I can entirely eliminate many of them entirely. It also makes top-level implementation simpler, since more logic can go inside the sources. Sources are also application-aware, will be responsible for placeholder text, and have a slightly nicer debug view. Test Plan: {F112859} Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T4420 Differential Revision: https://secure.phabricator.com/D8228
This commit is contained in:
parent
7a96de10f0
commit
75c4a185a9
9 changed files with 312 additions and 6 deletions
|
@ -1163,6 +1163,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorApplicationTransactionValidationException' => 'applications/transactions/exception/PhabricatorApplicationTransactionValidationException.php',
|
'PhabricatorApplicationTransactionValidationException' => 'applications/transactions/exception/PhabricatorApplicationTransactionValidationException.php',
|
||||||
'PhabricatorApplicationTransactionView' => 'applications/transactions/view/PhabricatorApplicationTransactionView.php',
|
'PhabricatorApplicationTransactionView' => 'applications/transactions/view/PhabricatorApplicationTransactionView.php',
|
||||||
'PhabricatorApplicationTransactions' => 'applications/transactions/application/PhabricatorApplicationTransactions.php',
|
'PhabricatorApplicationTransactions' => 'applications/transactions/application/PhabricatorApplicationTransactions.php',
|
||||||
|
'PhabricatorApplicationTypeahead' => 'applications/typeahead/application/PhabricatorApplicationTypeahead.php',
|
||||||
'PhabricatorApplicationUIExamples' => 'applications/uiexample/application/PhabricatorApplicationUIExamples.php',
|
'PhabricatorApplicationUIExamples' => 'applications/uiexample/application/PhabricatorApplicationUIExamples.php',
|
||||||
'PhabricatorApplicationUninstallController' => 'applications/meta/controller/PhabricatorApplicationUninstallController.php',
|
'PhabricatorApplicationUninstallController' => 'applications/meta/controller/PhabricatorApplicationUninstallController.php',
|
||||||
'PhabricatorApplicationXHProf' => 'applications/xhprof/application/PhabricatorApplicationXHProf.php',
|
'PhabricatorApplicationXHProf' => 'applications/xhprof/application/PhabricatorApplicationXHProf.php',
|
||||||
|
@ -2120,8 +2121,13 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorTrivialTestCase' => 'infrastructure/testing/__tests__/PhabricatorTrivialTestCase.php',
|
'PhabricatorTrivialTestCase' => 'infrastructure/testing/__tests__/PhabricatorTrivialTestCase.php',
|
||||||
'PhabricatorTwoColumnExample' => 'applications/uiexample/examples/PhabricatorTwoColumnExample.php',
|
'PhabricatorTwoColumnExample' => 'applications/uiexample/examples/PhabricatorTwoColumnExample.php',
|
||||||
'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadCommonDatasourceController.php',
|
'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadCommonDatasourceController.php',
|
||||||
|
'PhabricatorTypeaheadCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php',
|
||||||
|
'PhabricatorTypeaheadDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php',
|
||||||
'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadDatasourceController.php',
|
'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadDatasourceController.php',
|
||||||
|
'PhabricatorTypeaheadModularDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php',
|
||||||
|
'PhabricatorTypeaheadMonogramDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php',
|
||||||
'PhabricatorTypeaheadResult' => 'applications/typeahead/storage/PhabricatorTypeaheadResult.php',
|
'PhabricatorTypeaheadResult' => 'applications/typeahead/storage/PhabricatorTypeaheadResult.php',
|
||||||
|
'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadRuntimeCompositeDatasource.php',
|
||||||
'PhabricatorUIConfigOptions' => 'applications/config/option/PhabricatorUIConfigOptions.php',
|
'PhabricatorUIConfigOptions' => 'applications/config/option/PhabricatorUIConfigOptions.php',
|
||||||
'PhabricatorUIExample' => 'applications/uiexample/examples/PhabricatorUIExample.php',
|
'PhabricatorUIExample' => 'applications/uiexample/examples/PhabricatorUIExample.php',
|
||||||
'PhabricatorUIExampleRenderController' => 'applications/uiexample/controller/PhabricatorUIExampleRenderController.php',
|
'PhabricatorUIExampleRenderController' => 'applications/uiexample/controller/PhabricatorUIExampleRenderController.php',
|
||||||
|
@ -3801,6 +3807,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorApplicationTransactionValidationException' => 'Exception',
|
'PhabricatorApplicationTransactionValidationException' => 'Exception',
|
||||||
'PhabricatorApplicationTransactionView' => 'AphrontView',
|
'PhabricatorApplicationTransactionView' => 'AphrontView',
|
||||||
'PhabricatorApplicationTransactions' => 'PhabricatorApplication',
|
'PhabricatorApplicationTransactions' => 'PhabricatorApplication',
|
||||||
|
'PhabricatorApplicationTypeahead' => 'PhabricatorApplication',
|
||||||
'PhabricatorApplicationUIExamples' => 'PhabricatorApplication',
|
'PhabricatorApplicationUIExamples' => 'PhabricatorApplication',
|
||||||
'PhabricatorApplicationUninstallController' => 'PhabricatorApplicationsController',
|
'PhabricatorApplicationUninstallController' => 'PhabricatorApplicationsController',
|
||||||
'PhabricatorApplicationXHProf' => 'PhabricatorApplication',
|
'PhabricatorApplicationXHProf' => 'PhabricatorApplication',
|
||||||
|
@ -4896,7 +4903,12 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorTrivialTestCase' => 'PhabricatorTestCase',
|
'PhabricatorTrivialTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorTwoColumnExample' => 'PhabricatorUIExample',
|
'PhabricatorTwoColumnExample' => 'PhabricatorUIExample',
|
||||||
'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
|
'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
|
||||||
|
'PhabricatorTypeaheadCompositeDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
|
'PhabricatorTypeaheadDatasource' => 'Phobject',
|
||||||
'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
|
'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
|
||||||
|
'PhabricatorTypeaheadModularDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
|
||||||
|
'PhabricatorTypeaheadMonogramDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
|
'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
'PhabricatorUIConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorUIConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
'PhabricatorUIExampleRenderController' => 'PhabricatorController',
|
'PhabricatorUIExampleRenderController' => 'PhabricatorController',
|
||||||
'PhabricatorUIListFilterExample' => 'PhabricatorUIExample',
|
'PhabricatorUIListFilterExample' => 'PhabricatorUIExample',
|
||||||
|
|
|
@ -16,11 +16,6 @@ class AphrontDefaultApplicationConfiguration
|
||||||
|
|
||||||
public function getURIMap() {
|
public function getURIMap() {
|
||||||
return $this->getResourceURIMapRules() + array(
|
return $this->getResourceURIMapRules() + array(
|
||||||
'/typeahead/' => array(
|
|
||||||
'common/(?P<type>\w+)/'
|
|
||||||
=> 'PhabricatorTypeaheadCommonDatasourceController',
|
|
||||||
),
|
|
||||||
|
|
||||||
'/oauthserver/' => array(
|
'/oauthserver/' => array(
|
||||||
'auth/' => 'PhabricatorOAuthServerAuthController',
|
'auth/' => 'PhabricatorOAuthServerAuthController',
|
||||||
'test/' => 'PhabricatorOAuthServerTestController',
|
'test/' => 'PhabricatorOAuthServerTestController',
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorApplicationTypeahead extends PhabricatorApplication {
|
||||||
|
|
||||||
|
public function getRoutes() {
|
||||||
|
return array(
|
||||||
|
'/typeahead/' => array(
|
||||||
|
'common/(?P<type>\w+)/'
|
||||||
|
=> 'PhabricatorTypeaheadCommonDatasourceController',
|
||||||
|
'class/(?:(?P<class>\w+)/)?'
|
||||||
|
=> 'PhabricatorTypeaheadModularDatasourceController',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldAppearInLaunchView() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canUninstall() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,7 +14,6 @@ final class PhabricatorTypeaheadCommonDatasourceController
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processRequest() {
|
public function processRequest() {
|
||||||
|
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$viewer = $request->getUser();
|
$viewer = $request->getUser();
|
||||||
$query = $request->getStr('q');
|
$query = $request->getStr('q');
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorTypeaheadModularDatasourceController
|
||||||
|
extends PhabricatorTypeaheadDatasourceController {
|
||||||
|
|
||||||
|
private $class;
|
||||||
|
|
||||||
|
public function shouldAllowPublic() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->class = idx($data, 'class');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$viewer = $request->getUser();
|
||||||
|
$query = $request->getStr('q');
|
||||||
|
|
||||||
|
// Default this to the query string to make debugging a little bit easier.
|
||||||
|
$raw_query = nonempty($request->getStr('raw'), $query);
|
||||||
|
|
||||||
|
// This makes form submission easier in the debug view.
|
||||||
|
$this->class = nonempty($request->getStr('class'), $this->class);
|
||||||
|
|
||||||
|
$sources = id(new PhutilSymbolLoader())
|
||||||
|
->setAncestorClass('PhabricatorTypeaheadDatasource')
|
||||||
|
->loadObjects();
|
||||||
|
|
||||||
|
if (isset($sources[$this->class])) {
|
||||||
|
$source = $sources[$this->class];
|
||||||
|
|
||||||
|
$composite = new PhabricatorTypeaheadRuntimeCompositeDatasource();
|
||||||
|
$composite->addDatasource($source);
|
||||||
|
|
||||||
|
$composite
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setQuery($query)
|
||||||
|
->setRawQuery($raw_query);
|
||||||
|
|
||||||
|
$results = $composite->loadResults();
|
||||||
|
} else {
|
||||||
|
$results = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = mpull($results, 'getWireFormat');
|
||||||
|
|
||||||
|
if ($request->isAjax()) {
|
||||||
|
return id(new AphrontAjaxResponse())->setContent($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's a non-Ajax request to this endpoint, show results in a tabular
|
||||||
|
// format to make it easier to debug typeahead output.
|
||||||
|
|
||||||
|
$options = array_fuse(array_keys($sources));
|
||||||
|
asort($options);
|
||||||
|
|
||||||
|
$form = id(new AphrontFormView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setAction('/typeahead/class/')
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSelectControl())
|
||||||
|
->setLabel(pht('Source Class'))
|
||||||
|
->setName('class')
|
||||||
|
->setValue($this->class)
|
||||||
|
->setOptions($options))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormTextControl())
|
||||||
|
->setLabel(pht('Query'))
|
||||||
|
->setName('q')
|
||||||
|
->setValue($request->getStr('q')))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormTextControl())
|
||||||
|
->setLabel(pht('Raw Query'))
|
||||||
|
->setName('raw')
|
||||||
|
->setValue($request->getStr('raw')))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSubmitControl())
|
||||||
|
->setValue(pht('Query')));
|
||||||
|
|
||||||
|
$form_box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Token Query'))
|
||||||
|
->setForm($form);
|
||||||
|
|
||||||
|
$table = new AphrontTableView($content);
|
||||||
|
$table->setHeaders(
|
||||||
|
array(
|
||||||
|
'Name',
|
||||||
|
'URI',
|
||||||
|
'PHID',
|
||||||
|
'Priority',
|
||||||
|
'Display Name',
|
||||||
|
'Display Type',
|
||||||
|
'Image URI',
|
||||||
|
'Priority Type',
|
||||||
|
));
|
||||||
|
|
||||||
|
$result_box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Token Results (%s)', $this->class))
|
||||||
|
->appendChild($table);
|
||||||
|
|
||||||
|
return $this->buildApplicationPage(
|
||||||
|
array(
|
||||||
|
$form_box,
|
||||||
|
$result_box,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => pht('Typeahead Results'),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorTypeaheadCompositeDatasource
|
||||||
|
extends PhabricatorTypeaheadDatasource {
|
||||||
|
|
||||||
|
abstract public function getComponentDatasources();
|
||||||
|
|
||||||
|
public function getDatasourceApplicationClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadResults() {
|
||||||
|
$results = array();
|
||||||
|
foreach ($this->getUsableDatasources() as $source) {
|
||||||
|
$source
|
||||||
|
->setRawQuery($this->getRawQuery())
|
||||||
|
->setQuery($this->getQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->setLimit($this->getLimit());
|
||||||
|
|
||||||
|
$results[] = $source->loadResults();
|
||||||
|
}
|
||||||
|
return array_mergev($results);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getUsableDatasources() {
|
||||||
|
$sources = $this->getComponentDatasources();
|
||||||
|
|
||||||
|
$usable = array();
|
||||||
|
foreach ($sources as $source) {
|
||||||
|
$application_class = $source->getDatasourceApplicationClass();
|
||||||
|
|
||||||
|
if ($application_class) {
|
||||||
|
$result = id(new PhabricatorApplicationQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withClasses(array($application_class))
|
||||||
|
->execute();
|
||||||
|
if (!$result) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$usable[] = $source;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $usable;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
||||||
|
|
||||||
|
private $viewer;
|
||||||
|
private $query;
|
||||||
|
private $rawQuery;
|
||||||
|
private $limit;
|
||||||
|
|
||||||
|
public function setLimit($limit) {
|
||||||
|
$this->limit = $limit;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLimit() {
|
||||||
|
return $this->limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setViewer(PhabricatorUser $viewer) {
|
||||||
|
$this->viewer = $viewer;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getViewer() {
|
||||||
|
return $this->viewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRawQuery($raw_query) {
|
||||||
|
$this->rawQuery = $raw_query;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRawQuery() {
|
||||||
|
return $this->rawQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setQuery($query) {
|
||||||
|
$this->query = $query;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuery() {
|
||||||
|
return $this->query;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function getPlaceholderText();
|
||||||
|
abstract public function getDatasourceApplicationClass();
|
||||||
|
abstract public function loadResults();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorTypeaheadMonogramDatasource
|
||||||
|
extends PhabricatorTypeaheadDatasource {
|
||||||
|
|
||||||
|
public function getPlaceholderText() {
|
||||||
|
return pht('Type an object name...');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasourceApplicationClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadResults() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$raw_query = $this->getRawQuery();
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
|
||||||
|
$objects = id(new PhabricatorObjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withNames(array($raw_query))
|
||||||
|
->execute();
|
||||||
|
if ($objects) {
|
||||||
|
$handles = id(new PhabricatorHandleQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(mpull($objects, 'getPHID'))
|
||||||
|
->execute();
|
||||||
|
$handle = head($handles);
|
||||||
|
if ($handle) {
|
||||||
|
$results[] = id(new PhabricatorTypeaheadResult())
|
||||||
|
->setName($handle->getFullName())
|
||||||
|
->setDisplayType($handle->getTypeName())
|
||||||
|
->setURI($handle->getURI())
|
||||||
|
->setPHID($handle->getPHID())
|
||||||
|
->setPriorityType('jump');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorTypeaheadRuntimeCompositeDatasource
|
||||||
|
extends PhabricatorTypeaheadCompositeDatasource {
|
||||||
|
|
||||||
|
private $datasources = array();
|
||||||
|
|
||||||
|
public function getComponentDatasources() {
|
||||||
|
return $this->datasources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceholderText() {
|
||||||
|
throw new Exception(pht('This source is not usable directly.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addDatasource(PhabricatorTypeaheadDatasource $source) {
|
||||||
|
$this->datasources[] = $source;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue