Skip to content
Snippets Groups Projects
clientside_validation.module 20.8 KiB
Newer Older
<?php
// $Id: 
/**
 * @file
 * Add client side validation to a webform.
 */

/**
 * Implementation of hook_form_alter().
 */
function clientside_validation_form_alter(&$form, &$form_state, $form_id) {
  if (strpos($form_id, 'webform_client_form') !== FALSE) {
    $form['#after_build'][] = 'clientside_validation_webform_after_build';
  } 
  else {
    switch ($form['#id']) {
      case 'node-form':
      case 'webform-component-edit-form':
        $form['#after_build'][] = 'clientside_validation_form_after_build';
        break;
      default:
        $form['#after_build'][] = 'clientside_validation_form_after_build';
        break;
    }
  }
}

/**
 */
function clientside_validation_webform_after_build(&$form, &$form_state) {
  $js_rules = array();
  clientside_validation_webform_after_build_recurse($form['#id'], $form, $form_state, $js_rules);
    if ($webform_validation_rules = clientside_validation_webform_validation($form_state['values']['details']['nid'])) {
    dpm($webform_validation_rules);
    foreach ($webform_validation_rules as $webform_validation_rule) {
      switch ($webform_validation_rule['validator']) {
        case 'min_length':
          foreach ($webform_validation_rule['components'] as $component) {
          $message = t('!name field has a minimum length of !minl characters.', array('!name' => $component['name'], '!minl' => $webform_validation_rule['data']));
           _clientside_validation_set_minmaxlength('submitted[' . $component['form_key'] . ']', $component['name'], $webform_validation_rule['data'], '', $js_rules, $message);
          }
          break;
        case 'max_length':
          foreach ($webform_validation_rule['components'] as $component) {
            $message = t('!name field has a maximum length of !maxl characters.', array('!name' => $component['name'], '!maxl' => $webform_validation_rule['data']));
            _clientside_validation_set_minmaxlength('submitted[' . $component['form_key'] . ']', $component['name'], '',$webform_validation_rule['data'], $js_rules, $message);
          }
          break;
        case 'numeric':
          foreach ($webform_validation_rule['components'] as $component) {
            $range = explode('-', $webform_validation_rule['data']);
            if (!empty($range[0]) && $range[0] != '0') {
              if (!isset($range[1])) {
                $range[1] = $range[0];
                $range[0] = 0;
              }
              _clientside_validation_set_minmax('submitted[' . $component['form_key'] . ']', $component['name'], $range[0], $range[1], $js_rules);
            }
          }
          break;
        case 'equal':
          $others = $webform_validation_rule['components'];
          $firstone = array_shift($others);
          foreach ($others as $component) {
            _clientside_validation_set_equal('submitted[' . $component['form_key'] . ']', $component['name'], $firstone, $js_rules);
          }
          break;
        case 'unique':
          $all = $webform_validation_rule['components'];
          while (count($all) > 1) {
            $firstone = array_shift($all);
            foreach ($all as $component) {
              $message = (isset($webform_validation_rule['error_message']) && !empty($webform_validation_rule['error_message'])) ? $webform_validation_rule['error_message'] : '';
              _clientside_validation_set_not_equal('submitted[' . $component['form_key'] . ']', $component['name'], $firstone, $js_rules, $message);
            }
          }
          break;
        case 'specific_value':
          foreach ($webform_validation_rule['components'] as $component) {
            $value = explode(',', $webform_validation_rule['data']);
            $message = (isset($webform_validation_rule['error_message']) && !empty($webform_validation_rule['error_message'])) ? $webform_validation_rule['error_message'] : '';
            _clientside_validation_set_specific_value('submitted[' . $component['form_key'] . ']', $component['name'], $value, $js_rules, $message);
          }
          break;
        case 'select_min':
          foreach ($webform_validation_rule['components'] as $component) {
            _clientside_validation_set_minmaxlength ('submitted[' . $component['form_key'] . ']', $component['name'], $webform_validation_rule['data'], '', $js_rules);
          }
          break;
        case 'select_max':
          foreach ($webform_validation_rule['components'] as $component) {
            _clientside_validation_set_minmaxlength ('submitted[' . $component['form_key'] . ']', $component['name'], '', $webform_validation_rule['data'], $js_rules);
          }
          break;
        case 'select_exact':
          foreach ($webform_validation_rule['components'] as $component) {
            _clientside_validation_set_minmaxlength ('submitted[' . $component['form_key'] . ']', $component['name'], $webform_validation_rule['data'], $webform_validation_rule['data'], $js_rules);
          }
          break;
        case 'validEAN':
          foreach ($webform_validation_rule['components'] as $component) {
            $message = (isset($webform_validation_rule['error_message']) && !empty($webform_validation_rule['error_message'])) ? $webform_validation_rule['error_message'] : '';
            _clientside_validation_set_ean('submitted[' . $component['form_key'] . ']', $component['name'], $js_rules, $message);
          }
          break;
      }
    }
  }
  if (!empty($js_rules)) {
    $settings['clientsideValidation']['general'] = array (
      "errorClass" => "error",
      "wrapper" => "li",
    );
    $settings['clientsideValidation']['forms'][$form['#id']]['settings'] = array(
      "errorContainer" => "#formerrors-" . $form['#id'],
      "errorLabelContainer" => "#formerrors-" . $form['#id'] . " ul",
    );
    foreach($js_rules as $key => $rule) {
      $settings['clientsideValidation']['forms'][$form['#id']]['rules'][$key] = $rule;
    }
Peter Droogmans's avatar
Peter Droogmans committed
    drupal_add_js(drupal_get_path('module', 'clientside_validation') . '/jquery-validate/jquery.validate.js');
    drupal_add_js(drupal_get_path('module', 'clientside_validation') . '/clientside_validation.js');
    drupal_add_js($settings, 'setting');
  }
  return $form;
}

