=== WPCoreTools Email Verify: full changelog ===

The readme lists only the latest releases. This file keeps every version.

= 1.0.7 =

* Removed the "More tools" settings tab, the "More tools by WPCoreTools" block on the last wizard step, the "Pair with Disposable Email Guard" suggestion on the Settings tab, and the bundled icons of those plugins. The plugin no longer links to or offers to install any other plugin (WordPress.org guideline 11: no advertising in the dashboard). When Disposable Email Guard is active, the Settings tab and the wizard still say so in one line, as before.
* Closing a conflict warning on the Dashboard or Plugins screen with its X now dismisses it for good (per user), the same as the "Dismiss permanently" link; before, the X hid it only until the next page load.

= 1.0.6 =

* Readme rewritten: a short description, fewer FAQ entries, and only the latest releases in the changelog. The full history moved to this file.
* The readme now says plainly that the settings pages and the last wizard step link to the author's other free plugins.
* Removed the Plugin URI header (its web page is not published yet) and the Domain Path header (the plugin ships no languages folder; WordPress.org provides the translations).
* The coding-standards configuration file (phpcs.xml.dist) no longer sits inside the plugin folder.
* Code comments only: no functional change.

= 1.0.5 =

* **Updating puts both confirmations in step by itself.** After an update, with WooCommerce 11, a one-off background sync runs once: customers verified by this plugin get WooCommerce's "Email confirmation" box ticked (exempt roles never), and customers WooCommerce has confirmed for their current address while this plugin still waited for them are verified here. It uses the same batches as the Tools tab's "Sync verified customers to WooCommerce" panel, which shows its progress. No email is sent. So after updating from any earlier version the profile shows one consistent answer.
* Dates on the profile, the Verifications list, the Settings page and Site Health are shown in the site's time zone (Settings > General), not UTC.

= 1.0.4 =

**Changed**

* **One switch on the user profile with WooCommerce 11.** WooCommerce 11 has its own "Email confirmation" box on the profile, which made two settings for the same thing. When it is there, it is now the one switch: this plugin hides its own status section, follows the box both ways (ticking it verifies the customer here too, clearing it makes the customer unverified here too; saving a profile without changing the box changes nothing), and only adds how the address was verified, or the pending confirmation email with a Resend button, right under it. Without WooCommerce 11 the plugin's own Email verification section stays as before.

**Added**

* **"Verified by" on the user profile**: confirmation link, password reset email, WooCommerce email confirmation, marked verified by an admin, and so on (the reason recorded since 1.0.3). New filter `wpcev_verified_via_label( $label, $via, $user_id )` lets plugins that verify through `mark_verified( $user_id, '<reason>' )` name their reason.
* Ticking WooCommerce's box on the profile is recorded as "Marked verified by an admin".

= 1.0.3 =

**Added**

* **Verified column on the WooCommerce Orders list** (HPOS and the older posts-based orders screen, WooCommerce 9.2+). Each row shows the order customer's status with the same badges as the Users list (Verified, Pending, Exempt, or a dash for pre-existing users), "Guest" for guest orders, and the customer's name linking to their profile. The customers of the whole page are loaded in two queries. Shown to people who can list users or edit orders; hide it under Screen Options if you do not need it.

