Gatilab Products

Changelogs

Improvements, fixes, and the details behind each release.

All Products

Release history across our plugins and themes.

Beta

Core Forms 4.2.0-beta8

Read release notes

Pre-release. Builds on v4.2.0-beta7.

What changed

Rewrote the Analytics + Revisions tabs to use standard WordPress admin chrome instead of custom card panels. They now look at home in the WP admin alongside the rest of the post-edit screens.

Native patterns now used

  • `.postbox` / `.postbox-header` / `.hndle` / `.inside` containers — the same primitives WP uses for dashboard widgets and post-edit metaboxes.
  • `.wp-list-table widefat striped` for the field-interactions, top-sources, and revisions history tables.
  • `.tablenav top` for the analytics date-range toolbar.
  • `.row-actions` (View diff | Restore) on revision rows, mirroring the Posts list pattern.
  • `.notice notice-info inline` / `.notice notice-warning inline` for empty states.
  • `.column-primary` + `data-colname` on revision rows for native responsive behavior.
  • Native `.button` / `.button-primary` for date-range presets.

Plugin polish kept

Only the bits that genuinely have no native equivalent: - Severity-tinted left-bar recommendations (a list of typed alerts has no built-in WP component). - The daily-activity SVG line chart. - A small "Live" pill badge on the current-version revision row.

No more rounded custom panels, no oversized stat numbers, no plugin-flavored colors. The tabs blend with the rest of WP admin.

Deployed and verified

Live on a test install — plugin active at 4.2.0-beta8, frontend 200.

Carries over everything from v4.2.0-beta7 (WSOD fix, analytics/revisions bugfixes, security hardening, WhatsApp, CF7 migration).

Beta

Core Forms 4.2.0-beta7

Read release notes

Pre-release. Hot-fix for the WSOD that affected beta5 and beta6.

What was wrong

Beta5 added an init-action callback registered as `'Core_Forms\_cf_wire_revisions'`. But `src/functions.php` doesn't declare a namespace — every function in it (`cf_get_form`, `cf_get_settings`, `_cf_wire_revisions`, etc.) lives in the global namespace despite the `use Core_Forms\Form;` at the top of the file.

The namespaced callback string resolved to nothing, and at init time WordPress fataled with \"function not found\" — taking the admin (and depending on cache state, the whole site) down.

Beta6's `function_exists('add_action')` guard helped CLI tools (PHPUnit, PHPStan, dump-autoload) load `functions.php` without fataling, but never addressed the runtime fatal. Sites running beta5 or beta6 still saw the WSOD.

What's fixed

The init callback is now registered as plain `'_cf_wire_revisions'`.

Verified on a live install: - Plugin active at 4.2.0-beta7 - Frontend `200` - Admin `302` (login redirect — no fatal) - `php -r 'require autoload.php; var_dump(function_exists(\"_cf_wire_revisions\"));' → bool(true)`

Upgrade strategy

If you tested beta5 or beta6 and hit a blank screen, this is your fix. Download the attached zip and upload via Plugins → Add New → Upload — WordPress will replace your existing install.

Everything else from the beta5/beta6 release notes (analytics tab, revisions tab, CF7 migration, WhatsApp actions, security hardening) is intact and unchanged.

Beta

Core Forms 4.2.0-beta6

Read release notes

Pre-release. Critical fix on top of v4.2.0-beta5.

⚠️ Critical — fixes a WSOD some users hit on beta5

beta5 added a top-level `add_action()` call at the bottom of `src/functions.php` that fired during composer's files-autoload step. Inside a WordPress request that was fine — `plugin.php` (which defines `add_action`) loads before plugins. But outside WP — PHPUnit, PHPStan, custom CLI tools, or any other plugin that requires our `vendor/autoload.php` before WP's plugin loader runs — `add_action` was undefined and the plugin fataled at autoload, taking the whole site down.

The registration is now wrapped in `function_exists( 'add_action' )` so `functions.php` is safe to load standalone.

This was the root cause of the WSOD reported on test sites running beta5.

Analytics tab bugfixes

  • Field-interactions table reads the `interactions` column the Dashboard actually returns (was reading `count`, so every row showed 0).
  • Top-sources panel maps the Dashboard's `{utm_sources, referrers}` shape correctly (was reading the non-existent `top_sources`). Falls back to referrer-domain rows when no UTM tags are in use.
  • Recommendations substitute the `value` (e.g. completion-rate %) into the printf-style `message` template instead of showing the literal `%s%%`.

