Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.406 diff -u -p -r1.406 form.inc --- includes/form.inc 24 Nov 2009 21:56:15 -0000 1.406 +++ includes/form.inc 25 Nov 2009 16:36:00 -0000 @@ -726,7 +726,7 @@ function drupal_prepare_form($form_id, & * web service requests, or other expensive requests that should * not be repeated in the submission step. */ -function drupal_validate_form($form_id, $form, &$form_state) { +function drupal_validate_form($form_id, &$form, &$form_state) { $validated_forms = &drupal_static(__FUNCTION__, array()); if (isset($validated_forms[$form_id]) && empty($form_state['must_validate'])) { @@ -834,7 +834,7 @@ function drupal_redirect_form($form_stat * A unique string identifying the form for validation, submission, * theming, and hook_form_alter functions. */ -function _form_validate($elements, &$form_state, $form_id = NULL) { +function _form_validate(&$elements, &$form_state, $form_id = NULL) { // Also used in the installer, pre-database setup. $t = get_t(); Index: modules/simpletest/tests/form.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form.test,v retrieving revision 1.25 diff -u -p -r1.25 form.test --- modules/simpletest/tests/form.test 18 Nov 2009 18:51:11 -0000 1.25 +++ modules/simpletest/tests/form.test 26 Nov 2009 11:39:27 -0000 @@ -153,6 +153,63 @@ class FormsTestCase extends DrupalWebTes } /** + * Test form validation handlers. + */ +class FormValidationTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Form validation handlers', + 'description' => 'Tests form processing and alteration via form validation handlers.', + 'group' => 'Form API', + ); + } + + function setUp() { + parent::setUp('form_test'); + } + + /** + * Tests form alterations by #element_validate, #validate, and form_set_value(). + */ + function testValidate() { + // Verify that #element_validate handlers can alter the form and submitted + // form values. + $edit = array( + 'name' => 'element_validate', + ); + $this->drupalPost('form-test/validate', $edit, 'Save'); + $this->assertFieldByName('name', '#value changed by #element_validate', t('Form element #value was altered.')); + $this->assertText('Name value: value changed by form_set_value() in #element_validate', t('Form element value in $form_state was altered.')); + + // Verify that #validate handlers can alter the form and submitted + // form values. + $edit = array( + 'name' => 'validate', + ); + $this->drupalPost('form-test/validate', $edit, 'Save'); + $this->assertFieldByName('name', '#value changed by #validate', t('Form element #value was altered.')); + $this->assertText('Name value: value changed by form_set_value() in #validate', t('Form element value in $form_state was altered.')); + + // Verify that #element_validate handlers can make form elements + // inaccessible, but values persist. + $edit = array( + 'name' => 'element_validate_access', + ); + $this->drupalPost('form-test/validate', $edit, 'Save'); + $this->assertNoFieldByName('name', t('Form element was hidden.')); + $this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.')); + + // Verify that value for inaccessible form element persists. + $this->drupalPost(NULL, array(), 'Save'); + $this->assertNoFieldByName('name', t('Form element was hidden.')); + // As of now, $form_state['values'] is ignored when rebuilding a form. + // @see http://drupal.org/node/622922 + // @todo Uncomment this assertion when aforementioned bug is fixed. + // $this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.')); + } +} + +/** * Test the tableselect form element for expected behavior. */ class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase { Index: modules/simpletest/tests/form_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form_test.module,v retrieving revision 1.17 diff -u -p -r1.17 form_test.module --- modules/simpletest/tests/form_test.module 21 Nov 2009 17:01:31 -0000 1.17 +++ modules/simpletest/tests/form_test.module 26 Nov 2009 11:31:53 -0000 @@ -10,6 +10,14 @@ * Implement hook_menu(). */ function form_test_menu() { + $items['form-test/validate'] = array( + 'title' => 'Form validation handlers test', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_validate_form'), + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + $items['form_test/tableselect/multiple-true'] = array( 'title' => 'Tableselect checkboxes test', 'page callback' => 'drupal_get_form', @@ -82,6 +90,76 @@ function form_test_menu() { } /** + * Form builder for testing drupal_validate_form(). + * + * Serves for testing form processing and alterations by form validation + * handlers, especially for the case of a validation error: + * - form_set_value() should be able to alter submitted values in + * $form_state['values'] without affecting the form element. + * - #element_validate handlers should be able to alter the $element in the form + * structure and the alterations should be contained in the rebuilt form. + * - #validate handlers should be able to alter the $form and the alterations + * should be contained in the rebuilt form. + */ +function form_test_validate_form($form, &$form_state) { + $form['name'] = array( + '#type' => 'textfield', + '#title' => 'Name', + '#default_value' => '', + '#required' => TRUE, + '#element_validate' => array('form_test_element_validate_name'), + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => 'Save', + ); + return $form; +} + +/** + * Form element validation handler for 'name' in form_test_validate_form(). + */ +function form_test_element_validate_name(&$element, &$form_state) { + if ($form_state['values']['name'] == 'element_validate') { + // Alter the form element. + $element['#value'] = '#value changed by #element_validate'; + // Alter the submitted value in $form_state. + form_set_value($element, 'value changed by form_set_value() in #element_validate', $form_state); + // Output the element's value from $form_state. + drupal_set_message(t('@label value: @value', array('@label' => $element['#title'], '@value' => $form_state['values']['name']))); + + // Trigger a form validation error, so the form is rebuilt. + form_set_error(''); + } + if ($form_state['values']['name'] == 'element_validate_access') { + // Alter the form element. + $element['#access'] = FALSE; + // Output the element's value from $form_state. + drupal_set_message(t('@label value: @value', array('@label' => $element['#title'], '@value' => $form_state['values']['name']))); + + // Trigger a form validation error, so the form is rebuilt. + form_set_error(''); + } +} + +/** + * Form validation handler for form_test_validate_form(). + */ +function form_test_validate_form_validate(&$form, &$form_state) { + if ($form_state['values']['name'] == 'validate') { + // Alter the form element. + $form['name']['#value'] = '#value changed by #validate'; + // Alter the submitted value in $form_state. + form_set_value($form['name'], 'value changed by form_set_value() in #validate', $form_state); + // Output the element's value from $form_state. + drupal_set_message(t('@label value: @value', array('@label' => $form['name']['#title'], '@value' => $form_state['values']['name']))); + + // Trigger a form validation error, so the form is rebuilt. + form_set_error(''); + } +} + +/** * Create a header and options array. Helper function for callbacks. */ function _form_test_tableselect_get_data() {