Projet

Général

Profil

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

root / drupal7 / sites / all / modules / webform_validation / webform_validation.validators.inc @ 87dbc3bf

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