You are here

postal_code.module in Postal Code 7

Same filename and directory in other branches
  1. 8 postal_code.module

File

postal_code.module
View source
<?php

/**
 * @file
 * Postal Code field type module file
 *
 * This module allows site administrators to add a postal code field type
 * with basic validation for US and Canadian
 * Postal Codes.
 *
 * Based on examples from
 * http://www.slideshare.net/zugec/fields-in-core-how-to-create-a-custom-field
 * and http://drupal.org/project/examples .
 * Some regexes from:
 *  - US & Canadian regexes from
 * http://geekswithblogs.net/MainaD/archive/2007/12/03/117321.aspx
 * "http://www.pixelenvision.com/1708/zip-postal-code-validation-regex-php-code
 * -for-12-countries/"
 */
if (module_exists('content_migrate')) {
  module_load_include('inc', 'postal_code', 'content_migrate.zipcode');
}

/**
 * Implements hook_help().
 */
function postal_code_help($path, $arg) {
  require_once DRUPAL_ROOT . "/includes/iso.inc";
  if ($path == 'admin/help#postal_code') {
    $country_list = _country_get_predefined_list();
    $postal_code_validation_data = postal_code_validation();
    $country_list_items = '';
    foreach ($postal_code_validation_data as $country => $info) {
      $country_list_items .= '<li>' . $country_list[drupal_strtoupper($country)] . '</li>';
    }
    $configurl = url('admin/config/postal_code');
    $conturl = url('admin/structure/types');
    $helptext = <<<EOT
<p>A very minimal D7 Postal Code field with validation for one country (listed below) or a combination of countries.</p>

<p>Countries with validation:
<ul>
  {<span class="php-variable">$country_list_items</span>}
</ul>
</p>
<p>To configure this module, navigate to the <a href="{<span class="php-variable">$configurl</span>}">Structure > Postal Code</a>. Selecting countries in the 'Valid "Any" Countries' list will validate the submitted postal code against regexes for those countries using the "Any Country" widget type. If you want submissions validated, make sure to check the "Validate" checkbox.</p>
<p>To configure content types to add this field type, navigate to <a href="{<span class="php-variable">$conturl</span>}">Structure > Content Types</a>. Select "manage fields" beside the content type (eg: blog, page, article...) and follow the normal procedure to add a new field, choosing "Postal Code" under field type. Beneath "Widget" a number of selections will appear for each country type, as well as an "Any" country which is configurable (see above) to validate any included country's postal code.</p>
<p>Thanks to <a href="http://www.pixelenvision.com/1708/zip-postal-code-validation-regex-php-code-for-12-countries/">Pixel Envision</a> and <a href="http://geekswithblogs.net/MainaD/archive/2007/12/03/117321.aspx">Geeks With Blogs</a> for the list of countries and <a href="http://www.knowclassic.com">Classic Graphics</a> for the time to complete it.</p>
EOT;
    return t($helptext);
  }
}

/**
 * Implements hook_menu().
 */
