Projet

Général

Profil

Paste
Télécharger (33,4 ko) Statistiques
| Branche: | Révision:

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

1
<?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
        'number',
28
        'textfield',
29
        'hidden',
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
          . 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
        '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
        'number',
43
        'textfield',
44
        'textarea',
45
        'email',
46
        'hidden',
47
      ),
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
      'description' => t('Verifies that a user-entered value contains at least the specified number of characters.'),
53
    ),
54
    'max_length' => array(
55
      'name' => t('Maximum length'),
56
      'component_types' => array(
57
        'number',
58
        'textfield',
59
        'textarea',
60
        'email',
61
        'hidden',
62
      ),
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
      'description' => t('Verifies that a user-entered value contains at most the specified number of characters.'),
68
    ),
69
    'min_words' => array(
70
      'name' => t('Minimum number of words'),
71
      'component_types' => array(
72
        'textfield',
73
        'textarea',
74
        'hidden',
75
        'html_textarea',
76
      ),
77
      'custom_data' => array(
78
        'label' => t('Minimum number of words'),
79
        '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
      ),
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
        'textfield',
87
        'textarea',
88
        'hidden',
89
        'html_textarea',
90
      ),
91
      'custom_data' => array(
92
        'label' => t('Maximum number of words'),
93
        '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
      ),
95
      'description' => t('Verifies that a user-entered value contains at most the specified number of words.'),
96
    ),
97
    'equal' => array(
98
      'name' => t('Equal values'),
99
      'component_types' => array(
100
        'number',
101
        'textfield',
102
        'email',
103
        'select',
104
        'hidden',
105
      ),
106
      'min_components' => 2,
107
      'description' => t('Verifies that all specified components contain equal values.'),
108
    ),
109
    'comparison' => array(
110
      'name' => t('Compare two values'),
111
      'component_types' => array(
112
        'date',
113
        'email',
114
        'hidden',
115
        'number',
116
        'textfield',
117
        'select',
118
        'time',
119
      ),
120
      'custom_error' => TRUE,
121
      'custom_data' => array(
122
        'label' => t('Comparison operator'),
123
        '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.'),
124
      ),
125
      'min_components' => 2,
126
      'max_components' => 2,
127
      'description' => t('Compare two values for greater than or less than, or equal to.'),
128
    ),
129
    'unique' => array(
130
      'name' => t('Unique values'),
131
      'component_types' => array(
132
        'number',
133
        'textfield',
134
        'email',
135
        'select',
136
        'hidden',
137
      ),
138
      'min_components' => 2,
139
      'description' => t('Verifies that all specified components contain unique values.'),
140
    ),
141
    'specific_value' => array(
142
      'name' => t('Specific value(s)'),
143
      'negatable' => TRUE,
144
      'component_types' => array(
145
        'number',
146
        'select',
147
        'textfield',
148
        'textarea',
149
        'email',
150
        'hidden',
151
      ),
152
      'custom_error' => TRUE,
153
      'custom_data' => array(
154
        'label' => t('(Key) value'),
155
        '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.'),
156
      ),
157
      'max_components' => 1,
158
      'description' => t('Verifies that the value of the specified component is from a list of allowed values.'),
159
    ),
160
    'default_value' => array(
161
      'name' => t('Default value'),
162
      'negatable' => TRUE,
163
      'component_types' => array(
164
        'select',
165
        'number',
166
        'textfield',
167
        'textarea',
168
        'email',
169
        'hidden',
170
      ),
171
      'custom_error' => TRUE,
172
      'description' => t('Verifies that the user-entered value is the default value for that component. Negate if you want not the default value.'),
173
    ),
