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

Summary: Ref T10939. Fixes T4887. Supports "username!" to add a reviewer as blocking.

Test Plan: Added and removed blocking and non-blocking reviewers via CLI.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T4887, T10939

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

+175 -47
+16 -4
src/applications/differential/customfield/DifferentialCustomField.php
··· 35 35 protected function parseObjectList( 36 36 $value, 37 37 array $types, 38 - $allow_partial = false) { 38 + $allow_partial = false, 39 + array $suffixes = array()) { 39 40 return id(new PhabricatorObjectListQuery()) 40 41 ->setViewer($this->getViewer()) 41 42 ->setAllowedTypes($types) 42 43 ->setObjectList($value) 43 44 ->setAllowPartialResults($allow_partial) 45 + ->setSuffixes($suffixes) 44 46 ->execute(); 45 47 } 46 48 47 - protected function renderObjectList(array $handles) { 49 + protected function renderObjectList( 50 + array $handles, 51 + array $suffixes = array()) { 52 + 48 53 if (!$handles) { 49 54 return null; 50 55 } 51 56 52 57 $out = array(); 53 58 foreach ($handles as $handle) { 59 + $phid = $handle->getPHID(); 60 + 54 61 if ($handle->getPolicyFiltered()) { 55 - $out[] = $handle->getPHID(); 62 + $token = $phid; 56 63 } else if ($handle->isComplete()) { 57 - $out[] = $handle->getCommandLineObjectName(); 64 + $token = $handle->getCommandLineObjectName(); 58 65 } 66 + 67 + $suffix = idx($suffixes, $phid); 68 + $token = $token.$suffix; 69 + 70 + $out[] = $token; 59 71 } 60 72 61 73 return implode(', ', $out);
+63 -41
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. 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. 44 - 45 - $old_status = $this->getValue(); 46 - $old_status = mpull($old_status, 'getStatus', 'getReviewerPHID'); 47 - 48 39 $datasource = id(new DifferentialBlockingReviewerDatasource()) 49 40 ->setViewer($request->getViewer()); 50 41 51 42 $new_phids = $request->getArr($this->getFieldKey()); 52 43 $new_phids = $datasource->evaluateTokens($new_phids); 53 44 54 - $status_blocking = DifferentialReviewerStatus::STATUS_BLOCKING; 55 - 56 - $specs = array(); 45 + $reviewers = array(); 57 46 foreach ($new_phids as $spec) { 58 47 if (!is_array($spec)) { 59 - $spec = array( 60 - 'type' => DifferentialReviewerStatus::STATUS_ADDED, 61 - 'phid' => $spec, 62 - ); 48 + $reviewers[$spec] = DifferentialReviewerStatus::STATUS_ADDED; 49 + } else { 50 + $reviewers[$spec['phid']] = $spec['type']; 63 51 } 64 - $specs[$spec['phid']] = $spec; 65 52 } 66 53 67 - $new_status = array(); 68 - foreach ($specs as $phid => $spec) { 69 - $new = $spec['type']; 70 - $old = idx($old_status, $phid); 54 + $this->updateReviewers($this->getValue(), $reviewers); 55 + } 56 + 57 + private function updateReviewers(array $old_reviewers, array $new_map) { 58 + // Compute a new set of reviewer objects. We're going to respect the new 59 + // reviewer order, add or remove any new or missing reviewers, and respect 60 + // any blocking or unblocking changes. For reviewers who were there before 61 + // and are still there, we're going to keep the old value because it 62 + // may be something like "Accept", "Reject", etc. 63 + 64 + $old_map = mpull($old_reviewers, 'getStatus', 'getReviewerPHID'); 65 + $status_blocking = DifferentialReviewerStatus::STATUS_BLOCKING; 66 + 67 + $new_reviewers = array(); 68 + foreach ($new_map as $phid => $new) { 69 + $old = idx($old_map, $phid); 71 70 72 71 // If we have an old status and this didn't make the reviewer blocking 73 72 // or nonblocking, just retain the old status. This makes sure we don't ··· 76 75 $is_block = ($old !== $status_blocking && $new === $status_blocking); 77 76 $is_unblock = ($old === $status_blocking && $new !== $status_blocking); 78 77 if (!$is_block && !$is_unblock) { 79 - $new_status[$phid] = $old; 78 + $new_reviewers[$phid] = $old; 80 79 continue; 81 80 } 82 81 } 83 82 84 - $new_status[$phid] = $new; 83 + $new_reviewers[$phid] = $new; 85 84 } 86 85 87 - foreach ($new_status as $phid => $status) { 88 - $new_status[$phid] = new DifferentialReviewer( 86 + foreach ($new_reviewers as $phid => $status) { 87 + $new_reviewers[$phid] = new DifferentialReviewer( 89 88 $phid, 90 89 array( 91 90 'status' => $status, 92 91 )); 93 92 } 94 93 95 - $this->setValue($new_status); 94 + $this->setValue($new_reviewers); 96 95 } 97 96 98 97 public function renderEditControl(array $handles) { ··· 187 186 PhabricatorPeopleUserPHIDType::TYPECONST, 188 187 PhabricatorProjectProjectPHIDType::TYPECONST, 189 188 PhabricatorOwnersPackagePHIDType::TYPECONST, 190 - )); 189 + ), 190 + false, 191 + array('!')); 191 192 } 192 193 193 194 public function getRequiredHandlePHIDsForCommitMessage() { ··· 195 196 } 196 197 197 198 public function readValueFromCommitMessage($value) { 198 - $current_reviewers = $this->getObject()->getReviewerStatus(); 199 - $current_reviewers = mpull($current_reviewers, null, 'getReviewerPHID'); 199 + $reviewers = array(); 200 + foreach ($value as $spec) { 201 + $phid = $spec['phid']; 200 202 201 - $reviewers = array(); 202 - foreach ($value as $phid) { 203 - $reviewer = idx($current_reviewers, $phid); 204 - if ($reviewer) { 205 - $reviewers[] = $reviewer; 203 + $is_blocking = isset($spec['suffixes']['!']); 204 + if ($is_blocking) { 205 + $status = DifferentialReviewerStatus::STATUS_BLOCKING; 206 206 } else { 207 - $data = array( 208 - 'status' => DifferentialReviewerStatus::STATUS_ADDED, 209 - ); 210 - $reviewers[] = new DifferentialReviewer($phid, $data); 207 + $status = DifferentialReviewerStatus::STATUS_ADDED; 211 208 } 209 + 210 + $reviewers[$phid] = $status; 212 211 } 213 212 214 - $this->setValue($reviewers); 213 + $this->updateReviewers( 214 + $this->getObject()->getReviewerStatus(), 215 + $reviewers); 215 216 216 217 return $this; 217 218 } 218 219 219 220 public function renderCommitMessageValue(array $handles) { 220 - return $this->renderObjectList($handles); 221 + $suffixes = array(); 222 + 223 + $status_blocking = DifferentialReviewerStatus::STATUS_BLOCKING; 224 + 225 + foreach ($this->getValue() as $reviewer) { 226 + if ($reviewer->getStatus() == $status_blocking) { 227 + $phid = $reviewer->getReviewerPHID(); 228 + $suffixes[$phid] = '!'; 229 + } 230 + } 231 + 232 + return $this->renderObjectList($handles, $suffixes); 221 233 } 222 234 223 235 public function validateCommitMessageValue($value) { ··· 226 238 $config_self_accept_key = 'differential.allow-self-accept'; 227 239 $allow_self_accept = PhabricatorEnv::getEnvConfig($config_self_accept_key); 228 240 229 - foreach ($value as $phid) { 241 + foreach ($value as $spec) { 242 + $phid = $spec['phid']; 243 + 230 244 if (($phid == $author_phid) && !$allow_self_accept) { 231 245 throw new DifferentialFieldValidationException( 232 246 pht('The author of a revision can not be a reviewer.')); ··· 263 277 } 264 278 265 279 return $warnings; 280 + } 281 + 282 + public function getProTips() { 283 + return array( 284 + pht( 285 + 'You can mark a reviewer as blocking by adding an exclamation '. 286 + 'mark ("!") after their name.'), 287 + ); 266 288 } 267 289 268 290 }
+64 -1
src/applications/phid/query/PhabricatorObjectListQuery.php
··· 6 6 private $objectList; 7 7 private $allowedTypes = array(); 8 8 private $allowPartialResults; 9 + private $suffixes = array(); 9 10 10 11 public function setAllowPartialResults($allow_partial_results) { 11 12 $this->allowPartialResults = $allow_partial_results; ··· 14 15 15 16 public function getAllowPartialResults() { 16 17 return $this->allowPartialResults; 18 + } 19 + 20 + public function setSuffixes(array $suffixes) { 21 + $this->suffixes = $suffixes; 22 + return $this; 23 + } 24 + 25 + public function getSuffixes() { 26 + return $this->suffixes; 17 27 } 18 28 19 29 public function setAllowedTypes(array $allowed_types) { ··· 87 97 } 88 98 } 89 99 100 + // If we're parsing with suffixes, strip them off any tokens and keep 101 + // track of them for later. 102 + $suffixes = $this->getSuffixes(); 103 + if ($suffixes) { 104 + $suffixes = array_fuse($suffixes); 105 + $suffix_map = array(); 106 + $stripped_map = array(); 107 + foreach ($name_map as $key => $name) { 108 + $found_suffixes = array(); 109 + do { 110 + $has_any_suffix = false; 111 + foreach ($suffixes as $suffix) { 112 + if (!$this->hasSuffix($name, $suffix)) { 113 + continue; 114 + } 115 + 116 + $key = $this->stripSuffix($key, $suffix); 117 + $name = $this->stripSuffix($name, $suffix); 118 + 119 + $found_suffixes[] = $suffix; 120 + $has_any_suffix = true; 121 + break; 122 + } 123 + } while ($has_any_suffix); 124 + 125 + $stripped_map[$key] = $name; 126 + $suffix_map[$key] = array_fuse($found_suffixes); 127 + } 128 + $name_map = $stripped_map; 129 + } 130 + 90 131 $objects = $this->loadObjects(array_keys($name_map)); 91 132 92 133 $types = array(); ··· 140 181 } 141 182 } 142 183 143 - return array_values(array_unique(mpull($objects, 'getPHID'))); 184 + $result = array_unique(mpull($objects, 'getPHID')); 185 + 186 + if ($suffixes) { 187 + foreach ($result as $key => $phid) { 188 + $result[$key] = array( 189 + 'phid' => $phid, 190 + 'suffixes' => idx($suffix_map, $key, array()), 191 + ); 192 + } 193 + } 194 + 195 + return array_values($result); 144 196 } 145 197 146 198 private function loadObjects($names) { ··· 186 238 return $results; 187 239 } 188 240 241 + private function hasSuffix($key, $suffix) { 242 + return (substr($key, -strlen($suffix)) === $suffix); 243 + } 244 + 245 + private function stripSuffix($key, $suffix) { 246 + if ($this->hasSuffix($key, $suffix)) { 247 + return substr($key, 0, -strlen($suffix)); 248 + } 249 + 250 + return $key; 251 + } 189 252 190 253 }
+32 -1
src/applications/phid/query/__tests__/PhabricatorObjectListQueryTestCase.php
··· 28 28 $result = $this->parseObjectList(''); 29 29 $this->assertEqual(array(), $result); 30 30 31 + $result = $this->parseObjectList("{$name}!", array(), false, array('!')); 32 + $this->assertEqual( 33 + array( 34 + array( 35 + 'phid' => $phid, 36 + 'suffixes' => array('!' => '!'), 37 + ), 38 + ), 39 + $result); 31 40 32 41 $package = PhabricatorOwnersPackage::initializeNewPackage($user) 33 42 ->setName(pht('Query Test Package')) ··· 42 51 $result = $this->parseObjectList("{$package_mono} Any Text, {$name}"); 43 52 $this->assertEqual(array($package_phid, $phid), $result); 44 53 54 + $result = $this->parseObjectList( 55 + "{$package_mono} Any Text!, {$name}", 56 + array(), 57 + false, 58 + array('!')); 59 + $this->assertEqual( 60 + array( 61 + array( 62 + 'phid' => $package_phid, 63 + 'suffixes' => array('!' => '!'), 64 + ), 65 + array( 66 + 'phid' => $phid, 67 + 'suffixes' => array(), 68 + ), 69 + ), 70 + $result); 45 71 46 72 // Expect failure when loading a user if objects must be of type "DUCK". 47 73 $caught = null; ··· 81 107 private function parseObjectList( 82 108 $list, 83 109 array $types = array(), 84 - $allow_partial = false) { 110 + $allow_partial = false, 111 + $suffixes = array()) { 85 112 86 113 $query = id(new PhabricatorObjectListQuery()) 87 114 ->setViewer(PhabricatorUser::getOmnipotentUser()) ··· 93 120 94 121 if ($allow_partial) { 95 122 $query->setAllowPartialResults(true); 123 + } 124 + 125 + if ($suffixes) { 126 + $query->setSuffixes($suffixes); 96 127 } 97 128 98 129 return $query->execute();