home | career | drupal | java | mac | mysql | perl | php | scala | uml | unix

Drupal example source code file (options.module)

This example Drupal source code file (options.module) is included in the DevDaily.com "Drupal Source Code Warehouse" project. The intent of this project is to help you "Learn Drupal by Example".

PHP - Drupal tags/keywords

array, default_value, element, field, function, if, label, multiple, options, php, properties, result, return, values

The options.module Drupal example source code

<?php
// $Id: options.module,v 1.30 2010/12/06 18:04:27 webchick Exp $

/**
 * @file
 * Defines selection, check box and radio button widgets for text and numeric fields.
 */

/**
 * Implements hook_help().
 */
function options_help($path, $arg) {
  switch ($path) {
    case 'admin/help#options':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('The Options module defines checkbox, selection, and other input widgets for the Field module. See the <a href="@field-help">Field module help page</a> for more information about fields.', array('@field-help' => url('admin/help/field'))) . '</p>';
      return $output;
  }
}

/**
 * Implements hook_theme().
 */
function options_theme() {
  return array(
    'options_none' => array(
      'variables' => array('instance' => NULL, 'option' => NULL),
    ),
  );
}

/**
 * Implements hook_field_widget_info().
 *
 * Field type modules willing to use those widgets should:
 * - Use hook_field_widget_info_alter() to append their field own types to the
 *   list of types supported by the widgets,
 * - Implement hook_options_list() to provide the list of options.
 * See list.module.
 */
function options_field_widget_info() {
  return array(
    'options_select' => array(
      'label' => t('Select list'),
      'field types' => array(),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_CUSTOM,
      ),
    ),
    'options_buttons' => array(
      'label' => t('Check boxes/radio buttons'),
      'field types' => array(),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_CUSTOM,
      ),
    ),
    'options_onoff' => array(
      'label' => t('Single on/off checkbox'),
      'field types' => array(),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_CUSTOM,
      ),
      'settings' => array('display_label' => 0),
    ),
  );
}

/**
 * Implements hook_field_widget_form().
 */
function options_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  // Abstract over the actual field columns, to allow different field types to
  // reuse those widgets.
  $value_key = key($field['columns']);

  $type = str_replace('options_', '', $instance['widget']['type']);
  $multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
  $required = $element['#required'];
  $has_value = isset($items[0][$value_key]);
  $properties = _options_properties($type, $multiple, $required, $has_value);

  // Prepare the list of options.
  $options = _options_get_options($field, $instance, $properties);

  // Put current field values in shape.
  $default_value = _options_storage_to_form($items, $options, $value_key, $properties);

  switch ($type) {
    case 'select':
      $element += array(
        '#type' => 'select',
        '#default_value' => $default_value,
        // Do not display a 'multiple' select box if there is only one option.
        '#multiple' => $multiple && count($options) > 1,
        '#options' => $options,
      );
      break;

    case 'buttons':
      // If required and there is one single option, preselect it.
      if ($required && count($options) == 1) {
        reset($options);
        $default_value = array(key($options));
      }
      $element += array(
        '#type' => $multiple ? 'checkboxes' : 'radios',
        // Radio buttons need a scalar value.
        '#default_value' => $multiple ? $default_value : reset($default_value),
        '#options' => $options,
      );
      break;

    case 'onoff':
      $keys = array_keys($options);
      $off_value = array_shift($keys);
      $on_value = array_shift($keys);
      $element += array(
        '#type' => 'checkbox',
        '#default_value' => (isset($default_value[0]) && $default_value[0] == $on_value) ? 1 : 0,
        '#on_value' => $on_value,
        '#off_value' => $off_value,
      );
      // Override the title from the incoming $element.
      $element['#title'] = isset($options[$on_value]) ? $options[$on_value] : '';

      if ($instance['widget']['settings']['display_label']) {
        $element['#title'] = $instance['label'];
      }
      break;
  }

  $element += array(
    '#value_key' => $value_key,
    '#element_validate' => array('options_field_widget_validate'),
    '#properties' => $properties,
  );

  return $element;
}

/**
 * Implements hook_field_widget_settings_form().
 */
function options_field_widget_settings_form($field, $instance) {
  $form = array();
  if ($instance['widget']['type'] == 'options_onoff') {
    $form['display_label'] = array(
      '#type' => 'checkbox',
      '#title' => t('Use field label instead of the "On value" as label'),
      '#default_value' => $instance['widget']['settings']['display_label'],
      '#weight' => -1,
    );
  }
  return $form;
}

/**
 * Form element validation handler for options element.
 */
function options_field_widget_validate($element, &$form_state) {
  if ($element['#required'] && $element['#value'] == '_none') {
    form_error($element, t('!name field is required.', array('!name' => $element['#title'])));
  }
  // Transpose selections from field => delta to delta => field, turning
  // multiple selected options into multiple parent elements.
  $items = _options_form_to_storage($element);
  form_set_value($element, $items, $form_state);
}

/**
 * Describes the preparation steps required by each widget.
 */
