Change monitor (unsaved changes warning) is only available in the `document` form design, with no way to opt in from other designs
Body
Summary
The backend change monitor — the browser warning when you navigate away from a form with unsaved changes — is hardcoded into a single design partial. Only the document design enables it; basic, sidebar, survey and popup do not, and there is no supported way for a plugin to turn it on for those designs.
Tested on October CMS v4.3.8 (october/backend v4.3.8).
Current state
data-change-monitor appears in exactly two places in the codebase:
modules/backend/formdesigns/documentdesign/partials/_mode.php:10<?= Form::open(['class' => 'position-relative h-100', 'data-change-monitor' => true]) ?>modules/tailor/controllers/entries/_form_mode_document.php:1(same design, controller-level override)
The other designs open the form without it:
modules/backend/formdesigns/basicdesign/partials/_mode.php:4—Form::open(['class' => 'd-flex flex-column h-100 design-basic'])modules/backend/formdesigns/sidebardesign/partials/_mode.php:31—Form::open(['class' => 'position-relative h-100'])modules/backend/formdesigns/popupdesign/partials/_mode.php:1— id +data-popup-sizeonlySurveyDesignextendsBasicDesignand shares its partials, so it inherits the omission
The control itself is generic and design-agnostic (modules/system/assets/toolbox/controls/change-monitor/) — nothing about it is specific to the document layout.
Why this is a problem
Any backend form rendered through formRenderDesign() with the default basic design silently discards unsaved edits when the user hits Cancel, clicks a menu link, or closes the tab. That is the majority of plugin backend forms. Before the form designs refactor this was less visible; now that basic is the default path, the inconsistency is easy to hit and there is no configuration for it.
The only workaround today is to register a custom design class extending BasicDesign and copy basicdesign/partials/_mode.php verbatim into the plugin just to add one attribute to Form::open(). That copy then has to be diffed against core on every October update, for a one-attribute difference.
Proposals
In order of how small the change is:
1. A design config option (preferred). FormDesignBase::getDesignConfig() already exists and reads design[...] from the FormController YAML with a context-specific fallback, so the plumbing is in place:
design:
displayMode: basic
changeMonitor: trueand in each _mode.php:
<?= Form::open(['class' => '...', 'data-change-monitor' => $this->getDesignConfig('changeMonitor', false)]) ?>No API change, defaults to the current behaviour, one line per partial. (getDesignConfig() is protected, so it would need to be reachable from the partial — either made public or exposed through a small helper.)
2. A form attributes hook. Add FormDesignBase::getDesignFormAttributes(): array returning [] by default, merged into Form::open() in every _mode.php. A plugin could then override attributes in a design subclass without copying any partial — useful beyond the change monitor.
3. Enable it by default in basic, matching document. Most consistent, but it changes behaviour for every existing backend form, so option 1 with a false default seems safer.
Source: octobercms/october