From 0d74d52c2a2dec423cd35a903df593554da90a93 Mon Sep 17 00:00:00 2001
From: Dries Buytaert <dries@buytaert.net>
Date: Sun, 7 Nov 2010 21:46:09 +0000
Subject: [PATCH] - Patch #915936 by sun: make it easier to define
 checkboxes/radios with customized sub-elements.

---
 includes/form.inc                         | 75 +++++++++++++----------
 modules/comment/comment.module            | 15 -----
 modules/simpletest/tests/form.test        | 60 ++++++++++++++++++
 modules/simpletest/tests/form_test.module | 71 +++++++++++++++++++++
 4 files changed, 174 insertions(+), 47 deletions(-)

diff --git a/includes/form.inc b/includes/form.inc
index 8bf2ba072a5b..e3a217bda820 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -2786,22 +2786,28 @@ function weight_value(&$form) {
  */
 function form_process_radios($element) {
   if (count($element['#options']) > 0) {
+    $weight = 0;
     foreach ($element['#options'] as $key => $choice) {
-      if (!isset($element[$key])) {
-        // Generate the parents as the autogenerator does, so we will have a
-        // unique id for each radio button.
-        $parents_for_id = array_merge($element['#parents'], array($key));
-        $element[$key] = array(
-          '#type' => 'radio',
-          '#title' => $choice,
-          '#return_value' => check_plain($key),
-          '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : NULL,
-          '#attributes' => $element['#attributes'],
-          '#parents' => $element['#parents'],
-          '#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)),
-          '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
-        );
-      }
+      // Maintain order of options as defined in #options, in case the element
+      // defines custom option sub-elements, but does not define all option
+      // sub-elements.
+      $weight += 0.001;
+
+      $element += array($key => array());
+      // Generate the parents as the autogenerator does, so we will have a
+      // unique id for each radio button.
+      $parents_for_id = array_merge($element['#parents'], array($key));
+      $element[$key] += array(
+        '#type' => 'radio',
+        '#title' => $choice,
+        '#return_value' => check_plain($key),
+        '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : NULL,
+        '#attributes' => $element['#attributes'],
+        '#parents' => $element['#parents'],
+        '#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)),
+        '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
+        '#weight' => $weight,
+      );
     }
   }
   return $element;
@@ -2911,25 +2917,30 @@ function form_process_checkboxes($element) {
     if (!isset($element['#default_value']) || $element['#default_value'] == 0) {
       $element['#default_value'] = array();
     }
+    $weight = 0;
     foreach ($element['#options'] as $key => $choice) {
-      if (!isset($element[$key])) {
-        // Integer 0 is not a valid #return_value, so use '0' instead.
-        // @see form_type_checkbox_value().
-        // @todo For Drupal 8, cast all integer keys to strings for consistency
-        //   with form_process_radios().
-        if ($key === 0) {
-          $key = '0';
-        }
-        $element[$key] = array(
-          '#type' => 'checkbox',
-          '#processed' => TRUE,
-          '#title' => $choice,
-          '#return_value' => $key,
-          '#default_value' => isset($value[$key]) ? $key : NULL,
-          '#attributes' => $element['#attributes'],
-          '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
-        );
+      // Integer 0 is not a valid #return_value, so use '0' instead.
+      // @see form_type_checkbox_value().
+      // @todo For Drupal 8, cast all integer keys to strings for consistency
+      //   with form_process_radios().
+      if ($key === 0) {
+        $key = '0';
       }
+      // Maintain order of options as defined in #options, in case the element
+      // defines custom option sub-elements, but does not define all option
+      // sub-elements.
+      $weight += 0.001;
+
+      $element += array($key => array());
+      $element[$key] += array(
+        '#type' => 'checkbox',
+        '#title' => $choice,
+        '#return_value' => $key,
+        '#default_value' => isset($value[$key]) ? $key : NULL,
+        '#attributes' => $element['#attributes'],
+        '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
+        '#weight' => $weight,
+      );
     }
   }
   return $element;
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index 03c274c83fb6..c0058f40fe58 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -1190,28 +1190,13 @@ function comment_form_node_form_alter(&$form, $form_state) {
       COMMENT_NODE_HIDDEN => t('Hidden'),
     ),
     COMMENT_NODE_OPEN => array(
-      '#type' => 'radio',
-      '#title' => t('Open'),
       '#description' => t('Users with the "Post comments" permission can post comments.'),
-      '#return_value' => COMMENT_NODE_OPEN,
-      '#default_value' => $comment_settings,
-      '#parents' => array('comment'),
     ),
     COMMENT_NODE_CLOSED => array(
-      '#type' => 'radio',
-      '#title' => t('Closed'),
       '#description' => t('Users cannot post comments, but existing comments will be displayed.'),
-      '#return_value' => COMMENT_NODE_CLOSED,
-      '#default_value' => $comment_settings,
-      '#parents' => array('comment'),
     ),
     COMMENT_NODE_HIDDEN => array(
-      '#type' => 'radio',
-      '#title' => t('Hidden'),
       '#description' => t('Comments are hidden from view.'),
-      '#return_value' => COMMENT_NODE_HIDDEN,
-      '#default_value' => $comment_settings,
-      '#parents' => array('comment'),
     ),
   );
   // If the node doesn't have any comments, the "hidden" option makes no
diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test
index 1f76318f571b..a0df090524e9 100644
--- a/modules/simpletest/tests/form.test
+++ b/modules/simpletest/tests/form.test
@@ -369,6 +369,66 @@ class FormsTestCase extends DrupalWebTestCase {
   }
 }
 
+/**
+ * Tests building and processing of core form elements.
+ */
+class FormElementTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Element processing',
+      'description' => 'Tests building and processing of core form elements.',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp(array('form_test'));
+  }
+
+  /**
+   * Tests expansion of #options for #type checkboxes and radios.
+   */
+  function testOptions() {
+    $this->drupalGet('form-test/checkboxes-radios');
+
+    // Verify that all options appear in their defined order.
+    foreach (array('checkbox', 'radio') as $type) {
+      $elements = $this->xpath('//input[@type=:type]', array(':type' => $type));
+      $expected_values = array('0', 'foo', '1', 'bar');
+      foreach ($elements as $element) {
+        $expected = array_shift($expected_values);
+        $this->assertIdentical((string) $element['value'], $expected);
+      }
+    }
+
+    // Enable customized option sub-elements.
+    $this->drupalGet('form-test/checkboxes-radios/customize');
+
+    // Verify that all options appear in their defined order, taking a custom
+    // #weight into account.
+    foreach (array('checkbox', 'radio') as $type) {
+      $elements = $this->xpath('//input[@type=:type]', array(':type' => $type));
+      $expected_values = array('0', 'foo', 'bar', '1');
+      foreach ($elements as $element) {
+        $expected = array_shift($expected_values);
+        $this->assertIdentical((string) $element['value'], $expected);
+      }
+    }
+    // Verify that custom #description properties are output.
+    foreach (array('checkboxes', 'radios') as $type) {
+      $elements = $this->xpath('//input[@name=:name]/following-sibling::div[@class=:class]', array(
+        ':name' => $type . '[foo]',
+        ':class' => 'description',
+      ));
+      $this->assertTrue(count($elements), t('Custom %type option description found.', array(
+        '%type' => $type,
+      )));
+    }
+  }
+}
+
 /**
  * Test form alter hooks.
  */
diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module
index 4d717c04b82e..abaae06c1c5d 100644
--- a/modules/simpletest/tests/form_test.module
+++ b/modules/simpletest/tests/form_test.module
@@ -98,6 +98,12 @@ function form_test_menu() {
     'page arguments' => array('form_test_select'),
     'access callback' => TRUE,
   );
+  $items['form-test/checkboxes-radios'] = array(
+    'title' => t('Checkboxes, Radios'),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('form_test_checkboxes_radios'),
+    'access callback' => TRUE,
+  );
 
   $items['form-test/disabled-elements'] = array(
     'title' => t('Form test'),
@@ -186,6 +192,14 @@ function form_test_menu() {
   return $items;
 }
 
+/**
+ * Form submit handler to return form values as JSON.
+ */
+function _form_test_submit_values_json($form, &$form_state) {
+  drupal_json_output($form_state['values']);
+  drupal_exit();
+}
+
 /**
  * Form builder for testing hook_form_alter() and hook_form_FORM_ID_alter().
  */
@@ -892,6 +906,63 @@ function form_test_select_submit($form, &$form_state) {
   exit();
 }
 
+/**
+ * Form constructor to test expansion of #type checkboxes and radios.
+ */
+function form_test_checkboxes_radios($form, &$form_state, $customize = FALSE) {
+  $form['#submit'] = array('_form_test_submit_values_json');
+
+  // Expand #type checkboxes, setting custom element properties for some but not
+  // all options.
+  $form['checkboxes'] = array(
+    '#type' => 'checkboxes',
+    '#title' => 'Checkboxes',
+    '#options' => array(
+      0 => 'Zero',
+      'foo' => 'Foo',
+      1 => 'One',
+      'bar' => 'Bar',
+    ),
+  );
+  if ($customize) {
+    $form['checkboxes'] += array(
+      'foo' => array(
+        '#description' => 'Enable to foo.',
+      ),
+      1 => array(
+        '#weight' => 10,
+      ),
+    );
+  }
+
+  // Expand #type radios, setting custom element properties for some but not
+  // all options.
+  $form['radios'] = array(
+    '#type' => 'radios',
+    '#title' => 'Radios',
+    '#options' => array(
+      0 => 'Zero',
+      'foo' => 'Foo',
+      1 => 'One',
+      'bar' => 'Bar',
+    ),
+  );
+  if ($customize) {
+    $form['radios'] += array(
+      'foo' => array(
+        '#description' => 'Enable to foo.',
+      ),
+      1 => array(
+        '#weight' => 10,
+      ),
+    );
+  }
+
+  $form['submit'] = array('#type' => 'submit', '#value' => 'Submit');
+
+  return $form;
+}
+
 /**
  * Build a form to test disabled elements.
  */
-- 
GitLab