array( 'arguments' => array( 'element' => NULL, ), ), 'select_or_other_none' => array( 'arguments' => array( 'field' => NULL, ), ), ); } /** * Theme a Select (or other) element. */ function theme_select_or_other($element) { // Load the JS file to hide/show the 'other' box when needed. drupal_add_js(drupal_get_path('module', 'select_or_other') .'/select_or_other.js'); $output = "
".print_r($form_state,true));
$form_state['storage'] = $form_state['values'];
}
/**
* Implementation of hook_menu().
*/
function select_or_other_menu() {
$items = array();
$items['select-or-other-test-form'] = array(
'title' => 'select_or_other test',
'page callback' => 'drupal_get_form',
'page arguments' => array('select_or_other_test_form'),
'access arguments' => array('access administration pages'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implementation of hook_widget_info().
*
* This is a CCK hook.
*/
function select_or_other_widget_info() {
return array(
'select_or_other' => array(
'label' => t('Select (or other) list'),
'field types' => array('text', 'number_integer', 'number_decimal', 'number_float'),
'multiple values' => CONTENT_HANDLE_MODULE,
'callbacks' => array(
'default value' => CONTENT_CALLBACK_DEFAULT,
),
),
'select_or_other_sort' => array(
'label' => t('Select (or other) drag and drop lists'),
'field types' => array('text', 'number_integer', 'number_decimal', 'number_float'),
'multiple values' => CONTENT_HANDLE_CORE,
'callbacks' => array(
'default value' => CONTENT_CALLBACK_DEFAULT,
),
),
'select_or_other_buttons' => array(
'label' => t('Select (or other) check boxes/radio buttons'),
'field types' => array('text', 'number_integer', 'number_decimal', 'number_float'),
'multiple values' => CONTENT_HANDLE_MODULE,
'callbacks' => array(
'default value' => CONTENT_CALLBACK_DEFAULT,
),
),
);
}
/**
* Implementation of hook_widget().
*
* This is a CCK hook.
*/
function select_or_other_widget(&$form, &$form_state, $field, $items, $delta = NULL) {
$options = array();
// Create options - similar to code from content_allowed_values().
$list = explode("\n", $field['widget']['available_options']);
if (!empty($field['widget']['available_options_php'])) {
ob_start();
$list = eval($field['widget']['available_options_php']);
ob_end_clean();
}
$list = array_map('trim', $list);
$list = array_filter($list, 'strlen');
foreach ($list as $opt) {
// Sanitize the user input with a permissive filter.
$opt = content_filter_xss($opt);
if (strpos($opt, '|') !== FALSE) {
list($key, $value) = explode('|', $opt);
$options[$key] = (isset($value) && $value !=='') ? html_entity_decode($value) : $key;
}
else {
$options[$opt] = html_entity_decode($opt);
}
}
// Add a 'none' option for non-required single selects, sort selects, and radios
if (!$field['required']) {
if (($field['widget']['type'] == 'select_or_other_buttons' && !$field['multiple'])
|| ($field['widget']['type'] == 'select_or_other' && !$field['multiple'])
|| ($field['widget']['type'] == 'select_or_other_sort')) {
$options = array('' => theme('select_or_other_none', $field)) + $options;
}
}
// Construct the element.
$element = array(
'#type' => 'select_or_other',
'#other' => $field['widget']['other'],
'#default_value' => isset($items[$delta]) ? $items[$delta] : NULL,
'#options' => $options,
'#description' => $field['widget']['description'],
'#multiple' => $field['multiple'],
'#required' => $field['required'],
//'#other_delimiter' => $field['widget']['other_delimiter'] == 'FALSE' ? FALSE : $field['widget']['other_delimiter'],
'#other_delimiter' => FALSE,
'#other_unknown_defaults' => $field['widget']['other_unknown_defaults'],
'#element_validate' => array('select_or_other_cck_validate'),
'#cck_widget' => $field['widget']['type'],
);
// Set select type's.
switch($field['widget']['type']) {
case 'select_or_other':
$element['#select_type'] = 'select';
break;
case 'select_or_other_sort':
$element['#select_type'] = 'select';
// Also disable multiples for this select type.
$element['#multiple'] = FALSE;
break;
case 'select_or_other_buttons':
$element['#select_type'] = $field['multiple'] ? 'checkboxes' : 'radios';
break;
}
// In situations where we handle our own multiples (checkboxes and multiple selects) set defaults differently.
if ($element['#multiple']) {
$element['#default_value'] = array();
foreach ($items as $delta => $item) {
$element['#default_value'][$delta] = $item['value'];
}
}
return $element;
}
/**
* Implementation of hook_widget_settings().
*
* This is a CCK hook.
*/
function select_or_other_widget_settings($op, $widget) {
switch ($op) {
case 'form':
$form = array();
$form['available_options'] = array(
'#type' => 'textarea',
'#title' => t('Available options'),
'#description' => t('A list of values that are, by default, available for selection. Enter one value per line, in the format key|label. The key is the value that will be stored in the database, and the label is what will be displayed to the user. This is not the same as allowed values (below) which will also restrict what a user can type into the other textfield.'),
'#default_value' => isset($widget['available_options']) ? $widget['available_options'] : '',
);
$form['available_options_fieldset']['advanced_options'] = array(
'#type' => 'fieldset',
'#title' => t('PHP code'),
'#collapsible' => TRUE,
'#collapsed' => empty($widget['available_options_php']),
);
if (user_access('Use PHP input for field settings (dangerous - grant with care)')) {
$form['available_options_fieldset']['advanced_options']['available_options_php'] = array(
'#type' => 'textarea',
'#title' => t('Code'),
'#default_value' => !empty($widget['available_options_php']) ? $widget['available_options_php'] : '',
'#rows' => 6,
'#description' => t('Advanced usage only: PHP code that returns a keyed array of available options. Should not include <?php ?> delimiters. If this field is filled out, the array returned by this code will override the available options list above.'),
);
}
else {
$form['available_options_fieldset']['advanced_options']['markup_available_options_php'] = array(
'#type' => 'item',
'#title' => t('Code'),
'#value' => !empty($widget['available_options_php']) ? ''. check_plain($widget['available_options_php']) .'' : t('<none>'),
'#description' => empty($widget['available_options_php']) ? t("You're not allowed to input PHP code.") : t('This PHP code was set by an administrator and will override the allowed values list above.'),
);
}
$form['other'] = array(
'#type' => 'textfield',
'#title' => t('Other option'),
'#description' => t('Label for the option that the user will choose when they want to supply an other value.'),
'#default_value' => isset($widget['other']) ? $widget['other'] : t('Other'),
'#required' => TRUE,
);
$form['other_unknown_defaults'] = array(
'#type' => 'select',
'#title' => t('Other value as default value'),
'#description' => t("If any incoming default values do not appear in available options (i.e. set as other values), what should happen?"),
'#options' => array(
'other' => t('Add the values to the other textfield'),
'append' => t('Append the values to the current list'),
'available' => t('Append the values to the available options'),
'ignore' => t('Ignore the values'),
),
'#default_value' => isset($widget['other_unknown_defaults']) ? $widget['other_unknown_defaults'] : 'other',
'#required' => TRUE,
);
/*
There are design issues with saving multiple other values with some CCK widgets - this needs a rethink.
$form['other_delimiter'] = array(
'#type' => 'textfield',
'#title' => t('Other delimiter'),
'#description' => t("Delimiter string to delimit multiple 'other' values into the other textfield. If you enter FALSE only the last value will be used."),
'#default_value' => isset($widget['other_delimiter']) ? $widget['other_delimiter'] : ', ',
'#required' => TRUE,
'#size' => 5,
);
*/
return $form;
case 'validate':
if (empty($widget['available_options']) && empty($widget['available_options_php'])) {
form_set_error('available_options', t('You must provide Available options.'));
}
break;
case 'save':
return array('available_options', 'available_options_php', 'other', 'other_unknown_defaults', /*'other_delimiter'*/);
}
}
/**
* Element validate callback for a Select (or other) CCK widget.
*/
function select_or_other_cck_validate($element, &$form_state) {
$other_selected = FALSE;
if (is_array($element['select']['#value']) && isset($element['select']['#value']['select_or_other'])) {
// This is a multiselect. assoc arrays
$other_selected = TRUE;
$value = $element['select']['#value'];
unset($value['select_or_other']);
$value[$element['other']['#value']] = $element['other']['#value'];
}
else if (is_string($element['select']['#value']) && $element['select']['#value'] == 'select_or_other') {
// This is a single select.
$other_selected = TRUE;
$value = $element['other']['#value'];
}
else {
$value = $element['select']['#value'];
}
if ($other_selected && !$element['other']['#value'] && $form_state['values']['form_id'] != 'content_field_edit_form') {
form_error($element['other'], t('%name: @title is required', array('%name' => t($element['select']['#title']), '@title' => $element['#other'])));
}
if (isset($value)) {
if (in_array($element['#cck_widget'], array('select_or_other', 'select_or_other_buttons'))) {
// Filter out 'none' value (if present, will always be in key 0)
if (isset($items[0]['value']) && $items[0]['value'] === '') {
unset($items[0]);
}
if ($element['#multiple'] >= 2 && count($value) > $element['#multiple']) {
form_error($element['select'], t('%name: this field cannot hold more than @count values.', array('%name' => t($element['select']['#title']), '@count' => $element['#multiple'])));
}
$delta = 0;
$values = array();
foreach ((array)$value as $v) {
$values[$delta++]['value'] = $v;
}
$value = $values;
}
else if ($element['#cck_widget'] == 'select_or_other_sort') {
$value = array('value' => $value);
}
form_set_value($element, $value, $form_state);
$form_state['clicked_button']['#post'][$element['#name']] = $value; // Is this something we should do?
}
// Add values to available options is configured to do so.
$field_name = &$element['#parents'][0];
$type = &$form_state['values']['type'];
module_load_include('inc', 'content', 'includes/content.crud');
$instance = reset(content_field_instance_read(array('type_name' => $type, 'field_name' => $field_name)));
if ($instance['widget']['other_unknown_defaults'] == 'available') {
if (
($element['select']['#value'] == 'select_or_other' || (is_array($element['select']['#value']) && isset($element['select']['#value']['select_or_other']))) &&
!empty($element['other']['#value']) &&
!isset($element['#options'][$element['other']['#value']])
) {
// Make the change.
$instance['widget']['available_options'] .= "\n" . $element['other']['#value'];
// Save the instance.
content_field_instance_update($instance);
}
}
return $element;
}
/**
* Theme the label for the empty value for options that are not required.
* The default theme will display N/A for a radio list and blank for a select.
*/
function theme_select_or_other_none($field) {
switch ($field['widget']['type']) {
case 'select_or_other_buttons':
return t('N/A');
case 'select_or_other':
case 'select_or_other_sort':
return t('- None -');
default :
return '';
}
}
/**
* Implementation of hook_content_allowed_values_alter().
*
* Integrate with CCK for properly displaying key|value options.
*/
function select_or_other_content_allowed_values_alter(&$allowed_values, $field) {
// Test the operation to avoid validation error with text field validation on saving.
if (
$field['widget']['module'] == 'select_or_other' &&
(empty($_POST['op']) || ( !empty($_POST['op']) && $_POST['op'] != t('Save'))) &&
(empty($_POST['op']) || ( !empty($_POST['op']) && $_POST['op'] != t('Delete')))
) {
$list = explode("\n", $field['widget']['available_options']);
$list = array_map('trim', $list);
$list = array_filter($list, 'strlen');
foreach ($list as $option) {
// Sanitize the user input with a permissive filter.
$option = content_filter_xss($option);
if (strpos($option, '|') !== FALSE) {
list($key, $value) = explode('|', $option);
$allowed_values[$key] = (isset($value) && $value !== '') ? html_entity_decode($value) : $key;
}
else {
$allowed_values[$option] = html_entity_decode($option);
}
}
// Add the other value to the allowed values.
if (!empty($_POST[$field['field_name']]['other'])) {
// Sanitize the user input with a permissive filter.
$value = check_plain(content_filter_xss(trim($_POST[$field['field_name']]['other'])));
if ($value !== '') {
$allowed_values[$value] = $value;
}
}
}
}
/**
* Implementation of hook_apachesolr_cck_fields_alter().
*
* Integrate with apachesolr.module.
*/
function select_or_other_apachesolr_cck_fields_alter(&$mappings) {
$map = array(
'display_callback' => 'apachesolr_cck_text_field_callback',
'indexing_callback' => 'apachesolr_cck_text_indexing_callback',
'index_type' => 'string',
'facets' => TRUE,
);
$mappings['text']['select_or_other_buttons'] = &$map;
$mappings['text']['select_or_other'] = &$map;
$mappings['text']['select_or_other_sort'] = &$map;
}
/**
* Helper function to check keys in multidimensional array.
*
* @param $needle
* The key.
* @param $haystack
* The array to check.
* @return
* Boolean indicating if the key is set.
*/
function select_or_other_multi_array_key_exists($needle, $haystack) {
if (array_key_exists($needle, $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;
}
/**
* Implementation of hook_content_multigroup_allowed_widgets().
*/
function select_or_other_content_multigroup_allowed_widgets() {
return array('select_or_other', 'select_or_other_sort', 'select_or_other_buttons');
}
/**
* Implementation of hook_content_multigroup_no_remove_widgets().
*/
function select_or_other_content_multigroup_no_remove_widgets() {
return array('select_or_other', 'select_or_other_sort', 'select_or_other_buttons');
}