includes/form.inc | 35 ++++++++++++++++++++- modules/field/modules/options/options.module | 2 +- modules/simpletest/tests/form.test | 41 ++++++++++++++++++++++++- modules/simpletest/tests/form_test.module | 43 ++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 3 deletions(-) diff --git a/includes/form.inc b/includes/form.inc index 1b163a0..4fb98b3 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -2239,6 +2239,39 @@ function form_type_checkbox_value($element, $input = FALSE) { } /** + * Helper function to determine the value for a radios form element. + * + * @param $element + * The form element whose value is being populated. + * @param $input + * The incoming input to populate the form element. If this is FALSE, + * the element's default value is returned. + * @return + * The data that will appear in the $element_state['values'] collection + * for this element. Return nothing to use the default. + */ +function form_type_radios_value(&$element, $input = FALSE) { + // User submitted. + if ($input !== FALSE) { + // User submission and nothing selected. + if (!isset($input)) { + // We do not want form builder to override our NULL with an empty string. + $element['#has_garbage_value'] = TRUE; + // There was a user submission so validation is a must. This way, the + // required checker will produce an error as necessary. + $element['#needs_validation'] = TRUE; + } + // Whatever the user submission was, that's the value. + return $input; + } + // Default value now. + if (isset($element['#default_value'])) { + return $element['#default_value']; + } + // NULL. +} + +/** * Helper function to determine the value for a checkboxes form element. * * @param $element @@ -2954,7 +2987,7 @@ function form_process_radios($element) { // The key is sanitized in drupal_attributes() during output from the // theme function. '#return_value' => $key, - '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : NULL, + '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : FALSE, '#attributes' => $element['#attributes'], '#parents' => $element['#parents'], '#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)), diff --git a/modules/field/modules/options/options.module b/modules/field/modules/options/options.module index d4d05ec..25381cb 100644 --- a/modules/field/modules/options/options.module +++ b/modules/field/modules/options/options.module @@ -105,7 +105,7 @@ function options_field_widget_form(&$form, &$form_state, $field, $instance, $lan $element += array( '#type' => $multiple ? 'checkboxes' : 'radios', // Radio buttons need a scalar value. - '#default_value' => $multiple ? $default_value : reset($default_value), + '#default_value' => $multiple ? $default_value : ($default_value ? reset($default_value) : NULL), '#options' => $options, ); break; diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test index 386880e..fad1a8f 100644 --- a/modules/simpletest/tests/form.test +++ b/modules/simpletest/tests/form.test @@ -122,6 +122,45 @@ class FormsTestCase extends DrupalWebTestCase { } /** + * Tests #required checkboxes and radios. + * + * This test programmatically submits a test form containing several types of + * input elements. The form is submitted twice, once with values and once + * without, and each is checked for the proper #required error messages. + * + * @see form_test_validate_required_form() + */ + function testRequiredCheckboxesRadio() { + $form = $form_state = array(); + $form = form_test_validate_required_form($form, $form_state); + + // Verify each error message shows when required value not selected. + $edit = array(); + $this->drupalPost('form-test/validate-required', $edit, 'Submit'); + + foreach (array('textfield', 'checkboxes', 'select', 'radios') as $key) { + $this->assertText(t('!name field is required.', array('!name' => $form[$key]['#title']))); + } + + // The unhelpful catch-all generic error should not appear. + $this->assertNoText(t('An illegal choice has been detected. Please contact the site administrator.')); + + // Verify that no error appears with valid values. + $edit = array( + 'textfield' => $this->randomString(), + 'checkboxes[foo]' => TRUE, + 'select' => 'foo', + 'radios' => 'bar', + ); + $this->drupalPost('form-test/validate-required', $edit, 'Submit'); + + foreach (array('textfield', 'checkboxes', 'select', 'radios') as $key) { + $this->assertNoText(t('!name field is required.', array('!name' => $form[$key]['#title']))); + } + $this->assertNoText(t('An illegal choice has been detected. Please contact the site administrator.')); + } + + /** * Test default value handling for checkboxes. * * @see _form_test_checkbox() @@ -545,7 +584,7 @@ class FormValidationTestCase extends DrupalWebTestCase { */ function testValidateLimitErrors() { $edit = array( - 'test' => 'invalid', + 'test' => 'invalid', 'test_numeric_index[0]' => 'invalid', 'test_substring[foo]' => 'invalid', ); diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module index 36a6648..50f9c27 100644 --- a/modules/simpletest/tests/form_test.module +++ b/modules/simpletest/tests/form_test.module @@ -23,6 +23,12 @@ function form_test_menu() { 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); + $items['form-test/validate-required'] = array( + 'title' => 'Form #required validation', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_validate_required_form'), + 'access callback' => TRUE, + ); $items['form-test/limit-validation-errors'] = array( 'title' => 'Form validation with some error suppression', 'page callback' => 'drupal_get_form', @@ -338,6 +344,43 @@ function form_test_validate_form_validate(&$form, &$form_state) { } /** + * Form constructor for simple #required tests. + */ +function form_test_validate_required_form($form, &$form_state) { + $form['textfield'] = array( + '#type' => 'textfield', + '#title' => 'Textfield', + '#required' => TRUE, + ); + $form['checkboxes'] = array( + '#type' => 'checkboxes', + '#title' => 'Checkboxes', + '#options' => drupal_map_assoc(array('foo', 'bar')), + '#required' => TRUE, + ); + $form['select'] = array( + '#type' => 'select', + '#title' => 'Select', + '#options' => drupal_map_assoc(array('foo', 'bar')), + '#required' => TRUE, + ); + $form['radios'] = array( + '#type' => 'radios', + '#title' => 'Radios', + '#options' => drupal_map_assoc(array('foo', 'bar')), + '#required' => TRUE, + ); + $form['radios_optional'] = array( + '#type' => 'radios', + '#title' => 'Radios (optional)', + '#options' => drupal_map_assoc(array('foo', 'bar')), + ); + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array('#type' => 'submit', '#value' => 'Submit'); + return $form; +} + +/** * Builds a simple form with a button triggering partial validation. */ function form_test_limit_validation_errors_form($form, &$form_state) {