=== modified file 'includes/form.inc' --- includes/form.inc 2009-12-17 21:59:31 +0000 +++ includes/form.inc 2009-12-25 08:04:27 +0000 @@ -280,6 +280,7 @@ function form_state_defaults() { 'cache'=> FALSE, 'method' => 'post', 'groups' => array(), + 'validate_exclusively_on_button' => array(), ); } @@ -412,6 +413,7 @@ function form_state_keys_no_cache() { 'method', 'submit_handlers', 'submitted', + 'validate_exclusively_on_button', 'validate_handlers', 'values', ); @@ -786,6 +788,12 @@ function drupal_validate_form($form_id, } } + // Only keep the validate_exclusively_on_button state if there is a section + // relating to the pressed button. + if (!empty($form_state['validate_exclusively_on_button']) && !in_array($form_state['clicked_button']['#value'], $form_state['validate_exclusively_on_button'])) { + $form_state['validate_exclusively_on_button'] = array(); + } + _form_validate($form, $form_state, $form_id); $validated_forms[$form_id] = TRUE; } @@ -884,7 +892,9 @@ function _form_validate(&$elements, &$fo // Recurse through all children. foreach (element_children($elements) as $key) { - if (isset($elements[$key]) && $elements[$key]) { + // If there was no button-specific validate section specified or this + // element is inside that section then validate. + if (empty($form_state['validate_exclusively_on_button']) || (isset($elements[$key]['#validate_exclusively_on_button']) && in_array($form_state['clicked_button']['#value'], $elements[$key]['#validate_exclusively_on_button']))) { _form_validate($elements[$key], $form_state); } } @@ -967,8 +977,9 @@ function form_execute_handlers($type, &$ if (isset($form_state[$type . '_handlers'])) { $handlers = $form_state[$type . '_handlers']; } - // Otherwise, check for a form-level handler. - elseif (isset($form['#' . $type])) { + // Otherwise, check for a form-level handler. If a per-button validate is in + // effect then neither the validate nor the submit handler should fire. + elseif (isset($form['#' . $type]) && empty($form_state['validate_exclusively_on_button'])) { $handlers = $form['#' . $type]; } else { @@ -1006,7 +1017,7 @@ function form_execute_handlers($type, &$ * @param $message * The error message to present to the user. * @return - * Return value is for internal use only. To get a list of errors, use + * Return value is for internal use only. To get a list of errors, use * form_get_errors() or form_get_error(). */ function form_set_error($name = NULL, $message = '') { @@ -1170,6 +1181,10 @@ function form_builder($form_id, $element // later. unset($element['#sorted']); } + if (isset($element['#validate_exclusively_on_button'])) { + $element[$key]['#validate_exclusively_on_button'] = $element['#validate_exclusively_on_button']; + $form_state['validate_exclusively_on_button'] = array_merge($form_state['validate_exclusively_on_button'], $element['#validate_exclusively_on_button']); + } $element[$key] = form_builder($form_id, $element[$key], $form_state); $count++; } @@ -3261,7 +3276,7 @@ function batch_process($redirect = NULL, $batch =& batch_get(); drupal_theme_initialize(); - + if (isset($batch)) { // Add process information $process_info = array( @@ -3276,7 +3291,7 @@ function batch_process($redirect = NULL, ); $batch += $process_info; - // The batch is now completely built. Allow other modules to make changes to the + // The batch is now completely built. Allow other modules to make changes to the // batch so that it is easier to reuse batch processes in other enviroments. drupal_alter('batch', $batch); === modified file 'modules/field/field.form.inc' --- modules/field/field.form.inc 2009-12-21 13:47:31 +0000 +++ modules/field/field.form.inc 2009-12-25 07:07:21 +0000 @@ -227,6 +227,7 @@ function field_multiple_value_form($fiel '#field_name' => $field_name, '#language' => $langcode, ); + $field_elements[$field_name]['#validate_exclusively_on_button'] = array(t('Add another item')); } } } === modified file 'modules/poll/poll.module' --- modules/poll/poll.module 2009-12-14 20:38:15 +0000 +++ modules/poll/poll.module 2009-12-25 07:08:53 +0000 @@ -234,6 +234,7 @@ function poll_form($node, &$form_state) // Add a wrapper for the choices and more button. $form['choice_wrapper'] = array( '#tree' => FALSE, + '#validate_exclusively_on_button' => array(t('More choices')), '#weight' => -4, '#prefix' => '
', '#suffix' => '
', @@ -901,4 +902,3 @@ function poll_user_cancel($edit, $accoun break; } } - === modified file 'modules/simpletest/tests/form.test' --- modules/simpletest/tests/form.test 2009-12-17 17:18:02 +0000 +++ modules/simpletest/tests/form.test 2009-12-25 07:19:51 +0000 @@ -231,6 +231,19 @@ class FormValidationTestCase extends Dru $this->assertNoFieldByName('name', t('Form element was hidden.')); $this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.')); } + + /** + * Test button press validates a section exclusively. + */ + function testValidateExclusivelyOnButton() { + $edit = array('test' => 'pass'); + $this->drupalPost('form-test/validate-exclusively-on-button', $edit, t('Partial validate')); + $this->assertNoText(t('!name field is required.', array('!name' => 'Title'))); + $this->assertText('Test is validated'); + $this->drupalPost('form-test/validate-exclusively-on-button', $edit, t('Full validate')); + $this->assertText(t('!name field is required.', array('!name' => 'Title'))); + $this->assertText('Test is validated'); + } } /** === modified file 'modules/simpletest/tests/form_test.module' --- modules/simpletest/tests/form_test.module 2009-12-17 17:18:02 +0000 +++ modules/simpletest/tests/form_test.module 2009-12-25 07:47:41 +0000 @@ -17,6 +17,13 @@ function form_test_menu() { 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); + $items['form-test/validate-exclusively-on-button'] = array( + 'title' => 'Form validation exclusively on button test', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_validate_exclusively_on_button'), + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); $items['form_test/tableselect/multiple-true'] = array( 'title' => 'Tableselect checkboxes test', @@ -203,6 +210,34 @@ function form_test_validate_form_validat } } +function form_test_validate_exclusively_on_button($form) { + $form['title'] = array( + '#type' => 'textfield', + '#title' => 'Title', + '#required' => TRUE, + ); + $form['wrapper']['#validate_exclusively_on_button'] = array(t('Partial validate')); + $form['wrapper']['test'] = array( + '#type' => 'textfield', + '#element_validate' => array('form_test_validate_test_element'), + ); + $form['buttons']['partial'] = array( + '#type' => 'submit', + '#value' => t('Partial validate'), + ); + $form['buttons']['full'] = array( + '#type' => 'submit', + '#value' => t('Full validate'), + ); + return $form; +} + +function form_test_validate_test_element($form, $form_state) { + if ($form_state['values']['test'] == 'pass') { + drupal_set_message('Test is validated'); + } +} + /** * Create a header and options array. Helper function for callbacks. */ @@ -895,7 +930,7 @@ function form_test_state_persist($form, /** * Submit handler. - * + * * @see form_test_state_persist() */ function form_test_state_persist_submit($form, &$form_state) { @@ -905,7 +940,7 @@ function form_test_state_persist_submit( /** * Implements hook_form_FORM_ID_alter(). - * + * * @see form_test_state_persist() */ function form_test_form_form_test_state_persist_alter(&$form, &$form_state) {