webform_validation.validators.inc in Webform Validation 6
Same filename and directory in other branches
Provides validation functionality and hooks
File
webform_validation.validators.incView source
<?php
/**
* @file
* Provides validation functionality and hooks
*/
/**
* Implementation of 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(
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'),
),
'not_default_value' => array(
'name' => "Not default value",
'component_types' => array(
'select',
'textfield',
'textarea',
'email',
'hidden',
),
'custom_error' => TRUE,
'description' => t('Verifies that the user-entered value is not the default value for that component.'),
),
'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"),
),
);
}
/**
* Implementation of hook_webform_validation_validate().
*/
function webform_validation_webform_validation_validate($validator_name, $items, $components, $rule) {
/**
* Preparation for select_* validation rules and the handling of key 0 for select components
*
* Only webform 3 handles a select list with key 0 properly.
* The array_filter callback function is only loaded for webform 3 to make sure
* it works perfectly for webform 3, and works in all cases except with key 0 for webform 2
*/
$version = webform_validation_check_version();
$check_false_callback = $version == 3 ? '_webform_validation_check_false' : NULL;
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 "not_default_value":
foreach ($items as $key => $val) {
if (is_array($val)) {
$val = _webform_validation_flatten_array($val);
}
if ($val == $components[$key]['value']) {
$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[] = _webform_filter_xss($components[$value]['name']);
}
return array(
$keys[0] => t('You have to specify at least one of these items:') . theme('item_list', $names),
);
}
break;
case "select_min":
$min_selections = $rule['data'];
foreach ($items as $key => $val) {
$val = is_array($val) ? $val : array(
$val,
);
// workaround for single select components
$selected_values = isset($check_false_callback) ? array_filter($val, $check_false_callback) : array_filter($val);
if (is_array($val) && count($selected_values) < $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) {
$val = is_array($val) ? $val : array(
$val,
);
// workaround for single select components
$selected_values = isset($check_false_callback) ? array_filter($val, $check_false_callback) : array_filter($val);
if (is_array($val) && count($selected_values) > $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) {
$selected_values = isset($check_false_callback) ? array_filter($val, $check_false_callback) : array_filter($val);
if (is_array($val) && count($selected_values) != $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_result(db_query("SELECT COUNT(*) FROM {users} WHERE name = '%s' AND status = 1", $val))) {
// 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;
}
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)) {
$version = webform_validation_check_version();
if ($version == 2) {
return webform_load_components();
}
else {
return array_keys(webform_components());
}
return $componentslist_function();
}
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;
}
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;
}
/**
* 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;
}
Functions
Name | Description |
---|---|
webform_validation_get_validators | Get a list of validator definitions |
webform_validation_get_validators_selection | |
webform_validation_get_validator_info | |
webform_validation_valid_component_types | 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. |
webform_validation_webform_validation_validate | Implementation of hook_webform_validation_validate(). |
webform_validation_webform_validation_validators | Implementation of hook_webform_validation_validators(). |
_webform_numeric_check_data | Process the numeric value validation range that was provided in the numeric validator options |
_webform_validation_all_allowed | Helper function to check whether all components are allowed to be used for a certain validator |
_webform_validation_check_false | Helper function used by array_filter to determine if a value was selected or not |
_webform_validation_flatten_array | 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. |
_webform_validation_i18n_error_message | Handle translatable error messages, if available |