feedStory) { $story = $this->loadFeedStory(); $this->feedStory = $story; } return $this->feedStory; } /** * Get the viewer for the act of publishing. * * NOTE: Publishing currently uses the omnipotent viewer because it depends * on loading external accounts. Possibly we should tailor this. See T3732. * Using the actor for most operations might make more sense. * * @return PhabricatorUser Viewer. * @task context */ protected function getViewer() { return PhabricatorUser::getOmnipotentUser(); } /** * Get the @{class:DoorkeeperFeedStoryPublisher} which handles this object. * * @return DoorkeeperFeedStoryPublisher Object publisher. * @task context */ protected function getPublisher() { return $this->publisher; } /** * Get the primary object the story is about, like a * @{class:DifferentialRevision} or @{class:ManiphestTask}. * * @return object Object which the story is about. * @task context */ protected function getStoryObject() { if (!$this->storyObject) { $story = $this->getFeedStory(); try { $object = $story->getPrimaryObject(); } catch (Exception $ex) { throw new PhabricatorWorkerPermanentFailureException( $ex->getMessage()); } $this->storyObject = $object; } return $this->storyObject; } /* -( Internals )---------------------------------------------------------- */ /** * Load the @{class:DoorkeeperFeedStoryPublisher} which corresponds to this * object. Publishers provide a common API for pushing object updates into * foreign systems. * * @return DoorkeeperFeedStoryPublisher Publisher for the story's object. * @task internal */ private function loadPublisher() { $story = $this->getFeedStory(); $viewer = $this->getViewer(); $object = $this->getStoryObject(); $publishers = id(new PhutilSymbolLoader()) ->setAncestorClass('DoorkeeperFeedStoryPublisher') ->loadObjects(); foreach ($publishers as $publisher) { if (!$publisher->canPublishStory($story, $object)) { continue; } $publisher ->setViewer($viewer) ->setFeedStory($story); $object = $publisher->willPublishStory($object); $this->storyObject = $object; $this->publisher = $publisher; break; } return $this->publisher; } /* -( Inherited )---------------------------------------------------------- */ /** * Doorkeeper workers set up some context, then call * @{method:publishFeedStory}. */ final protected function doWork() { if (!$this->isEnabled()) { $this->log("Doorkeeper worker '%s' is not enabled.\n", get_class($this)); return; } $publisher = $this->loadPublisher(); if (!$publisher) { $this->log("Story is about an unsupported object type.\n"); return; } else { $this->log("Using publisher '%s'.\n", get_class($publisher)); } $this->publishFeedStory(); } /** * By default, Doorkeeper workers perform a small number of retries with * exponential backoff. A consideration in this policy is that many of these * workers are laden with side effects. */ public function getMaximumRetryCount() { return 4; } /** * See @{method:getMaximumRetryCount} for a description of Doorkeeper * retry defaults. */ public function getWaitBeforeRetry(PhabricatorWorkerTask $task) { $count = $task->getFailureCount(); return (5 * 60) * pow(8, $count); } }