@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 upstream/main 352 lines 9.7 kB view raw
1<?php 2 3final class PhabricatorProjectTrigger 4 extends PhabricatorProjectDAO 5 implements 6 PhabricatorApplicationTransactionInterface, 7 PhabricatorPolicyInterface, 8 PhabricatorIndexableInterface, 9 PhabricatorDestructibleInterface { 10 11 protected $name; 12 protected $ruleset = array(); 13 protected $editPolicy; 14 15 private $triggerRules; 16 private $viewer; 17 private $usage = self::ATTACHABLE; 18 19 public static function initializeNewTrigger() { 20 $default_edit = PhabricatorPolicies::POLICY_USER; 21 22 return id(new self()) 23 ->setName('') 24 ->setEditPolicy($default_edit); 25 } 26 27 protected function getConfiguration() { 28 return array( 29 self::CONFIG_AUX_PHID => true, 30 self::CONFIG_SERIALIZATION => array( 31 'ruleset' => self::SERIALIZATION_JSON, 32 ), 33 self::CONFIG_COLUMN_SCHEMA => array( 34 'name' => 'text255', 35 ), 36 self::CONFIG_KEY_SCHEMA => array( 37 ), 38 ) + parent::getConfiguration(); 39 } 40 41 public function getPHIDType() { 42 return PhabricatorProjectTriggerPHIDType::TYPECONST; 43 } 44 45 public function getViewer() { 46 return $this->viewer; 47 } 48 49 public function setViewer(PhabricatorUser $user) { 50 $this->viewer = $user; 51 return $this; 52 } 53 54 public function getDisplayName() { 55 $name = $this->getName(); 56 if (strlen($name)) { 57 return $name; 58 } 59 60 return $this->getDefaultName(); 61 } 62 63 public function getDefaultName() { 64 return pht('Custom Trigger'); 65 } 66 67 public function getURI() { 68 return urisprintf( 69 '/project/trigger/%d/', 70 $this->getID()); 71 } 72 73 public function getObjectName() { 74 return pht('Trigger %d', $this->getID()); 75 } 76 77 public function setRuleset(array $ruleset) { 78 // Clear any cached trigger rules, since we're changing the ruleset 79 // for the trigger. 80 $this->triggerRules = null; 81 82 parent::setRuleset($ruleset); 83 } 84 85 public function getTriggerRules($viewer = null) { 86 if ($this->triggerRules === null) { 87 if (!$viewer) { 88 $viewer = $this->getViewer(); 89 } 90 91 $trigger_rules = self::newTriggerRulesFromRuleSpecifications( 92 $this->getRuleset(), 93 $allow_invalid = true, 94 $viewer); 95 96 $this->triggerRules = $trigger_rules; 97 } 98 99 return $this->triggerRules; 100 } 101 102 public static function newTriggerRulesFromRuleSpecifications( 103 array $list, 104 $allow_invalid, 105 PhabricatorUser $viewer) { 106 107 // NOTE: With "$allow_invalid" set, we're trying to preserve the database 108 // state in the rule structure, even if it includes rule types we don't 109 // have implementations for, or rules with invalid rule values. 110 111 // If an administrator adds or removes extensions which add rules, or 112 // an upgrade affects rule validity, existing rules may become invalid. 113 // When they do, we still want the UI to reflect the ruleset state 114 // accurately and "Edit" + "Save" shouldn't destroy data unless the 115 // user explicitly modifies the ruleset. 116 117 // In this mode, when we run into rules which are structured correctly but 118 // which have types we don't know about, we replace them with "Unknown 119 // Rules". If we know about the type of a rule but the value doesn't 120 // validate, we replace it with "Invalid Rules". These two rule types don't 121 // take any actions when a card is dropped into the column, but they show 122 // the user what's wrong with the ruleset and can be saved without causing 123 // any collateral damage. 124 125 $rule_map = PhabricatorProjectTriggerRule::getAllTriggerRules(); 126 127 // If the stored rule data isn't a list of rules (or we encounter other 128 // fundamental structural problems, below), there isn't much we can do 129 // to try to represent the state. 130 if (!is_array($list)) { 131 throw new PhabricatorProjectTriggerCorruptionException( 132 pht( 133 'Trigger ruleset is corrupt: expected a list of rule '. 134 'specifications, found "%s".', 135 phutil_describe_type($list))); 136 } 137 138 $trigger_rules = array(); 139 foreach ($list as $key => $rule) { 140 if (!is_array($rule)) { 141 throw new PhabricatorProjectTriggerCorruptionException( 142 pht( 143 'Trigger ruleset is corrupt: rule (at index "%s") should be a '. 144 'rule specification, but is actually "%s".', 145 $key, 146 phutil_describe_type($rule))); 147 } 148 149 try { 150 PhutilTypeSpec::checkMap( 151 $rule, 152 array( 153 'type' => 'string', 154 'value' => 'wild', 155 )); 156 } catch (PhutilTypeCheckException $ex) { 157 throw new PhabricatorProjectTriggerCorruptionException( 158 pht( 159 'Trigger ruleset is corrupt: rule (at index "%s") is not a '. 160 'valid rule specification: %s', 161 $key, 162 $ex->getMessage())); 163 } 164 165 $record = id(new PhabricatorProjectTriggerRuleRecord()) 166 ->setType(idx($rule, 'type')) 167 ->setValue(idx($rule, 'value')); 168 169 if (!isset($rule_map[$record->getType()])) { 170 if (!$allow_invalid) { 171 throw new PhabricatorProjectTriggerCorruptionException( 172 pht( 173 'Trigger ruleset is corrupt: rule type "%s" is unknown.', 174 $record->getType())); 175 } 176 177 $rule = new PhabricatorProjectTriggerUnknownRule(); 178 } else { 179 $rule = clone $rule_map[$record->getType()]; 180 } 181 182 try { 183 $rule->setRecord($record); 184 } catch (Exception $ex) { 185 if (!$allow_invalid) { 186 throw new PhabricatorProjectTriggerCorruptionException( 187 pht( 188 'Trigger ruleset is corrupt, rule (of type "%s") does not '. 189 'validate: %s', 190 $record->getType(), 191 $ex->getMessage())); 192 } 193 194 $rule = id(new PhabricatorProjectTriggerInvalidRule()) 195 ->setRecord($record) 196 ->setException($ex); 197 } 198 $rule->setViewer($viewer); 199 200 $trigger_rules[] = $rule; 201 } 202 203 return $trigger_rules; 204 } 205 206 207 public function getDropEffects() { 208 $effects = array(); 209 210 $rules = $this->getTriggerRules(); 211 foreach ($rules as $rule) { 212 foreach ($rule->getDropEffects() as $effect) { 213 $effects[] = $effect; 214 } 215 } 216 217 return $effects; 218 } 219 220 public function newDropTransactions( 221 PhabricatorUser $viewer, 222 PhabricatorProjectColumn $column, 223 $object) { 224 225 $trigger_xactions = array(); 226 foreach ($this->getTriggerRules($viewer) as $rule) { 227 $rule 228 ->setTrigger($this) 229 ->setColumn($column) 230 ->setObject($object); 231 232 $xactions = $rule->getDropTransactions( 233 $object, 234 $rule->getRecord()->getValue()); 235 236 if (!is_array($xactions)) { 237 throw new Exception( 238 pht( 239 'Expected trigger rule (of class "%s") to return a list of '. 240 'transactions from "newDropTransactions()", but got "%s".', 241 get_class($rule), 242 phutil_describe_type($xactions))); 243 } 244 245 $expect_type = get_class($object->getApplicationTransactionTemplate()); 246 assert_instances_of($xactions, $expect_type); 247 248 foreach ($xactions as $xaction) { 249 $trigger_xactions[] = $xaction; 250 } 251 } 252 253 return $trigger_xactions; 254 } 255 256 public function getPreviewEffect() { 257 $header = pht('Trigger: %s', $this->getDisplayName()); 258 259 return id(new PhabricatorProjectDropEffect()) 260 ->setIcon('fa-cogs') 261 ->setColor('blue') 262 ->setIsHeader(true) 263 ->setContent($header); 264 } 265 266 public function getSoundEffects() { 267 $sounds = array(); 268 269 foreach ($this->getTriggerRules() as $rule) { 270 foreach ($rule->getSoundEffects() as $effect) { 271 $sounds[] = $effect; 272 } 273 } 274 275 return $sounds; 276 } 277 278 public function getUsage() { 279 return $this->assertAttached($this->usage); 280 } 281 282 public function attachUsage(PhabricatorProjectTriggerUsage $usage) { 283 $this->usage = $usage; 284 return $this; 285 } 286 287 288/* -( PhabricatorApplicationTransactionInterface )------------------------- */ 289 290 291 public function getApplicationTransactionEditor() { 292 return new PhabricatorProjectTriggerEditor(); 293 } 294 295 public function getApplicationTransactionTemplate() { 296 return new PhabricatorProjectTriggerTransaction(); 297 } 298 299 300/* -( PhabricatorPolicyInterface )----------------------------------------- */ 301 302 303 public function getCapabilities() { 304 return array( 305 PhabricatorPolicyCapability::CAN_VIEW, 306 PhabricatorPolicyCapability::CAN_EDIT, 307 ); 308 } 309 310 public function getPolicy($capability) { 311 switch ($capability) { 312 case PhabricatorPolicyCapability::CAN_VIEW: 313 return PhabricatorPolicies::getMostOpenPolicy(); 314 case PhabricatorPolicyCapability::CAN_EDIT: 315 return $this->getEditPolicy(); 316 } 317 } 318 319 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 320 return false; 321 } 322 323 324/* -( PhabricatorDestructibleInterface )----------------------------------- */ 325 326 327 public function destroyObjectPermanently( 328 PhabricatorDestructionEngine $engine) { 329 330 $this->openTransaction(); 331 $conn = $this->establishConnection('w'); 332 333 // Remove the reference to this trigger from any columns which use it. 334 queryfx( 335 $conn, 336 'UPDATE %R SET triggerPHID = null WHERE triggerPHID = %s', 337 new PhabricatorProjectColumn(), 338 $this->getPHID()); 339 340 // Remove the usage index row for this trigger, if one exists. 341 queryfx( 342 $conn, 343 'DELETE FROM %R WHERE triggerPHID = %s', 344 new PhabricatorProjectTriggerUsage(), 345 $this->getPHID()); 346 347 $this->delete(); 348 349 $this->saveTransaction(); 350 } 351 352}