Projet

Général

Profil

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

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

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 e4c061ad Assos Assos
      'description' => t('Compare two values for greater than (>), less than (<), greater than or equal to (>=), or less than or equal to (<=).'),
150 85ad3d82 Assos Assos
    ),
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 e4c061ad Assos Assos
  // Ensure later calls to key() get the first item.
450
  reset($items);
451 85ad3d82 Assos Assos
452 5c451ca3 Assos Assos
  $errors = array();
453
  switch ($validator_name) {
454
    case 'numeric':
455
      $num_range = _webform_numeric_check_data($rule['data']);
456
      foreach ($items as $key => $val) {
457
        foreach ($val as $subval) {
458
          // first check if the value is numeric
459
          if (is_numeric($subval)) {
460
            $subval = (float) $subval;
461
          }
462
          else {
463
            $errors[$key] = t('%item is not numeric.', array('%item' => $components[$key]['name']));
464
            continue;
465
          }
466
467
          // now validate the entered numeric value against the validator range settings, if appropriate
468
          // a. validate min & max
469
          if (isset($num_range['min']) && isset($num_range['max'])) {
470
            // validate the min - max range
471
            if ($subval < $num_range['min'] || $subval > $num_range['max']) {
472
              $errors[$key] = t('%item is not within the allowed range %range.', array('%item' => $components[$key]['name'], '%range' => str_replace('|' , ' - ', $rule['data'])));
473 85ad3d82 Assos Assos
            }
474 5c451ca3 Assos Assos
          }
475
          else {
476
            // b. validate min
477
            if (isset($num_range['min'])) {
478
              if ($subval < $num_range['min']) {
479
                $errors[$key] = t('%item should be greater than or equal to %val.', array('%item' => $components[$key]['name'], '%val' => $num_range['min']));
480 85ad3d82 Assos Assos
              }
481 5c451ca3 Assos Assos
            }
482
            // c. validate max
483
            if (isset($num_range['max'])) {
484
              if ($subval > $num_range['max']) {
485
                $errors[$key] = t('%item should be less than or equal to %val.', array('%item' => $components[$key]['name'], '%val' => $num_range['max']));
486 85ad3d82 Assos Assos
              }
487
            }
488
          }
489
        }
490 5c451ca3 Assos Assos
      }
491
      return $errors;
492
    case 'min_length':
493
      $min_length = $rule['data'];
494
      foreach ($items as $key => $val) {
495
        foreach ($val as $subval) {
496
          if (drupal_strlen($subval) < $min_length) {
497
            $errors[$key] = t('%item should be at least %num characters long.', array('%item' => $components[$key]['name'], '%num' => $min_length));
498 85ad3d82 Assos Assos
          }
499
        }
500 5c451ca3 Assos Assos
      }
501
      return $errors;
502
    case 'max_length':
503
      $max_length = $rule['data'];
504
      foreach ($items as $key => $val) {
505
        foreach ($val as $subval) {
506
          if (drupal_strlen($subval) > $max_length) {
507
            $errors[$key] = t('%item should be at most %num characters long.', array('%item' => $components[$key]['name'], '%num' => $max_length));
508 85ad3d82 Assos Assos
          }
509
        }
510 5c451ca3 Assos Assos
      }
511
      return $errors;
512
    case 'min_words':
513
      $min_words = $rule['data'];
514
      foreach ($items as $key => $val) {
515
        foreach ($val as $subval) {
516
          if (_webform_validation_count_words($subval) < $min_words) {
517
            $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']));
518
            $errors[$key] = $error;
519 85ad3d82 Assos Assos
          }
520
        }
521 5c451ca3 Assos Assos
      }
522
      return $errors;
523
    case 'max_words':
524
      $max_words = $rule['data'];
525
      foreach ($items as $key => $val) {
526
        foreach ($val as $subval) {
527
          if (_webform_validation_count_words($subval) > $max_words) {
528
            $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']));
529
            $errors[$key] = $error;
530 85ad3d82 Assos Assos
          }
531
        }
532 5c451ca3 Assos Assos
      }
533
      return $errors;
534
    case 'sum':
535
      // Get the numbers to sum.
536
      $sum = array();
537
      foreach ($items as $item) {
538
        if (isset($item[0])) {
539
          $sum[] = $item[0];
540
        }
541
      }
542
      // If none of the components are completed, don't run this validator.
543
      if (!count($sum)) {
544
        return array();
545
      }
546
      $sum = array_sum($sum);
547
548
      // Number being compared with.
549
      $compare_number = (float) preg_replace('/^[^0-9]+/', '', $rule['data']);
550
551
      // Parse the comparision operator and do comparison.
552 bb746689 Assos Assos
      module_load_include('inc', 'webform', 'components/number');
553 5c451ca3 Assos Assos
      $error = FALSE;
554
      if (substr($rule['data'], 0, 2) === '<=') {
555 bb746689 Assos Assos
        if (!(webform_compare_floats($sum, $compare_number) <= 0)) {
556 5c451ca3 Assos Assos
          $error = t('less than or equal to');
557
        }
558
      }
559
      elseif (substr($rule['data'], 0, 1) === '<') {
560 bb746689 Assos Assos
        if (!(webform_compare_floats($sum, $compare_number) < 0)) {
561 5c451ca3 Assos Assos
          $error = t('less than');
562
        }
563
      }
564
      elseif (substr($rule['data'], 0, 2) === '>=') {
565 bb746689 Assos Assos
        if (!(webform_compare_floats($sum, $compare_number) >= 0)) {
566 5c451ca3 Assos Assos
          $error = t('greater than or equal to');
567
        }
568
      }
569
      elseif (substr($rule['data'], 0, 1) === '>') {
570 bb746689 Assos Assos
        if (!(webform_compare_floats($sum, $compare_number) > 0)) {
571 5c451ca3 Assos Assos
          $error = t('greater than');
572
        }
573
      }
574
      else {
575 bb746689 Assos Assos
        if (!(webform_compare_floats($sum, $compare_number) === 0)) {
576 5c451ca3 Assos Assos
          $error = t('exactly');
577
        }
578
      }
579
      // Set error if needed.
580
      if ($error) {
581
        $keys = array_keys($items);
582
        $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)));
