You are here

boolean.module in Boolean Field 7

Defines boolean field type.

The field has three states: on, off and not set. The corresponding storage values are 1, 0, and -1.

@author Jim Berry ("solotandem", http://drupal.org/user/240748)

File

boolean.module
View source
<?php

/**
 * @file
 * Defines boolean field type.
 *
 * The field has three states: on, off and not set. The corresponding storage
 * values are 1, 0, and -1.
 *
 * @author Jim Berry ("solotandem", http://drupal.org/user/240748)
 */

/**
 * Implements hook_help().
 */
function boolean_help($path, $arg) {
  switch ($path) {
    case 'admin/help#boolean':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('The Boolean module defines a 3-state boolean field type 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_field_info().
 *
 * @todo Adding settings causes 'undefined index' notice until settings are saved.
 */
function boolean_field_info() {
  return array(
    'number_boolean' => array(
      'label' => t('Boolean (3-states)'),
      'description' => t('This field stores simple on/off or yes/no options along with a not set state.'),
      'instance_settings' => array(
        'labels' => array(),
        'suppress_warning' => FALSE,
        'omit_strings' => FALSE,
        'none' => array(
          'prefix' => '',
          'suffix' => '',
        ),
        'false' => array(
          'prefix' => '',
          'suffix' => '',
        ),
        'true' => array(
          'prefix' => '',
          'suffix' => '',
        ),
      ),
      'default_widget' => 'boolean_checkbox',
      'default_formatter' => 'number_boolean',
      // For use with Entity API and Rules modules.
      'property_type' => 'number_boolean',
    ),
  );
}

/**
 * Implements hook_field_settings_form().
 */
function boolean_field_settings_form($field, $instance, $has_data) {

  //   $settings = $field['settings'];
  $form = array();
  return $form;
}

/**
 * Implements hook_field_instance_settings_form().
 *
 * Initially build the form without the vertical tabs so that the instance
 * settings form items are parented like instance[settings][true][prefix]. If we
 * try to build it all at once, then the parenting is nested two more levels
 * like instance[settings][state][tabs][true][prefix] and does not line up with
 * the instance settings as defined in the hook_field_info().
 *
 * Then use an #after_build handler to insert the fieldset and vertical tabs.
 */
function boolean_field_instance_settings_form($field, $instance) {
  $settings = $instance['settings'];
  $defaults = boolean_field_info();
  $defaults = $defaults['number_boolean']['instance_settings'];
  $settings += $defaults;
  $form['labels'] = array(
    '#type' => 'textarea',
    '#title' => t('Labels'),
    '#default_value' => $settings['labels'],
    '#cols' => 20,
    '#description' => t("The labels to use in place of 'delta' in the display strings and on edit form. Enter one value per line. Only applies if cardinality is not unlimited."),
  );
  $form['suppress_warning'] = array(
    '#type' => 'checkbox',
    '#title' => t('Omit warning'),
    '#default_value' => $settings['suppress_warning'],
    '#description' => t("Omit the 'value not set' warning for unset fields."),
  );
  $form['omit_strings'] = array(
    '#type' => 'checkbox',
    '#title' => t('Omit strings'),
    '#default_value' => $settings['omit_strings'],
    '#description' => t('Omit the prefix and suffix strings on edit form.'),
  );
  foreach (boolean_value_info() as $state) {
    $form[$state] = array(
      '#type' => 'fieldset',
      '#title' => 'Strings to display when value is ' . $state,
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#tree' => TRUE,
      '#weight' => 5,
    );
    $form[$state]['prefix'] = array(
      '#type' => 'textfield',
      '#title' => t('Prefix'),
      '#default_value' => $settings[$state]['prefix'],
      '#size' => 60,
      '#description' => t("The string to be prefixed to the 'delta' value. Leave blank for none."),
    );
    $form[$state]['suffix'] = array(
      '#type' => 'textfield',
      '#title' => t('Suffix'),
      '#default_value' => $settings[$state]['suffix'],
      '#size' => 60,
      '#description' => t("The string to be suffixed to the 'delta' value. Leave blank for none."),
    );
  }
  $form['#after_build'][] = 'boolean_field_instance_settings_form_after_build';
  return $form;
}

/**
 * Form handler for after_build.
 *
 * Rebuild the element form items as vertical tabs. Retain form items added by
 * other modules.
 *
 * @see boolean_field_instance_settings_form()
 */
function boolean_field_instance_settings_form_after_build($element, $form_state) {
  $string = 'The display value of this field is equal to its "delta" value surrounded by the prefix and suffix strings defined below.' . ' The "delta" value is only displayed if the "number of values" for this field is greater than one.' . ' For example, if there are five values of this field, then the delta values are numbered zero through four.' . ' The corresponding display values are "prefix delta suffix."';
  $element['state'] = array(
    '#type' => 'fieldset',
    '#title' => 'Display strings',
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
    '#tree' => FALSE,
    '#weight' => 5,
    '#description' => t($string),
  );
  $element['state']['tabs'] = array(
    '#type' => 'vertical_tabs',
    '#default_tab' => 'edit-true',
  );
  foreach (boolean_value_info() as $state) {
    $element['state']['tabs'][$state] = $element[$state];
    unset($element[$state]);
  }
  return $element;
}

/**
 * Implements hook_field_is_empty().
 */
function boolean_field_is_empty($item, $field) {
  if (empty($item['value']) && (string) $item['value'] !== '0') {
    return TRUE;
  }
  return FALSE;
}

/**
 * Implements hook_field_formatter_info().
 */
function boolean_field_formatter_info() {
  return array(
    'number_boolean' => array(
      'label' => t('Default'),
      'field types' => array(
        'number_boolean',
      ),
      'settings' => array(
        'labels' => FALSE,
        'omit_unchecked' => FALSE,
        'prefix_suffix' => TRUE,
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_settings_form().
 */
function boolean_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];

  // @todo This is not treated as a setting; presence of labels is used.
  $element['labels'] = array(
    '#type' => 'checkbox',
    '#title' => t('Labels'),
    '#default_value' => $settings['labels'],
    '#description' => t('Display the labels in place of delta.'),
  );
  $element['omit_unchecked'] = array(
    '#type' => 'checkbox',
    '#title' => t('Omit unchecked'),
    '#default_value' => $settings['omit_unchecked'],
    '#description' => t('Omit the value if not checked.'),
  );
  $element['prefix_suffix'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display prefix and suffix.'),
    '#default_value' => $settings['prefix_suffix'],
  );
  return $element;
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function boolean_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $labels = $settings['labels'] ? 'labels' : 'delta';
  $unchecked = $settings['omit_unchecked'] ? ' only if checked' : '';
  $with = $settings['prefix_suffix'] ? 'with' : 'without';
  $summary = t('Display !labels !with prefix and suffix!unchecked.', array(
    '!labels' => $labels,
    '!unchecked' => $unchecked,
    '!with' => $with,
  ));
  return $summary;
}

/**
 * Returns boolean values and related strings.
 */
function boolean_value_info() {
  return array(
    -1 => 'none',
    0 => 'false',
    1 => 'true',
  );
}

/**
 * Implements hook_field_formatter_view().
 */
function boolean_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  $settings = $display['settings'];

  // Allow for a site using the cardinality patch.
  // @see https://www.drupal.org/files/issues/1029298-60-move-cardinality-setting.patch
  $cardinality = isset($instance['cardinality']) ? $instance['cardinality'] : $field['cardinality'];
  $labels = explode("\n", $instance['settings']['labels']);
  switch ($display['type']) {
    case 'number_boolean':
      $display_delta = count($items) > 1;
      foreach ($items as $delta => $item) {
        if ($settings['omit_unchecked'] && $item['value'] != 1) {
          continue;
        }
        $display_label = '';
        if ($cardinality == FIELD_CARDINALITY_UNLIMITED) {
          $display_label = $delta;
        }
        elseif ($settings['labels'] && !empty($labels[$delta])) {
          $display_label = $labels[$delta];
        }
        elseif ($cardinality > 1) {
          $display_label = $delta;
        }
        $prefix = $suffix = '';
        $states = boolean_value_info();
        $strings = $instance['settings'][$states[$item['value']]];
        $class = 'state-' . $states[$item['value']];

        //         if (TRUE || $item['value']) { // @todo Do we need a check here?
        if ($settings['prefix_suffix']) {
          $prefix = isset($strings['prefix']) ? $strings['prefix'] . ' ' : 'Instance ';
          $suffix = isset($strings['suffix']) ? ' ' . $strings['suffix'] : '';
        }
        elseif (!$settings['labels']) {
          $prefix = 'Instance ';
          $suffix = '';
        }
        $output = '<span class="' . $class . '">';
        $output .= $prefix . $display_label . $suffix;
        $output .= '</span>';
        $element[$delta] = array(
          '#markup' => $output,
          '#attributes' => array(
            'class' => 'state-' . $states[$item['value']],
          ),
        );

        //         }
      }
      break;
  }
  return $element;
}

/**
 * Implements hook_field_widget_info().
 */
function boolean_field_widget_info() {
  return array(
    'boolean_checkbox' => array(
      'label' => t('Checkbox'),
      'field types' => array(
        'number_boolean',
      ),
      'behaviors' => array(
        // @todo Is FIELD_BEHAVIOR_CUSTOM what causes the list_boolean field not
        // to have multiple values?
        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
      ),
    ),
  );
}

/**
 * Implements hook_field_widget_form().
 */
function boolean_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {

  // Allow for a site using the cardinality patch.
  // @see https://www.drupal.org/files/issues/1029298-60-move-cardinality-setting.patch
  $cardinality = isset($instance['cardinality']) ? $instance['cardinality'] : $field['cardinality'];
  $labels = explode("\n", $instance['settings']['labels']);

  // If the boolean field is added to an entity after instances of that entity
  // exist, then $delta == count($items) is TRUE when $delta == 0. Add condition
  // on $delta > 0 to catch this use case and display the field.
  if ($cardinality == FIELD_CARDINALITY_UNLIMITED && $delta && $delta == count($items)) {

    // Suppress the automatic display of another item by the Field API to
    // prevent the item being added to the database if the form is submitted.
    // This should be resolved in HTML 5 when a checkbox will have a third state
    // indicating whether it is set.
    return array();
  }
  $is_set = isset($items[$delta]) && $items[$delta]['value'] != -1;
  $suppress = empty($instance['settings']['suppress_warning']) ? FALSE : TRUE;
  $value = $is_set ? $items[$delta]['value'] : 0;
  $display_delta = count($items) > 1;

  // @todo Is this ever true? This is passed one item.
  $display_label = '';
  if ($cardinality == FIELD_CARDINALITY_UNLIMITED) {
    $display_label = $delta;
  }
  elseif (!empty($labels[$delta])) {
    $display_label = $labels[$delta];
  }
  elseif ($cardinality > 1) {
    $display_label = $delta;
  }
  $element += array(
    '#type' => 'checkbox',
    '#default_value' => $value,
    '#on_value' => $on_value = 1,
    '#off_value' => $off_value = 0,
    '#no_value' => -1,
    '#is_set' => $is_set,
  );
  $prefix = $suffix = '';
  if (empty($instance['settings']['omit_strings'])) {

    // Add prefix and suffix.
    // When editing, always display the text applicable to a true state.
    $states = boolean_value_info();
    $strings = $instance['settings'][$states[1]];

    // [$items[$delta]['value']]];
    $prefix = isset($strings['prefix']) ? $strings['prefix'] . ' ' : '';
    $suffix = isset($strings['suffix']) ? ' ' . $strings['suffix'] : '';
  }

  // Set the title.
  $element['#title'] = $prefix . $display_label . $suffix;
  if (!$is_set && !$suppress) {

    // Highlight the field so the user knows it has not been set.
    $element['#title'] .= ' (value not set)';
    $element += array(
      '#prefix' => '<div class="not-set" style="color: red;">',
      '#suffix' => '</div>',
    );
  }
  $element['#value_callback'] = 'boolean_field_widget_value';
  $element['#element_validate'][] = 'boolean_field_widget_validate';
  return array(
    'value' => $element,
  );
}

/**
 * Form element validation handler for boolean field element.
 */
function boolean_field_widget_validate($element, &$form_state, $form) {
  $value = $element['#value'];

  // Transform '0 / 1' into the 'on / off / not set' values.
  if (!$element['#is_set'] && $value === 0) {
    $value = $element['#no_value'];
  }
  else {
    $value = $value ? $element['#on_value'] : $element['#off_value'];
  }
  form_set_value($element, $value, $form_state);
}

/**
 * Form element value callback for the boolean field element.
 */
function boolean_field_widget_value($element, $input = FALSE, $form_state = array()) {
  if ($input === FALSE) {
    return isset($element['#default_value']) ? $element['#default_value'] : 0;
  }
  else {

    // With 3 states, a value of -1 is also considered TRUE. So check for 1.
    return $input == 1 ? $input : 0;
  }
}

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