select_or_other.module in Select (or other) 7.3
Same filename and directory in other branches
The Select (or other) module.
File
select_or_other.moduleView source
<?php
/**
* @file
* The Select (or other) module.
*/
// Include the inc files.
module_load_include('inc', 'select_or_other', 'select_or_other.field_widget');
module_load_include('inc', 'select_or_other', 'select_or_other.field_formatter');
/**
* Implements hook_theme().
*/
function select_or_other_theme() {
return array(
'select_or_other' => array(
'render element' => 'element',
),
);
}
/**
* Theme a Select (or other) element.
*/
function theme_select_or_other($variables) {
$element = $variables['element'];
$output = "<div class=\"select-or-other\">\n";
$output .= drupal_render_children($element) . "\n";
$output .= "</div>\n";
return $output;
}
/**
* Implements hook_element_info().
*/
function select_or_other_element_info() {
return array(
'select_or_other' => array(
'#select_type' => 'select',
'#input' => TRUE,
'#multiple' => FALSE,
'#default_value' => NULL,
'#process' => array(
'select_or_other_element_process',
),
'#element_validate' => array(
'select_or_other_element_validate',
),
'#other' => t('Other'),
'#theme' => 'select_or_other',
'#theme_wrappers' => array(
'form_element',
),
),
);
}
/**
* Implements form_type_hook_value().
*/
function form_type_select_or_other_field_value($element, $edit, $form_state) {
if (func_num_args() == 1) {
return $element['#default_value'];
}
return $edit;
}
/**
* Process callback for a Select (or other) element.
*/
function select_or_other_element_process($element, &$form_state) {
$element['#tree'] = TRUE;
$element['#processed'] = TRUE;
// Load the JS file to hide/show the 'other' box when needed.
$element['#attached']['js'][] = drupal_get_path('module', 'select_or_other') . '/select_or_other.js';
// Create the main select box
// Note that #title, #title_display, #default_value, #multiple, #required,
// #size, #options, and #attributes are passed to the select box from the
// main element automatically.
$element['select'] = array(
'#type' => $element['#select_type'],
'#title' => $element['#title'],
'#title_display' => $element['#title_display'],
'#default_value' => $element['#default_value'],
'#multiple' => $element['#multiple'],
'#required' => $element['#required'],
'#size' => isset($element['#size']) ? $element['#size'] : NULL,
'#options' => $element['#options'],
'#attributes' => $element['#attributes'],
'#weight' => 10,
);
foreach (array(
'#empty_option',
'#empty_value',
) as $key) {
if (isset($element[$key])) {
$element['select'][$key] = $element[$key];
}
}
// Remove the default value on the container level so it doesn't get rendered there.
$element['#value'] = NULL;
// Remove the required parameter so FAPI doesn't force us to fill in the textfield.
$element['#required'] = NULL;
// Now we must handle the default values.
$other_default = array();
// Easier to work with the defaults if they are an array.
if (!is_array($element['select']['#default_value'])) {
$element['select']['#default_value'] = array(
$element['select']['#default_value'],
);
}
// Process the default value.
foreach ($element['select']['#default_value'] as $key => $val) {
if ($val && isset($element['select']['#options']) && is_array($element['select']['#options']) && !select_or_other_multi_array_key_exists($val, $element['select']['#options']) && !in_array($val, $element['select']['#options'])) {
// Not a valid option - add it to 'other'.
if ($element['#other_unknown_defaults'] == 'other') {
if ($element['#other_delimiter']) {
$other_default[] = $val;
}
else {
$other_default = array(
$val,
);
}
// Remove it from the select's default value.
unset($element['select']['#default_value'][$key]);
}
elseif ($element['#other_unknown_defaults'] == 'append' || $element['#other_unknown_new_option']) {
$element['select']['#options'][$val] = $val;
}
}
}
// If the expected default value is a string/integer, remove the array wrapper.
if ($element['#select_type'] == 'radios' || $element['#select_type'] == 'select' && !$element['#multiple']) {
$element['select']['#default_value'] = reset($element['select']['#default_value']);
}
$other_default_string = '';
if (!empty($other_default)) {
$other_default_string = implode($element['#other_delimiter'], $other_default);
if (is_array($element['select']['#default_value'])) {
$element['select']['#default_value'][] = 'select_or_other';
}
else {
$element['select']['#default_value'] = 'select_or_other';
}
}
// Add in the 'other' option.
if (empty($element['#other_disable'])) {
$element['select']['#options']['select_or_other'] = $element['#other'];
// Create the 'other' textfield without the required attribute, if any.
$element['other'] = array(
'#type' => 'textfield',
'#weight' => 20,
'#default_value' => $other_default_string,
'#attributes' => array_diff_key($element['#attributes'], array(
'required' => NULL,
)),
);
}
// Populate properties set specifically as #select_property or #other_property
$sub_elements = array(
'select',
'other',
);
foreach ($sub_elements as $sub_element) {
foreach ($element as $key => $value) {
if (strpos($key, '#' . $sub_element . '_') === 0) {
$element[$sub_element][str_replace('#' . $sub_element . '_', '#', $key)] = $value;
}
}
// Also add in a custom class for each.
$element[$sub_element]['#attributes']['class'][] = 'select-or-other-' . $sub_element;
}
if (!empty($element['#maxlength'])) {
$element['other']['#maxlength'] = $element['#maxlength'];
}
if (isset($element['#other_size'])) {
$element['other']['#size'] = $element['#other_size'];
}
drupal_alter('select_or_other_process', $element, $form_state, $context);
return $element;
}
/**
* Implements hook_process_HOOK().
*/
function select_or_other_process_form_element(&$variables) {
// Hide the title from the wrapper.
if (isset($variables['element']['#theme']) && $variables['element']['#theme'] === 'select_or_other') {
$variables['element']['#title'] = NULL;
}
}
/**
* Element validate callback for a Select (or other) element.
*/
function select_or_other_element_validate($element, &$form_state) {
$other_selected = FALSE;
if (is_array($element['select']['#value']) && isset($element['select']['#value']['select_or_other'])) {
// This is a multi select, which uses associated arrays.
$other_selected = TRUE;
$value = $element['select']['#value'];
unset($value['select_or_other']);
$value[$element['other']['#value']] = $element['other']['#value'];
}
elseif (is_string($element['select']['#value']) && $element['select']['#value'] == 'select_or_other') {
// This is a single select, which uses strings.
$other_selected = TRUE;
$value = $element['other']['#value'];
}
else {
$value = $element['select']['#value'];
}
if ($other_selected && $element['other']['#value'] === '') {
form_error($element['other'], t('!title field is required.', array(
'!title' => $element['select']['#title'],
)));
}
if (isset($value)) {
form_set_value($element, $value, $form_state);
$form_state['clicked_button']['#post'][$element['#name']] = $value;
// Is this something we should do?
}
return $element;
}
/**
* Helper function to check keys in multidimensional array.
*
* @param $needle
* The key.
* @param $haystack
* The array to check.
* @return boolean
* Boolean indicating if the key is set.
*/
function select_or_other_multi_array_key_exists($needle, $haystack) {
if (array_key_exists(html_entity_decode($needle, ENT_QUOTES), $haystack)) {
return TRUE;
}
else {
foreach ($haystack as $key => $value) {
if (is_array($value) && select_or_other_multi_array_key_exists($needle, $value)) {
return TRUE;
}
}
}
return FALSE;
}
Functions
Name![]() |
Description |
---|---|
form_type_select_or_other_field_value | Implements form_type_hook_value(). |
select_or_other_element_info | Implements hook_element_info(). |
select_or_other_element_process | Process callback for a Select (or other) element. |
select_or_other_element_validate | Element validate callback for a Select (or other) element. |
select_or_other_multi_array_key_exists | Helper function to check keys in multidimensional array. |
select_or_other_process_form_element | Implements hook_process_HOOK(). |
select_or_other_theme | Implements hook_theme(). |
theme_select_or_other | Theme a Select (or other) element. |