Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
da29a27
feat: per-form option to disable default SureForms styling + fix Cust…
vanshk141999 Jul 7, 2026
372318b
test: cover disable-default-styling helpers and conditional style enq…
vanshk141999 Jul 7, 2026
74d11c7
feat: add Payment History widget to Elementor and Bricks (#2930)
vanshk141999 Jul 8, 2026
4042d93
test: add coverage for Payment History widgets; fix PHP Insights blan…
vanshk141999 Jul 8, 2026
207055c
feat: expose disable default styling toggle via Abilities API
vanshk141999 Jul 8, 2026
acef36c
refactor: make disable-default-styling programmatic-only
vanshk141999 Jul 9, 2026
1b6aef3
Merge pull request #2933 from brainstormforce/master-dev-2.12.1-post
vanshk141999 Jul 9, 2026
443cb23
Merge pull request #2934 from brainstormforce/dev-nr-2.12.1-post
vanshk141999 Jul 9, 2026
62e6594
fix: open SureForms Description panel by default in the form editor
vanshk141999 Jul 9, 2026
31a5678
fix: address review — single Custom CSS output path + reusable-block …
vanshk141999 Jul 13, 2026
b9e789c
Merge pull request #2935 from brainstormforce/fix/2288-description-pa…
vanshk141999 Jul 15, 2026
81b322e
Merge pull request #2925 from brainstormforce/feat/disable-default-st…
vanshk141999 Jul 16, 2026
c914caf
Merge pull request #2931 from brainstormforce/feat/2930-payment-histo…
vanshk141999 Jul 16, 2026
c1e4a96
fix: reset notice actions margin to fix editor nudge button alignment
avi1080p Jul 16, 2026
745c9ba
Merge pull request #2944 from brainstormforce/fix/editor-nudge-button…
vanshk141999 Jul 16, 2026
6dbf3ed
Version Bump 2.12.2
vanshk141999 Jul 16, 2026
04a0520
Update 2.12.2 changelog entries
vanshk141999 Jul 16, 2026
7ee2bf5
Merge pull request #2945 from brainstormforce/version-bump-2.12.2
vanshk141999 Jul 16, 2026
741dee8
fix(settings): render global settings after all filters register
vanshk141999 Jul 17, 2026
d2de0a4
Merge pull request #2947 from brainstormforce/fix/settings-nav-render…
vanshk141999 Jul 20, 2026
2522fdb
Merge branch 'dev' of https://github.com/brainstormforce/sureforms in…
vanshk141999 Jul 20, 2026
0c30acd
Merge pull request #2953 from brainstormforce/dev-nr-2.12.2-2
vanshk141999 Jul 20, 2026
5b455ed
chore: update i18n translations
github-actions[bot] Jul 20, 2026
fdb1e31
Merge pull request #2958 from brainstormforce/i18n/next-release
vanshk141999 Jul 20, 2026
ba0d05f
chore: set 2.12.2 release date to 20th July 2026
vanshk141999 Jul 20, 2026
a3f6822
Merge pull request #2959 from brainstormforce/chore/release-date-2.12.2
vanshk141999 Jul 20, 2026
c8145b5
chore: update 2.12.2 changelog in readme
vanshk141999 Jul 20, 2026
d6e2c1f
Merge pull request #2960 from brainstormforce/chore/readme-release-no…
vanshk141999 Jul 20, 2026
acda89a
Merge pull request #2946 from brainstormforce/next-release
vanshk141999 Jul 20, 2026
de0b1ba
ci: pin Node 24.16.0 in deploy/asset workflows so npm ci matches the …
vanshk141999 Jul 20, 2026
14d4483
Merge pull request #2962 from brainstormforce/chore/deploy-pin-node
vanshk141999 Jul 20, 2026
ea86cfa
ci: revert deploy/asset workflows to npm install --force
vanshk141999 Jul 20, 2026
c62e63a
Merge pull request #2963 from brainstormforce/chore/deploy-revert-npm…
vanshk141999 Jul 20, 2026
229740b
ci: use npm ci with Node 24.16.0 in deploy/asset workflows
vanshk141999 Jul 20, 2026
047e966
Merge pull request #2965 from brainstormforce/chore/deploy-npm-ci-nod…
vanshk141999 Jul 20, 2026
d80cf1a
chore: strip internal-only paths from public mirror sync
vanshk141999 Jul 20, 2026
f8752c2
Sync master from upstream
vanshk141999 Jul 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
**Contributors:** [brainstormforce](https://profiles.wordpress.org/brainstormforce/)
**Tags:** forms, contact form, form builder, survey, payment form
**Requires at least:** 6.4
**Tested up to:** 7.0
**Tested up to:** 7.0.1
**Requires PHP:** 7.4
**Stable tag:** 2.12.1
**Stable tag:** 2.12.2
**License:** GPLv2 or later
**License URI:** http://www.gnu.org/licenses/gpl-2.0.html

Expand Down Expand Up @@ -458,6 +458,9 @@ Yes. SureForms Business includes fully functional user registration forms and lo
You can report security issues through our [Bug Bounty Program](https://brainstormforce.com/bug-bounty-program/). We collaborate with Patchstack to provide opportunities for researchers to report vulnerabilities. The Patchstack team will help validate, triage, and handle any reported security issues.

## Changelog ##
### 2.12.2 - 20th July 2026 ###
* New: Added a Payment History widget for Elementor and Bricks.
* Fix: Custom CSS now applies correctly on the single form view.
### 2.12.1 - 8th July 2026 ###
* New: Added an AI-powered quick draft flow on the dashboard to generate forms faster, with built-in usage tracking.
* Improvement: Email fields now enforce standard RFC 5321 length limits (a 64-character local part and 255-character domain) to prevent oversized submissions, with a filter to customize the limits.
Expand All @@ -470,10 +473,6 @@ You can report security issues through our [Bug Bounty Program](https://brainsto
* Fix: Forms with multiple Cloudflare Turnstile widgets now submit correctly instead of silently failing with a generic error.
* Fix: Resolved an issue where the Stripe Payment Element failed to render on live accounts that have Bacs Direct Debit, Link, Cash App, or BNPL enabled.
* Fix: Restored the form editor on WordPress 6.x sites running plugins that register older-style blocks such as ThirstyAffiliates, Ninja Forms, and Gravity Forms.
### 2.11.1 - 16th June 2026 ###
* Fix: Phone field auto country detection always resolved to the United States.
* Fix: Corrected the Cloudflare Turnstile "Get Keys" link.
* Fix: This update addressed a security bug. Props to Yaswanth Reddy Sunkara for reporting it responsibly to our team.
The full changelog is available [here](https://sureforms.com/whats-new/?utm_source=wordpress.org&utm_medium=whats_new).

## Upgrade Notice ##
Expand Down
4 changes: 3 additions & 1 deletion inc/abilities/ABILITIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ Complete reference for all 15 abilities registered with the WordPress Abilities
- `emailConfirmation` — `{name, subject, emailBody}`
- `compliance` — `{enableCompliance, neverStoreEntries, autoDeleteEntries, autoDeleteEntriesDays}`
- `instantForm` — `{instantForm, showTitle, bannerColor, useBannerColorAsBackground, formBackgroundColor, formWidth, formSlug}`
- `styling` — `{primaryColor, textColor, textColorOnPrimary, fieldSpacing, submitAlignment}`
- `styling` — `{submitAlignment}`
- `formStyling` — `{primaryColor, textColor, textColorOnPrimary, fieldSpacing, disableDefaultStyles}` (`disableDefaultStyles: true` disables SureForms' default frontend styles so the theme styling applies)
- Note on `disableDefaultStyles`: unstyled mode also drops SureForms' column/grid layout CSS (multi-column layouts collapse until the site's CSS re-provides them). The per-form Custom CSS on embedded views is scoped to the form container, so unscoped selectors like `body { … }` have no effect there. Custom CSS relies on native CSS nesting (2023+ browsers).

**Output:**
```json
Expand Down
12 changes: 8 additions & 4 deletions inc/abilities/forms/create-form.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,23 +137,27 @@ public function get_input_schema() {
'type' => 'object',
'description' => __( 'Form styling settings including colors and spacing.', 'sureforms' ),
'properties' => [
'primaryColor' => [
'primaryColor' => [
'type' => 'string',
'description' => __( 'Primary/accent color as hex (e.g. #111C44).', 'sureforms' ),
],
'textColor' => [
'textColor' => [
'type' => 'string',
'description' => __( 'Text color as hex (e.g. #1E1E1E).', 'sureforms' ),
],
'textColorOnPrimary' => [
'textColorOnPrimary' => [
'type' => 'string',
'description' => __( 'Text color on primary backgrounds as hex (e.g. #FFFFFF).', 'sureforms' ),
],
'fieldSpacing' => [
'fieldSpacing' => [
'type' => 'string',
'description' => __( 'Spacing between fields.', 'sureforms' ),
'enum' => [ 'small', 'medium', 'large' ],
],
'disableDefaultStyles' => [
'type' => 'boolean',
'description' => __( 'When true, SureForms does not enqueue its default frontend styles for this form, so the active theme styling applies instead.', 'sureforms' ),
],
],
],
'emailNotification' => [
Expand Down
3 changes: 3 additions & 0 deletions inc/abilities/forms/form-metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ protected function apply_metadata_overrides( $post_metas, $meta_data ) {
$styling_data['field_spacing'] = $form_styling['fieldSpacing'];
}
}
if ( isset( $form_styling['disableDefaultStyles'] ) ) {
$styling_data['disable_default_styles'] = (bool) $form_styling['disableDefaultStyles'];
}

$post_metas['_srfm_forms_styling'] = $styling_data;
}
Expand Down
12 changes: 8 additions & 4 deletions inc/abilities/forms/update-form.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,23 +140,27 @@ public function get_input_schema() {
'type' => 'object',
'description' => __( 'Form styling settings including colors and spacing.', 'sureforms' ),
'properties' => [
'primaryColor' => [
'primaryColor' => [
'type' => 'string',
'description' => __( 'Primary/accent color as hex (e.g. #111C44).', 'sureforms' ),
],
'textColor' => [
'textColor' => [
'type' => 'string',
'description' => __( 'Text color as hex (e.g. #1E1E1E).', 'sureforms' ),
],
'textColorOnPrimary' => [
'textColorOnPrimary' => [
'type' => 'string',
'description' => __( 'Text color on primary backgrounds as hex (e.g. #FFFFFF).', 'sureforms' ),
],
'fieldSpacing' => [
'fieldSpacing' => [
'type' => 'string',
'description' => __( 'Spacing between fields.', 'sureforms' ),
'enum' => [ 'small', 'medium', 'large' ],
],
'disableDefaultStyles' => [
'type' => 'boolean',
'description' => __( 'When true, SureForms does not enqueue its default frontend styles for this form, so the active theme styling applies instead.', 'sureforms' ),
],
],
],
'emailNotification' => [
Expand Down
140 changes: 140 additions & 0 deletions inc/form-styling.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,146 @@ public static function has_custom_styling( $block_attrs ) {
return 'inherit' !== ( $block_attrs['formTheme'] ?? 'inherit' );
}

/**
* Check if the form has default SureForms styling disabled.
*
* When enabled (via the `_srfm_forms_styling` meta set through REST/MCP, or
* the `srfm_disable_default_styles` filter), the form is rendered without the
* SureForms frontend stylesheets and inline CSS variables so the site's own
* CSS fully controls the form's appearance.
*
* @param int|string $form_id Form post ID.
* @return bool True when default styling is disabled for the form.
* @since 2.12.2
*/
public static function is_default_styling_disabled( $form_id ) {
$form_id = absint( $form_id );
if ( ! $form_id ) {
return false;
}

$form_styling = get_post_meta( $form_id, '_srfm_forms_styling', true );
$disabled = is_array( $form_styling ) && ! empty( $form_styling['disable_default_styles'] );

/**
* Filters whether SureForms' default frontend styling is disabled for a form.
*
* Lets themes/plugins toggle the unstyled mode programmatically, overriding the
* stored per-form meta. Return true to render the form without SureForms' default
* stylesheets and inline CSS variables.
*
* @param bool $disabled Whether default styling is disabled (from meta).
* @param int $form_id Form post ID.
* @since 2.12.2
*/
return (bool) apply_filters( 'srfm_disable_default_styles', $disabled, $form_id );
}

/**
* Check whether the SureForms frontend stylesheets can be skipped for a post.
*
* Returns true only when every SureForms form found on the post has default
* styling disabled. When the forms on the post cannot be determined, the
* stylesheets are kept as the safe default.
*
* @param \WP_Post $post Post being rendered.
* @return bool True when the frontend stylesheets can be skipped.
* @since 2.12.2
*/
public static function should_skip_frontend_styles( $post ) {
if ( SRFM_FORMS_POST_TYPE === $post->post_type ) {
$form_ids = [ $post->ID ];
} else {
$form_ids = self::get_form_ids_from_content( $post->post_content );
}

if ( empty( $form_ids ) ) {
return false;
}

foreach ( $form_ids as $form_id ) {
if ( ! self::is_default_styling_disabled( $form_id ) ) {
return false;
}
}

return true;
}

/**
* Extract SureForms form IDs from post content.
*
* Looks for srfm/form blocks (including nested ones) and [sureforms]
* shortcodes.
*
* @param string $content Post content.
* @return array<int> Unique form IDs found in the content.
* @since 2.12.2
*/
public static function get_form_ids_from_content( $content ) {
$form_ids = [];

// Also parse when the content embeds reusable/synced patterns (core/block
// refs) — a form inside a pattern appears as wp:block {"ref":N}, which
// has_block( 'srfm/form' ) alone would never see.
if ( has_block( 'srfm/form', $content ) || has_block( 'core/block', $content ) ) {
$visited_refs = [];
$form_ids = self::collect_form_block_ids( parse_blocks( $content ), $visited_refs );
}

if ( has_shortcode( $content, 'sureforms' ) && preg_match_all( '/' . get_shortcode_regex( [ 'sureforms' ] ) . '/', $content, $matches ) ) {
foreach ( $matches[3] as $atts_string ) {
$atts = shortcode_parse_atts( $atts_string );
if ( is_array( $atts ) && ! empty( $atts['id'] ) ) {
$form_ids[] = absint( $atts['id'] );
}
}
}

return array_values( array_unique( array_filter( $form_ids ) ) );
}

/**
* Recursively collect form IDs from parsed srfm/form blocks, following
* reusable/synced pattern references (core/block) into their wp_block posts.
*
* @param array<mixed> $blocks Parsed blocks from parse_blocks().
* @param array<int, true> $visited_refs Reusable-block post IDs already expanded,
* keyed by ID — guards against reference cycles.
* @return array<int> Form IDs found in srfm/form blocks.
* @since 2.12.2
*/
private static function collect_form_block_ids( $blocks, &$visited_refs = [] ) {
$form_ids = [];

foreach ( $blocks as $block ) {
if ( ! is_array( $block ) ) {
continue;
}
$attrs = isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : [];
if ( isset( $block['blockName'] ) && 'srfm/form' === $block['blockName'] && ! empty( $attrs['id'] ) && is_scalar( $attrs['id'] ) ) {
$form_ids[] = absint( $attrs['id'] );
}
// Reusable/synced pattern: expand the referenced wp_block post so a
// form living inside a pattern is detected like an inline block.
if ( isset( $block['blockName'] ) && 'core/block' === $block['blockName'] && ! empty( $attrs['ref'] ) && is_scalar( $attrs['ref'] ) ) {
$ref = absint( $attrs['ref'] );
if ( $ref && ! isset( $visited_refs[ $ref ] ) ) {
$visited_refs[ $ref ] = true;
$ref_post = get_post( $ref );
if ( $ref_post instanceof \WP_Post && 'wp_block' === $ref_post->post_type && 'publish' === $ref_post->post_status && '' !== $ref_post->post_content ) {
$form_ids = array_merge( $form_ids, self::collect_form_block_ids( parse_blocks( $ref_post->post_content ), $visited_refs ) );
}
}
}
if ( ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) {
$form_ids = array_merge( $form_ids, self::collect_form_block_ids( $block['innerBlocks'], $visited_refs ) );
}
}

return $form_ids;
}

/**
* Map dimension block attributes (Top/Right/Bottom/Left/Unit) to form styling.
*
Expand Down
23 changes: 15 additions & 8 deletions inc/frontend-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,27 +161,34 @@ public function register_scripts() {

if ( $load_assets ) {
// Load needed styles in head tag if current requested page has SureForms form.
self::enqueue_scripts_and_styles();
// Skip the SureForms stylesheets when every form on the page has default styling disabled.
self::enqueue_scripts_and_styles( Form_Styling::should_skip_frontend_styles( $current_post ) );
}
}
}

/**
* Enqueue scripts and styles.
*
* @param bool $skip_form_styles When true, the SureForms stylesheets are not enqueued
* (default styling disabled for the form). External library
* styles and scripts are always loaded so advanced fields
* like Dropdown and Phone keep working.
* @return void
* @since 0.0.11
*/
public static function enqueue_scripts_and_styles() {
public static function enqueue_scripts_and_styles( $skip_form_styles = false ) {
// Load the styles.
foreach ( self::$css_assets as $handle => $path ) {
if ( ! $skip_form_styles ) {
foreach ( self::$css_assets as $handle => $path ) {

// Skip single form styles if not on single form page.
if ( 'single' === $handle && ! is_singular( SRFM_FORMS_POST_TYPE ) ) {
continue;
}
// Skip single form styles if not on single form page.
if ( 'single' === $handle && ! is_singular( SRFM_FORMS_POST_TYPE ) ) {
continue;
}

wp_enqueue_style( SRFM_SLUG . '-' . $handle );
wp_enqueue_style( SRFM_SLUG . '-' . $handle );
}
}

// Load the external styles. Like Phone and Tom Select.
Expand Down
Loading