=== Camerce Popup Campaigns – Exit Intent, Email Opt-in & Announcement Bar ===
Contributors: camerce
Tags: popup, exit intent, email opt-in, newsletter, announcement bar
Requires at least: 6.7
Tested up to: 7.1
Requires PHP: 8.1
Stable tag: 1.0.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Popups, slide-ins and bars that work behind a page cache. Exit intent, scroll and schedule triggers, with conversion reporting built in.

== Description ==

Camerce Popup Campaigns is a WordPress popup plugin for exit-intent popups, email opt-in forms, slide-ins and announcement bars. Build a campaign in a visual builder, choose when it appears, and watch the conversions — without an account, an impression limit, or a third-party service.

It is also built to survive a full-page cache, which most popup plugins are not. More on that below.

**[See every format running on the live demo site](https://demo.camerce.com/popup-campaigns/)**

= Features =

* **Visual popup builder** — headings, text, images, buttons and opt-in forms, arranged on a live preview
* **Five templates** and eight block patterns to start from
* **Email opt-in and lead capture** — submissions stored in your own database, exportable as CSV
* **Conversion analytics** — views, interactions, conversions and rate, per campaign and per day
* **Spam protection** — honeypot, timing check, per-IP rate limiting and email validation
* **Block editor support** — opt-in form, dismiss button and countdown blocks
* **No impression limits, no account, no phoning home**
* **Lightweight** — about 10.4 KB of JavaScript and 3.5 KB of CSS, gzipped, with no jQuery

= Campaign formats =

* **Centre modal** — the classic dialog over a dimmed page
* **Announcement bar** — a slim notification bar pinned to the top or bottom
* **Slide-in** — a small card in a corner, less interruptive than a modal

= Popup triggers =

Exit intent, time on page, scroll depth, click on any CSS selector, element scrolling into view, visitor inactivity, and manual opening from a link or your own JavaScript. Every trigger is in the free plugin.

= Targeting and scheduling =

Show a popup everywhere, or on a specific page, post type, taxonomy term, URL pattern, the front page, search results, archives or a 404. Narrow it further by login state, user role, device, new or returning visitor, referrer and UTM parameters. Schedule a campaign to a date range, particular weekdays, or a time of day.

Frequency is yours to set: always, once ever, once per session, or once every few days — and you can stop showing a campaign once someone converts or dismisses it.

= It works behind a page cache =

Most popup plugins decide who sees what in PHP while the page is being built. That makes the HTML different for every visitor, which is exactly what a full-page cache cannot store.

This plugin splits the decision in two. Page rules run on the server and cache along with the page. Visitor rules run in the browser from inert JSON. A cached page and an uncached page behave identically, and you do not need to exclude anything from your cache.

If no campaign targets the current URL, the plugin outputs **nothing at all** — no stylesheet, no script, no empty container.

= Camerce Popup Campaigns Pro =

The free plugin is the whole engine, not a sample of one. Pro adds the full-screen mat and inline embed formats, a countdown element, more builder elements and templates, email provider integrations (Mailchimp, Brevo, ConvertKit and others), and advanced analytics.

**[See what Pro adds](https://camerce.com/plugins/popup-campaigns/)**

== Installation ==

1. Install and activate the plugin.
2. Open **Camerce Popup Campaigns** in the admin menu and press **New campaign**.
3. Pick a template, or start from scratch and add elements.
4. Set the format, trigger, targeting and frequency in the panel on the right.
5. Press **Save**, then switch the campaign from **Draft** to **Live**.

Requires WordPress 6.7 or newer and PHP 8.1 or newer. If your site does not meet those, the plugin will not activate rather than failing halfway.

No configuration is required. There is no account to create, no API key to enter, and nothing is contacted on the internet unless you connect an email provider yourself.

== Frequently Asked Questions ==

= Is it accessible? =

Campaigns render as real dialogs with a focus trap, an accessible name, and Escape to close. The close control keeps a 44px hit area for touch, and its focus ring is keyboard-only. Animations respect `prefers-reduced-motion`. Colour is never the only carrier of state.

A visitor always has a way out: if a campaign that behaves as a dialog is saved with the close button, Escape and overlay-click all disabled, the close button is restored automatically rather than leaving a dialog nobody can dismiss.

= How do I get leads out of it? =

Leads are stored in your own database and exported to CSV from Camerce Popup Campaigns → Leads.

For anything more automatic, the free plugin fires a `camerce_popups_lead_captured` action on every submission, with the lead and the campaign ID, so you can deliver leads wherever you like in a few lines of code.

Ready-made connections to Mailchimp, Brevo, MailerLite and FluentCRM, plus a generic webhook, are part of the paid add-on, together with the settings screen that stores their keys and checks that they work. The free plugin makes no request to any of them.

= What is there for developers? =

* Around 35 filters covering settings, targeting, markup, templates, fonts, rate limiting, lead sync and provider payloads
* `wp camerce-popups` WP-CLI commands for listing campaigns, exporting leads and rebuilding the rules bundle
* Template library extendable through `camerce_popups_template_directories`
* Add-ons contribute formats, elements, containers and templates through `camerce_popup_types`, `camerce_popups_element_types`, `camerce_popups_container_types`, `camerce_popups_element_markup` and `camerce_popups_template_directories`
* Developer documentation at https://camerce.com/plugins/popup-campaigns/

= Does it work with my cache plugin? =

Yes, that is what it was built for. Page rules are decided in PHP and cache with the page; visitor rules are decided in the browser. You do not need to exclude any page from your cache, and you do not need to configure anything in your caching plugin.

= Does it slow my site down? =

On a page with no matching campaign, Camerce Popup Campaigns adds nothing whatsoever — no CSS, no JS, no markup. On a page with one, it is roughly 10 KB of JavaScript and 3.5 KB of CSS, gzipped, with no jQuery and no external requests.

= Is it GDPR friendly? =

It stores no IP addresses and no user agents. Analytics rows hold a hashed session identifier, the page path with the query string, a device bucket and a timestamp. Retention is configurable and defaults to 30 days for events and 90 days for daily totals.

Two things to be aware of: leads you collect are personal data you are responsible for, and a campaign using a Google font causes the visitor's browser to contact Google. The font default is "inherit from the theme", which contacts nobody.

= Can I use my own form plugin? =

Yes. Drop your form's shortcode into a campaign. Camerce Popup Campaigns detects a successful submission from several popular form plugins and can close the campaign and record a conversion.

= What happens to my data when I uninstall? =

By default, nothing is deleted. Deleting the plugin leaves your campaigns, leads, events, totals, options and capabilities exactly as they were, so uninstalling to test a conflict never costs you your history.

For a clean removal, tick "Delete everything when Camerce Popup Campaigns is uninstalled" in Camerce Popup Campaigns → Settings first. With that on, deleting the plugin removes every campaign and its meta, drops the three tables, deletes the `camerce_popups_*` options and transients, clears the cron job, and strips the capabilities it added. On multisite the setting is per site.

= Why does the free version include exit intent? =

Because a popup plugin that cannot decide when to appear is a demo, not a tool. Triggers and targeting are the plugin. The paid add-on adds formats, templates, elements and reporting depth on top of a free version that is complete on its own.

= Will popups stack on top of each other? =

No. One campaign shows at a time. When several match the same page, the highest priority wins, and ties go to whichever was published first.

= Does it need jQuery, or load anything from a CDN? =

No jQuery, and nothing from a CDN. The only outbound requests the plugin can make are to an email provider you have connected, and to Google Fonts if a campaign explicitly uses one.

= Where are leads stored, and can I export them? =

In a table in your own database. Camerce Popup Campaigns → Leads lists them with search and paging, and exports to CSV.

= How do I open a popup from a button or a link? =

Set the trigger to **Manual**, then link to `#camerce-popups-123`, or add `data-camerce-popups-open="123"` to any element, where 123 is the campaign ID.

= Can I show a campaign in the middle of a post instead of over it? =

Not with this plugin. Its three formats all appear over the page. Placing a campaign inside the content instead is one of the things the separate paid add-on does; this plugin has no code for it.

= Does it work on multisite? =

Yes, as a per-site plugin. Each site has its own campaigns, tables, settings and capabilities, and the uninstall setting is decided per site.

= Something is not appearing. How do I debug it? =

Camerce Popup Campaigns → Settings has a diagnostics panel that checks the tables, the cron job, the rules bundle and the REST routes. The most common causes are a campaign left as a draft, a frequency cap already met in your browser, or an exclusion rule matching the page.

== Free and paid ==

The free plugin has no impression limit, no watermark, no account, no trial and no feature that stops working. Nothing in it is locked or waiting to be paid for: the paid features are not in this plugin at all, they are a separate add-on that installs alongside it.

**In the free plugin**

* Three formats: centre modal, notification bar, slide-in
* Nine builder elements: heading, text, image, button, form, list, divider, spacer, dismiss link
* Five templates: newsletter signup, exit-intent discount, announcement bar, cookie consent, welcome slide-in
* Eight block patterns, and the form, dismiss and countdown blocks for the block editor
* Every trigger, including exit intent, scroll depth, element visible, inactivity and click
* The full targeting engine: page rules, visitor rules, frequency caps, scheduling, priority
* Conversion analytics, lead capture and CSV export
* A `camerce_popups_lead_captured` action, so leads can be delivered anywhere you can write a hook for
* Google Fonts and system font stacks

Triggers and targeting are deliberately not split. A popup that cannot decide who sees it is not a popup, and gating exit intent — the feature nearly every other plugin puts behind a paywall — would make the free version a demo rather than a tool.

**In the separate paid add-on**

A different plugin, installed alongside this one. None of its code is in this plugin, so nothing here is disabled, limited or waiting to be unlocked.

* Two more formats: full-screen mat and inline embed
* Four more builder elements: countdown, multi-column row, badge, custom HTML
* Fifteen more templates, including the image-led designs
* Nine email provider integrations: Mailchimp, Brevo, MailerLite, Kit, Klaviyo, ActiveCampaign, HubSpot, FluentCRM and a generic webhook, with a screen to connect them

Planned for it, and not written yet: A/B testing, advanced analytics, WooCommerce targeting, revenue attribution and multi-step campaigns. Treat those as a plan and not a promise.

A campaign built with the add-on keeps its saved settings if the add-on is later removed. Nothing is rewritten or deleted: the format and the elements stay in the database exactly as you left them, and reinstalling the add-on brings the campaign back looking as it did. While the add-on is gone, a campaign using one of its formats has no styling for that format, because the code for it lives in the add-on and not here.

Note that the countdown exists twice: as a **block** for the block editor, which is in the free plugin, and as a **builder element**, which is part of the add-on.

This plugin offers three formats and implements exactly those three. It carries no code for the add-on's formats or elements -- not disabled, not hidden, simply absent -- which is why removing the add-on takes its formats with it rather than leaving a half-working copy behind.

== External services ==

This plugin can connect to third-party services. None of them is contacted
unless you switch it on, and none of them is used on a default install.

**Google Fonts** — https://fonts.googleapis.com and https://fonts.gstatic.com

A campaign only requests a Google font if you pick one for that campaign; the
default font setting is "inherit from the theme", which sends no request at
all. When a campaign that uses a Google font renders on a page, the visitor's
browser loads one stylesheet from fonts.googleapis.com and the font files it
names from fonts.gstatic.com. That request carries the visitor's IP address and
user agent to Google, as any browser request does. If that matters for your
jurisdiction, leave the font set to "Theme font" or "System font", which are
served entirely from the visitor's own device.

Google Fonts terms: https://developers.google.com/fonts/terms
Google privacy policy: https://policies.google.com/privacy

**Email marketing providers** — not part of this plugin

This plugin sends leads nowhere. There is no provider code in it, no screen to
enter an API key, and no outbound request: a captured lead is written to your
own database and stays there unless you write a hook to move it.

Connecting Mailchimp, Brevo, MailerLite, Kit, Klaviyo, ActiveCampaign, HubSpot,
FluentCRM or a webhook is done by a separate add-on plugin, which carries its
own code and its own documentation of those services. Nothing in this plugin
waits on it.

== Screenshots ==

1. The campaign builder. Elements on the left, a live preview in the middle, and the design panel on the right. Three formats, nine elements, and every setting on one screen.
2. Five templates to start from, filtered by what you are trying to do. Every word, colour and setting stays editable afterwards.
3. Page targeting. These rules run on the server, so they are cached along with the page and never break a full-page cache.
4. Every campaign on the site, with views, conversions and the rate for each.
5. An exit-intent modal on the front end, shown when the pointer heads for the browser chrome.
6. An announcement bar pinned to the top of the window, sitting below the WordPress admin bar.
7. Conversion analytics, with no third-party service involved.
8. Leads collected by your forms, stored in your own database and exportable as CSV.

== Changelog ==

= 1.0.0 =

First public release.

* Visual campaign builder with thirteen element types, drag-to-reorder layers, undo and redo, device previews and a resizable settings panel
* Three campaign formats in the free plugin: centre modal, slide-in and notification bar. Full-screen mat and inline embed are part of the paid add-on
* Seven triggers: time on page, scroll depth, exit intent, click, element visible, inactivity and manual
* Server-side page targeting: post, post type, taxonomy, URL pattern, front page, blog index, search, archive and 404, with exclusions
* Browser-side visitor targeting: login state, role, device, new versus returning, referrer and UTM
* Frequency caps counted per browser, with stop-on-conversion and stop-on-dismiss
* Scheduling by date range, weekday and time of day, plus priority for overlapping campaigns
* Twenty templates, five of them in the free plugin, and eight block patterns
* Three blocks for the block editor: opt-in form, dismiss button and countdown
* Lead capture with honeypot, timing check, rate limiting and email validation, plus streaming CSV export
* Conversion analytics with nightly aggregation into a daily totals table
* A `camerce_popups_lead_captured` action for delivering leads onward; the provider integrations themselves are part of the paid add-on
* Eighteen font choices: the theme font, three system stacks and fourteen Google fonts, loaded only when a campaign asks for one
* Cache-safe two-layer rendering, and zero output on pages with no matching campaign
* Fully translatable, with around 35 filters and a `wp camerce-popups` WP-CLI command

== Upgrade Notice ==

= 1.0.0 =

First public release.

== Privacy ==

**What Camerce Popup Campaigns stores, and where**

`{prefix}camerce_popups_events` — one row per analytics event. Columns: campaign ID, variant label, event type (`view`, `interaction`, `conversion` or `close`), a hashed session identifier, the page path, a device bucket (`desktop`, `tablet` or `mobile`), and a timestamp. There is no IP address column, no user-agent column and no user-ID column. The submitted URL is reduced to its path and query before storage — scheme, host, credentials and fragment are discarded. Retention is configurable and defaults to 30 days.

`{prefix}camerce_popups_stats_daily` — one row per campaign, variant and date, holding four counters. It contains no visitor data of any kind. Retention is configurable and defaults to 90 days.

`{prefix}camerce_popups_leads` — one row per form submission: campaign ID, email address, name, the remaining submitted fields as JSON, the page the form was submitted from, a status and a sync status, and a timestamp. This is the only Camerce Popup Campaigns table that contains personal data, and it exists only because a visitor typed the data into a form. Storage can be switched off per campaign, in which case the submission is handed to your email service and never written locally.

Post meta prefixed `_camerce_popups_` holds campaign settings and the compiled rule bundle. Options prefixed `camerce_popups_` hold plugin settings, the autoloaded targeting index and the schema version. The free plugin stores no email-service credentials, because it connects to no provider. The paid add-on keeps them in the `camerce_popups_integration_keys` option rather than post meta, which is exposed through revisions, exports and the REST API in ways an API key should not be, and never sends a raw credential to the browser.

**What is hashed**

The session identifier. The browser generates a random token, keeps it in `sessionStorage`, and sends it with each event; the server hashes it again with your site's authentication salts before storing it, so the value in the table is meaningless outside your install.

The rate limiter's client key. The visitor's IP address is read, hashed with your site's salts, truncated, and used as a transient name. The address is not stored, not logged, and not returned to anything.

**What is never stored**

IP addresses, user agents, WordPress user IDs, referrer strings, and UTM values. The referrer and the UTM parameters are read in the browser to decide whether a campaign is eligible, and are never sent to the server.

The visitor's device class is the one exception, and it is deliberate. It is resolved in the browser — the server's copy of the page is cached and shared, so it cannot sniff a user agent for this — and it travels with each analytics event so that reporting can tell mobile from desktop. What reaches the table is one of three words, `desktop`, `tablet` or `mobile`, and nothing finer.

**Telemetry**

There is a "Share anonymous usage data" setting. It is off by default and only ever on if you switch it on. Version 1.0.0 ships no code that transmits usage data, so nothing is sent whether the box is ticked or not.

The plugin makes no other outbound request of its own: no update check of its own, no licence check, no CDN asset, and no font or script from a third-party host.

**External services**

The only third-party host this plugin can reach is Google Fonts, and only when
a campaign is set to use a Google font. See the "External services" section
above for what that sends and how to avoid it.

Leads are never transmitted. They are written to a table in your own database
and read back only by you.
