- Patch #642702 by sun: form validation handlers cannot alter structure.

merge-requests/26/head
Dries Buytaert 2009-11-28 14:39:31 +00:00
parent 0ba214f943
commit b608482678
3 changed files with 154 additions and 3 deletions

View File

@ -726,7 +726,13 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
* A unique string identifying the form for validation, submission,
* theming, and hook_form_alter functions.
* @param $form
* An associative array containing the structure of the form.
* An associative array containing the structure of the form, which is passed
* by reference. Form validation handlers are able to alter the form structure
* (like #process and #after_build callbacks during form building) in case of
* a validation error. If a validation handler alters the form structure, it
* is responsible for validating the values of changed form elements in
* $form_state['values'] to prevent form submit handlers from receiving
* unvalidated values.
* @param $form_state
* A keyed array containing the current state of the form. The current
* user-submitted data is stored in $form_state['values'], though
@ -738,7 +744,7 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
* 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'])) {
@ -846,7 +852,7 @@ function drupal_redirect_form($form_state) {
* 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();

View File

@ -152,6 +152,61 @@ class FormsTestCase extends DrupalWebTestCase {
}
}
/**
* 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() {
$this->drupalGet('form-test/validate');
// Verify that #element_validate handlers can alter the form and submitted
// form values.
$edit = array(
'name' => 'element_validate',
);
$this->drupalPost(NULL, $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(NULL, $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(NULL, $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.'));
$this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.'));
}
}
/**
* Test the tableselect form element for expected behavior.
*/

View File

@ -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',
@ -81,6 +89,88 @@ function form_test_menu() {
return $items;
}
/**
* 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' => '',
'#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) {
$triggered = FALSE;
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);
$triggered = TRUE;
}
if ($form_state['values']['name'] == 'element_validate_access') {
// To simplify this test, enable form caching and use form storage to
// remember our alteration.
$form_state['cache'] = TRUE;
$form_state['storage']['form_test_name'] = $form_state['values']['name'];
// Alter the form element.
$element['#access'] = FALSE;
$triggered = TRUE;
}
elseif (!empty($form_state['storage']['form_test_name'])) {
// To simplify this test, just take over the element's value into $form_state.
form_set_value($element, $form_state['storage']['form_test_name'], $form_state);
$triggered = TRUE;
}
if ($triggered) {
// 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 to see our changes.
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 to see our changes.
form_set_error('');
}
}
/**
* Create a header and options array. Helper function for callbacks.
*/