583
      }
584
      return $errors;
585
    case 'equal':
586
      $first_entry_key = key($items);
587
      $first_entry = array_shift($items);
588
      foreach ($items as $key => $val) {
589
        if ($val !== $first_entry) {
590
          $errors[$key] = t('%item_checked does not match %item_first.', array('%item_checked' => $components[$key]['name'], '%item_first' => $components[$first_entry_key]['name']));
591
        }
592
      }
593
      return $errors;
594
    case 'comparison':
595
      foreach (array(1, 2) as $count) {
596
        $entry[$count]['key'] = key($items);
597
        $value = array_shift($items);
598
        if ($components[$entry[$count]['key']]['type'] === 'date') {
599
          if (checkdate((int) $value['month'], (int) $value['day'], (int) $value['year'])) {
600
            $entry[$count]['value'] = date('Y-m-d', mktime(0, 0, 0, (int) $value['month'], (int) $value['day'], (int) $value['year']));
601
          }
602
          else {
603
            $entry[$count]['value'] = NULL;
604 85ad3d82 Assos Assos
          }
605
        }
606 5c451ca3 Assos Assos
        elseif ($components[$entry[$count]['key']]['type'] === 'time') {
607
          $time = $value['hour'] . ':' . $value['minute'];
608
          if (!empty($value['ampm'])) {
609
            $time .= ' ' . $value['ampm'];
610 85ad3d82 Assos Assos
          }
611 5c451ca3 Assos Assos
          $time = strtotime($time);
612
          if ($time) {
613
            $entry[$count]['value'] = date('H:i', $time);
614 85ad3d82 Assos Assos
          }
615
          else {
616 5c451ca3 Assos Assos
            $entry[$count]['value'] = NULL;
617 85ad3d82 Assos Assos
          }
618
        }
619 5c451ca3 Assos Assos
        else {
620
          $entry[$count]['value'] = _webform_validation_flatten_array($value);
621 85ad3d82 Assos Assos
        }
622 5c451ca3 Assos Assos
      }
623 85ad3d82 Assos Assos
624 5c451ca3 Assos Assos
      // Don't validate if either are empty.
625
      if ((string) $entry[1]['value'] === '' || (string) $entry[2]['value'] === '') {
626
        return array();
627
      }
628 85ad3d82 Assos Assos
629 5c451ca3 Assos Assos
      $error = FALSE;
