diff --git a/cck_select_other.install b/cck_select_other.install
index b234b14..6f7ee5c 100644
--- a/cck_select_other.install
+++ b/cck_select_other.install
@@ -1,12 +1,19 @@
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', 'list_number', 'list_text'),
+ 'field types' => array('list_integer', 'list_float', 'list_text'),
'settings' => array(
'select_list_options' => '',
'select_list_options_fieldset' => array(
@@ -30,20 +30,6 @@ function cck_select_other_field_widget_info() {
}
/**
- * Implementation of hook_element_info().
- */
-function cck_select_other_element_info() {
- return array(
- 'cck_select_other' => array(
- '#input' => TRUE,
- '#process' => array('cck_select_other_process'),
- '#post_render' => array('cck_select_other_post_render'),
- '#pre_render' => array('cck_select_other_pre_render'),
- ),
- );
-}
-
-/**
* Implementation of hook_field_formatter_info().
*/
function cck_select_other_field_formatter_info() {
@@ -51,7 +37,7 @@ function cck_select_other_field_formatter_info() {
'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', 'list_boolean'),
+ 'field types' => array('list_integer', 'list_float', 'list_text'),
),
);
}
@@ -65,9 +51,9 @@ function cck_select_other_field_formatter_view($entity_type, $entity, $field, $i
$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(
@@ -122,63 +108,61 @@ function cck_select_other_field_widget_settings_form($field, $instance) {
*/
function cck_select_other_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
$options = cck_select_other_options($instance);
- $def = $instance['required'] ? '' : '_none';
- $otherdef = '';
-
- if (empty($items)) {
- $items[] = array('value' => '');
- }
- if (!isset($items[$delta])) {
- $items[$delta] = array('value' => '');
- }
-
- // Set default value if we have instance data for delta, a default
- // value setting, or something basic if none at all.
- if (!empty($items[$delta]['value'])) {
- $def = (in_array($items[$delta]['value'], array_keys($options))) ? $items[$delta]['value'] : 'other';
- $otherdef = ($def == 'other') ? $items[$delta]['value'] : '';
- }
- else if (isset($instance['default_value'])) {
- $def = isset($instance['default_value'][0]['select_other_list']) ? $instance['default_value'][0]['select_other_list'] : 'other';
- $otherdef = ($def == 'other') ? $instance['default_value'][0]['value'] : '';
- }
-
- // This needs to be pulled out of our original element.
- $description = $element['#description'];
+ $wrapper_attributes = array(
+ 'id' => 'field-' . str_replace('_', '-', substr($field['field_name'], 6)) . '-' . $langcode . '-' . $delta . '-wrapper',
+ 'class' => array('cck-select-other-wrapper'),
+ );
- $element = array(
- '#type' => $instance['widget']['type'],
- '#default_value' => '',
- '#prefix' => '
',
- '#suffix' => '
',
- '#field_name' => $field['field_name'], // Required fields for field_conditional_state.
- '#field_parents' => isset($form['#parents']) ? $form['#parents'] : array(),
+ // 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'),
);
$element['select_other_list'] = array(
- '#title' => check_plain($instance['label']),
- '#description' => !empty($description) ? $description : t('You may specify your own option.'),
+ '#title' => $element['#title'],
+ '#description' => $element['#description'],
'#type' => 'select',
'#options' => $options,
- '#default_value' => $def,
+ '#required' => $instance['required'],
'#attributes' => array(
- 'class' => array('form-text form-select select_other_list'),
+ 'class' => array('form-text form-select select-other-list'),
),
- '#required' => $instance['required'],
);
- // @fixme - #states is REALLY slow here so I'm using my own shit again.
+ // Setup text input.
$element['select_other_text_input'] = array(
'#type' => 'textfield',
- '#default_value' => $otherdef,
+ '#title' => t('Provide other option'),
+ '#title_display' => 'invisible',
+ '#size' => 60,
'#attributes' => array(
- 'class' => array('form-text select_other_text_input'),
+ 'class' => array('form-text select-other-text-input'),
),
- '#element_validate' => array('cck_select_other_widget_validate'), // Always send through this validate function!
);
+ // Default empty values.
+ $list_default = $instance['required'] ? '' : '_none';
+ $text_default = '';
+ $value = isset($items[$delta]['value']) ? $items[$delta]['value'] : '';
+
+ // Value is not empty and value is in list or other.
+ if ($value && in_array($value, array_keys($options))) {
+ $list_default = $value;
+ }
+ elseif ($value) {
+ $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;
}
@@ -209,40 +193,63 @@ function cck_select_other_form_alter(&$form, &$form_state, $form_id) {
* Validate empty text input for other selection.
*/
function cck_select_other_widget_validate($element, &$form_state) {
- // Reverse element parents because of element containers, notably profile2.
- $reversed = array_reverse($element['#parents']);
-
- $element_name = array_shift($reversed);
- $delta = array_shift($reversed);
- $langcode = array_shift($reversed);
- $field_name = array_shift($reversed);
-
- if (isset($form_state['field'][$field_name])) {
- // Retrieve stored field & instance info and form state values.
- $field = $form_state['field'][$field_name];
- $values = &$form_state['values'];
+ $values = drupal_array_get_nested_value($form_state['values'], $element['#array_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 (!empty($reversed) && isset($form_state['field']['#parents'])) {
- // Profile 2 exception.
- $container = array_shift($reversed);
- $field = $form_state['field']['#parents'][$container]['#fields'][$field_name];
- $values = &$form_state['values'][$container];
+ 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 {
- // Catastrophic error..?
- form_set_error($element['#name'], t('An error occurred trying to validate this field.'));
- watchdog('cck_select_other', 'Could not find field info in form state array for select other field, %name.', array('%name' => $field_name), WATCHDOG_ERROR);
+ // Non-empty select list value.
+ form_set_value($element, array('value' => $values['select_other_list']), $form_state);
}
+}
- if ($field[$langcode]['instance']['required'] && $values[$field_name][$langcode][$delta]['select_other_list'] == 'other' && empty($values[$field_name][$langcode][$delta]['select_other_text_input'])) {
- // Empty other field.
- form_set_error($element['#name'], t('A non-empty value is required for this option.'));
- }
+/**
+ * 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) {
+ $settings = array(
+ 'list_element' => $element['select_other_list']['#id'],
+ 'input_element' => $element['select_other_text_input']['#id'],
+ );
- if (!$field[$langcode]['instance']['required'] && $values[$field_name][$langcode][$delta]['select_other_list'] == '_none') {
- // Non-required field value.
- form_set_value($element, array(NULL), $form_state);
- }
+ $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;
}
/**
@@ -299,157 +306,6 @@ function cck_select_other_options($field) {
}
/**
- * CCK Select Other widget process callback
- * @param $element
- * @param &$form_state
- * @return $element;
- */
-function cck_select_other_process($element, &$form_state) {
- if (!isset($element['#name'])) {
- return $element;
- }
-
- $keys = $element['#parents'];
-
- // field_values need to be a reference!
- $field_values = &$form_state['values'];
- foreach ($keys as $key) {
- $field_values = &$field_values[$key];
- }
-
- // Reverse array parents because of element containers, notably profile2.
- $reversed = array_reverse($keys);
-
- $delta = $reversed[0];
- $langcode = $reversed[1];
- $field_name = $reversed[2];
-
- if (isset($field_values) && !empty($field_values)) {
- if ($field_values['select_other_list'] == '_none') {
- // If we are not a required field, then we do not set a value.
- $element['#value'] = '';
- $field_values = array(
- 'value' => '',
- );
- }
- else if ($field_values['select_other_list'] == 'other') {
- // Use text input if we have 'other' selected
- $element['#value'] = $field_values['select_other_text_input'];
- $field_values = array(
- 'value' => $field_values['select_other_text_input'],
- );
- }
- else {
- // Use the select list otherwise
- $element['#value'] = $field_values['select_other_list'];
- $field_values = array(
- 'value' => $field_values['select_other_list'],
- );
- }
-
- return $element;
- }
- else {
- $element['#value'] = '';
- if (isset($element['select_other_list']['#default_value'])) {
- $element['select_other_list']['#value'] = $element['select_other_list']['#default_value'];
- }
- }
-
- return $element;
-}
-
-/**
- * Pre render callback for the form so we can recreate the fake form after it gets
- * blown away by the CCK process callback.
- * @param $element the element
- * @param $form_state
- * @return $form
- */
-function cck_select_other_pre_render($element, $form_state = NULL) {
- static $js;
-
- $errors = form_get_errors();
- if (!empty($errors)) {
- // Validation errors for the text input box get lost so need to be injected.
- $text_element = $element['#name'] . '[select_other_text_input]';
- if (in_array($text_element, array_keys($errors))) {
- $element['select_other_text_input']['#attributes']['class'][] = 'error';
- }
- }
-
- if (!isset($form_state)) {
- return $element;
- }
-
- if (!isset($element['#type']) || $element['#type'] <> 'cck_select_other') {
- return $element;
- }
-
- // No matches = not our field.
- $n = preg_match_all("/[A-Za-z0-9\-\_]+/", $element['#name'], $matches);
- if ($n == 0) {
- return $element;
- }
-
- // By any chance if we don't have any array keys, get out of here.
- $keys = isset($matches[0]) ? $matches[0]: NULL;
- if (!isset($keys)) {
- return $element;
- }
-
- foreach ($keys as $key => $val) {
- $keys[$key] = preg_replace("/_/", '-', $val);
- }
- $field_id = implode('-', $keys);
-
- if (!$js) {
- drupal_add_js(drupal_get_path('module', 'cck_select_other') . '/cck_select_other.js');
- $js = TRUE;
- }
- drupal_add_js(array('CCKSelectOther' => array(array('field_id' => $field_id))), array('type' => 'setting'));
-}
-
-/**
- * Post-render callback to add javascript functionality
- * @param $content
- * @param $element
- * @return $form
- */
-function cck_select_other_post_render($content, $element) {
- static $js;
-
- if ($element['#type'] <> 'cck_select_other') {
- return $content;
- }
-
- // No matches = not our field.
- $n = preg_match_all("/[A-Za-z0-9\-\_]+/", $element['#name'], $matches);
- if ($n == 0) {
- return $element;
- }
-
- // By any chance if we don't have any array keys, get out of here.
- $keys = isset($matches[0]) ? $matches[0]: NULL;
- if (!isset($keys)) {
- return $element;
- }
-
- foreach ($keys as $key => $val) {
- $keys[$key] = preg_replace("/_/", '-', $val);
- }
- $field_id = implode('-', $keys);
-
- if (!$js) {
- drupal_add_js(drupal_get_path('module', 'cck_select_other') . '/cck_select_other.js');
- $js = TRUE;
- }
- drupal_add_js(array('CCKSelectOther' => array(array('field_id' => $field_id))), array('type' => 'setting'));
-
- return $content;
-}
-
-/**
* Implementation of hook_content_migrate_field_alter().
*/
function cck_select_other_content_migrate_field_alter(&$field_value) {
diff --git a/tests/cck_select_other.test b/tests/cck_select_other.test
index 45d1663..51c699f 100644
--- a/tests/cck_select_other.test
+++ b/tests/cck_select_other.test
@@ -1,15 +1,13 @@
drupalLogin($this->web_user);
$this->drupalPost('node/' . $this->test_node->nid . '/edit', $edit, t('Save'));
- $this->assertNoRaw(t('A non-empty value is required for this option.'), t('Did not fail validation for non-required field.'));
+ $this->assertNoRaw(t('You must provide a value for this option.'), t('Did not fail validation for non-required field.'));
$this->drupalLogout();
@@ -220,7 +218,7 @@ class CCKSelectOtherBasicTest extends CCKSelectOtherTest {
$field_str = str_replace('_', '-', $this->test_field['field_name']);
$this->drupalPost('node/' . $this->test_node->nid . '/edit', $edit, t('Save'));
- $this->assertRaw(t('A non-empty value is required for this option.'), t('Failed validation for required field.'));
+ $this->assertRaw(t('You must provide a value for this option.'), t('Failed validation for required field.'));
$elements = $this->xpath('//input[@name="' . $text_field .'" and contains(@class, "error")]');
$this->assertEqual(count($elements), 1, t('Found error class on %field element.', array('%field' => $text_field)));