Revisions tab bugfixes

  • Restore redirects back to Core Forms admin (`?page=core-forms&view=edit&form_id=X&tab=revisions&restored=1`) instead of WordPress's native post.php editor, which doesn't render Core Forms' custom UI. A success flash confirms the restore.
  • Meta-only saves create revisions. `wp_save_post_revision_check_for_changes` is set to `false` for `core-form`, so settings-only or messages-only saves snapshot a revision instead of being silently dropped by WP's content-diff check.
  • Revision-meta filter is now version-agnostic. `wp_post_revision_meta_keys` passes the parent in WP 6.4+ but earlier patches may have passed the revision; the callback resolves a parent id defensively so the check matches on both.

Build pipeline

  • Regenerated `phpstan-baseline.neon` (previous baseline was stale after the beta CSRF default flip — `enable_nonce` shape change broke ignore patterns).
  • Build script now exits clean: 148 unit tests pass, PHPStan analyse clean.
  • Production zip (738KB) ships without phpstan, phpunit, tests, or composer.json — all dev deps stripped via `composer install --no-dev` in the build step.
  • Use `bash build.sh` for releases. Never rsync the dev vendor directly — that's how dev-only `autoload_files` entries (phpstan/bootstrap.php) ended up in the deployed autoloader earlier today.
Beta

Core Forms 4.2.0-beta4

Read release notes

Pre-release. Quick UI consistency fix on top of v4.2.0-beta3.

What changed

Removed the `body.toplevel_page_core-forms .notice` CSS override in `design-system.css` that gave admin notices on Core Forms screens a rounded 4px-left-bar look. Notices now match WordPress's native styling exactly — squared corners, native left border, no plugin-flavored variation.

The Import page's custom `.cf-import__flash` flash variants were also swapped for native `.notice .notice-success` / `.notice-warning`, and the orphaned CSS rules removed.

Carries over everything from v4.2.0-beta3 (WhatsApp picker, headless referer fix, Import page redesign, prominent Display mode card).

Beta

Core Forms 4.2.0-beta3

Read release notes

Pre-release. Iterating on v4.2.0-beta2 based on tester feedback.

What changed

WhatsApp actions now show in the per-form Add Action picker

All three v4.2.0 WhatsApp actions (Cloud API, Twilio WhatsApp, Click-to-WhatsApp redirect) live under a new Messaging & SMS category alongside Twilio SMS. They show up regardless of whether global WhatsApp Cloud credentials have been configured — credentials can be filled per-action when no global ones exist. Previously the picker only surfaced actions present in the static category map, so the three new actions were silently hidden.

Headless [CF_REFERRER_URL] etc. now resolve correctly

The headless submit endpoint now reads _cf_page_url from the JSON body and substitutes it into $_SERVER['HTTP_REFERER'] for the duration of Forms::process(). The bundled embed.js automatically forwards window.location.href; the native-fetch snippet in the Headless tab now shows this pattern too.

