Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2892269
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
34 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment