/** * Maps variables from rules state into the plugin context. * * @param \Drupal\Core\Plugin\ContextAwarePluginInterface $plugin * The plugin that is populated with context values. * @param \Drupal\rules\Engine\RulesStateInterface $state * The Rules state containing available variables. * * @throws \Drupal\rules\Exception\RulesEvaluationException * In case a required context is missing for the plugin. */ protected function mapContext(CoreContextAwarePluginInterface $plugin, RulesStateInterface $state) { $context_definitions = $plugin->getContextDefinitions(); foreach ($context_definitions as $name => $definition) { // Check if a data selector is configured that maps to the state. if (isset($this->configuration['context_mapping'][$name])) { $typed_data = $state->applyDataSelector($this->configuration['context_mapping'][$name]); if ($typed_data->getValue() === NULL && !$definition->isAllowedNull()) { throw new RulesEvaluationException(SafeMarkup::format('The value of data selector @selector is NULL, but the context @name in @plugin requires a value.', ['@selector' => $this->configuration['context_mapping'][$name], '@name' => $name, '@plugin' => $plugin->getPluginId()])); } $plugin->getContext($name)->setContextData($typed_data); } elseif (array_key_exists($name, $this->configuration['context_values'])) { if ($this->configuration['context_values'][$name] === NULL && !$definition->isAllowedNull()) { throw new RulesEvaluationException(SafeMarkup::format('The context value for @name is NULL, but the context @name in @plugin requires a value.', ['@name' => $name, '@plugin' => $plugin->getPluginId()])); } $plugin->getContext($name)->setContextValue($this->configuration['context_values'][$name]); } elseif ($definition->isRequired()) { throw new RulesEvaluationException(SafeMarkup::format('Required context @name is missing for plugin @plugin.', ['@name' => $name, '@plugin' => $plugin->getPluginId()])); } } }
/** * {@inheritdoc} */ public function process($value, RulesStateInterface $rules_state) { $replacements = []; // The Token API requires this metadata object, but it is useless for us // here so we just always pass the same instance and ignore it. $bubbleable_metdata = new BubbleableMetadata(); // We only use the token service to scan for tokens in the text. The // replacements are done by using the data selector logic. foreach ($this->tokenService->scan($value) as $var_name => $tokens) { foreach ($tokens as $token) { // Remove the opening and closing bracket to form a data selector. $data_selector = substr($token, 1, -1); try { $replacement_data = $rules_state->applyDataSelector($data_selector); $replacements[$token] = $replacement_data->getString(); } catch (RulesEvaluationException $exception) { // Data selector is invalid, so try to resolve the token with the // token service. if ($rules_state->hasVariable($var_name)) { $variable = $rules_state->getVariable($var_name); $token_type = $variable->getContextDefinition()->getDataType(); // The Token system does not know about "enity:" data type prefixes, // so we have to remove them. $token_type = str_replace('entity:', '', $token_type); $data = [$token_type => $variable->getContextValue()]; $replacements += $this->tokenService->generate($token_type, $tokens, $data, ['sanitize' => FALSE], $bubbleable_metdata); } else { $replacements += $this->tokenService->generate($var_name, $tokens, [], ['sanitize' => FALSE], $bubbleable_metdata); } // Remove tokens if no replacement value is found. $replacements += array_fill_keys($tokens, ''); } } } // Apply the replacements now. $tokens = array_keys($replacements); $values = array_values($replacements); return str_replace($tokens, $values, $value); }