Projet

Général

Profil

Paste
Télécharger (8,06 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / rules / modules / rules_core.eval.inc @ 76e2e7c3

1
<?php
2

    
3
/**
4
 * @file
5
 * Contains rules core integration needed during evaluation.
6
 *
7
 * @addtogroup rules
8
 * @{
9
 */
10

    
11
/**
12
 * Action and condition callback: Invokes a rules component.
13
 *
14
 * We do not use the execute() method, but handle executing ourself. That way
15
 * we can utilize the existing state for saving passed variables.
16
 */
17
function rules_element_invoke_component($arguments, RulesPlugin $element) {
18
  $info = $element->info();
19
  $state = $arguments['state'];
20
  $wrapped_args = $state->currentArguments;
21

    
22
  if ($component = rules_get_cache('comp_' . $info['#config_name'])) {
23
    $replacements = array('%label' => $component->label(), '@plugin' => $component->plugin());
24
    // Handle recursion prevention.
25
    if ($state->isBlocked($component)) {
26
      return rules_log('Not evaluating @plugin %label to prevent recursion.', $replacements, RulesLog::INFO, $component);
27
    }
28
    $state->block($component);
29
    rules_log('Evaluating @plugin %label.', $replacements, RulesLog::INFO, $component, TRUE);
30
    module_invoke_all('rules_config_execute', $component);
31

    
32
    // Manually create a new evaluation state and evaluate the component.
33
    $args = array_intersect_key($wrapped_args, $component->parameterInfo());
34
    $new_state = $component->setUpState($wrapped_args);
35
    $return = $component->evaluate($new_state);
36

    
37
    // Care for the right return value in case we have to provide vars.
38
    if ($component instanceof RulesActionInterface && !empty($info['provides'])) {
39
      $return = array();
40
      foreach ($info['provides'] as $var => $var_info) {
41
        $return[$var] = $new_state->get($var);
42
      }
43
    }
44

    
45
    // Now merge the info about to be saved variables in the parent state.
46
    $state->mergeSaveVariables($new_state, $component, $element->settings);
47
    $state->unblock($component);
48

    
49
    // Cleanup the state, what saves not mergable variables now.
50
    $new_state->cleanup();
51
    rules_log('Finished evaluation of @plugin %label.', $replacements, RulesLog::INFO, $component, FALSE);
52
    return $return;
53
  }
54
  else {
55
    throw new RulesEvaluationException('Unable to get the component %name', array('%name' => $info['#config_name']), $element, RulesLog::ERROR);
56
  }
57
}
58

    
59
/**
60
 * A class implementing a rules input evaluator processing date input. This is
61
 * needed to treat relative date inputs for strtotime right, consider "now".
62
 */
63
class RulesDateInputEvaluator extends RulesDataInputEvaluator {
64

    
65
  const DATE_REGEX_LOOSE = '/^(\d{4})-?(\d{2})-?(\d{2})([T\s]?(\d{2}):?(\d{2}):?(\d{2})?)?$/';
66

    
67
  public function prepare($text, $var_info) {
68
    if (is_numeric($text)) {
69
      // Let rules skip this input evaluators in case it's already a timestamp.
70
      $this->setting = NULL;
71
    }
72
  }
73

    
74
  public function evaluate($text, $options, RulesState $state) {
75
    return self::gmstrtotime($text);
76
  }
77

    
78
  /**
79
   * Convert a time string to a GMT (UTC) unix timestamp.
80
   */
81
  public static function gmstrtotime($date) {
82
    // Pass the current timestamp in UTC to ensure the retrieved time is UTC.
83
    return strtotime($date, time());
84
  }
85

    
86
  /**
87
   * Determine whether the given date string specifies a fixed date.
88
   */
89
  public static function isFixedDateString($date) {
90
    return is_string($date) && preg_match(self::DATE_REGEX_LOOSE, $date);
91
  }
92
}
93

    
94
/**
95
 * A class implementing a rules input evaluator processing URI inputs to make
96
 * sure URIs are absolute and path aliases get applied.
97
 */
98
class RulesURIInputEvaluator extends RulesDataInputEvaluator {
99

    
100
  public function prepare($uri, $var_info) {
101
    if (!isset($this->processor) && valid_url($uri, TRUE)) {
102
      // Only process if another evaluator is used or the url is not absolute.
103
      $this->setting = NULL;
104
    }
105
  }
106

    
107
  public function evaluate($uri, $options, RulesState $state) {
108
    if (!url_is_external($uri)) {
109
      // Extract the path and build the URL using the url() function, so URL
110
      // aliases are applied and query parameters and fragments get handled.
111
      $url = drupal_parse_url($uri);
112
      $url_options = array('absolute' => TRUE);
113
      $url_options['query'] = $url['query'];
114
      $url_options['fragment'] = $url['fragment'];
115
      return url($url['path'], $url_options);
116
    }
117
    elseif (valid_url($uri)) {
118
      return $uri;
119
    }
120
    throw new RulesEvaluationException('Input evaluation generated an invalid URI.', array(), NULL, RulesLog::WARN);
121
  }
122
}
123

    
124
/**
125
 * A data processor for applying date offsets.
126
 */
127
class RulesDateOffsetProcessor extends RulesDataProcessor {
128

    
129
  protected static function form($settings, $var_info) {
130
    $settings += array('value' => '');
131
    $form = array(
132
      '#type' => 'fieldset',
133
      '#title' => t('Add offset'),
134
      '#collapsible' => TRUE,
135
      '#collapsed' => empty($settings['value']),
136
      '#description' => t('Add an offset to the selected date.'),
137
    );
138
    $form['value'] = array(
139
      '#type' => 'rules_duration',
140
      '#title' => t('Offset'),
141
      '#description' => t('Note that you can also specify negative numbers.'),
142
      '#default_value' => $settings['value'],
143
      '#weight' => 5,
144
    );
145
    return $form;
146
  }
147

    
148
  public function process($value, $info, RulesState $state, RulesPlugin $element) {
149
    $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element) : $value;
150
    return RulesDateOffsetProcessor::applyOffset($value, $this->setting['value']);
151
  }
152

    
153
  /**
154
   * Intelligently applies the given date offset in seconds.
155
   *
156
   * Intelligently apply duration values > 1 day, i.e. convert the duration
157
   * to its biggest possible unit (months, days) and apply it to the date with
158
   * the given unit. That's necessary as the number of days in a month
159
   * differs, as well as the number of hours for a day (on DST changes).
160
   */
161
  public static function applyOffset($timestamp, $offset) {
162
    if (abs($offset) >= 86400) {
163

    
164
      // Get the days out of the seconds.
165
      $days = intval($offset / 86400);
166
      $sec = $offset % 86400;
167
      // Get the months out of the number of days.
168
      $months = intval($days / 30);
169
      $days = $days % 30;
170

    
171
      // Apply the offset using the DateTime::modify and convert it back to a
172
      // timestamp.
173
      $date = date_create("@$timestamp");
174
      $date->modify("$months months $days days $sec seconds");
175
      return $date->format('U');
176
    }
177
    else {
178
      return $timestamp + $offset;
179
    }
180
  }
181
}
182

    
183
/**
184
 * A data processor for applying numerical offsets.
185
 */
