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

Modernize "Responsible Users" tokenizer and add "exact(user)" token

Summary:
Ref T10939. Fixes T9263. Ref T4144.

First, this resolves users (converting users into all packages and projects they are responsible for) earlier, so bucketing can act on that data correctly. Previously, your own blocking reviews would appear in "Must Review" but your packages/projects' would not. Now, all of them will.

Second, this adds `exact(username)` to mean "just me, not my packages/projects". You can use this along with "Bucket: By Required Action" to create a personal view of "Active Revisions" if you'd like, and ignore all your project/package reviews.

Test Plan: Queried by "me" and "exact(me)", got reasonable looking results.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T4144, T9263, T10939

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

+244 -29
+6
src/__phutil_library_map__.php
··· 435 435 'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php', 436 436 'DifferentialDraft' => 'applications/differential/storage/DifferentialDraft.php', 437 437 'DifferentialEditPolicyField' => 'applications/differential/customfield/DifferentialEditPolicyField.php', 438 + 'DifferentialExactUserFunctionDatasource' => 'applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php', 438 439 'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php', 439 440 'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php', 440 441 'DifferentialFindConduitAPIMethod' => 'applications/differential/conduit/DifferentialFindConduitAPIMethod.php', ··· 491 492 'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php', 492 493 'DifferentialRepositoryLookup' => 'applications/differential/query/DifferentialRepositoryLookup.php', 493 494 'DifferentialRequiredSignaturesField' => 'applications/differential/customfield/DifferentialRequiredSignaturesField.php', 495 + 'DifferentialResponsibleDatasource' => 'applications/differential/typeahead/DifferentialResponsibleDatasource.php', 496 + 'DifferentialResponsibleUserDatasource' => 'applications/differential/typeahead/DifferentialResponsibleUserDatasource.php', 494 497 'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php', 495 498 'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php', 496 499 'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php', ··· 4643 4646 'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher', 4644 4647 'DifferentialDraft' => 'DifferentialDAO', 4645 4648 'DifferentialEditPolicyField' => 'DifferentialCoreCustomField', 4649 + 'DifferentialExactUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 4646 4650 'DifferentialFieldParseException' => 'Exception', 4647 4651 'DifferentialFieldValidationException' => 'Exception', 4648 4652 'DifferentialFindConduitAPIMethod' => 'DifferentialConduitAPIMethod', ··· 4705 4709 'DifferentialRepositoryField' => 'DifferentialCoreCustomField', 4706 4710 'DifferentialRepositoryLookup' => 'Phobject', 4707 4711 'DifferentialRequiredSignaturesField' => 'DifferentialCoreCustomField', 4712 + 'DifferentialResponsibleDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 4713 + 'DifferentialResponsibleUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 4708 4714 'DifferentialRevertPlanField' => 'DifferentialStoredCustomField', 4709 4715 'DifferentialReviewedByField' => 'DifferentialCoreCustomField', 4710 4716 'DifferentialReviewer' => 'Phobject',
+2 -24
src/applications/differential/query/DifferentialRevisionQuery.php
··· 501 501 $basic_authors = $this->authors; 502 502 $basic_reviewers = $this->reviewers; 503 503 504 - $authority_phids = $this->responsibles; 505 - 506 - $authority_projects = id(new PhabricatorProjectQuery()) 507 - ->setViewer($this->getViewer()) 508 - ->withMemberPHIDs($this->responsibles) 509 - ->execute(); 510 - foreach ($authority_projects as $project) { 511 - $authority_phids[] = $project->getPHID(); 512 - } 513 - 514 - // NOTE: We're querying by explicit owners to make this a little faster, 515 - // since we've already expanded project membership so we don't need to 516 - // have the PackageQuery do it again. 517 - $authority_packages = id(new PhabricatorOwnersPackageQuery()) 518 - ->setViewer($this->getViewer()) 519 - ->withOwnerPHIDs($authority_phids) 520 - ->execute(); 521 - foreach ($authority_packages as $package) { 522 - $authority_phids[] = $package->getPHID(); 523 - } 524 - 525 504 try { 526 505 // Build the query where the responsible users are authors. 527 506 $this->authors = array_merge($basic_authors, $this->responsibles); 507 + 528 508 $this->reviewers = $basic_reviewers; 529 509 $selects[] = $this->buildSelectStatement($conn_r); 530 510 531 511 // Build the query where the responsible users are reviewers, or 532 512 // projects they are members of are reviewers. 533 513 $this->authors = $basic_authors; 534 - $this->reviewers = array_merge( 535 - $basic_reviewers, 536 - $authority_phids); 514 + $this->reviewers = array_merge($basic_reviewers, $this->responsibles); 537 515 $selects[] = $this->buildSelectStatement($conn_r); 538 516 539 517 // Put everything back like it was.
+1 -1
src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php
··· 17 17 18 18 $this->objects = $objects; 19 19 20 - $phids = $query->getParameter('responsiblePHIDs', array()); 20 + $phids = $query->getEvaluatedParameter('responsiblePHIDs', array()); 21 21 if (!$phids) { 22 22 throw new Exception( 23 23 pht(
+3 -2
src/applications/differential/query/DifferentialRevisionSearchEngine.php
··· 51 51 52 52 protected function buildCustomSearchFields() { 53 53 return array( 54 - id(new PhabricatorUsersSearchField()) 54 + id(new PhabricatorSearchDatasourceField()) 55 55 ->setLabel(pht('Responsible Users')) 56 56 ->setKey('responsiblePHIDs') 57 57 ->setAliases(array('responsiblePHID', 'responsibles', 'responsible')) 58 + ->setDatasource(new DifferentialResponsibleDatasource()) 58 59 ->setDescription( 59 60 pht('Find revisions that a given user is responsible for.')), 60 61 id(new PhabricatorUsersSearchField()) ··· 67 68 ->setLabel(pht('Reviewers')) 68 69 ->setKey('reviewerPHIDs') 69 70 ->setAliases(array('reviewer', 'reviewers', 'reviewerPHID')) 70 - ->setDatasource(new DiffusionAuditorDatasource()) 71 + ->setDatasource(new DiffusionAuditorFunctionDatasource()) 71 72 ->setDescription( 72 73 pht('Find revisions with specific reviewers.')), 73 74 id(new PhabricatorSearchDatasourceField())
+115
src/applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php
··· 1 + <?php 2 + 3 + final class DifferentialExactUserFunctionDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Users'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type exact(<user>)...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return 'PhabricatorDifferentialApplication'; 16 + } 17 + 18 + public function getComponentDatasources() { 19 + return array( 20 + new PhabricatorPeopleDatasource(), 21 + ); 22 + } 23 + 24 + public function getDatasourceFunctions() { 25 + return array( 26 + 'exact' => array( 27 + 'name' => pht('Exact: ...'), 28 + 'arguments' => pht('username'), 29 + 'summary' => pht('Find results matching users exactly.'), 30 + 'description' => pht( 31 + "This function allows you to find results associated only with ". 32 + "a user, exactly, and not any of their projects or packages. For ". 33 + "example, this will find results associated with only `%s`:". 34 + "\n\n%s\n\n", 35 + 'alincoln', 36 + '> exact(alincoln)'), 37 + ), 38 + ); 39 + } 40 + 41 + protected function didLoadResults(array $results) { 42 + foreach ($results as $result) { 43 + $result 44 + ->setColor(null) 45 + ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) 46 + ->setIcon('fa-asterisk') 47 + ->setPHID('exact('.$result->getPHID().')') 48 + ->setDisplayName(pht('Exact User: %s', $result->getDisplayName())) 49 + ->setName($result->getName().' exact'); 50 + } 51 + 52 + return $results; 53 + } 54 + 55 + protected function evaluateFunction($function, array $argv_list) { 56 + $phids = array(); 57 + foreach ($argv_list as $argv) { 58 + $phids[] = head($argv); 59 + } 60 + 61 + return $this->resolvePHIDs($phids); 62 + } 63 + 64 + public function renderFunctionTokens($function, array $argv_list) { 65 + $phids = array(); 66 + foreach ($argv_list as $argv) { 67 + $phids[] = head($argv); 68 + } 69 + 70 + $phids = $this->resolvePHIDs($phids); 71 + 72 + $tokens = $this->renderTokens($phids); 73 + foreach ($tokens as $token) { 74 + $token->setColor(null); 75 + if ($token->isInvalid()) { 76 + $token 77 + ->setValue(pht('Exact User: Invalid User')); 78 + } else { 79 + $token 80 + ->setIcon('fa-asterisk') 81 + ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) 82 + ->setKey('exact('.$token->getKey().')') 83 + ->setValue(pht('Exact User: %s', $token->getValue())); 84 + } 85 + } 86 + 87 + return $tokens; 88 + } 89 + 90 + private function resolvePHIDs(array $phids) { 91 + $usernames = array(); 92 + foreach ($phids as $key => $phid) { 93 + if (phid_get_type($phid) != PhabricatorPeopleUserPHIDType::TYPECONST) { 94 + $usernames[$key] = $phid; 95 + } 96 + } 97 + 98 + if ($usernames) { 99 + $users = id(new PhabricatorPeopleQuery()) 100 + ->setViewer($this->getViewer()) 101 + ->withUsernames($usernames) 102 + ->execute(); 103 + $users = mpull($users, null, 'getUsername'); 104 + foreach ($usernames as $key => $username) { 105 + $user = idx($users, $username); 106 + if ($user) { 107 + $phids[$key] = $user->getPHID(); 108 + } 109 + } 110 + } 111 + 112 + return $phids; 113 + } 114 + 115 + }
+27
src/applications/differential/typeahead/DifferentialResponsibleDatasource.php
··· 1 + <?php 2 + 3 + final class DifferentialResponsibleDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Responsible Users'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type a user, project, or package name, or function...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return 'PhabricatorDifferentialApplication'; 16 + } 17 + 18 + public function getComponentDatasources() { 19 + return array( 20 + new DifferentialResponsibleUserDatasource(), 21 + new DifferentialExactUserFunctionDatasource(), 22 + new PhabricatorProjectDatasource(), 23 + new PhabricatorOwnersPackageDatasource(), 24 + ); 25 + } 26 + 27 + }
+58
src/applications/differential/typeahead/DifferentialResponsibleUserDatasource.php
··· 1 + <?php 2 + 3 + final class DifferentialResponsibleUserDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Users'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type a user name...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return 'PhabricatorDifferentialApplication'; 16 + } 17 + 18 + public function getComponentDatasources() { 19 + return array( 20 + new PhabricatorPeopleDatasource(), 21 + ); 22 + } 23 + 24 + protected function evaluateValues(array $values) { 25 + $viewer = $this->getViewer(); 26 + 27 + $phids = array(); 28 + foreach ($values as $value) { 29 + if (phid_get_type($value) == PhabricatorPeopleUserPHIDType::TYPECONST) { 30 + $phids[] = $value; 31 + } 32 + } 33 + 34 + if (!$phids) { 35 + return $values; 36 + } 37 + 38 + $projects = id(new PhabricatorProjectQuery()) 39 + ->setViewer($viewer) 40 + ->withMemberPHIDs($phids) 41 + ->execute(); 42 + foreach ($projects as $project) { 43 + $phids[] = $project->getPHID(); 44 + $values[] = $project->getPHID(); 45 + } 46 + 47 + $packages = id(new PhabricatorOwnersPackageQuery()) 48 + ->setViewer($viewer) 49 + ->withOwnerPHIDs($phids) 50 + ->execute(); 51 + foreach ($packages as $package) { 52 + $values[] = $package->getPHID(); 53 + } 54 + 55 + return $values; 56 + } 57 + 58 + }
+3 -2
src/applications/search/engine/PhabricatorApplicationSearchEngine.php
··· 143 143 * @param PhabricatorSavedQuery The saved query to operate on. 144 144 * @return The result of the query. 145 145 */ 146 - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 147 - $saved = clone $saved; 146 + public function buildQueryFromSavedQuery(PhabricatorSavedQuery $original) { 147 + $saved = clone $original; 148 148 $this->willUseSavedQuery($saved); 149 149 150 150 $fields = $this->buildSearchFields(); ··· 158 158 $map[$field->getKey()] = $value; 159 159 } 160 160 161 + $original->attachParameterMap($map); 161 162 $query = $this->buildQueryFromParameters($map); 162 163 163 164 $object = $this->newResultObject();
+11
src/applications/search/storage/PhabricatorSavedQuery.php
··· 7 7 protected $queryKey; 8 8 protected $engineClassName; 9 9 10 + private $parameterMap = self::ATTACHABLE; 11 + 10 12 protected function getConfiguration() { 11 13 return array( 12 14 self::CONFIG_SERIALIZATION => array( ··· 50 52 51 53 public function newEngine() { 52 54 return newv($this->getEngineClassName(), array()); 55 + } 56 + 57 + public function attachParameterMap(array $map) { 58 + $this->parameterMap = $map; 59 + return $this; 60 + } 61 + 62 + public function getEvaluatedParameter($key, $default = null) { 63 + return $this->assertAttachedKey($this->parameterMap, $key, $default); 53 64 } 54 65 55 66
+8
src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php
··· 142 142 return parent::canEvaluateFunction($function); 143 143 } 144 144 145 + protected function evaluateValues(array $values) { 146 + foreach ($this->getUsableDatasources() as $source) { 147 + $values = $source->evaluateValues($values); 148 + } 149 + 150 + return parent::evaluateValues($values); 151 + } 152 + 145 153 protected function evaluateFunction($function, array $argv) { 146 154 foreach ($this->getUsableDatasources() as $source) { 147 155 if ($source->canEvaluateFunction($function)) {
+10
src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php
··· 334 334 /** 335 335 * @task functions 336 336 */ 337 + protected function evaluateValues(array $values) { 338 + return $values; 339 + } 340 + 341 + 342 + /** 343 + * @task functions 344 + */ 337 345 public function evaluateTokens(array $tokens) { 338 346 $results = array(); 339 347 $evaluate = array(); ··· 344 352 $evaluate[] = $token; 345 353 } 346 354 } 355 + 356 + $results = $this->evaluateValues($results); 347 357 348 358 foreach ($evaluate as $function) { 349 359 $function = self::parseFunction($function);