123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598 |
- <?php
- /**
- * @file
- * Provides validation functionality and hooks
- */
- /**
- * Implements hook_webform_validation_validators().
- *
- * This function returns an array of validators, in the validator key => options array form.
- * Possible options:
- * - name (required): name of the validator
- * - component types (required): defines which component types can be validated by this validator. Specify 'all' to allow all types
- * - custom_error (optional): define whether a user can specify a custom error message upon creating the validation rule.
- * - custom_data (optional): define whether custom data can be added to the validation rule
- * - min_components (optional): define the minimum number of components to be selected for creating a validation rule
- * - max_components (optional): define the maximum number of components to be selected for creating a validation rule
- * - description (optional): provide a descriptive explanation about the validator
- */
- function webform_validation_webform_validation_validators() {
- return array(
- 'numeric' => array(
- 'name' => "Numeric values",
- 'component_types' => array(
- 'textfield',
- 'hidden',
- ),
- 'custom_data' => array(
- 'label' => t('Specify numeric validation range'),
- 'description' => t('Optionally specify the minimum-maximum range to validate the user-entered numeric value against.') . ' ' . t('Usage') . ':'
- . 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 & less than or equal to 100'), t('"10|100": greater than or equal to 10 & less than or equal to 100'), t('"-100|-10": greater than or equal to -100 & less than or equal to -10')))),
- 'required' => FALSE,
- ),
- 'description' => t('Verifies that user-entered values are numeric, with the option to specify min and / or max values.'),
- ),
- 'min_length' => array(
- 'name' => "Minimum length",
- 'component_types' => array(
- 'textfield',
- 'textarea',
- 'email',
- 'hidden',
- ),
- 'custom_data' => array(
- 'label' => t('Minimum number of characters'),
- 'description' => t('Specify the minimum number of characters that have to be entered to pass validation.'),
- ),
- 'description' => t('Verifies that a user-entered value contains at least the specified number of characters'),
- ),
- 'max_length' => array(
- 'name' => "Maximum length",
- 'component_types' => array(
- 'textfield',
- 'textarea',
- 'email',
- 'hidden',
- ),
- 'custom_data' => array(
- 'label' => t('Maximum number of characters'),
- 'description' => t('Specify the maximum number of characters that can be entered to pass validation.'),
- ),
- 'description' => t('Verifies that a user-entered value contains at most the specified number of characters'),
- ),
- 'min_words' => array(
- 'name' => "Minimum number of words",
- 'component_types' => array(
- 'textfield',
- 'textarea',
- 'hidden',
- ),
- 'custom_data' => array(
- 'label' => t('Minimum number of words'),
- '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.')
- ),
- 'description' => t('Verifies that a user-entered value contains at least the specified number of words'),
- ),
- 'max_words' => array(
- 'name' => "Maximum number of words",
- 'component_types' => array(
- 'textfield',
- 'textarea',
- 'hidden',
- ),
- 'custom_data' => array(
- 'label' => t('Maximum number of words'),
- '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.')
- ),
- 'description' => t('Verifies that a user-entered value contains at most the specified number of words'),
- ),
- 'equal' => array(
- 'name' => "Equal values",
- 'component_types' => array(
- 'textfield',
- 'email',
- 'select',
- 'hidden',
- ),
- 'min_components' => 2,
- 'description' => t('Verifies that all specified components contain equal values'),
- ),
- 'unique' => array(
- 'name' => "Unique values",
- 'component_types' => array(
- 'textfield',
- 'email',
- 'select',
- 'hidden',
- ),
- 'min_components' => 2,
- 'description' => t('Verifies that all specified components contain unique values'),
- ),
- 'specific_value' => array(
- 'name' => "Specific value(s)",
- 'component_types' => array(
- 'select',
- 'textfield',
- 'textarea',
- 'email',
- 'hidden',
- ),
- 'custom_error' => TRUE,
- 'custom_data' => array(
- 'label' => t('(Key) value'),
- '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.'),
- ),
- 'max_components' => 1,
- 'description' => t('Verifies that the specified component contains a defined value'),
- ),
- 'oneoftwo' => array(
- 'name' => "Require at least one of two fields",
- 'component_types' => array(
- 'textfield',
- 'textarea',
- 'email',
- 'select',
- ),
- 'min_components' => 2,
- 'max_components' => 2,
- 'description' => t('Forces the user to specify / select at least one of two selected webform components'),
- ),
- 'oneofseveral' => array(
- 'name' => "Require at least one of several fields",
- 'component_types' => array(
- 'textfield',
- 'textarea',
- 'email',
- 'select',
- ),
- 'min_components' => 2,
- 'description' => t('Forces the user to specify / select at least one of several selected webform components'),
- ),
- 'select_min' => array(
- 'name' => "Minimum number of selections required",
- 'component_types' => array(
- 'select',
- ),
- 'custom_data' => array(
- 'label' => t('Minimum number of selections'),
- 'description' => t('Specify the minimum number of options a user should select.'),
- ),
- 'description' => t('Forces the user to select at least a defined number of options from the specified webform components'),
- ),
- 'select_max' => array(
- 'name' => "Maximum number of selections allowed",
- 'component_types' => array(
- 'select',
- ),
- 'custom_data' => array(
- 'label' => t('Maximum number of selections'),
- 'description' => t('Specify the maximum number of options a user can select.'),
- ),
- 'description' => t('Forces the user to select at most a defined number of options from the specified webform components'),
- ),
- 'select_exact' => array(
- 'name' => "Exact number of selections required",
- 'component_types' => array(
- 'select',
- ),
- 'custom_data' => array(
- 'label' => t('Number of selections'),
- 'description' => t('Specify how many options a user can select.'),
- ),
- 'description' => t('Forces the user to select exactly the defined number of options from the specified webform components'),
- ),
- 'plain_text' => array(
- 'name' => "Plain text (disallow tags)",
- 'component_types' => array(
- 'textfield',
- 'textarea',
- 'email',
- 'hidden',
- ),
- 'description' => t("Verifies that user-entered data doesn't contain HTML tags"),
- ),
- 'regex' => array(
- 'name' => "Regular expression",
- 'component_types' => array(
- 'textfield',
- 'textarea',
- 'email',
- 'hidden',
- ),
- 'custom_error' => TRUE,
- 'custom_data' => array(
- 'label' => t('Regex code'),
- 'description' => t('Specify regex code to validate the user input against.'),
- ),
- 'description' => t("Validates user-entered text against a specified regular expression. Note: don't include delimiters such as /."),
- ),
- 'must_be_empty' => array(
- 'name' => "Must be empty",
- 'component_types' => array(
- 'textfield',
- 'hidden',
- ),
- 'description' => t('Verifies that a specified textfield remains empty - Recommended use case: used as an anti-spam measure by hiding the element with CSS'),
- ),
- 'blacklist' => array(
- 'name' => "Words blacklist",
- 'component_types' => array(
- 'textfield',
- 'textarea',
- 'email',
- 'hidden',
- ),
- 'custom_error' => TRUE,
- 'custom_data' => array(
- 'label' => t('Blacklisted words'),
- 'description' => t('Specify illegal words, seperated by commas. Make sure to escape reserved regex characters with an escape (\) character.'),
- ),
- 'description' => t("Validates that user-entered data doesn't contain any of the specified illegal words"),
- ),
- 'username' => array(
- 'name' => "Must match a username",
- 'component_types' => array(
- 'textfield',
- 'hidden',
- ),
- 'description' => t("Validates that user-entered data matches a username"),
- ),
- );
- }
- /**
- * Implements hook_webform_validation_validate().
- */
- function webform_validation_webform_validation_validate($validator_name, $items, $components, $rule) {
- if ($items) {
- $errors = array();
- switch ($validator_name) {
- case 'numeric':
- $num_range = _webform_numeric_check_data($rule['data']);
- foreach ($items as $key => $val) {
- if ($val != '') {
- // first check if the value is numeric
- if (!is_numeric($val)) {
- $errors[$key] = t('%item is not numeric', array('%item' => $components[$key]['name']));
- }
- // now validate the entered numeric value against the validator range settings, if appropriate
- // a. validate min & max
- if (isset($num_range['min']) && isset($num_range['max'])) {
- // validate the min - max range
- if ($val < $num_range['min'] || $val > $num_range['max']) {
- $errors[$key] = t('%item is not within the allowed range %range', array('%item' => $components[$key]['name'], '%range' => str_replace('|' , ' - ', $rule['data'])));
- }
- }
- else {
- // b. validate min
- if (isset($num_range['min'])) {
- if ($val < $num_range['min']) {
- $errors[$key] = t('%item should be greater than or equal to %val', array('%item' => $components[$key]['name'], '%val' => $num_range['min']));
- }
- }
- // c. validate max
- if (isset($num_range['max'])) {
- if ($val > $num_range['max']) {
- $errors[$key] = t('%item should be less than or equal to %val', array('%item' => $components[$key]['name'], '%val' => $num_range['max']));
- }
- }
- }
- }
- }
- return $errors;
- break;
- case 'min_length':
- $min_length = $rule['data'];
- foreach ($items as $key => $val) {
- if ($val != '' && (drupal_strlen($val) < $min_length)) {
- $errors[$key] = t('%item needs to be at least %num characters long', array('%item' => $components[$key]['name'], '%num' => $min_length));
- }
- }
- return $errors;
- break;
- case 'max_length':
- $max_length = $rule['data'];
- foreach ($items as $key => $val) {
- if ($val != '' && (drupal_strlen($val) > $max_length)) {
- $errors[$key] = t('%item can be maximum %num characters long', array('%item' => $components[$key]['name'], '%num' => $max_length));
- }
- }
- return $errors;
- break;
- case 'min_words':
- $min_words = $rule['data'];
- foreach ($items as $key => $val) {
- if ($val != '' && (count(preg_split("/[\s]+/", trim($val))) < $min_words)) {
- $error = format_plural($min_words, '%item needs to be at least 1 word long', '%item needs to be at least @count words long', array('%item' => $components[$key]['name']));
- $errors[$key] = $error;
- }
- }
- return $errors;
- break;
- case 'max_words':
- $max_words = $rule['data'];
- foreach ($items as $key => $val) {
- if ($val != '' && (count(preg_split("/[\s]+/", trim($val))) > $max_words)) {
- $error = format_plural($max_words, '%item can be maximum 1 word long', '%item can be maximum @count words long', array('%item' => $components[$key]['name']));
- $errors[$key] = $error;
- }
- }
- return $errors;
- break;
- case "equal":
- $first_entry_key = key($items);
- $first_entry = array_shift($items);
- $first_entry = _webform_validation_flatten_array($first_entry); // flatten in case of array
- // now check if following components equal the first one
- foreach ($items as $key => $val) {
- $val = _webform_validation_flatten_array($val); // flatten in case of array
- if ($val !== $first_entry) {
- $errors[$key] = t('%item_checked does not match %item_first', array('%item_checked' => $components[$key]['name'], '%item_first' => $components[$first_entry_key]['name']));
- }
- }
- return $errors;
- break;
- case "unique":
- foreach ($items as $key => $val) {
- if (is_array($val)) {
- // make sure to flatten arrays first
- $items[$key] = _webform_validation_flatten_array($val);
- }
- if (empty($items[$key])) {
- // items without a value selected shouldn't be validated
- unset($items[$key]);
- }
- }
- // now we count how many times each value appears, and find out which values appear more than once
- $items_count = array_count_values(array_map('strtolower', array_map('trim', $items)));
- $doubles = array_filter($items_count, create_function('$x', 'return $x > 1;'));
- foreach ($items as $key => $val) {
- if (in_array(strtolower($val), array_keys($doubles))) {
- $errors[$key] = t('The value of %item is not unique', array('%item' => $components[$key]['name']));
- }
- }
- return $errors;
- break;
- case "specific_value":
- $specific_values = explode(',', $rule['data']);
- $specific_values = array_map('trim', $specific_values);
- foreach ($items as $key => $val) {
- if (is_array($val)) {
- $val = _webform_validation_flatten_array($val);
- }
- if (!in_array($val, $specific_values)) {
- $errors[$key] = _webform_validation_i18n_error_message($rule);
- }
- }
- return $errors;
- break;
- case "oneoftwo":
- // $components should have 2 items
- $keys = array_keys($items);
- $item1 = array_shift($keys);
- $item2 = array_shift($keys);
- $entry1 = _webform_validation_flatten_array($items[$item1]);
- $entry2 = _webform_validation_flatten_array($items[$item2]);
- if (empty($entry1) && empty($entry2)) {
- return array($item1 => t('You have to specify %item1 or %item2 (or both)', array('%item1' => $components[$item1]['name'], '%item2' => $components[$item2]['name'])));
- }
- break;
- case "oneofseveral":
- foreach ($items as $key => $val) {
- if (is_array($val)) {
- // make sure to flatten arrays first
- $items[$key] = _webform_validation_flatten_array($val);
- }
- }
- // $components should have at least one of several items
- if (count(array_filter($items)) < 1) {
- $keys = array_keys($items);
- $names = array();
- foreach ($keys as $value) {
- $names[] = $components[$value]['name'];
- }
- return array($keys[0] => t('You have to specify at least one of these items:') . theme('item_list', array('items' => $names)));
- }
- break;
- case "select_min":
- $min_selections = $rule['data'];
- foreach ($items as $key => $val) {
- if (is_array($val) && (count(array_filter($val, '_webform_validation_check_false')) < $min_selections)) {
- $errors[$key] = t('Please select at least %num options for %item', array('%num' => $min_selections, '%item' => $components[$key]['name']));
- }
- }
- return $errors;
- break;
- case "select_max":
- $max_selections = $rule['data'];
- foreach ($items as $key => $val) {
- if (is_array($val) && (count(array_filter($val, '_webform_validation_check_false')) > $max_selections)) {
- $errors[$key] = t('Please select maximum %num options for %item', array('%num' => $max_selections, '%item' => $components[$key]['name']));
- }
- }
- return $errors;
- break;
- case "select_exact":
- $allowed_selections = $rule['data'];
- foreach ($items as $key => $val) {
- if (is_array($val) && (count(array_filter($val, '_webform_validation_check_false')) != $allowed_selections)) {
- $errors[$key] = t('Please select %num options for %item', array('%num' => $allowed_selections, '%item' => $components[$key]['name']));
- }
- }
- return $errors;
- break;
- case "plain_text":
- foreach ($items as $key => $val) {
- if ($val != '' && (strcmp($val, strip_tags($val)))) {
- $errors[$key] = t('%item only allows the use of plain text', array('%item' => $components[$key]['name']));
- }
- }
- return $errors;
- break;
- case "regex":
- mb_regex_encoding('UTF-8');
- $regex = $rule['data'];
- foreach ($items as $key => $val) {
- if ($val != '' && (!mb_ereg("$regex", $val))) {
- $errors[$key] = _webform_validation_i18n_error_message($rule);
- }
- }
- return $errors;
- break;
- case 'must_be_empty':
- foreach ($items as $key => $val) {
- if ($val) {
- $errors[$key] = t('%item does not contain the correct data', array('%item' => $components[$key]['name']));
- }
- }
- return $errors;
- break;
- case "blacklist":
- $blacklist = explode(',', $rule['data']);
- $blacklist = array_map('trim', $blacklist);
- $blacklist_regex = implode('|', $blacklist);
- foreach ($items as $key => $val) {
- if ($val != '' && (preg_match("/$blacklist_regex/i", $val))) {
- $errors[$key] = _webform_validation_i18n_error_message($rule);
- }
- }
- return $errors;
- break;
- case "username":
- foreach ($items as $key => $val) {
- // load user - if username does not match or status 0 throw error
- if ($val != '') {
- if (!db_query("SELECT COUNT(*) FROM {users} WHERE name = :username AND status = 1", array(':username' => $val))->fetchField()) {
- // Username doesn't exist
- $errors[$key] = t('The %item field does not match an active username.', array('%item' => $components[$key]['name']));
- }
- }
- }
- return $errors;
- break;
- }
- }
- }
- /**
- * Helper function to deal with submitted values that are arrays (e.g. multiple select component)
- * We flatten the array as a comma-separated list to do the comparison.
- */
- function _webform_validation_flatten_array($val) {
- if (is_array($val)) {
- $arr = array_filter($val, '_webform_validation_check_false');
- return implode(',', $arr);
- }
- return $val;
- }
- /**
- * Get a list of validator definitions
- */
- function webform_validation_get_validators() {
- $validators = module_invoke_all("webform_validation_validators");
- // let modules use hook_webform_validator_alter($validators) to change validator settings
- drupal_alter('webform_validator', $validators);
- return $validators;
- }
- /**
- * @todo Please document this function.
- * @see http://drupal.org/node/1354
- */
- function webform_validation_get_validators_selection() {
- $selection = array();
- $validators = webform_validation_get_validators();
- if ($validators) {
- foreach ($validators as $validator_key => $validator_info) {
- $selection[$validator_key] = $validator_info['name'];
- }
- }
- return $selection;
- }
- /**
- * Get a list of valid component types per validator, as defined via hook_webform_validation_validators().
- * If 'all' is specified, all available component types will be returned.
- */
- function webform_validation_valid_component_types($validator) {
- $validators = webform_validation_get_validators();
- if ($info = $validators[$validator]) {
- $allowed_types = $info['component_types'];
- if (_webform_validation_all_allowed($allowed_types)) {
- return array_keys(webform_components());
- }
- return $info['component_types'];
- }
- }
- /**
- * Helper function to check whether all components are allowed to be used for a certain validator
- */
- function _webform_validation_all_allowed($allowed) {
- if ($allowed) {
- foreach ($allowed as $type) {
- if ($type == "all") {
- return TRUE;
- }
- }
- }
- return FALSE;
- }
- /**
- * @todo Please document this function.
- * @see http://drupal.org/node/1354
- */
- function webform_validation_get_validator_info($validator_key) {
- $validators = webform_validation_get_validators();
- return $validators[$validator_key];
- }
- /**
- * Handle translatable error messages, if available
- */
- function _webform_validation_i18n_error_message($rule) {
- $rule['error_message'] = filter_xss($rule['error_message']);
- if (module_exists('i18nstrings')) {
- return i18nstrings('webform_validation:error_message:' . $rule['ruleid'] . ':message', $rule['error_message']);
- }
- return $rule['error_message'];
- }
- /**
- * Helper function used by array_filter to determine if a value was selected or not
- */
- function _webform_validation_check_false($var) {
- return $var !== FALSE && $var !== 0;
- }
- /**
- * Process the numeric value validation range that was provided in the numeric validator options
- */
- function _webform_numeric_check_data($data) {
- $range = array('min' => NULL, 'max' => NULL);
- // if no value was specified, don't validate
- if ($data == '') {
- return $range;
- }
- // If only one numeric value was specified, this is the min value
- if (is_numeric($data)) {
- $range['min'] = (int) $data;
- }
- if (strpos($data, '|') !== FALSE) {
- list($min, $max) = explode('|', $data);
- if ($min != '' && is_numeric($min)) {
- $range['min'] = (int) $min;
- }
- if ($max != '' && is_numeric($max)) {
- $range['max'] = (int) $max;
- }
- }
- return $range;
- }
|