@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+249 -1
+8
src/__phutil_library_map__.php
··· 2617 2617 'PhabricatorCountdownViewController' => 'applications/countdown/controller/PhabricatorCountdownViewController.php', 2618 2618 'PhabricatorCursorPagedPolicyAwareQuery' => 'infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php', 2619 2619 'PhabricatorCustomField' => 'infrastructure/customfield/field/PhabricatorCustomField.php', 2620 + 'PhabricatorCustomFieldApplicationSearchAnyFunctionDatasource' => 'infrastructure/customfield/datasource/PhabricatorCustomFieldApplicationSearchAnyFunctionDatasource.php', 2621 + 'PhabricatorCustomFieldApplicationSearchDatasource' => 'infrastructure/customfield/datasource/PhabricatorCustomFieldApplicationSearchDatasource.php', 2622 + 'PhabricatorCustomFieldApplicationSearchNoneFunctionDatasource' => 'infrastructure/customfield/datasource/PhabricatorCustomFieldApplicationSearchNoneFunctionDatasource.php', 2620 2623 'PhabricatorCustomFieldAttachment' => 'infrastructure/customfield/field/PhabricatorCustomFieldAttachment.php', 2621 2624 'PhabricatorCustomFieldConfigOptionType' => 'infrastructure/customfield/config/PhabricatorCustomFieldConfigOptionType.php', 2622 2625 'PhabricatorCustomFieldDataNotAvailableException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldDataNotAvailableException.php', ··· 4387 4390 'PhabricatorTypeaheadInvalidTokenException' => 'applications/typeahead/exception/PhabricatorTypeaheadInvalidTokenException.php', 4388 4391 'PhabricatorTypeaheadModularDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php', 4389 4392 'PhabricatorTypeaheadMonogramDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php', 4393 + 'PhabricatorTypeaheadProxyDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadProxyDatasource.php', 4390 4394 'PhabricatorTypeaheadResult' => 'applications/typeahead/storage/PhabricatorTypeaheadResult.php', 4391 4395 'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadRuntimeCompositeDatasource.php', 4392 4396 'PhabricatorTypeaheadTestNumbersDatasource' => 'applications/typeahead/datasource/__tests__/PhabricatorTypeaheadTestNumbersDatasource.php', ··· 8122 8126 'PhabricatorCountdownViewController' => 'PhabricatorCountdownController', 8123 8127 'PhabricatorCursorPagedPolicyAwareQuery' => 'PhabricatorPolicyAwareQuery', 8124 8128 'PhabricatorCustomField' => 'Phobject', 8129 + 'PhabricatorCustomFieldApplicationSearchAnyFunctionDatasource' => 'PhabricatorTypeaheadDatasource', 8130 + 'PhabricatorCustomFieldApplicationSearchDatasource' => 'PhabricatorTypeaheadProxyDatasource', 8131 + 'PhabricatorCustomFieldApplicationSearchNoneFunctionDatasource' => 'PhabricatorTypeaheadDatasource', 8125 8132 'PhabricatorCustomFieldAttachment' => 'Phobject', 8126 8133 'PhabricatorCustomFieldConfigOptionType' => 'PhabricatorConfigOptionType', 8127 8134 'PhabricatorCustomFieldDataNotAvailableException' => 'Exception', ··· 10182 10189 'PhabricatorTypeaheadInvalidTokenException' => 'Exception', 10183 10190 'PhabricatorTypeaheadModularDatasourceController' => 'PhabricatorTypeaheadDatasourceController', 10184 10191 'PhabricatorTypeaheadMonogramDatasource' => 'PhabricatorTypeaheadDatasource', 10192 + 'PhabricatorTypeaheadProxyDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 10185 10193 'PhabricatorTypeaheadResult' => 'Phobject', 10186 10194 'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 10187 10195 'PhabricatorTypeaheadTestNumbersDatasource' => 'PhabricatorTypeaheadDatasource',
+58
src/applications/typeahead/datasource/PhabricatorTypeaheadProxyDatasource.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorTypeaheadProxyDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + private $datasource; 7 + 8 + public function setDatasource(PhabricatorTypeaheadDatasource $datasource) { 9 + $this->datasource = $datasource; 10 + $this->setParameters( 11 + array( 12 + 'class' => get_class($datasource), 13 + 'parameters' => $datasource->getParameters(), 14 + )); 15 + return $this; 16 + } 17 + 18 + public function getDatasource() { 19 + if (!$this->datasource) { 20 + $class = $this->getParameter('class'); 21 + 22 + $parent = 'PhabricatorTypeaheadDatasource'; 23 + if (!is_subclass_of($class, $parent)) { 24 + throw new Exception( 25 + pht( 26 + 'Configured datasource class "%s" must be a valid subclass of '. 27 + '"%s".', 28 + $class, 29 + $parent)); 30 + } 31 + 32 + $datasource = newv($class, array()); 33 + $datasource->setParameters($this->getParameter('parameters', array())); 34 + $this->datasource = $datasource; 35 + } 36 + 37 + return $this->datasource; 38 + } 39 + 40 + public function getComponentDatasources() { 41 + return array( 42 + $this->getDatasource(), 43 + ); 44 + } 45 + 46 + public function getDatasourceApplicationClass() { 47 + return $this->getDatasource()->getDatasourceApplicationClass(); 48 + } 49 + 50 + public function getBrowseTitle() { 51 + return $this->getDatasource()->getBrowseTitle(); 52 + } 53 + 54 + public function getPlaceholderText() { 55 + return $this->getDatasource()->getPlaceholderText(); 56 + } 57 + 58 + }
+70
src/infrastructure/customfield/datasource/PhabricatorCustomFieldApplicationSearchAnyFunctionDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorCustomFieldApplicationSearchAnyFunctionDatasource 4 + extends PhabricatorTypeaheadDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Any'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type "any()"...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return null; 16 + } 17 + 18 + public function getDatasourceFunctions() { 19 + return array( 20 + 'any' => array( 21 + 'name' => pht('Any Value'), 22 + 'summary' => pht('Find results with any value.'), 23 + 'description' => pht( 24 + "This function includes results which have any value. Use a query ". 25 + "like this to find results with any value:\n\n%s". 26 + '> any()'), 27 + ), 28 + ); 29 + } 30 + 31 + public function loadResults() { 32 + $results = array( 33 + $this->newAnyFunction(), 34 + ); 35 + return $this->filterResultsAgainstTokens($results); 36 + } 37 + 38 + protected function evaluateFunction($function, array $argv_list) { 39 + $results = array(); 40 + 41 + foreach ($argv_list as $argv) { 42 + $results[] = new PhabricatorQueryConstraint( 43 + PhabricatorQueryConstraint::OPERATOR_ANY, 44 + null); 45 + } 46 + 47 + return $results; 48 + } 49 + 50 + public function renderFunctionTokens($function, array $argv_list) { 51 + $results = array(); 52 + foreach ($argv_list as $argv) { 53 + $results[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult( 54 + $this->newAnyFunction()); 55 + } 56 + return $results; 57 + } 58 + 59 + private function newAnyFunction() { 60 + $name = pht('Any Value'); 61 + return $this->newFunctionResult() 62 + ->setName($name.' any') 63 + ->setDisplayName($name) 64 + ->setIcon('fa-circle-o') 65 + ->setPHID('any()') 66 + ->setUnique(true) 67 + ->addAttribute(pht('Select results with any value.')); 68 + } 69 + 70 + }
+17
src/infrastructure/customfield/datasource/PhabricatorCustomFieldApplicationSearchDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorCustomFieldApplicationSearchDatasource 4 + extends PhabricatorTypeaheadProxyDatasource { 5 + 6 + public function getComponentDatasources() { 7 + $datasources = parent::getComponentDatasources(); 8 + 9 + $datasources[] = 10 + new PhabricatorCustomFieldApplicationSearchAnyFunctionDatasource(); 11 + $datasources[] = 12 + new PhabricatorCustomFieldApplicationSearchNoneFunctionDatasource(); 13 + 14 + return $datasources; 15 + } 16 + 17 + }
+72
src/infrastructure/customfield/datasource/PhabricatorCustomFieldApplicationSearchNoneFunctionDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorCustomFieldApplicationSearchNoneFunctionDatasource 4 + extends PhabricatorTypeaheadDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse No Value'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type "none()"...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return null; 16 + } 17 + 18 + public function getDatasourceFunctions() { 19 + return array( 20 + 'none' => array( 21 + 'name' => pht('No Value'), 22 + 'summary' => pht('Find results with no value.'), 23 + 'description' => pht( 24 + "This function includes results which have no value. Use a query ". 25 + "like this to find results with no value:\n\n%s\n\n", 26 + 'If you combine this function with other constraints, results '. 27 + 'which have no value or the specified values will be returned.', 28 + '> any()'), 29 + ), 30 + ); 31 + } 32 + 33 + public function loadResults() { 34 + $results = array( 35 + $this->newNoneFunction(), 36 + ); 37 + return $this->filterResultsAgainstTokens($results); 38 + } 39 + 40 + protected function evaluateFunction($function, array $argv_list) { 41 + $results = array(); 42 + 43 + foreach ($argv_list as $argv) { 44 + $results[] = new PhabricatorQueryConstraint( 45 + PhabricatorQueryConstraint::OPERATOR_NULL, 46 + null); 47 + } 48 + 49 + return $results; 50 + } 51 + 52 + public function renderFunctionTokens($function, array $argv_list) { 53 + $results = array(); 54 + foreach ($argv_list as $argv) { 55 + $results[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult( 56 + $this->newNoneFunction()); 57 + } 58 + return $results; 59 + } 60 + 61 + private function newNoneFunction() { 62 + $name = pht('No Value'); 63 + return $this->newFunctionResult() 64 + ->setName($name.' none') 65 + ->setDisplayName($name) 66 + ->setIcon('fa-ban') 67 + ->setPHID('none()') 68 + ->setUnique(true) 69 + ->addAttribute(pht('Select results with no value.')); 70 + } 71 + 72 + }
+24 -1
src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldTokenizer.php
··· 33 33 $control = id(new AphrontFormTokenizerControl()) 34 34 ->setLabel($this->getFieldName()) 35 35 ->setName($this->getFieldKey()) 36 - ->setDatasource($this->getDatasource()) 36 + ->setDatasource($this->newApplicationSearchDatasource()) 37 37 ->setValue(nonempty($value, array())); 38 38 39 39 $form->appendControl($control); 40 + } 41 + 42 + public function applyApplicationSearchConstraintToQuery( 43 + PhabricatorApplicationSearchEngine $engine, 44 + PhabricatorCursorPagedPolicyAwareQuery $query, 45 + $value) { 46 + if ($value) { 47 + 48 + $datasource = $this->newApplicationSearchDatasource() 49 + ->setViewer($this->getViewer()); 50 + $value = $datasource->evaluateTokens($value); 51 + 52 + $query->withApplicationSearchContainsConstraint( 53 + $this->newStringIndex(null), 54 + $value); 55 + } 40 56 } 41 57 42 58 public function getHeraldFieldValueType($condition) { ··· 118 134 ->setAsInline(true) 119 135 ->render(); 120 136 } 137 + } 138 + 139 + protected function newApplicationSearchDatasource() { 140 + $datasource = $this->getDatasource(); 141 + 142 + return id(new PhabricatorCustomFieldApplicationSearchDatasource()) 143 + ->setDatasource($datasource); 121 144 } 122 145 123 146 }