630
      switch ($rule['data']) {
631
        case '>':
632
          if (!($entry[1]['value'] > $entry[2]['value'])) {
633
            $error = TRUE;
634 85ad3d82 Assos Assos
          }
635 5c451ca3 Assos Assos
          break;
636
        case '>=':
637
          if (!($entry[1]['value'] >= $entry[2]['value'])) {
638
            $error = TRUE;
639
          }
640
          break;
641
        case '<':
642
          if (!($entry[1]['value'] < $entry[2]['value'])) {
643
            $error = TRUE;
644
          }
645
          break;
646
        case '<=':
647
          if (!($entry[1]['value'] <= $entry[2]['value'])) {
648
            $error = TRUE;
649
          }
650
          break;
651
      }
652
653
      if ($error) {
654
        $errors[$entry[2]['key']] = _webform_validation_i18n_error_message($rule);
655
      }
656
      return $errors;
657
    case 'unique':
658
      foreach ($items as $key => $val) {
659
        $items[$key] = _webform_validation_flatten_array($val);
660
      }
661
      // now we count how many times each value appears, and find out which values appear more than once
662
      $items_count = array_count_values(array_map('drupal_strtolower', array_map('trim', $items)));
663
      $doubles = array_filter($items_count, create_function('$x', 'return $x > 1;'));
664
      foreach ($items as $key => $val) {
665
        if (in_array(drupal_strtolower($val), array_keys($doubles))) {
666
          $errors[$key] = t('The value of %item is not unique.', array('%item' => $components[$key]['name']));
667 85ad3d82 Assos Assos
        }
668 5c451ca3 Assos Assos
      }
669
      return $errors;
670
    case 'specific_value':
671
      $specific_values = explode(',', $rule['data']);
672
      $specific_values = array_map('trim', $specific_values);
673
      foreach ($items as $key => $val) {
674
        // Selects: Fail if at least one checked and at least one not in the allowed values.
675
        $val = array_filter($val);
676
        $test = count($val) && count(array_diff($val, $specific_values));
677
        _webform_validation_test($errors, $key, $rule, $test);
678
      }
679
      return $errors;
680
    case 'default_value':
681
      foreach ($items as $key => $val) {
682
        $val = _webform_validation_flatten_array($val);
683
        $test = $val != $components[$key]['value'];
684
        _webform_validation_test($errors, $key, $rule, $test);
685
      }
686
      return $errors;
687
    case 'someofseveral':
688
      // Determine the number of components completed.
689
      foreach ($items as $key => $val) {
690
        $items[$key] = _webform_validation_flatten_array($val);
691
        $items[$key] = (bool) strlen($items[$key]);
692
      }
693
      $number_completed = count(array_filter($items));
694
695
      // Number being compared with.
696
      $compare_number = (int) preg_replace('/[^0-9]+/', '', $rule['data']);
697
      if ($compare_number < 1) {
698
        $compare_number = 1;
699
      }
700
      elseif ($compare_number > count($rule['components'])) {
701
        $compare_number = count($rule['components']);
702
      }
703
704
      // Parse the comparision operator and do comparison.
705
      $error = FALSE;
706
      if (substr($rule['data'], 0, 1) === '=') {
707
        if (!($number_completed === $compare_number)) {
708
          $error = t('exactly');
709 85ad3d82 Assos Assos
        }
710 5c451ca3 Assos Assos
      }
711
      elseif (substr($rule['data'], 0, 2) === '<=') {
712
        if (!($number_completed <= $compare_number)) {
713
          $error = t('at most');
714 85ad3d82 Assos Assos
        }
715 5c451ca3 Assos Assos
      }
716
      else {
717
        if (!($number_completed >= $compare_number)) {
718
          $error = t('at least');
719 85ad3d82 Assos Assos
        }
720 5c451ca3 Assos Assos
      }
721
722
      // Set error if needed.
723
      if ($error) {
724
        $keys = array_keys($items);
725
        $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)));
726
      }
727
      return $errors;
728
    case 'select_min':
729
      $min_selections = $rule['data'];
730
      foreach ($items as $key => $val) {
731
        if (count(array_filter($val, '_webform_validation_check_false')) < $min_selections) {
732
          $errors[$key] = t('Please select at least %num options for %item.', array('%num' => $min_selections, '%item' => $components[$key]['name']));
733 85ad3d82 Assos Assos
        }
734 5c451ca3 Assos Assos
      }
735
      return $errors;
736
    case 'select_max':
737
      $max_selections = $rule['data'];