function postal_code_menu() {
  $items = array();
  $items['admin/config/regional/postal_code'] = array(
    'title' => 'Postal Codes',
    'description' => 'Configuration for the Postal Code module.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'postal_code_admin_settings',
    ),
    'access arguments' => array(
      'administer postal code',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}

/**
 * Implements hook_permission().
 */
function postal_code_permission() {
  return array(
    'administer postal code' => array(
      'title' => t('administer postal code'),
      'description' => t('This will allow user to administer postal code module settings'),
    ),
  );
}

/**
 * Page callback for admin settings form.
 */
function postal_code_admin_settings() {
  require_once DRUPAL_ROOT . "/includes/iso.inc";
  $postal_code_validation_data = postal_code_validation();
  $options = array();
  $country_list = _country_get_predefined_list();
  foreach ($postal_code_validation_data as $country_code => $info) {
    $options[$country_code] = $country_list[drupal_strtoupper($country_code)];
  }
  $form['postal_code_valid_countries'] = array(
    '#type' => 'select',
    '#title' => t('Valid "Any" Countries'),
    '#size' => 16,
    '#multiple' => TRUE,
    '#options' => $options,
    '#default_value' => variable_get('postal_code_valid_countries', 'us'),
    '#description' => '<p>' . t('Select the countr(y/ies) for Postal Code Validation for "Any" field type.') . '</p><p><em>' . t('This is most useful when you have a form that allows, for example, US and Canadian addresses.') . '</em></p><p><strong>' . t('VALIDATION ONLY OCCURS IF THE "VALIDATE" CHECKBOX BELOW IS SELECTED.') . '</strong></p>',
  );
  $form['postal_code_validate'] = array(
    '#type' => 'checkbox',
    '#title' => t('Validate'),
    '#default_value' => variable_get('postal_code_validate', 0),
    '#description' => t('Validate submitted postal codes?'),
  );
  return system_settings_form($form);
}

/**
 * Implements hook_field_info().
 */
function postal_code_field_info() {
  return array(
    'postal_code' => array(
      'label' => t('Postal Code'),
      'description' => t('Postal Code field.'),
      'settings' => array(
        'max_length' => 16,
      ),
      'default_widget' => 'postal_code_any_postal_code_form',
      'default_formatter' => 'postal_code_simple_text',
    ),
  );
}

/**
 * Implements hook_field_is_empty().
 */
function postal_code_field_is_empty($item, $field) {
  return empty($item['postal']);
}

/**
 * Implements hook_field_widget_info().
 */
function postal_code_field_widget_info() {
  require_once DRUPAL_ROOT . "/includes/iso.inc";
  $postal_code_validation_data = postal_code_validation();
  $widgets = array(
    'postal_code_any_postal_code_form' => array(
      'label' => t('Postal Code: Any Format'),
      'field types' => array(
        'postal_code',
      ),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
        'default value' => FIELD_BEHAVIOR_DEFAULT,
      ),
    ),
  );
  $countrylist = _country_get_predefined_list();
  foreach ($postal_code_validation_data as $country => $regex) {
    $widgets['postal_code_' . $country . '_postal_code_form'] = array(
      'label' => t('Postal Code: @countrylist', array(
        '@countrylist' => $countrylist[drupal_strtoupper($country)],
      )),
      'field types' => array(
        'postal_code',
      ),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
        'default value' => FIELD_BEHAVIOR_DEFAULT,
      ),
    );
  }
  return $widgets;
}

/**
 * Implements hook_field_widget_form().
 */
function postal_code_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $matches = array();
  if (preg_match('/^postal_code_([a-z]{2,3})_postal_code_form$/', $instance['widget']['type'], $matches)) {
    $element['postal'] = array(
      '#type' => 'textfield',
      '#title' => filter_xss_admin($instance['label']),
      '#maxlength' => 16,
      '#required' => $element['#required'],
      '#default_value' => isset($items[$delta]['postal']) ? check_plain($items[$delta]['postal']) : NULL,
      '#element_validate' => array(
        '_postal_code_postal_validate',
      ),
      '#description' => filter_xss_admin($instance['description']),
    );
    $element['postal_type'] = array(
      '#type' => 'hidden',
      '#value' => $matches[1],
    );
  }
  return $element;
}

/**
 * Validation of Postal Code element.
 */
function _postal_code_postal_validate($element, &$form_state, $form) {
  $value = trim($element['#value']);
  if (!empty($value) && variable_get('postal_code_validate', 0)) {

    // Locate 'postal_type' in the form.
    $arr = explode('[', str_replace(']', '', str_replace('[postal]', '[postal_type]', $element['#name'])));
    $country_code = $form_state['values'];
    for ($i = 0, $j = count($arr); $i < $j; $i++) {

      // Format the array properly to get back down to postal_type.
      $country_code = $country_code[$arr[$i]];
    }
    if (!empty($country_code)) {
      if ($country_code != 'any') {
        $error_array = _postal_code_validator($country_code, $value);
      }
      else {
        $validatable_countries = variable_get('postal_code_valid_countries');
        foreach ($validatable_countries as $key => $country) {
          $err_array[] = _postal_code_validator($country, $value);
        }
        foreach ($err_array as $k => $v) {
          $error_array[] = $v[0];
        }
      }
    }
    else {
      form_error($element, t('This form has been altered in a way in which Postal Code validation will not work, but the validation option remains enabled. Please correct the changes to the form or disable the validation option.'));
    }
    if (!in_array(TRUE, $error_array)) {
      form_error($element, t('Invalid Postal Code Provided.'));
    }
  }
}

