Gatilab Products

Changelogs

Improvements, fixes, and the details behind each release.

Core Forms

Build forms, collect submissions, and connect the actions your site needs.

View Product
Stable

Core Forms 4.4.0

Read release notes

Core Forms 4.4.0 includes the complete feature set originally planned for roadmap versions 4.4 and 4.5.

Added

  • Added inline Stripe Payment Element checkout with server-owned product pricing, idempotent PaymentIntent creation, and signed webhook confirmation.
  • Added first-class Razorpay hosted checkout with payment-link, captured-payment, failure, cancellation, and refund handling.
  • Added one-click importers for Ninja Forms and Formidable Forms alongside the existing CF7, Gravity Forms, WPForms, and Fluent Forms migration tools.
  • Added WP-CLI commands for form import/export/listing, submission maintenance, product and plan administration, license reporting, order reporting, and expiry maintenance.
  • Added native actions for WooCommerce, Easy Digital Downloads, SureCart, WP Simple Pay, MemberPress, LearnDash, Paid Memberships Pro, Groundhogg, Jetpack CRM, WP-CRM System, and SureContact.
  • Added AutomatorWP, Uncanny Automator, and OttoKit triggers and actions with form and submission payloads.
  • Added signed inbound WhatsApp webhooks for Meta Cloud API and Twilio, including reply correlation and delivery-state history in submission conversations.
  • Added a multi-product licensing and lightweight ecommerce system with products, plans, orders, subscriptions, customer accounts, activation limits, refunds, renewals, and audited signed downloads.
  • Added a dedicated customer dashboard for orders, masked/revealable keys, product downloads, activated sites, and subscription controls.
  • Added free, Stripe, Razorpay, and renewal license-form templates with complete payment and fulfillment actions.
  • Added a default Sample Product with free, USD annual, and INR annual plans on new and upgraded sites.
  • Added native Bricks and Elementor integrations plus a per-form WCAG 2.2 audit and conservative one-click repairs.

Changed

  • The form editor now renders only the active tab instead of executing every tab's PHP, JavaScript, analytics, and REST requests on every page load.
  • The visual builder now boots from server-rendered form and schema data, removing its two blocking initial REST requests.
  • Builder saves now persist markup and schema atomically through one endpoint with a bounded timeout and a standard form-post fallback.
  • Tab-specific saves merge with existing form settings, preserving payment and action configuration when saving Fields, Messages, or other individual tabs.
  • Licensing actions are always visible under a dedicated Commerce & Licensing category instead of disappearing when the public activation API is disabled.
  • Product and plan slugs are generated automatically, normalized, and made unique; manually entered slugs remain supported.
  • Plan prices are entered in customer-facing major units such as 49.00 instead of minor units such as 4900.
  • Product packages use the WordPress Media Library picker and uploader instead of requiring an attachment ID.
  • The licensing backend now includes catalog statistics, quick-start guidance, direct Add plan/Add package links, and file names instead of raw attachment IDs.
  • The existing core-forms.com CF Licenser client, option keys, 32-character key format, six-hour heartbeat, and update hooks remain separate from and compatible with the new merchant licensing subsystem.

Fixed

  • Fixed the Fields builder remaining on an indefinite loading spinner when either initial REST request stalled.
  • Fixed inactive Analytics requests and form-builder assets loading on unrelated form tabs.
  • Fixed paid plans being fulfillable from a non-payment form or before verified payment confirmation.
  • Fixed payment fulfillment accepting a mismatched paid amount or currency when validation is enabled.
  • Fixed free plans being incorrectly coupled to a payment requirement.
  • Fixed action-variable references showing a permanent Loading message when the Fields builder was not present.
  • Fixed stale admin asset caches by versioning the main admin stylesheet and licensing script from their file modification times while keeping the plugin version at 4.4.0.

Security

  • License fulfillment is idempotent per submission and product selection, preventing duplicate orders or keys during webhook retries.
  • Signed package links expire, verify the stored SHA-256 digest before streaming, and record downloads in an audit table.
  • Verified refunds disable issued licenses and cancel related subscriptions.
  • Inbound messaging and payment webhooks require provider signatures before they can change submission, payment, or fulfillment state.
Stable

Core Forms 4.3.8

Read release notes

Core Forms 4.3.8