174
    'someofseveral' => array(
175
      'name' => t('At least one or more out of two or more'),
176
      'component_types' => array(
177
        'number',
178
        'textfield',
179
        'textarea',
180
        'email',
181
        'select',
182
      ),
183
      'custom_data' => array(
184
        'label' => t('Minimum number to be completed'),
185
        'description' => t('Specify the minimum number that must be completed. For example, enter 1 if the user must complete at least 1 of the selected components.'),
186
      ),
187
      'min_components' => 2,
188
      'description' => t('Requires the user to complete at least one or more of two or more Webform components, such as 3 out of 5 or 1 out of 3.'),
189
    ),
190
    'select_min' => array(
191
      'name' => t('Minimum number of selections required'),
192
      'component_types' => array(
193
        'select',
194
      ),
195
      'custom_data' => array(
196
        'label' => t('Minimum number of selections'),
197
        'description' => t('Specify the minimum number of options a user should select.'),
198
      ),
199
      'description' => t('Forces the user to select at least a defined number of options from the specified webform components.'),
200
    ),
201
    'select_max' => array(
202
      'name' => t('Maximum number of selections allowed'),
203
      'component_types' => array(
204
        'select',
205
      ),
206
      'custom_data' => array(
207
        'label' => t('Maximum number of selections'),
208
        'description' => t('Specify the maximum number of options a user can select.'),
209
      ),
210
      'description' => t('Forces the user to select at most a defined number of options from the specified webform components.'),
211
    ),
212
    'select_exact' => array(
213
      'name' => t('Exact number of selections required'),
214
      'negatable' => TRUE,
215
      'component_types' => array(
216
        'select',
217
      ),
218
      'custom_data' => array(
219
        'label' => t('Number of selections'),
220
        'description' => t('Specify how many options a user can select.'),
221
      ),
222
      'description' => t('Forces the user to select exactly the defined number of options from the specified webform components.'),
223
    ),
224
    'plain_text' => array(
225
      'name' => t('Plain text (disallow tags)'),
226
      'negatable' => TRUE,
227
      'component_types' => array(
228
        'textfield',
229
        'textarea',
230
        'email',
231
        'hidden',
232
      ),
233
      'description' => t('Verifies that user-entered data doesn\'t contain HTML tags.'),
234
    ),
235
    'starts_with' => array(
236
      'name' => t('Starts with'),
237
      'negatable' => TRUE,
238
      'component_types' => array(
239
        'number',
240
        'textfield',
241
        'textarea',
242
        'email',
243
        'hidden',
244
      ),
245
      'custom_data' => array(
246
        'label' => t('Starts with'),
247
        'description' => t('Enter the text that this field must start with.'),
248
      ),
249
      'description' => t('Verifies that user-entered data starts with a given string.'),
250
    ),
251
    'ends_with' => array(
252
      'name' => t('Ends with'),
253
      'negatable' => TRUE,
254
      'component_types' => array(
255
        'number',
256
        'textfield',
257
        'textarea',
258
        'email',
259
        'hidden',
260
      ),
261
      'custom_data' => array(
262
        'label' => t('Ends with'),
263
        'description' => t('Enter the text that this field must end with.'),
264
      ),
265
      'description' => t('Verifies that user-entered data ends with a given string.'),
266
    ),
267
    'pattern' => array(
268
      'name' => 'Pattern',
269
      'component_types' => array(
270
        'textfield',
271
        'hidden',
272
      ),
273
      'custom_error' => TRUE,
274
      'custom_data' => array(
275
        'label' => t('Pattern'),
276
        'description' => t('Specify a pattern where:')
277
          . 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'))))
278
          . t('Examples')
279
          . theme('item_list', array('items' => array(t('North American phone number: (###) ###-####'), t('D-2500 series model numbers: D-25##'), t('UK Postal Code: @# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA')))),
280
      ),
281
      'description' => t('Verifies that a user-entered value follows to a specified pattern.'),
282
    ),