738
      foreach ($items as $key => $val) {
739
        if (count(array_filter($val, '_webform_validation_check_false')) > $max_selections) {
740
          $errors[$key] = t('Please select maximum %num options for %item.', array('%num' => $max_selections, '%item' => $components[$key]['name']));
741 85ad3d82 Assos Assos
        }
742 5c451ca3 Assos Assos
      }
743
      return $errors;
744
    case 'select_exact':
745
      $allowed_selections = $rule['data'];
746
      foreach ($items as $key => $val) {
747
        $error_strings = array(
748
          'regular' => 'Please select %num options for %item.',
749
          'negated' => 'Please do not select %num options for %item.',
750
        );
751
        $error_vars = array('%num' => $allowed_selections, '%item' => $components[$key]['name']);
752
        $test = count(array_filter($val, '_webform_validation_check_false')) != $allowed_selections;
753
        _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
754
      }
755
      return $errors;
756
    case 'plain_text':
757
      foreach ($items as $key => $val) {
758
        $error_strings = array(
759
          'regular' => '%item only allows the use of plain text.',
760
          'negated' => '%item must contain HTML tags.',
761
        );
762
        $error_vars = array('%item' => $components[$key]['name']);
763
        foreach ($val as $subval) {
764
          $test = strcmp($subval, strip_tags($subval));
765 85ad3d82 Assos Assos
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
766
        }
767 5c451ca3 Assos Assos
      }
768
      return $errors;
769
    case 'starts_with':
770
    case 'ends_with':
771
      $pattern = preg_quote($rule['data'], '/');
772
      if ($validator_name === 'starts_with') {
773
        $pattern = '^' . $pattern;
774
        $verb = t('start');
775
      }
776
      else {
777
        $pattern .= '$';
778
        $verb = t('end');
779
      }
780
      $pattern = '/' . $pattern . '/';
781
782
      foreach ($items as $key => $val) {
783
        $error_strings = array(
784
          'regular' => '%item must %verb with "%string".',
785
          'negated' => '%item must not %verb with "%string".',
786
        );
787
        $error_vars = array('%item' => $components[$key]['name'], '%verb' => $verb, '%string' => $rule['data']);
788
        foreach ($val as $subval) {
789
          $test = !preg_match($pattern, $subval);
790
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
791 85ad3d82 Assos Assos
        }
792 5c451ca3 Assos Assos
      }
793
      return $errors;
794
    case 'pattern':
795
      $pattern = preg_quote($rule['data'], '/');
796
      $pattern = str_replace('@', '[a-zA-Z]', $pattern);
797
      $pattern = str_replace('#', '[0-9]', $pattern);
798
      $pattern = '(' . $pattern . ')';
799
      // Un-escape "|" operator.
800
      $pattern = preg_replace('/(\\s*)(\\\\)(\\|)(\\s*)/', ')|(', $pattern);
801
      foreach ($items as $key => $val) {
802
        foreach ($val as $subval) {
803
          $test = !preg_match('/^(' . $pattern . ')$/', $subval);
804
          _webform_validation_test($errors, $key, $rule, $test);
805 85ad3d82 Assos Assos
        }
806 5c451ca3 Assos Assos
      }
807
      return $errors;
808
    case 'regex':
809
    case 'regexi':
810
      mb_regex_encoding('UTF-8');
811
      $regex = $rule['data'];
812
      foreach ($items as $key => $val) {
813
        foreach ($val as $subval) {
814
          if ($validator_name === 'regexi') {
815
            $match = (bool) mb_eregi($regex, $subval);
816 a2baadd1 Assos Assos
          }
817 5c451ca3 Assos Assos
          else {
818
            $match = (bool) mb_ereg($regex, $subval);
819 a2baadd1 Assos Assos
          }
820 5c451ca3 Assos Assos
          $test = !$match;
821
          _webform_validation_test($errors, $key, $rule, $test);
822 85ad3d82 Assos Assos
        }
823 5c451ca3 Assos Assos
      }
824
      return $errors;
825
    case 'must_be_empty':
826
      foreach ($items as $key => $val) {
827
        if (count($val) !== 0) {
828
          $errors[$key] = t('%item does not contain the correct data.', array('%item' => $components[$key]['name']));
829 85ad3d82 Assos Assos
        }
830 5c451ca3 Assos Assos
      }
831
      return $errors;
832
    case 'blacklist':
833
      $blacklist = preg_quote($rule['data'], '/');
