PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.1.5
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.1.5
5.6.2 5.6.3 5.6.1 5.6.0 5.5.0 5.4.0 5.3.2 5.3.1 5.1.6 5.1.5 trunk 2.1.5 2.11 2.12 2.13 2.15 3.0.0 3.0.1 3.0.2 3.0.3 3.0.5 3.0.51 3.0.60 3.0.61 3.0.62 All 38 releases
double-opt-in / Readme.txt

Readme.txt in Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification 5.1.5, at Readme.txt

689 lines 44.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 === Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification ===
2 Contributors: forge12
3 Donate link: https://www.paypal.com/donate?hosted_button_id=MGZTVZH3L5L2G
4 Tags: contact form 7, double opt-in, gdpr, email verification
5 Requires at least: 6.0
6 Tested up to: 7.0
7 Requires PHP: 7.4
8 Stable tag: 5.1.5
9 License: GPLv3
10 License URI: http://www.gnu.org/licenses/gpl-3.0.html
11
12 **Protect your Contact Form 7 forms with GDPR-compliant Double Opt-In.**
13 Ensure valid emails, prevent fake signups, and stay compliant. Extend with paid addons for Avada, Elementor, Gravity Forms, WPForms and more.
14
15 == Description ==
16
17 **Double Opt-In** adds a mandatory email verification step to your Contact Form 7 forms.
18 When a visitor submits your form, the original mail is **not** sent immediately. Instead, the plugin:
19
20 1. Stores the submission in a secure database table.
21 2. Sends a confirmation email with a unique, time-limited link.
22 3. Only after the visitor clicks that link is the original form mail delivered.
23
24 This ensures:
25
26 * Only **valid, verified email addresses** reach your inbox.
27 * **GDPR / DSGVO requirements** are met with proper consent tracking, IP logging, and data retention.
28 * Your database stays **clean and reliable** -- no fake or mistyped addresses.
29
30 Out-of-the-box support for **Contact Form 7**. Additional form systems — Avada, Elementor, Gravity Forms, WPForms — are available as separate addon plugins.
31
32 = How It Works =
33
34 1. A visitor fills out your Contact Form 7 form and clicks submit.
35 2. The plugin intercepts the submission, stores the form data, and generates a unique hash.
36 3. A confirmation email is sent to the visitor's email address containing a verification link.
37 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).
38 5. The confirmed opt-in is logged in the admin dashboard with timestamps and IP addresses for full GDPR compliance.
39
40 = Quick Start =
41
42 [Read the Quick Guide](https://www.forge12.com/blog/so-verwendest-du-das-double-opt-in-fuer-contact-form-7/)
43
44 = Free Features =
45
46 * **Visual Email Editor** -- drag & drop block-based email template editor with live preview and mobile preview
47 * **Double Opt-In for Contact Form 7** -- per-form activation with full CF7 integration
48 * **Centralized Form Settings** -- manage all form integrations from a single admin panel
49 * **Email Template Presets** -- choose from pre-built templates or create your own
50 * **Send Test Email** -- preview your confirmation emails before going live
51 * **Custom Confirmation Pages** -- redirect users to a specific page after confirmation
52 * **Dynamic Conditions** -- enable opt-in based on user input (e.g. only when a checkbox is checked)
53 * **Resend Confirmation** -- resend the confirmation email from the admin dashboard
54 * **Delete Confirmation Modal** -- safety dialog before deleting an opt-in record to prevent accidental deletion
55 * **GDPR Consent Export** -- export individual consent records as JSON or CSV directly from the opt-in detail view
56 * **CAPTCHA Compatibility** -- automatically bypasses Forge12 Captcha, Google reCAPTCHA, and hCaptcha during opt-in confirmation to ensure mail delivery
57 * **Rate Limiting** -- configurable IP and email rate limits to prevent abuse
58 * **Error Redirect Page** -- redirect users to a custom page when an opt-in error occurs (rate limit, invalid email)
59 * **Token Expiry** -- confirmation links expire after a configurable time period
60 * **GDPR Data Storage** -- tracks Form ID, Email, Registration/Confirmation Date & IP, Consent Text
61 * **GDPR Anonymization** -- anonymize personal data instead of deleting it
62 * **WordPress Privacy Tools** -- integrates with WordPress personal data export and erasure requests
63 * **Automatic Cleanup** -- configurable auto-deletion of confirmed and unconfirmed entries
64 * **Category System** -- organize opt-ins into categories for better management
65 * **Pagination & Search** -- search and filter opt-in records in the admin dashboard
66 * **Admin Tooltips** -- contextual help tooltips throughout the admin interface
67 * **WordPress Multisite** -- network-wide activation creates tables on all sites automatically
68 * **Developer Hooks** -- 18 action hooks, 23 filters, and 11 typed events for full extensibility
69
70 = Pro Features =
71
72 Unlock the full potential of Double Opt-In with the [Pro version](https://www.forge12.com):
73
74 **Additional Form Integrations:**
75
76 * **Double Opt-In for Elementor Forms** -- seamless integration with Elementor's form widget
77 * **Double Opt-In for WPForms** -- full support for WPForms submissions
78 * **Double Opt-In for Gravity Forms** -- complete Gravity Forms integration
79
80 **Email Validation & Spam Protection:**
81
82 * **Unique Email Validation** -- prevent duplicate submissions per email address (block, silent, or redirect mode)
83 * **MX Validation** -- verify that the email domain has a valid mail server before sending
84 * **Domain Blocklist** -- block disposable and temporary email domains
85
86 **Email & Communication:**
87
88 * **Double Opt-Out System** -- unique opt-out links per submission with confirmation emails
89 * **Opt-In Reminder System** -- automatic reminders for unconfirmed opt-ins via cron
90 * **Conditional Email Templates** -- dynamic content blocks based on form data
91 * **Multi-Column Layouts** -- 2-column, 3-column, and sidebar layouts in the email editor
92 * **Image & Social Blocks** -- add images and social media icons to your emails
93
94 **Analytics & Export:**
95
96 * **Analytics Dashboard** -- charts and statistics for opt-in/opt-out rates
97 * **CSV Export** -- export all opt-in records for external processing
98
99 **User Management:**
100
101 * **Auto User Creation** -- automatically create WordPress users after opt-in confirmation with configurable role assignment
102
103 **Support:**
104
105 * **Premium Support** -- priority email support
106
107 == Installation ==
108
109 = Automatic Installation =
110
111 1. Go to **Plugins > Add New** in your WordPress admin.
112 2. Search for **"Double Opt-In"**.
113 3. Click **Install Now** and then **Activate**.
114
115 = Manual Installation =
116
117 1. Download the plugin ZIP file.
118 2. Upload it to `/wp-content/plugins/double-opt-in/` or use **Plugins > Add New > Upload Plugin**.
119 3. Activate via the WordPress **Plugins** menu.
120
121 = First-Time Setup =
122
123 1. After activation, go to **Double Opt-In** in the WordPress admin menu.
124 2. Navigate to **Forms** to see all detected Contact Form 7 forms.
125 3. Click on a form to enable Double Opt-In and configure the confirmation email.
126 4. Set the **Recipient Field** to the form field that contains the visitor's email address (e.g. `your-email`).
127 5. Customize the **Subject** and **Body** of the confirmation email, or choose a template preset.
128 6. Save the settings and test the form.
129
130 = Requirements =
131
132 * WordPress 6.0 or higher
133 * PHP 7.4 or higher
134 * Contact Form 7 5.0+ (for the CF7 integration bundled with Core)
135
136 == Frequently Asked Questions ==
137
138 = How does Double Opt-In work? =
139
140 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.
141
142 = Is this plugin GDPR / DSGVO compliant? =
143
144 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.
145
146 = Which form plugins are supported? =
147
148 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).
149
150 = I used Avada with this plugin before. What happens now? =
151
152 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.
153
154 = Can I customize the confirmation email? =
155
156 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.
157
158 = What happens if the user does not confirm? =
159
160 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.
161
162 = Can I redirect the user to a specific page after confirmation? =
163
164 Yes. In the per-form settings, you can select a **Confirmation Page**. The user will be redirected there after clicking the confirmation link.
165
166 = Does the plugin work with CAPTCHA plugins? =
167
168 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.
169
170 = Can I enable Double Opt-In only when a checkbox is checked? =
171
172 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.
173
174 = How do I access form data after confirmation? =
175
176 **Legacy approach (WordPress hook):**
177
178 `add_action( 'f12_cf7_doubleoptin_after_confirm', function( $hash, $optIn ) {`
179 ` $data = maybe_unserialize( $optIn->get_content() );`
180 `}, 10, 2 );`
181
182 **Modern approach (typed event, since 4.0):**
183
184 Use `OptInConfirmedEvent` via the EventDispatcher. The event provides `getFormData()`, `getEmail()`, `getFormId()`, and more. See `docs/hooks-and-events.md` for the complete reference.
185
186 = Does it work with WordPress Multisite? =
187
188 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.
189
190 = Can I use this without Contact Form 7 or Avada? =
191
192 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.
193
194 = Where can I find the developer documentation? =
195
196 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.
197
198 = How do I report a bug or request a feature? =
199
200 Please visit [forge12.com](https://www.forge12.com) or contact us via the WordPress support forum.
201
202 == Screenshots ==
203
204 1. **Opt-In Dashboard** -- Overview of all opt-in records with status, email, form, date, and actions.
205 2. **Form Settings** -- Per-form configuration with sender, subject, recipient field, confirmation page, and conditions.
206 3. **Email Template Editor** -- Visual drag & drop editor with blocks, live preview, and mobile preview.
207 4. **Template Presets** -- Choose from pre-built email template designs.
208 5. **Single Opt-In View** -- Detailed view of an opt-in record with form data, timestamps, and IP addresses.
209 6. **Global Settings** -- Configure data retention, token expiry, telemetry, and opt-out settings.
210 7. **Category Management** -- Organize opt-in records into categories.
211
212 == Privacy & Telemetry ==
213
214 Starting with version **3.1.0**, the Double Opt-In plugin includes **optional anonymous telemetry** (opt-out).
215 This helps us understand which features are used most, so we can improve usability and remove unused complexity.
216
217 **We never sell or share data.**
218 Telemetry is used **only for product improvement and maintenance**.
219
220 = Telemetry data collected =
221
222 * `plugin_slug`, `plugin_version`
223 * `snapshot_date`
224 * `settings_json` (anonymized plugin settings)
225 * `features_json` (enabled features)
226 * `created_at`, `first_seen`, `last_seen`
227 * `counters_json` (opt-in/opt-out event counts)
228 * `wp_version`, `php_version`, `locale`
229
230 = GDPR / DSGVO Compliance =
231
232 * No personal data, no cookies, no user tracking.
233 * Legal basis: *Art. 6 Abs. 1 lit. f DSGVO* (legitimate interest -- plugin optimization).
234 * Telemetry is fully optional and can be disabled anytime in **Double Opt-In > Settings**.
235
236 == Upgrade Notice ==
237
238 = 5.1.5 =
239 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.
240
241 = 5.1.4 =
242 Maintenance release — safe to update, no schema changes.
243
244 = 5.1.2 =
245 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.
246
247 = 5.1.1 =
248 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.
249
250 = 5.1.0 =
251 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.
252
253 = 5.0.0 =
254 **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).
255
256 = 3.7.2 =
257 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.
258
259 = 3.7.1 =
260 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.
261
262 = 3.7.0 =
263 CSS fix for table width on admin pages. Improved compatibility with Pro 3.7.0 license system. Safe to update.
264
265 = 3.6.0 =
266 Moved consent export to Pro plugin. The export UI and AJAX endpoint are no longer available without the Pro plugin.
267 Added `f12_doi_database_page_after_forms` hook for extensibility. Safe to update.
268
269 = 3.5.0 =
270 Fixed confirmation mail not being sent for forms with Quiz, Acceptance, or required fields.
271 CF7 validation is now bypassed during opt-in confirmation mail delivery. Safe to update.
272
273 = 3.4.0 =
274 Fixed translation loading issues on WordPress 6.7+, review notice not displaying, and database table missing errors.
275 Added 133+ missing German translations for the Email Editor and related features. Safe to update.
276
277 = 3.3.0 =
278 New: Delete confirmation modal, GDPR consent export (JSON/CSV), admin tooltips, error redirect page, hCaptcha compatibility.
279 New: Unique Email redirect behavior (Pro).
280 Fixed reCAPTCHA re-activation typo. Safe to update -- no database changes.
281
282 = 3.2.3 =
283 Bugfix release: Fixes broken toggle switches on the settings page. Safe to update -- no database changes.
284
285 = 3.2.2 =
286 Bugfix release: Fixes double-firing of the after_confirm hook. Safe to update -- no database changes.
287
288 = 3.2.1 =
289 Bugfix release: Fixes a fatal error on new, unsaved CF7 forms. Safe to update.
290
291 = 3.2.0 =
292 **Important: Major Update -- Please backup before updating!**
293 This version includes significant changes to the form management system, email templates, and database structure.
294 We strongly recommend creating a full site backup before updating.
295 New features: Visual email editor, centralized form settings, GDPR anonymization, and more.
296
297 = 3.1.0 =
298 Adds optional anonymous telemetry (opt-out). No breaking changes.
299
300 == Changelog ==
301
302 = 5.1.5 =
303
304 **Security & hardening:**
305
306 * 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.
307 * Fix: the consent CSV export neutralises spreadsheet formula injection (values beginning with `=` `+` `-` `@`).
308 * 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.
309 * Fix: the form-URL email placeholder is URL-escaped.
310 * Maintenance: removed leftover debug logging, added ABSPATH guards to directly-reachable files, and corrected a text domain (wordpress.org compliance).
311
312 = 5.1.4 =
313
314 * 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.)
315
316 = 5.1.2 =
317
318 **Email Template Editor fixes:**
319
320 * 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 `<div>` instead of a `<p>`, so alignment survives multi-line rich-text content (a block-level tag inside a `<p>` is invalid HTML and email clients dropped the alignment).
321 * 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.
322 * New: The Social Icons block now has an editable settings panel (network + URL per icon, add/remove, plus icon size, spacing, alignment, and padding).
323
324 = 5.1.1 =
325
326 * Improved: Admin menu label renamed from "DOI Admin" to "Double Opt-In".
327 * 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).
328 * Maintenance: Excluded a stray TypeScript build-cache file from the distributed plugin.
329 * Maintenance: WordPress compatibility updated to 7.0 ("Tested up to").
330
331 = 5.1.0 =
332
333 **Form Completeness Gate:**
334
335 * 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.
336 * 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.
337 * New: Forms-list "Incomplete" badge + disabled toggle for incomplete forms, with parity between the React UI and the REST gate.
338 * 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.
339 * 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.
340 * 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.
341
342 **File Lifecycle (GDPR data minimization):**
343
344 * 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).
345 * 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.
346 * New: CF7 post-mail file-cleanup hook removes attachments from the temporary store as soon as the confirmation mail leaves the system.
347 * New: Reset-feature integration with the file-lifecycle so a manual reset cleans up attached files alongside the opt-in row.
348 * Improved: WP_DEBUG-gated reset-confirmation endpoint + admin button for developer-only re-testing of the confirmation pipeline.
349
350 **Form Settings UX:**
351
352 * New: Page-level completeness banner with a sticky warning marker until all required fields are filled.
353 * Improved: General tab — relabelled fields, clearer helper text, required-field markers, page-section descriptions.
354 * 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).
355 * 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.
356 * Improved: Forms-tabs polish + addon-settings routing — `/addon-settings/<id>` is now the canonical mount point for Pro and free addons.
357 * Fix: Removed the fake header Save button that lied to users — only the per-tab Save action persists settings.
358 * Fix: AdminLayout no longer reverts `enabled=true` when a save is rejected by the completeness gate; the gate marker stays sticky until the user fixes the underlying problem.
359
360 **Addon Platform:**
361
362 * New: Marketplace AddonsPage with state-aware CTAs (install / activate / a single "Upgrade to Pro" bundle CTA), plus a registry-driven Features Overview card on the dashboard.
363 * New: Per-addon feature toggle pages, decoupled from plugin activation — a feature can be installed but disabled without uninstalling.
364 * New: Addons self-contribute sidebar entries via a manifest, replacing the old hard-coded core sidebar.
365 * New: Per-addon mount points on Form Settings → Pro Features (e.g. unique-email, conditional, user-registration self-render their per-form panel).
366 * Improved: Bundle-only licensing — every paid module is unlocked by the one Pro bundle license, so ProGate/AddonGate show either install guidance or a single bundle-activation CTA, with no per-addon purchase links or per-module license states.
367
368 **REST API:**
369
370 * New: `POST /f12-doi/v1/optout/page/generate` — idempotently creates the Opt-Out landing page with both shortcodes, returns 409 with an edit link on title collision.
371 * New: `GET /f12-doi/v1/consent-export/history?limit=N` — recent audit-runs for the consent-export "Recent exports" card.
372 * New: `GET /f12-doi/v1/consent-export/stream-info` — live hint-counts: how many records this stream has exported already and when the last run was.
373 * New: `DELETE /f12-doi/v1/consent-export/history` and `DELETE /f12-doi/v1/consent-export/history/<id>` — bulk and per-run audit deletion.
374
375 **Bug Fixes:**
376
377 * Fix: `consent_text` snapshot was lost when the runtime migrated from the legacy `OptInFrontend` to `AbstractFormIntegration`. Opt-in records since then displayed "Not recorded" instead of the actual configured consent text. The new `buildOptInProperties()` shared base now captures it (and the new `consent_field`) on every opt-in.
378 * Fix: Legacy frontend `FormData` proxy now resolves `getFormType` correctly so consent-field plumbing works on CF7.
379 * Fix: Elementor `form_fields` are unwrapped in placeholder substitution — `[doi_email]` and friends now resolve in confirmation mails on Elementor forms regardless of nesting.
380 * Fix: Confirmation and error redirect pages now resolve at runtime via the page-resolver, not at save time, so renames stay in sync.
381 * Fix: `href="#"` in legacy templates no longer breaks the confirmation link in inline-styled mails.
382 * Fix: Table doesn't exist error for `f12_cf7_doubleoptin_categories` on manual file upload — both custom tables now verify on every update cycle.
383
384 **Architecture:**
385
386 * New: Migration registry — addons register schema migrations; Core applies pending ones on `admin_init`.
387 * New: Symmetric `f12_doi_settings_dto_from_array` / `f12_doi_settings_dto_sanitize` filter pair — addons can round-trip arbitrary keys through the form settings DTO without monkey-patching the model.
388 * Improved: Plugin is now part of a monorepo; build pipeline produces one ZIP per package; PHPUnit gate aborts the build on red tests.
389 * Improved: Test count Core: 655 → 1712 unit tests across the monorepo, all green.
390
391 = 5.0.0 =
392
393 **Breaking change: plugin family restructured into Core + paid addons.**
394
395 * Breaking: Avada Forms integration removed from Core. Available as a separate paid addon (`double-opt-in-avada`). Existing free-plugin users with DOI configured on Avada forms get a free permanent grandfather license via a one-click claim button in admin.
396 * Breaking: PHP minimum lowered from 8.0 to 7.4 to align with WordPress's supported PHP versions.
397 * New: Addon API stabilised and covered by semver (`F12_DOI_CORE_API_VERSION` = 4.3.0). See `docs/addon-api.md`.
398 * New: Addon license registry (`AddonLicenseRegistryInterface`) — license providers (Pro bundle, standalone keys) grant entitlements; addons check `isLicensed()`.
399 * New: Migration registry (`MigrationRegistry`) — addons register schema migrations; Core applies pending ones on admin_init.
400 * New: GDPR Art. 7 consent-acceptance evidence chain. Form Settings → General now exposes a "Consent acceptance field" dropdown; on every opt-in Core captures the consent text, the acceptance field name, and the user-acknowledged value. The opt-in detail view shows a proper Consent Evidence card.
401 * New: Form Settings → Pro Features tab is now contributed by addons (unique-email, conditional, user-registration, …) via the `forms.pro-features` mount point. With no Pro addon active, the tab disappears entirely instead of rendering an empty panel.
402 * New: Dashboard widgets are now contributed via the `dashboard.widget` mount point. With the Analytics addon active, Top Forms / Activity / Conversion Rate cards appear; without it, the base dashboard shows totals + recent opt-ins only.
403 * Fix: `fieldMapping` save bug — placeholder mappings configured under Form Settings → Mapping were captured by the React form but silently dropped before persistence. Now correctly round-trips through the new symmetric `f12_doi_settings_dto_from_array` / `f12_doi_settings_dto_sanitize` filter pair.
404 * Fix: `consent_text` snapshot was lost when the runtime migrated from the legacy `OptInFrontend` to `AbstractFormIntegration` — opt-in records since then displayed "Not recorded" instead of the actual configured consent text. The new `buildOptInProperties()` shared base captures it (and the new `consent_field`) on every opt-in.
405 * Fix: Tailwind utilities now reliably beat WP-admin's unlayered tag-level CSS inside the SPA (added `important: '#doi-admin-root'` config + Radix Portal container so popovers stay styled).
406 * Improved: All public interfaces tagged `@api`; implementation details tagged `@internal`. Deprecation policy: 1 minor release of warning before removal.
407 * Improved: Plugin is now part of a monorepo; build pipeline produces one ZIP per package.
408 * Improved: PHPUnit gate in the build pipeline — red unit tests now abort the build. Total Core test count: 466 → 655 (+189).
409
410 = 3.7.2 =
411
412 **Bug Fixes:**
413
414 * Fix: Fixed all form field placeholders (`[doi_email]`, `[doi_name]`, `[doi_phone]`, etc.) not being replaced in confirmation emails for Avada forms. The Avada integration stores opt-in content in a nested structure (`{data: {...}, field_labels: {...}}`), but the placeholder replacement expected a flat field array. The nested `data` key is now extracted correctly before replacement.
415 * Fix: Applied the same nested content handling to the legacy `OptInFrontend::addPlaceholders()` code path.
416 * Fix: Added missing `[doubleoptin_privacy_url]` system placeholder to the new `AbstractFormIntegration::addSystemPlaceholders()` method. This placeholder was available in the legacy code but was not ported to the 4.0.0 integration architecture, causing it to appear unreplaced in emails.
417 * Fix: Fixed the admin opt-in detail view displaying Avada metadata keys (`data`, `field_labels`, `field_types`, etc.) instead of actual form field values. The nested content structure is now unwrapped before rendering.
418 * Fix: Fixed the AJAX opt-in detail modal showing the same incorrect metadata for Avada opt-ins.
419
420 = 3.7.1 =
421
422 **Bug Fixes:**
423
424 * Fix: Fixed the toggle switch in the admin form list showing an incorrect state for forms with custom conditions. The `getForms()` method used the runtime `isOptInEnabled()` check (which evaluates `$_GET['optin']` and `$_POST` condition fields) instead of reading the stored database value. This caused the toggle to display as "off" even when DOI was enabled, and clicking "enable" would actually disable it.
425 * Fix: Fixed Avada forms ignoring Double Opt-In settings entirely. The conditions check in `isOptInEnabled()` looked for form field values in `$_POST[$condition]`, but Avada sends form data inside `$_POST['formData']` as a URL-encoded string. The `AvadaIntegration` now overrides `isOptInEnabled()` to parse Avada's POST format correctly.
426 * Fix: Fixed Avada forms showing "No valid email address was found" error on submission. `AvadaIntegration::resolveRecipient()` did not strip square brackets from the recipient field name (e.g. `[email]` → `email`), so the field was never matched in the form data. Now uses the same bracket-stripping logic as `CF7Integration`.
427
428 **Announcements:**
429
430 * Notice: Starting with version 3.8.0, Avada Forms integration will move to the Pro version. Contact Form 7 support remains free.
431 * New: Dismissible admin notice for sites with active Avada/Fusion Builder, informing about the upcoming change.
432 * New: Yellow info banner on the Forms management page in the Avada section.
433 * New: Plugin update message warning when Avada is active.
434
435 = 3.7.0 =
436
437 **Bug Fixes:**
438
439 * Fix: Fixed `.doi-table` not using full width on admin pages due to conflicting CSS rules. Table width now uses `!important` to ensure consistent layout.
440
441 **Compatibility:**
442
443 * Updated: Full compatibility with Pro version 3.7.0 and its new license management system.
444
445 = 3.6.0 =
446
447 **Architecture:**
448
449 * Moved: Consent export (CSV/JSON) is now a Pro-only feature. The `ConsentExportController` and `ConsentExportService` have been removed from the free plugin and moved to the Pro plugin.
450 * Security: The `doi_export_consent` AJAX endpoint is no longer registered in the free plugin, preventing unauthorized access without a Pro license.
451 * New: Added `f12_doi_database_page_after_forms` action hook on the Database admin page, allowing extensions to render additional UI after the built-in database management forms.
452
453 = 3.5.0 =
454
455 **Bug Fixes:**
456
457 * Fix: Fixed confirmation mail not being sent after opt-in verification for forms using Quiz fields (`[quiz]`), Acceptance checkboxes (`[acceptance]`), or other validated field types. CF7 re-ran all form validations when creating a `WPCF7_Submission` instance during confirmation, which failed because quiz answers and checkbox states are not available in a GET request context. Validation is now bypassed during confirmation mail delivery.
458 * Fix: Applied the same validation bypass to the legacy `CF7Frontend::sendDefaultMail()` code path, which had the same issue.
459
460 **Improvements:**
461
462 * Improved: `beforeSendConfirmationMail()` now disables CF7 field validation (`wpcf7_validate`), spam detection (`wpcf7_spam`), and spam check (`wpcf7_skip_spam_check`) in addition to the existing CAPTCHA bypasses. All filters are properly restored in `afterSendConfirmationMail()`.
463
464 = 3.4.0 =
465
466 **Bug Fixes:**
467
468 * Fix: Fixed translation loading too early warning on WordPress 6.7+ (`_load_textdomain_just_in_time` notice).
469 * Fix: Fixed review notice never displaying due to namespace resolution issue.
470 * Fix: Fixed Free and Pro plugin constant/function redeclaration conflicts when both plugins are active simultaneously.
471 * Fix: Fixed TestEmailBlocker fatal error in distribution builds where test dependencies are not included.
472 * Fix: Fixed Pro upgrade prompt ("Pro Feature", "The '{block}' block requires the Pro version.") displaying in English instead of the active language.
473 * Fix: Fixed database "table doesn't exist" error for `f12_cf7_doubleoptin_categories` when plugin files are uploaded manually or the database is restored without custom tables.
474
475 **Improvements:**
476
477 * Improved: Added 133+ missing German translations covering the Email Editor, Placeholder Mapping, Email Template Post Type, Email Presets, and Pro upgrade prompts.
478 * Improved: Added formal German (Sie) translations for all new strings.
479 * Improved: Database table existence safety net -- both custom tables are now verified and recreated on every update cycle, independent of the activation hook.
480 * Improved: Updated "Upgrade to Pro" links to point to the correct product page.
481
482 = 3.3.0 =
483
484 **New Features:**
485
486 * New: Delete confirmation modal -- clicking "Delete DOI" now opens a confirmation dialog to prevent accidental deletion. Dismissible via Cancel, overlay click, or Escape key.
487 * New: GDPR consent record export -- export individual opt-in records as JSON or CSV directly from the opt-in detail view.
488 * New: Admin tooltips -- contextual help tooltips with descriptions throughout the admin interface.
489 * New: Error redirect page -- configure a per-form redirect page for opt-in errors (rate limit, invalid email, etc.).
490 * New: hCaptcha compatibility -- hCaptcha validation is now automatically bypassed during opt-in confirmation mail delivery, alongside Forge12 Captcha and Google reCAPTCHA.
491
492 **New Features (Pro):**
493
494 * New: Unique Email – Redirect behavior. When a duplicate email is detected, users can now be redirected to a configurable WordPress page instead of just seeing an error or silent rejection.
495 * New: Dedicated Redirect Page selector in the Unique Email settings (per-form). Only visible when behavior is set to "Redirect to page".
496 * New: `UNIQUE_EMAIL_DUPLICATE` error code for distinguishing duplicate email rejections from other validation errors (e.g. MX check).
497
498 **Bug Fixes:**
499
500 * Fix: Success and error messages (e.g. "Opt-In deleted") are now rendered as styled alerts instead of plain text.
501 * Fix: Fixed a typo in `OptInFrontend::afterSendDefaultMail()` that prevented Google reCAPTCHA from being re-enabled after opt-in confirmation mail delivery (`wpcf7_recaptcha_verifiy_response` → `wpcf7_recaptcha_verify_response`).
502 * Fix: Silent mode for Unique Email no longer shows the raw string `unique_email_rejected` in the toast notification. It now displays a properly translated message.
503 * Fix: CF7 no longer sends its default success mail when a duplicate email is detected. The original mail is now correctly blocked via `wpcf7_skip_mail`.
504 * Fix: CF7 now shows an inline error message (instead of the success message) when Unique Email rejects a submission in block or redirect mode.
505 * Fix: WPForms and Gravity Forms no longer display a contradictory success confirmation when a validation error occurs. The confirmation message is automatically hidden and replaced by the error toast or redirect.
506 * Fix: Elementor Forms now correctly validate unique emails. The `f12_cf7_doubleoptin_validate_recipient` filter was not called in the legacy `OptInFrontend::maybeCreateOptIn()` path used by Elementor, so duplicate emails were never detected.
507 * Fix: Elementor success messages are now hidden when a validation error (block/redirect) occurs, preventing contradictory success and error messages.
508 * Fix: Error notification AJAX polling no longer loops infinitely. The internal `doi_check_submission_error` request was intercepted by its own XHR hook, causing a continuous polling cycle every ~800ms.
509
510 **Improvements:**
511
512 * Improved: Updated translations (German, German formal, French, English).
513 * Improved: CAPTCHA bypass now covers Forge12 Captcha, Google reCAPTCHA, and hCaptcha across all three bypass layers (SpamMechanics, AbstractFormIntegration, OptInFrontend).
514 * Improved: ErrorNotification system now stores a `hide_confirmation` flag based on the validation error behavior (block/redirect vs. silent). The frontend uses this to hide form-plugin success messages when an error should be visible.
515 * Improved: Error handling in CF7 integration prevents mail sending for all rejection modes (block, silent, redirect).
516 * Improved: `OptInFrontend::maybeCreateOptIn()` now calls the `f12_cf7_doubleoptin_validate_recipient` filter, enabling MX validation, domain blocklist, and unique email checks for all legacy form integrations (Elementor).
517
518 **Testing:**
519
520 * New: Unit tests for SpamMechanics (10 tests) -- verifies CAPTCHA bypass for Forge12 Captcha, Google reCAPTCHA, and hCaptcha, including guard conditions (no hash, invalid hash, already confirmed).
521 * New: E2E tests for delete confirmation modal (7 tests) -- verifies modal open/close behavior (Cancel, overlay click, Escape), re-open, correct delete URL, and red button styling.
522
523 = 3.2.4 =
524
525 **New Features:**
526
527 * New: Universal Error Notification System – displays a toast notification to the user when an OptIn error occurs (rate limit, invalid email, etc.), independent of the form plugin used.
528 * New: Error Redirect Page – configure a per-form redirect page for OptIn errors. When set, users are redirected to the selected page instead of seeing a toast notification. The error code is appended as a query parameter (`?doi_error=rate_limit_ip`) for context-specific content.
529 * New: OptInError value object for typed, translatable error codes across all integrations.
530
531 **Improvements:**
532
533 * Improved: Error handling in all form integrations now uses the centralized OptInError and ErrorNotification system.
534 * Improved: Frontend error detection covers Contact Form 7, WPForms, Gravity Forms, Avada, Elementor, and generic AJAX/form submissions.
535
536 = 3.2.3 =
537
538 **Bug Fixes:**
539
540 * Fix: Fixed broken toggle switches on the settings page. Clicking the toggle button or its label text now correctly toggles the value.
541 * Fix: Removed stale `<label class="toggle-label">` elements that were rendered as duplicate toggle buttons due to WordPress admin CSS.
542 * Fix: Removed non-functional `<label class="overlay">` elements (leftover from an older CSS-only toggle pattern).
543 * Fix: Replaced incorrect `esc_attr_e()` with `echo esc_attr()` for HTML `for` attribute values in the telemetry toggle.
544
545 **Improvements:**
546
547 * Improved: The entire toggle row (button + description text) is now clickable, not just the small toggle button.
548 * Improved: Added CSS for `.f12-checkbox-toggle` for proper flex layout of toggle components.
549
550 = 3.2.2 =
551
552 **Bug Fixes:**
553
554 * Fix: Fixed double-firing of the `f12_cf7_doubleoptin_after_confirm` hook. The hook was triggered twice per confirmation (once by the EventDispatcher bridge and once manually). It now fires exactly once with the original `($hash, $optIn)` parameters.
555
556 **Developer Features:**
557
558 * New: Added `getFormData()` method to `OptInConfirmedEvent`, providing direct access to submitted form field data via the typed event system.
559 * New: Added `shouldBridgeToWordPress()` to the Event base class, allowing individual events to opt out of automatic WordPress hook bridging to prevent duplicate hook calls.
560 * New: Added comprehensive developer documentation (`docs/hooks-and-events.md`) with complete reference for all 18 action hooks, 23 filters, and 11 typed events.
561
562 **Improvements:**
563
564 * Improved: Updated hook usage hints in the admin panel with both legacy and event-based code examples.
565
566 = 3.2.1 =
567
568 **Bug Fixes:**
569
570 * Fix: Fixed a fatal error (TypeError) when opening the Double Opt-In panel on a new, unsaved Contact Form 7 form.
571
572 **Improvements:**
573
574 * Improved: Added a notice in the CF7 Double Opt-In tab prompting users to save the form before configuring Double Opt-In.
575
576 = 3.2.0 =
577
578 **Email Template Editor:**
579
580 * New: Visual drag & drop email template editor with block-based design.
581 * New: Pre-built email template presets (Blank, Dark Professional, Yellow Bold, Minimal Clean, Opt-Out Confirmation).
582 * New: Placeholder library with all available form fields and system variables.
583 * New: Opt-out email template support in the editor.
584 * New: Send test email functionality to preview emails before going live.
585 * New: Mobile preview mode to check responsive email design.
586 * New: Rich text editing with formatting options (bold, italic, links, lists).
587 * New: Block registry for extensible template components (Pro: multi-column, images, social icons).
588
589 **Form Management:**
590
591 * New: Centralized form settings management panel for all form integrations (CF7, Avada, Elementor).
592 * New: Resend confirmation email directly from the admin dashboard.
593 * New: Unified settings interface across all supported form plugins.
594 * New: Field mapping system for connecting form fields to email placeholders.
595
596 **WordPress & Multisite:**
597
598 * New: Full WordPress Multisite support -- network-wide activation creates database tables on all existing sites.
599 * New: Automatic table creation for new sites added to the network (via `wp_initialize_site` hook).
600
601 **GDPR & Security:**
602
603 * New: GDPR-compliant anonymization of personal data instead of deletion.
604 * New: Rate limiting for form submissions to prevent abuse (configurable per IP and per email).
605 * New: Consent text snapshot stored per opt-in record for audit trail.
606 * New: Consent export (CSV) for GDPR compliance.
607 * New: WordPress Privacy Tools integration (personal data export & erasure requests).
608 * New: Configurable token expiry settings (default: 48 hours).
609 * New: Configurable data retention settings for confirmed and unconfirmed entries.
610 * Security: Fixed potential XSS vulnerabilities in admin screens.
611 * Security: Improved input sanitization throughout the plugin.
612
613 **Architecture & Performance:**
614
615 * New: Event-driven architecture with 11 typed events for form submissions and opt-in lifecycle.
616 * New: Service container with dependency injection for improved extensibility.
617 * New: `WordPressHookBridge` for backward compatibility between legacy hooks and typed events.
618 * New: Form integration registry for pluggable form builder support.
619 * New: REST API for email template management (`/wp-json/f12-doi/v1/email-templates`).
620 * Improved: CSS extracted to external files for better caching.
621 * Improved: Code refactored to PSR-4 autoloading with modern PHP architecture.
622
623 **Bug Fixes & Improvements:**
624
625 * Fix: Fixed double mail sending issue on CF7 and Avada forms.
626 * Fix: Fixed email button URLs being incorrectly escaped when using placeholders.
627 * Improved: Refactored CF7 and Avada form integration architecture.
628 * Improved: Redesigned admin dashboard with dedicated opt-in management views.
629 * Improved: Updated translations (German).
630
631 = 3.1.1 =
632
633 * Improved: Enhanced compatibility with major CAPTCHA plugins to ensure smoother user verification.
634
635 = 3.1.0 =
636
637 * New: Added optional anonymous telemetry (opt-out) to improve plugin performance and usability.
638 * Privacy: Documented all telemetry fields collected.
639 * Improved: Minor optimizations for compatibility and maintainability.
640 * Change: Removed frontend support link injection for improved transparency and compliance with WordPress guidelines.
641 * Improved: Branding is now shown only in the plugin settings (admin area).
642
643 = 3.0.72 =
644
645 * Improved: Increased compatibility between Free and Pro version.
646 * Improved: Added support for Avada 7.12.2.
647
648 = 3.0.70 =
649
650 * Fixed: Fixed a bug stopping the CF7 forms to attach uploaded files after opt-in confirmation.
651
652 = 3.0.62 =
653
654 * New: Added hook `f12_cf7_doubleoptin_skip_option` to allow skipping opt-ins if required.
655
656 = 3.0.60 =
657
658 * Fix: Fixed a bug causing Elementor to stop sending opt-in mails.
659
660 = 3.0.51 =
661
662 * New: Avada Opt-In now leverages the Notification System for handling emails. The "Send to Email" action remains supported.
663
664 = 3.0.50 =
665
666 * New: Added Avada Forms integration.
667 * Improved: Reworked admin UI for better usability.
668
669 = 3.0.0 =
670
671 * New: Complete rewrite of the plugin core.
672 * New: Category system for organizing opt-in records.
673 * New: Improved admin dashboard with pagination and search.
674 * New: Custom confirmation page redirects.
675 * New: Dynamic conditions for enabling opt-in per form.
676 * Improved: Database schema with additional tracking fields.
677
678 = 2.0.0 =
679
680 * New: Support for custom email templates.
681 * New: IP address logging for registration and confirmation.
682 * Improved: Opt-in record management in the admin dashboard.
683
684 = 1.0.0 =
685
686 * Initial release.
687 * Double Opt-In for Contact Form 7.
688 * Basic confirmation email customization.
689