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

Add a link to the main Asana task from Differential

Summary: Ref T2852. When a Differential revision is linked to an Asana task, show the related task in Differential.

Test Plan: {F49234}

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2852

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

+117 -22
+2
src/__phutil_library_map__.php
··· 305 305 'DifferentialAffectedPath' => 'applications/differential/storage/DifferentialAffectedPath.php', 306 306 'DifferentialApplyPatchFieldSpecification' => 'applications/differential/field/specification/DifferentialApplyPatchFieldSpecification.php', 307 307 'DifferentialArcanistProjectFieldSpecification' => 'applications/differential/field/specification/DifferentialArcanistProjectFieldSpecification.php', 308 + 'DifferentialAsanaRepresentationFieldSpecification' => 'applications/differential/field/specification/DifferentialAsanaRepresentationFieldSpecification.php', 308 309 'DifferentialAuditorsFieldSpecification' => 'applications/differential/field/specification/DifferentialAuditorsFieldSpecification.php', 309 310 'DifferentialAuthorFieldSpecification' => 'applications/differential/field/specification/DifferentialAuthorFieldSpecification.php', 310 311 'DifferentialAuxiliaryField' => 'applications/differential/storage/DifferentialAuxiliaryField.php', ··· 2239 2240 'DifferentialAffectedPath' => 'DifferentialDAO', 2240 2241 'DifferentialApplyPatchFieldSpecification' => 'DifferentialFieldSpecification', 2241 2242 'DifferentialArcanistProjectFieldSpecification' => 'DifferentialFieldSpecification', 2243 + 'DifferentialAsanaRepresentationFieldSpecification' => 'DifferentialFieldSpecification', 2242 2244 'DifferentialAuditorsFieldSpecification' => 'DifferentialFieldSpecification', 2243 2245 'DifferentialAuthorFieldSpecification' => 'DifferentialFieldSpecification', 2244 2246 'DifferentialAuxiliaryField' => 'DifferentialDAO',
+1
src/applications/differential/field/selector/DifferentialDefaultFieldSelector.php
··· 32 32 new DifferentialDateCreatedFieldSpecification(), 33 33 new DifferentialAuditorsFieldSpecification(), 34 34 new DifferentialDiffViewPolicyFieldSpecification(), 35 + new DifferentialAsanaRepresentationFieldSpecification(), 35 36 ); 36 37 37 38 return $fields;
+76
src/applications/differential/field/specification/DifferentialAsanaRepresentationFieldSpecification.php
··· 1 + <?php 2 + 3 + final class DifferentialAsanaRepresentationFieldSpecification 4 + extends DifferentialFieldSpecification { 5 + 6 + public function shouldAppearOnRevisionView() { 7 + return (bool)PhabricatorEnv::getEnvConfig('asana.workspace-id'); 8 + } 9 + 10 + public function renderLabelForRevisionView() { 11 + return pht('In Asana:'); 12 + } 13 + 14 + public function renderValueForRevisionView() { 15 + $viewer = $this->getUser(); 16 + $src_phid = $this->getRevision()->getPHID(); 17 + $edge_type = PhabricatorEdgeConfig::TYPE_PHOB_HAS_ASANATASK; 18 + 19 + $query = id(new PhabricatorEdgeQuery()) 20 + ->withSourcePHIDs(array($src_phid)) 21 + ->withEdgeTypes(array($edge_type)) 22 + ->needEdgeData(true); 23 + 24 + $edges = $query->execute(); 25 + if (!$edges) { 26 + return null; 27 + } 28 + 29 + $edge = head($edges[$src_phid][$edge_type]); 30 + 31 + if (!empty($edge['data']['gone'])) { 32 + return phutil_tag( 33 + 'em', 34 + array(), 35 + pht('Asana Task Deleted')); 36 + } 37 + 38 + $ref = id(new DoorkeeperImportEngine()) 39 + ->setViewer($viewer) 40 + ->withPHIDs(array($edge['dst'])) 41 + ->needLocalOnly(true) 42 + ->executeOne(); 43 + 44 + if (!$ref) { 45 + return null; 46 + } 47 + 48 + $tag_id = celerity_generate_unique_node_id(); 49 + $xobj = $ref->getExternalObject(); 50 + $href = $xobj->getObjectURI(); 51 + 52 + Javelin::initBehavior( 53 + 'doorkeeper-tag', 54 + array( 55 + 'tags' => array( 56 + array( 57 + 'id' => $tag_id, 58 + 'ref' => array( 59 + $ref->getApplicationType(), 60 + $ref->getApplicationDomain(), 61 + $ref->getObjectType(), 62 + $ref->getObjectID(), 63 + ), 64 + ), 65 + ), 66 + )); 67 + 68 + return id(new PhabricatorTagView()) 69 + ->setID($tag_id) 70 + ->setName($href) 71 + ->setHref($href) 72 + ->setType(PhabricatorTagView::TYPE_OBJECT) 73 + ->setExternal(true); 74 + } 75 + 76 + }
+30 -18
src/applications/doorkeeper/engine/DoorkeeperImportEngine.php
··· 5 5 private $viewer; 6 6 private $refs = array(); 7 7 private $phids = array(); 8 + private $localOnly; 8 9 9 10 public function setViewer(PhabricatorUser $viewer) { 10 11 $this->viewer = $viewer; ··· 27 28 28 29 public function withPHIDs(array $phids) { 29 30 $this->phids = $phids; 31 + return $this; 32 + } 33 + 34 + public function needLocalOnly($local_only) { 35 + $this->localOnly = $local_only; 30 36 return $this; 31 37 } 32 38 ··· 64 70 } 65 71 } 66 72 67 - $bridges = id(new PhutilSymbolLoader()) 68 - ->setAncestorClass('DoorkeeperBridge') 69 - ->loadObjects(); 73 + if (!$this->localOnly) { 74 + $bridges = id(new PhutilSymbolLoader()) 75 + ->setAncestorClass('DoorkeeperBridge') 76 + ->loadObjects(); 70 77 71 - foreach ($bridges as $key => $bridge) { 72 - if (!$bridge->isEnabled()) { 73 - unset($bridges[$key]); 78 + foreach ($bridges as $key => $bridge) { 79 + if (!$bridge->isEnabled()) { 80 + unset($bridges[$key]); 81 + } 82 + $bridge->setViewer($viewer); 74 83 } 75 - $bridge->setViewer($viewer); 76 - } 77 84 78 - $working_set = $refs; 79 - foreach ($bridges as $bridge) { 80 - $bridge_refs = array(); 81 - foreach ($working_set as $key => $ref) { 82 - if ($bridge->canPullRef($ref)) { 83 - $bridge_refs[$key] = $ref; 84 - unset($working_set[$key]); 85 + $working_set = $refs; 86 + foreach ($bridges as $bridge) { 87 + $bridge_refs = array(); 88 + foreach ($working_set as $key => $ref) { 89 + if ($bridge->canPullRef($ref)) { 90 + $bridge_refs[$key] = $ref; 91 + unset($working_set[$key]); 92 + } 85 93 } 86 - } 87 - if ($bridge_refs) { 88 - $bridge->pullRefs($bridge_refs); 94 + if ($bridge_refs) { 95 + $bridge->pullRefs($bridge_refs); 96 + } 89 97 } 90 98 } 91 99 92 100 return $refs; 101 + } 102 + 103 + public function executeOne() { 104 + return head($this->execute()); 93 105 } 94 106 95 107 }
+2 -2
src/applications/doorkeeper/worker/DoorkeeperFeedWorkerAsana.php
··· 238 238 239 239 $extra_data = array(); 240 240 if ($main_edge) { 241 + $extra_data = $main_edge['data']; 242 + 241 243 $refs = id(new DoorkeeperImportEngine()) 242 244 ->setViewer($possessed_user) 243 245 ->withPHIDs(array($main_edge['dst'])) ··· 289 291 "Skipping main task update, cursor is ahead of the story.\n"); 290 292 } 291 293 } 292 - 293 - $extra_data = $main_edge['data']; 294 294 } else { 295 295 $parent = $this->makeAsanaAPICall( 296 296 $oauth_token,
+6 -2
src/infrastructure/daemon/workers/PhabricatorTaskmasterDaemon.php
··· 19 19 $task = $task->executeTask(); 20 20 $ex = $task->getExecutionException(); 21 21 if ($ex) { 22 - $this->log("Task {$id} failed!"); 23 - throw $ex; 22 + if ($ex instanceof PhabricatorWorkerPermanentFailureException) { 23 + $this->log("Task {$id} failed permanently."); 24 + } else { 25 + $this->log("Task {$id} failed!"); 26 + throw $ex; 27 + } 24 28 } else { 25 29 $this->log("Task {$id} complete! Moved to archive."); 26 30 }