186
class RulesNumericOffsetProcessor extends RulesDataProcessor {
187

    
188
  protected static function form($settings, $var_info) {
189
    $settings += array('value' => '');
190
    $form = array(
191
      '#type' => 'fieldset',
192
      '#title' => t('Add offset'),
193
      '#collapsible' => TRUE,
194
      '#collapsed' => empty($settings['value']),
195
      '#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.'),
196
    );
197
    $form['value'] = array(
198
      '#type' => 'textfield',
199
      '#title' => t('Offset'),
200
      '#description' => t('Note that you can also specify negative numbers.'),
201
      '#default_value' => $settings['value'],
202
      '#element_validate' => array('rules_ui_element_integer_validate'),
203
      '#weight' => 5,
204
    );
205
    return $form;
206
  }
207

    
208
  public function process($value, $info, RulesState $state, RulesPlugin $element) {
209
    $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element) : $value;
210
    return $value + $this->setting['value'];
211
  }
212
}
213

    
214

    
215
/**
216
 * A custom wrapper class for vocabularies that is capable of loading vocabularies by machine name.
217
 */
218
class RulesTaxonomyVocabularyWrapper extends EntityDrupalWrapper {
219

    
220
  /**
221
   * Overridden to support identifying vocabularies by machine names.
222
   */
223
  protected function setEntity($data) {
224
    if (isset($data) && $data !== FALSE && !is_object($data) && !is_numeric($data)) {
225
      // The vocabulary name has been passed.
226
      parent::setEntity(taxonomy_vocabulary_machine_name_load($data));
227
    }
228
    else {
229
      parent::setEntity($data);
230
    }
231
  }
232

    
233
  /**
234
   * Overriden to permit machine names as values.
235
   */
236
  public function validate($value) {
237
    if (isset($value) && is_string($value)) {
238
      return TRUE;
239
    }
240
    return parent::validate($value);
241
  }
242
}