mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 22:10: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',
|
'PhabricatorProjectIcon' => 'applications/project/icon/PhabricatorProjectIcon.php',
|
||||||
'PhabricatorProjectInterface' => 'applications/project/interface/PhabricatorProjectInterface.php',
|
'PhabricatorProjectInterface' => 'applications/project/interface/PhabricatorProjectInterface.php',
|
||||||
'PhabricatorProjectListController' => 'applications/project/controller/PhabricatorProjectListController.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',
|
'PhabricatorProjectMemberOfProjectEdgeType' => 'applications/project/edge/PhabricatorProjectMemberOfProjectEdgeType.php',
|
||||||
'PhabricatorProjectMembersDatasource' => 'applications/project/typeahead/PhabricatorProjectMembersDatasource.php',
|
'PhabricatorProjectMembersDatasource' => 'applications/project/typeahead/PhabricatorProjectMembersDatasource.php',
|
||||||
'PhabricatorProjectMembersEditController' => 'applications/project/controller/PhabricatorProjectMembersEditController.php',
|
'PhabricatorProjectMembersEditController' => 'applications/project/controller/PhabricatorProjectMembersEditController.php',
|
||||||
'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php',
|
'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php',
|
||||||
'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php',
|
'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php',
|
||||||
|
'PhabricatorProjectNoProjectsDatasource' => 'applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php',
|
||||||
'PhabricatorProjectObjectHasProjectEdgeType' => 'applications/project/edge/PhabricatorProjectObjectHasProjectEdgeType.php',
|
'PhabricatorProjectObjectHasProjectEdgeType' => 'applications/project/edge/PhabricatorProjectObjectHasProjectEdgeType.php',
|
||||||
'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php',
|
'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php',
|
||||||
'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php',
|
'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php',
|
||||||
|
@ -5674,11 +5678,15 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectFeedController' => 'PhabricatorProjectController',
|
'PhabricatorProjectFeedController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectIcon' => 'Phobject',
|
'PhabricatorProjectIcon' => 'Phobject',
|
||||||
'PhabricatorProjectListController' => 'PhabricatorProjectController',
|
'PhabricatorProjectListController' => 'PhabricatorProjectController',
|
||||||
|
'PhabricatorProjectLogicDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
|
'PhabricatorProjectLogicalAndDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
|
'PhabricatorProjectLogicalOrNotDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
'PhabricatorProjectMemberOfProjectEdgeType' => 'PhabricatorEdgeType',
|
'PhabricatorProjectMemberOfProjectEdgeType' => 'PhabricatorEdgeType',
|
||||||
'PhabricatorProjectMembersDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
'PhabricatorProjectMembersDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
'PhabricatorProjectMembersEditController' => 'PhabricatorProjectController',
|
'PhabricatorProjectMembersEditController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController',
|
'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectMoveController' => 'PhabricatorProjectController',
|
'PhabricatorProjectMoveController' => 'PhabricatorProjectController',
|
||||||
|
'PhabricatorProjectNoProjectsDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
'PhabricatorProjectObjectHasProjectEdgeType' => 'PhabricatorEdgeType',
|
'PhabricatorProjectObjectHasProjectEdgeType' => 'PhabricatorEdgeType',
|
||||||
'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
|
'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
|
// query to child sources. This makes it easier to implement function
|
||||||
// sources in terms of real object sources.
|
// sources in terms of real object sources.
|
||||||
$raw_query = $this->getRawQuery();
|
$raw_query = $this->getRawQuery();
|
||||||
|
|
||||||
|
$is_function = false;
|
||||||
if (self::isFunctionToken($raw_query)) {
|
if (self::isFunctionToken($raw_query)) {
|
||||||
$function = $this->parseFunction($raw_query, $allow_partial = true);
|
$is_function = true;
|
||||||
if ($function) {
|
|
||||||
$raw_query = head($function['argv']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$stack = $this->getFunctionStack();
|
||||||
|
|
||||||
$results = array();
|
$results = array();
|
||||||
foreach ($this->getUsableDatasources() as $source) {
|
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
|
$source
|
||||||
->setRawQuery($raw_query)
|
->setFunctionStack($source_stack)
|
||||||
|
->setRawQuery($source_query)
|
||||||
->setQuery($this->getQuery())
|
->setQuery($this->getQuery())
|
||||||
->setViewer($this->getViewer());
|
->setViewer($this->getViewer());
|
||||||
|
|
||||||
|
@ -106,7 +128,6 @@ abstract class PhabricatorTypeaheadCompositeDatasource
|
||||||
return parent::canEvaluateFunction($function);
|
return parent::canEvaluateFunction($function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function evaluateFunction($function, array $argv) {
|
protected function evaluateFunction($function, array $argv) {
|
||||||
foreach ($this->getUsableDatasources() as $source) {
|
foreach ($this->getUsableDatasources() as $source) {
|
||||||
if ($source->canEvaluateFunction($function)) {
|
if ($source->canEvaluateFunction($function)) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
||||||
private $offset;
|
private $offset;
|
||||||
private $limit;
|
private $limit;
|
||||||
private $parameters = array();
|
private $parameters = array();
|
||||||
|
private $functionStack = array();
|
||||||
|
|
||||||
public function setLimit($limit) {
|
public function setLimit($limit) {
|
||||||
$this->limit = $limit;
|
$this->limit = $limit;
|
||||||
|
@ -274,6 +275,14 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
||||||
* @task functions
|
* @task functions
|
||||||
*/
|
*/
|
||||||
protected function canEvaluateFunction($function) {
|
protected function canEvaluateFunction($function) {
|
||||||
|
return $this->shouldStripFunction($function);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task functions
|
||||||
|
*/
|
||||||
|
protected function shouldStripFunction($function) {
|
||||||
$functions = $this->getDatasourceFunctions();
|
$functions = $this->getDatasourceFunctions();
|
||||||
return isset($functions[$function]);
|
return isset($functions[$function]);
|
||||||
}
|
}
|
||||||
|
@ -337,7 +346,7 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
||||||
$matches = null;
|
$matches = null;
|
||||||
|
|
||||||
if ($allow_partial) {
|
if ($allow_partial) {
|
||||||
$ok = preg_match('/^([^(]+)\((.*)$/', $token, $matches);
|
$ok = preg_match('/^([^(]+)\((.*?)\)?$/', $token, $matches);
|
||||||
} else {
|
} else {
|
||||||
$ok = preg_match('/^([^(]+)\((.*)\)$/', $token, $matches);
|
$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