diff options
-rw-r--r-- | includes/common.inc | 3 | ||||
-rw-r--r-- | includes/form.inc | 53 | ||||
-rw-r--r-- | modules/field/modules/text/text.module | 12 | ||||
-rw-r--r-- | modules/field/modules/text/text.test | 37 | ||||
-rw-r--r-- | modules/field/tests/field.test | 4 | ||||
-rw-r--r-- | modules/simpletest/tests/form.test | 2 | ||||
-rw-r--r-- | themes/seven/style.css | 4 |
7 files changed, 101 insertions, 14 deletions
diff --git a/includes/common.inc b/includes/common.inc index fc5971dc0..fec01bc1b 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -5776,6 +5776,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', ), diff --git a/includes/form.inc b/includes/form.inc index c5525c9e2..c3c74e15d 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -1126,7 +1126,10 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) { // 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('<a href="#!field_id">!name</a> field is required.', array( + '!field_id' => $elements['#id'], + '!name' => $elements['#title'], + ))); } // Call user-defined form level validators. @@ -3452,6 +3455,9 @@ function theme_form_element_label($variables) { // 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(); @@ -3469,7 +3475,50 @@ function theme_form_element_label($variables) { } // 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 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 .= ' <span' . drupal_attributes($attributes) . '>' . $error . '</span>'; + } + + return $output; } /** diff --git a/modules/field/modules/text/text.module b/modules/field/modules/text/text.module index 5862ae920..dd7b602fc 100644 --- a/modules/field/modules/text/text.module +++ b/modules/field/modules/text/text.module @@ -639,11 +639,13 @@ function text_field_prepare_translation($entity_type, $entity, $field, $instance // If the translating user is not permitted to use the assigned text format, // we must not expose the source values. $field_name = $field['field_name']; - $formats = filter_formats(); - foreach ($source_entity->{$field_name}[$source_langcode] as $delta => $item) { - $format_id = $item['format']; - if (!filter_access($formats[$format_id])) { - unset($items[$delta]); + if (!empty($source_entity->{$field_name}[$source_langcode])) { + $formats = filter_formats(); + foreach ($source_entity->{$field_name}[$source_langcode] as $delta => $item) { + $format_id = $item['format']; + if (!empty($format_id) && !filter_access($formats[$format_id])) { + unset($items[$delta]); + } } } } diff --git a/modules/field/modules/text/text.test b/modules/field/modules/text/text.test index 51e2ce44a..67aad26b0 100644 --- a/modules/field/modules/text/text.test +++ b/modules/field/modules/text/text.test @@ -402,10 +402,38 @@ class TextTranslationTestCase extends DrupalWebTestCase { } /** + * Test that a plaintext textfield widget is correctly populated. + */ + function testTextField() { + // Disable text processing for body. + $edit = array('instance[settings][text_processing]' => 0); + $this->drupalPost('admin/structure/types/manage/article/fields/body', $edit, t('Save settings')); + + // Login as translator. + $this->drupalLogin($this->translator); + + // Create content. + $langcode = LANGUAGE_NONE; + $body = $this->randomName(); + $edit = array( + "title" => $this->randomName(), + "language" => 'en', + "body[$langcode][0][value]" => $body, + ); + + // Translate the article in french. + $this->drupalPost('node/add/article', $edit, t('Save')); + $node = $this->drupalGetNodeByTitle($edit['title']); + $this->drupalGet("node/$node->nid/translate"); + $this->clickLink(t('add translation')); + $this->assertFieldByXPath("//textarea[@name='body[fr][0][value]']", $body, t('The textfield widget is populated.')); + } + + /** * Check that user that does not have access the field format cannot see the * source value when creating a translation. */ - function testMultipleTextField() { + function testTextFieldFormatted() { // Make node body multiple. $edit = array('field[cardinality]' => -1); $this->drupalPost('admin/structure/types/manage/article/fields/body', $edit, t('Save settings')); @@ -419,8 +447,9 @@ class TextTranslationTestCase extends DrupalWebTestCase { // Create an article with the first body input format set to "Full HTML". $langcode = 'en'; + $title = $this->randomName(); $edit = array( - "title" => $this->randomName(), + 'title' => $title, 'language' => $langcode, ); $this->drupalPost('node/add/article', $edit, t('Save')); @@ -438,11 +467,11 @@ class TextTranslationTestCase extends DrupalWebTestCase { } // Login as translator. - $this->drupalLogout(); $this->drupalLogin($this->translator); // Translate the article in french. - $this->drupalGet('node/1/translate'); + $node = $this->drupalGetNodeByTitle($title); + $this->drupalGet("node/$node->nid/translate"); $this->clickLink(t('add translation')); $this->assertNoText($body[0], t('The body field with delta @delta is hidden.', array('@delta' => 0))); $this->assertText($body[1], t('The body field with delta @delta is shown.', array('@delta' => 1))); diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test index 479b0a966..b932479c8 100644 --- a/modules/field/tests/field.test +++ b/modules/field/tests/field.test @@ -1300,7 +1300,7 @@ class FieldFormTestCase extends FieldTestCase { // 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 FieldTestCase { $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() { diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test index 525a8a291..32865c01e 100644 --- a/modules/simpletest/tests/form.test +++ b/modules/simpletest/tests/form.test @@ -63,7 +63,7 @@ class FormsTestCase extends DrupalWebTestCase { $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) { diff --git a/themes/seven/style.css b/themes/seven/style.css index a6bd0c43b..71a8d5013 100644 --- a/themes/seven/style.css +++ b/themes/seven/style.css @@ -256,6 +256,10 @@ div.error { div.error p.error { color: #333; } +div.error a { + color: #fff; + text-decoration: underline; +} div.status { color: #234600; background: #f8fff0; |