283
    'regex' => array(
284
      'name' => t('Regular expression, case-sensitive'),
285
      'negatable' => TRUE,
286
      'component_types' => array(
287
        'number',
288
        'textfield',
289
        'textarea',
290
        'email',
291
        'hidden',
292
      ),
293
      'custom_error' => TRUE,
294
      'custom_data' => array(
295
        'label' => t('Regex code'),
296
        'description' => t('Specify regex code to validate the user input against.'),
297
      ),
298
      'description' => t('Validates user-entered text against a specified case-sensitive regular expression. Note: don\'t include delimiters such as /.'),
299
    ),
300
    'regexi' => array(
301
      'name' => t('Regular expression, case-insensitive'),
302
      'negatable' => TRUE,
303
      'component_types' => array(
304
        'number',
305
        'textfield',
306
        'textarea',
307
        'email',
308
        'hidden',
309
      ),
310
      'custom_error' => TRUE,
311
      'custom_data' => array(
312
        'label' => t('Regex code'),
313
        'description' => t('Specify regex code to validate the user input against.'),
314
      ),
315
      'description' => t('Validates user-entered text against a specified case-insensitive regular expression. Note: don\'t include delimiters such as /.'),
316
    ),
317
    'must_be_empty' => array(
318
      'name' => t('Must be empty'),
319
      'component_types' => array(
320
        'number',
321
        'textfield',
322
        'hidden',
323
      ),
324
      'description' => t('Verifies that a specified textfield remains empty. Recommended use case: used as an anti-spam measure by hiding the element with CSS.'),
325
    ),
326
    'blacklist' => array(
327
      'name' => t('Words blacklist'),
328
      'negatable' => TRUE,
329
      'component_types' => array(
330
        'textfield',
331
        'textarea',
332
        'email',
333
        'hidden',
334
      ),
335
      'custom_error' => TRUE,
336
      'custom_data' => array(
337
        'label' => t('Blacklisted words'),
338
        'description' => t('Specify illegal words, seperated by commas.'),
339
      ),
340
      'description' => t('Validates that user-entered data doesn\'t contain any of the specified illegal words.'),
341
    ),
342
   'username' => array(
343
      'name' => t('Must match a username'),
344
      'negatable' => TRUE,
345
      'component_types' => array(
346
        'textfield',
347
        'hidden',
348
      ),
349
      'description' => t('Validates that user-entered data matches a username.'),
350
    ),
351
    'valid_url' => array(
352
      'name' => 'Valid URL',
353
      'negatable' => TRUE,
354
      'component_types' => array(
355
        'textfield',
356
        'hidden',
357
      ),
358
      'description' => t('Validates that the user-entered data is a valid URL.'),
359
    ),
360
  );
361

    
362
  if (module_exists('email_verify')) {
363
    $validators['email_verify'] = array(
364
      'name' => 'Email Verify',
365
      'component_types' => array(
366
        'email',
367
      ),
368
      'description' => t('Verifies that an email address actually exists using the <a href="https://drupal.org/project/email_verify">Email Verification module</a>.'),
369
    );
370
  }
371

    
372
  return $validators;
373
}
374

    
375
/**
376
 * Implements hook_webform_validation_validate().
377
 */
