123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- <?php
- /**
- * @file
- * Contains rules core integration needed during evaluation.
- *
- * @addtogroup rules
- * @{
- */
- /**
- * Action and condition callback: Invokes a rules component.
- *
- * We do not use the execute() method, but handle executing ourself. That way
- * we can utilize the existing state for saving passed variables.
- */
- function rules_element_invoke_component($arguments, RulesPlugin $element) {
- $info = $element->info();
- $state = $arguments['state'];
- $wrapped_args = $state->currentArguments;
- if ($component = rules_get_cache('comp_' . $info['#config_name'])) {
- $replacements = array('%label' => $component->label(), '@plugin' => $component->plugin());
- // Handle recursion prevention.
- if ($state->isBlocked($component)) {
- return rules_log('Not evaluating @plugin %label to prevent recursion.', $replacements, RulesLog::INFO, $component);
- }
- $state->block($component);
- rules_log('Evaluating @plugin %label.', $replacements, RulesLog::INFO, $component, TRUE);
- module_invoke_all('rules_config_execute', $component);
- // Manually create a new evaluation state and evaluate the component.
- $args = array_intersect_key($wrapped_args, $component->parameterInfo());
- $new_state = $component->setUpState($wrapped_args);
- $return = $component->evaluate($new_state);
- // Care for the right return value in case we have to provide vars.
- if ($component instanceof RulesActionInterface && !empty($info['provides'])) {
- $return = array();
- foreach ($info['provides'] as $var => $var_info) {
- $return[$var] = $new_state->get($var);
- }
- }
- // Now merge the info about to be saved variables in the parent state.
- $state->mergeSaveVariables($new_state, $component, $element->settings);
- $state->unblock($component);
- // Cleanup the state, what saves not mergable variables now.
- $new_state->cleanup();
- rules_log('Finished evaluation of @plugin %label.', $replacements, RulesLog::INFO, $component, FALSE);
- return $return;
- }
- else {
- throw new RulesEvaluationException('Unable to get the component %name', array('%name' => $info['#config_name']), $element, RulesLog::ERROR);
- }
- }
- /**
- * A class implementing a rules input evaluator processing date input. This is
- * needed to treat relative date inputs for strtotime right, consider "now".
- */
- class RulesDateInputEvaluator extends RulesDataInputEvaluator {
- const DATE_REGEX_LOOSE = '/^(\d{4})-?(\d{2})-?(\d{2})([T\s]?(\d{2}):?(\d{2}):?(\d{2})?)?$/';
- public function prepare($text, $var_info) {
- if (is_numeric($text)) {
- // Let rules skip this input evaluators in case it's already a timestamp.
- $this->setting = NULL;
- }
- }
- public function evaluate($text, $options, RulesState $state) {
- return self::gmstrtotime($text);
- }
- /**
- * Convert a time string to a GMT (UTC) unix timestamp.
- */
- public static function gmstrtotime($date) {
- // Pass the current timestamp in UTC to ensure the retrieved time is UTC.
- return strtotime($date, time());
- }
- /**
- * Determine whether the given date string specifies a fixed date.
- */
- public static function isFixedDateString($date) {
- return is_string($date) && preg_match(self::DATE_REGEX_LOOSE, $date);
- }
- }
- /**
- * A class implementing a rules input evaluator processing URI inputs to make
- * sure URIs are absolute and path aliases get applied.
- */
- class RulesURIInputEvaluator extends RulesDataInputEvaluator {
- public function prepare($uri, $var_info) {
- if (!isset($this->processor) && valid_url($uri, TRUE)) {
- // Only process if another evaluator is used or the url is not absolute.
- $this->setting = NULL;
- }
- }
- public function evaluate($uri, $options, RulesState $state) {
- if (!url_is_external($uri)) {
- // Extract the path and build the URL using the url() function, so URL
- // aliases are applied and query parameters and fragments get handled.
- $url = drupal_parse_url($uri);
- $url_options = array('absolute' => TRUE);
- $url_options['query'] = $url['query'];
- $url_options['fragment'] = $url['fragment'];
- return url($url['path'], $url_options);
- }
- elseif (valid_url($uri)) {
- return $uri;
- }
- throw new RulesEvaluationException('Input evaluation generated an invalid URI.', array(), NULL, RulesLog::WARN);
- }
- }
- /**
- * A data processor for applying date offsets.
- */
- class RulesDateOffsetProcessor extends RulesDataProcessor {
- protected static function form($settings, $var_info) {
- $settings += array('value' => '');
- $form = array(
- '#type' => 'fieldset',
- '#title' => t('Add offset'),
- '#collapsible' => TRUE,
- '#collapsed' => empty($settings['value']),
- '#description' => t('Add an offset to the selected date.'),
- );
- $form['value'] = array(
- '#type' => 'rules_duration',
- '#title' => t('Offset'),
- '#description' => t('Note that you can also specify negative numbers.'),
- '#default_value' => $settings['value'],
- '#weight' => 5,
- );
- return $form;
- }
- public function process($value, $info, RulesState $state, RulesPlugin $element) {
- $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element) : $value;
- return RulesDateOffsetProcessor::applyOffset($value, $this->setting['value']);
- }
- /**
- * Intelligently applies the given date offset in seconds.
- *
- * Intelligently apply duration values > 1 day, i.e. convert the duration
- * to its biggest possible unit (months, days) and apply it to the date with
- * the given unit. That's necessary as the number of days in a month
- * differs, as well as the number of hours for a day (on DST changes).
- */
- public static function applyOffset($timestamp, $offset) {
- if (abs($offset) >= 86400) {
- // Get the days out of the seconds.
- $days = intval($offset / 86400);
- $sec = $offset % 86400;
- // Get the months out of the number of days.
- $months = intval($days / 30);
- $days = $days % 30;
- // Apply the offset using the DateTime::modify and convert it back to a
- // timestamp.
- $date = date_create("@$timestamp");
- $date->modify("$months months $days days $sec seconds");
- return $date->format('U');
- }
- else {
- return $timestamp + $offset;
- }
- }
- }
- /**
- * A data processor for applying numerical offsets.
- */
- class RulesNumericOffsetProcessor extends RulesDataProcessor {
- protected static function form($settings, $var_info) {
- $settings += array('value' => '');
- $form = array(
- '#type' => 'fieldset',
- '#title' => t('Add offset'),
- '#collapsible' => TRUE,
- '#collapsed' => empty($settings['value']),
- '#description' => t('Add an offset to the selected number. E.g. an offset of "1" adds 1 to the number before it is passed on as argument.'),
- );
- $form['value'] = array(
- '#type' => 'textfield',
- '#title' => t('Offset'),
- '#description' => t('Note that you can also specify negative numbers.'),
- '#default_value' => $settings['value'],
- '#element_validate' => array('rules_ui_element_integer_validate'),
- '#weight' => 5,
- );
- return $form;
- }
- public function process($value, $info, RulesState $state, RulesPlugin $element) {
- $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element) : $value;
- return $value + $this->setting['value'];
- }
- }
- /**
- * A custom wrapper class for vocabularies that is capable of loading vocabularies by machine name.
- */
- class RulesTaxonomyVocabularyWrapper extends EntityDrupalWrapper {
- /**
- * Overridden to support identifying vocabularies by machine names.
- */
- protected function setEntity($data) {
- if (isset($data) && $data !== FALSE && !is_object($data) && !is_numeric($data)) {
- // The vocabulary name has been passed.
- parent::setEntity(taxonomy_vocabulary_machine_name_load($data));
- }
- else {
- parent::setEntity($data);
- }
- }
- /**
- * Overriden to permit machine names as values.
- */
- public function validate($value) {
- if (isset($value) && is_string($value)) {
- return TRUE;
- }
- return parent::validate($value);
- }
- }
|