? diff.txt
? drushrc.php
? sites
? test.php
? tplphips.txt
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1255
diff -u -p -r1.1255 common.inc
--- includes/common.inc	17 Nov 2010 04:15:37 -0000	1.1255
+++ includes/common.inc	18 Nov 2010 23:44:39 -0000
@@ -6425,6 +6425,9 @@ function drupal_common_theme() {
     'form_element_label' => array(
       'render element' => 'element',
     ),
+    'form_error_marker' => array(
+      'render element' => 'element',
+    ),
     'vertical_tabs' => array(
       'render element' => 'element',
     ),
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.511
diff -u -p -r1.511 form.inc
--- includes/form.inc	17 Nov 2010 04:10:51 -0000	1.511
+++ includes/form.inc	18 Nov 2010 23:44:41 -0000
@@ -1252,7 +1252,10 @@ function _form_validate(&$elements, &$fo
         // Instead of setting no #title, form constructors are encouraged to set
         // #title_display to 'invisible' to improve accessibility.
         if (isset($elements['#title'])) {
-          form_error($elements, $t('!name field is required.', array('!name' => $elements['#title'])));
+          form_error($elements, $t('<a href="#!field_id">!name</a> field is required.', array(
+            '!field_id' => isset($elements['#attributes']['id']) ? $elements['#attributes']['id'] : $elements['#id'],
+            '!name' => $elements['#title'],
+          )));
         }
         else {
           form_error($elements);
@@ -1490,6 +1493,14 @@ function form_get_errors() {
  * errors on the element itself.
  */
 function form_get_error($element) {
+  // The link that has been squeezed into the error message in _form_validate()
+  // only applies to #required form elements. All other form validation errors
+  // are set individually by modules through form_set_error(), which directly
+  // outputs a message onto the screen.
+  if (!isset($element['#parents'])) {
+    return;
+  }
+
   $form = form_set_error();
   $parents = array();
   foreach ($element['#parents'] as $parent) {
@@ -3847,6 +3858,11 @@ function theme_form_element_label($varia
   // If the element is required, a required marker is appended to the label.
   $required = !empty($element['#required']) ? theme('form_required_marker', array('element' => $element)) : '';
 
+  // If the element has an error, append an error marker to the label. By
+  // default we append the error message text styled as invisible. This allows
+  // screen reader users to know which field has an error and what the error is.
+  $error = theme('form_error_marker', array('element' => $element));
+
   $title = filter_xss_admin($element['#title']);
 
   $attributes = array();
@@ -3864,7 +3880,50 @@ function theme_form_element_label($varia
   }
 
   // The leading whitespace helps visually separate fields from inline labels.
-  return ' <label' . drupal_attributes($attributes) . '>' . $t('!title !required', array('!title' => $title, '!required' => $required)) . "</label>\n";
+  return ' <label' . drupal_attributes($attributes) . '>' . $t('!title !required !error', array('!title' => $title, '!required' => $required, '!error' => $error)) . "</label>\n";
+}
+
+/**
+ * Theme an invisible error marker within the label for form elements.
+ *
+ * For each form element with an error, this function outputs an error marker
+ * that is appended to the label next to each field. By default this outputs
+ * the error message text in an invisible span. This allows screen reader users
+ * to know which fields have an error and what the error is. Override this
+ * function if you want to provide a different error marker, such as visible
+ * error text or an icon with a tooltip.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *
+ * @return
+ *   A string with a marker to identify an error, otherwise an empty string.
+ *
+ * @ingroup themeable
+ */
+function theme_form_error_marker($variables) {
+  $element = $variables['element'];
+  // This is also used in the installer, pre-database setup.
+  $t = get_t();
+
+  $output = '';
+  if ($raw_error = form_get_error($element)) {
+    // This is the same test used to validate #required fields.
+    // @see _form_validate()
+    if ($element['#required'] && (!count($element['#value']) || (is_string($element['#value']) && strlen(trim($element['#value'])) == 0) || $element['#value'] === 0)) {
+      $error = $t('This field is required.');
+    }
+    else {
+      $error = strip_tags($raw_error);
+    }
+    $attributes = array(
+      'class' => array('element-invisible', 'error'),
+    );
+    $output .= ' <span' . drupal_attributes($attributes) . '>' . $error . '</span>';
+  }
+
+  return $output;
 }
 
 /**
Index: modules/field/tests/field.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/tests/field.test,v
retrieving revision 1.42
diff -u -p -r1.42 field.test
--- modules/field/tests/field.test	28 Sep 2010 02:30:31 -0000	1.42
+++ modules/field/tests/field.test	18 Nov 2010 23:44:44 -0000
@@ -1300,7 +1300,7 @@ class FieldFormTestCase extends FieldTes
     // Submit with missing required value.
     $edit = array();
     $this->drupalPost('test-entity/add/test-bundle', $edit, t('Save'));
-    $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
+    $this->assertText(t('!name field is required.', array('!name' => $this->instance['label'])), t('Required field is actually mandatory'));
 
     // Create an entity
     $value = mt_rand(1, 127);
@@ -1316,7 +1316,8 @@ class FieldFormTestCase extends FieldTes
     $value = '';
     $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
     $this->drupalPost('test-entity/manage/' . $id . '/edit', $edit, t('Save'));
-    $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
+    $this->assertText(t('!name field is required.', array('!name' => $this->instance['label'])), t('Required field with no value fails validation'));
+
   }
 
 //  function testFieldFormMultiple() {
Index: modules/simpletest/tests/form.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form.test,v
retrieving revision 1.75
diff -u -p -r1.75 form.test
--- modules/simpletest/tests/form.test	15 Nov 2010 10:06:32 -0000	1.75
+++ modules/simpletest/tests/form.test	18 Nov 2010 23:44:44 -0000
@@ -66,7 +66,7 @@ class FormsTestCase extends DrupalWebTes
     $elements['file']['empty_values'] = $empty_strings;
 
     // Regular expression to find the expected marker on required elements.
-    $required_marker_preg = '@<label.*<span class="form-required" title="This field is required\.">\*</span></label>@';
+    $required_marker_preg = '@<label.*<span class="form-required" title="This field is required\.">\*</span>.*</label>@';
 
     // Go through all the elements and all the empty values for them.
     foreach ($elements as $type => $data) {
@@ -131,7 +131,7 @@ class FormsTestCase extends DrupalWebTes
     // First, try to submit without the required checkbox.
     $edit = array();
     $this->drupalPost('form-test/checkbox', $edit, t('Submit'));
-    $this->assertRaw(t('!name field is required.', array('!name' => 'required_checkbox')), t('A required checkbox is actually mandatory'));
+    $this->assertText(t('!name field is required.', array('!name' => 'required_checkbox')), t('A required checkbox is actually mandatory'));
 
     // Now try to submit the form correctly.
     $values = drupal_json_decode($this->drupalPost(NULL, array('required_checkbox' => 1), t('Submit')));
@@ -158,26 +158,71 @@ class FormsTestCase extends DrupalWebTes
   function testSelect() {
     $form = $form_state = array();
     $form = form_test_select($form, $form_state);
-    $error = '!name field is required.';
+    $error = '<a href="#!field_id">!name</a> field is required.';
     $this->drupalGet('form-test/select');
 
     // Posting without any values should throw validation errors.
     $this->drupalPost(NULL, array(), 'Submit');
-    $this->assertNoText(t($error, array('!name' => $form['select']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['select_required']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['select_optional']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['empty_value']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['empty_value_one']['#title'])));
-    $this->assertText(t($error, array('!name' => $form['no_default']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['no_default_optional']['#title'])));
-    $this->assertText(t($error, array('!name' => $form['no_default_empty_option']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['no_default_empty_option_optional']['#title'])));
-    $this->assertText(t($error, array('!name' => $form['no_default_empty_value']['#title'])));
-    $this->assertText(t($error, array('!name' => $form['no_default_empty_value_one']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['no_default_empty_value_optional']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['multiple']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['multiple_no_default']['#title'])));
-    $this->assertText(t($error, array('!name' => $form['multiple_no_default_required']['#title'])));
+    $this->assertNoText(t($error, array(
+      '!field_id' => $form['select']['#id'],
+      '!name' => $form['select']['#title'],
+    )));
+    $this->assertNoText(t($error, array(
+      '!field_id' => $form['select_required']['#id'],
+      '!name' => $form['select_required']['#title'],
+    )));
+    $this->assertNoText(t($error, array(
+      '!field_id' => $form['select_optional']['#id'],
+      '!name' => $form['select_optional']['#title'],
+    )));
+    $this->assertNoText(t($error, array(
+      '!field_id' => $form['empty_value']['#id'],
+      '!name' => $form['empty_value']['#title'],
+    )));
+    $this->assertNoText(t($error, array(
+      '!field_id' => $form['empty_value_one']['#id'],
+      '!name' => $form['empty_value_one']['#title'],
+    )));
+    $this->assertText(t($error, array(
+      '!field_id' => $form['no_default']['#id'],
+      '!name' => $form['no_default']['#title'],
+    )));
+    $this->assertNoText(t($error, array(
+      '!field_id' => $form['no_default_optional']['#id'],
+      '!name' => $form['no_default_optional']['#title'],
+    )));
+    $this->assertText(t($error, array(
+      '!field_id' => $form['no_default_empty_option']['#id'],
+      '!name' => $form['no_default_empty_option']['#title'],
+    )));
+    $this->assertNoText(t($error, array(
+      '!field_id' => $form['no_default_empty_option_optional']['#id'],
+      '!name' => $form['no_default_empty_option_optional']['#title'],
+    )));
+    $this->assertText(t($error, array(
+      '!field_id' => $form['no_default_empty_value']['#id'],
+      '!name' => $form['no_default_empty_value']['#title'],
+    )));
+    $this->assertText(t($error, array(
+      '!field_id' => $form['no_default_empty_value_one']['#id'],
+      '!name' => $form['no_default_empty_value_one']['#title'],
+    )));
+    $this->assertNoText(t($error, array(
+      '!field_id' => $form['no_default_empty_value_optional']['#id'],
+      '!name' => $form['no_default_empty_value_optional']['#title'],
+    )));
+    $this->assertNoText(t($error, array(
+      '!field_id' => $form['multiple']['#id'],
+      '!name' => $form['multiple']['#title'],
+    )));
+    $this->assertNoText(t($error, array(
+      '!field_id' => $form['multiple_no_default']['#id'],
+      '!name' => $form['multiple_no_default']['#title'],
+    )));
+    $this->assertText(t($error, array(
+      '!field_id' => $form['multiple_no_default_required']['#id'],
+      '!name' => $form['multiple_no_default_required']['#title'],
+    )));
 
     // Post values for required fields.
     $edit = array(