834
      $blacklist = explode(',', $blacklist);
835
      $blacklist = array_map('trim', $blacklist);
836
      $blacklist = '/\b(' . implode('|', $blacklist) . ')\b/i';
837
      foreach ($items as $key => $val) {
838
        foreach ($val as $subval) {
839
          $test = preg_match($blacklist, $subval);
840
          _webform_validation_test($errors, $key, $rule, $test);
841 85ad3d82 Assos Assos
        }
842 5c451ca3 Assos Assos
      }
843
      return $errors;
844
    case 'username':
845
      foreach ($items as $key => $val) {
846
        $error_strings = array(
847
          'regular' => 'The %item field does not match an active username.',
848
          'negated' => 'The %item field matches an active username.',
849
        );
850
        $error_vars = array('%item' => $components[$key]['name']);
851
        foreach ($val as $subval) {
852
          $test = !db_query_range('SELECT 1 FROM {users} WHERE name = :username AND status = 1', 0, 1, array(':username' => $subval))->fetchField();
853
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
854 85ad3d82 Assos Assos
        }
855 5c451ca3 Assos Assos
      }
856
      return $errors;
857
    case 'valid_url':
858
      foreach ($items as $key => $val) {
859
        $error_strings = array(
860
          'regular' => '%item does not appear to be a valid URL.',
861
          'negated' => '%item must not be a valid URL.',
862
        );
863
        $error_vars = array('%item' => $components[$key]['name']);
864
        foreach ($val as $subval) {
865
          $test = !valid_url($subval, TRUE);
866
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
867 85ad3d82 Assos Assos
        }
868 5c451ca3 Assos Assos
      }
869
      return $errors;
870
    case 'email_verify':
871
      if (module_exists('email_verify')) {
872 85ad3d82 Assos Assos
        foreach ($items as $key => $val) {
873 a2baadd1 Assos Assos
          foreach ($val as $subval) {
874 5c451ca3 Assos Assos
            $error = email_verify_check($subval);
875
            if ($error) {
876
              $errors[$key] = $error;
877 85ad3d82 Assos Assos
            }
878
          }
879
        }
880 5c451ca3 Assos Assos
      }
881
      return $errors;
882 85ad3d82 Assos Assos
  }
883
}
884
885 5c451ca3 Assos Assos
/**
886
 * Return an array of the names of the components in a validation rule.
887
 *
888
 * @param $rule array
889
 *   Array of information about a validation rule.
890
 *
891
 * @return array
892
 *   Array of the filtered names of the components in $rule.
893
 */
894
function _webform_validation_get_names_of_rule_components(array $rule) {
895
  $names = array();
896
  foreach ($rule['components'] as $component) {
897
    $names[] = _webform_filter_xss($component['name']);
898
  }
899
  return $names;
900
}
901
902 85ad3d82 Assos Assos
/**
903
 * Helper function to negate validation rules as needed and create the correct error message.
904
 */
905
function _webform_validation_test(&$errors, $key, $rule, $test, array $error_strings = NULL, array $error_vars = NULL) {
906
  $rule['negate'] = !empty($rule['negate']);
907
  if ($rule['negate']) {
908
    $test = !$test;
909
  }
910
  if ($test) {
911
    if ($error_strings) {
912
      $error = t($error_strings[$rule['negate'] ? 'negated' : 'regular'], $error_vars);
913
    }
914
    else {
915
      $error = _webform_validation_i18n_error_message($rule);
916
    }
917
    $errors[$key] = $error;
918
  }
919
}
920
921
/**
922
 * Count the number of words in a value.
923 a2baadd1 Assos Assos
 *
924
 * Strip HTML first.
925 85ad3d82 Assos Assos
 */
926
function _webform_validation_count_words($val) {
927 a2baadd1 Assos Assos
  $val = _webform_validation_flatten_array($val);
928
929
  $val = strip_tags($val);
930
  // Replace entities representing spaces with actual spaces.
931
  $val = str_replace('&nbsp;', ' ', $val);
932
  $val = str_replace('&#160;', ' ', $val);
933
934 85ad3d82 Assos Assos
  return str_word_count($val);
935
}
936
937
/**
938 a2baadd1 Assos Assos
 * Helper function to deal with submitted values that are arrays (for example, multiple select component).
939 85ad3d82 Assos Assos
 * We flatten the array as a comma-separated list to do the comparison.
940
 */
