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

Allow blocking reviewers to be added via the web UI

Summary:
Ref T10939. Adds a `blocking(...)` token.

This code is pretty iffy and going to get worse before it gets better, but the fix (T10967 + EditEngine) is going to be a fair chunk of work down the road.

Test Plan: {F1426966}

Reviewers: chad

Reviewed By: chad

Subscribers: scode

Maniphest Tasks: T10939

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

+221 -20
+4
src/__phutil_library_map__.php
··· 361 361 'DifferentialAuthorField' => 'applications/differential/customfield/DifferentialAuthorField.php', 362 362 'DifferentialBlameRevisionField' => 'applications/differential/customfield/DifferentialBlameRevisionField.php', 363 363 'DifferentialBlockHeraldAction' => 'applications/differential/herald/DifferentialBlockHeraldAction.php', 364 + 'DifferentialBlockingReviewerDatasource' => 'applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php', 364 365 'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php', 365 366 'DifferentialChangeDetailMailView' => 'applications/differential/mail/DifferentialChangeDetailMailView.php', 366 367 'DifferentialChangeHeraldFieldGroup' => 'applications/differential/herald/DifferentialChangeHeraldFieldGroup.php', ··· 497 498 'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php', 498 499 'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php', 499 500 'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php', 501 + 'DifferentialReviewerDatasource' => 'applications/differential/typeahead/DifferentialReviewerDatasource.php', 500 502 'DifferentialReviewerForRevisionEdgeType' => 'applications/differential/edge/DifferentialReviewerForRevisionEdgeType.php', 501 503 'DifferentialReviewerStatus' => 'applications/differential/constants/DifferentialReviewerStatus.php', 502 504 'DifferentialReviewersAddBlockingReviewersHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddBlockingReviewersHeraldAction.php', ··· 4561 4563 'DifferentialAuthorField' => 'DifferentialCustomField', 4562 4564 'DifferentialBlameRevisionField' => 'DifferentialStoredCustomField', 4563 4565 'DifferentialBlockHeraldAction' => 'HeraldAction', 4566 + 'DifferentialBlockingReviewerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 4564 4567 'DifferentialBranchField' => 'DifferentialCustomField', 4565 4568 'DifferentialChangeDetailMailView' => 'DifferentialMailView', 4566 4569 'DifferentialChangeHeraldFieldGroup' => 'HeraldFieldGroup', ··· 4713 4716 'DifferentialRevertPlanField' => 'DifferentialStoredCustomField', 4714 4717 'DifferentialReviewedByField' => 'DifferentialCoreCustomField', 4715 4718 'DifferentialReviewer' => 'Phobject', 4719 + 'DifferentialReviewerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 4716 4720 'DifferentialReviewerForRevisionEdgeType' => 'PhabricatorEdgeType', 4717 4721 'DifferentialReviewerStatus' => 'Phobject', 4718 4722 'DifferentialReviewersAddBlockingReviewersHeraldAction' => 'DifferentialReviewersHeraldAction',
+59 -19
src/applications/differential/customfield/DifferentialReviewersField.php
··· 36 36 } 37 37 38 38 public function readValueFromRequest(AphrontRequest $request) { 39 - // Compute a new set of reviewer objects. For reviewers who haven't been 40 - // added or removed, retain their existing status. Also, respect the new 41 - // order. 39 + // Compute a new set of reviewer objects. We're going to respect the new 40 + // reviewer order, add or remove any missing or new reviewers, and respect 41 + // any blocking or unblocking changes. For reviewers who were there before 42 + // and are still there, we're going to keep the current value because it 43 + // may be something like "Accept", "Reject", etc. 42 44 43 45 $old_status = $this->getValue(); 44 - $old_status = mpull($old_status, null, 'getReviewerPHID'); 46 + $old_status = mpull($old_status, 'getStatus', 'getReviewerPHID'); 47 + 48 + $datasource = id(new DifferentialBlockingReviewerDatasource()) 49 + ->setViewer($request->getViewer()); 45 50 46 51 $new_phids = $request->getArr($this->getFieldKey()); 47 - $new_phids = array_fuse($new_phids); 52 + $new_phids = $datasource->evaluateTokens($new_phids); 53 + 54 + $status_blocking = DifferentialReviewerStatus::STATUS_BLOCKING; 55 + 56 + $specs = array(); 57 + foreach ($new_phids as $spec) { 58 + if (!is_array($spec)) { 59 + $spec = array( 60 + 'type' => DifferentialReviewerStatus::STATUS_ADDED, 61 + 'phid' => $spec, 62 + ); 63 + } 64 + $specs[$spec['phid']] = $spec; 65 + } 48 66 49 67 $new_status = array(); 50 - foreach ($new_phids as $new_phid) { 51 - if (empty($old_status[$new_phid])) { 52 - $new_status[$new_phid] = new DifferentialReviewer( 53 - $new_phid, 54 - array( 55 - 'status' => DifferentialReviewerStatus::STATUS_ADDED, 56 - )); 57 - } else { 58 - $new_status[$new_phid] = $old_status[$new_phid]; 68 + foreach ($specs as $phid => $spec) { 69 + $new = $spec['type']; 70 + $old = idx($old_status, $phid); 71 + 72 + // If we have an old status and this didn't make the reviewer blocking 73 + // or nonblocking, just retain the old status. This makes sure we don't 74 + // throw away rejects, accepts, etc. 75 + if ($old) { 76 + $is_block = ($old !== $status_blocking && $new === $status_blocking); 77 + $is_unblock = ($old === $status_blocking && $new !== $status_blocking); 78 + if (!$is_block && !$is_unblock) { 79 + $new_status[$phid] = $old; 80 + continue; 81 + } 59 82 } 83 + 84 + $new_status[$phid] = $new; 85 + } 86 + 87 + foreach ($new_status as $phid => $status) { 88 + $new_status[$phid] = new DifferentialReviewer( 89 + $phid, 90 + array( 91 + 'status' => $status, 92 + )); 60 93 } 61 94 62 95 $this->setValue($new_status); 63 96 } 64 97 65 98 public function renderEditControl(array $handles) { 66 - $phids = array(); 67 - if ($this->getValue()) { 68 - $phids = mpull($this->getValue(), 'getReviewerPHID'); 99 + $status_blocking = DifferentialReviewerStatus::STATUS_BLOCKING; 100 + 101 + $value = array(); 102 + foreach ($this->getValue() as $reviewer) { 103 + $phid = $reviewer->getReviewerPHID(); 104 + if ($reviewer->getStatus() == $status_blocking) { 105 + $value[] = 'blocking('.$phid.')'; 106 + } else { 107 + $value[] = $phid; 108 + } 69 109 } 70 110 71 111 return id(new AphrontFormTokenizerControl()) 72 112 ->setUser($this->getViewer()) 73 113 ->setName($this->getFieldKey()) 74 - ->setDatasource(new DiffusionAuditorDatasource()) 75 - ->setValue($phids) 114 + ->setDatasource(new DifferentialReviewerDatasource()) 115 + ->setValue($value) 76 116 ->setError($this->getFieldError()) 77 117 ->setLabel($this->getFieldName()); 78 118 }
+128
src/applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php
··· 1 + <?php 2 + 3 + final class DifferentialBlockingReviewerDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Blocking Reviewers'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type a user, project, or package name...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return 'PhabricatorDifferentialApplication'; 16 + } 17 + 18 + public function getComponentDatasources() { 19 + return array( 20 + new PhabricatorPeopleDatasource(), 21 + new PhabricatorProjectDatasource(), 22 + new PhabricatorOwnersPackageDatasource(), 23 + ); 24 + } 25 + 26 + public function getDatasourceFunctions() { 27 + return array( 28 + 'blocking' => array( 29 + 'name' => pht('Blocking: ...'), 30 + 'arguments' => pht('reviewer'), 31 + 'summary' => pht('Select a blocking reviewer.'), 32 + 'description' => pht( 33 + "This function allows you to add a reviewer as a blocking ". 34 + "reviewer. For example, this will add `%s` as a blocking reviewer: ". 35 + "\n\n%s\n\n", 36 + 'alincoln', 37 + '> blocking(alincoln)'), 38 + ), 39 + ); 40 + } 41 + 42 + 43 + protected function didLoadResults(array $results) { 44 + foreach ($results as $result) { 45 + $result 46 + ->setColor('red') 47 + ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) 48 + ->setIcon('fa-asterisk') 49 + ->setPHID('blocking('.$result->getPHID().')') 50 + ->setDisplayName(pht('Blocking: %s', $result->getDisplayName())) 51 + ->setName($result->getName().' blocking'); 52 + } 53 + 54 + return $results; 55 + } 56 + 57 + protected function evaluateFunction($function, array $argv_list) { 58 + $phids = array(); 59 + foreach ($argv_list as $argv) { 60 + $phids[] = head($argv); 61 + } 62 + 63 + $phids = $this->resolvePHIDs($phids); 64 + 65 + $results = array(); 66 + foreach ($phids as $phid) { 67 + $results[] = array( 68 + 'type' => DifferentialReviewerStatus::STATUS_BLOCKING, 69 + 'phid' => $phid, 70 + ); 71 + } 72 + 73 + return $results; 74 + } 75 + 76 + public function renderFunctionTokens($function, array $argv_list) { 77 + $phids = array(); 78 + foreach ($argv_list as $argv) { 79 + $phids[] = head($argv); 80 + } 81 + 82 + $phids = $this->resolvePHIDs($phids); 83 + 84 + $tokens = $this->renderTokens($phids); 85 + foreach ($tokens as $token) { 86 + $token->setColor(null); 87 + if ($token->isInvalid()) { 88 + $token 89 + ->setValue(pht('Blocking: Invalid Reviewer')); 90 + } else { 91 + $token 92 + ->setIcon('fa-asterisk') 93 + ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) 94 + ->setColor('red') 95 + ->setKey('blocking('.$token->getKey().')') 96 + ->setValue(pht('Blocking: %s', $token->getValue())); 97 + } 98 + } 99 + 100 + return $tokens; 101 + } 102 + 103 + private function resolvePHIDs(array $phids) { 104 + $usernames = array(); 105 + foreach ($phids as $key => $phid) { 106 + if (phid_get_type($phid) != PhabricatorPeopleUserPHIDType::TYPECONST) { 107 + $usernames[$key] = $phid; 108 + } 109 + } 110 + 111 + if ($usernames) { 112 + $users = id(new PhabricatorPeopleQuery()) 113 + ->setViewer($this->getViewer()) 114 + ->withUsernames($usernames) 115 + ->execute(); 116 + $users = mpull($users, null, 'getUsername'); 117 + foreach ($usernames as $key => $username) { 118 + $user = idx($users, $username); 119 + if ($user) { 120 + $phids[$key] = $user->getPHID(); 121 + } 122 + } 123 + } 124 + 125 + return $phids; 126 + } 127 + 128 + }
+27
src/applications/differential/typeahead/DifferentialReviewerDatasource.php
··· 1 + <?php 2 + 3 + final class DifferentialReviewerDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Reviewers'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type a user, project, or package name...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return 'PhabricatorDifferentialApplication'; 16 + } 17 + 18 + public function getComponentDatasources() { 19 + return array( 20 + new PhabricatorPeopleDatasource(), 21 + new PhabricatorProjectDatasource(), 22 + new PhabricatorOwnersPackageDatasource(), 23 + new DifferentialBlockingReviewerDatasource(), 24 + ); 25 + } 26 + 27 + }
+3 -1
src/applications/differential/view/DifferentialAddCommentView.php
··· 69 69 ); 70 70 71 71 $mailable_source = new PhabricatorMetaMTAMailableDatasource(); 72 - $reviewer_source = new PhabricatorProjectOrUserDatasource(); 72 + 73 + // TODO: This should be a reviewers datasource, but it's a mess. 74 + $reviewer_source = new PhabricatorMetaMTAMailableDatasource(); 73 75 74 76 $form = new AphrontFormView(); 75 77 $form