378
function webform_validation_webform_validation_validate($validator_name, $items, $components, $rule) {
379
  if ($items) {
380
    // Some components, such as multiple select, send their values as arrays, others don't.
381
    // Convert all to arrays and write the rules to handle them that way. Remove empty values.
382
    foreach ($items as $key => $val) {
383
      $new_values = array();
384
      foreach ((array) $val as $val_key => $val_val) {
385
        if ((string) $val_val !== '') {
386
          $new_values[$val_key] = $val_val;
387
        }
388
      }
389
      $items[$key] = $new_values;
390
    }
391

    
392
    $errors = array();
393
    switch ($validator_name) {
394
      case 'numeric':
395
        $num_range = _webform_numeric_check_data($rule['data']);
396
        foreach ($items as $key => $val) {
397
          foreach ($val as $subval) {
398
            // first check if the value is numeric
399
            if (is_numeric($subval)) {
400
              $subval = (float) $subval;
401
            }
402
            else {
403
              $errors[$key] = t('%item is not numeric.', array('%item' => $components[$key]['name']));
404
              continue;
405
            }
406

    
407
            // now validate the entered numeric value against the validator range settings, if appropriate
408
            // a. validate min & max
409
            if (isset($num_range['min']) && isset($num_range['max'])) {
410
              // validate the min - max range
411
              if ($subval < $num_range['min'] || $subval > $num_range['max']) {
412
                $errors[$key] = t('%item is not within the allowed range %range.', array('%item' => $components[$key]['name'], '%range' => str_replace('|' , ' - ', $rule['data'])));
413
              }
414
            }
415
            else {
416
              // b. validate min
417
              if (isset($num_range['min'])) {
418
                if ($subval < $num_range['min']) {
419
                  $errors[$key] = t('%item should be greater than or equal to %val.', array('%item' => $components[$key]['name'], '%val' => $num_range['min']));
420
                }
421
              }
422
              // c. validate max
423
              if (isset($num_range['max'])) {
424
                if ($subval > $num_range['max']) {
425
                  $errors[$key] = t('%item should be less than or equal to %val.', array('%item' => $components[$key]['name'], '%val' => $num_range['max']));
426
                }
427
              }
428
            }
429
          }
430
        }
431
        return $errors;
432
      case 'min_length':
433
        $min_length = $rule['data'];
434
        foreach ($items as $key => $val) {
435
          foreach ($val as $subval) {
436
            if (drupal_strlen($subval) < $min_length) {
437
              $errors[$key] = t('%item should be at least %num characters long.', array('%item' => $components[$key]['name'], '%num' => $min_length));
438
            }
439
          }
440
        }
441
        return $errors;
442
      case 'max_length':
443
        $max_length = $rule['data'];
444
        foreach ($items as $key => $val) {
445
          foreach ($val as $subval) {
446
            if (drupal_strlen($subval) > $max_length) {
447
              $errors[$key] = t('%item should be at most %num characters long.', array('%item' => $components[$key]['name'], '%num' => $max_length));
448
            }
449
          }
450
        }
451
        return $errors;
452
      case 'min_words':
453
        $min_words = $rule['data'];
454
        foreach ($items as $key => $val) {
455
          foreach ($val as $subval) {
456
            if (_webform_validation_count_words($subval) < $min_words) {
457
              $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']));
458
              $errors[$key] = $error;
459
            }
460
          }
461
        }
462
        return $errors;
463
      case 'max_words':
464
        $max_words = $rule['data'];
465
        foreach ($items as $key => $val) {
466
          foreach ($val as $subval) {
467
            if (_webform_validation_count_words($subval) > $max_words) {
468
              $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']));
469
              $errors[$key] = $error;
470
            }
471
          }
472
        }
473
        return $errors;
474
      case 'equal':
475
        $first_entry_key = key($items);
476
        $first_entry = array_shift($items);
477
        foreach ($items as $key => $val) {
478
          if ($val !== $first_entry) {
479
            $errors[$key] = t('%item_checked does not match %item_first.', array('%item_checked' => $components[$key]['name'], '%item_first' => $components[$first_entry_key]['name']));
480
          }
481
        }
482
        return $errors;
483
      case 'comparison':
484
        foreach (array(1, 2) as $count) {
485
          $entry[$count]['key'] = key($items);
486
          $value = array_shift($items);
487
          if ($components[$entry[$count]['key']]['type'] === 'date') {
488
            if (checkdate((int) $value['month'], (int) $value['day'], (int) $value['year'])) {
489
              $entry[$count]['value'] = date('Y-m-d', mktime(0, 0, 0, (int) $value['month'], (int) $value['day'], (int) $value['year']));
490
            }
491
            else {
492
              $entry[$count]['value'] = NULL;
493
            }
494
          }
495
          elseif ($components[$entry[$count]['key']]['type'] === 'time') {
496
            $time = $value['hour'] . ':' . $value['minute'];
497
            if (!empty($value['ampm'])) {
498
              $time .= ' ' . $value['ampm'];
499
            }
500
            $time = strtotime($time);
501
            if ($time) {
502
              $entry[$count]['value'] = date('H:i', $time);
503
            }
504
            else {
505
              $entry[$count]['value'] = NULL;
506
            }
507
          }
508
          else {
509
            $entry[$count]['value'] = _webform_validation_flatten_array($value);
510
          }
511
        }
512

    
513
        // Don't validate if either are empty.
514
        if (empty($entry[1]['value']) || empty($entry[2]['value'])) {
515
          return;
516
        }
517

    
518
        $error = FALSE;
519
        switch ($rule['data']) {
520
          case '>':
521
            if (!($entry[1]['value'] > $entry[2]['value'])) {
522
              $error = TRUE;
523
            }
524
            break;
525
          case '>=':
526
            if (!($entry[1]['value'] >= $entry[2]['value'])) {
527
              $error = TRUE;
528
            }
529
            break;
530
          case '<':
531
            if (!($entry[1]['value'] < $entry[2]['value'])) {
532
              $error = TRUE;
533
            }
534
            break;
535
          case '<=':
536
            if (!($entry[1]['value'] <= $entry[2]['value'])) {
537
              $error = TRUE;
538
            }
539
            break;
540
        }
541

    
542
        if ($error) {
543
          $errors[$entry[2]['key']] = _webform_validation_i18n_error_message($rule);
544
        }
545
        return $errors;
546
      case 'unique':
547
        foreach ($items as $key => $val) {
548
          $items[$key] = _webform_validation_flatten_array($val);
549
        }
550
        // now we count how many times each value appears, and find out which values appear more than once
551
        $items_count = array_count_values(array_map('drupal_strtolower', array_map('trim', $items)));
552
        $doubles = array_filter($items_count, create_function('$x', 'return $x > 1;'));
553
        foreach ($items as $key => $val) {
554
          if (in_array(drupal_strtolower($val), array_keys($doubles))) {
555
            $errors[$key] = t('The value of %item is not unique.', array('%item' => $components[$key]['name']));
556
          }
557
        }
558
        return $errors;
559
      case 'specific_value':
560
        $specific_values = explode(',', $rule['data']);
561
        $specific_values = array_map('trim', $specific_values);
562
        foreach ($items as $key => $val) {
563
          // Selects: Fail if at least one checked and at least one not in the allowed values.
564
          $val = array_filter($val);
565
          $test = count($val) && count(array_diff($val, $specific_values));
566
          _webform_validation_test($errors, $key, $rule, $test);
567
        }
568
        return $errors;
569
      case 'default_value':
570
        foreach ($items as $key => $val) {
571
          $val = _webform_validation_flatten_array($val);
572
          $test = $val != $components[$key]['value'];
573
          _webform_validation_test($errors, $key, $rule, $test);
574
        }
575
        return $errors;
576
      case 'someofseveral':
577
        foreach ($items as $key => $val) {
578
          $items[$key] = _webform_validation_flatten_array($val);
579
          $items[$key] = (bool) strlen($items[$key]);
580
        }
581
        $min_selections = isset($rule['data']) ? ((int) $rule['data']) : 1;
582
        if (count(array_filter($items)) < $min_selections) {
583
          $keys = array_keys($items);
584
          $names = array();
585
          foreach ($keys as $value) {
586
            $names[] = _webform_filter_xss($components[$value]['name']);
587
          }
588
          return array($keys[0] => t('You must complete at least %min_selections of these items:', array('%min_selections' => $min_selections)) . theme('item_list', array('items' => $names)));
589
        }
590
        return;
591
      case 'select_min':
592
        $min_selections = $rule['data'];
593
        foreach ($items as $key => $val) {
594
          if (count(array_filter($val, '_webform_validation_check_false')) < $min_selections) {
595
            $errors[$key] = t('Please select at least %num options for %item.', array('%num' => $min_selections, '%item' => $components[$key]['name']));
596
          }
597
        }
598
        return $errors;
599
      case 'select_max':
600
        $max_selections = $rule['data'];
601
        foreach ($items as $key => $val) {
602
          if (count(array_filter($val, '_webform_validation_check_false')) > $max_selections) {
603
            $errors[$key] = t('Please select maximum %num options for %item.', array('%num' => $max_selections, '%item' => $components[$key]['name']));
604
          }
605
        }
606
        return $errors;
607
      case 'select_exact':
608
        $allowed_selections = $rule['data'];
609
        foreach ($items as $key => $val) {
610
          $error_strings = array(
611
            'regular' => 'Please select %num options for %item.',
612
            'negated' => 'Please do not select %num options for %item.',
613
          );
614
          $error_vars = array('%num' => $allowed_selections, '%item' => $components[$key]['name']);
615
          $test = count(array_filter($val, '_webform_validation_check_false')) != $allowed_selections;
616
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
617
        }
618
        return $errors;
619
      case 'plain_text':
620
        foreach ($items as $key => $val) {
621
          $error_strings = array(
622
            'regular' => '%item only allows the use of plain text.',
623
            'negated' => '%item must contain HTML tags.',
624
          );
625
          $error_vars = array('%item' => $components[$key]['name']);
626
          foreach ($val as $subval) {
627
            $test = strcmp($subval, strip_tags($subval));
628
            _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
629
          }
630
        }
631
        return $errors;
632
      case 'starts_with':
633
      case 'ends_with':
634
        $pattern = _webform_validation_preg_quote($rule['data'], '/');
635
        if ($validator_name === 'starts_with') {
636
          $pattern = '^' . $pattern;
637
          $verb = t('start');
638
        }
639
        else {
640
          $pattern .= '$';
641
          $verb = t('end');
642
        }
643
        $pattern = '/' . $pattern . '/';
644

    
645
        foreach ($items as $key => $val) {
646
          $error_strings = array(
647
            'regular' => '%item must %verb with "%string".',
648
            'negated' => '%item must not %verb with "%string".',
649
          );
650
          $error_vars = array('%item' => $components[$key]['name'], '%verb' => $verb, '%string' => $rule['data']);
651
          foreach ($val as $subval) {
652
            $test = !preg_match($pattern, $subval);
653
            _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
654
          }
655
        }
656
        return $errors;
657
      case 'pattern':
658
        $pattern = _webform_validation_preg_quote($rule['data'], '/');
659
        $pattern = str_replace('@', '[a-zA-Z]', $pattern);
660
        $pattern = str_replace('#', '[0-9]', $pattern);
661
        $pattern = '(' . $pattern . ')';
662
        // Un-escape "|" operator.
663
        $pattern = preg_replace('/(\\s*)(\\\\)(\\|)(\\s*)/', ')|(', $pattern);
664
        foreach ($items as $key => $val) {
665
          foreach ($val as $subval) {
666
            $test = !preg_match('/^(' . $pattern . ')$/', $subval);
667
            _webform_validation_test($errors, $key, $rule, $test);
668
          }
669
        }
670
        return $errors;
671
      case 'regex':
672
      case 'regexi':
673
        mb_regex_encoding('UTF-8');
674
        $regex = $rule['data'];
675
        foreach ($items as $key => $val) {
676
          foreach ($val as $subval) {
677
            if ($validator_name === 'regexi') {
678
              $match = (bool) mb_eregi($regex, $subval);
679
            }
680
            else {
681
              $match = (bool) mb_ereg($regex, $subval);
682
            }
683
            $test = !$match;
684
            _webform_validation_test($errors, $key, $rule, $test);
685
          }
686
        }
687
        return $errors;
688
      case 'must_be_empty':
689
        foreach ($items as $key => $val) {
690
          if (count($val) !== 0) {
691
            $errors[$key] = t('%item does not contain the correct data.', array('%item' => $components[$key]['name']));
692
          }
693
        }
694
        return $errors;
695
      case 'blacklist':
696
        $blacklist = _webform_validation_preg_quote($rule['data'], '/');
697
        $blacklist = explode(',', $blacklist);
698
        $blacklist = array_map('trim', $blacklist);
699
        $blacklist = '/\b(' . implode('|', $blacklist) . ')\b/i';
700
        foreach ($items as $key => $val) {
701
          foreach ($val as $subval) {
702
            $test = preg_match($blacklist, $subval);
703
            _webform_validation_test($errors, $key, $rule, $test);
704
          }
705
        }
706
        return $errors;
707
      case 'username':
708
        foreach ($items as $key => $val) {
709
          $error_strings = array(
710
            'regular' => 'The %item field does not match an active username.',
711
            'negated' => 'The %item field matches an active username.',
712
          );
713
          $error_vars = array('%item' => $components[$key]['name']);
714
          foreach ($val as $subval) {
715
            $test = !db_query_range('SELECT 1 FROM {users} WHERE name = :username AND status = 1', 0, 1, array(':username' => $subval))->fetchField();
716
            _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
717
          }
718
        }
719
        return $errors;
720
      case 'valid_url':
721
        foreach ($items as $key => $val) {
722
          $error_strings = array(
723
            'regular' => '%item does not appear to be a valid URL.',
724
            'negated' => '%item must not be a valid URL.',
725
          );
726
          $error_vars = array('%item' => $components[$key]['name']);
727
          foreach ($val as $subval) {
728
            $test = !valid_url($subval, TRUE);
729
            _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
730
          }
731
        }
732
        return $errors;
733
      case 'email_verify':
734
        if (module_exists('email_verify')) {
735
          foreach ($items as $key => $val) {
736
            foreach ($val as $subval) {
737
              $error = email_verify_check($subval);
738
              if ($error) {
739
                $errors[$key] = $error;
740
              }
741
            }
742
          }
743
        }
744
        return $errors;
745
    }
746
  }
747
}
748

    
749
/**
750
 * Helper function to negate validation rules as needed and create the correct error message.
751
 */
