Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1191 diff -u -p -r1.1191 common.inc --- includes/common.inc 7 Jul 2010 17:00:42 -0000 1.1191 +++ includes/common.inc 8 Jul 2010 18:25:53 -0000 @@ -5732,6 +5732,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.476 diff -u -p -r1.476 form.inc --- includes/form.inc 7 Jul 2010 17:56:42 -0000 1.476 +++ includes/form.inc 8 Jul 2010 18:25:53 -0000 @@ -1090,7 +1090,10 @@ function _form_validate(&$elements, &$fo // An unchecked checkbox has a #value of integer 0, different than string // '0', which could be a valid value. if (isset($elements['#needs_validation']) && $elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0) || $elements['#value'] === 0)) { - form_error($elements, $t('!name field is required.', array('!name' => $elements['#title']))); + form_error($elements, $t('!name field is required.', array( + '!field_id' => $elements['#id'], + '!name' => $elements['#title'], + ))); } // Call user-defined form level validators. @@ -3343,6 +3346,9 @@ 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 the themeable marker to the label. + $error = theme('form_error_marker', array('element' => $element)); + $title = filter_xss_admin($element['#title']); $attributes = array(); @@ -3360,7 +3366,50 @@ function theme_form_element_label($varia } // The leading whitespace helps visually separate fields from inline labels. - return ' ' . $t('!title !required', array('!title' => $title, '!required' => $required)) . "\n"; + return ' ' . $t('!title !required !error', array('!title' => $title, '!required' => $required, '!error' => $error)) . "\n"; +} + +/** + * Theme an invisible error marker for form elements. + * + * By default this function outputs the error message, if any, in an invisible + * span. This allows screen reader users to identify the fields with errors. + * Override this function to provide an alternative error marker, such as + * visible error text or an indicator with a tooltip to show the full error. + * + * @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)) { + // A simple call to empty() will not cut it here as some fields, like + // checkboxes, can return a valid value of '0'. Instead, check the + // length if it's a string, and the item count if it's an array. + // This is the same logic used when validating #required fields. + // @see _form_validate() + if ($element['#required'] && (!count($element['#value']) || (is_string($element['#value']) && strlen(trim($element['#value'])) == 0))) { + $error = $t('This field is required.'); + } + else { + $error = strip_tags($raw_error); + } + $attributes = array( + 'class' => array('element-invisible', 'error', 'error-marker'), + ); + $output .= ' ' . $error . ''; + } + + return $output; } /** Index: modules/field/tests/field.test =================================================================== RCS file: /cvs/drupal/drupal/modules/field/tests/field.test,v retrieving revision 1.33 diff -u -p -r1.33 field.test --- modules/field/tests/field.test 17 Jun 2010 13:16:57 -0000 1.33 +++ modules/field/tests/field.test 8 Jul 2010 18:25:53 -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'])), 'Required field with no value fails validation'); // Create an entity $value = mt_rand(1, 127); @@ -1316,7 +1316,7 @@ class FieldFormTestCase extends FieldTes $value = ''; $edit = array("{$this->field_name}[$langcode][0][value]" => $value); $this->drupalPost('test-entity/' . $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'])), '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.53 diff -u -p -r1.53 form.test --- modules/simpletest/tests/form.test 2 Jul 2010 12:37:57 -0000 1.53 +++ modules/simpletest/tests/form.test 8 Jul 2010 18:25:53 -0000 @@ -63,7 +63,7 @@ class FormsTestCase extends DrupalWebTes $elements['file']['empty_values'] = $empty_strings; // Regular expression to find the expected marker on required elements. - $required_marker_preg = '@\*@'; + $required_marker_preg = '@\*.*@'; // Go through all the elements and all the empty values for them. foreach ($elements as $type => $data) { Index: themes/seven/style.css =================================================================== RCS file: /cvs/drupal/drupal/themes/seven/style.css,v retrieving revision 1.57 diff -u -p -r1.57 style.css --- themes/seven/style.css 5 May 2010 16:41:57 -0000 1.57 +++ themes/seven/style.css 8 Jul 2010 18:25:53 -0000 @@ -263,6 +263,10 @@ div.error { div.error p.error { color: #333; } +div.error a { + color: #fff; + text-decoration: underline; +} div.status { color: #360; background: #cf8;