New - Field names accept uppercase. The builder's Name field no longer force-lowercases input (HTML `name`/`id` are case-sensitive). Illegal characters are still sanitized. - Textarea options: Rows (height), Resize behaviour (default / vertical / horizontal / both / none), and a live character counter that shows "used / limit" when a Max length is set. An explicit Rows value now wins over the 5-line default height. - Phone (tel) value limits: new Min length, Max length, and Pattern controls to constrain accepted values.

Fixed - The builder's HTML generator silently dropped textarea min/max-length and conditional-logic attributes on save, so those validations were lost. The textarea output now matches the server-side renderer.

Stable

Core Forms 4.3.7

Read release notes

Bug-fix release.

Fixed: critical error after plugin, theme, or core updates and rollbacks.

The upgrader_process_complete migration callback was registered under the plugin namespace (Core_Forms\_cf_on_upgrader_complete), but that function is defined in the global namespace, so it was an invalid callback that triggered a fatal TypeError whenever WordPress finished an update or rollback. It surfaced as "There has been a critical error on this website" on the update/rollback screen. The callback is now registered with the correct name, so updates complete cleanly.

PHPStan had originally flagged this; the error had been suppressed in phpstan-baseline.neon rather than fixed. That stale baseline entry is now removed.

Stable

Core Forms 4.3.6

Read release notes

Accessibility floor for every Core Form (and Core Poll) input.

This release is about making the default Core Form accessible by default — not "accessible if the author remembers to opt in." Whether you load only the skeleton (forms.css), only the themed stylesheet (form-theme.css), or both, the floor holds.

What's in

  • 44 × 44 CSS px touch target on every form control. WCAG 2.5.5 AAA target size. Applied to every text-type input, select, textarea, and button in both stylesheets so the rule survives whether the theme is loaded, disabled, or partially overridden.
  • Windows High Contrast Mode-safe keyboard focus. The themed :focus-visible state used to be outline: none + coloured box-shadow — HCM strips the box-shadow and the ring vanishes. Now :focus-visible pairs the box-shadow with a transparent 2px outline; HCM forces the transparent outline to the system focus colour, so keyboard users on HCM still see a ring.
  • Accessibility baseline in forms.css. Authors who disable the form theme used to drop back to a bare browser stylesheet. The skeleton now ships a tiny accessibility section at the top — 44px target, currentColor focus outline, prefers-reduced-motion transition kill-switch — so disabling the theme doesn't disable accessibility.
  • Textareas are 5 lines tall by default. Browsers ship rows="2" which is comically short for paragraph answers. Both stylesheets now set min-height: calc(5lh + 22px) (with a 140px px fallback for older browsers) — textareas grow with the host theme's line-height instead of being pinned to a px value.

Asset loading

  • Form and poll stylesheets now print at the bottom of <head>. Enqueue priority bumped from 20 → 99 so wp_enqueue_style calls land after the host theme's. WordPress prints in enqueue order → Core Forms styles cascade over theme styles cleanly, no !important, no specificity arms race.
  • Poll CSS is back in <head>. poll-frontend.css used to enqueue inside render_poll_shortcode() during the_content — after wp_head had already printed — so it landed in the footer. PollFrontend::maybe_enqueue_detected_poll_assets() now pre-scans for core-forms/poll blocks and [cf_poll] shortcodes at wp_enqueue_scripts priority 99 and pre-enqueues the stylesheet. Lands in <head> on poll pages, omitted entirely everywhere else.

Upgrading

Just update. If a textarea on a live form previously sat awkwardly between 2 and 4 lines, it'll now be a comfortable 5; everything else is non-visual or strictly additive.

🤖 Generated with Claude Code

Stable

Core Forms 4.3.5

Read release notes

Hotfix release — restores the Gutenberg block editor experience.

Root cause

If you've been running 4.3.3 or 4.3.4 and noticed the Core Forms block in the editor showing a generic blue "CF" square icon and a single dropdown — no live preview, no inspector panels — this release fixes it.

src/admin/class-admin.php was still enqueuing a legacy assets/js/gutenberg-block.js (a pre-4.0 hand-written registerBlockType call) on enqueue_block_editor_assets. That script registered core-forms/form first, so every modern block-form.js bundle — with ServerSideRender, inspector controls, and the correct form icon — was shipping in every zip but never actually loading in the editor.

The legacy wp_enqueue_script() call also passed no version argument, which is why network-tab diagnostics showed gutenberg-block.js?ver=7.0 (the WP core version) instead of the plugin version. Thanks to the user who reported this from their network tab — that's what pinpointed it.

