Remove product literal strings in "pht()", part 5
[phabricator.git] / src / applications / doorkeeper / worker / DoorkeeperFeedWorker.php
blob8bbcc0f4fa7f5755ef9e676063918d128f09906e
1 <?php
3 /**
4 * Publish events (like comments on a revision) to external objects which are
5 * linked through Doorkeeper (like a linked JIRA or Asana task).
7 * These workers are invoked by feed infrastructure during normal task queue
8 * operations. They read feed stories and publish information about them to
9 * external systems, generally mirroring comments and updates in Phabricator
10 * into remote systems by making API calls.
12 * @task publish Publishing Stories
13 * @task context Story Context
14 * @task internal Internals
16 abstract class DoorkeeperFeedWorker extends FeedPushWorker {
18 private $publisher;
19 private $feedStory;
20 private $storyObject;
23 /* -( Publishing Stories )------------------------------------------------- */
26 /**
27 * Actually publish the feed story. Subclasses will generally make API calls
28 * to publish some version of the story into external systems.
30 * @return void
31 * @task publish
33 abstract protected function publishFeedStory();
36 /**
37 * Enable or disable the worker. Normally, this checks configuration to
38 * see if Phabricator is linked to applicable external systems.
40 * @return bool True if this worker should try to publish stories.
41 * @task publish
43 abstract public function isEnabled();
46 /* -( Story Context )------------------------------------------------------ */
49 /**
50 * Get the @{class:PhabricatorFeedStory} that should be published.
52 * @return PhabricatorFeedStory The story to publish.
53 * @task context
55 protected function getFeedStory() {
56 if (!$this->feedStory) {
57 $story = $this->loadFeedStory();
58 $this->feedStory = $story;
60 return $this->feedStory;
64 /**
65 * Get the viewer for the act of publishing.
67 * NOTE: Publishing currently uses the omnipotent viewer because it depends
68 * on loading external accounts. Possibly we should tailor this. See T3732.
69 * Using the actor for most operations might make more sense.
71 * @return PhabricatorUser Viewer.
72 * @task context
74 protected function getViewer() {
75 return PhabricatorUser::getOmnipotentUser();
79 /**
80 * Get the @{class:DoorkeeperFeedStoryPublisher} which handles this object.
82 * @return DoorkeeperFeedStoryPublisher Object publisher.
83 * @task context
85 protected function getPublisher() {
86 return $this->publisher;
90 /**
91 * Get the primary object the story is about, like a
92 * @{class:DifferentialRevision} or @{class:ManiphestTask}.
94 * @return object Object which the story is about.
95 * @task context
97 protected function getStoryObject() {
98 if (!$this->storyObject) {
99 $story = $this->getFeedStory();
100 try {
101 $object = $story->getPrimaryObject();
102 } catch (Exception $ex) {
103 throw new PhabricatorWorkerPermanentFailureException(
104 $ex->getMessage());
106 $this->storyObject = $object;
108 return $this->storyObject;
112 /* -( Internals )---------------------------------------------------------- */
116 * Load the @{class:DoorkeeperFeedStoryPublisher} which corresponds to this
117 * object. Publishers provide a common API for pushing object updates into
118 * foreign systems.
120 * @return DoorkeeperFeedStoryPublisher Publisher for the story's object.
121 * @task internal
123 private function loadPublisher() {
124 $story = $this->getFeedStory();
125 $viewer = $this->getViewer();
126 $object = $this->getStoryObject();
128 $publishers = id(new PhutilClassMapQuery())
129 ->setAncestorClass('DoorkeeperFeedStoryPublisher')
130 ->execute();
132 foreach ($publishers as $publisher) {
133 if (!$publisher->canPublishStory($story, $object)) {
134 continue;
137 $publisher
138 ->setViewer($viewer)
139 ->setFeedStory($story);
141 $object = $publisher->willPublishStory($object);
142 $this->storyObject = $object;
144 $this->publisher = $publisher;
145 break;
148 return $this->publisher;
152 /* -( Inherited )---------------------------------------------------------- */
156 * Doorkeeper workers set up some context, then call
157 * @{method:publishFeedStory}.
159 final protected function doWork() {
160 if (PhabricatorEnv::getEnvConfig('phabricator.silent')) {
161 $this->log("%s\n", pht('This software is running in silent mode.'));
162 return;
165 if (!$this->isEnabled()) {
166 $this->log(
167 "%s\n",
168 pht("Doorkeeper worker '%s' is not enabled.", get_class($this)));
169 return;
172 $publisher = $this->loadPublisher();
173 if (!$publisher) {
174 $this->log("%s\n", pht('Story is about an unsupported object type.'));
175 return;
176 } else {
177 $this->log("%s\n", pht("Using publisher '%s'.", get_class($publisher)));
180 $this->publishFeedStory();
185 * By default, Doorkeeper workers perform a small number of retries with
186 * exponential backoff. A consideration in this policy is that many of these
187 * workers are laden with side effects.
189 public function getMaximumRetryCount() {
190 return 4;
195 * See @{method:getMaximumRetryCount} for a description of Doorkeeper
196 * retry defaults.
198 public function getWaitBeforeRetry(PhabricatorWorkerTask $task) {
199 $count = $task->getFailureCount();
200 return (5 * 60) * pow(8, $count);