multiselect.module in Multiselect 6
Same filename and directory in other branches
Allows users to select multiple items in an easier way than the normal node-reference widget.
File
multiselect.moduleView 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
Name | 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 |