=== modified file 'includes/form.inc' --- includes/form.inc 2009-11-27 15:14:58 +0000 +++ includes/form.inc 2009-11-29 13:32:25 +0000 @@ -333,11 +333,6 @@ function drupal_rebuild_form($form_id, & form_set_cache($form_build_id, $form, $form_state); } - // Clear out all post data, as we don't want the previous step's - // data to pollute this one and trigger validate/submit handling, - // then process the form for rendering. - $form_state['input'] = array(); - // Also clear out all group associations as these might be different // when rerendering the form. $form_state['groups'] = array(); @@ -931,9 +926,11 @@ function _form_validate($elements, &$for */ function form_execute_handlers($type, &$form, &$form_state) { $return = FALSE; + // If there was a button pressed, use its handlers. if (isset($form_state[$type . '_handlers'])) { $handlers = $form_state[$type . '_handlers']; } + // Otherwise, check for a form level handler. elseif (isset($form['#' . $type])) { $handlers = $form['#' . $type]; } @@ -1205,21 +1202,18 @@ function _form_builder_handle_input_elem foreach ($element['#parents'] as $parent) { $input = isset($input[$parent]) ? $input[$parent] : NULL; } - // If we have input for the current element, assign it to the #value property. - if (!$form_state['programmed'] || isset($input)) { - // Call #type_value to set the form value; - if (function_exists($value_callback)) { - $element['#value'] = $value_callback($element, $input, $form_state); - } - if (!isset($element['#value']) && isset($input)) { - $element['#value'] = $input; - } + // Call #type_value to set the form value; + if (function_exists($value_callback)) { + $element['#value'] = $value_callback($element, $input, $form_state); } - // Mark all posted values for validation. - if (isset($element['#value']) || (!empty($element['#required']))) { - $element['#needs_validation'] = TRUE; + if (!isset($element['#value']) && isset($input)) { + $element['#value'] = $input; } } + // Mark all posted values for validation. + if (isset($element['#value']) || (!empty($element['#required']))) { + $element['#needs_validation'] = TRUE; + } // Load defaults. if (!isset($element['#value'])) { // Call #type_value without a second argument to request default_value handling. === modified file 'modules/node/node.pages.inc' --- modules/node/node.pages.inc 2009-11-08 10:02:41 +0000 +++ modules/node/node.pages.inc 2009-11-29 14:31:32 +0000 @@ -111,6 +111,7 @@ function node_form($form, &$form_state, global $user; if (isset($form_state['node'])) { + $form_state['input'] = array(); $node = (object)($form_state['node'] + (array)$node); } if (isset($form_state['node_preview'])) { === modified file 'modules/simpletest/tests/ajax.test' --- modules/simpletest/tests/ajax.test 2009-11-22 02:48:37 +0000 +++ modules/simpletest/tests/ajax.test 2009-11-29 13:08:27 +0000 @@ -141,3 +141,48 @@ class AJAXCommandsTestCase extends AJAXT $this->assertTrue($command['command'] == 'restripe' && $command['selector'] == '#restripe_table', "'restripe' AJAX command issued with correct selector"); } } + +/** + * Test that $form_state['values'] is properly delivered to $ajax['callback']. + */ +class AJAXFormValuesTestCase extends AJAXTestCase { + public static function getInfo() { + return array( + 'name' => 'AJAX command form values', + 'description' => 'Tests that form values are properly delivered to AJAX callbacks.', + 'group' => 'AJAX', + ); + } + + function setUp() { + parent::setUp(); + + $this->web_user = $this->drupalCreateUser(array('access content')); + $this->drupalLogin($this->web_user); + } + + /** + * Create a simple form, then POST to system/ajax to change to it. + */ + function testSimpleAJAXFormValue() { + // Verify form values of a select element. + foreach(array('red', 'green', 'blue') as $item) { + $edit = array( + 'select' => $item, + ); + $commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'select'); + $data_command = $commands[2]; + $this->assertEqual($data_command['value'], $item); + } + + // Verify form values of a checkbox element. + foreach(array(FALSE, TRUE) as $item) { + $edit = array( + 'checkbox' => $item, + ); + $commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'checkbox'); + $data_command = $commands[2]; + $this->assertEqual((int) $data_command['value'], (int) $item); + } + } +} === modified file 'modules/simpletest/tests/ajax_forms_test.module' --- modules/simpletest/tests/ajax_forms_test.module 2009-11-22 02:48:37 +0000 +++ modules/simpletest/tests/ajax_forms_test.module 2009-11-29 13:34:09 +0000 @@ -57,6 +57,7 @@ function ajax_forms_test_simple_form($fo '#type' => 'submit', '#value' => t('submit'), ); + return $form; } === modified file 'modules/simpletest/tests/form.test' --- modules/simpletest/tests/form.test 2009-11-18 18:51:11 +0000 +++ modules/simpletest/tests/form.test 2009-11-29 13:00:35 +0000 @@ -70,7 +70,8 @@ class FormsTestCase extends DrupalWebTes foreach ($data['empty_values'] as $key => $empty) { foreach (array(TRUE, FALSE) as $required) { $form_id = $this->randomName(); - $form = $form_state = array(); + $form = array(); + $form_state = form_state_defaults(); form_clear_error(); $form['op'] = array('#type' => 'submit', '#value' => t('Submit')); $element = $data['element']['#title']; @@ -131,9 +132,7 @@ class FormsTestCase extends DrupalWebTes $this->assertRaw(t('!name field is required.', array('!name' => 'required_checkbox')), t('A required checkbox is actually mandatory')); // Now try to submit the form correctly. - $this->drupalPost(NULL, array('required_checkbox' => 1), t('Submit')); - - $values = json_decode($this->drupalGetContent(), TRUE); + $values = drupal_json_decode($this->drupalPost(NULL, array('required_checkbox' => 1), t('Submit'))); $expected_values = array( 'disabled_checkbox_on' => 'disabled_checkbox_on', 'disabled_checkbox_off' => '', @@ -504,8 +503,7 @@ class FormStateValuesCleanTestCase exten * Tests form_state_values_clean(). */ function testFormStateValuesClean() { - $this->drupalPost('form_test/form-state-values-clean', array(), t('Submit')); - $values = json_decode($this->content, TRUE); + $values = drupal_json_decode($this->drupalPost('form_test/form-state-values-clean', array(), t('Submit'))); // Setup the expected result. $result = array( @@ -533,3 +531,36 @@ class FormStateValuesCleanTestCase exten } } +/** + * Tests form rebuilding. + * + * @todo Add tests for other aspects of form rebuilding. + */ +class FormsRebuildTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Form rebuilding', + 'description' => 'Tests functionality of drupal_rebuild_form().', + 'group' => 'Form API', + ); + } + + function setUp() { + parent::setUp('form_test'); + + $this->web_user = $this->drupalCreateUser(array('access content')); + $this->drupalLogin($this->web_user); + } + + /** + * Tests preservation of values during an "add another item" operation. + */ + function testRebuildPreservesValues() { + $edit = array( + 'grocery' => 'apple', + ); + $this->drupalPost('form-test/form-rebuild-preserve-values', $edit, 'Submit'); + + $this->assertFieldByName('grocery', 'apple', t('Item value kept in a rebuild.')); + } +} === modified file 'modules/simpletest/tests/form_test.module' --- modules/simpletest/tests/form_test.module 2009-11-21 17:01:31 +0000 +++ modules/simpletest/tests/form_test.module 2009-11-29 12:58:56 +0000 @@ -78,6 +78,14 @@ function form_test_menu() { 'type' => MENU_CALLBACK, ); + $items['form-test/form-rebuild-preserve-values'] = array( + 'title' => 'Form values preservation during rebuild test', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_form_rebuild_preserve_values_form'), + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + return $items; } @@ -477,3 +485,29 @@ function _form_test_checkbox_submit($for drupal_json_output($form_state['values']); exit(); } + +/** + * Form builder for testing preservation of values during a rebuild. + * + * Creates a form with two "add another item" buttons, each controlling a + * different element. Used to test that a rebuilt form has the expected values. + */ +function form_test_form_rebuild_preserve_values_form($form, &$form_state) { + $form['grocery'] = array( + '#type' => 'textfield', + '#title' => 'Grocery item', + '#default_value' => 'DEFAULT', + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => 'Submit', + ); + return $form; +} + +/** + * Form submit handler for form_test_form_rebuild_preserve_values_form(). + */ +function form_test_form_rebuild_preserve_values_form_submit($form, &$form_state) { + $form_state['rebuild'] = TRUE; +}