Skip to content

fix: actually show the rejection message instead of GF's generic banner - #7

Merged
oxyc merged 1 commit into
mainfrom
fix/surface-validation-message
Jul 31, 2026
Merged

oxyc merged 1 commit into
mainfrom
fix/surface-validation-message

Conversation

@oxyc

@oxyc oxyc commented Jul 31, 2026

Copy link
Copy Markdown
Member

The bug

The message this plugin sets when it rejects a submission was never rendered.

validate() wrote it to $form['validation_summary_message'] — but Gravity Forms only reads that as a field context property (form_display.php:5685), never off the form array. $form['failed_validation_page'] was dead the same way: GF tracks the failed page through a by-reference parameter on validate(), not a form key. Both lines looked functional and did nothing.

What visitors actually saw was GF's unconditional fallback (form_display.php:5622):

There was a problem with your submission. Please review the fields below.

There are no fields to review — an ALTCHA rejection highlights nothing. So the banner instructs people to hunt for a broken field that doesn't exist, and whichever fields look most suspicious take the blame.

That isn't hypothetical. This is exactly how the recent expiry bug on the Snellman consumer-service forms got misdiagnosed as a produkt-field problem, costing a round of investigation into conditional logic that turned out to be entirely correct.

The fix

Hook gform_validation_message (filterable at form_display.php:5636) so the message reaches the DOM, and delete the two dead assignments.

  • Scoped to our own rejections. Only forms rejected in this request are affected; ordinary field errors keep GF's wording. Telling someone with a missing email address to reload the page would be worse than useless.
  • Per-form. Two forms on a page — rejecting one doesn't rewrite the other's banner.
  • Markup mirrors GF's, including deriving hide_summary from the form's own validationSummary setting rather than hardcoding it, so an admin's choice isn't overridden and GF's list-appending logic stays consistent. Existing theme styling applies unchanged.

The message itself is unchanged and still filterable via genero/gravityforms_altcha/error_message. It names the actual recovery action — reload and try again — which is correct for every rejection reason we produce.

Verification

End-to-end against a real GF submission:

BEFORE any rejection      → GF default, untouched
AFTER an ALTCHA rejection → "Sorry, your submission could not be verified.
                             Please reload the page and try again."
A DIFFERENT form          → GF default, untouched

4 new tests driving the real validate() reject path rather than a hand-set flag. 67/67 pass, pint --test clean.

Also adds a GFAddOn shell to the CI bootstrap — anything exercising shouldProtect() pulls in Settings, which extends it. Deliberately inert, so tests force protection through the documented should_protect filter instead of leaning on stub return values.

🤖 Generated with Claude Code

The message this plugin sets on rejection was never rendered. It was
written to `$form['validation_summary_message']`, which Gravity Forms only
reads as a *field* context property (form_display.php:5685), never off the
form array. `$form['failed_validation_page']` was dead in the same way --
GF tracks the failed page through a by-reference parameter on validate(),
not a form key. Both lines looked functional and did nothing.

So what a visitor actually saw was GF's unconditional fallback:

    There was a problem with your submission. Please review the fields below.

There are no fields to review -- an ALTCHA rejection highlights nothing.
The instruction sends people hunting for a broken field that doesn't
exist, and the most suspicious-looking fields take the blame. That is
precisely how this was once misdiagnosed as a form-configuration problem
on the Snellman consumer-service forms, costing a round of investigation
into conditional logic that turned out to be correct.

Hook `gform_validation_message` so the message reaches the DOM, and drop
the two dead assignments. Only forms this request rejected are affected,
so ordinary field errors keep GF's wording -- telling someone with a
missing email address to reload the page would be worse than useless.

Markup mirrors GFFormDisplay::get_validation_errors_markup(), deriving the
`hide_summary` class from the form's own validationSummary setting rather
than hardcoding it, so an admin's choice isn't overridden and existing
theme styling still applies.

Verified end-to-end: untouched before a rejection, replaced after one, and
a second form on the same page left alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@oxyc
oxyc merged commit 636c0d2 into main Jul 31, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant