mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 08:52:39 +01:00
Support "Any Value" and "No Value" search constraints for datasource Custom Fields
Summary: Depends on D19126. Ref T13090. For datasource custom fields, this proxies the datasource and provides "none()" and "any()" functions to allow you to search for objects with no values or any values. Test Plan: - Created a custom "Owning Group" field in Maniphest using a Projects datasource. - For a task with no owner assigned, searched for "none()" (hit) and "any()" (miss). - Assigned the task to an owning project. - Searched for "none()" (miss), "any()" (hit), the project it is now a member of (hit) and some random other project (miss). Maniphest Tasks: T13090 Differential Revision: https://secure.phabricator.com/D19127
This commit is contained in:
parent
d0591f3680
commit
3203fd9eea
6 changed files with 249 additions and 1 deletions
|
@ -2617,6 +2617,9 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCountdownViewController' => 'applications/countdown/controller/PhabricatorCountdownViewController.php',
|
||||
'PhabricatorCursorPagedPolicyAwareQuery' => 'infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php',
|
||||
'PhabricatorCustomField' => 'infrastructure/customfield/field/PhabricatorCustomField.php',
|
||||
'PhabricatorCustomFieldApplicationSearchAnyFunctionDatasource' => 'infrastructure/customfield/datasource/PhabricatorCustomFieldApplicationSearchAnyFunctionDatasource.php',
|
||||
'PhabricatorCustomFieldApplicationSearchDatasource' => 'infrastructure/customfield/datasource/PhabricatorCustomFieldApplicationSearchDatasource.php',
|
||||
'PhabricatorCustomFieldApplicationSearchNoneFunctionDatasource' => 'infrastructure/customfield/datasource/PhabricatorCustomFieldApplicationSearchNoneFunctionDatasource.php',
|
||||
'PhabricatorCustomFieldAttachment' => 'infrastructure/customfield/field/PhabricatorCustomFieldAttachment.php',
|
||||
'PhabricatorCustomFieldConfigOptionType' => 'infrastructure/customfield/config/PhabricatorCustomFieldConfigOptionType.php',
|
||||
'PhabricatorCustomFieldDataNotAvailableException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldDataNotAvailableException.php',
|
||||
|
@ -4387,6 +4390,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorTypeaheadInvalidTokenException' => 'applications/typeahead/exception/PhabricatorTypeaheadInvalidTokenException.php',
|
||||
'PhabricatorTypeaheadModularDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php',
|
||||
'PhabricatorTypeaheadMonogramDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php',
|
||||
'PhabricatorTypeaheadProxyDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadProxyDatasource.php',
|
||||
'PhabricatorTypeaheadResult' => 'applications/typeahead/storage/PhabricatorTypeaheadResult.php',
|
||||
'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadRuntimeCompositeDatasource.php',
|
||||
'PhabricatorTypeaheadTestNumbersDatasource' => 'applications/typeahead/datasource/__tests__/PhabricatorTypeaheadTestNumbersDatasource.php',
|
||||
|
@ -8122,6 +8126,9 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCountdownViewController' => 'PhabricatorCountdownController',
|
||||
'PhabricatorCursorPagedPolicyAwareQuery' => 'PhabricatorPolicyAwareQuery',
|
||||
'PhabricatorCustomField' => 'Phobject',
|
||||
'PhabricatorCustomFieldApplicationSearchAnyFunctionDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorCustomFieldApplicationSearchDatasource' => 'PhabricatorTypeaheadProxyDatasource',
|
||||
'PhabricatorCustomFieldApplicationSearchNoneFunctionDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorCustomFieldAttachment' => 'Phobject',
|
||||
'PhabricatorCustomFieldConfigOptionType' => 'PhabricatorConfigOptionType',
|
||||
'PhabricatorCustomFieldDataNotAvailableException' => 'Exception',
|
||||
|
@ -10182,6 +10189,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorTypeaheadInvalidTokenException' => 'Exception',
|
||||
'PhabricatorTypeaheadModularDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
|
||||
'PhabricatorTypeaheadMonogramDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorTypeaheadProxyDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorTypeaheadResult' => 'Phobject',
|
||||
'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorTypeaheadTestNumbersDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorTypeaheadProxyDatasource
|
||||
extends PhabricatorTypeaheadCompositeDatasource {
|
||||
|
||||
private $datasource;
|
||||
|
||||
public function setDatasource(PhabricatorTypeaheadDatasource $datasource) {
|
||||
$this->datasource = $datasource;
|
||||
$this->setParameters(
|
||||
array(
|
||||
'class' => get_class($datasource),
|
||||
'parameters' => $datasource->getParameters(),
|
||||
));
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDatasource() {
|
||||
if (!$this->datasource) {
|
||||
$class = $this->getParameter('class');
|
||||
|
||||
$parent = 'PhabricatorTypeaheadDatasource';
|
||||
if (!is_subclass_of($class, $parent)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Configured datasource class "%s" must be a valid subclass of '.
|
||||
'"%s".',
|
||||
$class,
|
||||
$parent));
|
||||
}
|
||||
|
||||
$datasource = newv($class, array());
|
||||
$datasource->setParameters($this->getParameter('parameters', array()));
|
||||
$this->datasource = $datasource;
|
||||
}
|
||||
|
||||
return $this->datasource;
|
||||
}
|
||||
|
||||
public function getComponentDatasources() {
|
||||
return array(
|
||||
$this->getDatasource(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return $this->getDatasource()->getDatasourceApplicationClass();
|
||||
}
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return $this->getDatasource()->getBrowseTitle();
|
||||
}
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return $this->getDatasource()->getPlaceholderText();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCustomFieldApplicationSearchAnyFunctionDatasource
|
||||
extends PhabricatorTypeaheadDatasource {
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse Any');
|
||||
}
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type "any()"...');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getDatasourceFunctions() {
|
||||
return array(
|
||||
'any' => array(
|
||||
'name' => pht('Any Value'),
|
||||
'summary' => pht('Find results with any value.'),
|
||||
'description' => pht(
|
||||
"This function includes results which have any value. Use a query ".
|
||||
"like this to find results with any value:\n\n%s".
|
||||
'> any()'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function loadResults() {
|
||||
$results = array(
|
||||
$this->newAnyFunction(),
|
||||
);
|
||||
return $this->filterResultsAgainstTokens($results);
|
||||
}
|
||||
|
||||
protected function evaluateFunction($function, array $argv_list) {
|
||||
$results = array();
|
||||
|
||||
foreach ($argv_list as $argv) {
|
||||
$results[] = new PhabricatorQueryConstraint(
|
||||
PhabricatorQueryConstraint::OPERATOR_ANY,
|
||||
null);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function renderFunctionTokens($function, array $argv_list) {
|
||||
$results = array();
|
||||
foreach ($argv_list as $argv) {
|
||||
$results[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult(
|
||||
$this->newAnyFunction());
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function newAnyFunction() {
|
||||
$name = pht('Any Value');
|
||||
return $this->newFunctionResult()
|
||||
->setName($name.' any')
|
||||
->setDisplayName($name)
|
||||
->setIcon('fa-circle-o')
|
||||
->setPHID('any()')
|
||||
->setUnique(true)
|
||||
->addAttribute(pht('Select results with any value.'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCustomFieldApplicationSearchDatasource
|
||||
extends PhabricatorTypeaheadProxyDatasource {
|
||||
|
||||
public function getComponentDatasources() {
|
||||
$datasources = parent::getComponentDatasources();
|
||||
|
||||
$datasources[] =
|
||||
new PhabricatorCustomFieldApplicationSearchAnyFunctionDatasource();
|
||||
$datasources[] =
|
||||
new PhabricatorCustomFieldApplicationSearchNoneFunctionDatasource();
|
||||
|
||||
return $datasources;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCustomFieldApplicationSearchNoneFunctionDatasource
|
||||
extends PhabricatorTypeaheadDatasource {
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse No Value');
|
||||
}
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type "none()"...');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getDatasourceFunctions() {
|
||||
return array(
|
||||
'none' => array(
|
||||
'name' => pht('No Value'),
|
||||
'summary' => pht('Find results with no value.'),
|
||||
'description' => pht(
|
||||
"This function includes results which have no value. Use a query ".
|
||||
"like this to find results with no value:\n\n%s\n\n",
|
||||
'If you combine this function with other constraints, results '.
|
||||
'which have no value or the specified values will be returned.',
|
||||
'> any()'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function loadResults() {
|
||||
$results = array(
|
||||
$this->newNoneFunction(),
|
||||
);
|
||||
return $this->filterResultsAgainstTokens($results);
|
||||
}
|
||||
|
||||
protected function evaluateFunction($function, array $argv_list) {
|
||||
$results = array();
|
||||
|
||||
foreach ($argv_list as $argv) {
|
||||
$results[] = new PhabricatorQueryConstraint(
|
||||
PhabricatorQueryConstraint::OPERATOR_NULL,
|
||||
null);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function renderFunctionTokens($function, array $argv_list) {
|
||||
$results = array();
|
||||
foreach ($argv_list as $argv) {
|
||||
$results[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult(
|
||||
$this->newNoneFunction());
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function newNoneFunction() {
|
||||
$name = pht('No Value');
|
||||
return $this->newFunctionResult()
|
||||
->setName($name.' none')
|
||||
->setDisplayName($name)
|
||||
->setIcon('fa-ban')
|
||||
->setPHID('none()')
|
||||
->setUnique(true)
|
||||
->addAttribute(pht('Select results with no value.'));
|
||||
}
|
||||
|
||||
}
|
|
@ -33,12 +33,28 @@ abstract class PhabricatorStandardCustomFieldTokenizer
|
|||
$control = id(new AphrontFormTokenizerControl())
|
||||
->setLabel($this->getFieldName())
|
||||
->setName($this->getFieldKey())
|
||||
->setDatasource($this->getDatasource())
|
||||
->setDatasource($this->newApplicationSearchDatasource())
|
||||
->setValue(nonempty($value, array()));
|
||||
|
||||
$form->appendControl($control);
|
||||
}
|
||||
|
||||
public function applyApplicationSearchConstraintToQuery(
|
||||
PhabricatorApplicationSearchEngine $engine,
|
||||
PhabricatorCursorPagedPolicyAwareQuery $query,
|
||||
$value) {
|
||||
if ($value) {
|
||||
|
||||
$datasource = $this->newApplicationSearchDatasource()
|
||||
->setViewer($this->getViewer());
|
||||
$value = $datasource->evaluateTokens($value);
|
||||
|
||||
$query->withApplicationSearchContainsConstraint(
|
||||
$this->newStringIndex(null),
|
||||
$value);
|
||||
}
|
||||
}
|
||||
|
||||
public function getHeraldFieldValueType($condition) {
|
||||
return id(new HeraldTokenizerFieldValue())
|
||||
->setKey('custom.'.$this->getFieldKey())
|
||||
|
@ -120,4 +136,11 @@ abstract class PhabricatorStandardCustomFieldTokenizer
|
|||
}
|
||||
}
|
||||
|
||||
protected function newApplicationSearchDatasource() {
|
||||
$datasource = $this->getDatasource();
|
||||
|
||||
return id(new PhabricatorCustomFieldApplicationSearchDatasource())
|
||||
->setDatasource($datasource);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue