From d672bcc4019552f987d1fa6887b42aaa64e43967 Mon Sep 17 00:00:00 2001 From: catch <catch@35733.no-reply.drupal.org> Date: Tue, 27 Mar 2012 15:23:35 +0900 Subject: [PATCH] Issue #1174938 by ericduran, aspilicious, voxpelli, David_Rothstein, effulgentsia: Added Natively support the HTML5 #required FAPI property. --- core/includes/form.inc | 17 ++++++++ core/modules/simpletest/tests/form.test | 42 +++++++++++++++++++ .../modules/simpletest/tests/form_test.module | 22 ++++++++++ core/modules/system/system.module | 6 +-- 4 files changed, 84 insertions(+), 3 deletions(-) diff --git a/core/includes/form.inc b/core/includes/form.inc index 35acd8439bf6..65c462a866df 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -3104,6 +3104,19 @@ function form_pre_render_conditional_form_element($element) { return $element; } +/** + * Processes a form button element. + */ +function form_process_button($element, $form_state) { + // If this is a button intentionally allowing incomplete form submission + // (e.g., a "Previous" or "Add another item" button), then also skip + // client-side validation. + if (isset($element['#limit_validation_errors']) && $element['#limit_validation_errors'] !== FALSE) { + $element['#attributes']['formnovalidate'] = 'formnovalidate'; + } + return $element; +} + /** * Sets the #checked property of a checkbox element. */ @@ -4312,6 +4325,10 @@ function _form_set_class(&$element, $class = array()) { // form_builder(). if (!empty($element['#required'])) { $element['#attributes']['class'][] = 'required'; + // @todo Rename the _form_set_class() function to reflect that we're setting + // non-class attributes too. + $element['#attributes']['required'] = 'required'; + $element['#attributes']['aria-required'] = 'true'; } if (isset($element['#parents']) && form_get_error($element)) { $element['#attributes']['class'][] = 'error'; diff --git a/core/modules/simpletest/tests/form.test b/core/modules/simpletest/tests/form.test index c51d3e659d7c..e79983c25f8a 100644 --- a/core/modules/simpletest/tests/form.test +++ b/core/modules/simpletest/tests/form.test @@ -451,6 +451,29 @@ class FormsTestCase extends DrupalWebTestCase { $this->drupalPost(NULL, array('checkboxes[one]' => TRUE, 'checkboxes[two]' => TRUE), t('Submit')); $this->assertText('An illegal choice has been detected.', t('Input forgery was detected.')); } + + /** + * Tests required attribute. + */ + function testRequiredAttribute() { + $this->drupalGet('form-test/required-attribute'); + $expected = 'required'; + // Test to make sure the elements have the proper required attribute. + foreach (array('textfield', 'password') as $type) { + $element = $this->xpath('//input[@id=:id and @required=:expected]', array( + ':id' => 'edit-' . $type, + ':expected' => $expected, + )); + $this->assertTrue(!empty($element), t('The @type has the proper required attribute.', array('@type' => $type))); + } + + // Test to make sure textarea has the proper required attribute. + $element = $this->xpath('//textarea[@id=:id and @required=:expected]', array( + ':id' => 'edit-textarea', + ':expected' => $expected, + )); + $this->assertTrue(!empty($element), t('The textarea has the proper required attribute.')); + } } /** @@ -633,6 +656,25 @@ class FormValidationTestCase extends DrupalWebTestCase { ); $path = 'form-test/limit-validation-errors'; + // Render the form, and verify that the buttons with limited server-side + // validation have the proper 'formnovalidate' attribute (to prevent + // client-side validation by the browser). + $this->drupalGet($path); + $expected = 'formnovalidate'; + foreach (array('partial', 'partial-numeric-index', 'substring') as $type) { + $element = $this->xpath('//input[@id=:id and @formnovalidate=:expected]', array( + ':id' => 'edit-' . $type, + ':expected' => $expected, + )); + $this->assertTrue(!empty($element), t('The @type button has the proper formnovalidate attribute.', array('@type' => $type))); + } + // The button with full server-side validation should not have the + // 'formnovalidate' attribute. + $element = $this->xpath('//input[@id=:id and not(@formnovalidate)]', array( + ':id' => 'edit-full', + )); + $this->assertTrue(!empty($element), t('The button with full server-side validation does not have the formnovalidate attribute.')); + // 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 diff --git a/core/modules/simpletest/tests/form_test.module b/core/modules/simpletest/tests/form_test.module index cddd9a6434ef..3dcd24742dd5 100644 --- a/core/modules/simpletest/tests/form_test.module +++ b/core/modules/simpletest/tests/form_test.module @@ -229,6 +229,13 @@ function form_test_menu() { 'type' => MENU_CALLBACK, ); + $items['form-test/required-attribute'] = array( + 'title' => 'Required', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_required_attribute'), + 'access callback' => TRUE, + ); + return $items; } @@ -1830,3 +1837,18 @@ function form_test_checkboxes_zero($form, &$form_state, $json = TRUE) { function _form_test_checkboxes_zero_no_redirect($form, &$form_state) { $form_state['redirect'] = FALSE; } + +/** + * Builds a form to test the required attribute. + */ +function form_test_required_attribute($form, &$form_state) { + foreach (array('textfield', 'textarea', 'password') as $type) { + $form[$type] = array( + '#type' => $type, + '#required' => TRUE, + '#title' => $type, + ); + } + + return $form; +} diff --git a/core/modules/system/system.module b/core/modules/system/system.module index e66202ed636d..e6d0972d93ed 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -333,7 +333,7 @@ function system_element_info() { '#button_type' => 'submit', '#executes_submit_callback' => TRUE, '#limit_validation_errors' => FALSE, - '#process' => array('ajax_process_form'), + '#process' => array('form_process_button', 'ajax_process_form'), '#theme_wrappers' => array('button'), ); $types['button'] = array( @@ -342,7 +342,7 @@ function system_element_info() { '#button_type' => 'submit', '#executes_submit_callback' => FALSE, '#limit_validation_errors' => FALSE, - '#process' => array('ajax_process_form'), + '#process' => array('form_process_button', 'ajax_process_form'), '#theme_wrappers' => array('button'), ); $types['image_button'] = array( @@ -350,7 +350,7 @@ function system_element_info() { '#button_type' => 'submit', '#executes_submit_callback' => TRUE, '#limit_validation_errors' => FALSE, - '#process' => array('ajax_process_form'), + '#process' => array('form_process_button', 'ajax_process_form'), '#return_value' => TRUE, '#has_garbage_value' => TRUE, '#src' => NULL, -- GitLab