Projet

Général

Profil

Paste
Télécharger (39 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / webform_validation / webform_validation.validators.inc @ a2bb1a14

1 85ad3d82 Assos Assos
<?php
2
3
/**
4
 * @file
5
 * Provides validation functionality and hooks
6
 */
7
8
/**
9
 * Implements hook_webform_validation_validators().
10
 *
11
 * This function returns an array of validators, in the validator key => options array form.
12
 * Possible options:
13
 * - name (required): name of the validator
14
 * - component types (required): defines which component types can be validated by this validator. Specify 'all' to allow all types
15
 * - negatable (optional): define whether the rule can be negated, meaning it will validate the inverse of the rule.
16
 * - custom_error (optional): define whether a user can specify a custom error message upon creating the validation rule.
17
 * - custom_data (optional): define whether custom data can be added to the validation rule
18
 * - min_components (optional): define the minimum number of components to be selected for creating a validation rule
19
 * - max_components (optional): define the maximum number of components to be selected for creating a validation rule
20
 * - description (optional): provide a descriptive explanation about the validator
21
 */
22
function webform_validation_webform_validation_validators() {
23
  $validators = array(
24
    'numeric' => array(
25
      'name' => t('Numeric values'),
26
      'component_types' => array(
27 5c451ca3 Assos Assos
        'hidden',
28 85ad3d82 Assos Assos
        'number',
29
        'textfield',
30
      ),
31
      'custom_data' => array(
32
        'label' => t('Specify numeric validation range'),
33
        'description' => t('Optionally specify the minimum-maximum range to validate the user-entered numeric value against.') . ' ' . t('Usage') . ':'
34 a2baadd1 Assos Assos
          . theme('item_list', array('items' => array(t('empty: no value validation'), t('"100": greater than or equal to 100'), t('"|100": less than or equal to 100 (including negative numbers)'), t('"0|100": greater than or equal to 0 &amp; less than or equal to 100'), t('"10|100": greater than or equal to 10 &amp; less than or equal to 100'), t('"-100|-10": greater than or equal to -100 &amp; less than or equal to -10')))),
35 85ad3d82 Assos Assos
        'required' => FALSE,
36
      ),
37
      'description' => t('Verifies that user-entered values are numeric, with the option to specify min and / or max values.'),
38
    ),
39
    'min_length' => array(
40
      'name' => t('Minimum length'),
41
      'component_types' => array(
42
        'email',
43
        'hidden',
44 5c451ca3 Assos Assos
        'number',
45
        'textarea',
46
        'textfield',
47 85ad3d82 Assos Assos
      ),
48
      'custom_data' => array(
49
        'label' => t('Minimum number of characters'),
50
        'description' => t('Specify the minimum number of characters that have to be entered to pass validation.'),
51
      ),
52 a2baadd1 Assos Assos
      'description' => t('Verifies that a user-entered value contains at least the specified number of characters.'),
53 85ad3d82 Assos Assos
    ),
54
    'max_length' => array(
55
      'name' => t('Maximum length'),
56
      'component_types' => array(
57
        'email',
58
        'hidden',
59 5c451ca3 Assos Assos
        'number',
60
        'textarea',
61
        'textfield',
62 85ad3d82 Assos Assos
      ),
63
      'custom_data' => array(
64
        'label' => t('Maximum number of characters'),
65
        'description' => t('Specify the maximum number of characters that can be entered to pass validation.'),
66
      ),
67 a2baadd1 Assos Assos
      'description' => t('Verifies that a user-entered value contains at most the specified number of characters.'),
68 85ad3d82 Assos Assos
    ),
69
    'min_words' => array(
70
      'name' => t('Minimum number of words'),
71
      'component_types' => array(
72
        'hidden',
73
        'html_textarea',
74 5c451ca3 Assos Assos
        'textarea',
75
        'textfield',
76 85ad3d82 Assos Assos
      ),
77
      'custom_data' => array(
78
        'label' => t('Minimum number of words'),
79 9df8b457 Assos Assos
        'description' => t('Specify the minimum number of words that have to be entered to pass validation. Words are defined as strings of letters separated by spaces.'),
80 85ad3d82 Assos Assos
      ),
81
      'description' => t('Verifies that a user-entered value contains at least the specified number of words.'),
82
    ),
83
    'max_words' => array(
84
      'name' => t('Maximum number of words'),
85
      'component_types' => array(
86
        'hidden',
87
        'html_textarea',
88 5c451ca3 Assos Assos
        'textarea',
89
        'textfield',
90 85ad3d82 Assos Assos
      ),
91
      'custom_data' => array(
92
        'label' => t('Maximum number of words'),
93 9df8b457 Assos Assos
        'description' => t('Specify the maximum number of words that have to be entered to pass validation. Words are defined as strings of letters separated by spaces.'),
94 85ad3d82 Assos Assos
      ),
95
      'description' => t('Verifies that a user-entered value contains at most the specified number of words.'),
96
    ),
97 bb746689 Assos Assos
    // Only available in Webform 4; removed below if not.
98 5c451ca3 Assos Assos
    'sum' => array(
99
      'name' => t('Adds up to'),
100
      'component_types' => array(
101
        'number',
102
      ),
103
      'custom_data' => array(
104
        'label' => t('Number the fields add up to'),
105
        'description' => t('Specify the number and the type of comparison. For example:') . theme('item_list', array('items' => array(
106
          t('Enter "=3" if the components must add up to exactly 3.'),
107
          t('Enter ">10" if the components must add up to greater than 10.'),
108
          t('Enter ">=10" if the components must add up to greater than or equal to 10.'),
109
          t('Enter "<20" if the components must add up to less than 20.'),
110
          t('Enter "<=20" if the components must add up to less than or equal to 20.'),
111
        ))),
112
      ),
113
      'description' => t('Require the values of the selected fields to add up to exactly, greater than or equal to, or less than or equal to a specified number.'),
114
    ),
115 85ad3d82 Assos Assos
    'equal' => array(
116
      'name' => t('Equal values'),
117
      'component_types' => array(
118 5c451ca3 Assos Assos
        'date',
119 85ad3d82 Assos Assos
        'email',
120
        'hidden',
121 5c451ca3 Assos Assos
        'number',
122
        'select',
123
        'textarea',
124
        'textfield',
125
        'time',
126 9df8b457 Assos Assos
        'boolean',
127 85ad3d82 Assos Assos
      ),
128
      'min_components' => 2,
129 5c451ca3 Assos Assos
      'description' => t('Verifies that all specified components contain equal values. If all components are of type email, they will get case-insensitive comparison.'),
130 85ad3d82 Assos Assos
    ),
131
    'comparison' => array(
132
      'name' => t('Compare two values'),
133
      'component_types' => array(
134
        'date',
135
        'email',
136
        'hidden',
137
        'number',
138
        'select',
139 5c451ca3 Assos Assos
        'textarea',
140
        'textfield',
141 85ad3d82 Assos Assos
        'time',
142
      ),
143
      'custom_error' => TRUE,
144
      'custom_data' => array(
145
        'label' => t('Comparison operator'),
146 5c451ca3 Assos Assos
        'description' => t('Specify the comparison operator you want to use. Must be one of: >, >=, <, <=. The validator will compare the first component with the second using this operator. If the components are of type email, they will get case-insensitive comparison.'),
147 85ad3d82 Assos Assos
      ),
148
      'min_components' => 2,
149
      'max_components' => 2,
150 e4c061ad Assos Assos
      'description' => t('Compare two values for greater than (>), less than (<), greater than or equal to (>=), or less than or equal to (<=).'),
151 85ad3d82 Assos Assos
    ),
152
    'unique' => array(
153
      'name' => t('Unique values'),
154
      'component_types' => array(
155 5c451ca3 Assos Assos
        'date',
156 85ad3d82 Assos Assos
        'email',
157
        'hidden',
158 5c451ca3 Assos Assos
        'number',
159
        'select',
160
        'textarea',
161
        'textfield',
162
        'time',
163 9df8b457 Assos Assos
        'boolean',
164 85ad3d82 Assos Assos
      ),
165
      'min_components' => 2,
166 5c451ca3 Assos Assos
      'description' => t('Verifies that none of the specified components contain the same value as another selected component in this submission. (To check that values are unique between submissions, use the unique validation option on the "Edit component" page for that component.) If all components are of type email, they will get case-insensitive comparison.'),
167 85ad3d82 Assos Assos
    ),
168
    'specific_value' => array(
169
      'name' => t('Specific value(s)'),
170
      'negatable' => TRUE,
171
      'component_types' => array(
172 5c451ca3 Assos Assos
        'email',
173
        'hidden',
174 85ad3d82 Assos Assos
        'number',
175
        'select',
176
        'textarea',
177 5c451ca3 Assos Assos
        'textfield',
178 9df8b457 Assos Assos
        'boolean',
179 85ad3d82 Assos Assos
      ),
180
      'custom_error' => TRUE,
181
      'custom_data' => array(
182
        'label' => t('(Key) value'),
183
        'description' => t('Specify the specific value(s) you want the component to contain. Separate multiple options by a comma. For components that have keys, use the key value instead.'),
184
      ),
185
      'max_components' => 1,
186
      'description' => t('Verifies that the value of the specified component is from a list of allowed values.'),
187
    ),
188
    'default_value' => array(
189
      'name' => t('Default value'),
190
      'negatable' => TRUE,
191
      'component_types' => array(
192
        'email',
193
        'hidden',
194 5c451ca3 Assos Assos
        'number',
195
        'select',
196
        'textarea',
197
        'textfield',
198 9df8b457 Assos Assos
        'boolean',
199 85ad3d82 Assos Assos
      ),
200
      'custom_error' => TRUE,
201
      'description' => t('Verifies that the user-entered value is the default value for that component. Negate if you want not the default value.'),
202
    ),
203 a2baadd1 Assos Assos
    'someofseveral' => array(
204 5c451ca3 Assos Assos
      'name' => t('Some of several'),
205 85ad3d82 Assos Assos
      'component_types' => array(
206 5c451ca3 Assos Assos
        'date',
207 85ad3d82 Assos Assos
        'email',
208 5c451ca3 Assos Assos
        'file',
209
        'number',
210 85ad3d82 Assos Assos
        'select',
211 5c451ca3 Assos Assos
        'textarea',
212
        'textfield',
213 9df8b457 Assos Assos
        'time',
214
        'boolean',
215 85ad3d82 Assos Assos
      ),
216 a2baadd1 Assos Assos
      'custom_data' => array(
217 5c451ca3 Assos Assos
        'label' => t('Number to be completed'),
218
        'description' => t('Specify the number that must be completed and the type of comparison. For example:') . theme('item_list', array('items' => array(
219
          t('Enter ">=1" if the user must complete <b>at least</b> 1 of the selected components.'),
220
          t('Enter "=3" if the user must complete <b>exactly</b> 3 of the selected components.'),
221
          t('Enter "<=2" if the user must complete <b>at most</b> 2 of the selected components.'),
222
        ))),
223 85ad3d82 Assos Assos
      ),
224
      'min_components' => 2,
225 5c451ca3 Assos Assos
      'description' => t('Requires the user to complete some number of components out of a group of components. For example, complete at least 2 out of 3, complete at most 4 out of 6, or complete exactly 3 our of 4.'),
226 85ad3d82 Assos Assos
    ),
227
    'select_min' => array(
228
      'name' => t('Minimum number of selections required'),
229
      'component_types' => array(
230
        'select',
231
      ),
232
      'custom_data' => array(
233
        'label' => t('Minimum number of selections'),
234
        'description' => t('Specify the minimum number of options a user should select.'),
235
      ),
236
      'description' => t('Forces the user to select at least a defined number of options from the specified webform components.'),
237
    ),
238
    'select_max' => array(
239
      'name' => t('Maximum number of selections allowed'),
240
      'component_types' => array(
241
        'select',
242
      ),
243
      'custom_data' => array(
244
        'label' => t('Maximum number of selections'),
245
        'description' => t('Specify the maximum number of options a user can select.'),
246
      ),
247
      'description' => t('Forces the user to select at most a defined number of options from the specified webform components.'),
248
    ),
249
    'select_exact' => array(
250
      'name' => t('Exact number of selections required'),
251
      'negatable' => TRUE,
252
      'component_types' => array(
253
        'select',
254
      ),
255
      'custom_data' => array(
256
        'label' => t('Number of selections'),
257
        'description' => t('Specify how many options a user can select.'),
258
      ),
259
      'description' => t('Forces the user to select exactly the defined number of options from the specified webform components.'),
260
    ),
261
    'plain_text' => array(
262
      'name' => t('Plain text (disallow tags)'),
263
      'negatable' => TRUE,
264
      'component_types' => array(
265
        'email',
266
        'hidden',
267 5c451ca3 Assos Assos
        'textarea',
268
        'textfield',
269 85ad3d82 Assos Assos
      ),
270
      'description' => t('Verifies that user-entered data doesn\'t contain HTML tags.'),
271
    ),
272
    'starts_with' => array(
273
      'name' => t('Starts with'),
274
      'negatable' => TRUE,
275
      'component_types' => array(
276
        'email',
277
        'hidden',
278 5c451ca3 Assos Assos
        'number',
279
        'textarea',
280
        'textfield',
281 85ad3d82 Assos Assos
      ),
282
      'custom_data' => array(
283
        'label' => t('Starts with'),
284
        'description' => t('Enter the text that this field must start with.'),
285
      ),
286
      'description' => t('Verifies that user-entered data starts with a given string.'),
287
    ),
288
    'ends_with' => array(
289
      'name' => t('Ends with'),
290
      'negatable' => TRUE,
291
      'component_types' => array(
292
        'email',
293
        'hidden',
294 5c451ca3 Assos Assos
        'number',
295
        'textarea',
296
        'textfield',
297 85ad3d82 Assos Assos
      ),
298
      'custom_data' => array(
299
        'label' => t('Ends with'),
300
        'description' => t('Enter the text that this field must end with.'),
301
      ),
302
      'description' => t('Verifies that user-entered data ends with a given string.'),
303
    ),
304
    'pattern' => array(
305 5c451ca3 Assos Assos
      'name' => t('Pattern'),
306 85ad3d82 Assos Assos
      'component_types' => array(
307
        'hidden',
308 5c451ca3 Assos Assos
        'textarea',
309
        'textfield',
310 85ad3d82 Assos Assos
      ),
311
      'custom_error' => TRUE,
312
      'custom_data' => array(
313
        'label' => t('Pattern'),
314
        'description' => t('Specify a pattern where:')
315 a2baadd1 Assos Assos
          . theme('item_list', array('items' => array(t('@ = any letter A-Z'), t('# = any numeral 0-9'), t('| = separates two or more acceptable patterns'), t('Any other character must appear in its exact position'))))
316
          . t('Examples')
317
          . theme('item_list', array('items' => array(t('North American phone number: (###) ###-####'), t('D-2500 series model numbers: D-25##'), t('UK Postal Code: @# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA')))),
318 85ad3d82 Assos Assos
      ),
319
      'description' => t('Verifies that a user-entered value follows to a specified pattern.'),
320
    ),
321
    'regex' => array(
322
      'name' => t('Regular expression, case-sensitive'),
323
      'negatable' => TRUE,
324
      'component_types' => array(
325
        'email',
326
        'hidden',
327 5c451ca3 Assos Assos
        'number',
328
        'textarea',
329
        'textfield',
330 85ad3d82 Assos Assos
      ),
331
      'custom_error' => TRUE,
332
      'custom_data' => array(
333 4f315dab Assos Assos
        'label' => t('Regex code (case-sensitive)'),
334
        'description' => t('Specify regex code to validate the user input against. Do not include delimiters such as /.'),
335 85ad3d82 Assos Assos
      ),
336 4f315dab Assos Assos
      'description' => t('Validates user-entered text against a case-sensitive regular expression.'),
337 85ad3d82 Assos Assos
    ),
338
    'regexi' => array(
339
      'name' => t('Regular expression, case-insensitive'),
340
      'negatable' => TRUE,
341
      'component_types' => array(
342
        'email',
343
        'hidden',
344 5c451ca3 Assos Assos
        'number',
345
        'textarea',
346
        'textfield',
347 85ad3d82 Assos Assos
      ),
348
      'custom_error' => TRUE,
349
      'custom_data' => array(
350 4f315dab Assos Assos
        'label' => t('Regex code (case-insensitive)'),
351
        'description' => t('Specify regex code to validate the user input against. Do not include delimiters such as /.'),
352 85ad3d82 Assos Assos
      ),
353 4f315dab Assos Assos
      'description' => t('Validates user-entered text against a case-insensitive regular expression.'),
354 85ad3d82 Assos Assos
    ),
355
    'must_be_empty' => array(
356
      'name' => t('Must be empty'),
357
      'component_types' => array(
358 5c451ca3 Assos Assos
        'hidden',
359 85ad3d82 Assos Assos
        'number',
360 5c451ca3 Assos Assos
        'textarea',
361 85ad3d82 Assos Assos
        'textfield',
362
      ),
363
      'description' => t('Verifies that a specified textfield remains empty. Recommended use case: used as an anti-spam measure by hiding the element with CSS.'),
364
    ),
365
    'blacklist' => array(
366
      'name' => t('Words blacklist'),
367
      'negatable' => TRUE,
368
      'component_types' => array(
369
        'email',
370
        'hidden',
371 5c451ca3 Assos Assos
        'textarea',
372
        'textfield',
373 85ad3d82 Assos Assos
      ),
374
      'custom_error' => TRUE,
375
      'custom_data' => array(
376
        'label' => t('Blacklisted words'),
377
        'description' => t('Specify illegal words, seperated by commas.'),
378
      ),
379
      'description' => t('Validates that user-entered data doesn\'t contain any of the specified illegal words.'),
380
    ),
381 9df8b457 Assos Assos
    'username' => array(
382 85ad3d82 Assos Assos
      'name' => t('Must match a username'),
383
      'negatable' => TRUE,
384
      'component_types' => array(
385
        'hidden',
386 5c451ca3 Assos Assos
        'textfield',
387 85ad3d82 Assos Assos
      ),
388
      'description' => t('Validates that user-entered data matches a username.'),
389
    ),
390
    'valid_url' => array(
391 5c451ca3 Assos Assos
      'name' => t('Valid URL'),
392 85ad3d82 Assos Assos
      'negatable' => TRUE,
393
      'component_types' => array(
394
        'hidden',
395 5c451ca3 Assos Assos
        'textfield',
396 85ad3d82 Assos Assos
      ),
397
      'description' => t('Validates that the user-entered data is a valid URL.'),
398
    ),
399
  );
400
401 bb746689 Assos Assos
  // Only available in Webform 4.
402
  module_load_include('inc', 'webform', 'components/number');
403
  if (!function_exists('webform_compare_floats')) {
404
    unset($validators['sum']);
405
  }
406
407 85ad3d82 Assos Assos
  if (module_exists('email_verify')) {
408
    $validators['email_verify'] = array(
409 5c451ca3 Assos Assos
      'name' => t('Email Verify'),
410 85ad3d82 Assos Assos
      'component_types' => array(
411
        'email',
412
      ),
413
      'description' => t('Verifies that an email address actually exists using the <a href="https://drupal.org/project/email_verify">Email Verification module</a>.'),
414
    );
415
  }
416
417
  return $validators;
418
}
419
420
/**
421
 * Implements hook_webform_validation_validate().
422
 */
423
function webform_validation_webform_validation_validate($validator_name, $items, $components, $rule) {
424 5c451ca3 Assos Assos
  if (!$items) {
425
    return NULL;
426
  }
427
428
  // For certain validators, if all components to be compared are email
429
  // components, make the values lower-case to avoid case-sensitive comparison.
430
  if (in_array($validator_name, array('equal', 'comparison', 'unique'))) {
431
    $all_email = TRUE;
432
    foreach ($rule['components'] as $component) {
433
      if ($component['type'] !== 'email') {
434
        $all_email = FALSE;
435
        break;
436 a2baadd1 Assos Assos
      }
437
    }
438 5c451ca3 Assos Assos
    if ($all_email) {
439
      $items = array_map('strtolower', $items);
440
    }
441
  }
442 a2baadd1 Assos Assos
443 5c451ca3 Assos Assos
  // Some components, such as multiple select, send their values as arrays, others don't.
444
  // Convert all to arrays and write the rules to handle them that way. Remove empty values.
445
  foreach ($items as $key => $val) {
446
    $new_values = array();
447
    foreach ((array) $val as $val_key => $val_val) {
448
      if ((string) $val_val !== '') {
449
        $new_values[$val_key] = $val_val;
450
      }
451
    }
452
    $items[$key] = $new_values;
453
  }
454 e4c061ad Assos Assos
  // Ensure later calls to key() get the first item.
455
  reset($items);
456 85ad3d82 Assos Assos
457 5c451ca3 Assos Assos
  $errors = array();
458
  switch ($validator_name) {
459
    case 'numeric':
460
      $num_range = _webform_numeric_check_data($rule['data']);
461
      foreach ($items as $key => $val) {
462
        foreach ($val as $subval) {
463
          // first check if the value is numeric
464
          if (is_numeric($subval)) {
465
            $subval = (float) $subval;
466
          }
467
          else {
468
            $errors[$key] = t('%item is not numeric.', array('%item' => $components[$key]['name']));
469
            continue;
470
          }
471
472
          // now validate the entered numeric value against the validator range settings, if appropriate
473
          // a. validate min & max
474
          if (isset($num_range['min']) && isset($num_range['max'])) {
475
            // validate the min - max range
476
            if ($subval < $num_range['min'] || $subval > $num_range['max']) {
477
              $errors[$key] = t('%item is not within the allowed range %range.', array('%item' => $components[$key]['name'], '%range' => str_replace('|' , ' - ', $rule['data'])));
478 85ad3d82 Assos Assos
            }
479 5c451ca3 Assos Assos
          }
480
          else {
481
            // b. validate min
482
            if (isset($num_range['min'])) {
483
              if ($subval < $num_range['min']) {
484
                $errors[$key] = t('%item should be greater than or equal to %val.', array('%item' => $components[$key]['name'], '%val' => $num_range['min']));
485 85ad3d82 Assos Assos
              }
486 5c451ca3 Assos Assos
            }
487
            // c. validate max
488
            if (isset($num_range['max'])) {
489
              if ($subval > $num_range['max']) {
490
                $errors[$key] = t('%item should be less than or equal to %val.', array('%item' => $components[$key]['name'], '%val' => $num_range['max']));
491 85ad3d82 Assos Assos
              }
492
            }
493
          }
494
        }
495 5c451ca3 Assos Assos
      }
496
      return $errors;
497
    case 'min_length':
498
      $min_length = $rule['data'];
499
      foreach ($items as $key => $val) {
500
        foreach ($val as $subval) {
501
          if (drupal_strlen($subval) < $min_length) {
502
            $errors[$key] = t('%item should be at least %num characters long.', array('%item' => $components[$key]['name'], '%num' => $min_length));
503 85ad3d82 Assos Assos
          }
504
        }
505 5c451ca3 Assos Assos
      }
506
      return $errors;
507
    case 'max_length':
508
      $max_length = $rule['data'];
509
      foreach ($items as $key => $val) {
510
        foreach ($val as $subval) {
511
          if (drupal_strlen($subval) > $max_length) {
512
            $errors[$key] = t('%item should be at most %num characters long.', array('%item' => $components[$key]['name'], '%num' => $max_length));
513 85ad3d82 Assos Assos
          }
514
        }
515 5c451ca3 Assos Assos
      }
516
      return $errors;
517
    case 'min_words':
518
      $min_words = $rule['data'];
519
      foreach ($items as $key => $val) {
520
        foreach ($val as $subval) {
521
          if (_webform_validation_count_words($subval) < $min_words) {
522
            $error = format_plural($min_words, '%item should be at least 1 word long.', '%item should be at least @count words long.', array('%item' => $components[$key]['name']));
523
            $errors[$key] = $error;
524 85ad3d82 Assos Assos
          }
525
        }
526 5c451ca3 Assos Assos
      }
527
      return $errors;
528
    case 'max_words':
529
      $max_words = $rule['data'];
530
      foreach ($items as $key => $val) {
531
        foreach ($val as $subval) {
532
          if (_webform_validation_count_words($subval) > $max_words) {
533
            $error = format_plural($max_words, '%item should be at most 1 word long.', '%item should be at most @count words long.', array('%item' => $components[$key]['name']));
534
            $errors[$key] = $error;
535 85ad3d82 Assos Assos
          }
536
        }
537 5c451ca3 Assos Assos
      }
538
      return $errors;
539
    case 'sum':
540
      // Get the numbers to sum.
541
      $sum = array();
542
      foreach ($items as $item) {
543
        if (isset($item[0])) {
544
          $sum[] = $item[0];
545
        }
546
      }
547
      // If none of the components are completed, don't run this validator.
548
      if (!count($sum)) {
549
        return array();
550
      }
551
      $sum = array_sum($sum);
552
553
      // Number being compared with.
554
      $compare_number = (float) preg_replace('/^[^0-9]+/', '', $rule['data']);
555
556
      // Parse the comparision operator and do comparison.
557 bb746689 Assos Assos
      module_load_include('inc', 'webform', 'components/number');
558 5c451ca3 Assos Assos
      $error = FALSE;
559
      if (substr($rule['data'], 0, 2) === '<=') {
560 bb746689 Assos Assos
        if (!(webform_compare_floats($sum, $compare_number) <= 0)) {
561 5c451ca3 Assos Assos
          $error = t('less than or equal to');
562
        }
563
      }
564
      elseif (substr($rule['data'], 0, 1) === '<') {
565 bb746689 Assos Assos
        if (!(webform_compare_floats($sum, $compare_number) < 0)) {
566 5c451ca3 Assos Assos
          $error = t('less than');
567
        }
568
      }
569
      elseif (substr($rule['data'], 0, 2) === '>=') {
570 bb746689 Assos Assos
        if (!(webform_compare_floats($sum, $compare_number) >= 0)) {
571 5c451ca3 Assos Assos
          $error = t('greater than or equal to');
572
        }
573
      }
574
      elseif (substr($rule['data'], 0, 1) === '>') {
575 bb746689 Assos Assos
        if (!(webform_compare_floats($sum, $compare_number) > 0)) {
576 5c451ca3 Assos Assos
          $error = t('greater than');
577
        }
578
      }
579
      else {
580 bb746689 Assos Assos
        if (!(webform_compare_floats($sum, $compare_number) === 0)) {
581 5c451ca3 Assos Assos
          $error = t('exactly');
582
        }
583
      }
584
      // Set error if needed.
585
      if ($error) {
586
        $keys = array_keys($items);
587
        $errors[$keys[0]] = t('These items must add up to %verb %compare_number:', array('%verb' => $error, '%compare_number' => $compare_number)) . theme('item_list', array('items' => _webform_validation_get_names_of_rule_components($rule)));
588
      }
589
      return $errors;
590
    case 'equal':
591
      $first_entry_key = key($items);
592
      $first_entry = array_shift($items);
593
      foreach ($items as $key => $val) {
594
        if ($val !== $first_entry) {
595
          $errors[$key] = t('%item_checked does not match %item_first.', array('%item_checked' => $components[$key]['name'], '%item_first' => $components[$first_entry_key]['name']));
596
        }
597
      }
598
      return $errors;
599
    case 'comparison':
600
      foreach (array(1, 2) as $count) {
601
        $entry[$count]['key'] = key($items);
602
        $value = array_shift($items);
603
        if ($components[$entry[$count]['key']]['type'] === 'date') {
604 1f623f01 Assos Assos
          if (!empty($value) && checkdate((int) $value['month'], (int) $value['day'], (int) $value['year'])) {
605 5c451ca3 Assos Assos
            $entry[$count]['value'] = date('Y-m-d', mktime(0, 0, 0, (int) $value['month'], (int) $value['day'], (int) $value['year']));
606
          }
607
          else {
608
            $entry[$count]['value'] = NULL;
609 85ad3d82 Assos Assos
          }
610
        }
611 5c451ca3 Assos Assos
        elseif ($components[$entry[$count]['key']]['type'] === 'time') {
612
          $time = $value['hour'] . ':' . $value['minute'];
613
          if (!empty($value['ampm'])) {
614
            $time .= ' ' . $value['ampm'];
615 85ad3d82 Assos Assos
          }
616 5c451ca3 Assos Assos
          $time = strtotime($time);
617
          if ($time) {
618
            $entry[$count]['value'] = date('H:i', $time);
619 85ad3d82 Assos Assos
          }
620
          else {
621 5c451ca3 Assos Assos
            $entry[$count]['value'] = NULL;
622 85ad3d82 Assos Assos
          }
623
        }
624 5c451ca3 Assos Assos
        else {
625
          $entry[$count]['value'] = _webform_validation_flatten_array($value);
626 85ad3d82 Assos Assos
        }
627 5c451ca3 Assos Assos
      }
628 85ad3d82 Assos Assos
629 5c451ca3 Assos Assos
      // Don't validate if either are empty.
630
      if ((string) $entry[1]['value'] === '' || (string) $entry[2]['value'] === '') {
631
        return array();
632
      }
633 85ad3d82 Assos Assos
634 5c451ca3 Assos Assos
      $error = FALSE;
635
      switch ($rule['data']) {
636
        case '>':
637
          if (!($entry[1]['value'] > $entry[2]['value'])) {
638
            $error = TRUE;
639 85ad3d82 Assos Assos
          }
640 5c451ca3 Assos Assos
          break;
641
        case '>=':
642
          if (!($entry[1]['value'] >= $entry[2]['value'])) {
643
            $error = TRUE;
644
          }
645
          break;
646
        case '<':
647
          if (!($entry[1]['value'] < $entry[2]['value'])) {
648
            $error = TRUE;
649
          }
650
          break;
651
        case '<=':
652
          if (!($entry[1]['value'] <= $entry[2]['value'])) {
653
            $error = TRUE;
654
          }
655
          break;
656
      }
657
658
      if ($error) {
659
        $errors[$entry[2]['key']] = _webform_validation_i18n_error_message($rule);
660
      }
661
      return $errors;
662
    case 'unique':
663
      foreach ($items as $key => $val) {
664
        $items[$key] = _webform_validation_flatten_array($val);
665
      }
666
      // now we count how many times each value appears, and find out which values appear more than once
667
      $items_count = array_count_values(array_map('drupal_strtolower', array_map('trim', $items)));
668
      $doubles = array_filter($items_count, create_function('$x', 'return $x > 1;'));
669
      foreach ($items as $key => $val) {
670
        if (in_array(drupal_strtolower($val), array_keys($doubles))) {
671
          $errors[$key] = t('The value of %item is not unique.', array('%item' => $components[$key]['name']));
672 85ad3d82 Assos Assos
        }
673 5c451ca3 Assos Assos
      }
674
      return $errors;
675
    case 'specific_value':
676
      $specific_values = explode(',', $rule['data']);
677
      $specific_values = array_map('trim', $specific_values);
678
      foreach ($items as $key => $val) {
679
        // Selects: Fail if at least one checked and at least one not in the allowed values.
680
        $val = array_filter($val);
681
        $test = count($val) && count(array_diff($val, $specific_values));
682
        _webform_validation_test($errors, $key, $rule, $test);
683
      }
684
      return $errors;
685
    case 'default_value':
686
      foreach ($items as $key => $val) {
687
        $val = _webform_validation_flatten_array($val);
688
        $test = $val != $components[$key]['value'];
689
        _webform_validation_test($errors, $key, $rule, $test);
690
      }
691
      return $errors;
692
    case 'someofseveral':
693
      // Determine the number of components completed.
694
      foreach ($items as $key => $val) {
695
        $items[$key] = _webform_validation_flatten_array($val);
696
        $items[$key] = (bool) strlen($items[$key]);
697
      }
698
      $number_completed = count(array_filter($items));
699
700
      // Number being compared with.
701
      $compare_number = (int) preg_replace('/[^0-9]+/', '', $rule['data']);
702
      if ($compare_number < 1) {
703
        $compare_number = 1;
704
      }
705
      elseif ($compare_number > count($rule['components'])) {
706
        $compare_number = count($rule['components']);
707
      }
708
709
      // Parse the comparision operator and do comparison.
710
      $error = FALSE;
711
      if (substr($rule['data'], 0, 1) === '=') {
712
        if (!($number_completed === $compare_number)) {
713
          $error = t('exactly');
714 85ad3d82 Assos Assos
        }
715 5c451ca3 Assos Assos
      }
716
      elseif (substr($rule['data'], 0, 2) === '<=') {
717
        if (!($number_completed <= $compare_number)) {
718
          $error = t('at most');
719 85ad3d82 Assos Assos
        }
720 5c451ca3 Assos Assos
      }
721
      else {
722
        if (!($number_completed >= $compare_number)) {
723
          $error = t('at least');
724 85ad3d82 Assos Assos
        }
725 5c451ca3 Assos Assos
      }
726
727
      // Set error if needed.
728
      if ($error) {
729
        $keys = array_keys($items);
730
        $errors[$keys[0]] = t('You must complete %verb %compare_number of these items:', array('%verb' => $error, '%compare_number' => $compare_number)) . theme('item_list', array('items' => _webform_validation_get_names_of_rule_components($rule)));
731
      }
732
      return $errors;
733
    case 'select_min':
734
      $min_selections = $rule['data'];
735
      foreach ($items as $key => $val) {
736
        if (count(array_filter($val, '_webform_validation_check_false')) < $min_selections) {
737
          $errors[$key] = t('Please select at least %num options for %item.', array('%num' => $min_selections, '%item' => $components[$key]['name']));
738 85ad3d82 Assos Assos
        }
739 5c451ca3 Assos Assos
      }
740
      return $errors;
741
    case 'select_max':
742
      $max_selections = $rule['data'];
743
      foreach ($items as $key => $val) {
744
        if (count(array_filter($val, '_webform_validation_check_false')) > $max_selections) {
745
          $errors[$key] = t('Please select maximum %num options for %item.', array('%num' => $max_selections, '%item' => $components[$key]['name']));
746 85ad3d82 Assos Assos
        }
747 5c451ca3 Assos Assos
      }
748
      return $errors;
749
    case 'select_exact':
750
      $allowed_selections = $rule['data'];
751
      foreach ($items as $key => $val) {
752
        $error_strings = array(
753
          'regular' => 'Please select %num options for %item.',
754
          'negated' => 'Please do not select %num options for %item.',
755
        );
756
        $error_vars = array('%num' => $allowed_selections, '%item' => $components[$key]['name']);
757
        $test = count(array_filter($val, '_webform_validation_check_false')) != $allowed_selections;
758
        _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
759
      }
760
      return $errors;
761
    case 'plain_text':
762
      foreach ($items as $key => $val) {
763
        $error_strings = array(
764
          'regular' => '%item only allows the use of plain text.',
765
          'negated' => '%item must contain HTML tags.',
766
        );
767
        $error_vars = array('%item' => $components[$key]['name']);
768
        foreach ($val as $subval) {
769
          $test = strcmp($subval, strip_tags($subval));
770 85ad3d82 Assos Assos
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
771
        }
772 5c451ca3 Assos Assos
      }
773
      return $errors;
774
    case 'starts_with':
775
    case 'ends_with':
776
      $pattern = preg_quote($rule['data'], '/');
777
      if ($validator_name === 'starts_with') {
778
        $pattern = '^' . $pattern;
779
        $verb = t('start');
780
      }
781
      else {
782
        $pattern .= '$';
783
        $verb = t('end');
784
      }
785
      $pattern = '/' . $pattern . '/';
786
787
      foreach ($items as $key => $val) {
788
        $error_strings = array(
789
          'regular' => '%item must %verb with "%string".',
790
          'negated' => '%item must not %verb with "%string".',
791
        );
792
        $error_vars = array('%item' => $components[$key]['name'], '%verb' => $verb, '%string' => $rule['data']);
793
        foreach ($val as $subval) {
794
          $test = !preg_match($pattern, $subval);
795
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
796 85ad3d82 Assos Assos
        }
797 5c451ca3 Assos Assos
      }
798
      return $errors;
799
    case 'pattern':
800
      $pattern = preg_quote($rule['data'], '/');
801
      $pattern = str_replace('@', '[a-zA-Z]', $pattern);
802
      $pattern = str_replace('#', '[0-9]', $pattern);
803
      $pattern = '(' . $pattern . ')';
804
      // Un-escape "|" operator.
805
      $pattern = preg_replace('/(\\s*)(\\\\)(\\|)(\\s*)/', ')|(', $pattern);
806
      foreach ($items as $key => $val) {
807
        foreach ($val as $subval) {
808
          $test = !preg_match('/^(' . $pattern . ')$/', $subval);
809
          _webform_validation_test($errors, $key, $rule, $test);
810 85ad3d82 Assos Assos
        }
811 5c451ca3 Assos Assos
      }
812
      return $errors;
813
    case 'regex':
814
    case 'regexi':
815
      mb_regex_encoding('UTF-8');
816
      $regex = $rule['data'];
817
      foreach ($items as $key => $val) {
818
        foreach ($val as $subval) {
819
          if ($validator_name === 'regexi') {
820
            $match = (bool) mb_eregi($regex, $subval);
821 a2baadd1 Assos Assos
          }
822 5c451ca3 Assos Assos
          else {
823
            $match = (bool) mb_ereg($regex, $subval);
824 a2baadd1 Assos Assos
          }
825 5c451ca3 Assos Assos
          $test = !$match;
826
          _webform_validation_test($errors, $key, $rule, $test);
827 85ad3d82 Assos Assos
        }
828 5c451ca3 Assos Assos
      }
829
      return $errors;
830
    case 'must_be_empty':
831
      foreach ($items as $key => $val) {
832
        if (count($val) !== 0) {
833
          $errors[$key] = t('%item does not contain the correct data.', array('%item' => $components[$key]['name']));
834 85ad3d82 Assos Assos
        }
835 5c451ca3 Assos Assos
      }
836
      return $errors;
837
    case 'blacklist':
838
      $blacklist = preg_quote($rule['data'], '/');
839
      $blacklist = explode(',', $blacklist);
840
      $blacklist = array_map('trim', $blacklist);
841
      $blacklist = '/\b(' . implode('|', $blacklist) . ')\b/i';
842
      foreach ($items as $key => $val) {
843
        foreach ($val as $subval) {
844
          $test = preg_match($blacklist, $subval);
845
          _webform_validation_test($errors, $key, $rule, $test);
846 85ad3d82 Assos Assos
        }
847 5c451ca3 Assos Assos
      }
848
      return $errors;
849
    case 'username':
850
      foreach ($items as $key => $val) {
851
        $error_strings = array(
852
          'regular' => 'The %item field does not match an active username.',
853
          'negated' => 'The %item field matches an active username.',
854
        );
855
        $error_vars = array('%item' => $components[$key]['name']);
856
        foreach ($val as $subval) {
857
          $test = !db_query_range('SELECT 1 FROM {users} WHERE name = :username AND status = 1', 0, 1, array(':username' => $subval))->fetchField();
858
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
859 85ad3d82 Assos Assos
        }
860 5c451ca3 Assos Assos
      }
861
      return $errors;
862
    case 'valid_url':
863
      foreach ($items as $key => $val) {
864
        $error_strings = array(
865
          'regular' => '%item does not appear to be a valid URL.',
866
          'negated' => '%item must not be a valid URL.',
867
        );
868
        $error_vars = array('%item' => $components[$key]['name']);
869
        foreach ($val as $subval) {
870
          $test = !valid_url($subval, TRUE);
871
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
872 85ad3d82 Assos Assos
        }
873 5c451ca3 Assos Assos
      }
874
      return $errors;
875
    case 'email_verify':
876
      if (module_exists('email_verify')) {
877 85ad3d82 Assos Assos
        foreach ($items as $key => $val) {
878 a2baadd1 Assos Assos
          foreach ($val as $subval) {
879 5c451ca3 Assos Assos
            $error = email_verify_check($subval);
880
            if ($error) {
881
              $errors[$key] = $error;
882 85ad3d82 Assos Assos
            }
883
          }
884
        }
885 5c451ca3 Assos Assos
      }
886
      return $errors;
887 85ad3d82 Assos Assos
  }
888
}
889
890 5c451ca3 Assos Assos
/**
891
 * Return an array of the names of the components in a validation rule.
892
 *
893
 * @param $rule array
894
 *   Array of information about a validation rule.
895
 *
896
 * @return array
897
 *   Array of the filtered names of the components in $rule.
898
 */
899
function _webform_validation_get_names_of_rule_components(array $rule) {
900
  $names = array();
901
  foreach ($rule['components'] as $component) {
902
    $names[] = _webform_filter_xss($component['name']);
903
  }
904
  return $names;
905
}
906
907 85ad3d82 Assos Assos
/**
908
 * Helper function to negate validation rules as needed and create the correct error message.
909
 */
910
function _webform_validation_test(&$errors, $key, $rule, $test, array $error_strings = NULL, array $error_vars = NULL) {
911
  $rule['negate'] = !empty($rule['negate']);
912
  if ($rule['negate']) {
913
    $test = !$test;
914
  }
915
  if ($test) {
916
    if ($error_strings) {
917
      $error = t($error_strings[$rule['negate'] ? 'negated' : 'regular'], $error_vars);
918
    }
919
    else {
920
      $error = _webform_validation_i18n_error_message($rule);
921
    }
922
    $errors[$key] = $error;
923
  }
924
}
925
926
/**
927
 * Count the number of words in a value.
928 a2baadd1 Assos Assos
 *
929
 * Strip HTML first.
930 1f623f01 Assos Assos
 *
931
 * @param string|array $val
932
 *   A string or array of strings in which to count words.
933
 *
934
 * @return int
935
 *   The number of words in the input.
936 85ad3d82 Assos Assos
 */
937
function _webform_validation_count_words($val) {
938 a2baadd1 Assos Assos
  $val = _webform_validation_flatten_array($val);
939
940 1f623f01 Assos Assos
  // Strip any HTML tags so they're not counted in the word count.
941 a2baadd1 Assos Assos
  $val = strip_tags($val);
942 1f623f01 Assos Assos
  // Convert character entities back to characters. Without this, entities for
943
  // whitespace characters would not be counted as word boundaries.
944
  $val = html_entity_decode($val);
945 a2baadd1 Assos Assos
946 1f623f01 Assos Assos
  return count(preg_split('/[[:space:]\xC2\xA0]+/', $val));
947 85ad3d82 Assos Assos
}
948
949
/**
950 a2baadd1 Assos Assos
 * Helper function to deal with submitted values that are arrays (for example, multiple select component).
951 85ad3d82 Assos Assos
 * We flatten the array as a comma-separated list to do the comparison.
952
 */
953
function _webform_validation_flatten_array($val) {
954
  if (is_array($val)) {
955
    $arr = array_filter($val, '_webform_validation_check_false');
956
    return implode(',', $arr);
957
  }
958
  return $val;
959
}
960
961
/**
962 5c451ca3 Assos Assos
 * Get a list of validator definitions.
963
 *
964
 * @return
965
 *   Array of information about validators.
966 85ad3d82 Assos Assos
 */
967
function webform_validation_get_validators() {
968
  $validators = module_invoke_all('webform_validation_validators');
969 5c451ca3 Assos Assos
  // Let modules use hook_webform_validator_alter($validators) to change validator settings.
970 85ad3d82 Assos Assos
  drupal_alter('webform_validator', $validators);
971 5c451ca3 Assos Assos
972
  // Remove entries for which only partial information exists.
973
  foreach ($validators as $validator_key => $validator_info) {
974
    if (!isset($validator_info['name']) || !isset($validator_info['component_types'])) {
975
      unset($validators[$validator_key]);
976
    }
977
  }
978
979 85ad3d82 Assos Assos
  return $validators;
980
}
981
982
/**
983 5c451ca3 Assos Assos
 * Return an array of the names of the validators.
984
 *
985
 * @return array
986
 *   Array of with keys being validator IDs and values validator names.
987 85ad3d82 Assos Assos
 */
988
function webform_validation_get_validators_selection() {
989
  $selection = array();
990
  $validators = webform_validation_get_validators();
991
  if ($validators) {
992
    foreach ($validators as $validator_key => $validator_info) {
993
      $selection[$validator_key] = $validator_info['name'];
994
    }
995
  }
996
  return $selection;
997
}
998
999
/**
1000
 * Get a list of valid component types per validator, as defined via hook_webform_validation_validators().
1001
 * If 'all' is specified, all available component types will be returned.
1002
 */
1003
function webform_validation_valid_component_types($validator) {
1004
  $validators = webform_validation_get_validators();
1005
  if ($info = $validators[$validator]) {
1006
    $allowed_types = $info['component_types'];
1007 5c451ca3 Assos Assos
    if (in_array('all', $allowed_types, TRUE)) {
1008 85ad3d82 Assos Assos
      return array_keys(webform_components());
1009
    }
1010
    return $info['component_types'];
1011
  }
1012
}
1013
1014
/**
1015 5c451ca3 Assos Assos
 * Return information about a specified validator.
1016
 *
1017
 * @param $validator_key string
1018
 *   The key of the validator to return information about.
1019
 *
1020
 * @return array
1021
 *   Array of information about the validator.
1022 85ad3d82 Assos Assos
 */
1023
function webform_validation_get_validator_info($validator_key) {
1024
  $validators = webform_validation_get_validators();
1025
  return $validators[$validator_key];
1026
}
1027
1028
/**
1029
 * Handle translatable error messages, if available
1030
 */
1031
function _webform_validation_i18n_error_message($rule) {
1032
  $rule['error_message'] = filter_xss($rule['error_message']);
1033 a2baadd1 Assos Assos
  if (module_exists('i18n')) {
1034
    return i18n_string('webform_validation:error_message:' . $rule['ruleid'] . ':message', $rule['error_message']);
1035 85ad3d82 Assos Assos
  }
1036
  return $rule['error_message'];
1037
}
1038
1039
/**
1040 4f315dab Assos Assos
 * Helper function used by array_filter() to determine if a value is empty.
1041
 *
1042
 * This is used in place of empty() because string '0' needs to be considered
1043
 * not empty and return FALSE.
1044
 *
1045
 * @param mixed $var
1046
 *   The value to check.
1047
 *
1048
 * @return bool
1049
 *   TRUE if $var is not one of FALSE, int 0, or empty string; FALSE otherwise.
1050
 *
1051
 * @see https://www.drupal.org/node/886458
1052
 * @see https://www.drupal.org/node/2638172
1053 85ad3d82 Assos Assos
 */
1054
function _webform_validation_check_false($var) {
1055 4f315dab Assos Assos
  return $var !== FALSE && $var !== 0 && $var !== '';
1056 85ad3d82 Assos Assos
}
1057
1058
/**
1059
 * Process the numeric value validation range that was provided in the numeric validator options
1060
 */
1061
function _webform_numeric_check_data($data) {
1062
  $range = array('min' => NULL, 'max' => NULL);
1063
  // if no value was specified, don't validate
1064
  if ($data == '') {
1065
    return $range;
1066
  }
1067
1068
  // If only one numeric value was specified, this is the min value
1069
  if (is_numeric($data)) {
1070
    $range['min'] = (float) $data;
1071
  }
1072
1073
  if (strpos($data, '|') !== FALSE) {
1074
    list($min, $max) = explode('|', $data);
1075
    if ($min != '' && is_numeric($min)) {
1076
      $range['min'] = (float) $min;
1077
    }
1078
    if ($max != '' && is_numeric($max)) {
1079
      $range['max'] = (float) $max;
1080
    }
1081
  }
1082
  return $range;
1083
}