Cross-origin Referer headers are stripped to the bare origin (https://example.com) by browsers under the default strict-origin-when-cross-origin policy, so without this [CF_REFERRER_URL], [CF_REFERRER_HOST], [CF_REFERRER_PATH], and [CF_POST_ID] were resolving to the WP REST endpoint's own URL on every embed.

Import page redesigned

  • Renamed in the sidebar from "Import (CF7)" to Import — this page will house migration paths from multiple form plugins.
  • New source-picker card grid: Contact Form 7 (active), plus Gravity Forms, WPForms, Fluent Forms, Ninja Forms, and Formidable as visibly muted "Coming soon" cards.
  • CF7 form list rebuilt as a custom card-style layout below the picker rather than a wp-list-table.
  • Replaced the WP-native red-bar notice-error with a clean inline empty state.

Typeform-style mode is prominent

Promoted from a buried row inside the "Add-ons" accordion at the bottom of the Settings tab to a dedicated Display mode card at the top, with a marketing-style pitch panel explaining what Fullscreen mode is for. Card auto-opens when the form is already in fullscreen.

Carried over from earlier betas

See v4.2.0-beta for the WhatsApp / CF7 migration / 14 security fixes changelog, and v4.2.0-beta2 for the new Core Forms icon.

Beta

Core Forms 4.2.0-beta2

Read release notes

Pre-release. Builds on v4.2.0-beta.

What's new

Replaced the abstract circular "C" admin-menu icon and the generic clipboard block-inserter icon with pen-field from Font Awesome Pro 7.2 — a square frame containing an input row and a pen.

Single visual language across every touchpoint:

  • WP admin sidebar menu
  • Gutenberg block inserter
  • In-editor Placeholder state (shown when no form is selected)

viewBox is normalised to a square 640×640 so the natural 640×512 glyph reads balanced at small render sizes. Menu fill stays black so WordPress's color-scheme stylesheet can tint it for active/hover states; block icons use currentColor for Gutenberg's hover/selected states.

Carried over from v4.2.0-beta

  • WhatsApp Cloud API, Twilio WhatsApp, and Click-to-WhatsApp redirect actions
  • CF7 → Core Forms migration tool (forms + messages, with preview)
  • WhatsApp activity logging through Email Logs
  • 14 security audit findings fixed (CSRF default-on, headless API key header-only, headless CORS allowlist, webhook SSRF guard, email header CRLF injection, custom CSS </style> escape, polls/drafts hardening, file upload blocklist extended, honeypot returns opaque success, and more)

See v4.2.0-beta release notes for the full v4.2.0 changelog.

Beta

Core Forms 4.2.0-beta

Read release notes

Pre-release. Distribution channel for testers before 4.2.0 GA.

WhatsApp, three ways

  • WhatsApp Cloud API action. Meta Graph API. Template + free-form modes. Global creds (Settings → WhatsApp Cloud API) with per-action override. Includes a Test connection button that pings /v21.0/{phone_number_id} and reports verified business name + quality rating.
  • Twilio WhatsApp action. Reuses your existing Twilio SID/token; posts to the whatsapp: channel. Optional Content Template SID for business-initiated outreach.
  • Click-to-WhatsApp redirect. Generates a wa.me deep link with the submission pre-filled into the chat draft and injects it as the post-submit redirect.

The cf_form_redirect_url filter now fires on every successful submission (not just when a per-form Redirect URL is set) and receives the Submission as a 4th argument. Existing 3-arg handlers keep working.

Contact Form 7 → Core Forms migration

New page at Core Forms → Import (CF7). Scans every wpcf7_contact_form post and converts: - Shortcode markup → plain HTML (text, email, tel, url, number, date, hidden, textarea, select, checkbox, radio, acceptance, file, submit, recaptcha) - CF7 mail template → Core Forms Send Email action - _messages meta → cf_message_* post meta (so your custom success/validation/spam copy carries over)

Each row in the importer table has a "Preview converted HTML" disclosure so you can sanity-check before committing. CF7 stays installed and untouched — run both side-by-side during cutover. Flamingo submission import is out of scope.

Operational polish

  • WhatsApp activity in Email Logs. Both Cloud API and Twilio sends log through the existing cf_email_logs table. The Type column labels them; Resend is suppressed for non-email rows (which would otherwise try to wp_mail a phone number).
  • cf_log_email() now switches between sanitize_email and sanitize_text_field based on action type.

Security hardening (14 audit findings fixed)

  • CSRF default flipped on. New installs get enable_nonce=1. Existing installs are preserved at 0 via a 4.2.0 migration that writes the prior value explicitly into stored settings. Sites that have always cached aggressively can keep nonces off; sites that haven't been should turn them on.
  • Submission mass-assignment closed. Forms::process() intersects POST keys with field names declared in the form markup. Filterable via cf_allowed_field_names.
  • Headless API key is header-only. The ?cf_key= query-string fallback is gone (leaked into logs/history/Referer).
  • Headless CORS allowlist. New textarea at Settings → Headless → Allowed origins. Empty preserves 4.1 behavior with an admin warning.
  • Workflow /actions/test requires manage_options. No longer a wp_mail / wp_remote / paid-SMS-anywhere primitive in editor-level hands.
  • Webhook SSRF guard. New cf_is_safe_outbound_url() helper rejects loopback, RFC1918, link-local, IPv6 unique-local, and non-HTTP schemes. Webhook auth header name/value also strip CR/LF.
  • Email header CRLF injection closed. New cf_esc_email_header() sanitizer replaces strip_tags on every header substitution in Email, Emailit, and Autoresponder actions.
  • Custom CSS </style> escape closed. The previous regex pattern didn't consume the closing >. Now strips every < from custom CSS at both save and render time.
  • process_save_form gains inline cap+nonce check (defense in depth — matches the existing guard on process_create_form).
  • Two ghost wp_ajax_* handlers removed (registered methods that didn't exist; calling them returned a fatal).
  • Polls trust REMOTE_ADDR only. HTTP_CLIENT_IP and HTTP_X_FORWARDED_FOR no longer honored by default. Opt back in via cf_poll_trust_forwarded_for for sites behind a trusted reverse proxy.
  • Draft saves rate-limited to 30 new drafts per IP+form per minute (filterable via cf_draft_save_rate_limit).
  • File upload blocklist extended with html, htm, xhtml, xml, xsl, xslt. Closes a stored-XSS path where nginx (which ignores the uploads .htaccess) served uploaded HTML as text/html on the WP origin.
  • Honeypot returns opaque success on missing/populated field instead of WordPress's silent 0 response — bots can no longer distinguish "rejected" from "accepted but never processed."

Upgrade notes

  • Backwards-compatible: the CSRF migration only writes enable_nonce=0 into installs that had no explicit value before, so existing form-bearing cached pages keep working.
  • Behavior changes worth knowing:
  • Webhooks pointing at 127.0.0.1, 192.168.*, 10.*, etc. will now fail. Local dev environments needing the loopback can override via the cf_is_safe_outbound_url filter.
  • Forms with JS-injected fields not present in the static markup will have those keys dropped. Use the cf_allowed_field_names filter to opt them in.
  • Editor-role users with edit_forms can no longer test arbitrary actions via the REST endpoint — they can still test by saving the action first.

Full commit: b37bfae

Stable

Core Forms 4.1.0

Read release notes

Core Forms 4.1.0 — stable

Five months of beta cooking, condensed into one release: payments on submission, headless / cross-origin REST, six new payment-ready templates, a full admin redesign, and a bootstrap hardening that protects the site during plugin upgrades.

Payments on submission

Five providers, all bundled, all webhook-verified.

  • Stripe Checkout — hosted Checkout Sessions, test + live keys side-by-side
  • PayPal — Orders API, fixed or donor-driven amounts via [field] references
  • Razorpay — UPI / cards / wallets via Razorpay Checkout
  • Polar.sh — Standard Webhooks signature verification
  • FluentCart — hosted-checkout handoff. Free + Pro, one-time + subscriptions. Address fields prefilled into FluentCart's checkout (and its tax engine). Order paid → submission paid via fluent_cart/order_paid_done; refunds round-trip.

New submission statuses (pending_payment, paid, payment_failed, refunded), atomic CAS on the paid-state transition so concurrent webhook retries can't double-fire the action loop, return-URL signing with 2-hour expiry to stop replay.

Headless / cross-origin REST

External sites — Astro, Next.js, Vue, Svelte, plain HTML — can now render and submit Core Forms over REST. Same validation, captcha, action loop and payment redirect machinery runs server-side; the headless site just submits JSON.

  • GET /wp-json/core-forms/v1/forms/{id} — schema + rendered HTML
  • POST /wp-json/core-forms/v1/forms/{id}/submit — runs the full pipeline
  • GET /wp-json/core-forms/v1/embed.js — drop-in vanilla JS widget
  • Single API key per site (Settings → Headless), per-form opt-in toggle
  • Snippets in three flavours: widget, native fetch, cURL — each pre-filled with your actual submit URL + active key
  • New "Keeping the API key private" guidance card with copy-paste server-side proxy templates for Cloudflare Workers / Pages Functions, Next.js App Router, and Astro server endpoints

Six new payment-ready starting points

Each ships with a provider preselected so the form lands one configuration step away from working:

| Template | Provider | Pricing | |---|---|---| | One-time payment | Stripe | Fixed $19 | | Donation | PayPal | Donor-entered via [AMOUNT] | | INR payment | Razorpay | Fixed ₹999 | | FluentCart product | FluentCart | Variation-driven, with address fieldset | | Subscription | FluentCart Pro | Variation-driven, recurring | | Event ticket | Stripe | Tier select drives [TIER] |

Templates registry gained settings and messages callbacks alongside markup. Every starting point — payment and non-payment — now ships with a context-appropriate success message ("Redirecting to PayPal…", "Booking received…") instead of the generic "We will be in touch soon."

Admin redesign

  • Per-form Settings, Messages, Payment, Headless tabs converted to native <details>/<summary> accordions with state badges (rate-limit count, schedule "window set", honeypot on/off, FluentCart variation price/type/subscription summary, custom CSS/JS indicators, headless On/Off, configured/not-configured per gateway).
  • Per-form Submissions list rebuilt: Inbox / Spam / All view tabs with live counts (no more bouncing to global Spam to triage one form), redesigned table with status badges (New / Read / Replied / Archived / Paid / Pending payment / Refunded / Payment failed / Spam), row stripes for new/spam, per-row actions, relative dates with absolute on hover, varied empty states per view.
  • All Submissions page rebuilt for WordPress 7.0: toolbar split into a filter card (form picker + date range + Filter + Clear) and a search card. Consistent control heights so taller WP 7.0 native dates don't stagger the row. Side-fix: filter inputs are now in their own GET form so form_id / date_from / date_to actually land in the URL — previous version had them inside the bulk-actions POST form so filtering silently did nothing.
  • WordPress Screen Options dropdown hidden across all Core Forms admin screens.
  • Persistent "Akismet spam protection is active" banner removed from form-edit pages — status info doesn't belong as a banner on screens unrelated to it.
  • New cf_get_form_submissions / cf_count_form_submissions is_spam argument; new bulk_mark_spam per-form action.

Bootstrap hardening

  • vendor/autoload.php require now guarded with file_exists() so a mid-extraction race during a WordPress in-place plugin upgrade can't fatal the site. The upgrader replaces the plugin directory non-atomically; a hard require in that window kills every PHP request including wp-login.php. Now falls through cleanly and surfaces an admin notice on the next page load if vendor/ is genuinely missing.

Modernisation

  • Minimum requirements: PHP 8.1, WordPress 6.4
  • Schema\Field and Schema\FormSchema properties typed, declare(strict_types=1)
  • CI matrix runs PHP 8.1 / 8.2 / 8.3
  • prefers-reduced-motion respected throughout; logical-property RTL fixes
  • Fullscreen multi-step forms expose aria-current="step" + a polite live region for step changes

New filters + actions

cf_payments_orchestrator, cf_payment_completed, cf_form_default_settings, cf_form_templates. Full upgrade guide at docs/getting-started/upgrade-to-4.1.md.

Upgrading

Drop-in compatible from 4.0.x. The vendor-autoload guard means in-place WP upgrades from 4.0.x → 4.1.0 won't fatal even on slow filesystems. Database migrations run automatically on activation / via upgrader_process_complete.

Beta

Core Forms 4.1.0-beta7

Read release notes

Headless tab — API key security guidance

A new "Keeping the API key private" accordion on every form's Headless tab walks through the three patterns that actually keep the site-wide API key out of the browser bundle.

Threat-model framing comes first — honest, not moralizing. The key only authorises form submissions on headless-enabled forms; the worst case is spam on those forms (no account access, no billing, no data exfil). But spam is annoying, so:

Server-side proxy templates — copy-paste, with the form's actual submit URL pre-filled: - Cloudflare Pages Functions / Workers (wrangler secret put) - Next.js App Router route handler (process.env, never NEXT_PUBLIC_*) - Astro server endpoint (import.meta.env, never PUBLIC_*)

Captcha note: Turnstile / reCAPTCHA on the WP install verifies on every submission through the same pipeline — headless or not. Links straight to the global settings.

Defense-in-depth checklist at the proxy: Origin allowlist, body size cap, User-Agent sanity, and an explicit "don't add a honeypot at the proxy" warning (browser autofill on field names like url / company / phone silently drops real submissions — the plugin has its own honeypot upstream).

"If you must put the key in client code" — pragmatic close: not the end of the world, rotate when abused, watch the per-form Spam tab.

Beta

Core Forms 4.1.0-beta6

Read release notes

Submissions page rebuilt for WP 7.0

WP 7.0's taller native form controls (especially date inputs) caused the old toolbar to wrap onto four lines — bulk actions, then the form filter, then dates, then pagination, each on its own row. New layout:

  • Filter card (top): form-picker + date range + Filter button + Clear link, in one row
  • Search card (right of the same row): search input + Search button
  • Bulk-actions row (under): bulk action select + Apply on the left, item count + pagination on the right
  • Cards grid below

Consistent min-height: 36px across selects, date inputs, and buttons so the taller WP 7.0 dates don't stagger the row vertically. Stacks under 900px viewport.

Bonus fix

The previous markup had the filter inputs nested inside the bulk-actions POST form, so the "Filter" button never actually wrote form_id / date_from / date_to to the URL — and the listing reads $_GET. Filtering now uses an independent GET form, so query-string state survives the round trip.

AllSubmissionsTable::extra_tablenav is now a no-op for the same reason (the WP_List_Table hook fires inside the wrong form).

Beta

Core Forms 4.1.0-beta5

Read release notes

FluentCart fixes

  • Address fields collected on the form. FluentCart's checkout requires billing address by default. Beta4's templates didn't collect it, so users hit a half-empty checkout. The FluentCart product + subscription templates now include a billing fieldset (address line 1/2, city, state, postcode, country) named to match FluentCart's checkout schema.
  • Form fields prefill FluentCart checkout. The gateway maps submission data into cart->checkout_data['form_data'] so FluentCart auto-fills its inputs and TaxManager picks up country/state for tax. Two-pass mapping: billing_* / shipping_* keys forward verbatim; common aliases (NAME, EMAIL, COUNTRY, etc.) map to FluentCart equivalents.
  • Test/Live badge now reads FluentCart's own mode. The per-form Payment tab no longer shows the global Core Forms test/live badge for the FluentCart provider (which it doesn't gate). Instead reads FluentCart\Api\CurrencySettings's order_mode and renders "FluentCart: Live" / "FluentCart: Test" with a tooltip linking to the FC store settings.
  • FluentCart hidden when not installed. The gateway only registers when FluentCart's PHP classes exist. The "FluentCart product" and "Subscription" starter templates are filtered out of the Add Form gallery on the same condition.
  • Provider-switch resilience. FluentCart templates ship with a fallback payment_amount + currency so swapping to Stripe/PayPal/Razorpay later doesn't trip the gateway's "amount must be greater than zero" guard.

Per-template submission messages

Templates registry gained a messages callback alongside markup and settings. Every template now ships with a context-appropriate Success message instead of the generic "We will be in touch soon":

  • Payment templates — "Redirecting to PayPal…", "Welcome aboard…", etc. + payment-specific error copy
  • Contact — "Your message is on its way"
  • Newsletter — "You're on the list. Check your inbox"
  • Support — "Support ticket received. We'll reply within one business day"
  • Booking — "Booking received. You'll get a confirmation email"
  • (and so on for the rest)

cf_get_template_messages() helper feeds the preset into cf_message_* post meta on form creation.

Beta

Core Forms 4.1.0-beta4

Read release notes

Payment-ready starting points

Six new templates in the Add Form gallery, each pre-configured with a payment provider so the form lands one step away from collecting money:

| Template | Provider | Pricing | |---|---|---| | One-time payment | Stripe | Fixed $19 | | Donation | PayPal | Donor-entered amount via [AMOUNT] | | INR payment | Razorpay | Fixed ₹999 | | FluentCart product | FluentCart | Variation-driven | | Subscription | FluentCart Pro | Variation-driven, recurring | | Event ticket | Stripe | Tier select drives [TIER] amount |

Templates registry now supports a settings callback alongside markup — new cf_get_template_settings() helper feeds the preset into the new form's _cf_settings meta on creation. Payment-category templates carry a blue "Payment ready" badge instead of the default "Accessible" one.

UI fixes

  • Global Payments settings: gateway accordions collapsed by default, not auto-opened on unconfigured gateways. Less visual noise; expand the one you're configuring.
  • Screen Options hidden: the WordPress Screen Options dropdown is suppressed across all Core Forms admin pages — All Forms, Add New, Settings, Spam, plus the per-form editor and submissions views.
  • Akismet banner removed: dropped the persistent "Akismet spam protection is active" notice on every form edit page. Status info doesn't belong as a banner on screens unrelated to it.