PluginProbe ʕ •ᴥ•ʔ
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.12.3
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.12.3
2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8 0.0.9 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.1.0 1.1.1 1.1.2 1.10.0 1.10.1 1.11.0 1.12.0 1.12.1 1.12.2 1.12.3 1.13.0 1.13.1 1.13.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5.0 1.5.1 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.8.0 1.9.0 1.9.1 2.0.0 2.0.1 2.0.2 2.1.0 2.1.1 2.2.0 2.2.1 2.2.2 2.3.0 2.4.0 2.5.0 2.5.2 2.6.0
sureforms / inc / form-styling.php
sureforms / inc Last commit date
abilities 3 weeks ago admin 3 months ago ai-form-builder 1 month ago blocks 2 months ago compatibility 3 weeks ago database 3 weeks ago email 3 weeks ago fields 3 weeks ago global-settings 1 month ago lib 1 month ago migrator 2 months ago page-builders 3 weeks ago payments 3 weeks ago single-form-settings 2 months ago traits 2 months ago activator.php 1 year ago admin-ajax.php 2 months ago background-process.php 9 months ago create-new-form.php 3 months ago duplicate-form.php 3 months ago entries.php 3 weeks ago events-scheduler.php 2 years ago export.php 3 months ago field-validation.php 3 weeks ago form-restriction.php 2 months ago form-styling.php 1 month ago form-submit.php 3 weeks ago forms-data.php 5 months ago frontend-assets.php 1 month ago generate-form-markup.php 3 weeks ago gutenberg-hooks.php 3 weeks ago helper.php 3 weeks ago learn.php 4 months ago onboarding.php 2 months ago post-types.php 1 month ago rest-api.php 3 weeks ago smart-tags.php 4 months ago submit-token.php 4 months ago translatable.php 1 month ago updater-callbacks.php 3 weeks ago updater.php 3 weeks ago
form-styling.php
323 lines
1 <?php
2 /**
3 * Form Styling Handler.
4 *
5 * Handles form styling customization for both embed (srfm/form block)
6 * and instant forms. Maps block attributes to form styling array and
7 * provides filter hooks for Pro to extend with theme functionality.
8 *
9 * @package sureforms
10 * @since 2.7.0
11 */
12
13 namespace SRFM\Inc;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 /**
20 * Form_Styling class.
21 *
22 * @since 2.7.0
23 */
24 class Form_Styling {
25 /**
26 * Map block attributes to form styling array.
27 *
28 * Converts camelCase block attributes to snake_case form styling keys.
29 * This allows per-embed customization when formTheme is not 'inherit'.
30 *
31 * @param array<string,mixed> $form_styling Existing form styling from post meta.
32 * @param array<string,mixed> $block_attrs Block attributes from srfm/form block.
33 * @return array<string,mixed> Modified form styling array.
34 * @since 2.7.0
35 */
36 public static function map_block_attrs_to_styling( $form_styling, $block_attrs ) {
37 if ( empty( $block_attrs ) ) {
38 return $form_styling;
39 }
40
41 // Form Theme - allows Pro to add custom themes.
42 if ( ! empty( $block_attrs['formTheme'] ) ) {
43 $form_styling['form_theme'] = Helper::get_string_value( $block_attrs['formTheme'] );
44
45 // Apply theme styling if a theme is selected.
46 $form_styling = self::apply_theme_styling( $form_styling, Helper::get_string_value( $block_attrs['formTheme'] ) );
47 }
48
49 // Colors.
50 if ( ! empty( $block_attrs['primaryColor'] ) ) {
51 $form_styling['primary_color'] = Helper::sanitize_css_value( $block_attrs['primaryColor'] );
52 }
53 if ( ! empty( $block_attrs['textColor'] ) ) {
54 $form_styling['text_color'] = Helper::sanitize_css_value( $block_attrs['textColor'] );
55 }
56 if ( ! empty( $block_attrs['textOnPrimaryColor'] ) ) {
57 $form_styling['text_color_on_primary'] = Helper::sanitize_css_value( $block_attrs['textOnPrimaryColor'] );
58 }
59
60 // Padding.
61 $form_styling = self::map_dimension_attrs( $form_styling, $block_attrs, 'formPadding', 'form_padding' );
62
63 // Border Radius.
64 $form_styling = self::map_dimension_attrs( $form_styling, $block_attrs, 'formBorderRadius', 'form_border_radius' );
65
66 // Background.
67 if ( ! empty( $block_attrs['bgType'] ) && in_array( $block_attrs['bgType'], [ 'color', 'gradient', 'image' ], true ) ) {
68 $form_styling['bg_type'] = $block_attrs['bgType'];
69 }
70 if ( ! empty( $block_attrs['bgColor'] ) ) {
71 $form_styling['bg_color'] = Helper::sanitize_css_value( $block_attrs['bgColor'] );
72 }
73 if ( ! empty( $block_attrs['bgGradient'] ) ) {
74 $form_styling['bg_gradient'] = Helper::sanitize_css_value( $block_attrs['bgGradient'] );
75 }
76 if ( ! empty( $block_attrs['bgImage'] ) ) {
77 $form_styling['bg_image'] = esc_url_raw( Helper::get_string_value( $block_attrs['bgImage'] ) );
78 }
79 if ( ! empty( $block_attrs['bgImagePosition'] ) ) {
80 $form_styling['bg_image_position'] = $block_attrs['bgImagePosition'];
81 }
82 if ( ! empty( $block_attrs['bgImageSize'] ) && in_array( $block_attrs['bgImageSize'], [ 'auto', 'cover', 'contain' ], true ) ) {
83 $form_styling['bg_image_size'] = $block_attrs['bgImageSize'];
84 }
85 if ( ! empty( $block_attrs['bgImageRepeat'] ) && in_array( $block_attrs['bgImageRepeat'], [ 'repeat', 'no-repeat', 'repeat-x', 'repeat-y' ], true ) ) {
86 $form_styling['bg_image_repeat'] = $block_attrs['bgImageRepeat'];
87 }
88 if ( ! empty( $block_attrs['bgImageAttachment'] ) && in_array( $block_attrs['bgImageAttachment'], [ 'scroll', 'fixed', 'local' ], true ) ) {
89 $form_styling['bg_image_attachment'] = $block_attrs['bgImageAttachment'];
90 }
91
92 // Field Spacing and Button Alignment.
93 if ( ! empty( $block_attrs['fieldSpacing'] ) && in_array( $block_attrs['fieldSpacing'], [ 'small', 'medium', 'large' ], true ) ) {
94 $form_styling['field_spacing'] = $block_attrs['fieldSpacing'];
95 }
96 if ( ! empty( $block_attrs['buttonAlignment'] ) && in_array( $block_attrs['buttonAlignment'], [ 'left', 'center', 'right', 'full', 'justify' ], true ) ) {
97 $form_styling['submit_button_alignment'] = $block_attrs['buttonAlignment'];
98 }
99
100 /**
101 * Filter to allow Pro to extend block attribute mapping.
102 *
103 * @param array<string,mixed> $form_styling Modified form styling array.
104 * @param array<string,mixed> $block_attrs Original block attributes.
105 * @since 2.7.0
106 */
107 return apply_filters( 'srfm_embed_block_attrs_to_styling', $form_styling, $block_attrs );
108 }
109
110 /**
111 * Apply theme styling to form styling array.
112 *
113 * Returns theme-specific CSS variable values. In the free version,
114 * this returns the original styling. Pro overrides via filter to
115 * apply predefined theme presets.
116 *
117 * @param array<string,mixed> $form_styling Current form styling array.
118 * @param string $theme_slug Theme identifier (e.g., 'modern', 'minimal').
119 * @return array<string,mixed> Form styling with theme values applied.
120 * @since 2.7.0
121 */
122 public static function apply_theme_styling( $form_styling, $theme_slug ) {
123 if ( empty( $theme_slug ) || 'default' === $theme_slug || 'inherit' === $theme_slug ) {
124 return $form_styling;
125 }
126
127 /**
128 * Filter to apply theme-specific styling.
129 *
130 * Pro uses this filter to merge predefined theme CSS variables
131 * into the form styling array.
132 *
133 * @param array<string,mixed> $form_styling Current form styling array.
134 * @param string $theme_slug Theme identifier.
135 * @since 2.7.0
136 */
137 return apply_filters( 'srfm_apply_form_theme_styling', $form_styling, $theme_slug );
138 }
139
140 /**
141 * Check if embed has custom styling (formTheme is not 'inherit').
142 *
143 * @param array<string,mixed> $block_attrs Block attributes.
144 * @return bool True if using custom embed styling.
145 * @since 2.7.0
146 */
147 public static function has_custom_styling( $block_attrs ) {
148 return 'inherit' !== ( $block_attrs['formTheme'] ?? 'inherit' );
149 }
150
151 /**
152 * Check if the form has default SureForms styling disabled.
153 *
154 * When enabled (via the `_srfm_forms_styling` meta set through REST/MCP, or
155 * the `srfm_disable_default_styles` filter), the form is rendered without the
156 * SureForms frontend stylesheets and inline CSS variables so the site's own
157 * CSS fully controls the form's appearance.
158 *
159 * @param int|string $form_id Form post ID.
160 * @return bool True when default styling is disabled for the form.
161 * @since 2.12.2
162 */
163 public static function is_default_styling_disabled( $form_id ) {
164 $form_id = absint( $form_id );
165 if ( ! $form_id ) {
166 return false;
167 }
168
169 $form_styling = get_post_meta( $form_id, '_srfm_forms_styling', true );
170 $disabled = is_array( $form_styling ) && ! empty( $form_styling['disable_default_styles'] );
171
172 /**
173 * Filters whether SureForms' default frontend styling is disabled for a form.
174 *
175 * Lets themes/plugins toggle the unstyled mode programmatically, overriding the
176 * stored per-form meta. Return true to render the form without SureForms' default
177 * stylesheets and inline CSS variables.
178 *
179 * @param bool $disabled Whether default styling is disabled (from meta).
180 * @param int $form_id Form post ID.
181 * @since 2.12.2
182 */
183 return (bool) apply_filters( 'srfm_disable_default_styles', $disabled, $form_id );
184 }
185
186 /**
187 * Check whether the SureForms frontend stylesheets can be skipped for a post.
188 *
189 * Returns true only when every SureForms form found on the post has default
190 * styling disabled. When the forms on the post cannot be determined, the
191 * stylesheets are kept as the safe default.
192 *
193 * @param \WP_Post $post Post being rendered.
194 * @return bool True when the frontend stylesheets can be skipped.
195 * @since 2.12.2
196 */
197 public static function should_skip_frontend_styles( $post ) {
198 if ( SRFM_FORMS_POST_TYPE === $post->post_type ) {
199 $form_ids = [ $post->ID ];
200 } else {
201 $form_ids = self::get_form_ids_from_content( $post->post_content );
202 }
203
204 if ( empty( $form_ids ) ) {
205 return false;
206 }
207
208 foreach ( $form_ids as $form_id ) {
209 if ( ! self::is_default_styling_disabled( $form_id ) ) {
210 return false;
211 }
212 }
213
214 return true;
215 }
216
217 /**
218 * Extract SureForms form IDs from post content.
219 *
220 * Looks for srfm/form blocks (including nested ones) and [sureforms]
221 * shortcodes.
222 *
223 * @param string $content Post content.
224 * @return array<int> Unique form IDs found in the content.
225 * @since 2.12.2
226 */
227 public static function get_form_ids_from_content( $content ) {
228 $form_ids = [];
229
230 // Also parse when the content embeds reusable/synced patterns (core/block
231 // refs) — a form inside a pattern appears as wp:block {"ref":N}, which
232 // has_block( 'srfm/form' ) alone would never see.
233 if ( has_block( 'srfm/form', $content ) || has_block( 'core/block', $content ) ) {
234 $visited_refs = [];
235 $form_ids = self::collect_form_block_ids( parse_blocks( $content ), $visited_refs );
236 }
237
238 if ( has_shortcode( $content, 'sureforms' ) && preg_match_all( '/' . get_shortcode_regex( [ 'sureforms' ] ) . '/', $content, $matches ) ) {
239 foreach ( $matches[3] as $atts_string ) {
240 $atts = shortcode_parse_atts( $atts_string );
241 if ( is_array( $atts ) && ! empty( $atts['id'] ) ) {
242 $form_ids[] = absint( $atts['id'] );
243 }
244 }
245 }
246
247 return array_values( array_unique( array_filter( $form_ids ) ) );
248 }
249
250 /**
251 * Recursively collect form IDs from parsed srfm/form blocks, following
252 * reusable/synced pattern references (core/block) into their wp_block posts.
253 *
254 * @param array<mixed> $blocks Parsed blocks from parse_blocks().
255 * @param array<int, true> $visited_refs Reusable-block post IDs already expanded,
256 * keyed by ID — guards against reference cycles.
257 * @return array<int> Form IDs found in srfm/form blocks.
258 * @since 2.12.2
259 */
260 private static function collect_form_block_ids( $blocks, &$visited_refs = [] ) {
261 $form_ids = [];
262
263 foreach ( $blocks as $block ) {
264 if ( ! is_array( $block ) ) {
265 continue;
266 }
267 $attrs = isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : [];
268 if ( isset( $block['blockName'] ) && 'srfm/form' === $block['blockName'] && ! empty( $attrs['id'] ) && is_scalar( $attrs['id'] ) ) {
269 $form_ids[] = absint( $attrs['id'] );
270 }
271 // Reusable/synced pattern: expand the referenced wp_block post so a
272 // form living inside a pattern is detected like an inline block.
273 if ( isset( $block['blockName'] ) && 'core/block' === $block['blockName'] && ! empty( $attrs['ref'] ) && is_scalar( $attrs['ref'] ) ) {
274 $ref = absint( $attrs['ref'] );
275 if ( $ref && ! isset( $visited_refs[ $ref ] ) ) {
276 $visited_refs[ $ref ] = true;
277 $ref_post = get_post( $ref );
278 if ( $ref_post instanceof \WP_Post && 'wp_block' === $ref_post->post_type && 'publish' === $ref_post->post_status && '' !== $ref_post->post_content ) {
279 $form_ids = array_merge( $form_ids, self::collect_form_block_ids( parse_blocks( $ref_post->post_content ), $visited_refs ) );
280 }
281 }
282 }
283 if ( ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) {
284 $form_ids = array_merge( $form_ids, self::collect_form_block_ids( $block['innerBlocks'], $visited_refs ) );
285 }
286 }
287
288 return $form_ids;
289 }
290
291 /**
292 * Map dimension block attributes (Top/Right/Bottom/Left/Unit) to form styling.
293 *
294 * @param array<string,mixed> $form_styling Form styling array.
295 * @param array<string,mixed> $block_attrs Block attributes.
296 * @param string $attr_prefix Block attribute prefix (e.g., 'formPadding').
297 * @param string $style_prefix Form styling key prefix (e.g., 'form_padding').
298 * @return array<string,mixed> Modified form styling array.
299 * @since 2.7.0
300 */
301 private static function map_dimension_attrs( $form_styling, $block_attrs, $attr_prefix, $style_prefix ) {
302 $sides = [ 'Top', 'Right', 'Bottom', 'Left' ];
303
304 foreach ( $sides as $side ) {
305 $attr_key = $attr_prefix . $side;
306 $style_key = $style_prefix . '_' . strtolower( $side );
307
308 if ( isset( $block_attrs[ $attr_key ] ) && is_scalar( $block_attrs[ $attr_key ] ) ) {
309 $form_styling[ $style_key ] = floatval( $block_attrs[ $attr_key ] );
310 }
311 }
312
313 $unit_attr_key = $attr_prefix . 'Unit';
314 $unit_style_key = $style_prefix . '_unit';
315
316 if ( ! empty( $block_attrs[ $unit_attr_key ] ) && in_array( $block_attrs[ $unit_attr_key ], [ 'px', 'em', 'rem', '%', 'vw', 'vh' ], true ) ) {
317 $form_styling[ $unit_style_key ] = $block_attrs[ $unit_attr_key ];
318 }
319
320 return $form_styling;
321 }
322 }
323