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

Kinda start bridging data in from GitHub via Nuance

Summary: Ref T10538. Very sloppy, but starting to sort of work. This sort of gets a piece of framework into a reasonable spot, next couple of diffs are going to be "extract comment text" and "show stuff in the UI" sorts of things.

Test Plan: {F1186726}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10538

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

+196 -26
+13 -3
src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHubIssue.php
··· 76 76 $ref->setAttribute('name', $body['title']); 77 77 78 78 $obj = $ref->getExternalObject(); 79 - if ($obj->getID()) { 80 - continue; 81 - } 82 79 83 80 $this->fillObjectFromData($obj, $result); 84 81 ··· 92 89 $body = $result->getBody(); 93 90 $uri = $body['html_url']; 94 91 $obj->setObjectURI($uri); 92 + 93 + $title = idx($body, 'title'); 94 + $description = idx($body, 'body'); 95 + 96 + $created = idx($body, 'created_at'); 97 + $created = strtotime($created); 98 + 99 + $state = idx($body, 'state'); 100 + 101 + $obj->setProperty('task.title', $title); 102 + $obj->setProperty('task.description', $description); 103 + $obj->setProperty('task.created', $created); 104 + $obj->setProperty('task.state', $state); 95 105 } 96 106 97 107 }
+4
src/applications/nuance/github/NuanceGitHubRawEvent.php
··· 90 90 return null; 91 91 } 92 92 93 + public function getComment() { 94 + return 'TODO: Actually extract comment text.'; 95 + } 96 + 93 97 public function getURI() { 94 98 $raw = $this->raw; 95 99
+176 -22
src/applications/nuance/item/NuanceGitHubEventItemType.php
··· 5 5 6 6 const ITEMTYPE = 'github.event'; 7 7 8 + private $externalObject; 9 + 8 10 public function getItemTypeDisplayName() { 9 11 return pht('GitHub Event'); 10 12 } ··· 79 81 80 82 // TODO: Link up the requestor, etc. 81 83 82 - $source = $item->getSource(); 83 - $token = $source->getSourceProperty('github.token'); 84 - $token = new PhutilOpaqueEnvelope($token); 85 - 86 - $ref = $this->getDoorkeeperRef($item); 87 - if ($ref) { 88 - $ref = id(new DoorkeeperImportEngine()) 89 - ->setViewer($viewer) 90 - ->setRefs(array($ref)) 91 - ->setThrowOnMissingLink(true) 92 - ->setContextProperty('github.token', $token) 93 - ->executeOne(); 84 + $is_dirty = false; 94 85 95 - if ($ref->getSyncFailed()) { 96 - $xobj = null; 97 - } else { 98 - $xobj = $ref->getExternalObject(); 99 - } 86 + $xobj = $this->reloadExternalObject($item); 100 87 101 - if ($xobj) { 102 - $item->setItemProperty('doorkeeper.xobj.phid', $xobj->getPHID()); 103 - $is_dirty = true; 104 - } 88 + if ($xobj) { 89 + $item->setItemProperty('doorkeeper.xobj.phid', $xobj->getPHID()); 90 + $is_dirty = true; 105 91 } 106 92 107 93 if ($item->getStatus() == NuanceItem::STATUS_IMPORTING) { ··· 137 123 ->setObjectID($full_ref); 138 124 } 139 125 126 + private function reloadExternalObject(NuanceItem $item, $local = false) { 127 + $ref = $this->getDoorkeeperRef($item); 128 + if (!$ref) { 129 + return null; 130 + } 131 + 132 + $source = $item->getSource(); 133 + $token = $source->getSourceProperty('github.token'); 134 + $token = new PhutilOpaqueEnvelope($token); 135 + 136 + $viewer = $this->getViewer(); 137 + 138 + $ref = id(new DoorkeeperImportEngine()) 139 + ->setViewer($viewer) 140 + ->setRefs(array($ref)) 141 + ->setThrowOnMissingLink(true) 142 + ->setContextProperty('github.token', $token) 143 + ->needLocalOnly($local) 144 + ->executeOne(); 145 + 146 + if ($ref->getSyncFailed()) { 147 + $xobj = null; 148 + } else { 149 + $xobj = $ref->getExternalObject(); 150 + } 151 + 152 + if ($xobj) { 153 + $this->externalObject = $xobj; 154 + } 155 + 156 + return $xobj; 157 + } 158 + 159 + private function getExternalObject(NuanceItem $item) { 160 + if ($this->externalObject === null) { 161 + $xobj = $this->reloadExternalObject($item, $local = true); 162 + if ($xobj) { 163 + $this->externalObject = $xobj; 164 + } else { 165 + $this->externalObject = false; 166 + } 167 + } 168 + 169 + if ($this->externalObject) { 170 + return $this->externalObject; 171 + } 172 + 173 + return null; 174 + } 175 + 140 176 private function newRawEvent(NuanceItem $item) { 141 177 $type = $item->getItemProperty('api.type'); 142 178 $raw = $item->getItemProperty('api.raw', array()); ··· 146 182 147 183 public function getItemActions(NuanceItem $item) { 148 184 $actions = array(); 185 + 186 + $xobj = $this->getExternalObject($item); 187 + if ($xobj) { 188 + $actions[] = $this->newItemAction($item, 'reload') 189 + ->setName(pht('Reload from GitHub')) 190 + ->setIcon('fa-refresh') 191 + ->setWorkflow(true) 192 + ->setRenderAsForm(true); 193 + } 149 194 150 195 $actions[] = $this->newItemAction($item, 'sync') 151 196 ->setName(pht('Import to Maniphest')) ··· 189 234 ->appendForm($form) 190 235 ->addCancelButton($item->getURI(), pht('Done')); 191 236 case 'sync': 237 + case 'reload': 192 238 $item->issueCommand($viewer->getPHID(), $action); 193 239 return id(new AphrontRedirectResponse())->setURI($item->getURI()); 194 240 } ··· 238 284 ->appendChild($property_list); 239 285 } 240 286 241 - protected function handleCommand(NuanceItem $item, $action) { 287 + protected function handleCommand( 288 + NuanceItem $item, 289 + NuanceItemCommand $command) { 290 + 291 + $action = $command->getCommand(); 292 + switch ($action) { 293 + case 'sync': 294 + return $this->syncItem($item, $command); 295 + case 'reload': 296 + $this->reloadExternalObject($item); 297 + return true; 298 + } 299 + 242 300 return null; 301 + } 302 + 303 + private function syncItem( 304 + NuanceItem $item, 305 + NuanceItemCommand $command) { 306 + 307 + $xobj_phid = $item->getItemProperty('doorkeeper.xobj.phid'); 308 + if (!$xobj_phid) { 309 + throw new Exception( 310 + pht( 311 + 'Unable to sync: no external object PHID.')); 312 + } 313 + 314 + // TODO: Write some kind of marker to prevent double-synchronization. 315 + 316 + $viewer = $this->getViewer(); 317 + 318 + $xobj = id(new DoorkeeperExternalObjectQuery()) 319 + ->setViewer($viewer) 320 + ->withPHIDs(array($xobj_phid)) 321 + ->executeOne(); 322 + if (!$xobj) { 323 + throw new Exception( 324 + pht( 325 + 'Unable to sync: failed to load object "%s".', 326 + $xobj_phid)); 327 + } 328 + 329 + $nuance_phid = id(new PhabricatorNuanceApplication())->getPHID(); 330 + 331 + $xactions = array(); 332 + 333 + $task = id(new ManiphestTaskQuery()) 334 + ->setViewer($viewer) 335 + ->withBridgedObjectPHIDs(array($xobj_phid)) 336 + ->executeOne(); 337 + if (!$task) { 338 + $task = ManiphestTask::initializeNewTask($viewer) 339 + ->setAuthorPHID($nuance_phid) 340 + ->setBridgedObjectPHID($xobj_phid); 341 + 342 + $title = $xobj->getProperty('task.title'); 343 + if (!strlen($title)) { 344 + $title = pht('Nuance Item %d Task', $item->getID()); 345 + } 346 + 347 + $description = $xobj->getProperty('task.description'); 348 + $created = $xobj->getProperty('task.created'); 349 + $state = $xobj->getProperty('task.state'); 350 + 351 + $xactions[] = id(new ManiphestTransaction()) 352 + ->setTransactionType(ManiphestTransaction::TYPE_TITLE) 353 + ->setNewValue($title) 354 + ->setDateCreated($created); 355 + 356 + $xactions[] = id(new ManiphestTransaction()) 357 + ->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION) 358 + ->setNewValue($description) 359 + ->setDateCreated($created); 360 + 361 + $task->setDateCreated($created); 362 + 363 + // TODO: Synchronize state. 364 + } 365 + 366 + $event = $this->newRawEvent($item); 367 + $comment = $event->getComment(); 368 + if (strlen($comment)) { 369 + $xactions[] = id(new ManiphestTransaction()) 370 + ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) 371 + ->attachComment( 372 + id(new ManiphestTransactionComment()) 373 + ->setContent($comment)); 374 + } 375 + 376 + // TODO: Preserve the item's original source. 377 + $source = PhabricatorContentSource::newForSource( 378 + PhabricatorContentSource::SOURCE_DAEMON, 379 + array()); 380 + 381 + // TOOD: This should really be the external source. 382 + $acting_phid = $nuance_phid; 383 + 384 + $editor = id(new ManiphestTransactionEditor()) 385 + ->setActor($viewer) 386 + ->setActingAsPHID($acting_phid) 387 + ->setContentSource($source) 388 + ->setContinueOnNoEffect(true) 389 + ->setContinueOnMissingFields(true); 390 + 391 + $xactions = $editor->applyTransactions($task, $xactions); 392 + 393 + return array( 394 + 'objectPHID' => $task->getPHID(), 395 + 'xactionPHIDs' => mpull($xactions, 'getPHID'), 396 + ); 243 397 } 244 398 245 399
+3 -1
src/applications/nuance/item/NuanceItemType.php
··· 131 131 ->applyTransactions($item, array($xaction)); 132 132 } 133 133 134 - protected function handleCommand(NuanceItem $item, $action) { 134 + protected function handleCommand( 135 + NuanceItem $item, 136 + NuanceItemCommand $command) { 135 137 return null; 136 138 } 137 139