Problem/Motivation

#1493324: Inline form errors for accessibility and UX aims to overhaul how error messages are displayed to the user.
While working through it, it was discovered that a few submit handlers are setting form errors.
This has no effect on the form workflow, and is effectively just a wrapper around drupal_set_message.

Proposed resolution

Replace calls to form_error/form_set_error with drupal_set_message in submit handlers.
This is not yet an API change, because we still need to call drupal_set_message until the UX issue goes in.

Remaining tasks

N/A

User interface changes

N/A

API changes

Earlier in the D8 cycle, file_save_upload() was changed to require the $form_state be passed to it, now its signature is back to the original D7 params.

n previous versions of Drupal, calling form_set_error() and form_error():
During validation would prevent submission, mark the offending element, and use drupal_set_message() to display an error message.
During submission would only use drupal_set_message() to display an error message, and the element would not be marked in any way.

In Drupal 8, calling form_set_error() and form_error() (now FormBuilderInterface::setErrorByName() or FormBuilderInterface::setError()) will only have an effect during validation.
Calling them during submission will do nothing. Those instances should be replaced by a drupal_set_message() call directly.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tim.plunkett’s picture

Status: Active » Needs review
FileSize
14.71 KB

This is major because it blocks a major.

sun’s picture

I 100% agree with this API "change". It is long overdue.

Not really an API change IMHO, this patch just makes code reflect reality. Much rather, this is a major bug fix in my book, because:

There have been plenty of security issues in contributed and custom modules in the past, in which developers assumed they'd do it right, because, "heck, there's a error message!" — but in reality, the code was dead-wrong and a security disaster.

Curious, do you see a way to go one step further and throw an \BadMethodCallException in the setError() methods, in case some code tries to set a form validation error outside of form validation?

Explicitly blowing up would make me even more happy.

  1. +++ b/core/lib/Drupal/Core/Form/FormBuilder.php
    @@ -878,6 +875,25 @@ public function validateForm($form_id, &$form, &$form_state) {
    +    // Store all of the errors for this form at the top level.
    +    $form['#errors'] = $this->getErrors($form_state);
    

    Hm...

    It's not a good idea to mix state into the $form structure.

    I know we have lots of legacy code that is still doing that, but it really should not — $form_state is the only correct home for stateful data.

    It looks like this change is only related to the parent/originating issue and not actually needed here? — If so, can we skip that here?

  2. +++ b/core/modules/file/file.module
    @@ -918,7 +918,7 @@ function file_save_upload($form_field_name, array &$form_state, $validators = ar
    -      form_set_error($form_field_name, $form_state, $message);
    +      drupal_set_message($message);
    
    @@ -928,7 +928,7 @@ function file_save_upload($form_field_name, array &$form_state, $validators = ar
    -      form_set_error($form_field_name, $form_state, t('File upload error. Could not move uploaded file.'));
    +      drupal_set_message(t('File upload error. Could not move uploaded file.'));
    

    (and elsewhere)

    drupal_set_message() $type defaults to 'status', not 'error' :-)

  3. +++ b/core/modules/update/update.manager.inc
    @@ -696,7 +696,7 @@ function update_manager_install_form_submit($form, &$form_state) {
         // @todo: Fix me in D8: We need a way to set multiple errors on the same
         // form element and have all of them appear!
    

    Very interesting @todo in the diff context — I wasn't aware of that idea/requirement.

tim.plunkett’s picture

FileSize
14.61 KB
15.27 KB
1.8 KB
899 bytes

#2.1 Yep, that snuck in. Let's debate the propriety of that in the other issue
#2.2 Whoops, that's just me being sloppy. I rechecked all the other calls, and I think it was just those two.

Re: BadMethodCallException, I've uploaded a version with something like that. However, I'm tempted to just open a dedicated issue and resolve the @todo that went along with it.

Status: Needs review » Needs work

The last submitted patch, 3: form-errors-2239299-3b.patch, failed testing.

sun’s picture

Status: Needs work » Reviewed & tested by the community

Happy to make the BadMethodCallException a follow-up.

Off-hand without studying, the test failures appear to be caused by a lack of a reset of $this->validatedForms, and because it's keyed by $form_id, those tests are failing because they submit the same form more than once.

Not sure why you went with that condition, but normally $form_state['submitted'] is the unique condition that FormBuilder::processForm() uses itself to determine whether to enter the submission phase, so theoretically a simple negated !$form_state['submitted'] should cut it.

catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 8.x, thanks!

  • Commit 26d253c on 8.x by catch:
    Issue #2239299 by tim.plunkett: Form errors should only be set during...
sun’s picture

Status: Fixed » Active
Issue tags: +Needs change record

Yay, thanks! :-)

This definitely needs a change notice though.

xjm’s picture

Title: Form errors should only be set during validation » Change record: Form errors should only be set during validation
Priority: Major » Critical
Issue tags: -Needs change record +Missing change record, +beta blocker
tim.plunkett’s picture

We can write a change record here if we'd like, but it won't have anything to back it up until #2241735: Throw an exception when form errors are set outside of validation goes in.
I'm not picky.

xjm’s picture

I'd say create the change record now and reference in both issues, since we need to ahem create it before we commit that issue anyway. ;)

tim.plunkett’s picture

Status: Active » Needs review
tim.plunkett’s picture

Priority: Critical » Major
Status: Needs review » Fixed
Issue tags: -Missing change record
tim.plunkett’s picture

Title: Change record: Form errors should only be set during validation » Form errors should only be set during validation
xjm’s picture

Issue tags: -beta blocker

Thanks @tim.plunkett!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.