Automattic / Automattic/jetpack
Forms: contact form block dirties templates on load by writing integration defaults into attributes
- Dominant language
- PHP
- Stars
- 1.8k
- Forks
- 898
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 774
Description
### Impacted plugin
Contact Form
### Quick summary
`jetpack/contact-form` marks any template or template part that contains it as having unsaved changes the moment the Site Editor loads it, without the user editing anything.
Cause: `projects/packages/forms/src/blocks/contact-form/shared/hooks/use-form-block-defaults.js` runs a mount `useEffect` that calls `setAttributes()` whenever an integration flag is `undefined`:
- `mailpoet.enabledForForm`
- `jetpackCRM`
- `salesforceData.sendToSalesforce`
Markup coming from a theme file (template, template part, or pattern) never contains these keys — `enabledForForm` is not part of the attribute schema defaults (see #47396, which fixed the server-side symptom of the same asymmetry). So the write fires on every load, the block-editor entity store registers a persistent edit, and the entity is dirty forever.
Observed entity-store diff on a block theme's footer part (WordPress 7.1, Jetpack trunk-era Forms package):
```
saved (parsed from theme file): "mailpoet": { "listId": null, "listName": null }
edited (after block mounts): "mailpoet": { "listId": null, "listName": null, "enabledForForm": false }
```
The `jetpackCRM` / `salesforceData` writes are latent for the same reason and fire for any form markup that omits those attributes.
### Steps to reproduce
1. Use a block theme that ships a `jetpack/contact-form` in a template part (e.g. a newsletter signup form in `parts/footer.html` / a pattern it references). The block markup must not contain a `mailpoet.enabledForForm` attribute — which is the normal case, since the editor never wrote one into exported markup before this hook existed.
2. Open the Site Editor and load any template that renders that part.
3. Without touching anything, the save button activates: "Review 1 change" → the footer template part is listed as modified.
4. Discard; reopen. It comes back every time the canvas loads the block.
Expected: opening a template containing a form leaves the entity clean. Integration defaults should be derived at read time (in the editor UI and the submission handler — the direction #47396 took server-side) rather than written into persisted attributes on mount; at minimum the write should be wrapped in `__unstableMarkNextChangeAsNotPersistent()`.
### Site owner impact
Fewer than 20% of the total website/platform users
### Severity
Minor inconvenience
### What other impact(s) does this issue have?
No revenue impact
### If a workaround is available, please outline it here.
Discard the change each time. If a user instead clicks Save, the template part is persisted as a DB customization that shadows the theme file — future theme updates to that part silently stop applying, and the saved markup carries plugin-specific attribute residue. Theme authors could bake `"mailpoet":{"enabledForForm":false}` etc. into their markup, but that hardcodes one plugin's integration state into distributed theme files.
### Platform (Simple and/or Atomic)
Self-hosted
### Logs or notes
Found while building the Serial theme (WordPress.com pub theme), whose footer newsletter form triggers this on every Site Editor session.
Contributor guide
Assessment
This issue has not been assessed yet.