? limit_validation_errors_numeric-953914.patch
? limit_validation_errors_substring-953914.patch
? sites/default/files
? sites/default/settings.php
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.509
diff -u -p -r1.509 form.inc
--- includes/form.inc	13 Nov 2010 14:04:08 -0000	1.509
+++ includes/form.inc	14 Nov 2010 19:11:37 -0000
@@ -163,7 +163,7 @@
  *   Any additional arguments are passed on to the functions called by
  *   drupal_get_form(), including the unique form constructor function. For
  *   example, the node_edit form requires that a node object is passed in here
- *   when it is called. These are available to implementations of 
+ *   when it is called. These are available to implementations of
  *   hook_form_alter() and hook_form_FORM_ID_alter() as the array
  *   $form_state['build_info']['args'].
  *
@@ -1442,12 +1442,17 @@ function form_set_error($name = NULL, $m
       // valid.
       $record = FALSE;
       foreach ($sections as $section) {
-        // Exploding by '][' reconstructs the element's #parents. If the
-        // reconstructed #parents begin with the same keys as the specified
-        // section, then the element's values are within the part of
-        // $form_state['values'] that the clicked button requires to be valid,
-        // so errors for this element must be recorded.
-        if (array_slice(explode('][', $name), 0, count($section)) === $section) {
+        // The element's name reflects its #parents hierarchy. We build a
+        // similar string reflecting the $section array. If this string is an
+        // exact prefix of the element's name, then the element's values are
+        // within the part of $form_state['values'] that the clicked button
+        // requires to be valid, so errors for this element must be recorded.
+        // Comparing strings rather than arrays correctly handles the case of
+        // $section including numeric indexes.  Append ']' to $name and 
+        // $section_string so that, for example, 'foobar' isn't considered 
+        // "inside" 'foo'.
+        $section_string = implode('][', $section);
+        if (strpos($name . ']', $section_string . ']') === 0) {
           $record = TRUE;
           break;
         }
Index: modules/simpletest/tests/form.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form.test,v
retrieving revision 1.74
diff -u -p -r1.74 form.test
--- modules/simpletest/tests/form.test	7 Nov 2010 21:46:09 -0000	1.74
+++ modules/simpletest/tests/form.test	14 Nov 2010 19:11:37 -0000
@@ -521,16 +521,33 @@ class FormValidationTestCase extends Dru
    * Tests partial form validation through #limit_validation_errors.
    */
   function testValidateLimitErrors() {
-    $edit = array('test' => 'invalid');
+    $edit = array(
+      'test' => 'invalid', 
+      'test_numeric_index[0]' => 'invalid',
+      'test_substring[foo]' => 'invalid',
+    );
     $path = 'form-test/limit-validation-errors';
 
-    // Submit the form by pressing the button with #limit_validation_errors and
-    // ensure that the title field is not validated, but the #element_validate
-    // handler for the 'test' field is triggered.
+    // Submit the form by pressing the 'Partial validate' button (uses
+    // #limit_validation_errors) and ensure that the title field is not
+    // validated, but the #element_validate handler for the 'test' field
+    // is triggered.
     $this->drupalPost($path, $edit, t('Partial validate'));
     $this->assertNoText(t('!name field is required.', array('!name' => 'Title')));
     $this->assertText('Test element is invalid');
 
+    // Edge case of #limit_validation_errors containing numeric indexes: same
+    // thing with the 'Partial validate (numeric index)' button and the
+    // 'test_numeric_index' field.
+    $this->drupalPost($path, $edit, t('Partial validate (numeric index)'));
+    $this->assertNoText(t('!name field is required.', array('!name' => 'Title')));
+    $this->assertText('Test (numeric index) element is invalid');
+
+    // Ensure something like 'foobar' isn't considered "inside" 'foo'.
+    $this->drupalPost($path, $edit, t('Partial validate (substring)'));
+    $this->assertNoText(t('!name field is required.', array('!name' => 'Title')));
+    $this->assertText('Test (substring) foo element is invalid');
+
     // Ensure not validated values are not available to submit handlers.
     $this->drupalPost($path, array('title' => '', 'test' => 'valid'), t('Partial validate'));
     $this->assertText('Only validated values appear in the form values.');
Index: modules/simpletest/tests/form_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form_test.module,v
retrieving revision 1.54
diff -u -p -r1.54 form_test.module
--- modules/simpletest/tests/form_test.module	7 Nov 2010 21:46:09 -0000	1.54
+++ modules/simpletest/tests/form_test.module	14 Nov 2010 19:11:37 -0000
@@ -333,16 +333,53 @@ function form_test_limit_validation_erro
     '#title' => 'Title',
     '#required' => TRUE,
   );
+
   $form['test'] = array(
+    '#title' => 'Test',
+    '#type' => 'textfield',
+    '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
+  );
+  $form['test_numeric_index'] = array(
+    '#tree' => TRUE,
+  );
+  $form['test_numeric_index'][0] = array(
+    '#title' => 'Test (numeric index)',
+    '#type' => 'textfield',
+    '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
+  );
+  
+  $form['test_substring'] = array(
+    '#tree' => TRUE,
+  );
+  $form['test_substring']['foo'] = array(
+    '#title' => 'Test (substring) foo',
     '#type' => 'textfield',
     '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
   );
+  $form['test_substring']['foobar'] = array(
+    '#title' => 'Test (substring) foobar',
+    '#type' => 'textfield',
+    '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
+  );
+  
   $form['actions']['partial'] = array(
     '#type' => 'submit',
     '#limit_validation_errors' => array(array('test')),
     '#submit' => array('form_test_limit_validation_errors_form_partial_submit'),
     '#value' => t('Partial validate'),
   );
+  $form['actions']['partial_numeric_index'] = array(
+    '#type' => 'submit',
+    '#limit_validation_errors' => array(array('test_numeric_index', 0)),
+    '#submit' => array('form_test_limit_validation_errors_form_partial_submit'),
+    '#value' => t('Partial validate (numeric index)'),
+  );
+  $form['actions']['substring'] = array(
+    '#type' => 'submit',
+    '#limit_validation_errors' => array(array('test_substring', 'foo')),
+    '#submit' => array('form_test_limit_validation_errors_form_partial_submit'),
+    '#value' => t('Partial validate (substring)'),
+  );
   $form['actions']['full'] = array(
     '#type' => 'submit',
     '#value' => t('Full validate'),
@@ -355,7 +392,7 @@ function form_test_limit_validation_erro
  */
 function form_test_limit_validation_errors_element_validate_test(&$element, &$form_state) {
   if ($element['#value'] == 'invalid') {
-    form_error($element, 'Test element is invalid');
+    form_error($element, t('@label element is invalid', array('@label' => $element['#title'])));
   }
 }
 
