Projet

Général

Profil

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

root / drupal7 / sites / all / modules / rules / modules / rules_core.eval.inc @ 950416da

1
<?php
2

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

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

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

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

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

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

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

    
60
/**
61
 * A class implementing a rules input evaluator processing date input.
62
 *
63
 * This is needed to treat relative date inputs for strtotime() correctly.
64
 * Consider for example "now".
65
 */
66
class RulesDateInputEvaluator extends RulesDataInputEvaluator {
67

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

    
70
  /**
71
   * Overrides RulesDataInputEvaluator::prepare().
72
   */
73
  public function prepare($text, $var_info) {
74
    if (is_numeric($text)) {
75
      // Let rules skip this input evaluators in case it's already a timestamp.
76
      $this->setting = NULL;
77
    }
78
  }
79

    
80
  /**
81
   * Overrides RulesDataInputEvaluator::evaluate().
82
   */
83
  public function evaluate($text, $options, RulesState $state) {
84
    return self::gmstrtotime($text);
85
  }
86

    
87
  /**
88
   * Convert a time string to a GMT (UTC) unix timestamp.
89
   */
90
  public static function gmstrtotime($date) {
91
    // Pass the current timestamp in UTC to ensure the retrieved time is UTC.
92
    return strtotime($date, time());
93
  }
94

    
95
  /**
96
   * Determine whether the given date string specifies a fixed date.
97
   */
98
  public static function isFixedDateString($date) {
99
    return is_string($date) && preg_match(self::DATE_REGEX_LOOSE, $date);
100
  }
101

    
102
}
103

    
104
/**
105
 * A class implementing a rules input evaluator processing URI inputs.
106
 *
107
 * Makes sure URIs are absolute and path aliases get applied.
108
 */
109
class RulesURIInputEvaluator extends RulesDataInputEvaluator {
110

    
111
  /**
112
   * Overrides RulesDataInputEvaluator::prepare().
113
   */
114
  public function prepare($uri, $var_info) {
115
    if (!isset($this->processor) && valid_url($uri, TRUE)) {
116
      // Only process if another evaluator is used or the url is not absolute.
117
      $this->setting = NULL;
118
    }
119
  }
120

    
121
  /**
122
   * Overrides RulesDataInputEvaluator::evaluate().
123
   */
124
  public function evaluate($uri, $options, RulesState $state) {
125
    if (!url_is_external($uri)) {
126
      // Extract the path and build the URL using the url() function, so URL
127
      // aliases are applied and query parameters and fragments get handled.
128
      $url = drupal_parse_url($uri);
129
      $url_options = array('absolute' => TRUE);
130
      $url_options['query'] = $url['query'];
131
      $url_options['fragment'] = $url['fragment'];
132
      return url($url['path'], $url_options);
133
    }
134
    elseif (valid_url($uri)) {
135
      return $uri;
136
    }
137
    throw new RulesEvaluationException('Input evaluation generated an invalid URI.', array(), NULL, RulesLog::WARN);
138
  }
139

    
140
}
141

    
142
/**
143
 * A data processor for applying date offsets.
144
 */
145
class RulesDateOffsetProcessor extends RulesDataProcessor {
146

    
147
  /**
148
   * Overrides RulesDataProcessor::form().
149
   */
150
  protected static function form($settings, $var_info) {
151
    $settings += array('value' => '');
152
    $form = array(
153
      '#type' => 'fieldset',
154
      '#title' => t('Add offset'),
155
      '#collapsible' => TRUE,
156
      '#collapsed' => empty($settings['value']),
157
      '#description' => t('Add an offset to the selected date.'),
158
    );
159
    $form['value'] = array(
160
      '#type' => 'rules_duration',
161
      '#title' => t('Offset'),
162
      '#description' => t('Note that you can also specify negative numbers.'),
163
      '#default_value' => $settings['value'],
164
      '#weight' => 5,
165
    );
166
    return $form;
167
  }
168

    
169
  /**
170
   * Overrides RulesDataProcessor::process().
171
   */
172
  public function process($value, $info, RulesState $state, RulesPlugin $element) {
173
    $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element) : $value;
174
    return RulesDateOffsetProcessor::applyOffset($value, $this->setting['value']);
175
  }
176

    
177
  /**
178
   * Intelligently applies the given date offset in seconds.
179
   *
180
   * Intelligently apply duration values > 1 day, i.e. convert the duration
181
   * to its biggest possible unit (months, days) and apply it to the date with
182
   * the given unit. That's necessary as the number of days in a month
183
   * differs, as well as the number of hours for a day (on DST changes).
184
   */
185
  public static function applyOffset($timestamp, $offset) {
186
    if (abs($offset) >= 86400) {
187

    
188
      // Get the days out of the seconds.
189
      $days = intval($offset / 86400);
190
      $sec = $offset % 86400;
191
      // Get the months out of the number of days.
192
      $months = intval($days / 30);
193
      $days = $days % 30;
194

    
195
      // Apply the offset using the DateTime::modify and convert it back to a
196
      // timestamp.
197
      $date = date_create("@$timestamp");
198
      $date->modify("$months months $days days $sec seconds");
199
      return $date->format('U');
200
    }
201
    else {
202
      return $timestamp + $offset;
203
    }
204
  }
205

    
206
}
207

    
208
/**
209
 * A data processor for applying numerical offsets.
210
 */
211
class RulesNumericOffsetProcessor extends RulesDataProcessor {
212

    
213
  /**
214
   * Overrides RulesDataProcessor::form().
215
   */
216
  protected static function form($settings, $var_info) {
217
    $settings += array('value' => '');
218
    $form = array(
219
      '#type' => 'fieldset',
220
      '#title' => t('Add offset'),
221
      '#collapsible' => TRUE,
222
      '#collapsed' => empty($settings['value']),
223
      '#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.'),
224
    );
225
    $form['value'] = array(
226
      '#type' => 'textfield',
227
      '#title' => t('Offset'),
228
      '#description' => t('Note that you can also specify negative numbers.'),
229
      '#default_value' => $settings['value'],
230
      '#element_validate' => array('rules_ui_element_integer_validate'),
231
      '#weight' => 5,
232
    );
233
    return $form;
234
  }
235

    
236
  /**
237
   * Overrides RulesDataProcessor::process().
238
   */
239
  public function process($value, $info, RulesState $state, RulesPlugin $element) {
240
    $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element) : $value;
241
    return $value + $this->setting['value'];
242
  }
243

    
244
}
245

    
246
/**
247
 * A custom wrapper class for vocabularies.
248
 *
249
 * This class is capable of loading vocabularies by machine name.
250
 */
251
class RulesTaxonomyVocabularyWrapper extends EntityDrupalWrapper {
252

    
253
  /**
254
   * Overridden to support identifying vocabularies by machine names.
255
   */
256
  protected function setEntity($data) {
257
    if (isset($data) && $data !== FALSE && !is_object($data) && !is_numeric($data)) {
258
      // The vocabulary name has been passed.
259
      parent::setEntity(taxonomy_vocabulary_machine_name_load($data));
260
    }
261
    else {
262
      parent::setEntity($data);
263
    }
264
  }
265

    
266
  /**
267
   * Overridden to permit machine names as values.
268
   */
269
  public function validate($value) {
270
    if (isset($value) && is_string($value)) {
271
      return TRUE;
272
    }
273
    return parent::validate($value);
274
  }
275

    
276
}
277

    
278
/**
279
 * @}
280
 */