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

Add an "only()" edge logic constraint, meaning "only the other constraints, exactly"

Summary:
See PHI57. For example, a query for "ios, only()" finds tags tasked with iOS, exactly, and no other tags.

I called this "only()" instead of "exact()" because we use the term/function "Exact" elsewhere with a different meaning, e.g. in Differential.

Test Plan:
Basic query for a tag:

{F5168857}

Same query with "only", finds tasks tagged with only that tag:

{F5168858}

Reviewers: chad

Reviewed By: chad

Differential Revision: https://secure.phabricator.com/D18543

+146 -1
+2
src/__phutil_library_map__.php
··· 3686 3686 'PhabricatorProjectLockTransaction' => 'applications/project/xaction/PhabricatorProjectLockTransaction.php', 3687 3687 'PhabricatorProjectLogicalAncestorDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalAncestorDatasource.php', 3688 3688 'PhabricatorProjectLogicalDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalDatasource.php', 3689 + 'PhabricatorProjectLogicalOnlyDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalOnlyDatasource.php', 3689 3690 'PhabricatorProjectLogicalOrNotDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php', 3690 3691 'PhabricatorProjectLogicalUserDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php', 3691 3692 'PhabricatorProjectLogicalViewerDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php', ··· 9179 9180 'PhabricatorProjectLockTransaction' => 'PhabricatorProjectTransactionType', 9180 9181 'PhabricatorProjectLogicalAncestorDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 9181 9182 'PhabricatorProjectLogicalDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 9183 + 'PhabricatorProjectLogicalOnlyDatasource' => 'PhabricatorTypeaheadDatasource', 9182 9184 'PhabricatorProjectLogicalOrNotDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 9183 9185 'PhabricatorProjectLogicalUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 9184 9186 'PhabricatorProjectLogicalViewerDatasource' => 'PhabricatorTypeaheadDatasource',
+1
src/applications/project/typeahead/PhabricatorProjectLogicalDatasource.php
··· 21 21 new PhabricatorProjectLogicalAncestorDatasource(), 22 22 new PhabricatorProjectLogicalOrNotDatasource(), 23 23 new PhabricatorProjectLogicalViewerDatasource(), 24 + new PhabricatorProjectLogicalOnlyDatasource(), 24 25 new PhabricatorProjectLogicalUserDatasource(), 25 26 ); 26 27 }
+76
src/applications/project/typeahead/PhabricatorProjectLogicalOnlyDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectLogicalOnlyDatasource 4 + extends PhabricatorTypeaheadDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Only'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type only()...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return 'PhabricatorProjectApplication'; 16 + } 17 + 18 + public function getDatasourceFunctions() { 19 + return array( 20 + 'only' => array( 21 + 'name' => pht('Only Match Other Constraints'), 22 + 'summary' => pht( 23 + 'Find results with only the specified tags.'), 24 + 'description' => pht( 25 + "This function is used with other tags, and causes the query to ". 26 + "match only results with exactly those tags. For example, to find ". 27 + "tasks tagged only iOS:". 28 + "\n\n". 29 + "> ios, only()". 30 + "\n\n". 31 + "This will omit results with any other project tag."), 32 + ), 33 + ); 34 + } 35 + 36 + public function loadResults() { 37 + $results = array( 38 + $this->renderOnlyFunctionToken(), 39 + ); 40 + return $this->filterResultsAgainstTokens($results); 41 + } 42 + 43 + protected function evaluateFunction($function, array $argv_list) { 44 + $results = array(); 45 + 46 + $results[] = new PhabricatorQueryConstraint( 47 + PhabricatorQueryConstraint::OPERATOR_ONLY, 48 + null); 49 + 50 + return $results; 51 + } 52 + 53 + public function renderFunctionTokens( 54 + $function, 55 + array $argv_list) { 56 + 57 + $tokens = array(); 58 + foreach ($argv_list as $argv) { 59 + $tokens[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult( 60 + $this->renderOnlyFunctionToken()); 61 + } 62 + 63 + return $tokens; 64 + } 65 + 66 + private function renderOnlyFunctionToken() { 67 + return $this->newFunctionResult() 68 + ->setName(pht('Only')) 69 + ->setPHID('only()') 70 + ->setIcon('fa-asterisk') 71 + ->setUnique(true) 72 + ->addAttribute( 73 + pht('Select only results with exactly the other specified tags.')); 74 + } 75 + 76 + }
+1 -1
src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php
··· 29 29 "\n\n". 30 30 "This normally means //your// projects, but if you save a query ". 31 31 "using this function and send it to someone else, it will mean ". 32 - "//their// projects when they run it (they become the currnet ". 32 + "//their// projects when they run it (they become the current ". 33 33 "viewer). This can be useful for building dashboard panels."), 34 34 ), 35 35 );
+1
src/infrastructure/query/constraint/PhabricatorQueryConstraint.php
··· 8 8 const OPERATOR_NULL = 'null'; 9 9 const OPERATOR_ANCESTOR = 'ancestor'; 10 10 const OPERATOR_EMPTY = 'empty'; 11 + const OPERATOR_ONLY = 'only'; 11 12 12 13 private $operator; 13 14 private $value;
+65
src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
··· 2069 2069 $op_null = PhabricatorQueryConstraint::OPERATOR_NULL; 2070 2070 $has_null = isset($constraints[$op_null]); 2071 2071 2072 + // If we're going to process an only() operator, build a list of the 2073 + // acceptable set of PHIDs first. We'll only match results which have 2074 + // no edges to any other PHIDs. 2075 + $all_phids = array(); 2076 + if (isset($constraints[PhabricatorQueryConstraint::OPERATOR_ONLY])) { 2077 + foreach ($constraints as $operator => $list) { 2078 + switch ($operator) { 2079 + case PhabricatorQueryConstraint::OPERATOR_ANCESTOR: 2080 + case PhabricatorQueryConstraint::OPERATOR_AND: 2081 + case PhabricatorQueryConstraint::OPERATOR_OR: 2082 + foreach ($list as $constraint) { 2083 + $value = (array)$constraint->getValue(); 2084 + foreach ($value as $v) { 2085 + $all_phids[$v] = $v; 2086 + } 2087 + } 2088 + break; 2089 + } 2090 + } 2091 + } 2092 + 2072 2093 foreach ($constraints as $operator => $list) { 2073 2094 $alias = $this->getEdgeLogicTableAlias($operator, $type); 2074 2095 ··· 2133 2154 $alias, 2134 2155 $type); 2135 2156 break; 2157 + case PhabricatorQueryConstraint::OPERATOR_ONLY: 2158 + $joins[] = qsprintf( 2159 + $conn, 2160 + 'LEFT JOIN %T %T ON %Q = %T.src AND %T.type = %d 2161 + AND %T.dst NOT IN (%Ls)', 2162 + $edge_table, 2163 + $alias, 2164 + $phid_column, 2165 + $alias, 2166 + $alias, 2167 + $type, 2168 + $alias, 2169 + $all_phids); 2170 + break; 2136 2171 } 2137 2172 } 2138 2173 } ··· 2159 2194 $alias = $this->getEdgeLogicTableAlias($operator, $type); 2160 2195 switch ($operator) { 2161 2196 case PhabricatorQueryConstraint::OPERATOR_NOT: 2197 + case PhabricatorQueryConstraint::OPERATOR_ONLY: 2162 2198 $full[] = qsprintf( 2163 2199 $conn, 2164 2200 '%T.dst IS NULL', ··· 2258 2294 // discussion, see T12753. 2259 2295 return true; 2260 2296 case PhabricatorQueryConstraint::OPERATOR_NULL: 2297 + case PhabricatorQueryConstraint::OPERATOR_ONLY: 2261 2298 return true; 2262 2299 } 2263 2300 } ··· 2371 2408 pht( 2372 2409 'This query is constrained by a project you do not have '. 2373 2410 'permission to see.')); 2411 + } 2412 + } 2413 + } 2414 + 2415 + $op_and = PhabricatorQueryConstraint::OPERATOR_AND; 2416 + $op_or = PhabricatorQueryConstraint::OPERATOR_OR; 2417 + $op_ancestor = PhabricatorQueryConstraint::OPERATOR_ANCESTOR; 2418 + 2419 + foreach ($this->edgeLogicConstraints as $type => $constraints) { 2420 + foreach ($constraints as $operator => $list) { 2421 + switch ($operator) { 2422 + case PhabricatorQueryConstraint::OPERATOR_ONLY: 2423 + if (count($list) > 1) { 2424 + throw new PhabricatorEmptyQueryException( 2425 + pht( 2426 + 'This query specifies only() more than once.')); 2427 + } 2428 + 2429 + $have_and = idx($constraints, $op_and); 2430 + $have_or = idx($constraints, $op_or); 2431 + $have_ancestor = idx($constraints, $op_ancestor); 2432 + if (!$have_and && !$have_or && !$have_ancestor) { 2433 + throw new PhabricatorEmptyQueryException( 2434 + pht( 2435 + 'This query specifies only(), but no other constraints '. 2436 + 'which it can apply to.')); 2437 + } 2438 + break; 2374 2439 } 2375 2440 } 2376 2441 }