@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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