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

Make "JIRA Issues" field work better with noncredentialed accounts

Summary:
Currently, users get an error when making any changes to this field if they don't have a linked JIRA account.

Instead:

- We should only raise an error if they're trying to //add// issues, and only on the new issues. It's always fine to remove issues, and existing issues the author can't see are also fine.
- When we can't add things because there's no account (vs because there's a permissions error or they don't exist), raise a more tailored exception.

Test Plan:
- As JIRA and non-JIRA users, made various edits to this field.
- Got appropriate exceptions, including better tailoring.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: mbishopim3, epriestley

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

+68 -11
+2
src/__phutil_library_map__.php
··· 622 622 'DoorkeeperFeedWorkerAsana' => 'applications/doorkeeper/worker/DoorkeeperFeedWorkerAsana.php', 623 623 'DoorkeeperFeedWorkerJIRA' => 'applications/doorkeeper/worker/DoorkeeperFeedWorkerJIRA.php', 624 624 'DoorkeeperImportEngine' => 'applications/doorkeeper/engine/DoorkeeperImportEngine.php', 625 + 'DoorkeeperMissingLinkException' => 'applications/doorkeeper/exception/DoorkeeperMissingLinkException.php', 625 626 'DoorkeeperObjectRef' => 'applications/doorkeeper/engine/DoorkeeperObjectRef.php', 626 627 'DoorkeeperRemarkupRule' => 'applications/doorkeeper/remarkup/DoorkeeperRemarkupRule.php', 627 628 'DoorkeeperRemarkupRuleAsana' => 'applications/doorkeeper/remarkup/DoorkeeperRemarkupRuleAsana.php', ··· 3196 3197 'DoorkeeperFeedWorkerAsana' => 'DoorkeeperFeedWorker', 3197 3198 'DoorkeeperFeedWorkerJIRA' => 'DoorkeeperFeedWorker', 3198 3199 'DoorkeeperImportEngine' => 'Phobject', 3200 + 'DoorkeeperMissingLinkException' => 'Exception', 3199 3201 'DoorkeeperObjectRef' => 'Phobject', 3200 3202 'DoorkeeperRemarkupRule' => 'PhutilRemarkupRule', 3201 3203 'DoorkeeperRemarkupRuleAsana' => 'DoorkeeperRemarkupRule',
+32 -9
src/applications/differential/customfield/DifferentialJIRAIssuesField.php
··· 116 116 } 117 117 118 118 public function getOldValueForApplicationTransactions() { 119 - return nonempty($this->getValue(), array()); 119 + return array_unique(nonempty($this->getValue(), array())); 120 120 } 121 121 122 122 public function getNewValueForApplicationTransactions() { 123 - return nonempty($this->getValue(), array()); 123 + return array_unique(nonempty($this->getValue(), array())); 124 124 } 125 125 126 126 public function validateApplicationTransactions( ··· 137 137 138 138 $transaction = null; 139 139 foreach ($xactions as $xaction) { 140 - $value = $xaction->getNewValue(); 140 + $old = $xaction->getOldValue(); 141 + $new = $xaction->getNewValue(); 142 + 143 + $add = array_diff($new, $old); 144 + if (!$add) { 145 + continue; 146 + } 147 + 148 + // Only check that the actor can see newly added JIRA refs. You're 149 + // allowed to remove refs or make no-op changes even if you aren't 150 + // linked to JIRA. 141 151 142 - $refs = id(new DoorkeeperImportEngine()) 143 - ->setViewer($this->getViewer()) 144 - ->setRefs($this->buildDoorkeeperRefs($value)) 145 - ->execute(); 152 + try { 153 + $refs = id(new DoorkeeperImportEngine()) 154 + ->setViewer($this->getViewer()) 155 + ->setRefs($this->buildDoorkeeperRefs($add)) 156 + ->setThrowOnMissingLink(true) 157 + ->execute(); 158 + } catch (DoorkeeperMissingLinkException $ex) { 159 + $this->error = pht('Not Linked'); 160 + $errors[] = new PhabricatorApplicationTransactionValidationError( 161 + $type, 162 + pht('Not Linked'), 163 + pht( 164 + 'You can not add JIRA issues (%s) to this revision because your '. 165 + 'Phabricator account is not linked to a JIRA account.', 166 + implode(', ', $add)), 167 + $xaction); 168 + continue; 169 + } 146 170 147 171 $bad = array(); 148 172 foreach ($refs as $ref) { ··· 155 179 $bad = implode(', ', $bad); 156 180 $this->error = pht('Invalid'); 157 181 158 - $error = new PhabricatorApplicationTransactionValidationError( 182 + $errors[] = new PhabricatorApplicationTransactionValidationError( 159 183 $type, 160 184 pht('Invalid'), 161 185 pht( ··· 163 187 "you may not have permission to view them: %s", 164 188 $bad), 165 189 $xaction); 166 - $errors[] = $error; 167 190 } 168 191 } 169 192
+15
src/applications/doorkeeper/bridge/DoorkeeperBridge.php
··· 3 3 abstract class DoorkeeperBridge extends Phobject { 4 4 5 5 private $viewer; 6 + private $throwOnMissingLink; 7 + 8 + public function setThrowOnMissingLink($throw_on_missing_link) { 9 + $this->throwOnMissingLink = $throw_on_missing_link; 10 + return $this; 11 + } 6 12 7 13 final public function setViewer($viewer) { 8 14 $this->viewer = $viewer; ··· 21 27 abstract public function pullRefs(array $refs); 22 28 23 29 public function fillObjectFromData(DoorkeeperExternalObject $obj, $result) { 30 + return; 31 + } 32 + 33 + public function didFailOnMissingLink() { 34 + if ($this->throwOnMissingLink) { 35 + throw new DoorkeeperMissingLinkException(); 36 + } 37 + 38 + return null; 24 39 } 25 40 26 41 }
+1 -1
src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php
··· 40 40 ->execute(); 41 41 42 42 if (!$accounts) { 43 - return; 43 + return $this->didFailOnMissingLink(); 44 44 } 45 45 46 46 // TODO: If the user has several linked Asana accounts, we just pick the
+1 -1
src/applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php
··· 34 34 ->execute(); 35 35 36 36 if (!$accounts) { 37 - return; 37 + return $this->didFailOnMissingLink(); 38 38 } 39 39 40 40 // TODO: When we support multiple JIRA instances, we need to disambiguate
+12
src/applications/doorkeeper/engine/DoorkeeperImportEngine.php
··· 6 6 private $refs = array(); 7 7 private $phids = array(); 8 8 private $localOnly; 9 + private $throwOnMissingLink; 9 10 10 11 public function setViewer(PhabricatorUser $viewer) { 11 12 $this->viewer = $viewer; ··· 33 34 34 35 public function needLocalOnly($local_only) { 35 36 $this->localOnly = $local_only; 37 + return $this; 38 + } 39 + 40 + 41 + /** 42 + * Configure behavior if remote refs can not be retrieved because an 43 + * authentication link is missing. 44 + */ 45 + public function setThrowOnMissingLink($throw) { 46 + $this->throwOnMissingLink = $throw; 36 47 return $this; 37 48 } 38 49 ··· 86 97 unset($bridges[$key]); 87 98 } 88 99 $bridge->setViewer($viewer); 100 + $bridge->setThrowOnMissingLink($this->throwOnMissingLink); 89 101 } 90 102 91 103 $working_set = $refs;
+5
src/applications/doorkeeper/exception/DoorkeeperMissingLinkException.php
··· 1 + <?php 2 + 3 + final class DoorkeeperMissingLinkException extends Exception { 4 + 5 + }