/**
 * Actual validation function.
 */
function _postal_code_validator($countrycode, $formvalue) {
  $postal_code_validation_data = postal_code_validation();
  $regex = $postal_code_validation_data[$countrycode][0];
  $error_array[] = preg_match($regex, $formvalue);
  return $error_array;
}

/**
 * Implements hook_field_formatter_info().
 */
function postal_code_field_formatter_info() {
  return array(
    'postal_code_simple_text' => array(
      'label' => t('Default'),
      'field types' => array(
        'postal_code',
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_view().
 */
function postal_code_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  switch ($display['type']) {
    case 'postal_code_simple_text':
      foreach ($items as $delta => $item) {
        $element[$delta] = array(
          '#markup' => check_plain($item['postal']),
        );
      }
      break;
  }
  return $element;
}

/**
 * Custom function defining regexes corresponding to different countries.
 */
function postal_code_validation() {
  $postal_code_validation_data = array(
    'us' => array(
      '/^\\d{5}(-\\d{4})?$/',
    ),
    'ca' => array(
      '/^[ABCEGHJKLMNPRSTVXY]\\d[ABCEGHJKLMNPRSTVWXYZ] ?\\d[ABCEGHJKLMNPRSTVWXYZ]\\d$/',
    ),
    'gb' => array(
      '/^(GIR|[A-Z]\\d[A-Z\\d]??|[A-Z]{2}\\d[A-Z\\d]??)[ ]??(\\d[A-Z]{2})$/i',
    ),
    'de' => array(
      '/^\\b((?:0[1-46-9]\\d{3})|(?:[1-357-9]\\d{4})|(?:[4][0-24-9]\\d{3})|(?:[6][013-9]\\d{3}))\\b$/',
    ),
    'fr' => array(
      '/^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$/',
    ),
    'it' => array(
      '/^(V-|I-)?[0-9]{5}$/',
    ),
    'au' => array(
      '/^(0[289][0-9]{2})|([1345689][0-9]{3})|(2[0-8][0-9]{2})|(290[0-9])|(291[0-4])|(7[0-4][0-9]{2})|(7[8-9][0-9]{2})$/',
    ),
    'nl' => array(
      '/^[1-9][0-9]{3}\\s?([a-zA-Z]{2})?$/',
    ),
    'es' => array(
      '/^([1-9]{2}|[0-9][1-9]|[1-9][0-9])[0-9]{3}$/',
    ),
    'dk' => array(
      '/^([D-d][K-k])?( |-)?([0-9]{1})?[0-9]{3}$/',
    ),
    'se' => array(
      '/^(s-|S-){0,1}[0-9]{3}\\s?[0-9]{2}$/',
    ),
    'be' => array(
      '/^[1-9]{1}[0-9]{3}$/',
    ),
    'in' => array(
      '/^([1-9][0-9]{2}\\s?[0-9]{3})$/',
    ),
    'pl' => array(
      '/^[0-9]{2}-[0-9]{3}$/',
    ),
  );
  return $postal_code_validation_data;
}

Functions

Namesort descending Description
postal_code_admin_settings Page callback for admin settings form.
postal_code_field_formatter_info Implements hook_field_formatter_info().
postal_code_field_formatter_view Implements hook_field_formatter_view().
postal_code_field_info Implements hook_field_info().
postal_code_field_is_empty Implements hook_field_is_empty().
postal_code_field_widget_form Implements hook_field_widget_form().
postal_code_field_widget_info Implements hook_field_widget_info().
postal_code_help Implements hook_help().
postal_code_menu Implements hook_menu().
postal_code_permission Implements hook_permission().
postal_code_validation Custom function defining regexes corresponding to different countries.
_postal_code_postal_validate Validation of Postal Code element.
_postal_code_validator Actual validation function.