752
function _webform_validation_test(&$errors, $key, $rule, $test, array $error_strings = NULL, array $error_vars = NULL) {
753
  $rule['negate'] = !empty($rule['negate']);
754
  if ($rule['negate']) {
755
    $test = !$test;
756
  }
757
  if ($test) {
758
    if ($error_strings) {
759
      $error = t($error_strings[$rule['negate'] ? 'negated' : 'regular'], $error_vars);
760
    }
761
    else {
762
      $error = _webform_validation_i18n_error_message($rule);
763
    }
764
    $errors[$key] = $error;
765
  }
766
}
767

    
768
/**
769
 * Count the number of words in a value.
770
 *
771
 * Strip HTML first.
772
 */
773
function _webform_validation_count_words($val) {
774
  $val = _webform_validation_flatten_array($val);
775

    
776
  $val = strip_tags($val);
777
  // Replace entities representing spaces with actual spaces.
778
  $val = str_replace('&nbsp;', ' ', $val);
779
  $val = str_replace('&#160;', ' ', $val);
780

    
781
  return str_word_count($val);
782
}
783

    
784
/**
785
 * Fixed version of preg_quote() to work around PHP bug #47229. Remove when
786
 * PHP_VERSION < 5.3 is no longer supported.
787
 */
788
function _webform_validation_preg_quote($str, $delimiter = NULL) {
789
  $str = preg_quote($str, $delimiter);
790
  if (PHP_VERSION < 5.3) {
791
    $str = str_replace('-', '\-', $str);
792
  }
793
  return $str;
794
}
795

    
796
/**
797
 * Helper function to deal with submitted values that are arrays (for example, multiple select component).
798
 * We flatten the array as a comma-separated list to do the comparison.
799
 */
