Projet

Général

Profil

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

root / drupal7 / sites / all / modules / webform_validation / webform_validation.validators.inc @ 5c451ca3

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