Changes

  • Removed the enqueue_block_editor_assets listener and enqueue_gutenberg_assets() method from class-admin.php.
  • Deleted the orphan assets/js/gutenberg-block.js.
  • block.json now owns the entire editor lifecycle — register_block_type(__DIR__ . '/block.json') loads assets/js/build/block-form.js with the modern Edit component, and the version: "4.3.5" field cache-busts it against CORE_FORMS_VERSION.
  • Reverted the 4.3.4 form-picker label change back to Title (slug) so forms sharing a base title stay distinguishable in the dropdown.

Upgrading

Just update. Hard-refresh the editor once (Cmd/Ctrl+Shift+R) to clear the cached gutenberg-block.js?ver=7.0 from your browser — after that the modern block editor UI is back.

🤖 Generated with Claude Code

Stable

Core Forms 4.3.4

Read release notes

Two performance memoisations and three UX improvements layered on top of the 4.3.3 bug fixes. No breaking changes, no migrations.

Performance

  • cf_get_form() is request-scoped memoised. The same form requested multiple times per page (block render, get_html, action loop, analytics tracker) no longer re-fetches the post + meta and rebuilds the settings/messages arrays. Each call returns a clone so future callers can't poison the cached instance with property mutations. Invalidation: save_post_core-form (always) and *_post_meta hooks gated on get_post_type() so other CPT meta touches don't trash the cache.
  • cf_count_form_submissions() is request-scoped memoised. The forms list table called this per-row — a 50-form admin issued 50 COUNT(*) queries; now it issues one per (form_id, is_spam) pair and the rest are cache hits. Invalidated on cf_submission_inserted.

UX

  • Block-editor preview skeleton. The Gutenberg block preview shows an animated shimmer skeleton (title bar → input rows → submit button) while ServerSideRender is fetching, instead of a bare spinner. Respects prefers-reduced-motion.
  • Cleaner form picker. The block's form-picker dropdown shows just the form title instead of \"Title (slug)\" — much cleaner when titles are distinct. The slug is still surfaced via the Edit Form deep link beneath the picker.
  • / keyboard shortcut on the forms list. Press / or s to focus the search box. Standard list-UI shortcut (GitHub, GitLab, Linear, every modern dashboard). Skipped when an input, textarea, or contenteditable already has focus so it never hijacks typing.

4.3.3 fixes are intact

  • The wp_cf_submission_replies table self-heal and the un-spam action-replay parity from 4.3.3 are untouched — verified 10 call sites still wired.

Upgrade notes

  • No data or schema migrations.
  • No breaking API changes.
  • The cache-invalidation hooks are guarded with function_exists('add_action') to match the existing convention in functions.php, so the file remains autoload-safe for standalone test environments.

Verified

  • 151 unit tests pass.
  • PHPStan reports no errors.
  • Production zip ships at 815 KB with no test framework, no PHPStan, no composer.json.
  • block-form.js rebuilt; bundle still contains wp.serverSideRender and the new cf-block-skeleton styles.
Stable

Core Forms 4.3.3

Read release notes

Bug-fix release.

Fixed: fatal "Table 'wp_cf_submission_replies' doesn't exist" when opening a submission. The reply-history feature queried a table that no installer or migration ever created. The table is now created on activation, on new-blog insert, and on multisite activation, and existing installs are backfilled by a 4.3.3 migration so they self-heal on update.

Fixed: submissions-table "Not Spam" bulk action did not fire form actions. Moving a submission out of spam from the submissions table now replays the form's success and configured actions (notifications, integrations) that were skipped at submit time, matching the behavior of the spam-page single and bulk paths. The replay is centralised in cf_replay_submission_actions() so the three un-spam paths can't drift apart.

Stable

Core Forms 4.3.2

Read release notes

Core Forms 4.3.2

  • Optimized frontend asset loading so forms.js, accessibility.js, analytics.js, and form styles load only when a Core Forms block or shortcode is present.
  • Removed redundant Core Form block viewScript metadata because rendered forms enqueue the runtime directly.
  • Added tests for form asset detection, including a poll-only case.
  • Synced release requirements to WordPress 6.4+ and PHP 8.1+.

Zip SHA-256: 1eb8f317ed899596f1df041fa84e501e72a191c9367b4a9adb4c4487557cc052

Stable

Core Forms 4.3.0

Read release notes

