PluginProbe
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management / 1.6.1
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management v1.6.1
1.6.1 1.6.0 1.5.1 1.5.0 1.4.0 1.3.0 trunk 0.0.1 1.0.0 1.1.0 1.1.1 1.1.2 1.2.0
← All changes | inc/fields/form-styling.php +139 -27 1.0.0 → 1.6.1 View file →
@@ -90,36 +90,40 @@
90 90 * @since 1.0.0
91 91 */
92 92 public static function get_defaults() {
93 93 return [
94 - 'bgType' => 'color',
95 - 'bgColor' => '',
96 - 'bgGradient' => 'linear-gradient(90deg,#FFC9B2 0%,#C7CBFF 100%)',
97 - 'bgImage' => '',
98 - 'bgImageId' => 0,
99 - 'bgImageSize' => 'cover',
100 - 'bgImagePosition' => 'center center',
101 - 'bgImageRepeat' => 'no-repeat',
94 + 'bgType' => 'color',
95 + 'bgColor' => '',
96 + 'bgGradient' => 'linear-gradient(90deg,#FFC9B2 0%,#C7CBFF 100%)',
97 + 'bgImage' => '',
98 + 'bgImageId' => 0,
99 + 'bgImageSize' => 'cover',
100 + 'bgImagePosition' => 'center center',
101 + 'bgImageRepeat' => 'no-repeat',
102 102 // Colors default to empty here so unset values fall through to the
103 103 // :root defaults in _variables.scss (the editor's STYLE_DEFAULTS seeds
104 104 // the actual hex values instead, only to populate the panel swatches).
105 - 'primaryColor' => '',
106 - 'textColor' => '',
107 - 'textOnPrimaryColor' => '',
108 - 'padding' => [
105 + 'primaryColor' => '',
106 + 'textColor' => '',
107 + 'textOnPrimaryColor' => '',
108 + 'padding' => [
109 109 'top' => '',
110 110 'right' => '',
111 111 'bottom' => '',
112 112 'left' => '',
113 113 ],
114 - 'borderRadius' => [
114 + 'borderRadius' => [
115 115 'top' => '',
116 116 'right' => '',
117 117 'bottom' => '',
118 118 'left' => '',
119 119 ],
120 - 'fieldSpacing' => 'medium',
121 - 'buttonAlignment' => 'justify',
120 + 'fieldSpacing' => 'medium',
121 + 'buttonAlignment' => 'justify',
122 + // When true the form renders without the SureDonation stylesheet and
123 + // inline CSS variables so the site's own CSS fully controls its
124 + // appearance (mirrors SureForms' disable_default_styles).
125 + 'disable_default_styles' => false,
122 126 ];
123 127 }
124 128
125 129 /**
@@ -187,13 +191,52 @@
187 191 $clean['borderRadius'] = self::sanitize_box( $decoded['borderRadius'] ?? [], $defaults['borderRadius'] );
188 192 $clean['fieldSpacing'] = in_array( $decoded['fieldSpacing'] ?? '', [ 'small', 'medium', 'large' ], true ) ? $decoded['fieldSpacing'] : 'medium';
189 193 $clean['buttonAlignment'] = in_array( $decoded['buttonAlignment'] ?? '', [ 'left', 'center', 'right', 'justify' ], true ) ? $decoded['buttonAlignment'] : 'justify';
190 194
195 + // Boolean flag, not a style value — must survive sanitization or an
196 + // editor save silently re-enables the default styling.
197 + $clean['disable_default_styles'] = ! empty( $decoded['disable_default_styles'] );
198 +
191 199 $encoded = wp_json_encode( $clean );
192 200 return is_string( $encoded ) ? $encoded : '';
193 201 }
194 202
195 203 /**
204 + * Check whether the form renders without SureDonation's default styling.
205 + *
206 + * When enabled the frontend stylesheet is not enqueued for the form and the
207 + * inline CSS-variable style attribute is omitted, so the site's own CSS
208 + * fully controls the form's appearance. The container is stamped with an
209 + * `sd-styling-none` marker class so custom CSS can target the state.
210 + *
211 + * @param int $form_id Form post ID.
212 + * @return bool True when default styling is disabled for the form.
213 + * @since 1.4.0
214 + */
215 + public static function is_default_styling_disabled( $form_id ) {
216 + $form_id = absint( $form_id );
217 + if ( ! $form_id ) {
218 + return false;
219 + }
220 +
221 + $settings = self::get_settings( $form_id );
222 + $disabled = ! empty( $settings['disable_default_styles'] );
223 +
224 + /**
225 + * Filters whether SureDonation's default frontend styling is disabled for a form.
226 + *
227 + * Lets themes/plugins toggle the unstyled mode programmatically, overriding
228 + * the stored per-form meta. Return true to render the form without the
229 + * SureDonation stylesheet and inline CSS variables.
230 + *
231 + * @param bool $disabled Whether default styling is disabled (from meta).
232 + * @param int $form_id Form post ID.
233 + * @since 1.4.0
234 + */
235 + return (bool) apply_filters( 'suredonation_disable_default_styles', $disabled, $form_id );
236 + }
237 +
238 + /**
196 239 * Build the inline CSS custom-property string for the form wrapper.
197 240 *
198 241 * Returns the CSS declarations only (no surrounding style attribute). The
199 242 * caller is expected to output the result via esc_attr() inside a style
@@ -203,18 +246,46 @@
203 246 * @return string CSS declarations, or '' when nothing is customized.
204 247 * @since 1.0.0
205 248 */
206 249 public static function get_style_attr( $form_id ) {
250 + // Unstyled mode: no inline CSS variables either — an inline style on the
251 + // container would override any site/custom CSS that themes the form.
252 + if ( self::is_default_styling_disabled( $form_id ) ) {
253 + return '';
254 + }
255 +
207 256 $settings = self::get_settings( $form_id );
208 257 $vars = [];
209 258
210 - // Colors.
259 + // Colors. Color-derived tints are emitted per-form so they track the
260 + // chosen color: the static :root @supports defaults in _variables.scss are
261 + // computed from the default brand/text colors and cannot see a per-form
262 + // override (they live on :root, the override on the form container), so
263 + // without this the button hover and field tints stay default. Ratios
264 + // mirror that @supports block and SureForms' inc/generate-form-markup.php.
265 + // Keep in sync with buildStyleVars() in src/editor/form-style-vars.js.
211 266 if ( '' !== $settings['primaryColor'] ) {
212 - $vars['--sd-color-scheme-primary'] = $settings['primaryColor'];
267 + $primary = $settings['primaryColor'];
268 + $vars['--sd-color-scheme-primary'] = $primary;
269 + $vars['--sd-color-scheme-primary-hover'] = "hsl(from {$primary} h s l / 0.9)";
270 + $vars['--sd-color-input-border-hover'] = "hsl(from {$primary} h s l / 0.65)";
271 + $vars['--sd-color-input-border-focus-glow'] = "hsl(from {$primary} h s l / 0.15)";
272 + $vars['--sd-color-input-selected'] = "hsl(from {$primary} h s l / 0.1)";
213 273 }
214 274 if ( '' !== $settings['textColor'] ) {
215 - $vars['--sd-color-input-text'] = $settings['textColor'];
216 - $vars['--sd-color-input-label'] = $settings['textColor'];
275 + $text = $settings['textColor'];
276 + $vars['--sd-color-input-text'] = $text;
277 + $vars['--sd-color-input-label'] = $text;
278 + $vars['--sd-color-input-description'] = "hsl(from {$text} h s l / 0.65)";
279 + $vars['--sd-color-input-placeholder'] = "hsl(from {$text} h s l / 0.5)";
280 + $vars['--sd-color-input-background'] = "hsl(from {$text} h s l / 0.02)";
281 + $vars['--sd-color-input-background-hover'] = "hsl(from {$text} h s l / 0.05)";
282 + $vars['--sd-color-input-border'] = "hsl(from {$text} h s l / 0.25)";
283 + $vars['--sd-color-donation-amount-svg'] = "hsl(from {$text} h s l / 0.7)";
284 + $vars['--sd-color-input-prefix'] = "hsl(from {$text} h s l / 0.65)";
285 + $vars['--sd-disabled-color'] = "hsl(from {$text} h s l / 0.5)";
286 + $vars['--sd-disabled-background-color'] = "hsl(from {$text} h s l / 0.07)";
287 + $vars['--sd-disabled-border'] = "hsl(from {$text} h s l / 0.15)";
217 288 }
218 289 if ( '' !== $settings['textOnPrimaryColor'] ) {
219 290 $vars['--sd-btn-text-color'] = $settings['textOnPrimaryColor'];
220 291 }
@@ -253,14 +324,42 @@
253 324 $vars['--sd-btn-align-items'] = $align_map[ $settings['buttonAlignment'] ];
254 325 $vars['--sd-btn-width'] = 'auto';
255 326 }
256 327
257 - if ( empty( $vars ) ) {
328 + /**
329 + * Filter the form style CSS custom properties before they are serialized
330 + * onto the `.sd-form-container` wrapper.
331 + *
332 + * Add-ons (e.g. SureDonation Pro) use this to contribute additional
333 + * `--sd-*` variables. Runs before the empty-check so an add-on can style a
334 + * form even when the free panel set nothing. Values must be pre-sanitized
335 + * CSS tokens — they are emitted verbatim inside the inline style attribute.
336 + *
337 + * @param array<string, string> $vars Map of `--sd-*` variable => value.
338 + * @param int $form_id Form post ID.
339 + * @param array<string, mixed> $settings Merged free style settings.
340 + * @since 1.5.0
341 + */
342 + $vars = apply_filters( 'suredonation_form_style_vars', $vars, (int) $form_id, $settings );
343 +
344 + if ( ! is_array( $vars ) || empty( $vars ) ) {
258 345 return '';
259 346 }
260 347
261 348 $declarations = [];
262 349 foreach ( $vars as $name => $value ) {
350 + // Defense-in-depth for the public filter above: only emit custom
351 + // properties with scalar, declaration-safe values, so a
352 + // non-sanitizing add-on callback cannot append arbitrary
353 + // declarations or trigger array-to-string notices. Values are
354 + // additionally escaped by the caller via esc_attr().
355 + if (
356 + ! is_scalar( $value )
357 + || ! preg_match( '/^--[A-Za-z0-9_-]+$/', (string) $name )
358 + || preg_match( '/[;{}]/', (string) $value )
359 + ) {
360 + continue;
361 + }
263 362 $declarations[] = $name . ':' . $value;
264 363 }
265 364
266 365 return implode( ';', $declarations ) . ';';
@@ -336,9 +435,9 @@
336 435 * @param array<string, mixed> $fallback Default box.
337 436 * @return array<string, mixed>
338 437 * @since 1.0.0
339 438 */
340 - private static function sanitize_box( $box, $fallback ) {
439 + public static function sanitize_box( $box, $fallback ) {
341 440 if ( ! is_array( $box ) ) {
342 441 return $fallback;
343 442 }
344 443
@@ -352,21 +451,25 @@
352 451
353 452 /**
354 453 * Validate a CSS length (e.g. "10px", "1.5rem"); bare numbers become px.
355 454 *
455 + * Negative values are rejected: every consumer here (padding, border
456 + * radius) is invalid with a negative length, which the browser would
457 + * silently drop.
458 + *
356 459 * @param mixed $value Incoming value.
357 - * @return string Valid length, or '' when invalid/empty.
460 + * @return string Valid length, or '' when invalid/empty/negative.
358 461 * @since 1.0.0
359 462 */
360 - private static function sanitize_length( $value ) {
463 + public static function sanitize_length( $value ) {
361 464 if ( is_numeric( $value ) ) {
362 - return ( 0 + $value ) . 'px';
465 + return $value < 0 ? '' : ( 0 + $value ) . 'px';
363 466 }
364 467 $value = is_string( $value ) ? trim( $value ) : '';
365 468 if ( '' === $value ) {
366 469 return '';
367 470 }
368 - return preg_match( '/^-?\d*\.?\d+(px|em|rem|%|vw|vh)$/', $value ) ? $value : '';
471 + return preg_match( '/^\d*\.?\d+(px|em|rem|%|vw|vh)$/', $value ) ? $value : '';
369 472 }
370 473
371 474 /**
372 475 * Sanitize a color value via a strict allowlist.
@@ -378,9 +481,9 @@
378 481 * @param mixed $value Incoming color.
379 482 * @return string Valid color, or '' when invalid/empty.
380 483 * @since 1.0.0
381 484 */
382 - private static function sanitize_color( $value ) {
485 + public static function sanitize_color( $value ) {
383 486 $value = is_string( $value ) ? trim( $value ) : '';
384 487 if ( '' === $value ) {
385 488 return '';
386 489 }
@@ -395,8 +498,17 @@
395 498 // Named color — letters only, so it cannot contain parens/url()/escapes.
396 499 if ( preg_match( '/^[a-z]+$/i', $value ) ) {
397 500 return $value;
398 501 }
502 + // CSS custom-property reference for theme/global palette colors, e.g.
503 + // var(--wp--preset--color--primary), with an optional safe fallback
504 + // (hex / named / numeric rgb()|hsl() / one nested var). The property name
505 + // is restricted to [A-Za-z0-9_-] and the whole value is anchored, so it
506 + // cannot contain quotes, semicolons, url() or escapes that would break out
507 + // of the inline style attribute.
508 + if ( preg_match( '/^var\(\s*--[A-Za-z0-9_-]+\s*(,\s*(#[A-Fa-f0-9]{3,8}|[A-Za-z]+|(?:rgb|rgba|hsl|hsla)\([0-9.,%\/\s]+\)|var\(\s*--[A-Za-z0-9_-]+\s*\)))?\s*\)$/i', $value ) ) {
509 + return $value;
510 + }
399 511 return '';
400 512 }
401 513
402 514 /**
@@ -409,9 +521,9 @@
409 521 * @param mixed $value Incoming gradient.
410 522 * @return string Valid gradient, or '' when invalid/empty.
411 523 * @since 1.0.0
412 524 */
413 - private static function sanitize_gradient( $value ) {
525 + public static function sanitize_gradient( $value ) {
414 526 $value = is_string( $value ) ? trim( $value ) : '';
415 527 if ( '' === $value ) {
416 528 return '';
417 529 }