1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 14:00:56 +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:
epriestley 2015-04-18 09:58:33 -07:00
parent eb69b115a7
commit c2fc065bfb
7 changed files with 300 additions and 7 deletions

View file

@ -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',

View file

@ -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(),
);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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)) {

View file

@ -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);
}
} }