* **Two-way sync with WooCommerce 11's built-in email verification.** Until now only WooCommerce to this plugin was synced. Now a customer verified here on proof of the inbox (confirmation link, finished password reset, multisite activation) or by an admin ("Mark verified" on a row, in bulk, on the profile, or with WP-CLI) is also marked verified in WooCommerce through WooCommerce's own verification service, and marking a user unverified clears WooCommerce's record. Exempt-role and filter-based verification are not mirrored. No loops and no double work: earlier guest orders are linked exactly once per verification. A change in WooCommerce can never cause a fatal error, and nothing happens on WooCommerce before 11 or without WooCommerce.
* **"Sync verified customers to WooCommerce"** panel on the Tools tab (only shown when WooCommerce's verification exists): shows how many verified customers WooCommerce does not know yet (only those who confirmed their address or were marked verified by an admin; earlier accounts with no recorded reason need a tick), asks for confirmation, then processes them in the background in batches of 200 with a progress bar (Action Scheduler, or WP-Cron when it is not available). Safe to run again. It sends no email; WooCommerce links earlier guest orders with the same address when it marks a customer verified.
* New hooks: action `wpcev_user_verified( $user_id, $reason )` (the reason is new), action `wpcev_user_unverified( $user_id, $reason )`, filter `wpcev_sync_to_woocommerce( $sync, $user_id, $reason, $direction )`.

**Fixed**

* The Orders list column no longer queries the database once per row for an order whose customer account was deleted.
* The status badges in the Users list had no colours (their stylesheet was only loaded on the plugin's own pages). It now loads on the Users screen too.
* The Users list bulk actions "Mark verified" and "Mark unverified" always ended with "The link you followed has expired" on current WordPress versions. They work again, still protected by a nonce.
* "Mark grandfathered users as unverified" no longer unverifies (or clears in WooCommerce) a customer who already confirmed their address through WooCommerce 11's own link.

= 1.0.2 =

Customers can now log in and shop around while they wait for the confirmation email, and always see why they cannot order yet. The setup wizard describes each setup in plain words, resend links count down, a mistyped address can be fixed from the notice, and a second verifier on the site is impossible to miss.

**Fixed**

* The setup wizard could not be opened ("Sorry, you are not allowed to access this page"), neither after activation nor from the settings page. It is a hidden admin page now and opens normally.
* WooCommerce 8.9 to 10.7 created a draft order record when an unconfirmed customer merely opened the Checkout block page. That path is now guarded as well; placing the order was always refused.
* With the WooCommerce email format set to Auto or WooCommerce, the first mail after a My Account sign-up used the plain-text fallback. It now uses the WooCommerce template.
* Changing the address on the Account details page now waits the resend time like every other send.
* The Site Health tests no longer fail when WordPress runs them in its weekly background check.

**Added (setup wizard and settings)**

* The wizard offers preset cards (Shop, Strict, Community, Custom). Each lists what it does for the customer, generated from the same values it applies. Re-running starts at your current settings, marks the matching card "Current" and never resets the email subject, text or sender. The settings page has a "Reconfigure with setup wizard" button.
* New setting "Resend wait" (30 to 3600 seconds, default 60), also in the wizard's email step next to the link validity.
* Defaults for new installs are now the Shop setup (login allowed, ordering blocked; comments, wp-admin, profile email and capabilities not restricted). Existing installs keep their saved switches.

**Added (customers)**

* Every resend link and button counts down ("Resend available in 0:42") and unlocks when the wait is over. The server enforces the wait on all paths; without JavaScript the answer is "please wait N seconds".
* "Wrong address? Change it" form in the My Account notice: correct the address and get a new link in one step, even when profile email changes are blocked. Cart, checkout and Cart/Checkout Blocks messages link to it.
* The emailed link verifies at once only for the customer logged in as that account. For anyone else (a link preview, a scanner, another device) a "Confirm my email" page appears and nothing changes until the button is pressed. All plugin endpoints are sent uncacheable.
* Finishing a password reset confirms the address (filter `wpcev_verify_on_password_reset` turns this off, also on WooCommerce 11).
* REST API comments from unconfirmed users are refused with a proper 403 when comment blocking is on.

**Added (WooCommerce 11 and other verifiers)**

* While enabled, WooCommerce 11's own verification prompt is hidden (11.1+); a verification in either place counts in both, and earlier guest orders are linked to the account after a real confirmation.
* The plugin's WooCommerce email is now "Account email verification (WPCoreTools)" in the Accounts group, shows sample values in WooCommerce's preview and test send, and uses WooCommerce's email button template when available.
* Second-verifier detection: hooks other code registers on registration, login and checkout, an extended list of known plugins, an on-demand code scan of active plugins, mu-plugins and the theme (finds verification done in a plugin's own login code), a count of users another tool marks unverified, WooCommerce 11's built-in verification state, and WooCommerce accounts created after checkout. Shown on plugin screens, the Plugins screen and the wizard; runs while the plugin is switched off.
* Site Health tests (second verifier, mail delivery, cleanup schedule, accounts waiting) and a debug-information section.

**Changed**

* Requires WordPress 6.4+ and WooCommerce 9.2+ (was 8.5), the oldest WooCommerce still inside its security-patch window. Tested up to WordPress 7.1 and WooCommerce 11.1.
* The admin styles follow the administrator's colour scheme.
* The WooCommerce email was renamed from "Email verification"; its settings and templates are unchanged.

**Added (earlier in 1.0.2)**

* A notice at the top of every WooCommerce My Account page (not just the dashboard tab) for unconfirmed customers, with the address the link was sent to, a resend link and a pointer to Account details for fixing a mistyped address.
* A notice with a resend link on the classic cart and checkout pages, and a disabled Place order button on classic checkout.
* Cart/Checkout Blocks: the "confirm your email" message now contains the resend link and the customer's address, and is no longer shown for an empty cart.
* A "we sent a confirmation link to ..." message right after registering through My Account (shown once), including a clear message when the email could not be sent.
* After clicking the confirmation link while logged out, the My Account login page says the email is confirmed and that they can log in. Expired and invalid link messages now include a way to get a new link.

**Changed**

* Clicking "Resend verification email" now redirects back to the same page, so a refresh no longer re-sends, and an out-of-date resend link explains itself instead of doing nothing.
* The settings page and wizard now explain what "Block login until email is confirmed" does when it is off. Stored settings and defaults are unchanged.
* Only one verification email is ever sent for a registration, even if another plugin fires the registration hook twice.
* The My Account notice now sits above the account navigation, so on a phone it is the first thing on the page instead of below the full menu.
* "Tested up to" is now WordPress 7.1.
* Cart/Checkout Blocks on WooCommerce before 10.8: opening the checkout as an unconfirmed customer no longer creates a draft order. No order of any status is created until the customer is confirmed (GET, PUT and PATCH on the checkout route are covered, with pretty permalinks or `?rest_route=`, in any letter case).
* The "Wrong address?" link in the My Account notice is only shown when changing the address would actually be accepted. A customer who had confirmed once and then mistyped a new address can now correct it (a few times, like a new registration) instead of being stuck.
* A stale resend link now says to use the resend link in the notice on the page, and tells an already confirmed customer so instead of pointing at a link that does not exist.
* CSV exports pass the `fputcsv()` escape character explicitly (same output; avoids a PHP 8.4 deprecation and keeps the code PHP 7.4 compatible).
* The old My Account banner (deprecated `woocommerce_before_my_account` hook, dashboard only, below the content) is replaced by the notice above, so there is never more than one.

= 1.0.1 =

Correctness, security and compatibility pass. Everything below was found by audit and verified against live WordPress and WooCommerce installs.

**Fixed — could break a site**

* Setup wizard: finishing the email step rewrote every on/off setting to off, including the master enable switch, so the wizard silently disabled the plugin while reporting success.
* Plain-text WooCommerce verification emails contained a dead link — the `&` in the URL was HTML-encoded, so the user ID never reached the site and clicking the link did nothing.
* Deleting the plugin from one site of a multisite network erased verification status for every user on every other site.
* The "delete unverified users" and "mark grandfathered users unverified" confirmation dialogs were a JavaScript syntax error, so both irreversible actions ran without asking.
* Auto-delete could remove long-standing accounts and destroy any content they had authored. It now only removes accounts that are themselves older than the window, and never removes an account with authored content.
* The verification list table showed the same user in every row.

**Fixed — security**

* Bulk actions on the Users screen performed changes without a nonce check (CSRF). (1.0.3: the check could never pass on current WordPress, so the bulk menu "Mark verified / unverified" always failed with "The link you followed has expired"; it now works.)
* On multisite, any request carrying a `key` query argument created a pre-verified account.
* The verification endpoint reported "expired" before checking the token, letting anyone trigger verification emails for arbitrary accounts.
* The resend throttle and send cooldown never engaged on register / email-change / admin-unverify, allowing email flooding.
* The login form revealed whether an account existed and was unverified before the password was checked.
* WooCommerce signed newly-registered customers in even when login blocking was enabled.
* GDPR erasure removed the verification flag, promoting an unverified account to unrestricted.
* CSV exports are now protected against spreadsheet formula injection.

**Fixed — features that did not work**

* Blocking profile email changes did nothing at all; it now works in wp-admin and in WooCommerce My Account.
* Email-change re-verification never ran for changes made in wp-admin, and an abandoned change disabled it permanently.
* The WooCommerce "Enable this notification" toggle was ignored; it now switches the email format instead of being inert.
* Sorting the Users list by verification status hid every pre-existing user.
* Searching the verification list failed for terms containing `&`, `'`, `"`, `<` or `>`.
* Pay-for-order links were checked after WooCommerce had already processed payment.
* Clicking a verification link twice reported "link is invalid" instead of "already confirmed".
* Forced logout destroyed the session before the resend handler could run, leaving users unable to recover.

**Changed**

* Requires WordPress 6.4+ and WooCommerce 8.5+ (was 6.2 / 5.0). WooCommerce 8.5 is where the HPOS and Cart Blocks paths this plugin uses actually exist.
* Cart/Checkout Blocks: unverified customers are now stopped during cart validation, before an order is created, instead of failing after the order exists.
* Companion-plugin icons are bundled rather than loaded from wordpress.org.
* Auto-delete previews and the grandfathered-user migration are now batched and cached instead of scanning every user on each page load.
* Network activation now seeds settings and the cleanup schedule on every site, and on sites added later.

= 1.0.0 =

Initial public release.

* **Verification core** — token-based, hashed at rest, `wp_mail` failure rolls back the previous token, configurable expiry, optional auto-resend on expired-link click, per-user attempt counter + cooldown.
* **Six independent restrictions** — login, checkout (classic + Cart Blocks + Pay-for-order), comments, signup-at-checkout, wp-admin, profile email change. Mix-and-match per site.
* **Capability-based blocking** — drop configured capabilities while user is unverified.
* **Quick presets** — one-click Strict and Soft modes.
* **Setup wizard** — 5 steps, auto-runs on fresh activation, re-runnable.
* **Conflict detection** — other email-verification plugins, WC signup-at-checkout, missing SMTP, competing auth filters.
* **Companion awareness** — WPCoreTools Disposable Email Guard surfaces as "layered protection".
* **Logged-out resend form** at `wp-login.php?action=wpcev_resend`, IP rate-limited, no enumeration.
* **Resend link injected into the wp-login error** when the failure was a verification block.
* **WordPress dashboard widget** with stats, recent pending, fail alerts, quick links.
* **Users list integration** — column, sortable, filter, bulk actions, row actions, profile-edit widget.
* **CSV exports** — pending users + failed-send log.
* **Failed-send ring buffer** (last 100) with admin tab + clear / export.
* **WooCommerce email-class integration** — appears in Woo → Settings → Emails, uses WC HTML template, theme-overridable.
* **"Send test email" button** on settings + wizard.
* **GDPR personal-data exporter + eraser**.
* **WP-CLI** commands.
* **Five public filters** for SSO / form / white-label integration.
* **HPOS + Cart Blocks compatibility** declared.
* **Multisite-aware**: signups activated via WP's multisite flow skip our second email.
* **Guest-checkout safe**: every restriction requires `is_user_logged_in()`.
* **Performance**: admin classes lazy-loaded; zero JavaScript; zero autoloaded options; one daily cron event (only active when auto-delete is enabled); CSS scoped to plugin pages.
