3 abstract class HeraldAction
extends Phobject
{
7 private $applyLog = array();
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';
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';
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);
28 abstract public function renderActionDescription($value);
30 public function getRequiredAdapterStates() {
34 protected function renderActionEffectDescription($type, $data) {
38 public function getActionGroupKey() {
42 public function getActionsForObject($object) {
43 return array($this->getActionConstant() => $this);
46 protected function getDatasource() {
47 throw new PhutilMethodNotImplementedException();
50 protected function getDatasourceValueMap() {
54 public function getHeraldActionStandardType() {
55 throw new PhutilMethodNotImplementedException();
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());
71 $value_map = $this->getDatasourceValueMap();
72 if ($value_map !== null) {
73 $tokenizer->setValueMap($value_map);
79 throw new PhutilMethodNotImplementedException();
82 public function willSaveActionValue($value) {
84 $type = $this->getHeraldActionStandardType();
85 } catch (PhutilMethodNotImplementedException
$ex) {
90 case self
::STANDARD_PHID_LIST
:
91 return array_keys($value);
97 public function getEditorValue(PhabricatorUser
$viewer, $target) {
99 $type = $this->getHeraldActionStandardType();
100 } catch (PhutilMethodNotImplementedException
$ex) {
105 case self
::STANDARD_PHID_LIST
:
106 $datasource = $this->getDatasource();
114 ->getWireTokens($target);
120 final public function setAdapter(HeraldAdapter
$adapter) {
121 $this->adapter
= $adapter;
125 final public function getAdapter() {
126 return $this->adapter
;
129 final public function setViewer(PhabricatorUser
$viewer) {
130 $this->viewer
= $viewer;
134 final public function getViewer() {
135 return $this->viewer
;
138 final public function getActionConstant() {
139 return $this->getPhobjectClassConstant('ACTIONCONST', 64);
142 final public static function getAllActions() {
143 return id(new PhutilClassMapQuery())
144 ->setAncestorClass(__CLASS__
)
145 ->setUniqueMethod('getActionConstant')
149 protected function logEffect($type, $data = null) {
150 if (!is_string($type)) {
153 'Effect type passed to "%s" must be a scalar string.',
157 $this->applyLog
[] = array(
165 final public function getApplyTranscript(HeraldEffect
$effect) {
166 $context = $this->applyLog
;
167 $this->applyLog
= array();
168 return new HeraldApplyTranscript($effect, true, $context);
171 protected function getActionEffectMap() {
172 throw new PhutilMethodNotImplementedException();
175 private function getActionEffectSpec($type) {
176 $map = $this->getActionEffectMap() +
$this->getStandardEffectMap();
177 return idx($map, $type, array());
180 final public function renderActionEffectIcon($type, $data) {
181 $map = $this->getActionEffectSpec($type);
182 return idx($map, 'icon');
185 final public function renderActionEffectColor($type, $data) {
186 $map = $this->getActionEffectSpec($type);
187 return idx($map, 'color');
190 final public function renderActionEffectName($type, $data) {
191 $map = $this->getActionEffectSpec($type);
192 return idx($map, 'name');
195 protected function renderHandleList($phids) {
196 if (!is_array($phids)) {
197 return pht('(Invalid List)');
200 return $this->getViewer()
201 ->renderHandleList($phids)
206 protected function loadStandardTargets(
208 array $allowed_types,
209 array $current_value) {
211 $phids = array_fuse($phids);
213 $this->logEffect(self
::DO_STANDARD_EMPTY
);
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]);
226 $this->logEffect(self
::DO_STANDARD_NO_EFFECT
, $no_effect);
233 $allowed_types = array_fuse($allowed_types);
235 foreach ($phids as $phid) {
236 $type = phid_get_type($phid);
237 if ($type == PhabricatorPHIDConstants
::PHID_TYPE_UNKNOWN
) {
239 unset($phids[$phid]);
243 if ($allowed_types && empty($allowed_types[$type])) {
245 unset($phids[$phid]);
251 $this->logEffect(self
::DO_STANDARD_INVALID
, $invalid);
258 $targets = id(new PhabricatorObjectQuery())
259 ->setViewer(PhabricatorUser
::getOmnipotentUser())
262 $targets = mpull($targets, null, 'getPHID');
264 $unloadable = array();
265 foreach ($phids as $phid) {
266 if (empty($targets[$phid])) {
267 $unloadable[] = $phid;
268 unset($phids[$phid]);
273 $this->logEffect(self
::DO_STANDARD_UNLOADABLE
, $unloadable);
280 $adapter = $this->getAdapter();
281 $object = $adapter->getObject();
283 if ($object instanceof PhabricatorPolicyInterface
) {
284 $no_permission = array();
285 foreach ($targets as $phid => $target) {
286 if (!($target instanceof PhabricatorUser
)) {
290 $can_view = PhabricatorPolicyFilter
::hasCapability(
293 PhabricatorPolicyCapability
::CAN_VIEW
);
298 $no_permission[] = $phid;
299 unset($targets[$phid]);
303 if ($no_permission) {
304 $this->logEffect(self
::DO_STANDARD_PERMISSION
, $no_permission);
310 protected function getStandardEffectMap() {
312 self
::DO_STANDARD_EMPTY
=> array(
315 'name' => pht('No Targets'),
317 self
::DO_STANDARD_NO_EFFECT
=> array(
318 'icon' => 'fa-circle-o',
320 'name' => pht('No Effect'),
322 self
::DO_STANDARD_INVALID
=> array(
325 'name' => pht('Invalid Targets'),
327 self
::DO_STANDARD_UNLOADABLE
=> array(
330 'name' => pht('Unloadable Targets'),
332 self
::DO_STANDARD_PERMISSION
=> array(
335 'name' => pht('No Permission'),
337 self
::DO_STANDARD_INVALID_ACTION
=> array(
340 'name' => pht('Invalid Action'),
342 self
::DO_STANDARD_WRONG_RULE_TYPE
=> array(
345 'name' => pht('Wrong Rule Type'),
347 self
::DO_STANDARD_FORBIDDEN
=> array(
350 'name' => pht('Forbidden'),
355 final public function renderEffectDescription($type, $data) {
356 $result = $this->renderActionEffectDescription($type, $data);
357 if ($result !== null) {
362 case self
::DO_STANDARD_EMPTY
:
364 'This action specifies no targets.');
365 case self
::DO_STANDARD_NO_EFFECT
:
366 if ($data && is_array($data)) {
368 'This action has no effect on %s target(s): %s.',
370 $this->renderHandleList($data));
372 return pht('This action has no effect.');
374 case self
::DO_STANDARD_INVALID
:
376 '%s target(s) are invalid or of the wrong type: %s.',
378 $this->renderHandleList($data));
379 case self
::DO_STANDARD_UNLOADABLE
:
381 '%s target(s) could not be loaded: %s.',
383 $this->renderHandleList($data));
384 case self
::DO_STANDARD_PERMISSION
:
386 '%s target(s) do not have permission to see this object: %s.',
388 $this->renderHandleList($data));
389 case self
::DO_STANDARD_INVALID_ACTION
:
391 'No implementation is available for rule "%s".',
393 case self
::DO_STANDARD_WRONG_RULE_TYPE
:
395 'This action does not support rules of type "%s".',
397 case self
::DO_STANDARD_FORBIDDEN
:
398 return HeraldStateReasons
::getExplanation($data);
404 public function getPHIDsAffectedByAction(HeraldActionRecord
$record) {
408 public function isActionAvailable() {