Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/differential/customfield/DifferentialJIRAIssuesField.php
12256 views
1
<?php
2
3
final class DifferentialJIRAIssuesField
4
extends DifferentialStoredCustomField {
5
6
private $error;
7
8
public function getFieldKey() {
9
return 'phabricator:jira-issues';
10
}
11
12
public function getFieldKeyForConduit() {
13
return 'jira.issues';
14
}
15
16
public function isFieldEnabled() {
17
return (bool)PhabricatorJIRAAuthProvider::getJIRAProvider();
18
}
19
20
public function canDisableField() {
21
return false;
22
}
23
24
public function getValueForStorage() {
25
return json_encode($this->getValue());
26
}
27
28
public function setValueFromStorage($value) {
29
try {
30
$this->setValue(phutil_json_decode($value));
31
} catch (PhutilJSONParserException $ex) {
32
$this->setValue(array());
33
}
34
return $this;
35
}
36
37
public function getFieldName() {
38
return pht('JIRA Issues');
39
}
40
41
public function getFieldDescription() {
42
return pht('Lists associated JIRA issues.');
43
}
44
45
public function shouldAppearInPropertyView() {
46
return true;
47
}
48
49
public function renderPropertyViewLabel() {
50
return $this->getFieldName();
51
}
52
53
public function renderPropertyViewValue(array $handles) {
54
$xobjs = $this->loadDoorkeeperExternalObjects($this->getValue());
55
if (!$xobjs) {
56
return null;
57
}
58
59
$links = array();
60
foreach ($xobjs as $xobj) {
61
$links[] = id(new DoorkeeperTagView())
62
->setExternalObject($xobj);
63
}
64
65
return phutil_implode_html(phutil_tag('br'), $links);
66
}
67
68
private function buildDoorkeeperRefs($value) {
69
$provider = PhabricatorJIRAAuthProvider::getJIRAProvider();
70
71
$refs = array();
72
if ($value) {
73
foreach ($value as $jira_key) {
74
$refs[] = id(new DoorkeeperObjectRef())
75
->setApplicationType(DoorkeeperBridgeJIRA::APPTYPE_JIRA)
76
->setApplicationDomain($provider->getProviderDomain())
77
->setObjectType(DoorkeeperBridgeJIRA::OBJTYPE_ISSUE)
78
->setObjectID($jira_key);
79
}
80
}
81
82
return $refs;
83
}
84
85
private function loadDoorkeeperExternalObjects($value) {
86
$refs = $this->buildDoorkeeperRefs($value);
87
if (!$refs) {
88
return array();
89
}
90
91
$xobjs = id(new DoorkeeperExternalObjectQuery())
92
->setViewer($this->getViewer())
93
->withObjectKeys(mpull($refs, 'getObjectKey'))
94
->execute();
95
96
return $xobjs;
97
}
98
99
public function shouldAppearInEditView() {
100
return PhabricatorJIRAAuthProvider::getJIRAProvider();
101
}
102
103
public function shouldAppearInApplicationTransactions() {
104
return PhabricatorJIRAAuthProvider::getJIRAProvider();
105
}
106
107
public function readValueFromRequest(AphrontRequest $request) {
108
$this->setValue($request->getStrList($this->getFieldKey()));
109
return $this;
110
}
111
112
public function renderEditControl(array $handles) {
113
return id(new AphrontFormTextControl())
114
->setLabel(pht('JIRA Issues'))
115
->setCaption(
116
pht('Example: %s', phutil_tag('tt', array(), 'JIS-3, JIS-9')))
117
->setName($this->getFieldKey())
118
->setValue(implode(', ', nonempty($this->getValue(), array())))
119
->setError($this->error);
120
}
121
122
public function getOldValueForApplicationTransactions() {
123
return array_unique(nonempty($this->getValue(), array()));
124
}
125
126
public function getNewValueForApplicationTransactions() {
127
return array_unique(nonempty($this->getValue(), array()));
128
}
129
130
public function validateApplicationTransactions(
131
PhabricatorApplicationTransactionEditor $editor,
132
$type,
133
array $xactions) {
134
135
$this->error = null;
136
137
$errors = parent::validateApplicationTransactions(
138
$editor,
139
$type,
140
$xactions);
141
142
$transaction = null;
143
foreach ($xactions as $xaction) {
144
$old = $xaction->getOldValue();
145
$new = $xaction->getNewValue();
146
147
$add = array_diff($new, $old);
148
if (!$add) {
149
continue;
150
}
151
152
// Only check that the actor can see newly added JIRA refs. You're
153
// allowed to remove refs or make no-op changes even if you aren't
154
// linked to JIRA.
155
156
try {
157
$refs = id(new DoorkeeperImportEngine())
158
->setViewer($this->getViewer())
159
->setRefs($this->buildDoorkeeperRefs($add))
160
->setThrowOnMissingLink(true)
161
->execute();
162
} catch (DoorkeeperMissingLinkException $ex) {
163
$this->error = pht('Not Linked');
164
$errors[] = new PhabricatorApplicationTransactionValidationError(
165
$type,
166
pht('Not Linked'),
167
pht(
168
'You can not add JIRA issues (%s) to this revision because your '.
169
'%s account is not linked to a JIRA account.',
170
implode(', ', $add),
171
PlatformSymbols::getPlatformServerName()),
172
$xaction);
173
continue;
174
}
175
176
$bad = array();
177
foreach ($refs as $ref) {
178
if (!$ref->getIsVisible()) {
179
$bad[] = $ref->getObjectID();
180
}
181
}
182
183
if ($bad) {
184
$bad = implode(', ', $bad);
185
$this->error = pht('Invalid');
186
187
$errors[] = new PhabricatorApplicationTransactionValidationError(
188
$type,
189
pht('Invalid'),
190
pht(
191
'Some JIRA issues could not be loaded. They may not exist, or '.
192
'you may not have permission to view them: %s',
193
$bad),
194
$xaction);
195
}
196
}
197
198
return $errors;
199
}
200
201
public function getApplicationTransactionTitle(
202
PhabricatorApplicationTransaction $xaction) {
203
204
$old = $xaction->getOldValue();
205
if (!is_array($old)) {
206
$old = array();
207
}
208
209
$new = $xaction->getNewValue();
210
if (!is_array($new)) {
211
$new = array();
212
}
213
214
$add = array_diff($new, $old);
215
$rem = array_diff($old, $new);
216
217
$author_phid = $xaction->getAuthorPHID();
218
if ($add && $rem) {
219
return pht(
220
'%s updated JIRA issue(s): added %d %s; removed %d %s.',
221
$xaction->renderHandleLink($author_phid),
222
phutil_count($add),
223
implode(', ', $add),
224
phutil_count($rem),
225
implode(', ', $rem));
226
} else if ($add) {
227
return pht(
228
'%s added %d JIRA issue(s): %s.',
229
$xaction->renderHandleLink($author_phid),
230
phutil_count($add),
231
implode(', ', $add));
232
} else if ($rem) {
233
return pht(
234
'%s removed %d JIRA issue(s): %s.',
235
$xaction->renderHandleLink($author_phid),
236
phutil_count($rem),
237
implode(', ', $rem));
238
}
239
240
return parent::getApplicationTransactionTitle($xaction);
241
}
242
243
public function applyApplicationTransactionExternalEffects(
244
PhabricatorApplicationTransaction $xaction) {
245
246
// Update the CustomField storage.
247
parent::applyApplicationTransactionExternalEffects($xaction);
248
249
// Now, synchronize the Doorkeeper edges.
250
$revision = $this->getObject();
251
$revision_phid = $revision->getPHID();
252
253
$edge_type = PhabricatorJiraIssueHasObjectEdgeType::EDGECONST;
254
$xobjs = $this->loadDoorkeeperExternalObjects($xaction->getNewValue());
255
$edge_dsts = mpull($xobjs, 'getPHID');
256
257
$edges = PhabricatorEdgeQuery::loadDestinationPHIDs(
258
$revision_phid,
259
$edge_type);
260
261
$editor = new PhabricatorEdgeEditor();
262
263
foreach (array_diff($edges, $edge_dsts) as $rem_edge) {
264
$editor->removeEdge($revision_phid, $edge_type, $rem_edge);
265
}
266
267
foreach (array_diff($edge_dsts, $edges) as $add_edge) {
268
$editor->addEdge($revision_phid, $edge_type, $add_edge);
269
}
270
271
$editor->save();
272
}
273
274
public function shouldAppearInConduitDictionary() {
275
return true;
276
}
277
278
public function shouldAppearInConduitTransactions() {
279
return true;
280
}
281
282
protected function newConduitEditParameterType() {
283
return new ConduitStringListParameterType();
284
}
285
286
}
287
288