PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.8.1
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.8.1
5.8.0 5.8.1 5.7.0 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 All 41 releases
double-opt-in / readme.txt

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

906 lines 67.3 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, newsletter
5 Requires at least: 6.0
6 Tested up to: 7.1
7 Requires PHP: 7.4
8 Stable tag: 5.8.1
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 * The form mail **arrives after the confirmation** -- every action that runs after the click is recorded, and temporary failures are retried automatically.
30
31 Out-of-the-box support for **Contact Form 7**. Additional form systems — Avada, Elementor, Gravity Forms, WPForms — are available as separate addon plugins.
32
33 = How It Works =
34
35 1. A visitor fills out your Contact Form 7 form and clicks submit.
36 2. The plugin intercepts the submission, stores the form data, and generates a unique hash.
37 3. A confirmation email is sent to the visitor's email address containing a verification link.
38 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).
39 5. The confirmed opt-in is logged in the admin dashboard with timestamps and IP addresses for full GDPR compliance.
40
41 = Quick Start =
42
43 After activation, a setup wizard asks for the sender, the forms that should ask for confirmation, the confirmation email and the page visitors see after the click. Everything is prefilled; it takes about three minutes and can be skipped.
44
45 [Read the Quick Guide](https://www.forge12.com/blog/so-verwendest-du-das-double-opt-in-fuer-contact-form-7/)
46
47 = Free Features =
48
49 * **Double Opt-In for Contact Form 7** -- per-form activation with full CF7 integration
50 * **Centralized Form Settings** -- manage all form integrations from a single admin panel
51 * **Built-In Email Designs** -- write the confirmation email per form and pick one of three ready-made HTML layouts, or send it plain
52 * **Resend Confirmation** -- resend the confirmation email to a single recipient from the opt-in detail view
53 * **Custom Confirmation Pages** -- redirect users to a specific page after confirmation
54 * **Dynamic Conditions** -- enable opt-in based on user input (e.g. only when a checkbox is checked)
55 * **Delete Confirmation Modal** -- safety dialog before deleting an opt-in record to prevent accidental deletion
56 * **GDPR Consent Export** -- export individual consent records as JSON or CSV directly from the opt-in detail view
57 * **CAPTCHA Compatibility** -- automatically bypasses Forge12 Captcha, Google reCAPTCHA, and hCaptcha during opt-in confirmation to ensure mail delivery
58 * **Rate Limiting** -- configurable IP and email rate limits to prevent abuse
59 * **Error Redirect Page** -- redirect users to a custom page when an opt-in error occurs (rate limit, invalid email)
60 * **Token Expiry** -- confirmation links expire after a configurable time period
61 * **GDPR Data Storage** -- tracks Form ID, Email, Registration/Confirmation Date & IP, Consent Text
62 * **GDPR Anonymization** -- anonymize personal data instead of deleting it
63 * **WordPress Privacy Tools** -- integrates with WordPress personal data export and erasure requests
64 * **Automatic Cleanup** -- configurable auto-deletion of confirmed and unconfirmed entries
65 * **Category System** -- organize opt-ins into categories for better management
66 * **Pagination & Search** -- search and filter opt-in records in the admin dashboard
67 * **Admin Tooltips** -- contextual help tooltips throughout the admin interface
68 * **WordPress Multisite** -- network-wide activation creates tables on all sites automatically
69 * **Developer Hooks** -- 48 action hooks, 82 filters, and 13 typed events for full extensibility
70
71 = Pro Features =
72
73 Unlock the full potential of Double Opt-In with the [Pro version](https://www.forge12.com/shop/contact-form-7-double-opt-in?from=readme&utm_source=double-opt-in&utm_medium=plugin&utm_campaign=readme):
74
75 **Additional Form Integrations:**
76
77 * **Double Opt-In for Elementor Forms** -- seamless integration with Elementor's form widget
78 * **Double Opt-In for WPForms** -- full support for WPForms submissions
79 * **Double Opt-In for Gravity Forms** -- complete Gravity Forms integration
80
81 **Email Validation & Spam Protection:**
82
83 * **Unique Email Validation** -- prevent duplicate submissions per email address (block, silent, or redirect mode)
84 * **MX Validation** -- verify that the email domain has a valid mail server before sending
85 * **Domain Blocklist** -- block disposable and temporary email domains
86
87 **Email & Communication:**
88
89 * **Double Opt-Out System** -- unique opt-out links per submission with confirmation emails
90 * **Opt-In Reminder System** -- automatic reminders for unconfirmed opt-ins via cron
91 * **Visual Email Editor** -- drag & drop block editor with live preview and mobile preview, template presets, unlimited saved templates and test emails
92 * **Conditional Email Templates** -- dynamic content blocks based on form data
93 * **Multi-Column Layouts** -- 2-column, 3-column, and sidebar layouts in the email editor
94 * **Image & Social Blocks** -- add images and social media icons to your emails
95
96 **Analytics & Export:**
97
98 * **Analytics Dashboard** -- charts and statistics for opt-in/opt-out rates
99 * **CSV Export** -- export all opt-in records for external processing
100
101 **User Management:**
102
103 * **Auto User Creation** -- automatically create WordPress users after opt-in confirmation with configurable role assignment
104
105 **Support:**
106
107 * **Premium Support** -- priority email support
108
109 == Installation ==
110
111 = Automatic Installation =
112
113 1. Go to **Plugins > Add New** in your WordPress admin.
114 2. Search for **"Double Opt-In"**.
115 3. Click **Install Now** and then **Activate**.
116
117 = Manual Installation =
118
119 1. Download the plugin ZIP file.
120 2. Upload it to `/wp-content/plugins/double-opt-in/` or use **Plugins > Add New > Upload Plugin**.
121 3. Activate via the WordPress **Plugins** menu.
122
123 = First-Time Setup =
124
125 1. After activation, go to **Double Opt-In** in the WordPress admin menu.
126 2. Navigate to **Forms** to see all detected Contact Form 7 forms.
127 3. Click on a form to enable Double Opt-In and configure the confirmation email.
128 4. Set the **Recipient Field** to the form field that contains the visitor's email address (e.g. `your-email`).
129 5. Customize the **Subject** and **Body** of the confirmation email, or choose a template preset.
130 6. Save the settings and test the form.
131
132 = Requirements =
133
134 * WordPress 6.0 or higher
135 * PHP 7.4 or higher
136 * Contact Form 7 5.0+ (for the CF7 integration bundled with Core)
137
138 == Frequently Asked Questions ==
139
140 = How does Double Opt-In work? =
141
142 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.
143
144 = Is this plugin GDPR / DSGVO compliant? =
145
146 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.
147
148 = Is double opt-in mandatory? =
149
150 For newsletters and other advertising emails in Germany, practically yes: the sender has to prove consent, and without the confirmation click that proof rarely holds up. No law names the procedure, but courts and data protection authorities expect it. This is general information, not legal advice. [More on the legal situation](https://www.forge12.com/de/blog/double-opt-in-wordpress-pflicht-rechtslage)
151
152 = Do I need double opt-in for a contact form? =
153
154 A plain contact request does not need a consent checkbox or double opt-in. It becomes necessary as soon as the form also signs people up for a newsletter or other advertising. You can switch double opt-in on per form, or only when a checkbox is ticked (see "Conditions" below).
155
156 = What about Switzerland and Austria? =
157
158 Advertising emails need prior consent there as well, and the sender has to be able to prove it. Double opt-in is the common way to do that, even where the law does not name it.
159
160 = Which form plugins are supported? =
161
162 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).
163
164 = I used Avada with this plugin before. What happens now? =
165
166 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.
167
168 = Can I customize the confirmation email? =
169
170 Yes. In the free version you write subject and text of the confirmation email in each form's settings and choose one of three built-in designs or plain text. Placeholders like `[doubleoptinlink]`, `[doubleoptin_form_date]`, and form field values are replaced automatically. The Pro version adds a visual drag & drop editor with template presets, reusable templates and test emails.
171
172 = What happens if the user does not confirm? =
173
174 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.
175
176 = Can I redirect the user to a specific page after confirmation? =
177
178 Yes. In the per-form settings, you can select a **Confirmation Page**. The user will be redirected there after clicking the confirmation link.
179
180 = How do I show the right message on the confirmation and error pages? =
181
182 Put these shortcodes on the page:
183
184 * `[doi_confirmation_status]` says whether the link just confirmed the address, was already used, has expired or is invalid. Each text can be replaced, for example `[doi_confirmation_status confirmed="Welcome aboard!"]`.
185 * `[doi_field name="your-name"]` shows a value from the form, only right after the confirmation.
186 * `[doi_error_message]` on the error page explains why a sign-up was refused (too many attempts, address already signed up, and so on).
187
188 A page without `[doi_confirmation_status]` still shows a short notice when the link has expired or is invalid.
189
190 = Does the plugin work with CAPTCHA plugins? =
191
192 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.
193
194 = Can I enable Double Opt-In only when a checkbox is checked? =
195
196 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.
197
198 = How do I access form data after confirmation? =
199
200 **Legacy approach (WordPress hook):**
201
202 `add_action( 'f12_cf7_doubleoptin_after_confirm', function( $hash, $optIn ) {`
203 ` $data = maybe_unserialize( $optIn->get_content() );`
204 `}, 10, 2 );`
205
206 **Modern approach (typed event, since 4.0):**
207
208 Use `OptInConfirmedEvent` via the EventDispatcher. The event provides `getFormData()`, `getEmail()`, `getFormId()`, and more. See `docs/hooks-and-events.md` for the complete reference.
209
210 = Does it work with WordPress Multisite? =
211
212 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.
213
214 = Can I use this without Contact Form 7 or Avada? =
215
216 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.
217
218 = Where can I find the developer documentation? =
219
220 A hook, filter, and event reference with code examples ships at `docs/hooks-and-events.md` inside the plugin directory. It documents 29 of the 48 action hooks, 24 of the 82 filters, and all 13 typed events — the ones extensions actually reach for. The rest are discoverable in the source; if you need one documented, ask and we will add it.
221
222 = How do I report a bug or request a feature? =
223
224 Please visit [forge12.com](https://www.forge12.com) or contact us via the WordPress support forum.
225
226 == Screenshots ==
227
228 1. **Dashboard** -- Total, confirmed and pending opt-ins at a glance, with the latest submissions.
229 2. **Opt-In list** -- Every record with form, status and date; search and filter by status, follow-up actions and mail delivery.
230 3. **Opt-in detail** -- Timestamps and IP addresses of request and confirmation, the consent text, and whether the form mail ran after the click.
231 4. **Forms** -- All Contact Form 7 forms with Double Opt-In switched on or off per form.
232 5. **Form settings** -- Consent text, acceptance field, category, sender, subject and confirmation page for one form.
233 6. **Setup wizard** -- Sender, forms, confirmation email and the page after the click, in four steps.
234 7. **Settings** -- Data retention, privacy policy page, token expiry and rate limits.
235 8. **What the visitor sees** -- After submitting: where the confirmation mail comes from and what to do if it does not arrive.
236
237 == Privacy & Telemetry ==
238
239 **As of version 5.1.7 the plugin no longer transmits any telemetry.** The daily
240 job that used to send a usage snapshot has been removed, and an update also
241 removes it from the WP-Cron schedule of sites that already had it.
242
243 Usage counters (how many opt-ins were confirmed, for example) are still kept,
244 but they never leave the site. They are stored in a single WordPress option and
245 are used only in the admin -- for instance to decide whether the plugin has been
246 useful long enough to ask you for a review.
247
248 **We never sell or share data.**
249
250 = GDPR / DSGVO Compliance =
251
252 * No personal data, no cookies, no user tracking.
253 * Nothing is sent to any external server. Counters stay in your database and are
254 removed when you uninstall the plugin.
255 * The telemetry setting under **Double Opt-In > Settings** is retained: should
256 transmission ever be reintroduced, it will be honoured before anything is sent.
257
258 = Bundled assets =
259
260 The admin interface uses the Inter typeface. It is **self-hosted** -- the font
261 file is embedded in the plugin's own admin bundle, so opening the plugin's
262 screens does not contact Google Fonts or any other third party. Inter is
263 licensed under the SIL Open Font License 1.1 (see licenses/inter-OFL-1.1.txt).
264
265 == Upgrade Notice ==
266
267 = 5.8.1 =
268 Fixes German admin texts. No functional changes.
269
270 = 5.8.0 =
271 Shows whether each confirmation email reached your mail server and tells Contact Form 7 visitors to confirm their address. Pro users: update the email editor and opt-out add-ons too.
272
273 = 5.7.0 =
274 Adds a setup wizard for new installations. Existing forms and settings are not changed.
275
276 = 5.6.3 =
277 Ships the hook and Addon API reference the readme refers to. No functional changes.
278
279 = 5.6.2 =
280 Visitors who forget the consent checkbox now see why their sign-up was not accepted, instead of a success message. No settings or data change.
281
282 = 5.6.1 =
283 Fixes a blank or partly loaded Double Opt-In admin on sites with Avada and other themes or plugins that use the Underscore/Lodash library. No settings or data change.
284
285 = 5.6.0 =
286 Security release — update recommended. Also records every action that runs after the confirmation click, retries temporary failures automatically and shows the result on each opt-in. Adds one database table, created automatically on update. If you use the Elementor, Avada, WPForms or Gravity Forms add-ons, update them after this release.
287
288 = 5.5.0 =
289 Recommended if you ever ran a Double Opt-In Pro older than 4.0. Such a plugin, left installed next to the current modules, made WordPress fail with a critical error that also locked you out of the admin. This release keeps the site reachable in that situation and adds two Site Health checks that name the problem and repair it in one click. Nothing is deleted from your server, and no schema changes.
290
291 = 5.4.0 =
292 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.
293
294 = 5.3.1 =
295 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.
296
297 = 5.3.0 =
298 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.
299
300 = 5.2.0 =
301 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.
302
303 = 5.1.7 =
304 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.
305
306 = 5.1.6 =
307 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.
308
309 = 5.1.5 =
310 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.
311
312 = 5.1.4 =
313 Maintenance release — safe to update, no schema changes.
314
315 = 5.1.2 =
316 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.
317
318 = 5.1.1 =
319 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.
320
321 = 5.1.0 =
322 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.
323
324 = 5.0.0 =
325 **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).
326
327 = 3.7.2 =
328 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.
329
330 = 3.7.1 =
331 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.
332
333 = 3.7.0 =
334 CSS fix for table width on admin pages. Improved compatibility with Pro 3.7.0 license system. Safe to update.
335
336 = 3.6.0 =
337 Moved consent export to Pro plugin. The export UI and AJAX endpoint are no longer available without the Pro plugin.
338 Added `f12_doi_database_page_after_forms` hook for extensibility. Safe to update.
339
340 = 3.5.0 =
341 Fixed confirmation mail not being sent for forms with Quiz, Acceptance, or required fields.
342 CF7 validation is now bypassed during opt-in confirmation mail delivery. Safe to update.
343
344 = 3.4.0 =
345 Fixed translation loading issues on WordPress 6.7+, review notice not displaying, and database table missing errors.
346 Added 133+ missing German translations for the Email Editor and related features. Safe to update.
347
348 = 3.3.0 =
349 New: Delete confirmation modal, GDPR consent export (JSON/CSV), admin tooltips, error redirect page, hCaptcha compatibility.
350 New: Unique Email redirect behavior (Pro).
351 Fixed reCAPTCHA re-activation typo. Safe to update -- no database changes.
352
353 = 3.2.3 =
354 Bugfix release: Fixes broken toggle switches on the settings page. Safe to update -- no database changes.
355
356 = 3.2.2 =
357 Bugfix release: Fixes double-firing of the after_confirm hook. Safe to update -- no database changes.
358
359 = 3.2.1 =
360 Bugfix release: Fixes a fatal error on new, unsaved CF7 forms. Safe to update.
361
362 = 3.2.0 =
363 **Important: Major Update -- Please backup before updating!**
364 This version includes significant changes to the form management system, email templates, and database structure.
365 We strongly recommend creating a full site backup before updating.
366 New features: Visual email editor, centralized form settings, GDPR anonymization, and more.
367
368 = 3.1.0 =
369 Adds optional anonymous telemetry (opt-out). No breaking changes.
370
371 == Changelog ==
372
373 = 5.8.1 =
374 * Fix: on sites in formal German (Sie), a sentence on the user creation page was shown in English, and the role appeared as its internal key ("subscriber") instead of its name.
375 * Fix: German admin texts used the English names "Conditional Templates", "User Creation" and "Unique Email"; they now use the German terms throughout.
376 * Fix: the breadcrumb on add-on settings pages read "Page".
377
378 = 5.8.0 =
379
380 **Know whether the confirmation email went out**
381
382 * New: every opt-in records whether its confirmation email was handed to your mail server or failed, and why. Shown in the opt-in list (with a filter), on the detail page and in Site Health; included in the WordPress privacy export and eraser.
383 * New: after a Contact Form 7 submission, visitors read "please confirm your address" with the sender and subject to look for, instead of "Thank you for your message". When the email could not be sent, they are told so.
384 * New: Site Health checks SPF and DMARC of the sender domain, the two most common reasons confirmation emails land in spam.
385 * New: a dashboard hint when many recent opt-ins stay unconfirmed.
386 * New: shortcodes [doi_confirmation_status], [doi_error_message] and [doi_field] for the confirmation and error pages, and a readable message when a link is broken or expired.
387 * New: a hidden honeypot field and a minimum fill time keep simple bots away from Contact Form 7 double opt-in forms.
388 * New: the forms page suggests add-ons only for form plugins that are installed on the site.
389 * New: after ten confirmations, a one-time request for a review, only on the plugin's own pages.
390 * Changed: the free plugin no longer contains locked Pro features. Email template management and the opt-out page generator now live in their add-ons. If you use them, update the email editor add-on to 1.1.0 and the opt-out add-on to 1.5.0; Site Health reminds you.
391 * Changed: a renewed consent after an opt-out no longer overwrites the original confirmation; it gets its own entry in the audit log.
392 * Fix: the Activate button on the Add-ons page and links in REST responses were broken ("&" in the URL).
393 * Fix: Avada forms without a notification were reported as a failed follow-up action.
394 * Fix: several admin texts stayed English on translated sites; the breadcrumb read "Page" on detail pages; a waiting button now shows a countdown.
395 * Developers: Core API 4.6.0 with the filters f12_doi_submit_notice_data and f12_doi_mail_headers, the events f12_doi_optin_opted_out and f12_doi_optin_reopted_in, follow-up adapters for add-ons and a shared resend service.
396
397 = 5.7.0 =
398
399 **A setup wizard gets new sites to the first working confirmation email**
400
401 * New: after installing the plugin, a short wizard sets up the sender, the Contact Form 7 forms that should ask for confirmation, the confirmation email and the page visitors see after clicking the link. Everything is prefilled; it takes about three minutes and can be skipped at any time. A test email to yourself shows the result before visitors see it.
402 * New: the sender name and address from the wizard are used for every form you switch on later. Confirmation emails without a sender name no longer arrive as "WordPress".
403 * New: if you also use Elementor Pro, WPForms, Gravity Forms or Avada Forms, the wizard tells you which add-on protects those forms.
404 * Improved: forms that already use Double Opt-In are left exactly as they are. Existing sites do not see the wizard unless you start it under Settings.
405 * Fix: this description listed the visual email editor, saved templates and test emails as free features. They are part of the email editor add-on; the free plugin offers three built-in email designs. Resending a confirmation email from the opt-in details is free.
406
407 = 5.6.3 =
408
409 * Fix: the developer reference this readme points to — `docs/hooks-and-events.md` and `docs/addon-api.md` — was never actually included in the plugin. Both files now ship.
410 * Improved: the notice for major updates on the Plugins screen no longer starts with an emoji; it already sits in a warning box.
411
412 = 5.6.2 =
413
414 **A refused consent no longer looks like a successful sign-up**
415
416 * Fix: when a visitor left the consent checkbox unticked, Contact Form 7 still showed "Thank you for your message. It has been sent." and emptied the form. The actual reason appeared only in a small notice that disappeared after ten seconds — so the visitor believed they were subscribed and waited for a confirmation mail that never came. The form now stops with "You must agree to the consent statement to continue." and keeps everything the visitor typed; they only have to tick the box.
417 * Fix: the same on Elementor, WPForms and Gravity Forms forms — the consent checkbox is now marked the way a missed required field is, and the form stays on the page with the visitor's input. This needs the Elementor add-on 1.2.1, the WPForms add-on 1.1.1 and the Gravity Forms add-on 1.1.1. With older add-ons the success message is at least hidden and the notice stays until the visitor closes it.
418 * Developer: new `AbstractFormIntegration::refusedConsentBeforeSubmit()` for integrations whose submit hook runs after the form plugin has accepted the submission (Core API 4.5.0, additive).
419 * Note: this applies to the consent checkbox only. Other refusals (rate limits, blocked domains, …) keep their current behaviour and can be shown in the form with the filter `f12_cf7_doubleoptin_show_validation_error`, which now also receives the error and the form ID.
420
421 = 5.6.1 =
422
423 **The admin no longer stays blank next to Avada and similar plugins**
424
425 * Fix: on some sites the Double Opt-In admin stayed blank or loaded only partly after 5.6.0, depending on the browser, with "clearTimeout is not a function" in the browser console. The admin script accidentally registered an internal helper under the global name `_`, which WordPress and many themes and plugins (Avada among them) use for the Underscore/Lodash library. Whichever loaded last won. The admin script now keeps all of its names to itself, and it no longer replaces WordPress' own `_` either.
426
427 = 5.6.0 =
428
429 **What happens after the confirmation click is now recorded, retried and visible**
430
431 * Security: a crafted request could switch off the double opt-in for a single submission, so the form's follow-up actions ran without a confirmed address. This is closed for all form systems. Update recommended.
432 * New: every action that runs after a subscriber confirms — the form's notification mail, the stored entry, and for Elementor each "Actions After Submit" step — is now recorded individually with its outcome. The opt-in detail page shows them in a new "Follow-up actions" panel.
433 * New: an action that fails for a temporary reason (mail server unreachable, timeout) is retried automatically after 1, 5 and 30 minutes. Actions that already succeeded are never run again, so a retry does not send a second mail or write a second entry. An action whose outcome is unclear (the connection broke after the request was sent) is not retried automatically; the panel says so and asks before you retry it by hand.
434 * New: a "Retry failed actions" button on the opt-in detail page, and a "Follow-ups need attention" filter in the opt-in list. A manual retry starts a fresh set of automatic attempts.
435 * New: a Site Health check when follow-up actions keep failing, and entries in the audit log for every attempt.
436 * Fix: on Elementor forms the actions after the confirmation could be cut short by a CAPTCHA or honeypot field that was checked a second time, long after the visitor had passed it.
437 * Fix: a second click on the confirmation link no longer runs the follow-up actions again.
438 * Fix: with debug logging switched on, the log files could be downloaded from the uploads folder. The folder is now protected and the files carry names that cannot be guessed; existing log files are renamed on the next request.
439 * Developer: new filter `f12_doi_follow_up_backoff` and adapter interface for form integrations; Core API 4.4.0 (additive, no breaking change).
440
441 = 5.5.0 =
442
443 **An old Pro plugin no longer takes the site down with it**
444
445 * Fix: with a Double Opt-In Pro older than 4.0 installed next to the current modules, WordPress failed with "There has been a critical error on this website" — including the admin, so the old plugin could not be deactivated, the licence page could not be opened, and even deleting it from the dashboard failed. The two products declare some program parts under the same names; the compatibility loader now skips the duplicate instead of loading it a second time. The site stays reachable, and one of the two copies is simply not run.
446 * New: a Site Health check naming the outdated Pro plugin, its folder and its version, with a one-click "Deactivate the old plugin now". It also appears on the dashboard and the plugins screen, where the problem is usually noticed.
447 * New: a Site Health check for leftover Pro setup markers. Pro records that it has migrated its licence and installed its modules, and those records survive uninstalling it — so a fresh installation used to find them and skip both steps: no licence, no modules, and the "Install" button on the Add-ons screen failing with a routing error. One click clears them; your licence key and all form settings are left alone.
448 * Note: neither repair deletes anything from your server. Removing the old plugin's folder stays a manual step over FTP or SSH on purpose — that version's uninstall routine drops the opt-out database table, and the WordPress delete button would run it.
449 * Fix: the "required field" labels in the admin appeared in English on German and French sites, along with 17 other untranslated strings.
450 * Improved: the hook reference shipped with the plugin now documents the consent-gate hooks and matches the hooks that actually exist.
451
452 = 5.4.0 =
453
454 **The consent checkbox is now enforced everywhere**
455
456 * 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."
457 * 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.
458 * 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.
459 * 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.
460 * 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.
461
462 = 5.3.2 =
463
464 * 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.
465 * 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.
466 * 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.
467 * 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.
468
469 = 5.3.1 =
470
471 * 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.
472
473 = 5.3.0 =
474
475 **Telling you when something is broken:**
476
477 * 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.
478 * 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.
479 * 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.
480 * New: addons contribute their own checks through the `f12_doi_health_checks` filter, so this covers future addons without further work.
481
482 = 5.2.0 =
483
484 **Getting hold of us:**
485
486 * 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.
487 * New: the review notice offers "Something not working? Tell us" alongside the review button.
488 * 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.
489 * New: an optional dialog when deactivating asks what went wrong. It never blocks or delays deactivation, sends nothing by itself, and appears once.
490
491 **Optional credit link:**
492
493 * 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`.
494 * 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.
495 * 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.
496
497 **Fixes:**
498
499 * 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.
500
501 = 5.1.7 =
502
503 **Telemetry removed:**
504
505 * 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.
506 * 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.
507 * Privacy: usage counters remain on your site and are never transmitted. They are deleted when the plugin is uninstalled.
508 * 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.
509
510 = 5.1.6 =
511
512 **PHP 7.4 compatibility:**
513
514 * 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.
515 * 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.
516 * 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.
517 * 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.
518
519 = 5.1.5 =
520
521 **Security & hardening:**
522
523 * 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.
524 * Fix: the consent CSV export neutralises spreadsheet formula injection (values beginning with `=` `+` `-` `@`).
525 * 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.
526 * Fix: the form-URL email placeholder is URL-escaped.
527 * Maintenance: removed leftover debug logging, added ABSPATH guards to directly-reachable files, and corrected a text domain (wordpress.org compliance).
528
529 = 5.1.4 =
530
531 * 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.)
532
533 = 5.1.2 =
534
535 **Email Template Editor fixes:**
536
537 * 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).
538 * 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.
539 * New: The Social Icons block now has an editable settings panel (network + URL per icon, add/remove, plus icon size, spacing, alignment, and padding).
540
541 = 5.1.1 =
542
543 * Improved: Admin menu label renamed from "DOI Admin" to "Double Opt-In".
544 * 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).
545 * Maintenance: Excluded a stray TypeScript build-cache file from the distributed plugin.
546 * Maintenance: WordPress compatibility updated to 7.0 ("Tested up to").
547
548 = 5.1.0 =
549
550 **Form Completeness Gate:**
551
552 * 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.
553 * 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.
554 * New: Forms-list "Incomplete" badge + disabled toggle for incomplete forms, with parity between the React UI and the REST gate.
555 * 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.
556 * 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.
557 * 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.
558
559 **File Lifecycle (GDPR data minimization):**
560
561 * 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).
562 * 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.
563 * New: CF7 post-mail file-cleanup hook removes attachments from the temporary store as soon as the confirmation mail leaves the system.
564 * New: Reset-feature integration with the file-lifecycle so a manual reset cleans up attached files alongside the opt-in row.
565 * Improved: WP_DEBUG-gated reset-confirmation endpoint + admin button for developer-only re-testing of the confirmation pipeline.
566
567 **Form Settings UX:**
568
569 * New: Page-level completeness banner with a sticky warning marker until all required fields are filled.
570 * Improved: General tab — relabelled fields, clearer helper text, required-field markers, page-section descriptions.
571 * 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).
572 * 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.
573 * Improved: Forms-tabs polish + addon-settings routing — `/addon-settings/<id>` is now the canonical mount point for Pro and free addons.
574 * Fix: Removed the fake header Save button that lied to users — only the per-tab Save action persists settings.
575 * 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.
576
577 **Addon Platform:**
578
579 * 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.
580 * New: Per-addon feature toggle pages, decoupled from plugin activation — a feature can be installed but disabled without uninstalling.
581 * New: Addons self-contribute sidebar entries via a manifest, replacing the old hard-coded core sidebar.
582 * New: Per-addon mount points on Form Settings → Pro Features (e.g. unique-email, conditional, user-registration self-render their per-form panel).
583 * 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.
584
585 **REST API:**
586
587 * 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.
588 * New: `GET /f12-doi/v1/consent-export/history?limit=N` — recent audit-runs for the consent-export "Recent exports" card.
589 * 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.
590 * New: `DELETE /f12-doi/v1/consent-export/history` and `DELETE /f12-doi/v1/consent-export/history/<id>` — bulk and per-run audit deletion.
591
592 **Bug Fixes:**
593
594 * 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.
595 * Fix: Legacy frontend `FormData` proxy now resolves `getFormType` correctly so consent-field plumbing works on CF7.
596 * Fix: Elementor `form_fields` are unwrapped in placeholder substitution — `[doi_email]` and friends now resolve in confirmation mails on Elementor forms regardless of nesting.
597 * Fix: Confirmation and error redirect pages now resolve at runtime via the page-resolver, not at save time, so renames stay in sync.
598 * Fix: `href="#"` in legacy templates no longer breaks the confirmation link in inline-styled mails.
599 * Fix: Table doesn't exist error for `f12_cf7_doubleoptin_categories` on manual file upload — both custom tables now verify on every update cycle.
600
601 **Architecture:**
602
603 * New: Migration registry — addons register schema migrations; Core applies pending ones on `admin_init`.
604 * 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.
605 * Improved: Plugin is now part of a monorepo; build pipeline produces one ZIP per package; PHPUnit gate aborts the build on red tests.
606 * Improved: Test count Core: 655 → 1712 unit tests across the monorepo, all green.
607
608 = 5.0.0 =
609
610 **Breaking change: plugin family restructured into Core + paid addons.**
611
612 * 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.
613 * Breaking: PHP minimum lowered from 8.0 to 7.4 to align with WordPress's supported PHP versions.
614 * New: Addon API stabilised and covered by semver (`F12_DOI_CORE_API_VERSION` = 4.3.0). See `docs/addon-api.md`.
615 * New: Addon license registry (`AddonLicenseRegistryInterface`) — license providers (Pro bundle, standalone keys) grant entitlements; addons check `isLicensed()`.
616 * New: Migration registry (`MigrationRegistry`) — addons register schema migrations; Core applies pending ones on admin_init.
617 * 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.
618 * 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.
619 * 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.
620 * 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.
621 * 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.
622 * 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).
623 * Improved: All public interfaces tagged `@api`; implementation details tagged `@internal`. Deprecation policy: 1 minor release of warning before removal.
624 * Improved: Plugin is now part of a monorepo; build pipeline produces one ZIP per package.
625 * Improved: PHPUnit gate in the build pipeline — red unit tests now abort the build. Total Core test count: 466 → 655 (+189).
626
627 = 3.7.2 =
628
629 **Bug Fixes:**
630
631 * 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.
632 * Fix: Applied the same nested content handling to the legacy `OptInFrontend::addPlaceholders()` code path.
633 * 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.
634 * 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.
635 * Fix: Fixed the AJAX opt-in detail modal showing the same incorrect metadata for Avada opt-ins.
636
637 = 3.7.1 =
638
639 **Bug Fixes:**
640
641 * 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.
642 * 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.
643 * 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`.
644
645 **Announcements:**
646
647 * Notice: Starting with version 3.8.0, Avada Forms integration will move to the Pro version. Contact Form 7 support remains free.
648 * New: Dismissible admin notice for sites with active Avada/Fusion Builder, informing about the upcoming change.
649 * New: Yellow info banner on the Forms management page in the Avada section.
650 * New: Plugin update message warning when Avada is active.
651
652 = 3.7.0 =
653
654 **Bug Fixes:**
655
656 * 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.
657
658 **Compatibility:**
659
660 * Updated: Full compatibility with Pro version 3.7.0 and its new license management system.
661
662 = 3.6.0 =
663
664 **Architecture:**
665
666 * 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.
667 * Security: The `doi_export_consent` AJAX endpoint is no longer registered in the free plugin, preventing unauthorized access without a Pro license.
668 * 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.
669
670 = 3.5.0 =
671
672 **Bug Fixes:**
673
674 * 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.
675 * Fix: Applied the same validation bypass to the legacy `CF7Frontend::sendDefaultMail()` code path, which had the same issue.
676
677 **Improvements:**
678
679 * 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()`.
680
681 = 3.4.0 =
682
683 **Bug Fixes:**
684
685 * Fix: Fixed translation loading too early warning on WordPress 6.7+ (`_load_textdomain_just_in_time` notice).
686 * Fix: Fixed review notice never displaying due to namespace resolution issue.
687 * Fix: Fixed Free and Pro plugin constant/function redeclaration conflicts when both plugins are active simultaneously.
688 * Fix: Fixed TestEmailBlocker fatal error in distribution builds where test dependencies are not included.
689 * Fix: Fixed Pro upgrade prompt ("Pro Feature", "The '{block}' block requires the Pro version.") displaying in English instead of the active language.
690 * 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.
691
692 **Improvements:**
693
694 * Improved: Added 133+ missing German translations covering the Email Editor, Placeholder Mapping, Email Template Post Type, Email Presets, and Pro upgrade prompts.
695 * Improved: Added formal German (Sie) translations for all new strings.
696 * Improved: Database table existence safety net -- both custom tables are now verified and recreated on every update cycle, independent of the activation hook.
697 * Improved: Updated "Upgrade to Pro" links to point to the correct product page.
698
699 = 3.3.0 =
700
701 **New Features:**
702
703 * New: Delete confirmation modal -- clicking "Delete DOI" now opens a confirmation dialog to prevent accidental deletion. Dismissible via Cancel, overlay click, or Escape key.
704 * New: GDPR consent record export -- export individual opt-in records as JSON or CSV directly from the opt-in detail view.
705 * New: Admin tooltips -- contextual help tooltips with descriptions throughout the admin interface.
706 * New: Error redirect page -- configure a per-form redirect page for opt-in errors (rate limit, invalid email, etc.).
707 * New: hCaptcha compatibility -- hCaptcha validation is now automatically bypassed during opt-in confirmation mail delivery, alongside Forge12 Captcha and Google reCAPTCHA.
708
709 **New Features (Pro):**
710
711 * 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.
712 * New: Dedicated Redirect Page selector in the Unique Email settings (per-form). Only visible when behavior is set to "Redirect to page".
713 * New: `UNIQUE_EMAIL_DUPLICATE` error code for distinguishing duplicate email rejections from other validation errors (e.g. MX check).
714
715 **Bug Fixes:**
716
717 * Fix: Success and error messages (e.g. "Opt-In deleted") are now rendered as styled alerts instead of plain text.
718 * 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`).
719 * 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.
720 * 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`.
721 * Fix: CF7 now shows an inline error message (instead of the success message) when Unique Email rejects a submission in block or redirect mode.
722 * 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.
723 * 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.
724 * Fix: Elementor success messages are now hidden when a validation error (block/redirect) occurs, preventing contradictory success and error messages.
725 * 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.
726
727 **Improvements:**
728
729 * Improved: Updated translations (German, German formal, French, English).
730 * Improved: CAPTCHA bypass now covers Forge12 Captcha, Google reCAPTCHA, and hCaptcha across all three bypass layers (SpamMechanics, AbstractFormIntegration, OptInFrontend).
731 * 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.
732 * Improved: Error handling in CF7 integration prevents mail sending for all rejection modes (block, silent, redirect).
733 * 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).
734
735 **Testing:**
736
737 * 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).
738 * 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.
739
740 = 3.2.4 =
741
742 **New Features:**
743
744 * 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.
745 * 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.
746 * New: OptInError value object for typed, translatable error codes across all integrations.
747
748 **Improvements:**
749
750 * Improved: Error handling in all form integrations now uses the centralized OptInError and ErrorNotification system.
751 * Improved: Frontend error detection covers Contact Form 7, WPForms, Gravity Forms, Avada, Elementor, and generic AJAX/form submissions.
752
753 = 3.2.3 =
754
755 **Bug Fixes:**
756
757 * Fix: Fixed broken toggle switches on the settings page. Clicking the toggle button or its label text now correctly toggles the value.
758 * Fix: Removed stale `<label class="toggle-label">` elements that were rendered as duplicate toggle buttons due to WordPress admin CSS.
759 * Fix: Removed non-functional `<label class="overlay">` elements (leftover from an older CSS-only toggle pattern).
760 * Fix: Replaced incorrect `esc_attr_e()` with `echo esc_attr()` for HTML `for` attribute values in the telemetry toggle.
761
762 **Improvements:**
763
764 * Improved: The entire toggle row (button + description text) is now clickable, not just the small toggle button.
765 * Improved: Added CSS for `.f12-checkbox-toggle` for proper flex layout of toggle components.
766
767 = 3.2.2 =
768
769 **Bug Fixes:**
770
771 * 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.
772
773 **Developer Features:**
774
775 * New: Added `getFormData()` method to `OptInConfirmedEvent`, providing direct access to submitted form field data via the typed event system.
776 * New: Added `shouldBridgeToWordPress()` to the Event base class, allowing individual events to opt out of automatic WordPress hook bridging to prevent duplicate hook calls.
777 * New: Added comprehensive developer documentation (`docs/hooks-and-events.md`) with complete reference for all 18 action hooks, 23 filters, and 11 typed events.
778
779 **Improvements:**
780
781 * Improved: Updated hook usage hints in the admin panel with both legacy and event-based code examples.
782
783 = 3.2.1 =
784
785 **Bug Fixes:**
786
787 * Fix: Fixed a fatal error (TypeError) when opening the Double Opt-In panel on a new, unsaved Contact Form 7 form.
788
789 **Improvements:**
790
791 * Improved: Added a notice in the CF7 Double Opt-In tab prompting users to save the form before configuring Double Opt-In.
792
793 = 3.2.0 =
794
795 **Email Template Editor:**
796
797 * New: Visual drag & drop email template editor with block-based design.
798 * New: Pre-built email template presets (Blank, Dark Professional, Yellow Bold, Minimal Clean, Opt-Out Confirmation).
799 * New: Placeholder library with all available form fields and system variables.
800 * New: Opt-out email template support in the editor.
801 * New: Send test email functionality to preview emails before going live.
802 * New: Mobile preview mode to check responsive email design.
803 * New: Rich text editing with formatting options (bold, italic, links, lists).
804 * New: Block registry for extensible template components (Pro: multi-column, images, social icons).
805
806 **Form Management:**
807
808 * New: Centralized form settings management panel for all form integrations (CF7, Avada, Elementor).
809 * New: Resend confirmation email directly from the admin dashboard.
810 * New: Unified settings interface across all supported form plugins.
811 * New: Field mapping system for connecting form fields to email placeholders.
812
813 **WordPress & Multisite:**
814
815 * New: Full WordPress Multisite support -- network-wide activation creates database tables on all existing sites.
816 * New: Automatic table creation for new sites added to the network (via `wp_initialize_site` hook).
817
818 **GDPR & Security:**
819
820 * New: GDPR-compliant anonymization of personal data instead of deletion.
821 * New: Rate limiting for form submissions to prevent abuse (configurable per IP and per email).
822 * New: Consent text snapshot stored per opt-in record for audit trail.
823 * New: Consent export (CSV) for GDPR compliance.
824 * New: WordPress Privacy Tools integration (personal data export & erasure requests).
825 * New: Configurable token expiry settings (default: 48 hours).
826 * New: Configurable data retention settings for confirmed and unconfirmed entries.
827 * Security: Fixed potential XSS vulnerabilities in admin screens.
828 * Security: Improved input sanitization throughout the plugin.
829
830 **Architecture & Performance:**
831
832 * New: Event-driven architecture with 11 typed events for form submissions and opt-in lifecycle.
833 * New: Service container with dependency injection for improved extensibility.
834 * New: `WordPressHookBridge` for backward compatibility between legacy hooks and typed events.
835 * New: Form integration registry for pluggable form builder support.
836 * New: REST API for email template management (`/wp-json/f12-doi/v1/email-templates`).
837 * Improved: CSS extracted to external files for better caching.
838 * Improved: Code refactored to PSR-4 autoloading with modern PHP architecture.
839
840 **Bug Fixes & Improvements:**
841
842 * Fix: Fixed double mail sending issue on CF7 and Avada forms.
843 * Fix: Fixed email button URLs being incorrectly escaped when using placeholders.
844 * Improved: Refactored CF7 and Avada form integration architecture.
845 * Improved: Redesigned admin dashboard with dedicated opt-in management views.
846 * Improved: Updated translations (German).
847
848 = 3.1.1 =
849
850 * Improved: Enhanced compatibility with major CAPTCHA plugins to ensure smoother user verification.
851
852 = 3.1.0 =
853
854 * New: Added optional anonymous telemetry (opt-out) to improve plugin performance and usability.
855 * Privacy: Documented all telemetry fields collected.
856 * Improved: Minor optimizations for compatibility and maintainability.
857 * Change: Removed frontend support link injection for improved transparency and compliance with WordPress guidelines.
858 * Improved: Branding is now shown only in the plugin settings (admin area).
859
860 = 3.0.72 =
861
862 * Improved: Increased compatibility between Free and Pro version.
863 * Improved: Added support for Avada 7.12.2.
864
865 = 3.0.70 =
866
867 * Fixed: Fixed a bug stopping the CF7 forms to attach uploaded files after opt-in confirmation.
868
869 = 3.0.62 =
870
871 * New: Added hook `f12_cf7_doubleoptin_skip_option` to allow skipping opt-ins if required.
872
873 = 3.0.60 =
874
875 * Fix: Fixed a bug causing Elementor to stop sending opt-in mails.
876
877 = 3.0.51 =
878
879 * New: Avada Opt-In now leverages the Notification System for handling emails. The "Send to Email" action remains supported.
880
881 = 3.0.50 =
882
883 * New: Added Avada Forms integration.
884 * Improved: Reworked admin UI for better usability.
885
886 = 3.0.0 =
887
888 * New: Complete rewrite of the plugin core.
889 * New: Category system for organizing opt-in records.
890 * New: Improved admin dashboard with pagination and search.
891 * New: Custom confirmation page redirects.
892 * New: Dynamic conditions for enabling opt-in per form.
893 * Improved: Database schema with additional tracking fields.
894
895 = 2.0.0 =
896
897 * New: Support for custom email templates.
898 * New: IP address logging for registration and confirmation.
899 * Improved: Opt-in record management in the admin dashboard.
900
901 = 1.0.0 =
902
903 * Initial release.
904 * Double Opt-In for Contact Form 7.
905 * Basic confirmation email customization.
906