Page MenuHomePhorge

No OneTemporary

diff --git a/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php b/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php
index 3609cb06f6..73ae32938b 100644
--- a/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php
+++ b/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php
@@ -1,51 +1,57 @@
<?php
final class AlmanacInterfaceDatasource
extends PhabricatorTypeaheadDatasource {
+ public function isBrowsable() {
+ // TODO: We should make this browsable, but need to make the result set
+ // orderable by device name.
+ return false;
+ }
+
public function getPlaceholderText() {
return pht('Type an interface name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$devices = id(new AlmanacDeviceQuery())
->setViewer($viewer)
->withNamePrefix($raw_query)
->execute();
if ($devices) {
$interfaces = id(new AlmanacInterfaceQuery())
->setViewer($viewer)
->withDevicePHIDs(mpull($devices, 'getPHID'))
->execute();
} else {
$interfaces = array();
}
if ($interfaces) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs(mpull($interfaces, 'getPHID'))
->execute();
} else {
$handles = array();
}
$results = array();
foreach ($handles as $handle) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName($handle->getName())
->setPHID($handle->getPHID());
}
return $results;
}
}
diff --git a/src/applications/diffusion/typeahead/DiffusionArcanistProjectDatasource.php b/src/applications/diffusion/typeahead/DiffusionArcanistProjectDatasource.php
index 963c0b2ca9..ce77b56f9b 100644
--- a/src/applications/diffusion/typeahead/DiffusionArcanistProjectDatasource.php
+++ b/src/applications/diffusion/typeahead/DiffusionArcanistProjectDatasource.php
@@ -1,30 +1,35 @@
<?php
final class DiffusionArcanistProjectDatasource
extends PhabricatorTypeaheadDatasource {
+ public function isBrowsable() {
+ // TODO: We should probably make this browsable, or maybe remove it.
+ return false;
+ }
+
public function getPlaceholderText() {
return pht('Type an arcanist project name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorDiffusionApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
$arcprojs = id(new PhabricatorRepositoryArcanistProject())->loadAll();
foreach ($arcprojs as $proj) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName($proj->getName())
->setPHID($proj->getPHID());
}
return $results;
}
}
diff --git a/src/applications/diffusion/typeahead/DiffusionSymbolDatasource.php b/src/applications/diffusion/typeahead/DiffusionSymbolDatasource.php
index 97ab83f44d..0203ff2971 100644
--- a/src/applications/diffusion/typeahead/DiffusionSymbolDatasource.php
+++ b/src/applications/diffusion/typeahead/DiffusionSymbolDatasource.php
@@ -1,48 +1,54 @@
<?php
final class DiffusionSymbolDatasource
extends PhabricatorTypeaheadDatasource {
+ public function isBrowsable() {
+ // This is slightly involved to make browsable, and browsing symbols
+ // does not seem likely to be very useful in any real software project.
+ return false;
+ }
+
public function getPlaceholderText() {
return pht('Type a symbol name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorDiffusionApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
if (strlen($raw_query)) {
$symbols = id(new DiffusionSymbolQuery())
->setViewer($viewer)
->setNamePrefix($raw_query)
->setLimit(15)
->needArcanistProjects(true)
->needRepositories(true)
->needPaths(true)
->execute();
foreach ($symbols as $symbol) {
$lang = $symbol->getSymbolLanguage();
$name = $symbol->getSymbolName();
$type = $symbol->getSymbolType();
$proj = $symbol->getArcanistProject()->getName();
$results[] = id(new PhabricatorTypeaheadResult())
->setName($name)
->setURI($symbol->getURI())
->setPHID(md5($symbol->getURI())) // Just needs to be unique.
->setDisplayName($name)
->setDisplayType(strtoupper($lang).' '.ucwords($type).' ('.$proj.')')
->setPriorityType('symb');
}
}
return $results;
}
}
diff --git a/src/applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php b/src/applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php
index c1228c9446..997e129bb3 100644
--- a/src/applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php
+++ b/src/applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php
@@ -1,45 +1,50 @@
<?php
final class HarbormasterBuildDependencyDatasource
extends PhabricatorTypeaheadDatasource {
+ public function isBrowsable() {
+ // TODO: This should be browsable, but fixing it is involved.
+ return false;
+ }
+
public function getPlaceholderText() {
return pht('Type another build step name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorHarbormasterApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$plan_phid = $this->getParameter('planPHID');
$step_phid = $this->getParameter('stepPHID');
$steps = id(new HarbormasterBuildStepQuery())
->setViewer($viewer)
->withBuildPlanPHIDs(array($plan_phid))
->execute();
$steps = mpull($steps, null, 'getPHID');
if (count($steps) === 0) {
return array();
}
$results = array();
foreach ($steps as $phid => $step) {
if ($step->getPHID() === $step_phid) {
continue;
}
$results[] = id(new PhabricatorTypeaheadResult())
->setName($step->getName())
->setURI('/')
->setPHID($phid);
}
return $results;
}
}
diff --git a/src/applications/legalpad/typeahead/LegalpadDocumentDatasource.php b/src/applications/legalpad/typeahead/LegalpadDocumentDatasource.php
index 853d775184..a536843d0b 100644
--- a/src/applications/legalpad/typeahead/LegalpadDocumentDatasource.php
+++ b/src/applications/legalpad/typeahead/LegalpadDocumentDatasource.php
@@ -1,31 +1,36 @@
<?php
final class LegalpadDocumentDatasource extends PhabricatorTypeaheadDatasource {
+ public function isBrowsable() {
+ // TODO: This should be made browsable.
+ return false;
+ }
+
public function getPlaceholderText() {
return pht('Type a document name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorLegalpadApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
$documents = id(new LegalpadDocumentQuery())
->setViewer($viewer)
->execute();
foreach ($documents as $document) {
$results[] = id(new PhabricatorTypeaheadResult())
->setPHID($document->getPHID())
->setName($document->getMonogram().' '.$document->getTitle());
}
return $results;
}
}
diff --git a/src/applications/macro/typeahead/PhabricatorMacroDatasource.php b/src/applications/macro/typeahead/PhabricatorMacroDatasource.php
index 339be08fa7..4b8d52b4d4 100644
--- a/src/applications/macro/typeahead/PhabricatorMacroDatasource.php
+++ b/src/applications/macro/typeahead/PhabricatorMacroDatasource.php
@@ -1,33 +1,38 @@
<?php
final class PhabricatorMacroDatasource extends PhabricatorTypeaheadDatasource {
+ public function isBrowsable() {
+ // TODO: This should be made browsable.
+ return false;
+ }
+
public function getPlaceholderText() {
return pht('Type a macro name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorMacroApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
$macros = id(new PhabricatorMacroQuery())
->setViewer($viewer)
->withStatus(PhabricatorMacroQuery::STATUS_ACTIVE)
->execute();
foreach ($macros as $macro) {
$results[] = id(new PhabricatorTypeaheadResult())
->setPHID($macro->getPHID())
->setName($macro->getName());
}
return $results;
}
}
diff --git a/src/applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php b/src/applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php
index 53f7d82e0e..c08da2a30d 100644
--- a/src/applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php
+++ b/src/applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php
@@ -1,33 +1,38 @@
<?php
final class PhabricatorMailingListDatasource
extends PhabricatorTypeaheadDatasource {
+ public function isBrowsable() {
+ // TODO: Make this browsable if we don't delete it before then.
+ return false;
+ }
+
public function getPlaceholderText() {
return pht('Type a mailing list name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorMailingListsApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
$lists = id(new PhabricatorMailingListQuery())
->setViewer($viewer)
->execute();
foreach ($lists as $list) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName($list->getName())
->setURI($list->getURI())
->setPHID($list->getPHID());
}
return $results;
}
}
diff --git a/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php
index 172caea1a6..568aa0636d 100644
--- a/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php
+++ b/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php
@@ -1,31 +1,31 @@
<?php
final class ManiphestTaskPriorityDatasource
extends PhabricatorTypeaheadDatasource {
public function getPlaceholderText() {
return pht('Type a task priority name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorManiphestApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
$priority_map = ManiphestTaskPriority::getTaskPriorityMap();
foreach ($priority_map as $value => $name) {
// NOTE: $value is not a PHID but is unique. This'll work.
$results[] = id(new PhabricatorTypeaheadResult())
->setPHID($value)
->setName($name);
}
- return $results;
+ return $this->filterResultsAgainstTokens($results);
}
}
diff --git a/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php
index e7eba2923c..27b3f90386 100644
--- a/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php
+++ b/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php
@@ -1,30 +1,30 @@
<?php
final class ManiphestTaskStatusDatasource
extends PhabricatorTypeaheadDatasource {
public function getPlaceholderText() {
return pht('Type a task status name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorManiphestApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
$status_map = ManiphestTaskStatus::getTaskStatusMap();
foreach ($status_map as $value => $name) {
// NOTE: $value is not a PHID but is unique. This'll work.
$results[] = id(new PhabricatorTypeaheadResult())
->setPHID($value)
->setName($name);
}
- return $results;
+ return $this->filterResultsAgainstTokens($results);
}
}
diff --git a/src/applications/meta/typeahead/PhabricatorApplicationDatasource.php b/src/applications/meta/typeahead/PhabricatorApplicationDatasource.php
index 19b40f727a..18b6683dfc 100644
--- a/src/applications/meta/typeahead/PhabricatorApplicationDatasource.php
+++ b/src/applications/meta/typeahead/PhabricatorApplicationDatasource.php
@@ -1,43 +1,43 @@
<?php
final class PhabricatorApplicationDatasource
extends PhabricatorTypeaheadDatasource {
public function getPlaceholderText() {
return pht('Type an application name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorApplicationsApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
$applications = PhabricatorApplication::getAllInstalledApplications();
foreach ($applications as $application) {
$uri = $application->getTypeaheadURI();
if (!$uri) {
continue;
}
$name = $application->getName().' '.$application->getShortDescription();
$img = 'phui-font-fa phui-icon-view '.$application->getFontIcon();
$results[] = id(new PhabricatorTypeaheadResult())
->setName($name)
->setURI($uri)
->setPHID($application->getPHID())
->setPriorityString($application->getName())
->setDisplayName($application->getName())
->setDisplayType($application->getShortDescription())
->setImageuRI($application->getIconURI())
->setPriorityType('apps')
->setImageSprite('phabricator-search-icon '.$img);
}
- return $results;
+ return $this->filterResultsAgainstTokens($results);
}
}
diff --git a/src/applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php b/src/applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php
index bbe5e0d8f6..887aecfd80 100644
--- a/src/applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php
+++ b/src/applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php
@@ -1,43 +1,48 @@
<?php
final class PhabricatorMetaMTAApplicationEmailDatasource
extends PhabricatorTypeaheadDatasource {
+ public function isBrowsable() {
+ // TODO: Make this browsable.
+ return false;
+ }
+
public function getPlaceholderText() {
return pht('Type an application email address...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorMetaMTAApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$emails = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer($viewer)
->withAddressPrefix($raw_query)
->setLimit($this->getLimit())
->execute();
if ($emails) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs(mpull($emails, 'getPHID'))
->execute();
} else {
$handles = array();
}
$results = array();
foreach ($handles as $handle) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName($handle->getName())
->setPHID($handle->getPHID());
}
return $results;
}
}
diff --git a/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php b/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php
index c0a6c92589..a8658549ec 100644
--- a/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php
+++ b/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php
@@ -1,34 +1,39 @@
<?php
final class PhabricatorOwnersPackageDatasource
extends PhabricatorTypeaheadDatasource {
+ public function isBrowsable() {
+ // TODO: Make this browsable.
+ return false;
+ }
+
public function getPlaceholderText() {
return pht('Type a package name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorOwnersApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
$packages = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->execute();
foreach ($packages as $package) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName($package->getName())
->setURI('/owners/package/'.$package->getID().'/')
->setPHID($package->getPHID());
}
return $results;
}
}
diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php
index e2a6de0e43..2672e7d79a 100644
--- a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php
+++ b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php
@@ -1,250 +1,255 @@
<?php
final class PhabricatorTypeaheadModularDatasourceController
extends PhabricatorTypeaheadDatasourceController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$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);
// This makes form submission easier in the debug view.
$class = nonempty($request->getURIData('class'), $request->getStr('class'));
$sources = id(new PhutilSymbolLoader())
->setAncestorClass('PhabricatorTypeaheadDatasource')
->loadObjects();
if (isset($sources[$class])) {
$source = $sources[$class];
$source->setParameters($request->getRequestData());
+ $source->setViewer($viewer);
// 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);
$composite
->setViewer($viewer)
->setQuery($query)
->setRawQuery($raw_query);
$hard_limit = 1000;
if ($is_browse) {
+ if (!$composite->isBrowsable()) {
+ return new Aphront404Response();
+ }
+
$limit = 10;
$offset = $request->getInt('offset');
if (($offset + $limit) >= $hard_limit) {
// Offset-based paging is intrinsically slow; hard-cap how far we're
// willing to go with it.
return new Aphront404Response();
}
$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);
if (($offset + (2 * $limit)) < $hard_limit) {
$next_uri = id(new PhutilURI($request->getRequestURI()))
->setQueryParam('offset', $offset + $limit);
$next_link = javelin_tag(
'a',
array(
'href' => $next_uri,
'class' => 'typeahead-browse-more',
'sigil' => 'typeahead-browse-more',
'mustcapture' => true,
),
pht('More Results'));
} else {
// If the user has paged through more than 1K results, don't
// offer to page any further.
$next_link = javelin_tag(
'div',
array(
'class' => 'typeahead-browse-hard-limit',
),
pht('You reach the edge of the abyss.'));
}
}
$items = array();
foreach ($results as $result) {
$token = PhabricatorTypeaheadTokenView::newForTypeaheadResult(
$result);
$items[] = phutil_tag(
'div',
array(
'class' => 'grouped',
),
$token);
}
$markup = array(
$items,
$next_link,
);
if ($request->isAjax()) {
$content = array(
'markup' => hsprintf('%s', $markup),
);
return id(new AphrontAjaxResponse())->setContent($content);
}
$this->requireResource('typeahead-browse-css');
$this->initBehavior('typeahead-browse');
$input_id = celerity_generate_unique_node_id();
$frame_id = celerity_generate_unique_node_id();
$config = array(
'inputID' => $input_id,
'frameID' => $frame_id,
'uri' => (string)$request->getRequestURI(),
);
$this->initBehavior('typeahead-search', $config);
$search = javelin_tag(
'input',
array(
'type' => 'text',
'id' => $input_id,
'class' => 'typeahead-browse-input',
'autocomplete' => 'off',
'placeholder' => $source->getPlaceholderText(),
));
$frame = phutil_tag(
'div',
array(
'class' => 'typeahead-browse-frame',
'id' => $frame_id,
),
$markup);
$browser = array(
phutil_tag(
'div',
array(
'class' => 'typeahead-browse-header',
),
$search),
$frame,
);
return $this->newDialog()
->setWidth(AphrontDialogView::WIDTH_FORM)
->setRenderDialogAsDiv(true)
->setTitle(get_class($source)) // TODO: Provide nice names.
->appendChild($browser)
->addCancelButton('/', pht('Close'));
}
} else if ($is_browse) {
return new Aphront404Response();
} 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.
foreach ($sources as $key => $source) {
// This can happen with composite sources like user or project, as well
// generic ones like NoOwner
if (!$source->getDatasourceApplicationClass()) {
continue;
}
if (!PhabricatorApplication::isClassInstalledForViewer(
$source->getDatasourceApplicationClass(),
$viewer)) {
unset($sources[$key]);
}
}
$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($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(
pht('Name'),
pht('URI'),
pht('PHID'),
pht('Priority'),
pht('Display Name'),
pht('Display Type'),
pht('Image URI'),
pht('Priority Type'),
pht('Icon'),
pht('Closed'),
pht('Sprite'),
));
$result_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Token Results (%s)', $class))
->appendChild($table);
return $this->buildApplicationPage(
array(
$form_box,
$result_box,
),
array(
'title' => pht('Typeahead Results'),
'device' => false,
));
}
}
diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php
index b1f55190c9..334d9354de 100644
--- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php
+++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php
@@ -1,68 +1,83 @@
<?php
abstract class PhabricatorTypeaheadCompositeDatasource
extends PhabricatorTypeaheadDatasource {
+ private $usable;
+
abstract public function getComponentDatasources();
+ public function isBrowsable() {
+ foreach ($this->getUsableDatasources() as $datasource) {
+ if (!$datasource->isBrowsable()) {
+ return false;
+ }
+ }
+
+ return parent::isBrowsable();
+ }
+
public function getDatasourceApplicationClass() {
return null;
}
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());
if ($limit) {
$source->setLimit($offset + $limit);
}
$results[] = $source->loadResults();
}
$results = array_mergev($results);
$results = msort($results, 'getSortKey');
$count = count($results);
if ($offset || $limit) {
if (!$limit) {
$limit = count($results);
}
$results = array_slice($results, $offset, $limit, $preserve_keys = true);
}
return $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;
+ if ($this->usable === null) {
+ $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;
+ $usable[] = $source;
+ }
+ $this->usable = $usable;
}
- return $usable;
+ return $this->usable;
}
}
diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php
index 3d6d2b75f2..a15a248e23 100644
--- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php
+++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php
@@ -1,105 +1,176 @@
<?php
abstract class PhabricatorTypeaheadDatasource extends Phobject {
private $viewer;
private $query;
private $rawQuery;
private $offset;
private $limit;
private $parameters = array();
public function setLimit($limit) {
$this->limit = $limit;
return $this;
}
public function getLimit() {
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;
}
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;
}
public function setParameters(array $params) {
$this->parameters = $params;
return $this;
}
public function getParameters() {
return $this->parameters;
}
public function getParameter($name, $default = null) {
return idx($this->parameters, $name, $default);
}
public function getDatasourceURI() {
$uri = new PhutilURI('/typeahead/class/'.get_class($this).'/');
$uri->setQueryParams($this->parameters);
return (string)$uri;
}
abstract public function getPlaceholderText();
abstract public function getDatasourceApplicationClass();
abstract public function loadResults();
public static function tokenizeString($string) {
$string = phutil_utf8_strtolower($string);
$string = trim($string);
if (!strlen($string)) {
return array();
}
$tokens = preg_split('/\s+|[-\[\]]/', $string);
return array_unique($tokens);
}
public function getTokens() {
return self::tokenizeString($this->getRawQuery());
}
protected function executeQuery(
PhabricatorCursorPagedPolicyAwareQuery $query) {
return $query
->setViewer($this->getViewer())
->setOffset($this->getOffset())
->setLimit($this->getLimit())
->execute();
}
+
+ /**
+ * Can the user browse through results from this datasource?
+ *
+ * Browsable datasources allow the user to switch from typeahead mode to
+ * a browse mode where they can scroll through all results.
+ *
+ * By default, datasources are browsable, but some datasources can not
+ * generate a meaningful result set or can't filter results on the server.
+ *
+ * @return bool
+ */
+ public function isBrowsable() {
+ return true;
+ }
+
+
+ /**
+ * Filter a list of results, removing items which don't match the query
+ * tokens.
+ *
+ * This is useful for datasources which return a static list of hard-coded
+ * or configured results and can't easily do query filtering in a real
+ * query class. Instead, they can just build the entire result set and use
+ * this method to filter it.
+ *
+ * For datasources backed by database objects, this is often much less
+ * efficient than filtering at the query level.
+ *
+ * @param list<PhabricatorTypeaheadResult> List of typeahead results.
+ * @return list<PhabricatorTypeaheadResult> Filtered results.
+ */
+ protected function filterResultsAgainstTokens(array $results) {
+ $tokens = $this->getTokens();
+ if (!$tokens) {
+ return $results;
+ }
+
+ $map = array();
+ foreach ($tokens as $token) {
+ $map[$token] = strlen($token);
+ }
+
+ foreach ($results as $key => $result) {
+ $rtokens = self::tokenizeString($result->getName());
+
+ // For each token in the query, we need to find a match somewhere
+ // in the result name.
+ foreach ($map as $token => $length) {
+ // Look for a match.
+ $match = false;
+ foreach ($rtokens as $rtoken) {
+ if (!strncmp($rtoken, $token, $length)) {
+ // This part of the result name has the query token as a prefix.
+ $match = true;
+ break;
+ }
+ }
+
+ if (!$match) {
+ // We didn't find a match for this query token, so throw the result
+ // away. Try with the next result.
+ unset($results[$key]);
+ break;
+ }
+ }
+ }
+
+ return $results;
+ }
+
}
diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php
index 267976b773..ebaf3d39d8 100644
--- a/src/applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php
+++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php
@@ -1,43 +1,50 @@
<?php
final class PhabricatorTypeaheadMonogramDatasource
extends PhabricatorTypeaheadDatasource {
+ public function isBrowsable() {
+ // This source isn't meaningfully browsable. Although it's technically
+ // possible to let users browse through every object on an install, there
+ // is no use case for it and it doesn't seem worth building.
+ return false;
+ }
+
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;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 16:32 (2 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1126505
Default Alt Text
(34 KB)

Event Timeline