800
function _webform_validation_flatten_array($val) {
801
  if (is_array($val)) {
802
    $arr = array_filter($val, '_webform_validation_check_false');
803
    return implode(',', $arr);
804
  }
805
  return $val;
806
}
807

    
808
/**
809
 * Get a list of validator definitions
810
 */
811
function webform_validation_get_validators() {
812
  $validators = module_invoke_all('webform_validation_validators');
813
  // let modules use hook_webform_validator_alter($validators) to change validator settings
814
  drupal_alter('webform_validator', $validators);
815
  return $validators;
816
}
817

    
818
/**
819
 * @todo Please document this function.
820
 * @see http://drupal.org/node/1354
821
 */
822
function webform_validation_get_validators_selection() {
823
  $selection = array();
824
  $validators = webform_validation_get_validators();
825
  if ($validators) {
826
    foreach ($validators as $validator_key => $validator_info) {
827
      $selection[$validator_key] = $validator_info['name'];
828
    }
829
  }
830
  return $selection;
831
}
832

    
833
/**
834
 * Get a list of valid component types per validator, as defined via hook_webform_validation_validators().
835
 * If 'all' is specified, all available component types will be returned.
836
 */
837
function webform_validation_valid_component_types($validator) {
838
  $validators = webform_validation_get_validators();
839
  if ($info = $validators[$validator]) {
840
    $allowed_types = $info['component_types'];
841
    if (_webform_validation_all_allowed($allowed_types)) {
842
      return array_keys(webform_components());
843
    }
844
    return $info['component_types'];
845
  }
846
}
847

    
848
/**
849
 * Helper function to check whether all components are allowed to be used for a certain validator
850
 */