function clientside_validation_webform_after_build_recurse($form_id, &$form, &$form_state, &$js_rules) {
  if ($children = array_values(element_children($form))) {
    foreach ($children as $index => $item) {
      $element = &$form[$item];
      if (isset($element['#title'])) {
        if ($element['#required']) {
          if (isset($element['#webform_component']) && $element['#webform_component']['type'] == 'time' && isset($element['hour']['#name'])) {
            $message = t('Hour in !name field is required.', array('!name' => $element['#title']));
            _clientside_validation_set_required($element['hour']['#name'], $element['#title'], TRUE, $js_rules, $message);
            $message = t('Minute in !name field is required.', array('!name' => $element['#title']));
            _clientside_validation_set_required($element['minute']['#name'], $element['#title'], TRUE, $js_rules, $message);
          }
          else if (isset($element['#webform_component']) && $element['#webform_component']['type'] == 'date') {
            $message = t('Month in !name field is required.', array('!name' => $element['#title']));
            _clientside_validation_set_required($element['#name'] . '[month]', $element['#title'], TRUE, $js_rules, $message);
            $message = t('Day in !name field is required.', array('!name' => $element['#title']));
            _clientside_validation_set_required($element['#name'] . '[day]', $element['#title'], TRUE, $js_rules, $message);
            $message = t('Year in !name field is required.', array('!name' => $element['#title']));
            _clientside_validation_set_required($element['#name'] . '[year]', $element['#title'], TRUE, $js_rules, $message);
            if (is_numeric($element['#year_start']) && is_numeric($element['#year_end'])) {
              $message = t('The entered date needs to be between the years @start and @end.', array('@start' => $element['#year_start'], '@end' => $element['#year_end']));
              _clientside_validation_set_minmax($element['#name'] . '[year]', $element['#title'], $element['#year_start'], $element['#year_end'], $js_rules, $message);
            }
          }
          else if ($element['#type'] == 'checkboxes') {
            $count = 0;
            foreach ($element['#options'] as $key => $value) {
              _clientside_validation_set_checkboxgroup_minmax($element[$key]['#name'], $element['#title'], '#webform-component-' . end($element['#parents']), $count, $js_rules);
              $count++;
            }
          }
          else if ($element['#type'] == 'select' && $element['#multiple']) {
            _clientside_validation_set_required($element['#name'] . '[]', $element['#title'], TRUE, $js_rules);
          }
          else if (isset($element['#type'])) {
            _clientside_validation_set_required($element['#name'], $element['#title'], TRUE, $js_rules);
          }
        }
        if (isset($element['#webform_component']) && $element['#webform_component']['type'] == 'file' && $element['#webform_component']['mandatory'] == "1") {
           _clientside_validation_set_required($element['#name'], $element['#title'], TRUE, $js_rules);
          if (isset($element['#webform_component']['extra']['filtering']['types'])) {
            $extensions = $element['#webform_component']['extra']['filtering']['types'];
            _clientside_validation_set_extensions($element['#name'], $extensions, $js_rules);
Peter Droogmans's avatar
Peter Droogmans committed
        if (isset($element['#maxlength']) && $element['#maxlength']) {
          $message = t('!name field has a max length of !maxl characters.', array('!name' => $element['#title'], '!maxl' => $element['#maxlength']));
          _clientside_validation_set_minmaxlength($element['#name'], $element['#title'], '', $element['#maxlength'], $js_rules, $message);
        if (isset($element['#webform_component']) && $element['#webform_component']['type'] == 'email') {
          _clientside_validation_set_email($element['#name'], $element['#title'], $js_rules);
        }
      }
      clientside_validation_webform_after_build_recurse($form_id, $element, $form_state, $js_rules);
    }
  }
}

/**
 * Implements hook_webform_validation().
 */
function clientside_validation_webform_validation($nid) {
  static $webform_validation_rules;
  if (!isset($webform_validation_rules[$nid])) {
    if (module_exists('webform_validation')) {
      $webform_validation_rules[$nid] = webform_validation_get_node_rules($nid);
    }
    else {
      $webform_validation_rules[$nid] = NULL;
    }
  }
  return $webform_validation_rules[$nid];
}

/**
 * Regular form + CCK
 */
function clientside_validation_form_after_build(&$form, &$form_state) {
  static $js_rules = array();
  clientside_validation_form_after_build_recurse($form['#id'], $form, $form_state, $js_rules);
  if (!empty($js_rules)) {
    $settings['clientsideValidation']['general'] = array (
      "errorClass" => "error",
      "wrapper" => "li",
    );
    $settings['clientsideValidation']['forms'][$form['#id']]['settings'] = array(
      "errorContainer" => "#formerrors-" . $form['#id'],
      "errorLabelContainer" => "#formerrors-" . $form['#id'] . " ul",
    );
    foreach($js_rules as $key => $rule) {
      $settings['clientsideValidation']['forms'][$form['#id']]['rules'][$key] = $rule;
    }
Peter Droogmans's avatar
Peter Droogmans committed
    drupal_add_js(drupal_get_path('module', 'clientside_validation') . '/jquery-validate/jquery.validate.js');
    drupal_add_js(drupal_get_path('module', 'clientside_validation') . '/clientside_validation.js');
    drupal_add_js($settings, 'setting');
  }
  
  return $form;
}

function clientside_validation_form_after_build_recurse($form_id, &$form, &$form_state, &$js_rules) {
  if ($children = array_values(element_children($form))) {
    foreach ($children as $index => $item) {
      $element = &$form[$item];
      $types = array(
        'textfield', 'textarea', 'select', 'radio', 'checkbox', 'password', 'file', 'radios', 'checkboxes',
      );
      if (isset($element['#type']) && in_array($element['#type'], $types)) {
        clientside_validation_regular ($form_id, $element, $js_rules);
      }
      clientside_validation_form_after_build_recurse($form_id, $element, $form_state, $js_rules);
    }
  }
}

function clientside_validation_regular ($form_id, $element, &$js_rules) {
  static $multiples = array();
  if (isset($element['#name']) && !isset($js_rules[$element['#name']])) {
    $el_name = $element['#name'];
    $el_title = $el_name;
    if (isset($element['#title'])) {
      $el_title = $element['#title'];
    }
    $is_multiple = FALSE;
    if (isset($element['#multiple'])) {
      $is_multiple = $element['#multiple'];
    }
    switch ($element['#type']) {
      case 'textfield':
      case 'password':
      case 'textarea':
      case 'file':
        if ($is_multiple) {
          // Only first field is required
          if (!isset($multiples[$form_id][$el_name])) {
            _clientside_validation_set_required ($el_name, $el_title, $element['#required'], $js_rules);
            $multiples[$form_id][$el_name] = 1;
          }
        else {
          _clientside_validation_set_required ($el_name, $el_title, $element['#required'], $js_rules);
        }
        if (isset($element['max_length']) && $element['max_length'] > 0) {
          _clientside_validation_set_minmaxlength ($el_name, $el_title, '', $element['max_length'], $js_rules);
      case 'select':
        if ($is_multiple) {
          $el_name .= '[]';
          if(!isset($element['#minlength'])){
            $element['#minlength'] = 0;
          }
          _clientside_validation_set_minmaxlength ($el_name, $el_title, $element['#minlength'], $is_multiple, $js_rules);
        _clientside_validation_set_required ($el_name, $el_title, $element['#required'], $js_rules);
        break;
      case 'radio':
      case 'radios':
        _clientside_validation_set_required ($el_name, $el_title, $element['#required'], $js_rules);
        break;
      case 'checkbox':
      case 'checkboxes':
        $is_multiple = (isset($element['#options']) && count($element['#options']>1));
        if ($is_multiple) {
          // We don't have a parent element to connect to, so no go, outer div has only a class
          // The checkboxes element has the unique name, but this isn't added to the outer div
          $count = 0;
          foreach ($element['#options'] as $key => $value) {
            $id = '#edit-' . str_replace('_', '-', $element['#parents'][0]);
            _clientside_validation_set_checkboxgroup_minmax($element[$key]['#name'], $element['#title'], $id, $count, $js_rules);
            $count++;
          }
        }
        else {
          _clientside_validation_set_required ($el_name, $el_title, $element['#required'], $js_rules);
        }
/**
 * Set validation rule for required fields.
 */
function _clientside_validation_set_required ($name, $title, $required, &$js_rules, $message = '') {
  if ($required) {
    $js_rules[$name]['required'] = TRUE;
    $js_rules[$name]['messages']['required'] = (empty($message)) ? t('!name field is required.', array('!name' => $title)) : $message;
/**
 * Set validation rule for number fields.
 */
function _clientside_validation_set_number ($name, $title, $decimalpoint, &$js_rules) {
  $js_rules[$name]['digits_negative'] = TRUE;
  $js_rules[$name]['messages']['digits_negative'] = t('!name field accepts only numbers.', array('!name' => $title));
}

/**
 * Set validation rule for decimal fields.
 */
function _clientside_validation_set_number_decimal ($name, $title, $decimalpoint, &$js_rules) {
  if ($decimalpoint == '.') {
    $js_rules[$name]['number'] = TRUE;
    $js_rules[$name]['messages']['number'] = t('!name field accepts only numbers (use a \'.\' as decimal point).', array('!name' => $title));
  }
  else {
    $js_rules[$name]['numberDE'] = TRUE;
    $js_rules[$name]['messages']['numberDE'] = t('!name field accepts only numbers (use a \',\' as decimal point).', array('!name' => $title));
  }
}

/**
 * Set validation rule for fields with a minimum and/or a maximum value.
 */
function _clientside_validation_set_minmax ($name, $title, $min, $max, &$js_rules, $message = '') {
  if (isset($min) && $min != '' && isset($max) && $max != '') {
    $js_rules[$name]['range'] = array($min, $max);
    $js_rules[$name]['messages']['range'] = (empty($message)) ? t('!name field has to be between !min and !max.', array('!name' => $title, '!min' => $min, '!max' => $max)) : $message;
  }
  else if (isset($min) && $min != '') {
    $js_rules[$name]['min'] = $min;
    $js_rules[$name]['messages']['min'] = (empty($message)) ? t('!name field has to be greater than !min.', array('!name' => $title, '!min' => $min)) : $message;
  }
  else if (isset($max) && $max != '') {
    $js_rules[$name]['max'] = $max;
    $js_rules[$name]['messages']['max'] = (empty($message)) ? t('!name field has to be smaller than !max.', array('!name' => $title, '!max' => $max)) : $message;
/**
 * Set validation rule for fields with a minimum and/or maximum length.
 */
function _clientside_validation_set_minmaxlength ($name, $title, $min, $max, &$js_rules, $message = '') {
  if (isset($min) && $min != '' && isset($max) && $max != '') {
    $js_rules[$name]['rangelength'] = array($min, $max);
    $js_rules[$name]['messages']['rangelength'] = (empty($message)) ? t('!name field has to have between !min and !max values.', array('!name' => $title, '!min' => $min, '!max' => $max)) : $message;
  }
  else if (isset($min) && $min != '') {
    $js_rules[$name]['minlength'] = $min;
    $js_rules[$name]['messages']['minlength'] = (empty($message)) ? t('!name field has to have minimal !min values.', array('!name' => $title, '!min' => $min)) : $message;
  }
  else if (isset($max) && $max != '') {
    $js_rules[$name]['maxlength'] = $max;
    $js_rules[$name]['messages']['maxlength'] = (empty($message)) ? t('!name field has to have maximum !max values.', array('!name' => $title, '!max' => $max)) : $message;
/**
 * Set validation rule for required fields that must equal a value from an other field.
 */
function _clientside_validation_set_equal($name, $title, $value, &$js_rules){
  $js_rules[$name]['equalTo'] = ':input[name=\'submitted[' . $value['form_key'] . ']\']';
  $js_rules[$name]['messages']['equalTo'] = t('!name field has to be equal to !firstone.', array('!name' => $title, '!firstone' => $value['name']));
}

/**
 * Set validation rule for fields that can not be equal to a value from an other field.
 */
function _clientside_validation_set_not_equal($name, $title, $value, &$js_rules, $message = ''){
  $js_rules[$name]['notEqualTo'] = ':input[name=\'submitted[' . $value['form_key'] . ']\']';
  $message = empty($message) ? t('!name field has to different from !firstone.', array('!name' => $title, '!firstone' => $value['name'])) : $message;
  $js_rules[$name]['messages']['notEqualTo'] = $message;
}

/**
 * Set validation rule for fields that must be equal to a specific value.
 */
function _clientside_validation_set_specific_value($name, $title, $value, &$js_rules, $message = ''){
  $js_rules[$name]['oneOf'] = $value;
  $message = empty($message) ? t('!name field has to different from !firstone.', array('!name' => $title, '!firstone' => $value['name'])) : $message;
  $js_rules[$name]['messages']['oneOf'] = $message;
}

/**
 * Set validation rule for ean number fields.
 */
function _clientside_validation_set_ean($name, $title, &$js_rules, $message = ''){
  $message = empty($message) ? t('!name field is not a valid EAN number.', array('!name' => $title)) : $message;
  $js_rules[$name]['validEAN'] = TRUE;
/**
 * Set validation rule for file fields that must have a certain extension.
 */
function _clientside_validation_set_extensions($name, $extensions, &$js_rules){
  $extension_list = preg_replace('#,(?![^,]+,)#', ' or', implode(', ', $extensions));
  $js_rules[$name]['accept'] = implode('|', $extensions);
  $js_rules[$name]['messages']['accept'] = t("Only files with a %exts extension are allowed.", array('%exts' => $extension_list));
}

/**
 * Set validation rule for checkboxes.
 */
function _clientside_validation_set_checkboxgroup_minmax($name, $title, $id, $count, &$js_rules, $min = 1, $max = 99){
  $js_rules[$name]['checkboxgroupminmax'] = array($min, $max, $id);
  if ($count == 0) {
    $js_rules[$name]['messages']['checkboxgroupminmax'] = t('!name field is required.', array('!name' => $title));
  }
  else {
    $js_rules[$name]['messages']['checkboxgroupminmax'] = false;
  }
}

/**
 * Set validation rule for email fields.
 */
function _clientside_validation_set_email($name, $title, &$js_rules){
  $js_rules[$name]['email'] = TRUE;
  $js_rules[$name]['messages']['email'] = t('The value in !name is not a valid email address.', array('!name' => $title));
}

function clientside_validation_drupal_json_encode($var) {
  return str_replace(array('<', '>', '&'), array('\u003c', '\u003e', '\u0026'), json_encode($var));
}