Changeset View
Changeset View
Standalone View
Standalone View
src/applications/project/storage/PhabricatorProjectTrigger.php
<?php | <?php | ||||
final class PhabricatorProjectTrigger | final class PhabricatorProjectTrigger | ||||
extends PhabricatorProjectDAO | extends PhabricatorProjectDAO | ||||
implements | implements | ||||
PhabricatorApplicationTransactionInterface, | PhabricatorApplicationTransactionInterface, | ||||
PhabricatorPolicyInterface, | PhabricatorPolicyInterface, | ||||
PhabricatorIndexableInterface, | PhabricatorIndexableInterface, | ||||
PhabricatorDestructibleInterface { | PhabricatorDestructibleInterface { | ||||
protected $name; | protected $name; | ||||
protected $ruleset = array(); | protected $ruleset = array(); | ||||
protected $editPolicy; | protected $editPolicy; | ||||
private $triggerRules; | private $triggerRules; | ||||
private $viewer; | |||||
private $usage = self::ATTACHABLE; | private $usage = self::ATTACHABLE; | ||||
public static function initializeNewTrigger() { | public static function initializeNewTrigger() { | ||||
$default_edit = PhabricatorPolicies::POLICY_USER; | $default_edit = PhabricatorPolicies::POLICY_USER; | ||||
return id(new self()) | return id(new self()) | ||||
->setName('') | ->setName('') | ||||
->setEditPolicy($default_edit); | ->setEditPolicy($default_edit); | ||||
Show All 12 Lines | return array( | ||||
), | ), | ||||
) + parent::getConfiguration(); | ) + parent::getConfiguration(); | ||||
} | } | ||||
public function getPHIDType() { | public function getPHIDType() { | ||||
return PhabricatorProjectTriggerPHIDType::TYPECONST; | return PhabricatorProjectTriggerPHIDType::TYPECONST; | ||||
} | } | ||||
public function getViewer() { | |||||
return $this->viewer; | |||||
} | |||||
public function setViewer(PhabricatorUser $user) { | |||||
$this->viewer = $user; | |||||
return $this; | |||||
} | |||||
public function getDisplayName() { | public function getDisplayName() { | ||||
$name = $this->getName(); | $name = $this->getName(); | ||||
if (strlen($name)) { | if (strlen($name)) { | ||||
return $name; | return $name; | ||||
} | } | ||||
return $this->getDefaultName(); | return $this->getDefaultName(); | ||||
} | } | ||||
Show All 15 Lines | final class PhabricatorProjectTrigger | ||||
public function setRuleset(array $ruleset) { | public function setRuleset(array $ruleset) { | ||||
// Clear any cached trigger rules, since we're changing the ruleset | // Clear any cached trigger rules, since we're changing the ruleset | ||||
// for the trigger. | // for the trigger. | ||||
$this->triggerRules = null; | $this->triggerRules = null; | ||||
parent::setRuleset($ruleset); | parent::setRuleset($ruleset); | ||||
} | } | ||||
public function getTriggerRules() { | public function getTriggerRules($viewer = null) { | ||||
epriestley: Can we make this `(PhabricatorUser $viewer)` and always pass it, or does that get tricky? | |||||
Done Inline ActionsWe can; the biggest side effect is that PhabricatorProjectTrigger->getDropEffects() and PhabricatorProjectTrigger->getSoundEffects() also have to take $viewer. If you're fine with that, I'll do that and remove most (all?) of the setViewer() calls. amckinley: We can; the biggest side effect is that `PhabricatorProjectTrigger->getDropEffects()` and… | |||||
Done Inline ActionsOh right; and then that means PhabricatorProjectColumn->getDropEffects() also has to take $viewer, and that was the point where I gave up and decided this was creeping into too many callsites. amckinley: Oh right; and then that means `PhabricatorProjectColumn->getDropEffects()` also has to take… | |||||
if ($this->triggerRules === null) { | if ($this->triggerRules === null) { | ||||
if (!$viewer) { | |||||
$viewer = $this->getViewer(); | |||||
} | |||||
$trigger_rules = self::newTriggerRulesFromRuleSpecifications( | $trigger_rules = self::newTriggerRulesFromRuleSpecifications( | ||||
$this->getRuleset(), | $this->getRuleset(), | ||||
$allow_invalid = true); | $allow_invalid = true, | ||||
$viewer); | |||||
$this->triggerRules = $trigger_rules; | $this->triggerRules = $trigger_rules; | ||||
} | } | ||||
return $this->triggerRules; | return $this->triggerRules; | ||||
} | } | ||||
public static function newTriggerRulesFromRuleSpecifications( | public static function newTriggerRulesFromRuleSpecifications( | ||||
array $list, | array $list, | ||||
$allow_invalid) { | $allow_invalid, | ||||
PhabricatorUser $viewer) { | |||||
// NOTE: With "$allow_invalid" set, we're trying to preserve the database | // NOTE: With "$allow_invalid" set, we're trying to preserve the database | ||||
// state in the rule structure, even if it includes rule types we don't | // state in the rule structure, even if it includes rule types we don't | ||||
// ha ve implementations for, or rules with invalid rule values. | // ha ve implementations for, or rules with invalid rule values. | ||||
// If an administrator adds or removes extensions which add rules, or | // If an administrator adds or removes extensions which add rules, or | ||||
// an upgrade affects rule validity, existing rules may become invalid. | // an upgrade affects rule validity, existing rules may become invalid. | ||||
// When they do, we still want the UI to reflect the ruleset state | // When they do, we still want the UI to reflect the ruleset state | ||||
// accurately and "Edit" + "Save" shouldn't destroy data unless the | // accurately and "Edit" + "Save" shouldn't destroy data unless the | ||||
// user explicitly modifies the ruleset. | // user explicitly modifies the ruleset. | ||||
// In this mode, when we run into rules which are structured correctly but | // In this mode, when we run into rules which are structured correctly but | ||||
// which have types we don't know about, we replace them with "Unknown | // which have types we don't know about, we replace them with "Unknown | ||||
// Rules". If we know about the type of a rule but the value doesn't | // Rules". If we know about the type of a rule but the value doesn't | ||||
// validate, we replace it with "Invalid Rules". These two rule types don't | // validate, we replace it with "Invalid Rules". These two rule types don't | ||||
// take any actions when a card is dropped into the column, but they show | // take any actions when a card is dropped into the column, but they show | ||||
// the user what's wrong with the ruleset and can be saved without causing | // the user what's wrong with the ruleset and can be saved without causing | ||||
// any collateral damage. | // any collateral damage. | ||||
$rule_map = PhabricatorProjectTriggerRule::getAllTriggerRules(); | $rule_map = PhabricatorProjectTriggerRule::getAllTriggerRules(); | ||||
Done Inline ActionsThis doesn't actually take $viewer, I think. epriestley: This doesn't actually take `$viewer`, I think. | |||||
// If the stored rule data isn't a list of rules (or we encounter other | // If the stored rule data isn't a list of rules (or we encounter other | ||||
// fundamental structural problems, below), there isn't much we can do | // fundamental structural problems, below), there isn't much we can do | ||||
// to try to represent the state. | // to try to represent the state. | ||||
if (!is_array($list)) { | if (!is_array($list)) { | ||||
throw new PhabricatorProjectTriggerCorruptionException( | throw new PhabricatorProjectTriggerCorruptionException( | ||||
pht( | pht( | ||||
'Trigger ruleset is corrupt: expected a list of rule '. | 'Trigger ruleset is corrupt: expected a list of rule '. | ||||
'specifications, found "%s".', | 'specifications, found "%s".', | ||||
phutil_describe_type($list))); | phutil_describe_type($list))); | ||||
} | } | ||||
$trigger_rules = array(); | $trigger_rules = array(); | ||||
foreach ($list as $key => $rule) { | foreach ($list as $key => $rule) { | ||||
if (!is_array($rule)) { | if (!is_array($rule)) { | ||||
throw new PhabricatorProjectTriggerCorruptionException( | throw new PhabricatorProjectTriggerCorruptionException( | ||||
pht( | pht( | ||||
'Trigger ruleset is corrupt: rule (with key "%s") should be a '. | 'Trigger ruleset is corrupt: rule (at index "%s") should be a '. | ||||
'rule specification, but is actually "%s".', | 'rule specification, but is actually "%s".', | ||||
$key, | $key, | ||||
phutil_describe_type($rule))); | phutil_describe_type($rule))); | ||||
} | } | ||||
try { | try { | ||||
PhutilTypeSpec::checkMap( | PhutilTypeSpec::checkMap( | ||||
$rule, | $rule, | ||||
array( | array( | ||||
'type' => 'string', | 'type' => 'string', | ||||
'value' => 'wild', | 'value' => 'wild', | ||||
)); | )); | ||||
} catch (PhutilTypeCheckException $ex) { | } catch (PhutilTypeCheckException $ex) { | ||||
throw new PhabricatorProjectTriggerCorruptionException( | throw new PhabricatorProjectTriggerCorruptionException( | ||||
pht( | pht( | ||||
'Trigger ruleset is corrupt: rule (with key "%s") is not a '. | 'Trigger ruleset is corrupt: rule (at index "%s") is not a '. | ||||
'valid rule specification: %s', | 'valid rule specification: %s', | ||||
$key, | $key, | ||||
$ex->getMessage())); | $ex->getMessage())); | ||||
} | } | ||||
$record = id(new PhabricatorProjectTriggerRuleRecord()) | $record = id(new PhabricatorProjectTriggerRuleRecord()) | ||||
->setType(idx($rule, 'type')) | ->setType(idx($rule, 'type')) | ||||
->setValue(idx($rule, 'value')); | ->setValue(idx($rule, 'value')); | ||||
Show All 22 Lines | foreach ($list as $key => $rule) { | ||||
$record->getType(), | $record->getType(), | ||||
$ex->getMessage())); | $ex->getMessage())); | ||||
} | } | ||||
$rule = id(new PhabricatorProjectTriggerInvalidRule()) | $rule = id(new PhabricatorProjectTriggerInvalidRule()) | ||||
->setRecord($record) | ->setRecord($record) | ||||
->setException($ex); | ->setException($ex); | ||||
} | } | ||||
$rule->setViewer($viewer); | |||||
$trigger_rules[] = $rule; | $trigger_rules[] = $rule; | ||||
} | } | ||||
return $trigger_rules; | return $trigger_rules; | ||||
} | } | ||||
Show All 11 Lines | final class PhabricatorProjectTrigger | ||||
} | } | ||||
public function newDropTransactions( | public function newDropTransactions( | ||||
PhabricatorUser $viewer, | PhabricatorUser $viewer, | ||||
PhabricatorProjectColumn $column, | PhabricatorProjectColumn $column, | ||||
$object) { | $object) { | ||||
$trigger_xactions = array(); | $trigger_xactions = array(); | ||||
foreach ($this->getTriggerRules() as $rule) { | foreach ($this->getTriggerRules($viewer) as $rule) { | ||||
$rule | $rule | ||||
->setViewer($viewer) | |||||
->setTrigger($this) | ->setTrigger($this) | ||||
->setColumn($column) | ->setColumn($column) | ||||
->setObject($object); | ->setObject($object); | ||||
$xactions = $rule->getDropTransactions( | $xactions = $rule->getDropTransactions( | ||||
$object, | $object, | ||||
$rule->getRecord()->getValue()); | $rule->getRecord()->getValue()); | ||||
▲ Show 20 Lines • Show All 117 Lines • Show Last 20 Lines |
Can we make this (PhabricatorUser $viewer) and always pass it, or does that get tricky?