cck_select_other.module in CCK Select Other 7.2
Same filename and directory in other branches
Implements a select list widget that lets a user provide an alternate option.
File
cck_select_other.moduleView source
<?php
/**
* @file
* Implements a select list widget that lets a user provide an alternate option.
*/
/**
* Implementation of hook_field_widget_info().
*/
function cck_select_other_field_widget_info() {
return array(
'cck_select_other' => array(
'label' => t('Select other list'),
'description' => t('Provides an "other" option, which allows the user to provide an alternate value.'),
'field types' => array(
'list_integer',
'list_float',
'list_text',
),
'settings' => array(
'select_list_options' => '',
'select_list_options_fieldset' => array(
'advanced_options' => array(
'select_list_options_php' => '',
),
),
),
'behaviors' => array(
'default value' => FIELD_BEHAVIOR_DEFAULT,
),
),
);
}
/**
* Implementation of hook_field_formatter_info().
*/
function cck_select_other_field_formatter_info() {
return array(
'cck_select_other' => array(
'label' => t('Select other'),
'description' => t('The default list module formatters do not take into account select other list widgets.'),
'field types' => array(
'list_integer',
'list_float',
'list_text',
),
),
);
}
/**
* Implementation of hook_field_formatter_view().
*/
function cck_select_other_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
if ($display['type'] == 'cck_select_other') {
// Only format for the cck_select_other display (just in case).
$settings = $instance['widget']['settings'];
$options = cck_select_other_options($instance);
$element = array();
foreach ($items as $delta => $item) {
$value = isset($options[$item['value']]) ? field_filter_xss($options[$item['value']]) : field_filter_xss($item['value']);
$element[$delta] = array(
'#markup' => $value,
);
}
return $element;
}
}
/**
* Implementation of hook_field_widget_settings_form().
*/
function cck_select_other_field_widget_settings_form($field, $instance) {
$widget = $instance['widget'];
$settings = $widget['settings'];
$form['select_list_options'] = array(
'#type' => 'textarea',
'#title' => t('Select list options'),
'#description' => t('CCK Select Other uses a separate text area to generate options. You may also put restricted values in the Allowed Values text area.'),
'#default_value' => !empty($settings['select_list_options']) ? $settings['select_list_options'] : 'other|Other',
);
$form['select_list_options_fieldset']['advanced_options'] = array(
'#type' => 'fieldset',
'#title' => t('PHP code'),
'#collapsible' => TRUE,
'#collapsed' => empty($settings['select_list_options_fieldset']['advanced_options']['select_list_options_php']),
);
$form['select_list_options_fieldset']['advanced_options']['select_list_options_php'] = array(
'#type' => 'textarea',
'#title' => t('Code'),
'#default_value' => !empty($settings['select_list_options_fieldset']['advanced_options']['select_list_options_php']) ? $settings['select_list_options_fieldset']['advanced_options']['select_list_options_php'] : '',
'#rows' => 6,
'#description' => t('Advanced usage only: PHP code that returns a keyed array of proposed select list options. Should not include <?php ?> delimiters. If this field is filled out, the array returned by this code will override the proposed select list options above.'),
);
if (!user_access('use PHP for settings')) {
$form['select_list_options_fieldset']['advanced_options']['select_list_options_php']['#disabled'] = TRUE;
$form['select_list_options_fieldset']['advanced_options']['select_list_options_php']['#prefix'] = t('You do not have access to write PHP code to generate select list options.');
}
return $form;
}
/**
* Implementation of hook_field_widget_form().
*/
function cck_select_other_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
$options = cck_select_other_options($instance);
// Setup select other wrapper.
$element += array(
'#bundle' => $instance['bundle'],
'#field_name' => $field['field_name'],
'#langcode' => $langcode,
'#element_validate' => array(
'cck_select_other_widget_validate',
),
'#pre_render' => array(
'cck_select_other_widget_pre_render',
),
'#attributes' => array(
'class' => array(
'form-select-other-wrapper',
'cck-select-other-wrapper',
),
),
);
// Setup select list.
$element['select_other_list'] = array(
'#title' => $element['#title'],
'#description' => $element['#description'],
'#type' => 'select',
'#options' => $options,
'#required' => $instance['required'],
'#attributes' => array(
'class' => array(
'form-text form-select form-select-other-list',
),
),
);
// Setup text input.
$element['select_other_text_input'] = array(
'#type' => 'textfield',
'#title' => t('Provide other option'),
'#title_display' => 'invisible',
'#size' => 60,
'#attributes' => array(
'class' => array(
'form-text form-select-other-text-input',
),
),
);
// Default empty values.
$list_default = $instance['required'] ? '' : '_none';
$text_default = '';
$value = '';
if (isset($items[$delta]['value'])) {
// Use the value provided in items.
$value = $items[$delta]['value'];
}
elseif (isset($instance['default_value'])) {
// Use the default value of the field if it is set.
$value = $instance['default_value'][0]['value'];
}
if ($value && in_array($value, array_keys($options))) {
// Value is not empty and value is in the list.
$list_default = $value;
}
elseif ($value) {
// Set the list default to other.
$list_default = 'other';
$text_default = $value;
}
// Set default values.
$element['select_other_list']['#default_value'] = $list_default;
$element['select_other_text_input']['#default_value'] = $text_default;
return $element;
}
/**
* Implementation of hook_form_alter().
*/
function cck_select_other_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'field_ui_field_edit_form' && isset($form_state['build_info']['args'][0]['widget']) && $form_state['build_info']['args'][0]['widget']['type'] == 'cck_select_other' && isset($form['field']['settings']['allowed_values']) && isset($form['widget']['settings']['allowed_values'])) {
// If the field is already created, just remove the validation callback.
// @todo fix Drupal core to not require a field validation function or make it alterable.
$form['field']['settings']['allowed_values']['#element_validate'] = NULL;
}
else {
if ($form_id == 'field_ui_field_settings_form' && isset($form_state['build_info']['args'][0]['widget']) && $form_state['build_info']['args'][0]['widget']['type'] == 'cck_select_other') {
// If the field is new, then we need to provide some better feedback to theh user about this.
$form['field']['settings']['allowed_values']['#prefix'] = '<div class="messages warning">' . t('Select other lists do not require allowed values. You will be prompted to provide select list options after you save field settings. <strong>It is highly recommended that you do not provide allowed values during this step.</strong>') . '</div>';
}
}
}
/**
* Validate empty text input for other selection.
*/
function cck_select_other_widget_validate($element, &$form_state) {
$values = drupal_array_get_nested_value($form_state['values'], $element['#array_parents']);
if (empty($values)) {
// Field UI does not behave in the same way as normal form operations, and
// values should be extracted from $element['#parents'] instead.
$values = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
}
if (!$element['select_other_list']['#required'] && $values['select_other_list'] == '_none') {
// Empty select list option.
form_set_value($element, array(
'value' => NULL,
), $form_state);
}
elseif ($element['select_other_list']['#required'] && $values['select_other_list'] == '') {
// Empty select list option for required field.
form_set_value($element, array(
'value' => '',
), $form_state);
form_error($element, t('You must select an option.'));
}
elseif ($element['select_other_list']['#required'] && $values['select_other_list'] == 'other' && !$values['select_other_text_input']) {
// Empty text input for required field.
form_set_value($element, array(
'value' => NULL,
), $form_state);
form_error($element['select_other_text_input'], t('You must provide a value for this option.'));
}
elseif ($values['select_other_list'] == 'other' && $values['select_other_text_input']) {
// Non-empty text input value.
form_set_value($element, array(
'value' => $values['select_other_text_input'],
), $form_state);
}
elseif ($values['select_other_list'] == 'other' && !$values['select_other_text_input']) {
// Empty text for non-required field.
form_set_value($element, array(
'value' => NULL,
), $form_state);
}
else {
// Non-empty select list value.
form_set_value($element, array(
'value' => $values['select_other_list'],
), $form_state);
}
$field = field_info_field($element['#field_name']);
// Validate integer and float values for other options.
if ($field['type'] == 'list_integer' && $values['select_other_list'] == 'other') {
if (!preg_match('/^-?\\d+$/', $values['select_other_text_input'])) {
form_error($element['select_other_text_input'], t('Only integers are allowed.'));
}
}
elseif ($field['type'] == 'list_float' && $values['select_other_list'] == 'other') {
if (!is_numeric($values['select_other_text_input'])) {
form_error($element['select_other_text_input'], t('Only valid numbers are allowed.'));
}
}
}
/**
* Attaches Javascript during pre build because this is when array parents
* should be defined to take advantage of modules that alter the element
* structure such as field_collection.
*
* @param $element
* The element array.
* @return array
* The element array.
*/
function cck_select_other_widget_pre_render($element) {
$key = $element['#field_name'] . '_' . $element['#delta'];
$settings = array(
$key => array(
'list_element' => $element['select_other_list']['#id'],
'input_element' => $element['select_other_text_input']['#id'],
),
);
$element['#attached'] = array(
'js' => array(
array(
'data' => array(
'CCKSelectOther' => $settings,
),
'type' => 'setting',
),
drupal_get_path('module', 'cck_select_other') . '/cck_select_other.js',
),
);
return $element;
}
/**
* Retrieve options for the select list
* @param $field the field instance we're working with
* @return an array of options to pass into the Form API.
*/
function cck_select_other_options($field) {
if (!isset($field['widget'])) {
return array();
}
$options = eval($field['widget']['settings']['select_list_options_fieldset']['advanced_options']['select_list_options_php']);
if (empty($options)) {
$options_str = $field['widget']['settings']['select_list_options'];
if (!empty($options_str)) {
$options_arr = preg_split("/[\r]?[\n]/", $options_str);
if (count($options_arr) > 0) {
foreach ($options_arr as $option_str) {
$option_arr = preg_split("/\\|/", $option_str);
if (count($option_arr) == 2) {
$options[check_plain($option_arr[0])] = t('@option', array(
'@option' => $option_arr[1],
));
}
else {
$options[check_plain($option_arr[0])] = t('@option', array(
'@option' => $option_arr[0],
));
}
}
}
}
}
else {
foreach ($options as $key => $option) {
if (!is_numeric($key)) {
$key = check_plain($key);
}
$options[$key] = t('@option', array(
'@option' => $option,
));
}
}
if (!isset($options['other'])) {
$options['other'] = t('Other');
}
if (!$field['required']) {
$options = array(
'_none' => t('- None - '),
) + $options;
}
else {
$options = array(
'' => t('- Select a value -'),
) + $options;
}
return $options;
}
/**
* Implementation of hook_content_migrate_field_alter().
*/
function cck_select_other_content_migrate_field_alter(&$field_value) {
if ($field_value['type'] == 'cck_select_other') {
$field_value['type'] = 'list_text';
$field_value['module'] = 'list';
}
}
/**
* Implementation of hook_content_migrate_instance_alter().
*/
function cck_select_other_content_migrate_instance_alter(&$instance_value) {
if ($instance_value['widget']['module'] == 'cck_select_other') {
// Yay! We actually don't need to do anything. But I'm going to call this anyway.
}
}
/**
* Implementation of hook_views_api().
*/
function cck_select_other_views_api() {
return array(
'api' => '3',
'path' => drupal_get_path('module', 'cck_select_other') . '/views',
);
}