Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1232
diff -u -p -r1.1232 common.inc
--- includes/common.inc 4 Oct 2010 17:46:00 -0000 1.1232
+++ includes/common.inc 5 Oct 2010 18:51:58 -0000
@@ -6058,6 +6058,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.500
diff -u -p -r1.500 form.inc
--- includes/form.inc 4 Oct 2010 18:00:45 -0000 1.500
+++ includes/form.inc 5 Oct 2010 18:51:58 -0000
@@ -1231,7 +1231,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('!title field is required.', array(
+ '!field_id' => $elements['#id'],
+ '!title' => $elements['#title'],
+ )));
}
else {
form_error($elements);
@@ -1469,6 +1472,9 @@ function form_get_errors() {
function form_get_error($element) {
$form = form_set_error();
$parents = array();
+ if (!isset($element['#parents'])) {
+ return;
+ }
foreach ($element['#parents'] as $parent) {
$parents[] = $parent;
$key = implode('][', $parents);
@@ -3589,6 +3595,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();
@@ -3606,7 +3617,50 @@ function theme_form_element_label($varia
}
// The leading whitespace helps visually separate fields from inline labels.
- return ' \n";
+ return ' \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 .= ' ' . $error . '';
+ }
+
+ 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 5 Oct 2010 18:51:58 -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('!title field is required.', array('!title' => $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/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('!title field is required.', array('!title' => $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.72
diff -u -p -r1.72 form.test
--- modules/simpletest/tests/form.test 4 Oct 2010 18:00:46 -0000 1.72
+++ modules/simpletest/tests/form.test 5 Oct 2010 18:51:58 -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 = '@\*@';
+ $required_marker_preg = '@\*.*@';
// 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('!title field is required.', array('!title' => '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,26 @@ class FormsTestCase extends DrupalWebTes
function testSelect() {
$form = $form_state = array();
$form = form_test_select($form, $form_state);
- $error = '!name field is required.';
+ $error = '!title 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('!title' => $form['select']['#title'])));
+ $this->assertNoText(t($error, array('!title' => $form['select_required']['#title'])));
+ $this->assertNoText(t($error, array('!title' => $form['select_optional']['#title'])));
+ $this->assertNoText(t($error, array('!title' => $form['empty_value']['#title'])));
+ $this->assertNoText(t($error, array('!title' => $form['empty_value_one']['#title'])));
+ $this->assertText(t($error, array('!title' => $form['no_default']['#title'])));
+ $this->assertNoText(t($error, array('!title' => $form['no_default_optional']['#title'])));
+ $this->assertText(t($error, array('!title' => $form['no_default_empty_option']['#title'])));
+ $this->assertNoText(t($error, array('!title' => $form['no_default_empty_option_optional']['#title'])));
+ $this->assertText(t($error, array('!title' => $form['no_default_empty_value']['#title'])));
+ $this->assertText(t($error, array('!title' => $form['no_default_empty_value_one']['#title'])));
+ $this->assertNoText(t($error, array('!title' => $form['no_default_empty_value_optional']['#title'])));
+ $this->assertNoText(t($error, array('!title' => $form['multiple']['#title'])));
+ $this->assertNoText(t($error, array('!title' => $form['multiple_no_default']['#title'])));
+ $this->assertText(t($error, array('!title' => $form['multiple_no_default_required']['#title'])));
// Post values for required fields.
$edit = array(
@@ -468,7 +468,7 @@ class FormValidationTestCase extends Dru
// 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->assertNoText(t('!title field is required.', array('!title' => 'Title')));
$this->assertText('Test element is invalid');
// Ensure not validated values are not available to submit handlers.
@@ -478,7 +478,7 @@ class FormValidationTestCase extends Dru
// Now test full form validation and ensure that the #element_validate
// handler is still triggered.
$this->drupalPost($path, $edit, t('Full validate'));
- $this->assertText(t('!name field is required.', array('!name' => 'Title')));
+ $this->assertText(t('!title field is required.', array('!title' => 'Title')));
$this->assertText('Test element is invalid');
}
}
@@ -895,7 +895,7 @@ class FormsFormStorageTestCase extends D
// Test it again, but first trigger a validation error, then test.
$this->drupalPost('form-test/state-persist', array('title' => ''), t('Submit'), $options);
- $this->assertText(t('!name field is required.', array('!name' => 'title')));
+ $this->assertText(t('!title field is required.', array('!title' => 'title')));
// Submit the form again triggering no validation error.
$this->drupalPost(NULL, array('title' => 'foo'), t('Submit'), $options);
$this->assertText('State persisted.');