diff --git a/CRM/Mailingtools/Form/Settings.php b/CRM/Mailingtools/Form/Settings.php index 533a815..adb1f70 100644 --- a/CRM/Mailingtools/Form/Settings.php +++ b/CRM/Mailingtools/Form/Settings.php @@ -21,6 +21,7 @@ * @see https://wiki.civicrm.org/confluence/display/CRMDOC/QuickForm+Reference */ class CRM_Mailingtools_Form_Settings extends CRM_Core_Form { + public function buildQuickForm() { $config = CRM_Mailingtools_Config::singleton(); @@ -130,7 +131,37 @@ public function buildQuickForm() { E::ts('Fix {contact.hash} Token') ); - // Token Tools + // Regex Tokens + $token_indices = range(0,CRM_Mailingtools_RegexToken::MT_REGEX_TOKEN_COUNT); + $this->assign('regex_token_indices', $token_indices); + foreach ($token_indices as $token_index) { + $this->add( + 'text', + "regex_token_{$token_index}_def", + E::ts('Regular Expression') + ); + $this->add( + 'select', + "regex_token_{$token_index}_op", + E::ts('Operator Type') + ); + $this->add( + 'text', + "regex_token_{$token_index}_val", + E::ts('Value Function') + ); + } + // set defaults + $current_tokens = CRM_Mailingtools_RegexToken::getTokenDefinitions(); + foreach ($current_tokens as $token_index => $token_definition) { + $this->setDefaults([ + "regex_token_{$token_index}_def" => $token_definition['def'], + "regex_token_{$token_index}_op" => $token_definition['op'], + "regex_token_{$token_index}_val" => $token_definition['val'], + ]); + } + + // Mosaico Save Message $this->add( 'checkbox', 'mosaico_save_message', diff --git a/CRM/Mailingtools/RegexToken.php b/CRM/Mailingtools/RegexToken.php new file mode 100644 index 0000000..bbe7c58 --- /dev/null +++ b/CRM/Mailingtools/RegexToken.php @@ -0,0 +1,143 @@ +[a-zA-Z_]+)::(?P[a-zA-Z_]+)$/'; + const VALUE_API_CALL = '/^(?P[a-zA-Z]+)[.](?P[a-zA-Z_]+)$/'; + + + /** + * Get the current token definition specs as an array of + * [ + * 'def' => (string) regular expression without delimiters + * 'op' => (string) operator type (api3, static, replace) + * 'val' => (string) call spec, e.g. "entity.action", or "class::function" + * ] + * @return array list of such specs + */ + public static function getTokenDefinitions() { + $value = Civi::settings()->get('mailingtools_regex_tokens'); + if (empty($value) || !is_array($value)) { + return []; + } else { + return $value; + } + } + + /** + * Set the current token definition specs + * @param $token_definitions array see getTokenDefinitions + */ + public static function setTokenDefinitions($token_definitions) { + Civi::settings()->set('mailingtools_regex_tokens', $token_definitions); + } + + + /** + * Verify the presented token definition, and return an + * error string if not valid + * + * @param $token_definition array definition, see getTokenDefinitions + * @return string|false error or false ("all clear") + */ + public static function verifyTokenDefinition($token_definition) { + // test if present + if (empty($token_definition['def'])) { + return E::ts("Incomplete definition: definition (regular expression) missing"); + } + if (empty($token_definition['op'])) { + return E::ts("Incomplete definition: value type missing"); + } + if (empty($token_definition['val'])) { + return E::ts("Incomplete definition: value missing"); + } + + // verify definition (regex) + try { + preg_match(self::REGEX_DELIMITER . $token_definition['def'] . self::REGEX_DELIMITER, 'doesntmatter'); + } catch (Exception $ex) { + return E::ts("Incomplete definition: definition is not a valid regular expression"); + } + + // verify operation + switch ($token_definition['op']) { + case self::OPERATOR_API3: + if (preg_match(self::VALUE_API_CALL, $token_definition['val'], $match)) { + // verify api entity.action + try { + $actions = civicrm_api3($match['entity'], 'getactions'); + $action_found = FALSE; + $our_action = strtolower($match['action']); + foreach ($actions['values'] as $known_action) { + if (strtolower($known_action) == $our_action) { + $action_found = TRUE; + break; + } + } + if (!$action_found) { + return E::ts("API3 action '%1' not found in entity '%2'", [1 => $match['entity'], 2 => $match['action']]); + } + } catch (Exception $ex) { + return E::ts("API3 entity '%1' not found", [1 => $match['entity']]); + } + } else { + return E::ts("API3 action should be defined as 'entity.action'"); + } + break; + + + case self::OPERATOR_STATIC: + if (preg_match(self::VALUE_STATIC_FUNCTION, $token_definition['val'], $match)) { + if (!class_exists($match['class'])) { + return E::ts("Class '%1' not found", [1 => $match['class']]); + } + if (!function_exists($match['class'])) { + return E::ts("Function '%1' not found", [1 => $token_definition['val']]); + } + } else { + return E::ts("Function definition should be 'SomeClass::someFunction'"); + } + break; + + + case self::OPERATOR_REPLACE: + try { + preg_replace(self::REGEX_DELIMITER . $token_definition['def'] . self::REGEX_DELIMITER, $token_definition['val'], 'doesntmatter'); + } catch (Exception $ex) { + return E::ts("Ill-defined replace expression"); + } + break; + + default: + return E::ts("Unknown value type/operator '%1'", [1 => $token_definition['op']]); + } + } +} \ No newline at end of file