diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index 20df089..1046499 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -606,12 +606,12 @@ public function processForm($form_id, &$form, &$form_state) { // form is processed, so scenarios that result in the form being built // behind the scenes and again for the browser don't increment all the // element IDs needlessly. - if (!$this->getAnyErrors($form_state)) { + if (!$this->getAnyErrors()) { // In case of errors, do not break HTML IDs of other forms. $this->drupalStaticReset('drupal_html_id'); } - if ($form_state['submitted'] && !$this->getAnyErrors($form_state) && !$form_state['rebuild']) { + if ($form_state['submitted'] && !$this->getAnyErrors() && !$form_state['rebuild']) { // Execute form submit handlers. $this->executeHandlers('submit', $form, $form_state); @@ -676,7 +676,7 @@ public function processForm($form_id, &$form, &$form_state) { // along with element-level #submit properties, it makes no sense to // have divergent form execution based on whether the triggering element // has #executes_submit_callback set to TRUE. - if (($form_state['rebuild'] || !$form_state['executed']) && !$this->getAnyErrors($form_state)) { + if (($form_state['rebuild'] || !$form_state['executed']) && !$this->getAnyErrors()) { // Form building functions (e.g., self::handleInputElement()) may use // $form_state['rebuild'] to determine if they are running in the // context of a rebuild, so ensure it is set. @@ -854,7 +854,11 @@ public function validateForm($form_id, &$form, &$form_state) { } } + // Recursively validate each form element. $this->doValidateForm($form, $form_state, $form_id); + // After validation, loop through and assign each element its errors. + $this->doCheckErrors($form, $form_state); + // Mark this form as validated. $this->validatedForms[$form_id] = TRUE; // If validation errors are limited then remove any non validated form values, @@ -1133,7 +1137,6 @@ protected function doValidateForm(&$elements, &$form_state, $form_id = NULL) { } $elements['#validated'] = TRUE; - $elements['#errors'] = $this->getError($elements, $form_state); } // Done validating this element, so turn off error suppression. @@ -1143,6 +1146,29 @@ protected function doValidateForm(&$elements, &$form_state, $form_id = NULL) { } /** + * Stores the errors of each element directly on the element. + * + * Because self::getError() and self::getErrors() require the $form_state, + * we must provide a way for non-form functions to check the errors for a + * specific element. The most common usage of this is a #pre_render callback. + * + * @param array $elements + * An associative array containing the structure of a form element. + * @param array $form_state + * An associative array containing the current state of the form. + */ + protected function doCheckErrors(array &$elements, array &$form_state) { + // Recurse through all children. + foreach ($this->elementChildren($elements) as $key) { + if (isset($elements[$key]) && $elements[$key]) { + $this->doCheckErrors($elements[$key], $form_state); + } + } + // Store the errors for this element on the element directly. + $elements['#errors'] = $this->getError($elements, $form_state); + } + + /** * {@inheritdoc} */ public function executeHandlers($type, &$form, &$form_state) { @@ -1232,24 +1258,10 @@ public function getErrors(array $form_state) { } /** - * Returns if there have been any errors during build. - * - * This will include any other forms built during this request. - * - * @param array $form_state - * An associative array containing the current state of the form. - * - * @return bool - * Whether there have been any errors. + * {@inheritdoc} */ - protected function getAnyErrors(array $form_state) { - if (!empty($form_state['errors'])) { - return TRUE; - } - if ($this->request->attributes->has('_form_errors')) { - return $this->request->attributes->get('_form_errors'); - } - return FALSE; + public function getAnyErrors() { + return (bool) $this->request->attributes->get('_form_errors'); } /** diff --git a/core/lib/Drupal/Core/Form/FormBuilderInterface.php b/core/lib/Drupal/Core/Form/FormBuilderInterface.php index ed5a0f8..6e580c7 100644 --- a/core/lib/Drupal/Core/Form/FormBuilderInterface.php +++ b/core/lib/Drupal/Core/Form/FormBuilderInterface.php @@ -780,4 +780,14 @@ public function setValue($element, $value, &$form_state); */ public function setRequest(Request $request); + /** + * Returns if there have been any errors during build. + * + * This will include any forms built during this request. + * + * @return bool + * Whether there have been any errors. + */ + public function getAnyErrors(); + } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php b/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php index 0c167d9..3a4a131 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php @@ -148,7 +148,7 @@ public function validateForm(array &$form, array &$form_state) { */ public function submitForm(array &$form, array &$form_state) { $validators = array('file_validate_extensions' => array('opml xml')); - if ($file = file_save_upload('upload', $validators, FALSE, 0)) { + if ($file = file_save_upload('upload', $form_state, $validators, FALSE, 0)) { $data = file_get_contents($file->getFileUri()); } else { diff --git a/core/modules/file/file.module b/core/modules/file/file.module index 97a153b..8ac1514 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -729,6 +729,8 @@ function file_cron() { * @param $form_field_name * A string that is the associative array key of the upload form element in * the form array. + * @param array $form_state + * An associative array containing the current state of the form. * @param $validators * An optional, associative array of callback functions used to validate the * file. See file_validate() for a full discussion of the array format. @@ -764,7 +766,7 @@ function file_cron() { * - source: Path to the file before it is moved. * - destination: Path to the file after it is moved (same as 'uri'). */ -function file_save_upload($form_field_name, $validators = array(), $destination = FALSE, $delta = NULL, $replace = FILE_EXISTS_RENAME) { +function file_save_upload($form_field_name, array &$form_state, $validators = array(), $destination = FALSE, $delta = NULL, $replace = FILE_EXISTS_RENAME) { $user = \Drupal::currentUser(); static $upload_cache; @@ -908,8 +910,6 @@ function file_save_upload($form_field_name, $validators = array(), $destination // Call the validation functions specified by this function's caller. $errors = file_validate($file, $validators); - // @todo Find a way to pass through the correct $form_state. - $form_state = form_state_defaults(); // Check for errors. if (!empty($errors)) { @@ -1276,7 +1276,7 @@ function file_managed_file_process($element, &$form_state, $form) { * * This function is assigned as a #value_callback in file_element_info(). */ -function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) { +function file_managed_file_value(&$element, $input, &$form_state) { // Find the current value of this field. $fids = !empty($input['fids']) ? explode(' ', $input['fids']) : array(); foreach ($fids as $key => $fid) { @@ -1289,7 +1289,7 @@ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) $return = $input; // Uploads take priority over all other values. - if ($files = file_managed_file_save_upload($element)) { + if ($files = file_managed_file_save_upload($element, $form_state)) { if ($element['#multiple']) { $fids = array_merge($fids, array_keys($files)); } @@ -1454,14 +1454,14 @@ function file_managed_file_submit($form, &$form_state) { * * @param $element * The FAPI element whose values are being saved. + * @param array $form_state + * An associative array containing the current state of the form. * * @return * An array of file entities for each file that was saved, keyed by its file * ID, or FALSE if no files were saved. */ -function file_managed_file_save_upload($element) { - // @todo Find a way to pass through the correct $form_state. - $form_state = form_state_defaults(); +function file_managed_file_save_upload($element, array &$form_state) { $upload_name = implode('_', $element['#parents']); if (empty($_FILES['files']['name'][$upload_name])) { return FALSE; @@ -1478,7 +1478,7 @@ function file_managed_file_save_upload($element) { $files_uploaded = $element['#multiple'] && count(array_filter($_FILES['files']['name'][$upload_name])) > 0; $files_uploaded |= !$element['#multiple'] && !empty($_FILES['files']['name'][$upload_name]); if ($files_uploaded) { - if (!$files = file_save_upload($upload_name, $element['#upload_validators'], $destination)) { + if (!$files = file_save_upload($upload_name, $form_state, $element['#upload_validators'], $destination)) { watchdog('file', 'The file upload failed. %upload', array('%upload' => $upload_name)); form_set_error($upload_name, $form_state, t('Files in the !name field were unable to be uploaded.', array('!name' => $element['#title']))); return array(); diff --git a/core/modules/file/tests/file_test/lib/Drupal/file_test/Form/FileTestForm.php b/core/modules/file/tests/file_test/lib/Drupal/file_test/Form/FileTestForm.php index f2d6dc2..3c5f1fe 100644 --- a/core/modules/file/tests/file_test/lib/Drupal/file_test/Form/FileTestForm.php +++ b/core/modules/file/tests/file_test/lib/Drupal/file_test/Form/FileTestForm.php @@ -101,7 +101,7 @@ public function submitForm(array &$form, array &$form_state) { $validators['file_validate_extensions'] = array($form_state['values']['extensions']); } - $file = file_save_upload('file_test_upload', $validators, $destination, 0, $form_state['values']['file_test_replace']); + $file = file_save_upload('file_test_upload', $form_state, $validators, $destination, 0, $form_state['values']['file_test_replace']); if ($file) { $form_state['values']['file_test_upload'] = $file; drupal_set_message(t('File @filepath was uploaded.', array('@filepath' => $file->getFileUri()))); diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index 1e3b21d..078e409 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -111,7 +111,7 @@ function locale_translate_import_form($form, &$form_state) { */ function locale_translate_import_form_submit($form, &$form_state) { // Ensure we have the file uploaded. - if ($file = file_save_upload('file', $form['file']['#upload_validators'], 'translations://', 0)) { + if ($file = file_save_upload('file', $form_state, $form['file']['#upload_validators'], 'translations://', 0)) { // Add language, if not yet supported. $language = language_load($form_state['values']['langcode']); diff --git a/core/modules/system/lib/Drupal/system/Form/ThemeSettingsForm.php b/core/modules/system/lib/Drupal/system/Form/ThemeSettingsForm.php index 49ca0eb..39d0caa 100644 --- a/core/modules/system/lib/Drupal/system/Form/ThemeSettingsForm.php +++ b/core/modules/system/lib/Drupal/system/Form/ThemeSettingsForm.php @@ -323,7 +323,7 @@ public function validateForm(array &$form, array &$form_state) { $validators = array('file_validate_is_image' => array()); // Check for a new uploaded logo. - $file = file_save_upload('logo_upload', $validators, FALSE, 0); + $file = file_save_upload('logo_upload', $form_state, $validators, FALSE, 0); if (isset($file)) { // File upload was attempted. if ($file) { @@ -339,7 +339,7 @@ public function validateForm(array &$form, array &$form_state) { $validators = array('file_validate_extensions' => array('ico png gif jpg jpeg apng svg')); // Check for a new uploaded favicon. - $file = file_save_upload('favicon_upload', $validators, FALSE, 0); + $file = file_save_upload('favicon_upload', $form_state, $validators, FALSE, 0); if (isset($file)) { // File upload was attempted. if ($file) { diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc index 9f9b55b..e411c2f 100644 --- a/core/modules/update/update.manager.inc +++ b/core/modules/update/update.manager.inc @@ -665,7 +665,7 @@ function update_manager_install_form_submit($form, &$form_state) { elseif ($_FILES['files']['name']['project_upload']) { $validators = array('file_validate_extensions' => array(archiver_get_extensions())); $field = 'project_upload'; - if (!($finfo = file_save_upload($field, $validators, NULL, 0, FILE_EXISTS_REPLACE))) { + if (!($finfo = file_save_upload($field, $form_state, $validators, NULL, 0, FILE_EXISTS_REPLACE))) { // Failed to upload the file. file_save_upload() calls form_set_error() on // failure. return; diff --git a/core/modules/user/lib/Drupal/user/Plugin/views/filter/Name.php b/core/modules/user/lib/Drupal/user/Plugin/views/filter/Name.php index 2fce86d..d033e72 100644 --- a/core/modules/user/lib/Drupal/user/Plugin/views/filter/Name.php +++ b/core/modules/user/lib/Drupal/user/Plugin/views/filter/Name.php @@ -52,7 +52,7 @@ protected function valueForm(&$form, &$form_state) { protected function valueValidate($form, &$form_state) { $values = drupal_explode_tags($form_state['values']['options']['value']); - $uids = $this->validate_user_strings($form['value'], $values); + $uids = $this->validate_user_strings($form['value'], $form_state, $values); if ($uids) { $form_state['values']['options']['value'] = $uids; @@ -92,7 +92,7 @@ public function validateExposed(&$form, &$form_state) { $values = drupal_explode_tags($input); if (!$this->options['is_grouped'] || ($this->options['is_grouped'] && ($input != 'All'))) { - $uids = $this->validate_user_strings($form[$identifier], $values); + $uids = $this->validate_user_strings($form[$identifier], $form_state, $values); } else { $uids = FALSE; @@ -108,7 +108,7 @@ public function validateExposed(&$form, &$form_state) { * or the exposed filter, this is abstracted out a bit so it can * handle the multiple input sources. */ - function validate_user_strings(&$form, $values) { + function validate_user_strings(&$form, array &$form_state, $values) { $uids = array(); $placeholders = array(); $args = array(); @@ -134,9 +134,6 @@ function validate_user_strings(&$form, $values) { } if ($missing) { - // @todo Find a way to pass through the correct $form_state. - $form_state = form_state_defaults(); - form_error($form, $form_state, format_plural(count($missing), 'Unable to find user: @users', 'Unable to find users: @users', array('@users' => implode(', ', array_keys($missing))))); } diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/filter/Date.php b/core/modules/views/lib/Drupal/views/Plugin/views/filter/Date.php index 6c7c6ac..2d6b0d3 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/filter/Date.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/filter/Date.php @@ -53,7 +53,7 @@ public function validateOptionsForm(&$form, &$form_state) { return; } - $this->validateValidTime($form['value'], $form_state['values']['options']['operator'], $form_state['values']['options']['value']); + $this->validateValidTime($form['value'], $form_state, $form_state['values']['options']['operator'], $form_state['values']['options']['value']); } public function validateExposed(&$form, &$form_state) { @@ -74,19 +74,16 @@ public function validateExposed(&$form, &$form_state) { $operator = $this->operator; } - $this->validateValidTime($this->options['expose']['identifier'], $operator, $value); + $this->validateValidTime($this->options['expose']['identifier'], $form_state, $operator, $value); } /** * Validate that the time values convert to something usable. */ - public function validateValidTime(&$form, $operator, $value) { + public function validateValidTime(&$form, array &$form_state, $operator, $value) { $operators = $this->operators(); - // @todo Find a way to pass through the correct $form_state. - $form_state = form_state_defaults(); - if ($operators[$operator]['values'] == 1) { $convert = strtotime($value['value']); if (!empty($form['value']) && ($convert == -1 || $convert === FALSE)) { diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php index 17388c9..4207b6b 100644 --- a/core/modules/views/lib/Drupal/views/ViewExecutable.php +++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php @@ -1080,10 +1080,8 @@ public function build($display_id = NULL) { if ($this->display_handler->usesExposed()) { $exposed_form = $this->display_handler->getPlugin('exposed_form'); - // @todo Find a way to pass through the correct $form_state. - $form_state = form_state_defaults(); $this->exposed_widgets = $exposed_form->renderExposedForm(); - if (form_get_errors($form_state) || !empty($this->build_info['abort'])) { + if (\Drupal::formBuilder()->getAnyErrors() || !empty($this->build_info['abort'])) { $this->built = TRUE; // Don't execute the query, $form_state, but rendering will still be executed to display the empty text. $this->executed = TRUE;