Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/herald/action/HeraldAction.php
12256 views
1
<?php
2
3
abstract class HeraldAction extends Phobject {
4
5
private $adapter;
6
private $viewer;
7
private $applyLog = array();
8
9
const STANDARD_NONE = 'standard.none';
10
const STANDARD_PHID_LIST = 'standard.phid.list';
11
const STANDARD_TEXT = 'standard.text';
12
const STANDARD_REMARKUP = 'standard.remarkup';
13
14
const DO_STANDARD_EMPTY = 'do.standard.empty';
15
const DO_STANDARD_NO_EFFECT = 'do.standard.no-effect';
16
const DO_STANDARD_INVALID = 'do.standard.invalid';
17
const DO_STANDARD_UNLOADABLE = 'do.standard.unloadable';
18
const DO_STANDARD_PERMISSION = 'do.standard.permission';
19
const DO_STANDARD_INVALID_ACTION = 'do.standard.invalid-action';
20
const DO_STANDARD_WRONG_RULE_TYPE = 'do.standard.wrong-rule-type';
21
const DO_STANDARD_FORBIDDEN = 'do.standard.forbidden';
22
23
abstract public function getHeraldActionName();
24
abstract public function supportsObject($object);
25
abstract public function supportsRuleType($rule_type);
26
abstract public function applyEffect($object, HeraldEffect $effect);
27
28
abstract public function renderActionDescription($value);
29
30
public function getRequiredAdapterStates() {
31
return array();
32
}
33
34
protected function renderActionEffectDescription($type, $data) {
35
return null;
36
}
37
38
public function getActionGroupKey() {
39
return null;
40
}
41
42
public function getActionsForObject($object) {
43
return array($this->getActionConstant() => $this);
44
}
45
46
protected function getDatasource() {
47
throw new PhutilMethodNotImplementedException();
48
}
49
50
protected function getDatasourceValueMap() {
51
return null;
52
}
53
54
public function getHeraldActionStandardType() {
55
throw new PhutilMethodNotImplementedException();
56
}
57
58
public function getHeraldActionValueType() {
59
switch ($this->getHeraldActionStandardType()) {
60
case self::STANDARD_NONE:
61
return new HeraldEmptyFieldValue();
62
case self::STANDARD_TEXT:
63
return new HeraldTextFieldValue();
64
case self::STANDARD_REMARKUP:
65
return new HeraldRemarkupFieldValue();
66
case self::STANDARD_PHID_LIST:
67
$tokenizer = id(new HeraldTokenizerFieldValue())
68
->setKey($this->getHeraldActionName())
69
->setDatasource($this->getDatasource());
70
71
$value_map = $this->getDatasourceValueMap();
72
if ($value_map !== null) {
73
$tokenizer->setValueMap($value_map);
74
}
75
76
return $tokenizer;
77
}
78
79
throw new PhutilMethodNotImplementedException();
80
}
81
82
public function willSaveActionValue($value) {
83
try {
84
$type = $this->getHeraldActionStandardType();
85
} catch (PhutilMethodNotImplementedException $ex) {
86
return $value;
87
}
88
89
switch ($type) {
90
case self::STANDARD_PHID_LIST:
91
return array_keys($value);
92
}
93
94
return $value;
95
}
96
97
public function getEditorValue(PhabricatorUser $viewer, $target) {
98
try {
99
$type = $this->getHeraldActionStandardType();
100
} catch (PhutilMethodNotImplementedException $ex) {
101
return $target;
102
}
103
104
switch ($type) {
105
case self::STANDARD_PHID_LIST:
106
$datasource = $this->getDatasource();
107
108
if (!$datasource) {
109
return array();
110
}
111
112
return $datasource
113
->setViewer($viewer)
114
->getWireTokens($target);
115
}
116
117
return $target;
118
}
119
120
final public function setAdapter(HeraldAdapter $adapter) {
121
$this->adapter = $adapter;
122
return $this;
123
}
124
125
final public function getAdapter() {
126
return $this->adapter;
127
}
128
129
final public function setViewer(PhabricatorUser $viewer) {
130
$this->viewer = $viewer;
131
return $this;
132
}
133
134
final public function getViewer() {
135
return $this->viewer;
136
}
137
138
final public function getActionConstant() {
139
return $this->getPhobjectClassConstant('ACTIONCONST', 64);
140
}
141
142
final public static function getAllActions() {
143
return id(new PhutilClassMapQuery())
144
->setAncestorClass(__CLASS__)
145
->setUniqueMethod('getActionConstant')
146
->execute();
147
}
148
149
protected function logEffect($type, $data = null) {
150
if (!is_string($type)) {
151
throw new Exception(
152
pht(
153
'Effect type passed to "%s" must be a scalar string.',
154
'logEffect()'));
155
}
156
157
$this->applyLog[] = array(
158
'type' => $type,
159
'data' => $data,
160
);
161
162
return $this;
163
}
164
165
final public function getApplyTranscript(HeraldEffect $effect) {
166
$context = $this->applyLog;
167
$this->applyLog = array();
168
return new HeraldApplyTranscript($effect, true, $context);
169
}
170
171
protected function getActionEffectMap() {
172
throw new PhutilMethodNotImplementedException();
173
}
174
175
private function getActionEffectSpec($type) {
176
$map = $this->getActionEffectMap() + $this->getStandardEffectMap();
177
return idx($map, $type, array());
178
}
179
180
final public function renderActionEffectIcon($type, $data) {
181
$map = $this->getActionEffectSpec($type);
182
return idx($map, 'icon');
183
}
184
185
final public function renderActionEffectColor($type, $data) {
186
$map = $this->getActionEffectSpec($type);
187
return idx($map, 'color');
188
}
189
190
final public function renderActionEffectName($type, $data) {
191
$map = $this->getActionEffectSpec($type);
192
return idx($map, 'name');
193
}
194
195
protected function renderHandleList($phids) {
196
if (!is_array($phids)) {
197
return pht('(Invalid List)');
198
}
199
200
return $this->getViewer()
201
->renderHandleList($phids)
202
->setAsInline(true)
203
->render();
204
}
205
206
protected function loadStandardTargets(
207
array $phids,
208
array $allowed_types,
209
array $current_value) {
210
211
$phids = array_fuse($phids);
212
if (!$phids) {
213
$this->logEffect(self::DO_STANDARD_EMPTY);
214
}
215
216
$current_value = array_fuse($current_value);
217
$no_effect = array();
218
foreach ($phids as $phid) {
219
if (isset($current_value[$phid])) {
220
$no_effect[] = $phid;
221
unset($phids[$phid]);
222
}
223
}
224
225
if ($no_effect) {
226
$this->logEffect(self::DO_STANDARD_NO_EFFECT, $no_effect);
227
}
228
229
if (!$phids) {
230
return;
231
}
232
233
$allowed_types = array_fuse($allowed_types);
234
$invalid = array();
235
foreach ($phids as $phid) {
236
$type = phid_get_type($phid);
237
if ($type == PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) {
238
$invalid[] = $phid;
239
unset($phids[$phid]);
240
continue;
241
}
242
243
if ($allowed_types && empty($allowed_types[$type])) {
244
$invalid[] = $phid;
245
unset($phids[$phid]);
246
continue;
247
}
248
}
249
250
if ($invalid) {
251
$this->logEffect(self::DO_STANDARD_INVALID, $invalid);
252
}
253
254
if (!$phids) {
255
return;
256
}
257
258
$targets = id(new PhabricatorObjectQuery())
259
->setViewer(PhabricatorUser::getOmnipotentUser())
260
->withPHIDs($phids)
261
->execute();
262
$targets = mpull($targets, null, 'getPHID');
263
264
$unloadable = array();
265
foreach ($phids as $phid) {
266
if (empty($targets[$phid])) {
267
$unloadable[] = $phid;
268
unset($phids[$phid]);
269
}
270
}
271
272
if ($unloadable) {
273
$this->logEffect(self::DO_STANDARD_UNLOADABLE, $unloadable);
274
}
275
276
if (!$phids) {
277
return;
278
}
279
280
$adapter = $this->getAdapter();
281
$object = $adapter->getObject();
282
283
if ($object instanceof PhabricatorPolicyInterface) {
284
$no_permission = array();
285
foreach ($targets as $phid => $target) {
286
if (!($target instanceof PhabricatorUser)) {
287
continue;
288
}
289
290
$can_view = PhabricatorPolicyFilter::hasCapability(
291
$target,
292
$object,
293
PhabricatorPolicyCapability::CAN_VIEW);
294
if ($can_view) {
295
continue;
296
}
297
298
$no_permission[] = $phid;
299
unset($targets[$phid]);
300
}
301
}
302
303
if ($no_permission) {
304
$this->logEffect(self::DO_STANDARD_PERMISSION, $no_permission);
305
}
306
307
return $targets;
308
}
309
310
protected function getStandardEffectMap() {
311
return array(
312
self::DO_STANDARD_EMPTY => array(
313
'icon' => 'fa-ban',
314
'color' => 'grey',
315
'name' => pht('No Targets'),
316
),
317
self::DO_STANDARD_NO_EFFECT => array(
318
'icon' => 'fa-circle-o',
319
'color' => 'grey',
320
'name' => pht('No Effect'),
321
),
322
self::DO_STANDARD_INVALID => array(
323
'icon' => 'fa-ban',
324
'color' => 'red',
325
'name' => pht('Invalid Targets'),
326
),
327
self::DO_STANDARD_UNLOADABLE => array(
328
'icon' => 'fa-ban',
329
'color' => 'red',
330
'name' => pht('Unloadable Targets'),
331
),
332
self::DO_STANDARD_PERMISSION => array(
333
'icon' => 'fa-lock',
334
'color' => 'red',
335
'name' => pht('No Permission'),
336
),
337
self::DO_STANDARD_INVALID_ACTION => array(
338
'icon' => 'fa-ban',
339
'color' => 'red',
340
'name' => pht('Invalid Action'),
341
),
342
self::DO_STANDARD_WRONG_RULE_TYPE => array(
343
'icon' => 'fa-ban',
344
'color' => 'red',
345
'name' => pht('Wrong Rule Type'),
346
),
347
self::DO_STANDARD_FORBIDDEN => array(
348
'icon' => 'fa-ban',
349
'color' => 'violet',
350
'name' => pht('Forbidden'),
351
),
352
);
353
}
354
355
final public function renderEffectDescription($type, $data) {
356
$result = $this->renderActionEffectDescription($type, $data);
357
if ($result !== null) {
358
return $result;
359
}
360
361
switch ($type) {
362
case self::DO_STANDARD_EMPTY:
363
return pht(
364
'This action specifies no targets.');
365
case self::DO_STANDARD_NO_EFFECT:
366
if ($data && is_array($data)) {
367
return pht(
368
'This action has no effect on %s target(s): %s.',
369
phutil_count($data),
370
$this->renderHandleList($data));
371
} else {
372
return pht('This action has no effect.');
373
}
374
case self::DO_STANDARD_INVALID:
375
return pht(
376
'%s target(s) are invalid or of the wrong type: %s.',
377
phutil_count($data),
378
$this->renderHandleList($data));
379
case self::DO_STANDARD_UNLOADABLE:
380
return pht(
381
'%s target(s) could not be loaded: %s.',
382
phutil_count($data),
383
$this->renderHandleList($data));
384
case self::DO_STANDARD_PERMISSION:
385
return pht(
386
'%s target(s) do not have permission to see this object: %s.',
387
phutil_count($data),
388
$this->renderHandleList($data));
389
case self::DO_STANDARD_INVALID_ACTION:
390
return pht(
391
'No implementation is available for rule "%s".',
392
$data);
393
case self::DO_STANDARD_WRONG_RULE_TYPE:
394
return pht(
395
'This action does not support rules of type "%s".',
396
$data);
397
case self::DO_STANDARD_FORBIDDEN:
398
return HeraldStateReasons::getExplanation($data);
399
}
400
401
return null;
402
}
403
404
public function getPHIDsAffectedByAction(HeraldActionRecord $record) {
405
return array();
406
}
407
408
public function isActionAvailable() {
409
return true;
410
}
411
412
}
413
414