941
function _webform_validation_flatten_array($val) {
942
  if (is_array($val)) {
943
    $arr = array_filter($val, '_webform_validation_check_false');
944
    return implode(',', $arr);
945
  }
946
  return $val;
947
}
948
949
/**
950 5c451ca3 Assos Assos
 * Get a list of validator definitions.
951
 *
952
 * @return
953
 *   Array of information about validators.
954 85ad3d82 Assos Assos
 */
955
function webform_validation_get_validators() {
956
  $validators = module_invoke_all('webform_validation_validators');
957 5c451ca3 Assos Assos
  // Let modules use hook_webform_validator_alter($validators) to change validator settings.
958 85ad3d82 Assos Assos
  drupal_alter('webform_validator', $validators);
959 5c451ca3 Assos Assos
960
  // Remove entries for which only partial information exists.
961
  foreach ($validators as $validator_key => $validator_info) {
962
    if (!isset($validator_info['name']) || !isset($validator_info['component_types'])) {
963
      unset($validators[$validator_key]);
964
    }
965
  }
966
967 85ad3d82 Assos Assos
  return $validators;
968
}
969
970
/**
971 5c451ca3 Assos Assos
 * Return an array of the names of the validators.
972
 *
973
 * @return array
974
 *   Array of with keys being validator IDs and values validator names.
975 85ad3d82 Assos Assos
 */
976
function webform_validation_get_validators_selection() {
977
  $selection = array();
978
  $validators = webform_validation_get_validators();
979
  if ($validators) {
980
    foreach ($validators as $validator_key => $validator_info) {
981
      $selection[$validator_key] = $validator_info['name'];
982
    }
983
  }
984
  return $selection;
985
}
986
987
/**
988
 * Get a list of valid component types per validator, as defined via hook_webform_validation_validators().
989
 * If 'all' is specified, all available component types will be returned.
990
 */
991
function webform_validation_valid_component_types($validator) {
992
  $validators = webform_validation_get_validators();
993
  if ($info = $validators[$validator]) {
994
    $allowed_types = $info['component_types'];
995 5c451ca3 Assos Assos
    if (in_array('all', $allowed_types, TRUE)) {
996 85ad3d82 Assos Assos
      return array_keys(webform_components());
997
    }
998
    return $info['component_types'];
999
  }
1000
}
1001
1002
/**
1003 5c451ca3 Assos Assos
 * Return information about a specified validator.
1004
 *
1005
 * @param $validator_key string
1006
 *   The key of the validator to return information about.
1007
 *
1008
 * @return array
1009
 *   Array of information about the validator.
1010 85ad3d82 Assos Assos
 */
1011
function webform_validation_get_validator_info($validator_key) {
1012
  $validators = webform_validation_get_validators();
1013
  return $validators[$validator_key];
1014
}
1015
1016
/**
1017
 * Handle translatable error messages, if available
1018
 */
1019
function _webform_validation_i18n_error_message($rule) {
1020
  $rule['error_message'] = filter_xss($rule['error_message']);
1021 a2baadd1 Assos Assos
  if (module_exists('i18n')) {
1022
    return i18n_string('webform_validation:error_message:' . $rule['ruleid'] . ':message', $rule['error_message']);
1023 85ad3d82 Assos Assos
  }
1024
  return $rule['error_message'];
1025
}
1026
1027
/**
1028
 * Helper function used by array_filter to determine if a value was selected or not
1029
 */
1030
function _webform_validation_check_false($var) {
1031
  return $var !== FALSE && $var !== 0;
1032
}
1033
1034
/**
1035
 * Process the numeric value validation range that was provided in the numeric validator options
1036
 */
1037
function _webform_numeric_check_data($data) {
1038
  $range = array('min' => NULL, 'max' => NULL);
1039
  // if no value was specified, don't validate
1040
  if ($data == '') {
1041
    return $range;
1042
  }
1043
1044
  // If only one numeric value was specified, this is the min value
1045
  if (is_numeric($data)) {
1046
    $range['min'] = (float) $data;
1047
  }
1048
1049
  if (strpos($data, '|') !== FALSE) {
1050
    list($min, $max) = explode('|', $data);
1051
    if ($min != '' && is_numeric($min)) {
1052
      $range['min'] = (float) $min;
1053
    }
1054
    if ($max != '' && is_numeric($max)) {
1055
      $range['max'] = (float) $max;
1056
    }
1057
  }
1058
  return $range;
1059
}