mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-19 12:00:55 +01:00
Implement a project logic datasource
Summary: Ref T4100. Ref T5595. This allows you to execute all "Projects", "Any Project", "Not in projects" and "include no projects" operations in one field. It doesn't actually implement such a field yet. Test Plan: {F375516} Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T5595, T4100 Differential Revision: https://secure.phabricator.com/D12460
This commit is contained in:
parent
eb69b115a7
commit
c2fc065bfb
7 changed files with 300 additions and 7 deletions
|
@ -2295,11 +2295,15 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectIcon' => 'applications/project/icon/PhabricatorProjectIcon.php',
|
||||
'PhabricatorProjectInterface' => 'applications/project/interface/PhabricatorProjectInterface.php',
|
||||
'PhabricatorProjectListController' => 'applications/project/controller/PhabricatorProjectListController.php',
|
||||
'PhabricatorProjectLogicDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicDatasource.php',
|
||||
'PhabricatorProjectLogicalAndDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalAndDatasource.php',
|
||||
'PhabricatorProjectLogicalOrNotDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php',
|
||||
'PhabricatorProjectMemberOfProjectEdgeType' => 'applications/project/edge/PhabricatorProjectMemberOfProjectEdgeType.php',
|
||||
'PhabricatorProjectMembersDatasource' => 'applications/project/typeahead/PhabricatorProjectMembersDatasource.php',
|
||||
'PhabricatorProjectMembersEditController' => 'applications/project/controller/PhabricatorProjectMembersEditController.php',
|
||||
'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php',
|
||||
'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php',
|
||||
'PhabricatorProjectNoProjectsDatasource' => 'applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php',
|
||||
'PhabricatorProjectObjectHasProjectEdgeType' => 'applications/project/edge/PhabricatorProjectObjectHasProjectEdgeType.php',
|
||||
'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php',
|
||||
'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php',
|
||||
|
@ -5674,11 +5678,15 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectFeedController' => 'PhabricatorProjectController',
|
||||
'PhabricatorProjectIcon' => 'Phobject',
|
||||
'PhabricatorProjectListController' => 'PhabricatorProjectController',
|
||||
'PhabricatorProjectLogicDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorProjectLogicalAndDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorProjectLogicalOrNotDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorProjectMemberOfProjectEdgeType' => 'PhabricatorEdgeType',
|
||||
'PhabricatorProjectMembersDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorProjectMembersEditController' => 'PhabricatorProjectController',
|
||||
'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController',
|
||||
'PhabricatorProjectMoveController' => 'PhabricatorProjectController',
|
||||
'PhabricatorProjectNoProjectsDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorProjectObjectHasProjectEdgeType' => 'PhabricatorEdgeType',
|
||||
'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorProjectLogicDatasource
|
||||
extends PhabricatorTypeaheadCompositeDatasource {
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type a project name or selector...');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorProjectApplication';
|
||||
}
|
||||
|
||||
public function getComponentDatasources() {
|
||||
return array(
|
||||
new PhabricatorProjectNoProjectsDatasource(),
|
||||
new PhabricatorProjectLogicalAndDatasource(),
|
||||
new PhabricatorProjectLogicalOrNotDatasource(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorProjectLogicalAndDatasource
|
||||
extends PhabricatorTypeaheadCompositeDatasource {
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type a project name...');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorProjectApplication';
|
||||
}
|
||||
|
||||
public function getComponentDatasources() {
|
||||
return array(
|
||||
new PhabricatorProjectDatasource(),
|
||||
);
|
||||
}
|
||||
|
||||
public function evaluateTokens(array $tokens) {
|
||||
$results = parent::evaluateTokens($tokens);
|
||||
|
||||
foreach ($results as $key => $result) {
|
||||
$results[$key] = new PhabricatorQueryConstraint(
|
||||
PhabricatorQueryConstraint::OPERATOR_AND,
|
||||
$result);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorProjectLogicalOrNotDatasource
|
||||
extends PhabricatorTypeaheadCompositeDatasource {
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type any(<project>) or not(<project>)...');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorProjectApplication';
|
||||
}
|
||||
|
||||
public function getComponentDatasources() {
|
||||
return array(
|
||||
new PhabricatorProjectDatasource(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getDatasourceFunctions() {
|
||||
return array(
|
||||
'any' => array(
|
||||
'name' => pht('Find results in any of several projects.'),
|
||||
),
|
||||
'not' => array(
|
||||
'name' => pht('Find results not in specific projects.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected function didLoadResults(array $results) {
|
||||
$function = $this->getCurrentFunction();
|
||||
$return_any = ($function !== 'not');
|
||||
$return_not = ($function !== 'any');
|
||||
|
||||
$return = array();
|
||||
foreach ($results as $result) {
|
||||
$result
|
||||
->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION)
|
||||
->setIcon('fa-asterisk');
|
||||
|
||||
if ($return_any) {
|
||||
$return[] = id(clone $result)
|
||||
->setPHID('any('.$result->getPHID().')')
|
||||
->setDisplayName(pht('In Any: %s', $result->getDisplayName()))
|
||||
->setName($result->getName().' any');
|
||||
}
|
||||
|
||||
if ($return_not) {
|
||||
$return[] = id(clone $result)
|
||||
->setPHID('not('.$result->getPHID().')')
|
||||
->setDisplayName(pht('Not In: %s', $result->getDisplayName()))
|
||||
->setName($result->getName().' not');
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
protected function evaluateFunction($function, array $argv_list) {
|
||||
$phids = array();
|
||||
foreach ($argv_list as $argv) {
|
||||
$phids[] = head($argv);
|
||||
}
|
||||
|
||||
$operator = array(
|
||||
'any' => PhabricatorQueryConstraint::OPERATOR_OR,
|
||||
'not' => PhabricatorQueryConstraint::OPERATOR_NOT,
|
||||
);
|
||||
|
||||
$results = array();
|
||||
foreach ($phids as $phid) {
|
||||
$results[] = new PhabricatorQueryConstraint(
|
||||
$operator[$function],
|
||||
$phid);
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
|
||||
public function renderFunctionTokens($function, array $argv_list) {
|
||||
$phids = array();
|
||||
foreach ($argv_list as $argv) {
|
||||
$phids[] = head($argv);
|
||||
}
|
||||
|
||||
$tokens = $this->renderTokens($phids);
|
||||
foreach ($tokens as $token) {
|
||||
if ($token->isInvalid()) {
|
||||
if ($function == 'any') {
|
||||
$token->setValue(pht('In Any: Invalid Project'));
|
||||
} else {
|
||||
$token->setValue(pht('Not In: Invalid Project'));
|
||||
}
|
||||
} else {
|
||||
$token
|
||||
->setIcon('fa-asterisk')
|
||||
->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION);
|
||||
|
||||
if ($function == 'any') {
|
||||
$token
|
||||
->setKey('any('.$token->getKey().')')
|
||||
->setValue(pht('In Any: %s', $token->getValue()));
|
||||
} else {
|
||||
$token
|
||||
->setKey('not('.$token->getKey().')')
|
||||
->setValue(pht('Not In: %s', $token->getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorProjectNoProjectsDatasource
|
||||
extends PhabricatorTypeaheadDatasource {
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type "not in any projects"...');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorProjectApplication';
|
||||
}
|
||||
|
||||
public function getDatasourceFunctions() {
|
||||
return array(
|
||||
'null' => array(
|
||||
'name' => pht('Find results which are not in any projects.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function loadResults() {
|
||||
$results = array(
|
||||
$this->buildNullResult(),
|
||||
);
|
||||
|
||||
return $this->filterResultsAgainstTokens($results);
|
||||
}
|
||||
|
||||
protected function evaluateFunction($function, array $argv_list) {
|
||||
$results = array();
|
||||
|
||||
foreach ($argv_list as $argv) {
|
||||
$results[] = new PhabricatorQueryConstraint(
|
||||
PhabricatorQueryConstraint::OPERATOR_NULL,
|
||||
'empty');
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function renderFunctionTokens($function, array $argv_list) {
|
||||
$results = array();
|
||||
foreach ($argv_list as $argv) {
|
||||
$results[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult(
|
||||
$this->buildNullResult());
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function buildNullResult() {
|
||||
$name = pht('Not In Any Projects');
|
||||
|
||||
return $this->newFunctionResult()
|
||||
->setUnique(true)
|
||||
->setPHID('null()')
|
||||
->setIcon('fa-ban')
|
||||
->setName('null '.$name)
|
||||
->setDisplayName($name);
|
||||
}
|
||||
|
||||
}
|
|
@ -30,17 +30,39 @@ abstract class PhabricatorTypeaheadCompositeDatasource
|
|||
// 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)) {
|
||||
$function = $this->parseFunction($raw_query, $allow_partial = true);
|
||||
if ($function) {
|
||||
$raw_query = head($function['argv']);
|
||||
}
|
||||
$is_function = true;
|
||||
}
|
||||
|
||||
$stack = $this->getFunctionStack();
|
||||
|
||||
$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
|
||||
->setRawQuery($raw_query)
|
||||
->setFunctionStack($source_stack)
|
||||
->setRawQuery($source_query)
|
||||
->setQuery($this->getQuery())
|
||||
->setViewer($this->getViewer());
|
||||
|
||||
|
@ -106,7 +128,6 @@ abstract class PhabricatorTypeaheadCompositeDatasource
|
|||
return parent::canEvaluateFunction($function);
|
||||
}
|
||||
|
||||
|
||||
protected function evaluateFunction($function, array $argv) {
|
||||
foreach ($this->getUsableDatasources() as $source) {
|
||||
if ($source->canEvaluateFunction($function)) {
|
||||
|
|
|
@ -11,6 +11,7 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
|||
private $offset;
|
||||
private $limit;
|
||||
private $parameters = array();
|
||||
private $functionStack = array();
|
||||
|
||||
public function setLimit($limit) {
|
||||
$this->limit = $limit;
|
||||
|
@ -274,6 +275,14 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
|||
* @task functions
|
||||
*/
|
||||
protected function canEvaluateFunction($function) {
|
||||
return $this->shouldStripFunction($function);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task functions
|
||||
*/
|
||||
protected function shouldStripFunction($function) {
|
||||
$functions = $this->getDatasourceFunctions();
|
||||
return isset($functions[$function]);
|
||||
}
|
||||
|
@ -337,7 +346,7 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
|||
$matches = null;
|
||||
|
||||
if ($allow_partial) {
|
||||
$ok = preg_match('/^([^(]+)\((.*)$/', $token, $matches);
|
||||
$ok = preg_match('/^([^(]+)\((.*?)\)?$/', $token, $matches);
|
||||
} else {
|
||||
$ok = preg_match('/^([^(]+)\((.*)\)$/', $token, $matches);
|
||||
}
|
||||
|
@ -367,4 +376,28 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task functions
|
||||
*/
|
||||
public function setFunctionStack(array $function_stack) {
|
||||
$this->functionStack = $function_stack;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task functions
|
||||
*/
|
||||
public function getFunctionStack() {
|
||||
return $this->functionStack;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task functions
|
||||
*/
|
||||
protected function getCurrentFunction() {
|
||||
return nonempty(last($this->functionStack), null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue