You are here

optionwidgets.module in Content Construction Kit (CCK) 6

Defines selection, check box and radio button widgets for text and numeric fields.

File

modules/optionwidgets/optionwidgets.module
View source
<?php

/**
 * @file
 * Defines selection, check box and radio button widgets for text and numeric fields.
 */
function optionwidgets_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == '_content_admin_field') {
    $output = '<p>' . t('Create a list of options as a list in <strong>Allowed values</strong> or as an array in PHP code. These values will be the same for %field in all content types.', array(
      '%field' => $form['widget']['label']['#default_value'],
    )) . '</p>';
    if ($form['widget']['widget_type']['#default_value'] == 'optionwidgets_onoff') {
      $output .= '<p>' . t("For a 'single on/off checkbox' widget, define the 'off' value first, then the 'on' value in the <strong>Allowed values</strong> section. Note that the checkbox will be labeled with the label of the 'on' value.") . '</p>';
    }
    elseif ($form['widget']['widget_type']['#default_value'] == 'optionwidgets_buttons') {
      $output .= '<p>' . t("The 'checkboxes/radio buttons' widget will display checkboxes if the multiple values option is selected for this field, otherwise radios will be displayed.") . '</p>';
    }
    if (in_array($form['type']['#value'], array(
      'text',
      'number_integer',
      'number_float',
      'number_decimal',
    )) && in_array($form['widget']['widget_type']['#default_value'], array(
      'optionwidgets_onoff',
      'optionwidgets_buttons',
      'optionwidgets_select',
    ))) {
      $form['field']['allowed_values_fieldset']['#collapsed'] = FALSE;
      $form['field']['allowed_values_fieldset']['#description'] = $output;
    }
  }
}

/**
 * Implementation of hook_theme().
 */
function optionwidgets_theme() {
  return array(
    'optionwidgets_select' => array(
      'arguments' => array(
        'element' => NULL,
      ),
    ),
    'optionwidgets_buttons' => array(
      'arguments' => array(
        'element' => NULL,
      ),
    ),
    'optionwidgets_onoff' => array(
      'arguments' => array(
        'element' => NULL,
      ),
    ),
    'optionwidgets_none' => array(
      'arguments' => array(
        'widget_type' => NULL,
        'field_name' => NULL,
        'node_type' => NULL,
      ),
    ),
  );
}

/**
 * Implementation of hook_widget_info().
 *
 * We need custom handling of multiple values because we need
 * to combine them into a options list rather than display
 * multiple elements. We will use the content module's default
 * handling for default values.
 *
 * Callbacks can be omitted if default handing is used.
 * They're included here just so this module can be used
 * as an example for custom modules that might do things
 * differently.
 */
function optionwidgets_widget_info() {
  return array(
    'optionwidgets_select' => array(
      'label' => t('Select list'),
      'field types' => array(
        'text',
        'number_integer',
        'number_decimal',
        'number_float',
      ),
      'multiple values' => CONTENT_HANDLE_MODULE,
      'callbacks' => array(
        'default value' => CONTENT_CALLBACK_DEFAULT,
      ),
    ),
    'optionwidgets_buttons' => array(
      'label' => t('Check boxes/radio buttons'),
      'field types' => array(
        'text',
        'number_integer',
        'number_decimal',
        'number_float',
      ),
      'multiple values' => CONTENT_HANDLE_MODULE,
      'callbacks' => array(
        'default value' => CONTENT_CALLBACK_DEFAULT,
      ),
    ),
    'optionwidgets_onoff' => array(
      'label' => t('Single on/off checkbox'),
      'field types' => array(
        'text',
        'number_integer',
        'number_decimal',
        'number_float',
      ),
      'multiple values' => CONTENT_HANDLE_MODULE,
      'callbacks' => array(
        'default value' => CONTENT_CALLBACK_DEFAULT,
      ),
    ),
  );
}

/**
 * Implementation of FAPI hook_elements().
 *
 * Any FAPI callbacks needed for individual widgets can be declared here,
 * and the element will be passed to those callbacks for processing.
 *
 * Drupal will automatically theme the element using a theme with
 * the same name as the hook_elements key.
 */
function optionwidgets_elements() {
  return array(
    'optionwidgets_select' => array(
      '#input' => TRUE,
      '#columns' => array(
        'value',
      ),
      '#delta' => 0,
      '#element_validate' => array(
        'optionwidgets_validate',
      ),
      '#process' => array(
        'optionwidgets_select_process',
      ),
    ),
    'optionwidgets_buttons' => array(
      '#input' => TRUE,
      '#columns' => array(
        'value',
      ),
      '#delta' => 0,
      '#element_validate' => array(
        'optionwidgets_validate',
      ),
      '#process' => array(
        'optionwidgets_buttons_process',
      ),
    ),
    'optionwidgets_onoff' => array(
      '#input' => TRUE,
      '#columns' => array(
        'value',
      ),
      '#delta' => 0,
      '#element_validate' => array(
        'optionwidgets_validate',
      ),
      '#process' => array(
        'optionwidgets_onoff_process',
      ),
    ),
  );
}

/**
 * Implementation of hook_widget().
 *
 * Attach a single form element to the form. It will be built out and
 * validated in the callback(s) listed in hook_elements. We build it
 * out in the callbacks rather than here in hook_widget so it can be
 * plugged into any module that can provide it with valid
 * $field information.
 *
 * Content module will set the weight, field name and delta values
 * for each form element. This is a change from earlier CCK versions
 * where the widget managed its own multiple values.
 *
 * If there are multiple values for this field, the content module will
 * call this function as many times as needed.
 *
 * @param $form
 *   the entire form array, $form['#node'] holds node information
 * @param $form_state
 *   the form_state, $form_state['values'] holds the form values.
 * @param $field
 *   the field array
 * @param $items
 *   an array of default values for this element
 * @param $delta
 *   the order of this item in the array of subelements (0, 1, 2, etc)
 *
 * @return
 *   the form item for a single element for this field
 */
function optionwidgets_widget(&$form, &$form_state, $field, $items, $delta = NULL) {
  $element = array(
    '#type' => $field['widget']['type'],
    '#default_value' => !empty($items) ? $items : array(),
  );
  return $element;
}

/**
 * Process an individual element.
 *
 * Build the form element. When creating a form using FAPI #process,
 * note that $element['#value'] is already set.
 *
 * The $fields array is in $form['#field_info'][$element['#field_name']].
 */
function optionwidgets_buttons_process($element, $edit, &$form_state, $form) {
  $field_name = $element['#field_name'];
  $field = $form['#field_info'][$field_name];
  $field_key = $element['#columns'][0];
  if (!$form_state['submitted']) {
    $element['#value'] = optionwidgets_data2form($element, $element['#default_value'], $field);
  }
  $options = optionwidgets_options($field);
  $element[$field_key] = array(
    '#type' => $field['multiple'] ? 'checkboxes' : 'radios',
    '#title' => $element['#title'],
    '#description' => $element['#description'],
    '#required' => $field['required'],
    '#multiple' => $field['multiple'],
    '#options' => $options,
    '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL,
  );

  // Make sure field info will be available to the validator which
  // does not get the values in $form.
  $form_state['#field_info'][$field['field_name']] = $field;
  return $element;
}

/**
 * Process an individual element.
 *
 * Build the form element. When creating a form using FAPI #process,
 * note that $element['#value'] is already set.
 *
 * The $fields array is in $form['#field_info'][$element['#field_name']].
 */
function optionwidgets_select_process($element, $edit, &$form_state, $form) {
  $field_name = $element['#field_name'];
  $field = $form['#field_info'][$field_name];
  $field_key = $element['#columns'][0];
  if (!$form_state['submitted']) {
    $element['#value'] = optionwidgets_data2form($element, $element['#default_value'], $field);
  }
  $options = optionwidgets_options($field);
  $element[$field_key] = array(
    '#type' => 'select',
    '#title' => $element['#title'],
    '#description' => $element['#description'],
    '#required' => $field['required'],
    '#multiple' => $field['multiple'],
    '#options' => $options,
    '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL,
  );

  // Make sure field info will be available to the validator which
  // does not get the values in $form.
  // TODO for some reason putting the $field array into $form_state['storage']
  // causes the node's hook_form_alter to be invoked twice, garbling the
  // results. Need to investigate why that is happening (a core bug?), but
  // in the meantime avoid using $form_state['storage'] to store anything.
  $form_state['#field_info'][$field['field_name']] = $field;
  return $element;
}

/**
 * Process an individual element.
 *
 * Build the form element. When creating a form using FAPI #process,
 * note that $element['#value'] is already set.
 *
 * The $fields array is in $form['#field_info'][$element['#field_name']].
 */
function optionwidgets_onoff_process($element, $edit, &$form_state, $form) {
  $field_name = $element['#field_name'];
  $field = $form['#field_info'][$field_name];
  $field_key = $element['#columns'][0];
  if (!$form_state['submitted']) {
    $element['#value'] = optionwidgets_data2form($element, $element['#value'], $field);
  }
  $options = optionwidgets_options($field);
  $keys = array_keys($options);
  $on_value = !empty($keys) ? $keys[1] : NULL;
  $element[$field_key] = array(
    '#type' => 'checkbox',
    '#title' => $options[$on_value],
    '#description' => t($field['widget']['description']),
    '#default_value' => isset($element['#value'][$field_key][0]) ? $element['#value'][$field_key][0] == $on_value : FALSE,
    '#return_value' => $on_value,
  );

  // Make sure field info will be available to the validator which
  // does not get the values in $form.
  $form_state['#field_info'][$field['field_name']] = $field;
  return $element;
}

/**
 * FAPI function to validate optionwidgets element.
 *
 * Transpose selections from field => delta to delta => field,
 * turning multiple selected options into multiple parent elements.
 * Immediate parent is the delta, need to get back to parent's parent
 * to create multiple elements.
 */
function optionwidgets_validate($element, &$form_state) {
  $field = $form_state['#field_info'][$element['#field_name']];
  $updated = optionwidgets_form2data($element, $element['#value'], $field);
  form_set_value($element, $updated, $form_state);
}

/**
 * Helper function to transpose the values as stored in the database
 * to the format the widget needs. Can be called anywhere this
 * transformation is needed.
 */
function optionwidgets_data2form($element, $items, $field) {
  $field_key = $element['#columns'][0];
  $options = optionwidgets_options($field);
  $items_transposed = content_transpose_array_rows_cols($items);
  $values = isset($items_transposed[$field_key]) && is_array($items_transposed[$field_key]) ? $items_transposed[$field_key] : array();
  $keys = array();
  foreach ($values as $value) {
    $key = array_search($value, array_keys($options));
    if (isset($key)) {
      $keys[] = $value;
    }
  }
  if ($field['multiple'] || $element['#type'] == 'optionwidgets_onoff') {
    return array(
      $field_key => $keys,
    );
  }
  else {
    return array(
      $field_key => reset($keys),
    );
  }
}

/**
 * Helper function to transpose the values returned by submitting the widget
 * to the format to be stored in the field. Can be called anywhere this
 * transformation is needed.
 */
function optionwidgets_form2data($element, $items, $field) {
  $field_key = $element['#columns'][0];
  $options = optionwidgets_options($field);
  $keys = isset($items[$field_key]) ? $items[$field_key] : array();
  if (!is_array($keys)) {
    $keys = array(
      $keys,
    );
  }
  $values = array();
  foreach ($keys as $key => $value) {
    if (array_key_exists($value, $options)) {
      $values[] = $value;
    }
  }
  if (empty($values)) {
    if ($element['#type'] == 'optionwidgets_onoff') {
      $keys = array_keys($options);
      $values[] = array_key_exists(0, $keys) ? $keys[0] : '';
    }
    else {
      $values[] = NULL;
    }
  }
  return content_transpose_array_rows_cols(array(
    $field_key => $values,
  ));
}

/**
 * Helper function for finding the allowed values list for a field.
 *
 * See if there is a module hook for the option values.
 * Otherwise, try content_allowed_values() for an options list.
 */
function optionwidgets_options($field) {
  $function = $field['module'] . '_allowed_values';
  $options = function_exists($function) ? $function($field) : (array) content_allowed_values($field);

  // Add an empty choice for non required radios and single-selects.
  if ($field['widget']['type'] != 'optionwidgets_onoff' && !$field['required'] && !$field['multiple']) {
    $options = array(
      '' => theme('optionwidgets_none', $field),
    ) + $options;
  }
  return $options;
}

/**
 *  Theme the label for the empty value for options that are not required.
 *  The default theme will display N/A for a radio list and blank for a select.
 */
function theme_optionwidgets_none($field) {
  switch ($field['widget']['type']) {
    case 'optionwidgets_buttons':
      return t('N/A');
    default:
      return '';
  }
}

/**
 * FAPI themes for optionwidgets.
 *
 * The select, checkboxes or radios are already rendered by the
 * select, checkboxes, or radios themes and the HTML output
 * lives in $element['#children']. Override this theme to
 * make custom changes to the output.
 *
 * $element['#field_name'] contains the field name
 * $element['#delta]  is the position of this element in the group
 */
function theme_optionwidgets_select($element) {
  return $element['#children'];
}
function theme_optionwidgets_onoff($element) {
  return $element['#children'];
}
function theme_optionwidgets_buttons($element) {
  return $element['#children'];
}

Functions

Namesort descending Description
optionwidgets_buttons_process Process an individual element.
optionwidgets_data2form Helper function to transpose the values as stored in the database to the format the widget needs. Can be called anywhere this transformation is needed.
optionwidgets_elements Implementation of FAPI hook_elements().
optionwidgets_form2data Helper function to transpose the values returned by submitting the widget to the format to be stored in the field. Can be called anywhere this transformation is needed.
optionwidgets_form_alter @file Defines selection, check box and radio button widgets for text and numeric fields.
optionwidgets_onoff_process Process an individual element.
optionwidgets_options Helper function for finding the allowed values list for a field.
optionwidgets_select_process Process an individual element.
optionwidgets_theme Implementation of hook_theme().
optionwidgets_validate FAPI function to validate optionwidgets element.
optionwidgets_widget Implementation of hook_widget().
optionwidgets_widget_info Implementation of hook_widget_info().
theme_optionwidgets_buttons
theme_optionwidgets_none Theme the label for the empty value for options that are not required. The default theme will display N/A for a radio list and blank for a select.
theme_optionwidgets_onoff
theme_optionwidgets_select FAPI themes for optionwidgets.