From 6821d6a9c6d86316f94f64f761312151bc791139 Mon Sep 17 00:00:00 2001
From: Bob Vincent <bobvin@pillars.net>
Date: Tue, 2 Aug 2011 17:54:33 -0400
Subject: [PATCH] Issue #811542 by cdale, Nick Lewis, sun, bojanz, mincn, chx,
 pillarsdotnet, loganfsmyth: Provide helpful error message
 when required radios form element is not selected.

---
 includes/form.inc                            |   33 ++++++++++++++++++++
 modules/field/modules/list/tests/list.test   |    4 ++
 modules/field/modules/options/options.module |    2 +-
 modules/simpletest/tests/form.test           |   39 +++++++++++++++++++++++-
 modules/simpletest/tests/form_test.module    |   43 ++++++++++++++++++++++++++
 5 files changed, 119 insertions(+), 2 deletions(-)

diff --git a/includes/form.inc b/includes/form.inc
index 8c3b14ab4b2329035b4e3a5114d62f69e20c1f3c..8a5b2e41e6e24d107839f96b62d77f40ae371ffc 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -2230,6 +2230,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
diff --git a/modules/field/modules/list/tests/list.test b/modules/field/modules/list/tests/list.test
index 765901a849932f29a352fa35649a2fa8e72713c3..a3aaeebfbabc687b93c9aa17abd73f599c94ea87 100644
--- a/modules/field/modules/list/tests/list.test
+++ b/modules/field/modules/list/tests/list.test
@@ -341,6 +341,10 @@ class ListFieldUITestCase extends FieldTestCase {
       'entity_type' => 'node',
       'bundle' => $this->type,
     );
+    if ($type === 'list_boolean') {
+      $instance['widget']['type'] = 'options_onoff';
+    }
+
     field_create_instance($instance);
 
     $this->admin_path = 'admin/structure/types/manage/' . $this->hyphen_type . '/fields/' . $this->field_name;
diff --git a/modules/field/modules/options/options.module b/modules/field/modules/options/options.module
index 385f3f47f6a7e92cda1698a5cec2bba94fb698ae..740776170e3b669d4d438c89a096c15e21ff3200 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 : (count($default_value) ? reset($default_value) : NULL),
         '#options' => $options,
       );
       break;
diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test
index 386880e78a163fc1826a0fcb407fa4a04e0d397f..af2167feedba24adf8f9781b50869dec8ad3250f 100644
--- a/modules/simpletest/tests/form.test
+++ b/modules/simpletest/tests/form.test
@@ -122,6 +122,43 @@ class FormsTestCase extends DrupalWebTestCase {
   }
 
   /**
+   * Tests #required checkboxes and radios.
+   *
+   * This is a functional test while testRequiredFields above attempts unit.
+   *
+   * @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 +582,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 36a66484511ae7538804358ca72faec3b2204210..50f9c27d6a051d0e60b8c572e7f4fdf5ffbcc860 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) {
-- 
1.7.5.4

