=== Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification === Contributors: forge12 Donate link: https://www.paypal.com/donate?hosted_button_id=MGZTVZH3L5L2G Tags: contact form 7, double opt-in, gdpr, email verification Requires at least: 6.0 Tested up to: 7.0 Requires PHP: 7.4 Stable tag: 5.4.0 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html **Protect your Contact Form 7 forms with GDPR-compliant Double Opt-In.** Ensure valid emails, prevent fake signups, and stay compliant. Extend with paid addons for Avada, Elementor, Gravity Forms, WPForms and more. == Description == **Double Opt-In** adds a mandatory email verification step to your Contact Form 7 forms. When a visitor submits your form, the original mail is **not** sent immediately. Instead, the plugin: 1. Stores the submission in a secure database table. 2. Sends a confirmation email with a unique, time-limited link. 3. Only after the visitor clicks that link is the original form mail delivered. This ensures: * Only **valid, verified email addresses** reach your inbox. * **GDPR / DSGVO requirements** are met with proper consent tracking, IP logging, and data retention. * Your database stays **clean and reliable** -- no fake or mistyped addresses. Out-of-the-box support for **Contact Form 7**. Additional form systems — Avada, Elementor, Gravity Forms, WPForms — are available as separate addon plugins. = How It Works = 1. A visitor fills out your Contact Form 7 form and clicks submit. 2. The plugin intercepts the submission, stores the form data, and generates a unique hash. 3. A confirmation email is sent to the visitor's email address containing a verification link. 4. The visitor clicks the link. The plugin verifies the hash, marks the opt-in as confirmed, and sends the original form mail (as if the form was just submitted). 5. The confirmed opt-in is logged in the admin dashboard with timestamps and IP addresses for full GDPR compliance. = Quick Start = [Read the Quick Guide](https://www.forge12.com/blog/so-verwendest-du-das-double-opt-in-fuer-contact-form-7/) = Free Features = * **Block-Based Email Templates** -- build your confirmation email from heading, text, button, spacer, divider and placeholder blocks * **Double Opt-In for Contact Form 7** -- per-form activation with full CF7 integration * **Centralized Form Settings** -- manage all form integrations from a single admin panel * **Email Template Presets** -- start from a pre-built template (one saved template on the free version) * **Send Test Email** -- preview your confirmation emails before going live * **Custom Confirmation Pages** -- redirect users to a specific page after confirmation * **Dynamic Conditions** -- enable opt-in based on user input (e.g. only when a checkbox is checked) * **Delete Confirmation Modal** -- safety dialog before deleting an opt-in record to prevent accidental deletion * **GDPR Consent Export** -- export individual consent records as JSON or CSV directly from the opt-in detail view * **CAPTCHA Compatibility** -- automatically bypasses Forge12 Captcha, Google reCAPTCHA, and hCaptcha during opt-in confirmation to ensure mail delivery * **Rate Limiting** -- configurable IP and email rate limits to prevent abuse * **Error Redirect Page** -- redirect users to a custom page when an opt-in error occurs (rate limit, invalid email) * **Token Expiry** -- confirmation links expire after a configurable time period * **GDPR Data Storage** -- tracks Form ID, Email, Registration/Confirmation Date & IP, Consent Text * **GDPR Anonymization** -- anonymize personal data instead of deleting it * **WordPress Privacy Tools** -- integrates with WordPress personal data export and erasure requests * **Automatic Cleanup** -- configurable auto-deletion of confirmed and unconfirmed entries * **Category System** -- organize opt-ins into categories for better management * **Pagination & Search** -- search and filter opt-in records in the admin dashboard * **Admin Tooltips** -- contextual help tooltips throughout the admin interface * **WordPress Multisite** -- network-wide activation creates tables on all sites automatically * **Developer Hooks** -- 29 action hooks, 63 filters, and 11 typed events for full extensibility = Pro Features = Unlock the full potential of Double Opt-In with the [Pro version](https://www.forge12.com): **Additional Form Integrations:** * **Double Opt-In for Elementor Forms** -- seamless integration with Elementor's form widget * **Double Opt-In for WPForms** -- full support for WPForms submissions * **Double Opt-In for Gravity Forms** -- complete Gravity Forms integration **Email Validation & Spam Protection:** * **Unique Email Validation** -- prevent duplicate submissions per email address (block, silent, or redirect mode) * **MX Validation** -- verify that the email domain has a valid mail server before sending * **Domain Blocklist** -- block disposable and temporary email domains **Email & Communication:** * **Double Opt-Out System** -- unique opt-out links per submission with confirmation emails * **Opt-In Reminder System** -- automatic reminders for unconfirmed opt-ins via cron * **Visual Email Editor** -- drag & drop editor with live preview and mobile preview, plus unlimited saved templates * **Resend Confirmation** -- resend the confirmation email to a single recipient from the admin dashboard * **Conditional Email Templates** -- dynamic content blocks based on form data * **Multi-Column Layouts** -- 2-column, 3-column, and sidebar layouts in the email editor * **Image & Social Blocks** -- add images and social media icons to your emails **Analytics & Export:** * **Analytics Dashboard** -- charts and statistics for opt-in/opt-out rates * **CSV Export** -- export all opt-in records for external processing **User Management:** * **Auto User Creation** -- automatically create WordPress users after opt-in confirmation with configurable role assignment **Support:** * **Premium Support** -- priority email support == Installation == = Automatic Installation = 1. Go to **Plugins > Add New** in your WordPress admin. 2. Search for **"Double Opt-In"**. 3. Click **Install Now** and then **Activate**. = Manual Installation = 1. Download the plugin ZIP file. 2. Upload it to `/wp-content/plugins/double-opt-in/` or use **Plugins > Add New > Upload Plugin**. 3. Activate via the WordPress **Plugins** menu. = First-Time Setup = 1. After activation, go to **Double Opt-In** in the WordPress admin menu. 2. Navigate to **Forms** to see all detected Contact Form 7 forms. 3. Click on a form to enable Double Opt-In and configure the confirmation email. 4. Set the **Recipient Field** to the form field that contains the visitor's email address (e.g. `your-email`). 5. Customize the **Subject** and **Body** of the confirmation email, or choose a template preset. 6. Save the settings and test the form. = Requirements = * WordPress 6.0 or higher * PHP 7.4 or higher * Contact Form 7 5.0+ (for the CF7 integration bundled with Core) == Frequently Asked Questions == = How does Double Opt-In work? = When a visitor submits your form, the plugin stores the submission and sends a confirmation email with a unique link. The original form mail is only delivered after the visitor clicks that link. This verifies that the email address is valid and belongs to the person who filled out the form. = Is this plugin GDPR / DSGVO compliant? = Yes. The plugin tracks all data required for GDPR compliance: consent text, registration and confirmation timestamps, IP addresses, and form data. It integrates with WordPress Privacy Tools for personal data export and erasure requests. You can configure automatic data retention and anonymization policies. = Which form plugins are supported? = The free Core plugin supports **Contact Form 7** out of the box. Support for **Avada Forms**, **Elementor Pro Forms**, **WPForms**, and **Gravity Forms** is available through separate paid addon plugins (install alongside Core). = I used Avada with this plugin before. What happens now? = If you configured Double Opt-In on an Avada form before Core 5.0, a one-time notice appears in your WordPress admin with a **"Claim free Avada grandfather license"** button. One click installs the paid Avada addon with a permanent free license bound to your site. Your existing setup continues working with zero configuration changes. The free claim window is open until October 2026. = Can I customize the confirmation email? = Yes. The plugin includes a visual drag & drop email editor with block-based design. You can choose from pre-built template presets or create your own. Placeholders like `[doubleoptinlink]`, `[doubleoptin_form_date]`, and form field values are replaced automatically. = What happens if the user does not confirm? = Unconfirmed opt-ins are stored in the database and can be cleaned up automatically. You can configure the retention period for unconfirmed entries in the settings (e.g. delete after 30 days). In the Pro version, you can also send automatic reminder emails. = Can I redirect the user to a specific page after confirmation? = Yes. In the per-form settings, you can select a **Confirmation Page**. The user will be redirected there after clicking the confirmation link. = Does the plugin work with CAPTCHA plugins? = Yes. The plugin automatically disables CAPTCHA validation (Google reCAPTCHA, hCaptcha, CF7 Captcha by Forge12) when re-sending the original form mail after confirmation. This prevents false spam detections during the confirmation step. CAPTCHA is re-enabled immediately after the mail has been sent. = Can I enable Double Opt-In only when a checkbox is checked? = Yes. Use the **Conditions** setting in the per-form configuration. Enter the name of a form field (e.g. a checkbox). Double Opt-In will only be triggered when that field has a value. = How do I access form data after confirmation? = **Legacy approach (WordPress hook):** `add_action( 'f12_cf7_doubleoptin_after_confirm', function( $hash, $optIn ) {` ` $data = maybe_unserialize( $optIn->get_content() );` `}, 10, 2 );` **Modern approach (typed event, since 4.0):** Use `OptInConfirmedEvent` via the EventDispatcher. The event provides `getFormData()`, `getEmail()`, `getFormId()`, and more. See `docs/hooks-and-events.md` for the complete reference. = Does it work with WordPress Multisite? = Yes. When activated network-wide, the plugin creates database tables on all existing sites. New sites added to the network automatically get their own tables via the `wp_initialize_site` hook. = Can I use this without Contact Form 7 or Avada? = The free version requires at least one supported form plugin. However, developers can register custom form integrations using the `f12_cf7_doubleoptin_register_integrations` action hook. See the developer documentation for details. = Where can I find the developer documentation? = The complete hook, filter, and event reference is available at `docs/hooks-and-events.md` inside the plugin directory. It covers all 18 action hooks, 23 filters, and 11 typed events with code examples. = How do I report a bug or request a feature? = Please visit [forge12.com](https://www.forge12.com) or contact us via the WordPress support forum. == Screenshots == 1. **Opt-In Dashboard** -- Overview of all opt-in records with status, email, form, date, and actions. 2. **Form Settings** -- Per-form configuration with sender, subject, recipient field, confirmation page, and conditions. 3. **Email Template Editor** -- Visual drag & drop editor with blocks, live preview, and mobile preview. 4. **Template Presets** -- Choose from pre-built email template designs. 5. **Single Opt-In View** -- Detailed view of an opt-in record with form data, timestamps, and IP addresses. 6. **Global Settings** -- Configure data retention, token expiry, telemetry, and opt-out settings. 7. **Category Management** -- Organize opt-in records into categories. == Privacy & Telemetry == **As of version 5.1.7 the plugin no longer transmits any telemetry.** The daily job that used to send a usage snapshot has been removed, and an update also removes it from the WP-Cron schedule of sites that already had it. Usage counters (how many opt-ins were confirmed, for example) are still kept, but they never leave the site. They are stored in a single WordPress option and are used only in the admin -- for instance to decide whether the plugin has been useful long enough to ask you for a review. **We never sell or share data.** = GDPR / DSGVO Compliance = * No personal data, no cookies, no user tracking. * Nothing is sent to any external server. Counters stay in your database and are removed when you uninstall the plugin. * The telemetry setting under **Double Opt-In > Settings** is retained: should transmission ever be reintroduced, it will be honoured before anything is sent. = Bundled assets = The admin interface uses the Inter typeface. It is **self-hosted** -- the font file is embedded in the plugin's own admin bundle, so opening the plugin's screens does not contact Google Fonts or any other third party. Inter is licensed under the SIL Open Font License 1.1 (see licenses/inter-OFL-1.1.txt). == Upgrade Notice == = 5.4.0 = Changes how submissions are handled on forms where you selected an acceptance field. That checkbox is now required at submit time on every form system, including those where it was previously only recorded — a submission that does not confirm it is rejected. Forms whose acceptance field no longer exists keep accepting submissions and are reported under Tools > Site Health instead, so a settings mistake cannot take your registrations offline. No schema changes. = 5.3.1 = Fixes the opt-out link in your emails. The opt-out page you selected was being discarded when settings were read, so `[doubleoptoutlink]` sent recipients to your front page instead of your consent centre. Unsubscribing still worked, but people never saw their overview. Recommended for everyone, no schema changes. = 5.3.0 = The plugin now checks its own runtime requirements and tells you when one is not met: a missing database table shows up under Tools > Site Health with the cause spelled out, plus an exportable "Double Opt-In" section in Site Health > Info to send along with support requests. Nothing to configure, no schema changes. = 5.2.0 = Adds Support and Feedback links so problems can reach us instead of only reaching the review page, and an optional credit link on the confirmation page -- off unless you switch it on. Also repairs the plugin's own links, which pointed at pages that no longer exist. No schema changes. = 5.1.7 = Telemetry is switched off for good: the daily snapshot is gone and the scheduled job is removed from your site. Nothing left the site before either -- the endpoint had been unreachable -- but the setting that was meant to prevent it was never checked. Recommended for everyone, no schema changes. = 5.1.6 = PHP 7.4 compatibility fix. The plugin declared support for PHP 7.4 but shipped a small amount of PHP 8 syntax, which would have caused a fatal error on a 7.4 server. Recommended for everyone — no schema changes. = 5.1.5 = Security & code-quality hardening. Note: the visitor IP is now read from REMOTE_ADDR by default — sites behind a CDN/reverse proxy should register their proxy ranges via the new `f12_doi_trusted_proxies` filter. Safe to update — no schema changes. = 5.1.4 = Maintenance release — safe to update, no schema changes. = 5.1.2 = Email Template editor fixes: centered text now stays centered in sent emails, the builder no longer shows a duplicate Save button, and the Social Icons block gained an editable settings panel. Safe to update — no schema changes. = 5.1.1 = Cosmetic + maintenance update: the admin menu now reads "Double Opt-In" (was "DOI Admin"), plus bundle-only Addons-page polish (a single "Upgrade to Pro" CTA, no per-module license prompts). Safe to update — no schema changes. = 5.1.0 = Form completeness gate: forms with missing required fields are now auto-disabled on upgrade and at save time, so a half-configured form can no longer silently swallow opt-ins. New file-lifecycle hooks delete attachments when an opt-in is deleted (CF7, Avada, Elementor, Gravity, WPForms). New REST endpoints for opt-out page generation and consent-export audit history. Several legacy-frontend and Avada placeholder fixes. Safe to update — no schema changes; one idempotent admin_init migration disables incomplete forms. = 5.0.0 = **Major release.** The free Core plugin now focuses on Contact Form 7. All other form integrations (Avada, Elementor, Gravity Forms, WPForms) move to separate paid addon plugins. Existing free-plugin users with Avada forms configured qualify for a free permanent grandfather license — a one-click claim button appears in admin. Requires PHP 7.4+ (no longer PHP 8.0). = 3.7.2 = Bugfix: Fixed placeholder replacement and admin display broken for Avada forms. Form field placeholders like `[doi_email]` and `[doi_name]` now work correctly in emails. Admin detail view and AJAX modal now show actual form data instead of metadata keys. Added missing `[doubleoptin_privacy_url]` system placeholder. Safe to update. = 3.7.1 = Bugfix: Fixed toggle switch, Avada DOI settings, and Avada recipient field resolution. **Important:** Avada Forms support will move to the Pro version in 3.8.0 -- upgrade now to keep using it. Contact Form 7 remains free. Safe to update. = 3.7.0 = CSS fix for table width on admin pages. Improved compatibility with Pro 3.7.0 license system. Safe to update. = 3.6.0 = Moved consent export to Pro plugin. The export UI and AJAX endpoint are no longer available without the Pro plugin. Added `f12_doi_database_page_after_forms` hook for extensibility. Safe to update. = 3.5.0 = Fixed confirmation mail not being sent for forms with Quiz, Acceptance, or required fields. CF7 validation is now bypassed during opt-in confirmation mail delivery. Safe to update. = 3.4.0 = Fixed translation loading issues on WordPress 6.7+, review notice not displaying, and database table missing errors. Added 133+ missing German translations for the Email Editor and related features. Safe to update. = 3.3.0 = New: Delete confirmation modal, GDPR consent export (JSON/CSV), admin tooltips, error redirect page, hCaptcha compatibility. New: Unique Email redirect behavior (Pro). Fixed reCAPTCHA re-activation typo. Safe to update -- no database changes. = 3.2.3 = Bugfix release: Fixes broken toggle switches on the settings page. Safe to update -- no database changes. = 3.2.2 = Bugfix release: Fixes double-firing of the after_confirm hook. Safe to update -- no database changes. = 3.2.1 = Bugfix release: Fixes a fatal error on new, unsaved CF7 forms. Safe to update. = 3.2.0 = **Important: Major Update -- Please backup before updating!** This version includes significant changes to the form management system, email templates, and database structure. We strongly recommend creating a full site backup before updating. New features: Visual email editor, centralized form settings, GDPR anonymization, and more. = 3.1.0 = Adds optional anonymous telemetry (opt-out). No breaking changes. == Changelog == = 5.4.0 = **The consent checkbox is now enforced everywhere** * Fix: a consent checkbox configured for an Elementor form was recorded but never required. The visitor could submit without ticking it, and the opt-in was stored with your consent text as proof of an agreement nobody had given. The same gap applied to Contact Form 7 and Avada forms running through the older compatibility path. Every form system now enforces the checkbox at submit time, and a submission without it is rejected with "You must agree to the consent statement to continue." * Change: if the acceptance field you configured is **not on the form any more** — renamed or deleted in your form builder — the submission is no longer rejected. It is accepted, and the mismatch is reported under Tools > Site Health instead. A settings mistake should not take your registrations offline, which is what used to happen: the form silently stopped accepting anyone and nothing said why. * New: a Site Health check listing every form whose acceptance field no longer exists, naming the form and the field. Until now nothing pointed this out unless you happened to open that particular form's settings. * New: filter `f12_doi_enforce_consent_gate` to switch the rejection off for a single form, and action `f12_doi_consent_field_unknown` to react to the mismatch yourself. * Improved: the warning on the form settings tab now says what actually happens — submissions go through, but without provable consent — instead of promising a rejection. It is also translated again; since 5.3.2 that warning had been showing in English on German and French sites. = 5.3.2 = * Fix: opt-ins collected through an Elementor form always showed "User acknowledged: no" in the consent audit, even when the visitor had ticked the consent checkbox. Elementor stores its submitted fields differently from the other integrations and the audit view did not know that layout, so it looked in the wrong place. It now reads every integration's layout, and existing records show their acknowledgment correctly without anything having to be re-saved. * Fix: the acceptance field you picked for a form was silently converted to lower case when saved. Any form field whose name contains a capital letter — which Elementor explicitly allows, and which is common on German sites ("Datenschutz") — therefore stopped matching, the form settings kept warning that the selected field does not exist, and picking it again changed nothing. Field names are now stored exactly as the form spells them, and a name that was already converted is repaired the next time the form settings are opened. * Fix: on the integrations that enforce the consent gate, that same conversion meant the configured checkbox was never found at submit time and every registration was rejected as "consent not given". Those forms accept registrations again. * Improved: the warning about a missing acceptance field no longer promises that submissions would be rejected on integrations where the consent gate does not run. = 5.3.1 = * Fix: the opt-out page you selected was discarded when settings were read, so the `[doubleoptoutlink]` placeholder in confirmation and reminder mails pointed at your front page instead of your consent centre. It now resolves to the page you configured. = 5.3.0 = **Telling you when something is broken:** * New: Double Opt-In now reports its own runtime preconditions under Tools → Site Health. If one of the plugin's database tables is missing, you see it there as a critical issue with the reason spelled out, instead of a feature silently failing for your visitors. * New: a "Double Opt-In" section in Site Health → Info listing table status and version numbers. It is exportable — send it along when you contact support and we can skip a round of questions. * New: an admin notice on the dashboard, the plugins screen and the plugin's own pages for as long as such a problem is unresolved. * New: addons contribute their own checks through the `f12_doi_health_checks` filter, so this covers future addons without further work. = 5.2.0 = **Getting hold of us:** * New: Support and Feedback links in the plugin sidebar, in the plugin list and in the admin menu. Until now the only route out of the plugin was the review page, so a problem could only be reported as a public rating that nobody could answer. * New: the review notice offers "Something not working? Tell us" alongside the review button. * Change: the review notice now waits for 25 confirmed opt-ins instead of 3. Three confirmations is too early to ask anyone to vouch for the plugin. * New: an optional dialog when deactivating asks what went wrong. It never blocks or delays deactivation, sends nothing by itself, and appears once. **Optional credit link:** * New: a "Double Opt-In by Forge12" link can be shown on the page a subscriber lands on after confirming. **Off by default** -- it appears only if you switch it on under Settings, and it is marked `nofollow`. * New: after 50 confirmed opt-ins the plugin asks once whether you would like to show it, with a preview of exactly what would appear. Asked once, either answer ends it. * New: filter `f12_doi_confirmation_output` for anyone who wants to put their own markup on the confirmation page -- the plugin had no hook there at all. **Fixes:** * Fix: the plugin's own links pointed into a section of forge12.com that does not exist. Every addon's "Visit plugin site" link, the documentation links, and the link in the Avada migration notice returned 404. Corrected everywhere, including inside the translations, which carried their own copies. = 5.1.7 = **Telemetry removed:** * Fix: the telemetry setting was never checked. The daily job was scheduled regardless of it, and the sending routine did not look at it either -- so switching telemetry off in the settings did not actually switch anything off. * Fix: the daily snapshot has been removed entirely, and updating also removes the job from your site's scheduled tasks. It had been posting to an endpoint that no longer exists, so it failed every day without saying so. * Privacy: usage counters remain on your site and are never transmitted. They are deleted when the plugin is uninstalled. * Note: nothing had actually been transmitted for some time -- the receiving server's TLS certificate was invalid, so WordPress refused the connection. The consent bug is fixed regardless. = 5.1.6 = **PHP 7.4 compatibility:** * Fix: the plugin declared `Requires PHP: 7.4` but shipped PHP 8 syntax (a nullsafe operator and a union return type), which would have caused a fatal error on a PHP 7.4 server. Both are gone — the whole plugin now parses and runs on 7.4. * New: minimum-PHP guard. On a server older than PHP 7.4 the plugin stops before loading anything and shows an admin notice, instead of taking the site down with a white screen. * Fix: the plugin header was missing `Requires at least` and `Requires PHP` entirely, so WordPress could not block activation on an unsupported server. Both are now declared. * Maintenance: the build now refuses to package any file that would fail on PHP 7.4, so this class of problem cannot come back unnoticed. = 5.1.5 = **Security & hardening:** * Fix: the legacy AJAX endpoints (opt-in details, template loader) now require the `manage_options` capability — not just a nonce — and the privileged nonce is no longer emitted on every wp-admin page. * Fix: the consent CSV export neutralises spreadsheet formula injection (values beginning with `=` `+` `-` `@`). * Fix: the visitor IP is resolved from `REMOTE_ADDR` and only trusts `X-Forwarded-For` from proxies you configure via the new `f12_doi_trusted_proxies` filter — this prevents spoofing the opt-in rate limiter and the stored GDPR consent IP. * Fix: the form-URL email placeholder is URL-escaped. * Maintenance: removed leftover debug logging, added ABSPATH guards to directly-reachable files, and corrected a text domain (wordpress.org compliance). = 5.1.4 = * Maintenance: internal refactor and packaging cleanup. (Automatic updates for the paid Pro bundle and addon plugins are handled by the Pro bundle plugin, not the free Core plugin.) = 5.1.2 = **Email Template Editor fixes:** * Fix: Centered (and right-aligned) text now keeps its alignment in the actual and test emails. The generator wraps text and footer content in a `
`, so alignment survives multi-line rich-text content (a block-level tag inside a `
` is invalid HTML and email clients dropped the alignment).
* Fix: The builder no longer shows two "Save" buttons — removed a deprecated legacy editor-bundle enqueue that could mount the editor twice, and hardened the mount path against double-mounting.
* New: The Social Icons block now has an editable settings panel (network + URL per icon, add/remove, plus icon size, spacing, alignment, and padding).
= 5.1.1 =
* Improved: Admin menu label renamed from "DOI Admin" to "Double Opt-In".
* Improved: Bundle-only licensing polish on the Addons page — a single "Upgrade to Pro" bundle CTA replaces per-addon purchase links, and gated pages no longer show per-module "license required" states (one key unlocks every included module).
* Maintenance: Excluded a stray TypeScript build-cache file from the distributed plugin.
* Maintenance: WordPress compatibility updated to 7.0 ("Tested up to").
= 5.1.0 =
**Form Completeness Gate:**
* New: Per-form completeness check — a form must have all its required fields (recipient field, subject, body, sender address) before Double Opt-In can be enabled. Half-configured forms are now caught at save time and at the toggle endpoint instead of silently dropping opt-ins at runtime.
* New: `getMissingRequiredFields()` on the form-settings model returns the list of unconfigured fields and powers the page-level banner + master-toggle lock in the admin UI.
* New: Forms-list "Incomplete" badge + disabled toggle for incomplete forms, with parity between the React UI and the REST gate.
* New: Live auto-disable on required-field clear — clearing the recipient field (or any other required input) instantly disables the form in the UI and removes the runtime hook, without waiting for a page reload.
* New: One-shot upgrade migration that audits every stored form on `admin_init` and disables any that fail the completeness check. Idempotent, runs once per site.
* New: REST `save` and `toggle` endpoints reject any payload that would leave a form incomplete-but-enabled, with a structured error code the React UI surfaces inline.
**File Lifecycle (GDPR data minimization):**
* New: `f12_doi_optin_pre_delete` cascade hook fires before an opt-in is removed, allowing addons to delete their own per-submission artefacts (uploaded files, third-party form-plugin entries).
* New: `FileStorage` service + template-method base for file hand-off — CF7, Avada, Elementor, Gravity Forms, and WPForms now all delete uploaded files when the parent opt-in is deleted or expires.
* New: CF7 post-mail file-cleanup hook removes attachments from the temporary store as soon as the confirmation mail leaves the system.
* New: Reset-feature integration with the file-lifecycle so a manual reset cleans up attached files alongside the opt-in row.
* Improved: WP_DEBUG-gated reset-confirmation endpoint + admin button for developer-only re-testing of the confirmation pipeline.
**Form Settings UX:**
* New: Page-level completeness banner with a sticky warning marker until all required fields are filled.
* Improved: General tab — relabelled fields, clearer helper text, required-field markers, page-section descriptions.
* Improved: Email tab — relabelled fields, clearer helper text, required markers, recipient-field stale-flag (recipient was set but the field no longer exists on the form).
* Improved: Mapping tab — expanded description with auto-detect hint, surfacing the symmetric `f12_doi_settings_dto_from_array` / `f12_doi_settings_dto_sanitize` filter pair so addons can round-trip arbitrary keys cleanly.
* Improved: Forms-tabs polish + addon-settings routing — `/addon-settings/