Adds a CF_DEMO_MODE wp-config constant guard so the public demo build at core-forms.com/demo can be safely handed out via token-gated R2 — the plugin self-disables anywhere except playground.wordpress.net subdomains. No-op for production…
Read full notes
Adds a CF_DEMO_MODE wp-config constant guard so the public demo build at core-forms.com/demo can be safely handed out via token-gated R2 — the plugin self-disables anywhere except playground.wordpress.net subdomains. No-op for production sites.
What was wrong: The bundled embed.js (served from /wp-json/core-forms/v1/embed.js) was rendering result.data.message directly into the success card. But the form processor returns it as an object — { type: 'success', text: '...' } — not a string. JavaScript stringified the object to literal [object Object], so every successful headless submission ended with that string in the success card. Same shape bug in the error path.
What's fixed: Added a pickText() helper that: - Plucks .text when the message is an object. - Returns the value when it's already a string (for forward-compat). - Falls back to a sensible default (Thanks! / Submission failed.) when the field is missing.
Who's affected: Anyone running the drop-in embed widget (the `
Per-form spam view routes correctly: ?page=core-forms&view=spam&form_id=X&tab=submissions now opens the form-edit screen on the Submissions tab in spam-filter mode instead of falling through to the all-forms list. The router branched on view=edit; it now branches on the presence of form_id so any view value (spam / all / inbox) routes correctly.
Submissions list — visual cleanup: Dropped the 3px blue / red inset left border on new and spam rows and the muted grey text on spam rows. Status is communicated entirely by the badge in the STATUS column (New / Read / Replied / Spam) — much cleaner against WP's native striped table.
Dashicon alignment fix for WP < 7.0: Buttons that pair a dashicon with text (Documentation, Refresh, Open, copy buttons in the Headless tab) had the icon glyph sitting ~2px above the text baseline on WordPress 6.9.x and earlier. WP 7.0 normalized this natively; for older WP a high-specificity rule scoped to Core Forms admin screens applies vertical-align: middle, line-height/font-size resets, and :has(.dashicons) inline-flex centering to match.
Stable release. Consolidates eleven betas (v4.2.0-beta through v4.2.0-beta11) into one shipping version.
Read full notes31 changes
Stable release. Consolidates eleven betas (v4.2.0-beta through v4.2.0-beta11) into one shipping version.
Both Cloud API and Twilio sends log through Email Logs. The cf_form_redirect_url filter now fires on every successful submission and receives the Submission as a 4th argument.
WhatsApp, three ways: Three new actions cover the full range of WhatsApp delivery models — pick whichever fits your existing stack.
WhatsApp Cloud API action. Send template messages and free-form text (24-hour window) via Meta's Graph API. Free tier covers 1,000 conversations per month. Credentials live globally under Settings → WhatsApp Cloud API with per-action override; a "Test connection" button reports the verified business name + quality rating.
Twilio WhatsApp action. For shops already on Twilio — reuses your Account SID / Auth Token, posts to the whatsapp: channel, supports approved Content Template SIDs.
Click-to-WhatsApp redirect. Generates a wa.me deep link with the submission pre-filled into the chat draft, used as the post-submit redirect.
Contact Form 7 → Core Forms migration tool: New page under Core Forms → Import that scans every wpcf7_contact_form post, converts shortcode markup to plain HTML (14 tag types covered), maps the CF7 mail template to a Send Email action, and carries messages overrides across as cf_message* post meta. Per-row preview lets you sanity-check before committing. Gravity Forms, WPForms, Fluent Forms, Ninja Forms, Formidable previewed as "Coming soon" cards.
Per-form analytics dashboard: New Analytics tab on every form-edit screen, powered by the existing cf/v1/analytics/* REST API: - One "Funnel" postbox with views, starts, submissions, spam. - SVG daily-activity chart with all three series overlaid. - Field interactions panel (where users focus/fill). - Top sources panel (UTM attribution with referrer-domain fallback). - Auto-generated recommendations — high spam rate, low completion, drop-off hotspots. - Date-range presets (7d / 30d / 90d / 1y) + custom from/to.
Form versioning + change history: The core-form post type now declares 'supports' => […, 'revisions', …]. cf_settings, _cf_hidden_columns, and every cf_message override persist alongside each revision. Settings-only or messages-only saves still create a revision (we force wp_save_post_revision_check_for_changes to false for core-form). New Revisions* tab lists each snapshot with author, timestamp, and a diff signal — restore drops back into the Core Forms admin with a flash and copies meta back automatically.
Security hardening — 14 audit findings fixed
CSRF on by default (existing installs preserved via 4.2.0 migration).
Submission mass-assignment closed via markup-derived allowlist.
Headless API key header-only; new CORS allowlist textarea.
Button styling unified — removed the .button-primary glow override that broke consistency.
Fullscreen / Typeform-style display mode promoted to its own top-level card on Settings.
WhatsApp actions surfaced in Add Action picker under new "Messaging & SMS" category.
Per-form Template picker unified with Add-New-Form gallery — all 12 templates available everywhere.
Headless [CF_REFERRER_URL] fix: Cross-origin Referer headers are stripped to the bare origin by browsers. The submit endpoint now reads _cf_page_url from the JSON body and substitutes it into $_SERVER['HTTP_REFERER'] for the duration of Forms::process(). Bundled embed.js forwards window.location.href automatically.
Build pipeline: Production builds run through bash build.sh which executes composer install --no-dev so dev-only autoload entries can't leak. 148 unit tests pass; PHPStan clean against regenerated baseline; production zip 741KB.
Upgrade: Download the attached zip and upload via Plugins → Add New → Upload. Existing data, settings, and forms are preserved. The 4.2.0 migration runs automatically on first admin pageload after upgrade.
Template picker on the per-form builder now reads from the same cf_get_form_templates() registry the Add-New-Form gallery uses — all 12 templates available everywhere instead of just 5. Button styling unified: removed the .button-primary…
Read full notes
Template picker on the per-form builder now reads from the same cf_get_form_templates() registry the Add-New-Form gallery uses — all 12 templates available everywhere instead of just 5. Button styling unified: removed the .button-primary brand-blue + glow override on Core Forms admin pages, so Save Form now matches Refresh, Restore, and every other native .button on the same screen.
Postbox header padding bumped from WP's tight default (8px/12px) to 14px/16px on the Analytics and Revisions tabs. Same WordPress chrome, just with breathing room.
Pre-release. Polish pass on beta8 based on visual feedback.
Read full notes6 changes
Pre-release. Polish pass on beta8 based on visual feedback.
` for the chart-empty and recommendations-empty cases.
Carries over everything from v4.2.0-beta8.
What changed
Stat boxes restructured: Beta8 left the four funnel stat numbers in individual postboxes — each one carried an empty postbox-header bar at top, wasting vertical space and not matching anything else in WP admin. Replaced with one Funnel postbox containing a 4-column grid of label / number / hint (matches the "At a Glance" dashboard widget pattern).
Empty states no longer use .notice: The .notice notice-info inline empty states inside postbox bodies brought their own colored left bar — which read as a UI element nested inside another UI element. Replaced with plain `
Recommendations now render as real .notice rows: Each recommendation is now a native .notice .notice-success / .notice-warning / .notice-info — so the colored left bar is WordPress's own variant rather than a plugin-flavored tint.
Revisions: dropped the "Live" pill badge: Replaced the custom badge with plain Live version text. No more custom badge colors.
Deployed and verified: Live on gatilab.com — plugin active at 4.2.0-beta9.
No more rounded custom panels, no oversized stat numbers, no plugin-flavored colors. The tabs blend with the rest of WP admin.
Carries over everything from v4.2.0-beta7 (WSOD fix, analytics/revisions bugfixes, security hardening, WhatsApp, CF7 migration).
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.
Deployed and verified: Live on a test install — plugin active at 4.2.0-beta8, frontend 200.
Pre-release. Hot-fix for the WSOD that affected beta5 and beta6.
Read full notes3 changes
Pre-release. Hot-fix for the WSOD that affected beta5 and beta6.
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.
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)
Everything else from the beta5/beta6 release notes (analytics tab, revisions tab, CF7 migration, WhatsApp actions, security hardening) is intact and unchanged.
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.
What's fixed: The init callback is now registered as plain '_cf_wire_revisions'.
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.
The registration is now wrapped in function_exists( 'add_action' ) so functions.php is safe to load standalone.
⚠️ 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.
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.
Pre-release. Quick UI consistency fix on top of v4.2.0-beta3.
Read full notes1 change
Pre-release. Quick UI consistency fix on top of v4.2.0-beta3.
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).
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.
Pre-release. Iterating on v4.2.0-beta2 based on tester feedback.
Read full notes10 changes
Pre-release. Iterating on v4.2.0-beta2 based on tester feedback.
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.
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.
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.