You are here

multiselect.module in Multiselect 6

Allows users to select multiple items in an easier way than the normal node-reference widget.

File

multiselect.module
View source
<?php

/**
 * @file
 * Allows users to select multiple items in an easier way than the normal node-reference widget.
 */

/**
 * Implementation of hook_help().
 */
function multiselect_help($path, $arg) {
  $output = '';
  switch ($path) {
    case 'admin/help#multiselect':
      $output = '<p>' . t('Provides a CCK widget for editing fields that allows users to select from a list of options in a left box and have them visually moved into the right box when options are chosen.') . '</p>';
      break;
  }
  return $output;
}

/**
 * Implementation of hook_form_alter().
 */
function multiselect_form_alter(&$form, $form_state, $form_id) {

  // Provide additional help for the field settings form.
  if ($form_id == 'content_field_edit_form' && isset($form['widget'])) {
    $widget_type = $form['#field']['widget']['type'];
    $field_type = $form['#field']['type'];
    $label = $form['#field']['widget']['label'];
    $output = '<p>' . t('Create a list of options as a list in <strong>Allowed values list</strong> or as an array in PHP code. These values will be the same for %field in all content types.', array(
      '%field' => $label,
    )) . '</p>';
    if (in_array($widget_type, array(
      'multiselect_select',
    ))) {
      if (in_array($field_type, array(
        'text',
        'number_integer',
        'number_float',
        'number_decimal',
      ))) {
        $form['field']['allowed_values_fieldset']['#collapsed'] = FALSE;
        $form['field']['allowed_values_fieldset']['#description'] = $output;

        // If no 'allowed values' were set yet, add a remainder in the messages area.
        if (empty($form_state['post']) && empty($form['field']['allowed_values_fieldset']['allowed_values']['#default_value']) && empty($form['field']['allowed_values_fieldset']['advanced_options']['allowed_values_php']['#default_value'])) {
          drupal_set_message(t("You need to specify the 'allowed values' for this field."), 'warning');
        }
      }
    }
  }
}

/**                                                                                                                                                  
 * Implementation of hook_widget_settings().
 */
function multiselect_widget_settings($op, $widget) {
  switch ($op) {
    case 'form':
      $form['settings'] = array(
        '#type' => 'fieldset',
        '#title' => t('Settings for Options'),
        '#collapsible' => TRUE,
        '#weight' => 10,
      );
      $form['settings']['show_depth'] = array(
        '#type' => 'checkbox',
        '#title' => t('Indent child terms with \' - \' signs'),
        '#default_value' => is_numeric($widget['show_depth']) ? $widget['show_depth'] : 1,
        '#description' => t('If this option is checked, a hierarchy gets visualized by indenting child terms, otherwise it\'s a flat list'),
      );
      return $form;
    case 'save':
      return array(
        'show_depth',
      );
  }
}

/**
 * Implementation of hook_widget_info().
 * This specifies the label and that it is a widget for the different field types.
 */