851
function _webform_validation_all_allowed($allowed) {
852
  if ($allowed) {
853
    foreach ($allowed as $type) {
854
      if ($type == 'all') {
855
        return TRUE;
856
      }
857
    }
858
  }
859
  return FALSE;
860
}
861

    
862
/**
863
 * @todo Please document this function.
864
 * @see http://drupal.org/node/1354
865
 */
866
function webform_validation_get_validator_info($validator_key) {
867
  $validators = webform_validation_get_validators();
868
  return $validators[$validator_key];
869
}
870

    
871
/**
872
 * Handle translatable error messages, if available
873
 */
874
function _webform_validation_i18n_error_message($rule) {
875
  $rule['error_message'] = filter_xss($rule['error_message']);
876
  if (module_exists('i18n')) {
877
    return i18n_string('webform_validation:error_message:' . $rule['ruleid'] . ':message', $rule['error_message']);
878
  }
879
  return $rule['error_message'];
880
}
881

    
882
/**
883
 * Helper function used by array_filter to determine if a value was selected or not
884
 */
885
function _webform_validation_check_false($var) {
886
  return $var !== FALSE && $var !== 0;
887
}
888

    
889
/**
890
 * Process the numeric value validation range that was provided in the numeric validator options
891
 */
892
function _webform_numeric_check_data($data) {
893
  $range = array('min' => NULL, 'max' => NULL);
894
  // if no value was specified, don't validate
895
  if ($data == '') {
896
    return $range;
897
  }
898

    
899
  // If only one numeric value was specified, this is the min value
900
  if (is_numeric($data)) {
901
    $range['min'] = (float) $data;
902
  }
903

    
904
  if (strpos($data, '|') !== FALSE) {
905
    list($min, $max) = explode('|', $data);
906
    if ($min != '' && is_numeric($min)) {
907
      $range['min'] = (float) $min;
908
    }
909
    if ($max != '' && is_numeric($max)) {
910
      $range['max'] = (float) $max;
911
    }
912
  }
913
  return $range;
914
}