Page MenuHomePhabricator

D20302.diff
No OneTemporary

D20302.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -4186,6 +4186,7 @@
'PhabricatorProjectTriggerQuery' => 'applications/project/query/PhabricatorProjectTriggerQuery.php',
'PhabricatorProjectTriggerRule' => 'applications/project/trigger/PhabricatorProjectTriggerRule.php',
'PhabricatorProjectTriggerRuleRecord' => 'applications/project/trigger/PhabricatorProjectTriggerRuleRecord.php',
+ 'PhabricatorProjectTriggerRulesetTransaction' => 'applications/project/xaction/trigger/PhabricatorProjectTriggerRulesetTransaction.php',
'PhabricatorProjectTriggerSearchEngine' => 'applications/project/query/PhabricatorProjectTriggerSearchEngine.php',
'PhabricatorProjectTriggerTransaction' => 'applications/project/storage/PhabricatorProjectTriggerTransaction.php',
'PhabricatorProjectTriggerTransactionQuery' => 'applications/project/query/PhabricatorProjectTriggerTransactionQuery.php',
@@ -10319,6 +10320,7 @@
'PhabricatorProjectTriggerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorProjectTriggerRule' => 'Phobject',
'PhabricatorProjectTriggerRuleRecord' => 'Phobject',
+ 'PhabricatorProjectTriggerRulesetTransaction' => 'PhabricatorProjectTriggerTransactionType',
'PhabricatorProjectTriggerSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorProjectTriggerTransaction' => 'PhabricatorModularTransaction',
'PhabricatorProjectTriggerTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
diff --git a/src/applications/project/controller/trigger/PhabricatorProjectTriggerEditController.php b/src/applications/project/controller/trigger/PhabricatorProjectTriggerEditController.php
--- a/src/applications/project/controller/trigger/PhabricatorProjectTriggerEditController.php
+++ b/src/applications/project/controller/trigger/PhabricatorProjectTriggerEditController.php
@@ -55,6 +55,7 @@
$v_name = $trigger->getName();
$v_edit = $trigger->getEditPolicy();
+ $v_rules = $trigger->getTriggerRules();
$e_name = null;
$e_edit = null;
@@ -65,8 +66,15 @@
$v_name = $request->getStr('name');
$v_edit = $request->getStr('editPolicy');
- $v_rules = $request->getStr('rules');
- $v_rules = phutil_json_decode($v_rules);
+ // Read the JSON rules from the request and convert them back into
+ // "TriggerRule" objects so we can render the correct form state
+ // if the user is modifying the rules
+ $raw_rules = $request->getStr('rules');
+ $raw_rules = phutil_json_decode($raw_rules);
+
+ $copy = clone $trigger;
+ $copy->setRuleset($raw_rules);
+ $v_rules = $copy->getTriggerRules();
$xactions = array();
if (!$trigger->getID()) {
@@ -84,7 +92,10 @@
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($v_edit);
- // TODO: Actually write the new rules to the database.
+ $xactions[] = $trigger->getApplicationTransactionTemplate()
+ ->setTransactionType(
+ PhabricatorProjectTriggerRulesetTransaction::TRANSACTIONTYPE)
+ ->setNewValue($raw_rules);
$editor = $trigger->getApplicationTransactionEditor()
->setActor($viewer)
@@ -207,6 +218,7 @@
$this->setupEditorBehavior(
$trigger,
+ $v_rules,
$form_id,
$table_id,
$create_id,
@@ -250,12 +262,12 @@
private function setupEditorBehavior(
PhabricatorProjectTrigger $trigger,
+ array $rule_list,
$form_id,
$table_id,
$create_id,
$input_id) {
- $rule_list = $trigger->getTriggerRules();
$rule_list = mpull($rule_list, 'toDictionary');
$rule_list = array_values($rule_list);
diff --git a/src/applications/project/storage/PhabricatorProjectTrigger.php b/src/applications/project/storage/PhabricatorProjectTrigger.php
--- a/src/applications/project/storage/PhabricatorProjectTrigger.php
+++ b/src/applications/project/storage/PhabricatorProjectTrigger.php
@@ -62,114 +62,128 @@
return pht('Trigger %d', $this->getID());
}
+ public function setRuleset(array $ruleset) {
+ // Clear any cached trigger rules, since we're changing the ruleset
+ // for the trigger.
+ $this->triggerRules = null;
+
+ parent::setRuleset($ruleset);
+ }
+
public function getTriggerRules() {
if ($this->triggerRules === null) {
+ $trigger_rules = self::newTriggerRulesFromRuleSpecifications(
+ $this->getRuleset(),
+ $allow_invalid = true);
+
+ $this->triggerRules = $trigger_rules;
+ }
+
+ return $this->triggerRules;
+ }
+
+ public static function newTriggerRulesFromRuleSpecifications(
+ array $list,
+ $allow_invalid) {
+
+ // 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
+ // ha ve implementations for, or rules with invalid rule values.
+
+ // If an administrator adds or removes extensions which add rules, or
+ // an upgrade affects rule validity, existing rules may become invalid.
+ // When they do, we still want the UI to reflect the ruleset state
+ // accurately and "Edit" + "Save" shouldn't destroy data unless the
+ // user explicitly modifies the ruleset.
+
+ // 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
+ // 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
+ // 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
+ // any collateral damage.
+
+ $rule_map = PhabricatorProjectTriggerRule::getAllTriggerRules();
+
+ // 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
+ // to try to represent the state.
+ if (!is_array($list)) {
+ throw new PhabricatorProjectTriggerCorruptionException(
+ pht(
+ 'Trigger ruleset is corrupt: expected a list of rule '.
+ 'specifications, found "%s".',
+ phutil_describe_type($list)));
+ }
+
+ $trigger_rules = array();
+ foreach ($list as $key => $rule) {
+ if (!is_array($rule)) {
+ throw new PhabricatorProjectTriggerCorruptionException(
+ pht(
+ 'Trigger ruleset is corrupt: rule (with key "%s") should be a '.
+ 'rule specification, but is actually "%s".',
+ $key,
+ phutil_describe_type($rule)));
+ }
- // TODO: Temporary hard-coded rule specification.
- $rule_specifications = array(
- array(
- 'type' => 'status',
- 'value' => ManiphestTaskStatus::getDefaultClosedStatus(),
- ),
- // This is an intentionally unknown rule.
- array(
- 'type' => 'quack',
- 'value' => 'aaa',
- ),
- // This is an intentionally invalid rule.
- array(
- 'type' => 'status',
- 'value' => 'quack',
- ),
- );
-
- // NOTE: We're trying to preserve the database state in the rule
- // structure, even if it includes rule types we don't have implementations
- // for, or rules with invalid rule values.
-
- // If an administrator adds or removes extensions which add rules, or
- // an upgrade affects rule validity, existing rules may become invalid.
- // When they do, we still want the UI to reflect the ruleset state
- // accurately and "Edit" + "Save" shouldn't destroy data unless the
- // user explicitly modifies the ruleset.
-
- // When we run into rules which are structured correctly but 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 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 the user
- // what's wrong with the ruleset and can be saved without causing any
- // collateral damage.
-
- $rule_map = PhabricatorProjectTriggerRule::getAllTriggerRules();
-
- // 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
- // to try to represent the state.
- if (!is_array($rule_specifications)) {
+ try {
+ PhutilTypeSpec::checkMap(
+ $rule,
+ array(
+ 'type' => 'string',
+ 'value' => 'wild',
+ ));
+ } catch (PhutilTypeCheckException $ex) {
throw new PhabricatorProjectTriggerCorruptionException(
pht(
- 'Trigger ("%s") has a corrupt ruleset: expected a list of '.
- 'rule specifications, found "%s".',
- $this->getPHID(),
- phutil_describe_type($rule_specifications)));
+ 'Trigger ruleset is corrupt: rule (with key "%s") is not a '.
+ 'valid rule specification: %s',
+ $key,
+ $ex->getMessage()));
}
- $trigger_rules = array();
- foreach ($rule_specifications as $key => $rule) {
- if (!is_array($rule)) {
+ $record = id(new PhabricatorProjectTriggerRuleRecord())
+ ->setType(idx($rule, 'type'))
+ ->setValue(idx($rule, 'value'));
+
+ if (!isset($rule_map[$record->getType()])) {
+ if (!$allow_invalid) {
throw new PhabricatorProjectTriggerCorruptionException(
pht(
- 'Trigger ("%s") has a corrupt ruleset: rule (with key "%s") '.
- 'should be a rule specification, but is actually "%s".',
- $this->getPHID(),
- $key,
- phutil_describe_type($rule)));
+ 'Trigger ruleset is corrupt: rule type "%s" is unknown.',
+ $record->getType()));
}
- try {
- PhutilTypeSpec::checkMap(
- $rule,
- array(
- 'type' => 'string',
- 'value' => 'wild',
- ));
- } catch (PhutilTypeCheckException $ex) {
+ $rule = new PhabricatorProjectTriggerUnknownRule();
+ } else {
+ $rule = clone $rule_map[$record->getType()];
+ }
+
+ try {
+ $rule->setRecord($record);
+ } catch (Exception $ex) {
+ if (!$allow_invalid) {
throw new PhabricatorProjectTriggerCorruptionException(
pht(
- 'Trigger ("%s") has a corrupt ruleset: rule (with key "%s") '.
- 'is not a valid rule specification: %s',
- $this->getPHID(),
- $key,
+ 'Trigger ruleset is corrupt, rule (of type "%s") does not '.
+ 'validate: %s',
+ $record->getType(),
$ex->getMessage()));
}
- $record = id(new PhabricatorProjectTriggerRuleRecord())
- ->setType(idx($rule, 'type'))
- ->setValue(idx($rule, 'value'));
-
- if (!isset($rule_map[$record->getType()])) {
- $rule = new PhabricatorProjectTriggerUnknownRule();
- } else {
- $rule = clone $rule_map[$record->getType()];
- }
-
- try {
- $rule->setRecord($record);
- } catch (Exception $ex) {
- $rule = id(new PhabricatorProjectTriggerInvalidRule())
- ->setRecord($record);
- }
-
- $trigger_rules[] = $rule;
+ $rule = id(new PhabricatorProjectTriggerInvalidRule())
+ ->setRecord($record);
}
- $this->triggerRules = $trigger_rules;
+ $trigger_rules[] = $rule;
}
- return $this->triggerRules;
+ return $trigger_rules;
}
+
public function getDropEffects() {
$effects = array();
diff --git a/src/applications/project/xaction/trigger/PhabricatorProjectTriggerRulesetTransaction.php b/src/applications/project/xaction/trigger/PhabricatorProjectTriggerRulesetTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/xaction/trigger/PhabricatorProjectTriggerRulesetTransaction.php
@@ -0,0 +1,65 @@
+<?php
+
+final class PhabricatorProjectTriggerRulesetTransaction
+ extends PhabricatorProjectTriggerTransactionType {
+
+ const TRANSACTIONTYPE = 'ruleset';
+
+ public function generateOldValue($object) {
+ return $object->getRuleset();
+ }
+
+ public function applyInternalEffects($object, $value) {
+ $object->setRuleset($value);
+ }
+
+ public function getTitle() {
+ return pht(
+ '%s updated the ruleset for this trigger.',
+ $this->renderAuthor());
+ }
+
+ public function validateTransactions($object, array $xactions) {
+ $errors = array();
+
+ foreach ($xactions as $xaction) {
+ $ruleset = $xaction->getNewValue();
+
+ try {
+ PhabricatorProjectTrigger::newTriggerRulesFromRuleSpecifications(
+ $ruleset,
+ $allow_invalid = false);
+ } catch (PhabricatorProjectTriggerCorruptionException $ex) {
+ $errors[] = $this->newInvalidError(
+ pht(
+ 'Ruleset specification is not valid. %s',
+ $ex->getMessage()),
+ $xaction);
+ continue;
+ }
+ }
+
+ return $errors;
+ }
+
+ public function hasChangeDetailView() {
+ return true;
+ }
+
+ public function newChangeDetailView() {
+ $viewer = $this->getViewer();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ $json = new PhutilJSON();
+ $old_json = $json->encodeAsList($old);
+ $new_json = $json->encodeAsList($new);
+
+ return id(new PhabricatorApplicationTransactionTextDiffDetailView())
+ ->setViewer($viewer)
+ ->setOldText($old_json)
+ ->setNewText($new_json);
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Sun, Mar 23, 5:54 AM (3 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7384424
Default Alt Text
D20302.diff (13 KB)

Event Timeline