@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.

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

+300 -7
+8
src/__phutil_library_map__.php
··· 2295 2295 'PhabricatorProjectIcon' => 'applications/project/icon/PhabricatorProjectIcon.php', 2296 2296 'PhabricatorProjectInterface' => 'applications/project/interface/PhabricatorProjectInterface.php', 2297 2297 'PhabricatorProjectListController' => 'applications/project/controller/PhabricatorProjectListController.php', 2298 + 'PhabricatorProjectLogicDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicDatasource.php', 2299 + 'PhabricatorProjectLogicalAndDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalAndDatasource.php', 2300 + 'PhabricatorProjectLogicalOrNotDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php', 2298 2301 'PhabricatorProjectMemberOfProjectEdgeType' => 'applications/project/edge/PhabricatorProjectMemberOfProjectEdgeType.php', 2299 2302 'PhabricatorProjectMembersDatasource' => 'applications/project/typeahead/PhabricatorProjectMembersDatasource.php', 2300 2303 'PhabricatorProjectMembersEditController' => 'applications/project/controller/PhabricatorProjectMembersEditController.php', 2301 2304 'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php', 2302 2305 'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php', 2306 + 'PhabricatorProjectNoProjectsDatasource' => 'applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php', 2303 2307 'PhabricatorProjectObjectHasProjectEdgeType' => 'applications/project/edge/PhabricatorProjectObjectHasProjectEdgeType.php', 2304 2308 'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php', 2305 2309 'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php', ··· 5674 5678 'PhabricatorProjectFeedController' => 'PhabricatorProjectController', 5675 5679 'PhabricatorProjectIcon' => 'Phobject', 5676 5680 'PhabricatorProjectListController' => 'PhabricatorProjectController', 5681 + 'PhabricatorProjectLogicDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 5682 + 'PhabricatorProjectLogicalAndDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 5683 + 'PhabricatorProjectLogicalOrNotDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 5677 5684 'PhabricatorProjectMemberOfProjectEdgeType' => 'PhabricatorEdgeType', 5678 5685 'PhabricatorProjectMembersDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 5679 5686 'PhabricatorProjectMembersEditController' => 'PhabricatorProjectController', 5680 5687 'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController', 5681 5688 'PhabricatorProjectMoveController' => 'PhabricatorProjectController', 5689 + 'PhabricatorProjectNoProjectsDatasource' => 'PhabricatorTypeaheadDatasource', 5682 5690 'PhabricatorProjectObjectHasProjectEdgeType' => 'PhabricatorEdgeType', 5683 5691 'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 5684 5692 'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
+22
src/applications/project/typeahead/PhabricatorProjectLogicDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectLogicDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getPlaceholderText() { 7 + return pht('Type a project name or selector...'); 8 + } 9 + 10 + public function getDatasourceApplicationClass() { 11 + return 'PhabricatorProjectApplication'; 12 + } 13 + 14 + public function getComponentDatasources() { 15 + return array( 16 + new PhabricatorProjectNoProjectsDatasource(), 17 + new PhabricatorProjectLogicalAndDatasource(), 18 + new PhabricatorProjectLogicalOrNotDatasource(), 19 + ); 20 + } 21 + 22 + }
+32
src/applications/project/typeahead/PhabricatorProjectLogicalAndDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectLogicalAndDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getPlaceholderText() { 7 + return pht('Type a project name...'); 8 + } 9 + 10 + public function getDatasourceApplicationClass() { 11 + return 'PhabricatorProjectApplication'; 12 + } 13 + 14 + public function getComponentDatasources() { 15 + return array( 16 + new PhabricatorProjectDatasource(), 17 + ); 18 + } 19 + 20 + public function evaluateTokens(array $tokens) { 21 + $results = parent::evaluateTokens($tokens); 22 + 23 + foreach ($results as $key => $result) { 24 + $results[$key] = new PhabricatorQueryConstraint( 25 + PhabricatorQueryConstraint::OPERATOR_AND, 26 + $result); 27 + } 28 + 29 + return $results; 30 + } 31 + 32 + }
+115
src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectLogicalOrNotDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getPlaceholderText() { 7 + return pht('Type any(<project>) or not(<project>)...'); 8 + } 9 + 10 + public function getDatasourceApplicationClass() { 11 + return 'PhabricatorProjectApplication'; 12 + } 13 + 14 + public function getComponentDatasources() { 15 + return array( 16 + new PhabricatorProjectDatasource(), 17 + ); 18 + } 19 + 20 + public function getDatasourceFunctions() { 21 + return array( 22 + 'any' => array( 23 + 'name' => pht('Find results in any of several projects.'), 24 + ), 25 + 'not' => array( 26 + 'name' => pht('Find results not in specific projects.'), 27 + ), 28 + ); 29 + } 30 + 31 + protected function didLoadResults(array $results) { 32 + $function = $this->getCurrentFunction(); 33 + $return_any = ($function !== 'not'); 34 + $return_not = ($function !== 'any'); 35 + 36 + $return = array(); 37 + foreach ($results as $result) { 38 + $result 39 + ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) 40 + ->setIcon('fa-asterisk'); 41 + 42 + if ($return_any) { 43 + $return[] = id(clone $result) 44 + ->setPHID('any('.$result->getPHID().')') 45 + ->setDisplayName(pht('In Any: %s', $result->getDisplayName())) 46 + ->setName($result->getName().' any'); 47 + } 48 + 49 + if ($return_not) { 50 + $return[] = id(clone $result) 51 + ->setPHID('not('.$result->getPHID().')') 52 + ->setDisplayName(pht('Not In: %s', $result->getDisplayName())) 53 + ->setName($result->getName().' not'); 54 + } 55 + } 56 + 57 + return $return; 58 + } 59 + 60 + protected function evaluateFunction($function, array $argv_list) { 61 + $phids = array(); 62 + foreach ($argv_list as $argv) { 63 + $phids[] = head($argv); 64 + } 65 + 66 + $operator = array( 67 + 'any' => PhabricatorQueryConstraint::OPERATOR_OR, 68 + 'not' => PhabricatorQueryConstraint::OPERATOR_NOT, 69 + ); 70 + 71 + $results = array(); 72 + foreach ($phids as $phid) { 73 + $results[] = new PhabricatorQueryConstraint( 74 + $operator[$function], 75 + $phid); 76 + } 77 + 78 + return $phids; 79 + } 80 + 81 + public function renderFunctionTokens($function, array $argv_list) { 82 + $phids = array(); 83 + foreach ($argv_list as $argv) { 84 + $phids[] = head($argv); 85 + } 86 + 87 + $tokens = $this->renderTokens($phids); 88 + foreach ($tokens as $token) { 89 + if ($token->isInvalid()) { 90 + if ($function == 'any') { 91 + $token->setValue(pht('In Any: Invalid Project')); 92 + } else { 93 + $token->setValue(pht('Not In: Invalid Project')); 94 + } 95 + } else { 96 + $token 97 + ->setIcon('fa-asterisk') 98 + ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION); 99 + 100 + if ($function == 'any') { 101 + $token 102 + ->setKey('any('.$token->getKey().')') 103 + ->setValue(pht('In Any: %s', $token->getValue())); 104 + } else { 105 + $token 106 + ->setKey('not('.$token->getKey().')') 107 + ->setValue(pht('Not In: %s', $token->getValue())); 108 + } 109 + } 110 + } 111 + 112 + return $tokens; 113 + } 114 + 115 + }
+62
src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectNoProjectsDatasource 4 + extends PhabricatorTypeaheadDatasource { 5 + 6 + public function getPlaceholderText() { 7 + return pht('Type "not in any projects"...'); 8 + } 9 + 10 + public function getDatasourceApplicationClass() { 11 + return 'PhabricatorProjectApplication'; 12 + } 13 + 14 + public function getDatasourceFunctions() { 15 + return array( 16 + 'null' => array( 17 + 'name' => pht('Find results which are not in any projects.'), 18 + ), 19 + ); 20 + } 21 + 22 + public function loadResults() { 23 + $results = array( 24 + $this->buildNullResult(), 25 + ); 26 + 27 + return $this->filterResultsAgainstTokens($results); 28 + } 29 + 30 + protected function evaluateFunction($function, array $argv_list) { 31 + $results = array(); 32 + 33 + foreach ($argv_list as $argv) { 34 + $results[] = new PhabricatorQueryConstraint( 35 + PhabricatorQueryConstraint::OPERATOR_NULL, 36 + 'empty'); 37 + } 38 + 39 + return $results; 40 + } 41 + 42 + public function renderFunctionTokens($function, array $argv_list) { 43 + $results = array(); 44 + foreach ($argv_list as $argv) { 45 + $results[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult( 46 + $this->buildNullResult()); 47 + } 48 + return $results; 49 + } 50 + 51 + private function buildNullResult() { 52 + $name = pht('Not In Any Projects'); 53 + 54 + return $this->newFunctionResult() 55 + ->setUnique(true) 56 + ->setPHID('null()') 57 + ->setIcon('fa-ban') 58 + ->setName('null '.$name) 59 + ->setDisplayName($name); 60 + } 61 + 62 + }
+27 -6
src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php
··· 30 30 // query to child sources. This makes it easier to implement function 31 31 // sources in terms of real object sources. 32 32 $raw_query = $this->getRawQuery(); 33 + 34 + $is_function = false; 33 35 if (self::isFunctionToken($raw_query)) { 34 - $function = $this->parseFunction($raw_query, $allow_partial = true); 35 - if ($function) { 36 - $raw_query = head($function['argv']); 37 - } 36 + $is_function = true; 38 37 } 39 38 39 + $stack = $this->getFunctionStack(); 40 + 40 41 $results = array(); 41 42 foreach ($this->getUsableDatasources() as $source) { 43 + $source_stack = $stack; 44 + 45 + $source_query = $raw_query; 46 + if ($is_function) { 47 + // If this source can't handle the function, skip it. 48 + $function = $source->parseFunction($raw_query, $allow_partial = true); 49 + if (!$function) { 50 + continue; 51 + } 52 + 53 + // If this source handles the function directly, strip the function. 54 + // Otherwise, this is something like a composite source which has 55 + // some internal source which can evaluate the function, but will 56 + // perform stripping later. 57 + if ($source->shouldStripFunction($function['name'])) { 58 + $source_query = head($function['argv']); 59 + $source_stack[] = $function['name']; 60 + } 61 + } 62 + 42 63 $source 43 - ->setRawQuery($raw_query) 64 + ->setFunctionStack($source_stack) 65 + ->setRawQuery($source_query) 44 66 ->setQuery($this->getQuery()) 45 67 ->setViewer($this->getViewer()); 46 68 ··· 105 127 106 128 return parent::canEvaluateFunction($function); 107 129 } 108 - 109 130 110 131 protected function evaluateFunction($function, array $argv) { 111 132 foreach ($this->getUsableDatasources() as $source) {
+34 -1
src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php
··· 11 11 private $offset; 12 12 private $limit; 13 13 private $parameters = array(); 14 + private $functionStack = array(); 14 15 15 16 public function setLimit($limit) { 16 17 $this->limit = $limit; ··· 274 275 * @task functions 275 276 */ 276 277 protected function canEvaluateFunction($function) { 278 + return $this->shouldStripFunction($function); 279 + } 280 + 281 + 282 + /** 283 + * @task functions 284 + */ 285 + protected function shouldStripFunction($function) { 277 286 $functions = $this->getDatasourceFunctions(); 278 287 return isset($functions[$function]); 279 288 } ··· 337 346 $matches = null; 338 347 339 348 if ($allow_partial) { 340 - $ok = preg_match('/^([^(]+)\((.*)$/', $token, $matches); 349 + $ok = preg_match('/^([^(]+)\((.*?)\)?$/', $token, $matches); 341 350 } else { 342 351 $ok = preg_match('/^([^(]+)\((.*)\)$/', $token, $matches); 343 352 } ··· 366 375 throw new PhutilMethodNotImplementedException(); 367 376 } 368 377 378 + 379 + /** 380 + * @task functions 381 + */ 382 + public function setFunctionStack(array $function_stack) { 383 + $this->functionStack = $function_stack; 384 + return $this; 385 + } 386 + 387 + 388 + /** 389 + * @task functions 390 + */ 391 + public function getFunctionStack() { 392 + return $this->functionStack; 393 + } 394 + 395 + 396 + /** 397 + * @task functions 398 + */ 399 + protected function getCurrentFunction() { 400 + return nonempty(last($this->functionStack), null); 401 + } 369 402 370 403 }