clientside_validation_field_validation.module in Clientside Validation 7
Same filename and directory in other branches
Add clientside validation support for Field Validation
File
clientside_validation_field_validation/clientside_validation_field_validation.moduleView source
<?php
/**
* @file
* Add clientside validation support for Field Validation
*/
/**
* Implements hook_clientside_validation_form_alter().
*/
function clientside_validation_field_validation_clientside_validation_form_alter(&$form, &$form_state, &$js_rules) {
$field_rules = array();
clientside_validation_field_validation_find_rules($form, $field_rules);
if (!empty($field_rules)) {
$form['#attached']['js'][] = drupal_get_path('module', 'clientside_validation_field_validation') . '/clientside_validation_field_validation.js';
if (module_exists('js')) {
// If JS module is enabled ensure the token for our module is available.
js_get_token('clientside_validation_field_validation', 'validate');
}
foreach ($field_rules as $rule) {
$element =& $form;
// Field validation 1.x
if (function_exists('field_validation_get_bundle_rules')) {
foreach ($rule['field']['#parents'] as $parent) {
$element =& $element[$parent];
}
clientside_validation_field_validation_after_build_recurse($form['#id'], $element, $form_state, $rule['rule'], $js_rules);
}
else {
$path = clientside_validation_array_key_path($rule['rule']['field_name'], $element);
if ($path !== FALSE) {
$element =& clientside_validation_array_path($form, $path);
clientside_validation_field_validation_after_build_recurse($form['#id'], $element, $form_state, $rule['rule'], $js_rules);
if (isset($element['#type']) && $element['#type'] == 'select') {
clientside_validation_field_validation_regular($form['#id'], $element, $rule['rule'], $js_rules);
}
}
}
}
}
}
/**
* Search for a key in an array, returning a path to the entry.
*
* @param $needle
* A key to look for.
* @param $haystack
* A keyed array.
* @param $forbidden
* A list of keys to ignore.
* @param $path
* The intermediate path. Internal use only.
* @param $depth
* The depth of the array to search.
* @param $current_depth
* The current depth searched. Internal use only.
* @return
* The path to the parent of the first occurrence of the key, represented as an array where entries are consecutive keys.
*/
function clientside_validation_array_key_path($needle, $haystack, $forbidden = array(), $path = array(), $depth = 3, $current_depth = 0) {
foreach ($haystack as $key => $val) {
if (in_array($key, $forbidden)) {
continue;
}
if (is_array($val) && $current_depth < $depth && is_array($sub = clientside_validation_array_key_path($needle, $val, $forbidden, array_merge($path, (array) $key), $depth, $current_depth + 1))) {
return $sub;
}
elseif ($key === $needle) {
return array_merge($path, (array) $key);
}
}
return FALSE;
}
/**
* Given a path, return a reference to the array entry.
*
* @param $array
* A keyed array.
* @param $path
* An array path, represented as an array where entries are consecutive keys.
* @return
* A reference to the entry that corresponds to the given path.
*/
function &clientside_validation_array_path(&$array, $path) {
$offset =& $array;
if ($path) {
foreach ($path as $index) {
$offset =& $offset[$index];
}
}
return $offset;
}
function clientside_validation_field_validation_find_rules($form, &$field_rules) {
if (isset($form['#entity_type']) && isset($form['#bundle'])) {
$rules = array();
if (function_exists('field_validation_get_bundle_rules')) {
$rules = field_validation_get_bundle_rules($form['#entity_type'], $form['#bundle']);
}
else {
ctools_include('export');
$rules = (array) ctools_export_load_object('field_validation_rule', 'conditions', array(
'entity_type' => $form['#entity_type'],
'bundle' => $form['#bundle'],
));
}
if (!empty($rules)) {
foreach ($rules as $rule) {
$rule = (array) $rule;
if (isset($form[$rule['field_name']])) {
$field_rules[] = array(
'rule' => $rule,
'field' => $form[$rule['field_name']],
);
}
}
}
}
foreach (element_children($form) as $child) {
clientside_validation_field_validation_find_rules($form[$child], $field_rules);
}
}
function clientside_validation_field_validation_after_build_recurse($form_id, &$form, &$form_state, $rule, &$js_rules) {
if ($children = array_values(element_children($form))) {
foreach ($children as $index => $item) {
$element =& $form[$item];
$types = array(
'emailfield',
'phonefield',
'telfield',
'link_field',
'textfield',
'textarea',
'select',
'radio',
'checkbox',
'password',
'file',
'radios',
'checkboxes',
);
if (isset($element['#type']) && in_array($element['#type'], $types) && isset($element['#name'])) {
clientside_validation_field_validation_regular($form_id, $element, $rule, $js_rules);
}
clientside_validation_field_validation_after_build_recurse($form_id, $element, $form_state, $rule, $js_rules);
}
}
}
function clientside_validation_field_validation_regular($form_id, $element, $rule, &$js_rules) {
if (!empty($rule['disabled'])) {
return;
}
if (isset($rule['col']) && $rule['col'] !== '') {
$parent_last = end($element['#parents']);
reset($element['#parents']);
if ($element['#type'] !== 'select' && $parent_last !== FALSE && $parent_last !== $rule['col']) {
return;
}
elseif ($element['#type'] == 'select' && isset($element['#value_key']) && $element['#value_key'] !== $rule['col']) {
return;
}
}
$el_name = $element['#name'];
if (isset($element['#multiple']) && $element['#multiple']) {
$el_name .= '[]';
}
$el_title = $el_name;
if (isset($element['#title'])) {
$el_title = $element['#title'];
}
switch ($rule['validator']) {
case 'regex':
case 'field_validation_regex_validator':
$message = isset($rule['error_message']) && !empty($rule['error_message']) ? $rule['error_message'] : t('Invalid value for !name', array(
'!name' => variable_get('clientside_validation_prefix', '') . $el_title . variable_get('clientside_validation_suffix', ''),
));
// Field Validation 1.x and 2.x
$data = isset($rule['data']) ? $rule['data'] : $rule['settings']['data'];
_clientside_validation_set_regex($el_name, $el_title, $js_rules, $data, $message);
break;
case 'min_length':
$message = isset($rule['error_message']) ? $rule['error_message'] : t('!name field has a minimum length of !minl characters.', array(
'!name' => variable_get('clientside_validation_prefix', '') . $el_title . variable_get('clientside_validation_suffix', ''),
'!minl' => $rule['data'],
));
_clientside_validation_set_minmaxlength($el_name, $el_title, $rule['data'], '', $js_rules, $message);
break;
case 'max_length':
$message = isset($rule['error_message']) ? $rule['error_message'] : t('!name field has a maximum length of !maxl characters.', array(
'!name' => variable_get('clientside_validation_prefix', '') . $el_title . variable_get('clientside_validation_suffix', ''),
'!maxl' => $rule['data'],
));
_clientside_validation_set_minmaxlength($el_name, $el_title, '', $rule['data'], $js_rules, $message);
break;
// Field Validation 2.x
case 'field_validation_length_validator':
$message = '';
$rule['settings']['min'] = isset($rule['settings']['min']) ? $rule['settings']['min'] : '';
$rule['settings']['max'] = isset($rule['settings']['max']) ? $rule['settings']['max'] : '';
if (isset($rule['error_message'])) {
$message = $rule['error_message'];
}
else {
if (!empty($rule['settings']['min'])) {
$message = t('!name field has a minimum length of !minl characters.', array(
'!name' => variable_get('clientside_validation_prefix', '') . $el_title . variable_get('clientside_validation_suffix', ''),
'!minl' => $rule['settings']['min'],
));
}
if (!empty($rule['settings']['max'])) {
$message = empty($message) ? t('!name field has a maximum length of !maxl characters.', array(
'!name' => variable_get('clientside_validation_prefix', '') . $el_title . variable_get('clientside_validation_suffix', ''),
'!maxl' => $rule['settings']['max'],
)) : $message . ' ' . t('and a maximum length of !maxl characters', array(
'!maxl' => $rule['settings']['maxl'],
));
}
}
_clientside_validation_set_minmaxlength($el_name, $el_title, $rule['settings']['min'], $rule['settings']['max'], $js_rules, $message);
break;
case 'min_words':
$message = isset($rule['error_message']) && !empty($rule['error_message']) ? $rule['error_message'] : '';
_clientside_validation_set_minmax_words($el_name, $el_title, $rule['data'], '', $js_rules, $message);
break;
case 'max_words':
$message = isset($rule['error_message']) && !empty($rule['error_message']) ? $rule['error_message'] : '';
_clientside_validation_set_minmax_words($el_name, $el_title, '', $rule['data'], $js_rules, $message);
break;
// Field Validation 2.x
case 'field_validation_words_validator':
$message = '';
$rule['settings']['min'] = isset($rule['settings']['min']) ? $rule['settings']['min'] : '';
$rule['settings']['max'] = isset($rule['settings']['max']) ? $rule['settings']['max'] : '';
if (isset($rule['error_message'])) {
$message = $rule['error_message'];
}
else {
if (!empty($rule['settings']['min'])) {
$message = t('!name field has a minimum length of !minl words.', array(
'!name' => variable_get('clientside_validation_prefix', '') . $el_title . variable_get('clientside_validation_suffix', ''),
'!minl' => $rule['settings']['min'],
));
}
if (!empty($rule['settings']['max'])) {
$message = empty($message) ? t('!name field has a maximum length of !maxl words.', array(
'!name' => variable_get('clientside_validation_prefix', '') . $el_title . variable_get('clientside_validation_suffix', ''),
'!maxl' => $rule['settings']['max'],
)) : $message . ' ' . t('and a maximum length of !maxl words', array(
'!maxl' => $rule['settings']['maxl'],
));
}
}
_clientside_validation_set_minmax_words($el_name, $el_title, $rule['settings']['min'], $rule['settings']['max'], $js_rules, $message);
break;
case 'plain_text':
case 'field_validation_plain_text_validator':
$message = isset($rule['error_message']) && !empty($rule['error_message']) ? $rule['error_message'] : '';
// Field Validation 1.x and 2.x
$data = isset($rule['data']) ? $rule['data'] : '';
_clientside_validation_set_plain_text($el_name, $el_title, $data, $js_rules, $message);
break;
case 'must_be_empty':
case 'field_validation_must_be_empty_validator':
$message = isset($rule['error_message']) && !empty($rule['error_message']) ? $rule['error_message'] : '';
_clientside_validation_set_specific_value($el_name, $el_title, '', $js_rules, $message);
break;
case 'blacklist':
case 'field_validation_blacklist_validator':
$message = isset($rule['error_message']) && !empty($rule['error_message']) ? $rule['error_message'] : '';
// Field Validation 1.x
if (isset($rule['data'])) {
$blacklist = explode(',', $rule['data']);
}
else {
$blacklist = explode(',', $rule['settings']['data']);
}
$blacklist = array_map('trim', $blacklist);
_clientside_validation_set_blacklist($el_name, $el_title, $blacklist, $js_rules, $message);
break;
case 'numeric':
case 'field_validation_numeric2_validator':
$range = array(
'min' => NULL,
'max' => NULL,
);
$message = isset($rule['error_message']) && !empty($rule['error_message']) ? $rule['error_message'] : '';
// Field Validation 1.x
if (isset($rule['data'])) {
$data = $rule['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;
}
}
else {
if ($data != '' && is_numeric($data)) {
$range['min'] = (int) $data;
}
}
}
else {
$range['min'] = isset($rule['settings']['min']) ? $rule['settings']['min'] : '';
$range['max'] = isset($rule['settings']['max']) ? $rule['settings']['max'] : '';
}
if (!empty($range['min']) || !empty($range['max'])) {
_clientside_validation_set_minmax($el_name, $el_title, $range['min'], $range['max'], $js_rules, $message);
}
else {
_clientside_validation_set_number_decimal($el_name, $el_title, '.', $js_rules, $message);
}
break;
case 'specific_value':
case 'field_validation_specific_value_validator':
// Field Validation 1.x and 2.x
$value = isset($rule['data']) ? explode(',', $rule['data']) : explode(',', $rule['settings']['data']);
$message = isset($rule['error_message']) && !empty($rule['error_message']) ? $rule['error_message'] : '';
_clientside_validation_set_specific_value($el_name, $el_title, $value, $js_rules, $message);
break;
case 'select_min':
$message = isset($rule['error_message']) && !empty($rule['error_message']) ? $rule['error_message'] : '';
_clientside_validation_set_minmaxlength_select($el_name, $el_title, $rule['data'], '', $js_rules, $message);
break;
case 'select_max':
$message = isset($rule['error_message']) && !empty($rule['error_message']) ? $rule['error_message'] : '';
_clientside_validation_set_minmaxlength_select($el_name, $el_title, '', $rule['data'], $js_rules, $message);
break;
case 'select_exact':
$message = isset($rule['error_message']) && !empty($rule['error_message']) ? $rule['error_message'] : '';
_clientside_validation_set_minmaxlength_select($el_name, $el_title, $rule['data'], $rule['data'], $js_rules, $message);
break;
case 'field_validation_number_of_selections_validator':
$message = '';
$rule['settings']['min'] = isset($rule['settings']['min']) ? $rule['settings']['min'] : '';
$rule['settings']['max'] = isset($rule['settings']['max']) ? $rule['settings']['max'] : '';
if (isset($rule['error_message'])) {
$message = $rule['error_message'];
}
else {
if (!empty($rule['settings']['min'])) {
$message = t('You must select a minimum of !minl items for !name.', array(
'!name' => _clientside_validation_set_title($el_title),
'!minl' => $rule['settings']['min'],
));
}
if (!empty($rule['settings']['max'])) {
$message = empty($message) ? t('You can\'t select more than !maxl items for !name.', array(
'!name' => _clientside_validation_set_title($el_title),
'!maxl' => $rule['settings']['max'],
)) : $message . ' ' . t('and a maximum of !maxl', array(
'!maxl' => $rule['settings']['maxl'],
));
}
}
_clientside_validation_set_minmaxlength_select($el_name, $el_title, $rule['settings']['min'], $rule['settings']['max'], $js_rules, $message);
break;
case 'field_validation_email_validator':
$message = isset($rule['error_message']) ? $rule['error_message'] : t('!name is not a valid email address', array(
'!name' => _clientside_validation_set_title($el_title),
));
_clientside_validation_set_email($el_name, $el_title, $js_rules, $message);
break;
case 'field_validation_url_validator':
$message = isset($rule['error_message']) ? $rule['error_message'] : t('!name is not a valid url', array(
'!name' => _clientside_validation_set_title($el_title),
));
_clientside_validation_set_url($el_name, $el_title, $js_rules, $message);
break;
default:
$message = isset($rule['error_message']) && !empty($rule['error_message']) ? $rule['error_message'] : t('Invalid value for !name', array(
'!name' => _clientside_validation_set_title($el_title),
));
$context = array(
'type' => 'field_validation',
'rule' => $rule,
'message' => $message,
);
if (isset($element['#entity_type'])) {
// Add an ajax based validation by default - but allow modules to alter
// it using hook_clientside_validation_rule_alter().
list($entity_id, $revision_id, $bundle) = entity_extract_ids($element['#entity_type'], $element['#entity']);
$js_rules[$el_name]['fieldValidationAjax'][$rule['name']] = array(
'language' => $element['#language'],
'delta' => $element['#delta'],
'entity_id' => $entity_id,
'revision_id' => $revision_id,
);
$variables = array(
'message' => $message,
'error_type' => 'fieldValidationAjax',
'element_name' => $el_name,
);
$js_rules[$el_name]['messages']['fieldValidationAjax'] = theme('clientside_error', $variables);
}
drupal_alter('clientside_validation_rule', $js_rules, $element, $context);
break;
}
}
/**
* Implements hook_menu().
*/
function clientside_validation_field_validation_menu() {
$items['js/clientside_validation_field_validation/validate'] = array(
'title' => 'Clientside validation ajax callback to run a field_validation',
'page callback' => '_clientside_validation_field_validation_ajax',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_js_info().
*/
function clientside_validation_field_validation_js_info() {
// Build dependency list.
ctools_include('plugins');
$plugins = ctools_get_plugins('field_validation', 'validator');
$module_data = system_rebuild_module_data();
$dependencies = array(
'field' => 'field',
'ctools' => 'ctools',
'clientside_validation' => 'clientside_validation',
);
foreach ($plugins as $plugin) {
if (!isset($dependencies[$plugin['module']])) {
$dependencies[$plugin['module']] = $plugin['module'];
if (isset($module_data[$plugin['module']]->requires)) {
$dependencies += array_combine(array_keys($module_data[$plugin['module']]->requires), array_keys($module_data[$plugin['module']]->requires));
}
}
}
return array(
'validate' => array(
'callback function' => '_clientside_validation_field_validation_ajax',
'includes' => array(
'unicode',
'theme',
'path',
'menu',
),
'dependencies' => $dependencies,
'skip init' => TRUE,
'delivery callback' => 'drupal_json_output',
'bootstrap' => DRUPAL_BOOTSTRAP_PAGE_HEADER,
),
);
}
/**
* Validates a field using a field validation rule.
*/
function _clientside_validation_field_validation_ajax() {
$data = _clientside_validation_field_validation_ajax_get_value();
if (isset($data['value']) && isset($data['rules']) && is_array($data['rules'])) {
// Fetch the rules to use and ensure the names are save.
$rule_names = array_map('check_plain', array_keys($data['rules']));
// Use ctools export API to fetch the rules.
ctools_include('export');
ctools_include('plugins');
$rules = ctools_export_load_object('field_validation_rule', 'names', $rule_names);
$errors = array();
$value = filter_xss($data['value']);
foreach ($rules as $rule_name => $rule) {
if (isset($data['rules'][$rule_name])) {
$settings = $data['rules'][$rule_name];
_clientside_validation_field_validation($rule, $errors, $value, $settings['entity_id'], $settings['revision_id'], $settings['language'], $settings['delta']);
}
}
if (!empty($errors)) {
drupal_json_output(implode("\n", $errors));
return;
}
}
drupal_json_output(TRUE);
}
/**
* Fetches the data for the validation from the POST data.
*
* @return array
* The data to validate.
*/
function _clientside_validation_field_validation_ajax_get_value($data = NULL) {
if (is_null($data)) {
$data = $_POST;
}
if (isset($data['fieldValidationAjax'])) {
return $data['fieldValidationAjax'];
}
foreach ($data as $key => $sub_data) {
if (isset($data['fieldValidationAjax'])) {
return $data['fieldValidationAjax'];
}
if (is_array($sub_data)) {
if (($return = _clientside_validation_field_validation_ajax_get_value($sub_data)) && !is_null($return)) {
return $return;
}
}
}
return NULL;
}
/**
* Run validation rule on value.
*
* Similar to field_validation_field_attach_validate().
*
* @param object $rule
* The rule to handle.
* @param array $errors
* The array to fill with errors.
* @param mixed $value
* The value to validate.
* @param string $entity_id
* The entity id of the related entity.
* @param string $revision_id
* The revision id of the related entity.
* @param string $langcode
* The langcode to use.
* @param int $delta
* The delta to use.
*
* @see field_validation_field_attach_validate()
*/
function _clientside_validation_field_validation($rule, array &$errors, $value, $entity_id, $revision_id = NULL, $langcode = 'und', $delta = 0) {
$plugin = ctools_get_plugins('field_validation', 'validator', $rule->validator);
$class = ctools_plugin_get_class($plugin, 'handler');
if (empty($class)) {
$errors[] = t("Plugin '@validator' doesn't define a validator class.", array(
'@validator' => $rule->validator,
));
return;
}
if (!is_subclass_of($rule->validator, 'field_validation_validator')) {
$errors[] = t("Plugin '@validator' should extends 'field_validation_validator'.", array(
'@validator' => $rule->validator,
));
return;
}
$entity_type = $rule->entity_type;
$bundle = $rule->bundle;
$field_name = $rule->field_name;
$entity_info = entity_get_info($entity_type);
$field = field_info_field($field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle);
// Build dummy entity to handle.
// @TODO Load full entity if entity_id / revision id is available?.
$entity = array();
if (isset($entity_info['entity keys']['id'])) {
$entity[$entity_info['entity keys']['id']] = $entity_id;
}
if (isset($entity_info['entity keys']['bundle'])) {
$entity[$entity_info['entity keys']['bundle']] = $bundle;
}
if (isset($info['entity keys']['revision'])) {
$entity[$entity_info['entity keys']['revision']] = $revision_id;
}
// Build default item for this field.
$entity[$field_name][$langcode][$delta] = field_get_default_value($entity_type, $entity, $field, $instance, $langcode);
// Now set the value to validate.
$entity[$field_name][$langcode][$delta][$rule->col] = $value;
$items = $entity[$field_name][$langcode];
$item = $entity[$field_name][$langcode][$delta];
// Ensure the entity is an object.
$entity = (object) $entity;
// Make sure the error is set into the errors array and not into the form.
$rule->settings['errors'] = TRUE;
// Run the validation.
$validator_errors = array();
$validator = new $class($entity_type, $entity, $field, $instance, $langcode, $items, $delta, $item, $value, $rule, $validator_errors);
if ($validator
->bypass_validation()) {
return;
}
$validator
->validate();
// Check for errors created by the validation and store it in the errors.
if (!empty($validator_errors[$field_name][$langcode][$delta])) {
foreach ($validator_errors[$field_name][$langcode][$delta] as $error) {
if (!empty($error['message'])) {
$errors[] = $error['message'];
}
}
}
}
/**
* Implements hook_conditional_fields_conditions_alter().
*/
function clientside_validation_field_validation_conditional_fields_conditions_alter(&$conditions) {
$conditions['clientside_validation'] = t('Valid');
$conditions['!clientside_validation'] = t('Invalid');
$conditions['clientside_validated'] = t('Validated');
$conditions['!clientside_validated'] = t('Not validated');
}