function multiselect_widget_info() {
  return array(
    'multiselect_select' => array(
      'label' => t('Multiselect'),
      'field types' => array(
        'nodereference',
        'text',
        'number_integer',
        'number_decimal',
        'number_float',
        'userreference',
        'content_taxonomy',
      ),
      '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.
 */
function multiselect_elements() {
  return array(
    'multiselect_select' => array(
      '#input' => TRUE,
      '#columns' => array(
        'uid',
      ),
      '#delta' => 0,
      '#process' => array(
        'multiselect_select_process',
      ),
    ),
  );
}

/**
 * Implementation of hook_widget().
 *
 * hook_widget is a CCK hook
 *
 * 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 $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 multiselect_widget(&$form, &$form_state, $field, $items, $delta = 0) {
  switch ($field['widget']['type']) {
    case 'multiselect_select':
      $element = array(
        '#type' => 'multiselect_select',
        '#default_value' => $items,
      );
      break;
  }
  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 multiselect_select_process($element, $edit, &$form_state, $form) {

  // Insert Javascript and CSS for this widget.
  $path = drupal_get_path('module', 'multiselect');
  drupal_add_js($path . '/multiselect.js');
  drupal_add_css($path . '/multiselect.css');
  $field_name = $element['#field_name'];
  $field = $form['#field_info'][$field_name];
  $field_key = $element['#columns'][0];

  // See if this element is in the database format or the transformed format,
  // and transform it if necessary.
  if (is_array($element['#value']) && !array_key_exists($field_key, $element['#value'])) {
    $element['#value'] = optionwidgets_data2form($element, $element['#default_value'], $field);
  }

  // Get a list of all options for this field.
  $options = optionwidgets_options($field, FALSE);

  // For this specific widget, HTML should be filtered out and entities left unencoded.
  // See content_allowed_values / content_filter_xss / filter_xss.
  content_allowed_values_filter_html($options);

  // Create some arrays for use later in the function.
  $unselected_options = array();
  $selected_options = array();

  // Add selected items to the array first
  if (is_array($element['#value'][$field_key])) {
    foreach ($element['#value'][$field_key] as $key => $value) {
      if (isset($options[$value])) {
        $selected_options[$value] = html_entity_decode(strip_tags($options[$value]));
      }
    }
  }

  // Add the remaining options to the arrays
  foreach ($options as $key => $value) {
    if (!isset($selected_options[$key])) {
      $unselected_options[$key] = $value;

      //$selected_options[$key] = $value;
    }
  }

  // Set up useful variables.
  $addbutton = $element['#field_name'] . "_add";
  $removebutton = $element['#field_name'] . "_remove";
  $selfield = $element['#field_name'] . "_sel";
  $unselfield = $element['#field_name'] . "_unsel";

  // Call methods to create prefix. (ie the non-selected table, etc)
  $prefix_pre = '<div class="form-item multiselect"><label for="edit-title">' . t($element['#title']) . ':';
  if ($field['required']) {
    $prefix_pre .= '<span class="form-required" title="' . t('This field is required.') . '"> * </span>';
  }
  $prefix_pre .= "</label>\n";
  $prefix_pre .= "<div id=\"multiselect_labels" . "_" . $element['#field_name'] . "\" class=\"multiselect_labels\"><div id=\"label_unselected" . "_" . $element['#field_name'] . "\" class=\"label_unselected\">" . t('Available Options') . ":</div>\n";
  $prefix_pre .= "<div id=\"label_selected" . "_" . $element['#field_name'] . "\" class=\"label_selected\">" . t('Selected Options') . ":</div>\n</div>\n";
  $prefix_pre .= "<div id=\"multiselect_available" . "_" . $element['#field_name'] . "\" class=\"multiselect_available\">";
  $prefix_pre .= _multiselect_html_for_unselected_box_start($unselfield, $element['#field_name']);
  $prefix_options = _multiselect_html_for_unselected_box_options($unselected_options);
  $prefix_post = "</select>\n</div>\n";
  $prefix_post .= _html_for_buttons($element['#field_name']);
  $element[$field_key] = array(
    '#type' => 'select',
    '#title' => $element['#title'],
    '#description' => $element['#description'],
    '#required' => isset($element['#required']) ? $element['#required'] : $field['required'],
    '#multiple' => isset($element['#multiple']) ? $element['#multiple'] : $field['multiple'],
    '#options' => $selected_options,
    '#size' => 10,
    '#prefix' => $prefix_pre . $prefix_options . $prefix_post,
    '#suffix' => "\n</div>\n",
    '#attributes' => array(
      'class' => "{$selfield} multiselect_sel",
      'id' => $element['#field_name'],
    ),
    '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL,
  );

  // Set #element_validate in a way that it will not wipe out other
  // validation functions already set by other modules.
  if (empty($element['#element_validate'])) {
    $element['#element_validate'] = array();
  }
  array_unshift($element['#element_validate'], 'optionwidgets_validate');

  // 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;
}

/**
 * Implementation of hook_theme().
 */
function multiselect_theme() {
  return array(
    'multiselect_select' => array(
      'arguments' => array(
        'element' => NULL,
      ),
    ),
  );
}

/**
 * FAPI theme for an individual elements.
 *
 * The textfield or select is already rendered by the
 * textfield or select 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_multiselect_select($element) {
  return $element['#children'];
}

/**
 * Provides html to draw the "not selected" box
 */
function _multiselect_html_for_unselected_box_start($unselfield, $fieldname) {
  $boxhtml = '';
  $boxhtml .= "<select name=\"" . $unselfield . "\" multiple=\"multiple\" class=\"form-select " . $unselfield . " multiselect_unsel\" id=\"" . $fieldname . "\" size=\"10\">\n";
  return $boxhtml;
}
function _multiselect_html_for_unselected_box_options($unselected_options) {
  $boxhtml = '';
  foreach ($unselected_options as $value => $name) {
    $boxhtml .= "<option value=\"" . $value . "\">" . $name . "</option>\n";
  }
  return $boxhtml;
}

/**
 * Provides html to display the buttons on the form.
 */
function _html_for_buttons($fieldname) {
  $buttons_code = "<ul id=\"multiselect_btns" . "_" . $fieldname . "\" class=\"multiselect_btns\">\n<li class=\"multiselect_add\" id=\"" . $fieldname . "\"><a href=\"javascript:;\">Add</a></li>\n<li class=\"multiselect_remove\" id=\"" . $fieldname . "\"><a href=\"javascript:;\">Remove</a></li>\n</ul>";
  return $buttons_code;
}

Functions

Namesort descending Description
multiselect_elements Implementation of FAPI hook_elements().
multiselect_form_alter Implementation of hook_form_alter().
multiselect_help Implementation of hook_help().
multiselect_select_process Process an individual element.
multiselect_theme Implementation of hook_theme().
multiselect_widget Implementation of hook_widget().
multiselect_widget_info Implementation of hook_widget_info(). This specifies the label and that it is a widget for the different field types.
multiselect_widget_settings Implementation of hook_widget_settings().
theme_multiselect_select FAPI theme for an individual elements.
_html_for_buttons Provides html to display the buttons on the form.
_multiselect_html_for_unselected_box_options
_multiselect_html_for_unselected_box_start Provides html to draw the "not selected" box