function _options_properties($type, $multiple, $required, $has_value) {
  $base = array(
    'filter_xss' => FALSE,
    'strip_tags' => FALSE,
    'empty_option' => FALSE,
    'optgroups' => FALSE,
  );

  $properties = array();

  switch ($type) {
    case 'select':
      $properties = array(
        // Select boxes do not support any HTML tag.
        'strip_tags' => TRUE,
        'optgroups' => TRUE,
      );
      if ($multiple) {
        // Multiple select: add a 'none' option for non-required fields.
        if (!$required) {
          $properties['empty_option'] = 'option_none';
        }
      }
      else {
        // Single select: add a 'none' option for non-required fields,
        // and a 'select a value' option for required fields that do not come
        // with a value selected.
        if (!$required) {
          $properties['empty_option'] = 'option_none';
        }
        else if (!$has_value) {
          $properties['empty_option'] = 'option_select';
        }
      }
      break;

    case 'buttons':
      $properties = array(
        'filter_xss' => TRUE,
      );
      // Add a 'none' option for non-required radio buttons.
      if (!$required && !$multiple) {
        $properties['empty_option'] = 'option_none';
      }
      break;

    case 'onoff':
      $properties = array(
        'filter_xss' => TRUE,
      );
      break;
  }

  return $properties + $base;
}

/**
 * Collects the options for a field.
 */
function _options_get_options($field, $instance, $properties) {
  // Get the list of options.
  $options = (array) module_invoke($field['module'], 'options_list', $field);

  // Sanitize the options.
  _options_prepare_options($options, $properties);

  if (!$properties['optgroups']) {
    $options = options_array_flatten($options);
  }

  if ($properties['empty_option']) {
    $label = theme('options_none', array('instance' => $instance, 'option' => $properties['empty_option']));
    $options = array('_none' => $label) + $options;
  }

  return $options;
}

/**
 * Sanitizes the options.
 *
 * The function is recursive to support optgroups.
 */
function _options_prepare_options(&$options, $properties) {
  foreach ($options as $value => $label) {
    // Recurse for optgroups.
    if (is_array($label)) {
      _options_prepare_options($options[$value], $properties);
    }
    else {
      if ($properties['strip_tags']) {
        $options[$value] = strip_tags($label);
      }
      if ($properties['filter_xss']) {
        $options[$value] = field_filter_xss($label);
      }
    }
  }
}

/**
 * Transforms stored field values into the format the widgets need.
 */
function _options_storage_to_form($items, $options, $column, $properties) {
  $items_transposed = options_array_transpose($items);
  $values = (isset($items_transposed[$column]) && is_array($items_transposed[$column])) ? $items_transposed[$column] : array();

  // Discard values that are not in the current list of options. Flatten the
  // array if needed.
  if ($properties['optgroups']) {
    $options = options_array_flatten($options);
  }
  $values = array_values(array_intersect($values, array_keys($options)));
  return $values;
}

/**
 * Transforms submitted form values into field storage format.
 */
function _options_form_to_storage($element) {
  $values = array_values((array) $element['#value']);
  $properties = $element['#properties'];

  // On/off checkbox: transform '0 / 1' into the 'on / off' values.
  if ($element['#type'] == 'checkbox') {
    $values = array($values[0] ? $element['#on_value'] : $element['#off_value']);
  }

  // Filter out the 'none' option. Use a strict comparison, because
  // 0 == 'any string'.
  if ($properties['empty_option']) {
    $index = array_search('_none', $values, TRUE);
    if ($index !== FALSE) {
      unset($values[$index]);
    }
  }

  // Make sure we populate at least an empty value.
  if (empty($values)) {
    $values = array(NULL);
  }

  $result = options_array_transpose(array($element['#value_key'] => $values));
  return $result;
}

/**
 * Manipulates a 2D array to reverse rows and columns.
 *
 * The default data storage for fields is delta first, column names second.
 * This is sometimes inconvenient for field modules, so this function can be
 * used to present the data in an alternate format.
 *
 * @param $array
 *   The array to be transposed. It must be at least two-dimensional, and
 *   the subarrays must all have the same keys or behavior is undefined.
 * @return
 *   The transposed array.
 */
function options_array_transpose($array) {
  $result = array();
  if (is_array($array)) {
    foreach ($array as $key1 => $value1) {
      if (is_array($value1)) {
        foreach ($value1 as $key2 => $value2) {
          if (!isset($result[$key2])) {
            $result[$key2] = array();
          }
          $result[$key2][$key1] = $value2;
        }
      }
    }
  }
  return $result;
}

/**
 * Flattens an array of allowed values.
 *
 * @param $array
 *   A single or multidimensional array.
 * @return
 *   A flattened array.
 */
function options_array_flatten($array) {
  $result = array();
  if (is_array($array)) {
    foreach ($array as $key => $value) {
      if (is_array($value)) {
        $result += options_array_flatten($value);
      }
      else {
        $result[$key] = $value;
      }
    }
  }
  return $result;
}

/**
 * Implements hook_field_widget_error().
 */
function options_field_widget_error($element, $error, $form, &$form_state) {
  form_error($element, $error['message']);
}

/**
 * Returns HTML for the label for the empty value for options that are not required.
 *
 * The default theme will display N/A for a radio list and '- None -' for a select.
 *
 * @param $variables
 *   An associative array containing:
 *   - instance: An array representing the widget requesting the options.
 *
 * @ingroup themeable
 */
function theme_options_none($variables) {
  $instance = $variables['instance'];
  $option = $variables['option'];

  $output = '';
  switch ($instance['widget']['type']) {
    case 'options_buttons':
      $output = t('N/A');
      break;

    case 'options_select':
      $output = ($option == 'option_none' ? t('- None -') : t('- Select a value -'));
      break;
  }

  return $output;
}

Other Drupal examples (source code examples)

Here is a short list of links related to this Drupal options.module source code file:

new blog posts

"Drupal" is a registered trademark of Dries Buytaert.

my drupal tutorials and examples  

Copyright 1998-2016 Alvin Alexander, alvinalexander.com
All Rights Reserved.

Beginning in 2016, a portion of the proceeds from pages under the '/drupal-code-examples/' URI will be donated to charity.