From 9c5e7bb71d5b10cf23eb648ded8193c2d1ab7bc9 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 22 Mar 2011 15:27:52 -0700 Subject: [PATCH] Horrendously rough cut of Herald rule editor. --- src/__phutil_library_map__.php | 2 + .../controller/rule/HeraldRuleController.php | 481 ++++++++++++++++++ .../herald/controller/rule/__init__.php | 29 ++ .../herald/storage/rule/HeraldRule.php | 2 +- 4 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 src/applications/herald/controller/rule/HeraldRuleController.php create mode 100644 src/applications/herald/controller/rule/__init__.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index db96c01108..1a51811ab5 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -197,6 +197,7 @@ phutil_register_library_map(array( 'HeraldObjectTranscript' => 'applications/herald/storage/transcript/object', 'HeraldRecursiveConditionsException' => 'applications/herald/engine/engine/exception', 'HeraldRule' => 'applications/herald/storage/rule', + 'HeraldRuleController' => 'applications/herald/controller/rule', 'HeraldRuleTranscript' => 'applications/herald/storage/transcript/rule', 'HeraldTranscript' => 'applications/herald/storage/transcript/base', 'HeraldValueTypeConfig' => 'applications/herald/config/valuetype', @@ -536,6 +537,7 @@ phutil_register_library_map(array( 'HeraldHomeController' => 'HeraldController', 'HeraldNewController' => 'HeraldController', 'HeraldRule' => 'HeraldDAO', + 'HeraldRuleController' => 'HeraldController', 'HeraldTranscript' => 'HeraldDAO', 'ManiphestController' => 'PhabricatorController', 'ManiphestDAO' => 'PhabricatorLiskDAO', diff --git a/src/applications/herald/controller/rule/HeraldRuleController.php b/src/applications/herald/controller/rule/HeraldRuleController.php new file mode 100644 index 0000000000..84c0e2b20b --- /dev/null +++ b/src/applications/herald/controller/rule/HeraldRuleController.php @@ -0,0 +1,481 @@ +id = (int)idx($data, 'id'); + } + + public function processRequest() { + + $request = $this->getRequest(); + $user = $request->getUser(); + + $content_type_map = HeraldContentTypeConfig::getContentTypeMap(); + + if ($this->id) { + $rule = id(new HeraldRule())->load($this->id); + if (!$rule) { + return new Aphront404Response(); + } + if ($rule->getAuthorPHID() != $user->getPHID()) { + throw new Exception("You don't own this rule and can't edit it."); + } + } else { + $rule = new HeraldRule(); + $rule->setAuthorPHID($user->getPHID()); + $rule->setMustMatchAll(true); + + $type = $request->getStr('type'); + if (!isset($content_type_map[$type])) { + $type = HeraldContentTypeConfig::CONTENT_TYPE_DIFFERENTIAL; + } + $rule->setContentType($type); + } + + $local_version = id(new HeraldRule())->getConfigVersion(); + if ($rule->getConfigVersion() > $local_version) { + throw new Exception( + "This rule was created with a newer version of Herald. You can not ". + "view or edit it in this older version. Try dev or wait for a push."); + } + + // Upgrade rule version to our version, since we might add newly-defined + // conditions, etc. + $rule->setConfigVersion($local_version); + + $rule_conditions = $rule->loadConditions(); + $rule_actions = $rule->loadActions(); + + $rule->attachConditions($rule_conditions); + $rule->attachActions($rule_actions); + + $arr = "\xC2\xAB"; + $e_name = true; + $errors = array(); + if ($request->isFormPost() && $request->getStr('save')) { + + $rule->setName($request->getStr('name')); + $rule->setMustMatchAll(($request->getStr('must_match') == 'all')); + + if (!strlen($rule->getName())) { + $e_name = "{$arr} Required"; + $errors[] = "Rule must have a name."; + } + + $data = json_decode($request->getStr('rule'), true); + if (!is_array($data) || + !$data['conditions'] || + !$data['actions']) { + throw new Exception("Failed to decode rule data."); + } + + $conditions = array(); + foreach ($data['conditions'] as $condition) { + $obj = new HeraldCondition(); + $obj->setFieldName($condition[0]); + $obj->setCondition($condition[1]); + + if (is_array($condition[2])) { + $obj->setValue(array_keys($condition[2])); + } else { + $obj->setValue($condition[2]); + } + + $cond_type = $obj->getCondition(); + + if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP) { + if (@preg_match($obj->getValue(), '') === false) { + $errors[] = + 'The regular expression "'.$obj->getValue().'" is not valid. '. + 'Regular expressions must have enclosing characters (e.g. '. + '"@/path/to/file@", not "/path/to/file") and be syntactically '. + 'correct.'; + } + } + + if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP_PAIR) { + $json = json_decode($obj->getValue(), true); + if (!is_array($json)) { + $errors[] = + 'The regular expression pair "'.$obj->getValue().'" is not '. + 'valid JSON. Enter a valid JSON array with two elements.'; + } else { + if (count($json) != 2) { + $errors[] = + 'The regular expression pair "'.$obj->getValue().'" must have '. + 'exactly two elements.'; + } else { + $key_regexp = array_shift($json); + $val_regexp = array_shift($json); + + if (@preg_match($key_regexp, '') === false) { + $errors[] = + 'The first regexp, "'.$key_regexp.'" in the regexp pair '. + 'is not a valid regexp.'; + } + if (@preg_match($val_regexp, '') === false) { + $errors[] = + 'The second regexp, "'.$val_regexp.'" in the regexp pair '. + 'is not a valid regexp.'; + } + } + } + } + + $conditions[] = $obj; + } + + $actions = array(); + foreach ($data['actions'] as $action) { + $obj = new HeraldAction(); + $obj->setAction($action[0]); + + if (!isset($action[1])) { + // Legitimate for any action which doesn't need a target, like + // "Do nothing". + $action[1] = null; + } + + if (is_array($action[1])) { + $obj->setTarget(array_keys($action[1])); + } else { + $obj->setTarget($action[1]); + } + + $actions[] = $obj; + } + + $rule->attachConditions($conditions); + $rule->attachActions($actions); + + if (!$errors) { + try { + + $rule->openTransaction(); + $rule->save(); + $rule->saveConditions($conditions); + $rule->saveActions($actions); + $rule->saveTransaction(); + + $uri = '/herald/view/'.$rule->getContentType().'/'; + + return id(new AphrontRedirectResponse()) + ->setURI($uri); + } catch (QueryDuplicateKeyException $ex) { + $e_name = "{$arr} Not Unique"; + $errors[] = "Rule name is not unique. Choose a unique name."; + } + } + + } + + $phids = array(); + $phids[] = $rule->getAuthorPHID(); + + foreach ($rule->getActions() as $action) { + foreach ($action->getTarget() as $target) { + $target = (array)$target; + foreach ($target as $phid) { + $phids[] = $phid; + } + } + } + + foreach ($rule->getConditions() as $condition) { + $value = $condition->getValue(); + if (is_array($value)) { + foreach ($value as $phid) { + $phids[] = $phid; + } + } + } + + $handles = id(new PhabricatorObjectHandleData($phids)) + ->loadHandles(); + + if ($errors) { + $errors = '!!';//; + } + + +// require_static('herald-css'); + + + $options = array( + 'all' => 'all of', + 'any' => 'any of', + ); + + $selected = $rule->getMustMatchAll() ? 'all' : 'any'; + + $must_match = array(); + foreach ($options as $key => $option) { + $must_match[] = phutil_render_tag( + 'option', + array( + 'selected' => ($selected == $key) ? 'selected' : null, + ), + phutil_escape_html($option)); + } + $must_match = + ''; + + if ($rule->getID()) { + $action = '/herald/rule/'.$rule->getID().'/'; + } else { + $action = '/herald/rule/'.$rule->getID().'/'; + } + + $type_name = $content_type_map[$rule->getContentType()]; + + $form = id(new AphrontFormView()) + ->setUser($user) + ->addHiddenInput('type', $rule->getContentType()) + ->addHiddenInput('save', true) + ->addHiddenInput('rule', '') + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel('Rule Name') + ->setError($e_name) + ->setValue($rule->getName())) + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel('Author') + ->setValue($handles[$rule->getAuthorPHID()]->getName())) + ->appendChild( + id(new AphrontFormMarkupControl()) + ->setValue( + "This rule triggers for {$type_name}.")) + ->appendChild( + '

Conditions

'. + '
'. + 'Create New Condition'. + '

When '.$must_match.' these conditions are met:

'. + '
'. + '
') + ->appendChild( + '

Action

'. + '
'. + 'Create New Action'. + '

Take these actions:

'. + '
'. + '
') + ->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue('Save') + ->addCancelButton('/herald/view/'.$rule->getContentType().'/')); +/* + $form = +
+ + + + + getStr('type')} /> + + {$errors} + +

Edit Rule

+ + + + getName()} /> + + + + {$handles[$rule->getAuthorPHID()]->getName()} + + + + This rule triggers for + {$content_type_map[$rule->getContentType()]}. + + + + +

Conditions

+ + +
+ + + When {$must_match} these conditions are met: + +
+
+ + + + +

Actions

+ +
+ + Take these actions: +
+
+
+ + +
+ + + Cancel + +
+ + + ; + +*/ + $serial_conditions = array( + array('default', 'default', ''), + ); + if ($rule->getConditions()) { + $serial_conditions = array(); + foreach ($rule->getConditions() as $condition) { + + $value = $condition->getValue(); + if (is_array($value)) { + $value_map = array(); + foreach ($value as $k => $fbid) { + $value_map[$fbid] = $handles[$fbid]->getExtendedDisplayName(); + } + $value = $value_map; + } + + $serial_conditions[] = array( + $condition->getFieldName(), + $condition->getCondition(), + $value, + ); + } + } + + $serial_actions = array( + array('default', ''), + ); + if ($rule->getActions()) { + $serial_actions = array(); + foreach ($rule->getActions() as $action) { + + $target_map = array(); + foreach ((array)$action->getTarget() as $fbid) { + $target_map[$fbid] = $handles[$fbid]->getExtendedDisplayName(); + } + + $serial_actions[] = array( + $action->getAction(), + $target_map, + ); + } + } + + $all_rules = id(new HeraldRule())->loadAllWhere( + 'authorPHID = %d AND contentType = %s', + $rule->getAuthorPHID(), + $rule->getContentType()); + $all_rules = mpull($all_rules, 'getName', 'getID'); + asort($all_rules); + unset($all_rules[$rule->getID()]); + + + $config_info = array(); + $config_info['fields'] + = HeraldFieldConfig::getFieldMapForContentType($rule->getContentType()); + $config_info['conditions'] = HeraldConditionConfig::getConditionMap(); + foreach ($config_info['fields'] as $field => $name) { + $config_info['conditionMap'][$field] = array_keys( + HeraldConditionConfig::getConditionMapForField($field)); + } + foreach ($config_info['fields'] as $field => $fname) { + foreach ($config_info['conditions'] as $condition => $cname) { + $config_info['values'][$field][$condition] = + HeraldValueTypeConfig::getValueTypeForFieldAndCondition( + $field, + $condition); + } + } + + $config_info['actions'] = + HeraldActionConfig::getActionMapForContentType($rule->getContentType()); + + foreach ($config_info['actions'] as $action => $name) { + $config_info['targets'][$action] = + HeraldValueTypeConfig::getValueTypeForAction($action); + } + +/* + Javelin::initBehavior( + 'herald-rule-editor', + array( + 'root' => 'qq',//$form->requireUniqueId(), + 'conditions' => (object) $serial_conditions, + 'actions' => (object) $serial_actions, + 'template' => $this->buildTokenizerTemplates() + array( + 'rules' => $all_rules, + ), + 'info' => $config_info, + )); + +*/ + + $panel = new AphrontPanelView(); + $panel->setHeader('Edit Herald Rule'); + $panel->setWidth(AphrontPanelView::WIDTH_WIDE); + $panel->appendChild($form); + + return $this->buildStandardPageResponse( + $panel, + array( + 'title' => 'Edit Rule', + )); + } + + protected function buildTokenizerTemplates() { + return array( + 'source' => array( + 'email' => '/datasource/mailable/', + 'user' => '/datasource/user/', + 'repository' => '/datasource/repository/', + 'tag' => '/datasource/tag/', + 'package' => '/datasource/package/', + ), + 'markup' => 'derp derp',//id()->toString(), + ); + } +} diff --git a/src/applications/herald/controller/rule/__init__.php b/src/applications/herald/controller/rule/__init__.php new file mode 100644 index 0000000000..8ce0b727a7 --- /dev/null +++ b/src/applications/herald/controller/rule/__init__.php @@ -0,0 +1,29 @@ +