Stable release. Marks the end of the v4.3 beta cycle (beta1 → beta6).

Polls — full rework

Tabbed editor (Question / Settings / Schedule / Embed / Results), drag-to-reorder options, "Other" write-in toggle, Duplicate row action.

Vote integrity rewrite: every vote stores a voter_hash (poll + IP + UA + cookie + user) and dedupe queries that one column regardless of mode. Cookie set on render with SameSite=Lax; Secure, persists across AJAX. vote_limit=user rejects anonymous voters at submit. Honeypot on by default, per-IP rate limiter (12/minute).

Scheduling (start date + hourly cf_poll_auto_close cron), change-vote window, Gutenberg block (core-forms/poll) with picker + display-mode, opt-in live results (15s, pauses on hidden tab), bar / columns / percent result styles, container-query-driven adaptive layout, REST API (/cf/v1/polls, /polls/{id}/results public, /polls/{id}/analytics auth), per-poll analytics screen + CSV export.

Accessibility: <fieldset><legend>, role="progressbar" with aria-valuenow, role="status" aria-live="polite", focus moves to result heading on submit, prefers-reduced-motion honored. jQuery removed from poll JS.

Schema migration (cf_db_version → 4.3.0) is additive with backfill so historical dedupe keeps working.

Importers — three new sources

  • Gravity Forms — GFAPI-driven, ~14 field types, notifications → Email actions with per-recipient conditions, confirmations → success message / redirect, conditional logic → data-show-if / data-hide-if. Field IDs resolve through a per-form name map so condition references stay valid.
  • WPForms (Lite + Pro) — Decodes the form JSON, maps notifications, optional entry import from wp_wpforms_entries (Pro) into wp_cf_submissions so admin history survives the cutover.
  • Fluent Forms — Reads wp_fluentform_forms + wp_fluentform_form_meta directly (no plugin dependency), maps fields, notifications, webhooks, and conditional logic.

All three plug into the existing Core Forms → Import page via a new cf_import_sources filter + cf_import_render_section action exposed by CF7Migration, with shared helpers in a BaseMigration abstract class.

Send Email — conditional emails (per recipient)

New "Recipient routes" repeater on the Send Email action. Each route has a field + operator + value + recipient address; the first matching route's recipient overrides the default To. The existing action-level conditions block keeps working — that gates whether the action runs at all; routes only re-target where it sends.

Forms list

Fix for the Duplicate row action: it used to drop the user into the editor for the original form because the URL carries a form_id and the overview page short-circuited into the editor before the list table's single-row handler could run.

Build & CI

ESLint inheritance cleared (~80 prettier autofixes + targeted eqeqeq / no-unsafe-wp-apis / no-noninteractive-element-interactions fixes); PHPStan baseline regenerated at level 5; PHP 8.1 / 8.2 / 8.3 unit tests green.

Upgrade notes

  • Migration is automatic on next admin pageload (or on plugin update via upgrader_process_complete).
  • New WP-Cron hook cf_poll_auto_close is scheduled hourly; unscheduled on deactivation.
  • New post meta keys for importer provenance: _cf_gf_source_id, _cf_wpforms_source_id, _cf_fluent_source_id — useful for debugging or re-import workflows.
Beta

Core Forms 4.3.0-beta6

Read release notes

Pre-release. Bug-fix pass on the beta5 importers and conditional emails.

Fixes

  • Per-recipient conditions never actually filtered the recipient. The three new migrators (Gravity, WPForms, Fluent) were saving conditions on Email actions using op / is / isnot, but cf_evaluate_action_conditions() consumes operator / equals / not_equals. Empty-operator rows were being skipped entirely, so every recipient match returned true and every notification fired regardless of the source plugin's conditional logic. Now writes the keys/vocab the engine actually understands.

  • Gravity / WPForms conditional logic referenced nonexistent field names. Both plugins reference fields in their rules by numeric ID. Our import was naively emitting field_<id> references while the rendered HTML used sanitize_key(label) names — so the data-show-if attributes pointed at fields that didn't exist on the page. Each migrator now builds a per-form field-id → rendered-name map up front and translates references through it.

  • Rendered field names collided when two source fields shared a label. sanitize_key('First Name') → firstname for every field labelled "First Name". Names now always carry an _<id> suffix so duplicates stay distinct (and empty labels still get field_<id>).

  • data-show-if attribute syntax was wrong. The runtime parser (in assets/js/forms.js) expects field:value, not field=value, and uses data-hide-if to express inversion rather than != inline. BaseMigration::build_conditional_attr() now emits the syntax the runtime actually parses and flips show↔hide when the rule is negated.

  • WPForms entry count now shows in the success notice. The importer was redirecting back with entries_total in the URL but nothing rendered it. The CF7 importer page now shows "(N entries imported too)" alongside the "Imported X forms" notice.

