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

at recaptime-dev/main 286 lines 7.6 kB view raw
1<?php 2 3final class DifferentialJIRAIssuesField 4 extends DifferentialStoredCustomField { 5 6 private $error; 7 8 public function getFieldKey() { 9 return 'phabricator:jira-issues'; 10 } 11 12 public function getFieldKeyForConduit() { 13 return 'jira.issues'; 14 } 15 16 public function isFieldEnabled() { 17 return (bool)PhabricatorJIRAAuthProvider::getJIRAProvider(); 18 } 19 20 public function canDisableField() { 21 return false; 22 } 23 24 public function getValueForStorage() { 25 return json_encode($this->getValue()); 26 } 27 28 public function setValueFromStorage($value) { 29 try { 30 $this->setValue(phutil_json_decode($value)); 31 } catch (PhutilJSONParserException $ex) { 32 $this->setValue(array()); 33 } 34 return $this; 35 } 36 37 public function getFieldName() { 38 return pht('JIRA Issues'); 39 } 40 41 public function getFieldDescription() { 42 return pht('Lists associated JIRA issues.'); 43 } 44 45 public function shouldAppearInPropertyView() { 46 return true; 47 } 48 49 public function renderPropertyViewLabel() { 50 return $this->getFieldName(); 51 } 52 53 public function renderPropertyViewValue(array $handles) { 54 $xobjs = $this->loadDoorkeeperExternalObjects($this->getValue()); 55 if (!$xobjs) { 56 return null; 57 } 58 59 $links = array(); 60 foreach ($xobjs as $xobj) { 61 $links[] = id(new DoorkeeperTagView()) 62 ->setExternalObject($xobj); 63 } 64 65 return phutil_implode_html(phutil_tag('br'), $links); 66 } 67 68 private function buildDoorkeeperRefs($value) { 69 $provider = PhabricatorJIRAAuthProvider::getJIRAProvider(); 70 71 $refs = array(); 72 if ($value) { 73 foreach ($value as $jira_key) { 74 $refs[] = id(new DoorkeeperObjectRef()) 75 ->setApplicationType(DoorkeeperBridgeJIRA::APPTYPE_JIRA) 76 ->setApplicationDomain($provider->getProviderDomain()) 77 ->setObjectType(DoorkeeperBridgeJIRA::OBJTYPE_ISSUE) 78 ->setObjectID($jira_key); 79 } 80 } 81 82 return $refs; 83 } 84 85 private function loadDoorkeeperExternalObjects($value) { 86 $refs = $this->buildDoorkeeperRefs($value); 87 if (!$refs) { 88 return array(); 89 } 90 91 $xobjs = id(new DoorkeeperExternalObjectQuery()) 92 ->setViewer($this->getViewer()) 93 ->withObjectKeys(mpull($refs, 'getObjectKey')) 94 ->execute(); 95 96 return $xobjs; 97 } 98 99 public function shouldAppearInEditView() { 100 return PhabricatorJIRAAuthProvider::getJIRAProvider(); 101 } 102 103 public function shouldAppearInApplicationTransactions() { 104 return PhabricatorJIRAAuthProvider::getJIRAProvider(); 105 } 106 107 public function readValueFromRequest(AphrontRequest $request) { 108 $this->setValue($request->getStrList($this->getFieldKey())); 109 return $this; 110 } 111 112 public function renderEditControl(array $handles) { 113 return id(new AphrontFormTextControl()) 114 ->setLabel(pht('JIRA Issues')) 115 ->setCaption( 116 pht('Example: %s', phutil_tag('tt', array(), 'JIS-3, JIS-9'))) 117 ->setName($this->getFieldKey()) 118 ->setValue(implode(', ', nonempty($this->getValue(), array()))) 119 ->setError($this->error); 120 } 121 122 public function getOldValueForApplicationTransactions() { 123 return array_unique(nonempty($this->getValue(), array())); 124 } 125 126 public function getNewValueForApplicationTransactions() { 127 return array_unique(nonempty($this->getValue(), array())); 128 } 129 130 public function validateApplicationTransactions( 131 PhabricatorApplicationTransactionEditor $editor, 132 $type, 133 array $xactions) { 134 135 $this->error = null; 136 137 $errors = parent::validateApplicationTransactions( 138 $editor, 139 $type, 140 $xactions); 141 142 $transaction = null; 143 foreach ($xactions as $xaction) { 144 $old = $xaction->getOldValue(); 145 $new = $xaction->getNewValue(); 146 147 $add = array_diff($new, $old); 148 if (!$add) { 149 continue; 150 } 151 152 // Only check that the actor can see newly added JIRA refs. You're 153 // allowed to remove refs or make no-op changes even if you aren't 154 // linked to JIRA. 155 156 try { 157 $refs = id(new DoorkeeperImportEngine()) 158 ->setViewer($this->getViewer()) 159 ->setRefs($this->buildDoorkeeperRefs($add)) 160 ->setThrowOnMissingLink(true) 161 ->execute(); 162 } catch (DoorkeeperMissingLinkException $ex) { 163 $this->error = pht('Not Linked'); 164 $errors[] = new PhabricatorApplicationTransactionValidationError( 165 $type, 166 pht('Not Linked'), 167 pht( 168 'You can not add JIRA issues (%s) to this revision because your '. 169 '%s account is not linked to a JIRA account.', 170 implode(', ', $add), 171 PlatformSymbols::getPlatformServerName()), 172 $xaction); 173 continue; 174 } 175 176 $bad = array(); 177 foreach ($refs as $ref) { 178 if (!$ref->getIsVisible()) { 179 $bad[] = $ref->getObjectID(); 180 } 181 } 182 183 if ($bad) { 184 $bad = implode(', ', $bad); 185 $this->error = pht('Invalid'); 186 187 $errors[] = new PhabricatorApplicationTransactionValidationError( 188 $type, 189 pht('Invalid'), 190 pht( 191 'Some JIRA issues could not be loaded. They may not exist, or '. 192 'you may not have permission to view them: %s', 193 $bad), 194 $xaction); 195 } 196 } 197 198 return $errors; 199 } 200 201 public function getApplicationTransactionTitle( 202 PhabricatorApplicationTransaction $xaction) { 203 204 $old = $xaction->getOldValue(); 205 if (!is_array($old)) { 206 $old = array(); 207 } 208 209 $new = $xaction->getNewValue(); 210 if (!is_array($new)) { 211 $new = array(); 212 } 213 214 $add = array_diff($new, $old); 215 $rem = array_diff($old, $new); 216 217 $author_phid = $xaction->getAuthorPHID(); 218 if ($add && $rem) { 219 return pht( 220 '%s updated JIRA issue(s): added %s: %s; removed %s: %s.', 221 $xaction->renderHandleLink($author_phid), 222 phutil_count($add), 223 implode(', ', $add), 224 phutil_count($rem), 225 implode(', ', $rem)); 226 } else if ($add) { 227 return pht( 228 '%s added %s JIRA issue(s): %s.', 229 $xaction->renderHandleLink($author_phid), 230 phutil_count($add), 231 implode(', ', $add)); 232 } else if ($rem) { 233 return pht( 234 '%s removed %s JIRA issue(s): %s.', 235 $xaction->renderHandleLink($author_phid), 236 phutil_count($rem), 237 implode(', ', $rem)); 238 } 239 240 return parent::getApplicationTransactionTitle($xaction); 241 } 242 243 public function applyApplicationTransactionExternalEffects( 244 PhabricatorApplicationTransaction $xaction) { 245 246 // Update the CustomField storage. 247 parent::applyApplicationTransactionExternalEffects($xaction); 248 249 // Now, synchronize the Doorkeeper edges. 250 $revision = $this->getObject(); 251 $revision_phid = $revision->getPHID(); 252 253 $edge_type = PhabricatorJiraIssueHasObjectEdgeType::EDGECONST; 254 $xobjs = $this->loadDoorkeeperExternalObjects($xaction->getNewValue()); 255 $edge_dsts = mpull($xobjs, 'getPHID'); 256 257 $edges = PhabricatorEdgeQuery::loadDestinationPHIDs( 258 $revision_phid, 259 $edge_type); 260 261 $editor = new PhabricatorEdgeEditor(); 262 263 foreach (array_diff($edges, $edge_dsts) as $rem_edge) { 264 $editor->removeEdge($revision_phid, $edge_type, $rem_edge); 265 } 266 267 foreach (array_diff($edge_dsts, $edges) as $add_edge) { 268 $editor->addEdge($revision_phid, $edge_type, $add_edge); 269 } 270 271 $editor->save(); 272 } 273 274 public function shouldAppearInConduitDictionary() { 275 return true; 276 } 277 278 public function shouldAppearInConduitTransactions() { 279 return true; 280 } 281 282 protected function newConduitEditParameterType() { 283 return new ConduitStringListParameterType(); 284 } 285 286}