Index: modules/simpletest/tests/form_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form_test.module,v retrieving revision 1.1 diff -u -r1.1 form_test.module --- modules/simpletest/tests/form_test.module 28 Jan 2009 07:43:26 -0000 1.1 +++ modules/simpletest/tests/form_test.module 15 Mar 2009 21:53:36 -0000 @@ -180,3 +180,32 @@ return _form_test_tableselect_form_builder($form_state, $options); } + +/** + * Dummy form used to test programmatic form submits. + */ +function _form_test_dummy_form($form_state) { + return array( + 'dummyfield' => array( + '#title' => 'Dummyfield', + '#type' => 'textfield' + ) + ); +} + +/** + * In order to test the validation handler, the dummy value + * is explicitly required. + */ +function _form_test_dummy_form_validate($form, &$form_state) { + if (empty($form_state['values']['dummyfield'])) { + form_set_error('dummyfield', t('This field is required although dummy.')); + } +} + +/** + * Process the submitted dummy value. + */ +function _form_test_dummy_form_submit($form, &$form_state) { + $form_state['dummy_submit'] = $form_state['values']['dummyfield']; +} Index: modules/simpletest/tests/form.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form.test,v retrieving revision 1.4 diff -u -r1.4 form.test --- modules/simpletest/tests/form.test 14 Mar 2009 20:13:27 -0000 1.4 +++ modules/simpletest/tests/form.test 15 Mar 2009 21:53:36 -0000 @@ -315,3 +315,56 @@ } +/** + * Test the programmatic form submit behavior. + */ +class FormsProgrammaticSubmitFunctionalTest extends DrupalWebTestCase { + + function getInfo() { + return array( + 'name' => t('Programmatically submitted forms test'), + 'description' => t('Test the programmatic form submit behavior.'), + 'group' => t('Form API'), + ); + } + + function setUp() { + parent::setUp('form_test'); + } + + /** + * Test multiple programmatic form submits (see http://drupal.org/node/260934). + */ + function testMultipleSubmit() { + // Backup the current batch status and reset it to avoid conflicts + // while processing the dummy form submit handler. + $current_batch = $batch =& batch_get(); + $batch = array(); + + $this->submitDummyForm(); + $this->submitDummyForm('test 1'); + $this->submitDummyForm(); + $this->submitDummyForm('test 2'); + + // Restore the current batch status. + $batch = $current_batch; + } + + /** + * Helper function used to programmatically submit the dummy form + * defined in form_test.module with the given value. + * + * @param string $value + * The dummyfield submitted value. + */ + private function submitDummyForm($value = NULL) { + $form_state = array('values' => array('dummyfield' => $value)); + drupal_execute('_form_test_dummy_form', $form_state); + $errors = form_get_errors(); + $valid_form = empty($errors); + $result = empty($value) ? !$valid_form : $valid_form; + $args = array('%value' => $value, '%errors' => $valid_form ? '' : implode(' ', $errors)); + $this->assertTrue($result, t('Dummyfield value: %value
Error: %errors', $args)); + $this->assertTrue(!$valid_form || $form_state['dummy_submit'] == $value, t('Form executed')); + } +} Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.324 diff -u -r1.324 form.inc --- includes/form.inc 14 Mar 2009 20:13:26 -0000 1.324 +++ includes/form.inc 15 Mar 2009 21:53:36 -0000 @@ -377,9 +377,12 @@ $form = drupal_retrieve_form($form_id, $form_state); $form_state['input'] = $form_state['values']; $form_state['programmed'] = TRUE; + $form_state['must_validate'] = TRUE; // Merge in default values. $form_state += form_state_defaults(); + _form_set_current($form); + drupal_prepare_form($form_id, $form, $form_state); drupal_process_form($form_id, $form, $form_state); } @@ -669,7 +672,6 @@ form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.')); } } - _form_validate($form, $form_state, $form_id); $validated_forms[$form_id] = TRUE; } @@ -892,13 +894,14 @@ if ($reset) { $form = array(); } - if (isset($name) && !isset($form[$name])) { - $form[$name] = $message; + $build_id = _form_set_current(); + if (isset($name) && !isset($form[$build_id][$name])) { + $form[$build_id][$name] = $message; if ($message) { drupal_set_message($message, 'error'); } } - return $form; + return isset($form[$build_id]) ? $form[$build_id] : array(); } /** @@ -1443,6 +1446,17 @@ } } +/** + * Stores the current form build id, generating it if missing. + */ +function _form_set_current(&$form = NULL) { + static $current = 'form-current'; + if (isset($form)) { + $current = isset($form['#build_id']) ? $form['#build_id'] : 'form-'. md5(uniqid(mt_rand(), TRUE)); + } + return $current; +} + function form_options_flatten($array, $reset = TRUE) { static $return;