Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/search/engine/PhabricatorDatasourceEngine.php b/src/applications/search/engine/PhabricatorDatasourceEngine.php
index 62304d26aa..a9cb766242 100644
--- a/src/applications/search/engine/PhabricatorDatasourceEngine.php
+++ b/src/applications/search/engine/PhabricatorDatasourceEngine.php
@@ -1,37 +1,55 @@
<?php
final class PhabricatorDatasourceEngine extends Phobject {
private $viewer;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function getAllQuickSearchDatasources() {
return PhabricatorDatasourceEngineExtension::getAllQuickSearchDatasources();
}
public function newJumpURI($query) {
$viewer = $this->getViewer();
$extensions = PhabricatorDatasourceEngineExtension::getAllExtensions();
foreach ($extensions as $extension) {
$jump_uri = id(clone $extension)
->setViewer($viewer)
->newJumpURI($query);
if ($jump_uri !== null) {
return $jump_uri;
}
}
return null;
}
+ public function newDatasourcesForCompositeDatasource(
+ PhabricatorTypeaheadCompositeDatasource $datasource) {
+ $viewer = $this->getViewer();
+ $extensions = PhabricatorDatasourceEngineExtension::getAllExtensions();
+
+ $sources = array();
+ foreach ($extensions as $extension) {
+ $extension_sources = id(clone $extension)
+ ->setViewer($viewer)
+ ->newDatasourcesForCompositeDatasource($datasource);
+ foreach ($extension_sources as $extension_source) {
+ $sources[] = $extension_source;
+ }
+ }
+
+ return $sources;
+ }
+
}
diff --git a/src/applications/search/engineextension/PhabricatorDatasourceEngineExtension.php b/src/applications/search/engineextension/PhabricatorDatasourceEngineExtension.php
index be015526da..c568e9e14d 100644
--- a/src/applications/search/engineextension/PhabricatorDatasourceEngineExtension.php
+++ b/src/applications/search/engineextension/PhabricatorDatasourceEngineExtension.php
@@ -1,40 +1,45 @@
<?php
abstract class PhabricatorDatasourceEngineExtension extends Phobject {
private $viewer;
final public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
final public function getViewer() {
return $this->viewer;
}
public function newQuickSearchDatasources() {
return array();
}
public function newJumpURI($query) {
return null;
}
+ public function newDatasourcesForCompositeDatasource(
+ PhabricatorTypeaheadCompositeDatasource $datasource) {
+ return array();
+ }
+
final public static function getAllExtensions() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->execute();
}
final public static function getAllQuickSearchDatasources() {
$extensions = self::getAllExtensions();
$datasources = array();
foreach ($extensions as $extension) {
$datasources[] = $extension->newQuickSearchDatasources();
}
return array_mergev($datasources);
}
}
diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php
index 33f06e4ae5..5594044c42 100644
--- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php
+++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php
@@ -1,320 +1,329 @@
<?php
abstract class PhabricatorTypeaheadCompositeDatasource
extends PhabricatorTypeaheadDatasource {
private $usable;
private $prefixString;
private $prefixLength;
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() {
$phases = array();
// We only need to do a prefix phase query if there's an actual query
// string. If the user didn't type anything, nothing can possibly match it.
if (strlen($this->getRawQuery())) {
$phases[] = self::PHASE_PREFIX;
}
$phases[] = self::PHASE_CONTENT;
$offset = $this->getOffset();
$limit = $this->getLimit();
$results = array();
foreach ($phases as $phase) {
if ($limit) {
$phase_limit = ($offset + $limit) - count($results);
} else {
$phase_limit = 0;
}
$phase_results = $this->loadResultsForPhase(
$phase,
$phase_limit);
foreach ($phase_results as $result) {
$results[] = $result;
}
if ($limit) {
if (count($results) >= $offset + $limit) {
break;
}
}
}
return $results;
}
protected function loadResultsForPhase($phase, $limit) {
if ($phase == self::PHASE_PREFIX) {
$this->prefixString = $this->getPrefixQuery();
$this->prefixLength = strlen($this->prefixString);
}
// If the input query is a function like `members(platy`, and we can
// parse the function, we strip the function off and hand the stripped
// query to child sources. This makes it easier to implement function
// sources in terms of real object sources.
$raw_query = $this->getRawQuery();
$is_function = false;
if (self::isFunctionToken($raw_query)) {
$is_function = true;
}
$stack = $this->getFunctionStack();
$is_browse = $this->getIsBrowse();
$results = array();
foreach ($this->getUsableDatasources() as $source) {
$source_stack = $stack;
$source_query = $raw_query;
if ($is_function) {
// If this source can't handle the function, skip it.
$function = $source->parseFunction($raw_query, $allow_partial = true);
if (!$function) {
continue;
}
// If this source handles the function directly, strip the function.
// Otherwise, this is something like a composite source which has
// some internal source which can evaluate the function, but will
// perform stripping later.
if ($source->shouldStripFunction($function['name'])) {
$source_query = head($function['argv']);
$source_stack[] = $function['name'];
}
}
$source
->setPhase($phase)
->setFunctionStack($source_stack)
->setRawQuery($source_query)
->setQuery($this->getQuery())
->setViewer($this->getViewer());
if ($is_browse) {
$source->setIsBrowse(true);
}
if ($limit) {
// If we are loading results from a source with a limit, it may return
// some results which belong to the wrong phase. We need an entire page
// of valid results in the correct phase AFTER any results for the
// wrong phase are filtered for pagination to work correctly.
// To make sure we can get there, we fetch more and more results until
// enough of them survive filtering to generate a full page.
// We start by fetching 150% of the results than we think we need, and
// double the amount we overfetch by each time.
$factor = 1.5;
while (true) {
$query_source = clone $source;
$total = (int)ceil($limit * $factor) + 1;
$query_source->setLimit($total);
$source_results = $query_source->loadResultsForPhase(
$phase,
$limit);
// If there are fewer unfiltered results than we asked for, we know
// this is the entire result set and we don't need to keep going.
if (count($source_results) < $total) {
$source_results = $query_source->didLoadResults($source_results);
$source_results = $this->filterPhaseResults(
$phase,
$source_results);
break;
}
// Otherwise, this result set have everything we need, or may not.
// Filter the results that are part of the wrong phase out first...
$source_results = $query_source->didLoadResults($source_results);
$source_results = $this->filterPhaseResults($phase, $source_results);
// Now check if we have enough results left. If we do, we're all set.
if (count($source_results) >= $total) {
break;
}
// We filtered out too many results to have a full page left, so we
// need to run the query again, asking for even more results. We'll
// keep doing this until we get a full page or get all of the
// results.
$factor = $factor * 2;
}
} else {
$source_results = $source->loadResults();
$source_results = $source->didLoadResults($source_results);
$source_results = $this->filterPhaseResults($phase, $source_results);
}
$results[] = $source_results;
}
$results = array_mergev($results);
$results = msort($results, 'getSortKey');
$results = $this->sliceResults($results);
return $results;
}
private function filterPhaseResults($phase, $source_results) {
foreach ($source_results as $key => $source_result) {
$result_phase = $this->getResultPhase($source_result);
if ($result_phase != $phase) {
unset($source_results[$key]);
continue;
}
$source_result->setPhase($result_phase);
}
return $source_results;
}
private function getResultPhase(PhabricatorTypeaheadResult $result) {
if ($this->prefixLength) {
$result_name = phutil_utf8_strtolower($result->getName());
if (!strncmp($result_name, $this->prefixString, $this->prefixLength)) {
return self::PHASE_PREFIX;
}
}
return self::PHASE_CONTENT;
}
protected function sliceResults(array $results) {
$offset = $this->getOffset();
$limit = $this->getLimit();
if ($offset || $limit) {
if (!$limit) {
$limit = count($results);
}
$results = array_slice($results, $offset, $limit, $preserve_keys = true);
}
return $results;
}
private function getUsableDatasources() {
if ($this->usable === null) {
+ $viewer = $this->getViewer();
+
$sources = $this->getComponentDatasources();
+ $extension_sources = id(new PhabricatorDatasourceEngine())
+ ->setViewer($viewer)
+ ->newDatasourcesForCompositeDatasource($this);
+ foreach ($extension_sources as $extension_source) {
+ $sources[] = $extension_source;
+ }
+
$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;
}
}
- $source->setViewer($this->getViewer());
+ $source->setViewer($viewer);
$usable[] = $source;
}
$this->usable = $usable;
}
return $this->usable;
}
public function getAllDatasourceFunctions() {
$results = parent::getAllDatasourceFunctions();
foreach ($this->getUsableDatasources() as $source) {
$results += $source->getAllDatasourceFunctions();
}
return $results;
}
protected function didEvaluateTokens(array $results) {
foreach ($this->getUsableDatasources() as $source) {
$results = $source->didEvaluateTokens($results);
}
return $results;
}
protected function canEvaluateFunction($function) {
foreach ($this->getUsableDatasources() as $source) {
if ($source->canEvaluateFunction($function)) {
return true;
}
}
return parent::canEvaluateFunction($function);
}
protected function evaluateValues(array $values) {
foreach ($this->getUsableDatasources() as $source) {
$values = $source->evaluateValues($values);
}
return parent::evaluateValues($values);
}
protected function evaluateFunction($function, array $argv) {
foreach ($this->getUsableDatasources() as $source) {
if ($source->canEvaluateFunction($function)) {
return $source->evaluateFunction($function, $argv);
}
}
return parent::evaluateFunction($function, $argv);
}
public function renderFunctionTokens($function, array $argv_list) {
foreach ($this->getUsableDatasources() as $source) {
if ($source->canEvaluateFunction($function)) {
return $source->renderFunctionTokens($function, $argv_list);
}
}
return parent::renderFunctionTokens($function, $argv_list);
}
protected function renderSpecialTokens(array $values) {
$result = array();
foreach ($this->getUsableDatasources() as $source) {
$special = $source->renderSpecialTokens($values);
foreach ($special as $key => $token) {
$result[$key] = $token;
unset($values[$key]);
}
if (!$values) {
break;
}
}
return $result;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Mar 14, 7:53 AM (4 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
71695
Default Alt Text
(13 KB)

Event Timeline