Remove product literal strings in "pht()", part 5
[phabricator.git] / src / applications / doorkeeper / worker / DoorkeeperJIRAFeedWorker.php
blob4ef8490b5e79416e7294efc327e14569f1ebdfcb
1 <?php
3 /**
4 * Publishes feed stories into JIRA, using the "JIRA Issues" field to identify
5 * linked issues.
6 */
7 final class DoorkeeperJIRAFeedWorker extends DoorkeeperFeedWorker {
9 private $provider;
12 /* -( Publishing Stories )------------------------------------------------- */
15 /**
16 * This worker is enabled when a JIRA authentication provider is active.
18 public function isEnabled() {
19 return (bool)PhabricatorJIRAAuthProvider::getJIRAProvider();
23 /**
24 * Publishes stories into JIRA using the JIRA API.
26 protected function publishFeedStory() {
27 $story = $this->getFeedStory();
28 $viewer = $this->getViewer();
29 $provider = $this->getProvider();
30 $object = $this->getStoryObject();
31 $publisher = $this->getPublisher();
33 $jira_issue_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
34 $object->getPHID(),
35 PhabricatorJiraIssueHasObjectEdgeType::EDGECONST);
36 if (!$jira_issue_phids) {
37 $this->log(
38 "%s\n",
39 pht('Story is about an object with no linked JIRA issues.'));
40 return;
43 $do_anything = ($this->shouldPostComment() || $this->shouldPostLink());
44 if (!$do_anything) {
45 $this->log(
46 "%s\n",
47 pht('JIRA integration is configured not to post anything.'));
48 return;
51 $xobjs = id(new DoorkeeperExternalObjectQuery())
52 ->setViewer($viewer)
53 ->withPHIDs($jira_issue_phids)
54 ->execute();
56 if (!$xobjs) {
57 $this->log(
58 "%s\n",
59 pht('Story object has no corresponding external JIRA objects.'));
60 return;
63 $try_users = $this->findUsersToPossess();
64 if (!$try_users) {
65 $this->log(
66 "%s\n",
67 pht('No users to act on linked JIRA objects.'));
68 return;
72 $xobjs = mgroup($xobjs, 'getApplicationDomain');
73 foreach ($xobjs as $domain => $xobj_list) {
74 $accounts = id(new PhabricatorExternalAccountQuery())
75 ->setViewer($viewer)
76 ->withUserPHIDs($try_users)
77 ->withProviderConfigPHIDs(
78 array(
79 $provider->getProviderConfigPHID(),
81 ->requireCapabilities(
82 array(
83 PhabricatorPolicyCapability::CAN_VIEW,
84 PhabricatorPolicyCapability::CAN_EDIT,
86 ->execute();
88 // Reorder accounts in the original order.
89 // TODO: This needs to be adjusted if/when we allow you to link multiple
90 // accounts.
91 $accounts = mpull($accounts, null, 'getUserPHID');
92 $accounts = array_select_keys($accounts, $try_users);
94 foreach ($xobj_list as $xobj) {
95 foreach ($accounts as $account) {
96 try {
97 $jira_key = $xobj->getObjectID();
99 if ($this->shouldPostComment()) {
100 $this->postComment($account, $jira_key);
103 if ($this->shouldPostLink()) {
104 $this->postLink($account, $jira_key);
107 break;
108 } catch (HTTPFutureResponseStatus $ex) {
109 phlog($ex);
110 $this->log(
111 "%s\n",
112 pht(
113 'Failed to update object %s using user %s.',
114 $xobj->getObjectID(),
115 $account->getUserPHID()));
123 /* -( Internals )---------------------------------------------------------- */
127 * Get the active JIRA provider.
129 * @return PhabricatorJIRAAuthProvider Active JIRA auth provider.
130 * @task internal
132 private function getProvider() {
133 if (!$this->provider) {
134 $provider = PhabricatorJIRAAuthProvider::getJIRAProvider();
135 if (!$provider) {
136 throw new PhabricatorWorkerPermanentFailureException(
137 pht('No JIRA provider configured.'));
139 $this->provider = $provider;
141 return $this->provider;
146 * Get a list of users to act as when publishing into JIRA.
148 * @return list<phid> Candidate user PHIDs to act as when publishing this
149 * story.
150 * @task internal
152 private function findUsersToPossess() {
153 $object = $this->getStoryObject();
154 $publisher = $this->getPublisher();
155 $data = $this->getFeedStory()->getStoryData();
157 // Figure out all the users related to the object. Users go into one of
158 // four buckets. For JIRA integration, we don't care about which bucket
159 // a user is in, since we just want to publish an update to linked objects.
161 $owner_phid = $publisher->getOwnerPHID($object);
162 $active_phids = $publisher->getActiveUserPHIDs($object);
163 $passive_phids = $publisher->getPassiveUserPHIDs($object);
164 $follow_phids = $publisher->getCCUserPHIDs($object);
166 $all_phids = array_merge(
167 array($owner_phid),
168 $active_phids,
169 $passive_phids,
170 $follow_phids);
171 $all_phids = array_unique(array_filter($all_phids));
173 // Even if the actor isn't a reviewer, etc., try to use their account so
174 // we can post in the correct voice. If we miss, we'll try all the other
175 // related users.
177 $try_users = array_merge(
178 array($data->getAuthorPHID()),
179 $all_phids);
180 $try_users = array_filter($try_users);
182 return $try_users;
185 private function shouldPostComment() {
186 return $this->getProvider()->shouldCreateJIRAComment();
189 private function shouldPostLink() {
190 return $this->getProvider()->shouldCreateJIRALink();
193 private function postComment($account, $jira_key) {
194 $provider = $this->getProvider();
196 $provider->newJIRAFuture(
197 $account,
198 'rest/api/2/issue/'.$jira_key.'/comment',
199 'POST',
200 array(
201 'body' => $this->renderStoryText(),
202 ))->resolveJSON();
205 private function renderStoryText() {
206 $object = $this->getStoryObject();
207 $publisher = $this->getPublisher();
209 $text = $publisher->getStoryText($object);
211 if ($this->shouldPostLink()) {
212 return $text;
213 } else {
214 // include the link in the comment
215 return $text."\n\n".$publisher->getObjectURI($object);
219 private function postLink($account, $jira_key) {
220 $provider = $this->getProvider();
221 $object = $this->getStoryObject();
222 $publisher = $this->getPublisher();
223 $icon_uri = celerity_get_resource_uri('rsrc/favicons/favicon-16x16.png');
225 $provider->newJIRAFuture(
226 $account,
227 'rest/api/2/issue/'.$jira_key.'/remotelink',
228 'POST',
230 // format documented at http://bit.ly/1K5T0Li
231 array(
232 'globalId' => $object->getPHID(),
233 'application' => array(
234 'type' => 'com.phacility.phabricator',
235 'name' => 'Phabricator',
237 'relationship' => 'implemented in',
238 'object' => array(
239 'url' => $publisher->getObjectURI($object),
240 'title' => $publisher->getObjectTitle($object),
241 'icon' => array(
242 'url16x16' => $icon_uri,
243 'title' => 'Phabricator',
245 'status' => array(
246 'resolved' => $publisher->isObjectClosed($object),
249 ))->resolveJSON();