Install on staging only. Real installs stay on 4.2.10 until stable 4.3.0.

Beta

Core Forms 4.3.0-beta5

Read release notes

Pre-release. Ships the four "Shipping now / v4.3" items from the public roadmap.

What's new

Gravity Forms importer

Reads forms via GFAPI, converts fields (text/email/phone/url/number/date/textarea/select/multiselect/radio/checkbox/file/name/address/consent and friends) to HTML. Every notification becomes a Send Email action — and when the source notification had its own conditional-logic block, that block carries across as per-recipient conditions on the Email action. The default confirmation maps to the success message or redirect URL. Field-level conditional logic carries across as data-show-if / data-hide-if attributes. Source Gravity Forms data is never modified.

WPForms importer (Lite + Pro)

Decodes the form JSON stored in wp_wpforms.post_content, converts the field map to HTML, turns each notification into an Email action (with per-recipient conditions when applicable). When the Pro wp_wpforms_entries table is present, optionally imports stored entries into wp_cf_submissions so admin history isn't lost during the cutover.

Fluent Forms importer

Reads wp_fluentform_forms + wp_fluentform_form_meta directly — no Fluent API dependency, so the importer also works on sites where the source plugin is deactivated but its data is still in the database. Converts the field map to HTML (recursing into Fluent's container columns), walks the notifications meta_key for Email actions, picks up webhook-shaped meta_keys as Trigger Webhook actions, and maps the form's default confirmation to the success message / redirect URL.

Conditional emails (per recipient)

New Recipient routes repeater under the Send Email action. Each route has a field + operator + value + recipient address; the first matching route wins, otherwise the default "To" address is used. The existing action-level conditions block keeps working — that decides whether the action runs at all; routes only re-target where it sends.

Importer extension point

CF7Migration's source picker is no longer hardcoded. Added cf_import_sources filter + cf_import_render_section action so third-party migrators can plug into the existing Core Forms → Import page. The three new migrators use these hooks via a shared BaseMigration abstract class.

Carried over from earlier 4.3 betas

Polls rework (tabbed editor, vote-integrity rewrite, Gutenberg block, REST, live results, analytics + CSV, accessibility pass, adaptive container-query layout). Duplicate row action fix on the forms list.

Install on staging only. Real installs stay on 4.2.10 until stable 4.3.0.

Beta

Core Forms 4.3.0-beta4

Read release notes

Pre-release. Visual + layout pass on the polls rework.

Changes vs beta3

Editor preview now matches the front of the site. The poll stylesheet only enqueued on the public side, so Gutenberg's ServerSideRender preview fell back to browser-default form styling. Registered the CSS as both style and editor_style on the block so editor and frontend use identical visuals.

Container-driven adaptive layout. Dropped the fixed 560px max-width for width: 100% + container queries. Padding, border radius, and type scale tune to the actual container width (cqi units + clamp), so a sidebar poll on a 4K monitor stays compact and a full-width poll on a phone collapses to a stacked footer. Viewport @media fallback for browsers without container query support.

Options auto-flow. Short option lists like "WordPress / Wix" now sit side-by-side when there's room (auto-fit, minmax(min(240px, 100%), 1fr)); forced single-column inside the bars layout so each bar gets full width.

Layout bug. The radio/checkbox was dropping the option label to a second line on the front-end because the label was display: block with a display: flex child. The label itself is now flex.

Visual polish. Pill result bars (10px instead of 24px), softer card shadow, larger submit hit area with active-state feedback, refined focus rings.

Carried over from earlier betas

Tabbed editor, drag-to-reorder, "Other" write-in, vote-integrity rewrite (voter_hash dedupe, cookie-on-render with SameSite=Lax; Secure, user-mode anon rejection, honeypot, per-IP rate limit), hourly auto-close cron, Gutenberg block, REST endpoints, live results, change-vote window, bar/columns/percent styles, analytics screen + CSV export, accessibility pass, jQuery removed, schema migration with backfill.

Install on staging only. Real installs stay on 4.2.10 until stable 4.3.0.