=== WT Waitlist ===
Contributors: webmastersteam
Donate link: https://Webmasters.Team/
Tags: woocommerce, waitlist, back-in-stock, stock-notifier, notifications
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 8.1
Stable tag: 1.0.10
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Lightweight waitlist for WooCommerce that automatically emails customers when an out-of-stock product (or variation) is back in stock.

== Description ==

**WT Waitlist** is a “back in stock” waitlist for WooCommerce without bloat. Customers leave their email on an out-of-stock product page, and when the stock returns, the plugin sends a notification in the background through Action Scheduler.

Built by the [Webmasters.Team](https://Webmasters.Team) team. Open-source code, no external APIs, all data stored locally in the WordPress database.

= Key features =

* **Signup form replaces Add to cart** for out-of-stock products, with variation support (per size/color).
* **Custom database tables** (`{prefix}wt_waitlist_subscriptions`, `{prefix}wt_waitlist_email_log`). We do not clutter `wp_postmeta`, and the plugin scales to tens of thousands of subscriptions.
* **Action Scheduler queue** (bundled with WooCommerce): batch of 50 emails per tick, automatic retries, retry log visible in `WooCommerce → Status → Scheduled Actions`.
* **5 min throttle** per product prevents duplicate sends when multiple hooks fire (e.g. quantity change followed by status change).
* **One-click unsubscribe** via a link in the email (SHA-256 token), no confirmation or login required.
* **GDPR-ready**: consent logging (anonymized IP, timestamp, user agent), integration with WordPress Privacy Tools (export and erasure of data per email).
* **CSV export** of subscribers from the admin panel (streamed, no timeouts on large lists).
* **Anti-spam**: honeypot plus rate limiter per IP (10 submissions per hour).
* **Theme compatibility**: classic themes (Storefront, Twenty Twenty-One/Two/Three) plus block themes (Twenty Twenty-Four/Five). The form attaches through a hook for classic templates and through a `render_block` filter for FSE.
* **Zero external dependencies**: apart from WooCommerce, requires only plain PHP 8.1+.

= Shortcode fallback =

`[wt_waitlist product_id="123"]`: use this if your custom product template does not fire the standard hooks.

= What WT Waitlist does NOT do (by design) =

* No SMS, no Mailchimp/MailerLite integrations in the free version.
* No double opt-in (minimal flow: leave an email, get a notification).
* No WYSIWYG editor for email templates (HTML template lives in the plugin file, override with the `wt_waitlist_notification_context` filter).

These features are planned for the Pro edition. The free edition covers 100% of the core waitlist flow.

== Installation ==

1. Install and activate WooCommerce (required).
2. Install **WT Waitlist** via WP Admin → Plugins → Add new, or manually over FTP (folder `wt-waitlist` in `wp-content/plugins/`).
3. Activate the plugin.
4. Open the **WT Waitlist** menu in the admin panel and configure data retention plus the administrator notification email address.

On first activation the plugin creates its database tables and schedules a daily job that purges old subscriptions.

== Frequently Asked Questions ==

= How does the form appear on the product page? =

Automatically. The plugin hooks into the standard WooCommerce action (`woocommerce_single_product_summary`) for classic themes and into the `render_block` filter for block themes (FSE). The form is shown only when the product is out of stock.

= What if my theme or template does not fire WooCommerce hooks? =

Use the shortcode `[wt_waitlist product_id="123"]`. It works everywhere, from page builders to custom single product templates.

= How many emails are sent at once? =

Action Scheduler processes batches of 50 subscribers per tick. For a product with 1000 subscribers we queue 20 batches, each processed asynchronously, without blocking the PHP request.

= Do emails go through my WP Mail configuration? =

Yes. We use `wp_mail()`, so if you have WP Mail SMTP, SendGrid, Amazon SES etc. configured, the mail travels through them. The plugin does not hardcode any provider.

= What happens to the data after uninstalling the plugin? =

The `uninstall.php` file drops the tables and deletes the options, transients and scheduled jobs (on every site of a multisite network). Clean. If you deactivate without deleting, the data stays and you can reactivate without losing subscriptions.

= GDPR compliance? =

The plugin stores anonymized IPs (last octet zeroed), consent timestamp and user agent. It integrates with WordPress Privacy Tools, so a customer can request data export or deletion through the standard WordPress flow.

= The signup form does not appear on my product page. What now? =

First check whether the product is actually out of stock. The form replaces the **Add to cart** button only when stock is zero (or the variation is unavailable). If the product is out of stock and the form still does not appear, your theme likely uses a custom single-product template that bypasses the standard WooCommerce hooks. Drop the shortcode `[wt_waitlist product_id="123"]` directly into the product description or template. The shortcode also works inside page builders (Elementor, Bricks, Divi).

= Customers submit the form but no notification email arrives. =

Open **WooCommerce → WT Waitlist → Settings tab** and click **Send test email to admin**. If the test email arrives, `wp_mail()` works and notifications will be delivered as soon as the product returns to stock (give Action Scheduler 1–2 minutes to process the batch). If the test email does **not** arrive, the problem is on your mail pipeline, not on the plugin. Install the free **WP Mail SMTP** plugin and route mail through a transactional provider (SMTP2GO, Brevo, SendGrid, Amazon SES). Shared hosting often blocks `mail()` silently.

= How do I check whether stock notifications are queued? =

Open **WooCommerce → Status → Scheduled Actions** and filter by hook `wt_waitlist_*`. You will see pending and completed jobs with timestamps. If nothing appears after a stock change, check the **System status** panel inside **WooCommerce → WT Waitlist → Settings tab**, which shows whether Action Scheduler is available and how many `wt_waitlist_*` jobs are pending.

= Something is broken and I want to send a support ticket. =

Open **WooCommerce → WT Waitlist → Settings tab**, click **Copy diagnostic info** and paste the resulting block into your email to support. It includes plugin/WP/WC/PHP versions, theme info, table state, subscription counts, recent email log, Action Scheduler status and a sample of out-of-stock products with their purchasable / backorders state, which is everything we need to diagnose the issue without going back and forth.

= I use Flatsome / Avada / Enfold / Porto and the form does not appear. =

Themes with custom product-page templates (Flatsome UX Builder, Avada Builder, Enfold templates, Porto) often replace the standard WooCommerce single-product template, which bypasses the hooks WT Waitlist uses to attach the signup form. As of **v1.0.3** we ship multiple compatibility layers, including a `wp_footer` JS DOM-injection fallback that should handle most of these themes automatically. The plugin also detects these themes and shows a compatibility note inside **WooCommerce → WT Waitlist → Settings tab → System status**.

If the form still does not show:

* In Flatsome, switch the product template to **Default** in *Flatsome → Theme Options → WooCommerce → Product Page*.
* Or place the shortcode `[wt_waitlist product_id="123"]` directly inside the product description / UX block / Custom HTML block.

= My product is marked out of stock but customers can still add it to the cart, and the waitlist form does not appear. =

That means the product has **backorders enabled** (Product → Inventory → Allow backorders). WooCommerce treats it as purchasable even though `is_in_stock()` returns false, and your customers see the standard “Add to cart” button alongside a backorder notice. WT Waitlist still renders its signup form for such products (so both signup paths coexist), but if the form is missing on a backorder-allowed product, check the **Sample out-of-stock products** section inside the diagnostic info (button **Copy diagnostic info**), which lists `is_purchasable / backorders_allowed / subscribers` per OOS product so you can see exactly how WooCommerce treats each item.

== Screenshots ==

1. Waitlist form on a WooCommerce out-of-stock product page.
2. Subscriber management screen in the admin with filters, statuses and CSV export.
3. Notification email delivered when the product is back in stock.

== Changelog ==

= 1.0.10 =
* Fix: shop managers can now save the Settings tab (saving was refused with a permissions error).
* New: WT Waitlist moved into the WooCommerce menu as one page with Subscribers, Settings and a new About tab (version, links, changelog). A shortcut is also in the shared Webmasters.Team menu, which lists our other plugins. Old page addresses redirect.

= 1.0.9 =
* **Privacy fix: notification content no longer leaks into other emails.** After a back-in-stock notification was sent, its plain-text version (including the customer’s personal unsubscribe link) was attached to other emails the site sent later in the same request, for example WooCommerce emails sent in the same background run. Later back-in-stock notifications were not affected (each one replaced it with its own content). The plain-text part is now limited to the notification it belongs to.
* Fix: database table changes are now applied when the plugin is updated, not only on activation. On multisite with network activation every site now gets its own tables on first use.
* Fix: deactivating or deleting the plugin now cancels its pending notification jobs (Action Scheduler and WP-Cron). Deleting the plugin also removes its temporary rate-limit data and cleans up every site of a multisite network.
* Fix: the purge of old subscriptions also removes their email log entries.
* Fix: removed an early translation loading notice (`_load_textdomain_just_in_time`) caused by scheduling the daily purge too early.
* Compatibility: declared compatibility with WooCommerce High-Performance Order Storage and the Cart and Checkout blocks; WooCommerce is now declared as a required plugin. Tested with WooCommerce 11.2.
* Developer: internal classes renamed (PHP namespace `Webmasters\Waitlist` changed to `WebmastersTeam\Waitlist`); old names kept as aliases for this release. Option, table, hook and filter names are unchanged.
* New: on PHP older than 8.2 administrators see a notice (Dashboard, Plugins screen and the plugin’s own pages, dismissible for 30 days) and a Site Health recommendation: version 1.1.0 will require PHP 8.2. No external requests are made for this.
* Translations: English source strings; Polish translation included.

= 1.0.8 =
* Fix: raised the minimum PHP version to 8.1 to match the code (the plugin uses readonly properties, introduced in PHP 8.1). This prevents a fatal error if the plugin is activated on PHP 8.0.

= 1.0.7 =
* Compatibility: tested up to WordPress 7.1.

= 1.0.6 =
* **Security: hardened client-IP detection.** The signup rate-limiter and the GDPR consent log previously trusted the `X-Forwarded-For`, `CF-Connecting-IP` and `X-Real-IP` headers, which are spoofable on sites that are not behind a trusted reverse proxy, allowing the rate limit to be bypassed and the recorded `consent_ip` to be forged. The client IP is now read from `REMOTE_ADDR` by default; proxy headers are honoured only via the new `wt_waitlist_trust_proxy_headers` filter.
* Security: the CSV subscriber export now neutralises formula-injection payloads. Values starting with `=`, `+`, `-`, `@`, TAB or CR are prefixed with an apostrophe so a crafted e-mail address submitted through the public form cannot execute as a formula when the export is opened in Excel or LibreOffice.
* Compatibility: pass an explicit escape character to `fputcsv()` to avoid the PHP 8.4 deprecation notice.

= 1.0.5 =
* Polish: friendlier empty state on the Subscribers admin screen with a hint pointing users to the shortcode fallback when the signup form does not appear automatically.

= 1.0.4 =
* **Fix: signup form rendered twice** on most themes. Two of the rendering layers shared one callback that did not check the “already rendered” guard, so the form appeared duplicated. Resolved, the form now renders exactly once.
* **Fix: signup form not appearing on variable products** when a selected variation is out of stock or on backorder. The variation-aware JavaScript relied on `is_in_stock`, which is `true` for `onbackorder` variations. The plugin now tags each variation server-side (`woocommerce_available_variation`) with an explicit waitlist flag, so the form shows correctly for every non-`instock` variation.
* New: **Editable signup form text**. The form heading, description, button label and success message can now be customized under WT Waitlist → Settings → “Signup form text”. Leave a field empty to keep the built-in translated default.
* i18n: translated the last two hardcoded Polish fallback strings in the frontend script (connection / generic error messages).

= 1.0.3 =
* **Fix: signup form was not appearing on backorder-allowed products.** WooCommerce has three stock statuses (`instock`, `outofstock` and `onbackorder`), and `WC_Product::is_in_stock()` returns `true` for both `instock` and `onbackorder`. The previous version’s check `!is_in_stock()` therefore skipped backorder products entirely. We now use `get_stock_status() !== 'instock'` so the form is rendered (and accepts signups) for both true-OOS and backorder products. This is the primary fix in 1.0.3 and is strongly recommended for any store using WooCommerce backorders.
* New: **Two additional rendering layers for theme compatibility**, `woocommerce_after_single_product_summary` (Layer 4) and `wp_footer` + JS DOM injection (Layer 5). Fixes “signup form does not appear” on themes that bypass the standard WooCommerce summary hooks (Flatsome UX Builder, Avada Builder, Enfold, Porto and similar page-builder driven themes).
* New: **Sample out-of-stock products** section in System status. For the first 5 OOS products it shows `is_in_stock / is_purchasable / backorders_allowed / subscribers` so backorder vs. waitlist conflicts are immediately visible.
* New: **Theme compatibility hints**. System status now reports the active theme template slug and surfaces a known-incompatibility note for Flatsome / Avada / Enfold / Porto.
* i18n: **All user-facing strings translated to English** (admin Settings labels, signup form, error messages, email templates, unsubscribe page, admin list table). Polish-only release is over, and the plugin now ships in English by default.
* Docs: new FAQ entries covering Flatsome/builder-theme rendering and backorder-vs-waitlist behavior.

= 1.0.2 =
* New: **System status** panel inside WT Waitlist → Settings with versions, DB tables, subscription counts, Action Scheduler state and recent email log at a glance.
* New: **Send test email to admin** button for one-click verification that `wp_mail()` works on this site (surfaces PHPMailer error string on failure).
* New: **Copy diagnostic info** button that generates a paste-ready support snippet so you do not have to copy values by hand.
* New: **Quick start** panel at the top of the settings page explaining the out-of-stock → form → Action Scheduler → email flow in four lines.
* Tweak: fixed `WT_WAITLIST_VERSION` constant that still reported `0.1.0` in cached environments.

= 1.0.1 =
* Fix: replaced inline `<style>` block in the unsubscribe page with `wp_register_style()` + `wp_print_styles()` (WP guideline compliance).

= 1.0.0 =
First public release.

* Waitlist form on product pages (simple + variable), classic and block theme support.
* Custom database tables (`{prefix}wt_waitlist_subscriptions`, `{prefix}wt_waitlist_email_log`).
* Stock-status change detection through WooCommerce hooks.
* Notification queue through Action Scheduler with 5 min throttle and batches of 50.
* One-click unsubscribe with SHA-256 token.
* Admin panel: subscriber list with filters and CSV export.
* Product-edit widget showing waitlist count.
* Integration with WordPress Privacy Tools (export + eraser).
* Shortcode `[wt_waitlist]` as fallback.
* Honeypot plus rate limiter (10/hour per IP).
* Automatic purge of old subscriptions (configurable retention).

== Upgrade Notice ==

= 1.0.10 =
WT Waitlist now lives in the WooCommerce menu (Subscribers, Settings and About tabs). Old page addresses redirect.

= 1.0.9 =
Privacy fix: a notification’s plain-text content, including the customer’s unsubscribe link, could be attached to other emails sent afterwards. Also applies database updates on plugin update and fully cleans up scheduled jobs on deactivation and deletion. Recommended for all users.

= 1.0.8 =
Requires PHP 8.1 or newer (the plugin already used PHP 8.1 features; this corrects the declared minimum).

= 1.0.7 =
Confirmed compatible with WordPress 7.1. No code changes.

= 1.0.6 =
Security fix: signup rate-limiting and the stored GDPR consent IP no longer trust spoofable proxy headers by default, and the CSV export is protected against formula injection. Behind Cloudflare/nginx? Enable the new `wt_waitlist_trust_proxy_headers` filter. Recommended for all users.

= 1.0.5 =
Friendlier empty state on the Subscribers admin screen. No behaviour or data changes.

= 1.0.4 =
Fixes the form rendering twice and the form not appearing on out-of-stock variable products. Adds editable form text. Recommended for all users on 1.0.3.

= 1.0.3 =
Critical fix: the signup form is now rendered for products on backorder (previously skipped), plus extra rendering layers for Flatsome / Avada / Enfold / Porto themes, sample-OOS-products diagnostics and full English UI. Strongly recommended upgrade.

= 1.0.2 =
Adds a System Status panel and a Send-test-email button to Settings, making it much easier to troubleshoot “form does not appear” or “emails not arriving” without leaving WP Admin.

= 1.0.1 =
Compliance fix for the unsubscribe page styling. No behavior changes.

= 1.0.0 =
First public release. Requires WooCommerce ≥ 7.0 and PHP 8.0+.
