PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.26.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.26.1
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / helpers / FrmStylesHelper.php

FrmStylesHelper.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.26.1, at classes/helpers/FrmStylesHelper.php

1,171 lines 29.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmStylesHelper {
7
8 /**
9 * @return array
10 */
11 public static function get_upload_base() {
12 $uploads = wp_upload_dir();
13
14 if ( is_ssl() && ! preg_match( '/^https:\/\/.*\..*$/', $uploads['baseurl'] ) ) {
15 $uploads['baseurl'] = str_replace( 'http://', 'https://', $uploads['baseurl'] );
16 }
17
18 return $uploads;
19 }
20
21 /**
22 * Called from the admin header.
23 *
24 * @since 4.0
25 *
26 * @return void
27 */
28 public static function save_button() {
29 ?>
30 <input type="submit" name="submit" class="button button-primary frm-button-primary" value="<?php esc_attr_e( 'Update', 'formidable' ); ?>" />
31 <?php
32 }
33
34 /**
35 * @since 2.05
36 *
37 * @return array
38 */
39 public static function get_css_label_positions() {
40 return array(
41 'none' => __( 'top', 'formidable' ),
42 'left' => __( 'left', 'formidable' ),
43 'right' => __( 'right', 'formidable' ),
44 'no_label' => __( 'none', 'formidable' ),
45 'inside' => __( 'inside', 'formidable' ),
46 );
47 }
48
49 /**
50 * @since 6.11 Added $field param.
51 *
52 * @param array|object $field
53 *
54 * @return array
55 */
56 public static function get_single_label_positions( $field = array() ) {
57 $label_positions = array(
58 'top' => __( 'Top', 'formidable' ),
59 'left' => __( 'Left', 'formidable' ),
60 'right' => __( 'Right', 'formidable' ),
61 'inline' => __( 'Inline (left without a set width)', 'formidable' ),
62 'none' => __( 'None', 'formidable' ),
63 'hidden' => __( 'Hidden (but leave the space)', 'formidable' ),
64 'inside' => __( 'Placeholder inside the field', 'formidable' ),
65 );
66
67 /**
68 * Allows updating label positions in field settings.
69 *
70 * @since 6.11
71 *
72 * @param array $label_positions
73 * @param array|object $field
74 */
75 return apply_filters( 'frm_single_label_positions', $label_positions, $field );
76 }
77
78 /**
79 * @return array
80 */
81 public static function minus_icons() {
82 return array(
83 0 => array(
84 '-' => '62e',
85 '+' => '62f',
86 ),
87 1 => array(
88 '-' => '600',
89 '+' => '602',
90 ),
91 2 => array(
92 '-' => '604',
93 '+' => '603',
94 ),
95 3 => array(
96 '-' => '633',
97 '+' => '632',
98 ),
99 4 => array(
100 '-' => '613',
101 '+' => '60f',
102 ),
103 );
104 }
105
106 /**
107 * @return array
108 */
109 public static function arrow_icons() {
110 $minus_icons = self::minus_icons();
111
112 return array(
113 6 => array(
114 '-' => '62d',
115 '+' => '62a',
116 ),
117 0 => array(
118 '-' => '60d',
119 '+' => '609',
120 ),
121 1 => array(
122 '-' => '60e',
123 '+' => '60c',
124 ),
125 2 => array(
126 '-' => '630',
127 '+' => '631',
128 ),
129 3 => array(
130 '-' => '62b',
131 '+' => '628',
132 ),
133 4 => array(
134 '-' => '62c',
135 '+' => '629',
136 ),
137 5 => array(
138 '-' => '635',
139 '+' => '634',
140 ),
141 'p0' => $minus_icons[0],
142 'p1' => $minus_icons[1],
143 'p2' => $minus_icons[2],
144 'p3' => $minus_icons[3],
145 'p4' => $minus_icons[4],
146 );
147 }
148
149 /**
150 * @since 2.0
151 *
152 * @param int|string $key Icon key.
153 * @param string $icon Icon state, `+` or `-`.
154 * @param string $type Icon type, such as `arrow`.
155 *
156 * @return string The class for this icon.
157 */
158 public static function icon_key_to_class( $key, $icon = '+', $type = 'arrow' ) {
159 if ( 'arrow' === $type && is_numeric( $key ) ) {
160 // frm_arrowup6_icon.
161 $arrow = array(
162 '-' => 'down',
163 '+' => 'up',
164 );
165 $class = 'frm_arrow' . $arrow[ $icon ];
166 } else {
167 // frm_minus1_icon.
168 $key = str_replace( 'p', '', $key );
169 $plus = array(
170 '-' => 'minus',
171 '+' => 'plus',
172 );
173 $class = 'frm_' . $plus[ $icon ];
174 }
175
176 if ( $key ) {
177 $class .= $key;
178 }
179
180 $class .= '_icon';
181
182 return $class;
183 }
184
185 /**
186 * @param WP_Post $style
187 * @param FrmStyle $frm_style
188 * @param string $type
189 *
190 * @return void
191 */
192 public static function bs_icon_select( $style, $frm_style, $type = 'arrow' ) {
193 $function_name = $type . '_icons';
194 $icons = self::$function_name();
195 unset( $function_name );
196
197 $name = 'arrow' === $type ? 'collapse_icon' : 'repeat_icon';
198 ?>
199 <div class="btn-group" id="frm_<?php echo esc_attr( $name ); ?>_select">
200 <button class="multiselect dropdown-toggle btn btn-default" data-toggle="dropdown" type="button">
201 <?php FrmAppHelper::icon_by_class( 'frmfont ' . self::icon_key_to_class( $style->post_content[ $name ], '+', $type ) ); ?>
202 <?php FrmAppHelper::icon_by_class( 'frmfont ' . self::icon_key_to_class( $style->post_content[ $name ], '-', $type ) ); ?>
203 <b class="caret"></b>
204 </button>
205 <ul class="multiselect-container frm-dropdown-menu">
206 <?php foreach ( $icons as $key => $icon ) { ?>
207 <li <?php echo $style->post_content['collapse_icon'] == $key ? 'class="active"' : ''; ?>>
208 <a href="javascript:void(0);">
209 <label>
210 <input type="radio" value="<?php echo esc_attr( $key ); ?>" name="<?php echo esc_attr( $frm_style->get_field_name( $name ) ); ?>" <?php checked( $style->post_content[ $name ], $key ); ?> />
211 <span>
212 <?php
213 FrmAppHelper::icon_by_class( 'frmfont ' . self::icon_key_to_class( $key, '+', $type ) );
214 FrmAppHelper::icon_by_class( 'frmfont ' . self::icon_key_to_class( $key, '-', $type ) );
215 ?>
216 </span>
217 </label>
218 </a>
219 </li>
220 <?php } ?>
221 </ul>
222 </div>
223 <?php
224 }
225
226 /**
227 * Convert a color setting to a RGB CSV (without the rgb()/rgba() wrapper).
228 *
229 * @since 2.0
230 *
231 * @param string $color Color setting value. This could be hex or rgb.
232 *
233 * @return string RGB value without the rgb() wrapper.
234 */
235 public static function hex2rgb( $color ) {
236 if ( 0 === strpos( $color, 'rgb' ) ) {
237 $rgb = self::get_rgb_array_from_rgb( $color );
238 } else {
239 $rgb = self::get_rgb_array_from_hex( $color );
240 }
241 return implode( ',', $rgb );
242 }
243
244 /**
245 * Remove the rgb()/rgba() wrapper from a RGB color and return its R, G and B values as an array.
246 *
247 * @since 6.8.3
248 *
249 * @param string $rgb RGB value including the rgb() or rgba() wrapper.
250 *
251 * @return array<string> including three numeric values for R, G, and B.
252 */
253 private static function get_rgb_array_from_rgb( $rgb ) {
254 $rgb = str_replace( array( 'rgb(', 'rgba(', ')' ), '', $rgb );
255 $rgb = explode( ',', $rgb );
256
257 if ( 4 === count( $rgb ) ) {
258 // Drop the alpha. The function is expected to only return r,g,b with no alpha.
259 array_pop( $rgb );
260 }
261 return $rgb;
262 }
263
264 /**
265 * Get the R, G, and B array values from a Hex color code.
266 *
267 * @since 6.8.3
268 *
269 * @param string $hex A hex color string.
270 *
271 * @return array<string> Including three numeric values for R, G, and B.
272 */
273 private static function get_rgb_array_from_hex( $hex ) {
274 $hex = str_replace( '#', '', $hex );
275 list( $r, $g, $b ) = sscanf( $hex, '%02x%02x%02x' );
276 return array( $r, $g, $b );
277 }
278
279 /**
280 * @since 4.0
281 *
282 * @param string $hex Hex color string.
283 * @param float|int|string $a Alpha channel value.
284 *
285 * @return string
286 */
287 public static function hex2rgba( $hex, $a ) {
288 $rgb = self::hex2rgb( $hex );
289
290 return 'rgba(' . $rgb . ',' . $a . ')';
291 }
292
293 /**
294 * @since 6.0
295 *
296 * @param string $rgba Color setting value. This could be hex or rgb.
297 *
298 * @return string Hex color value.
299 */
300 private static function rgb_to_hex( $rgba ) {
301 if ( strpos( $rgba, '#' ) === 0 ) {
302 // Color is already hex.
303 return $rgba;
304 }
305 preg_match( '/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i', $rgba, $by_color );
306 return sprintf( '%02x%02x%02x', $by_color[1], $by_color[2], $by_color[3] );
307 }
308
309 /**
310 * @since 6.8
311 *
312 * @param string $hsl
313 *
314 * @return string|null Null if it fails to parse the HSL string.
315 */
316 private static function hsl_to_hex( $hsl ) {
317 // Convert hsla to hsl.
318 $hsl = preg_replace( '/hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/', 'hsl($1, $2%, $3%)', $hsl );
319
320 // Extract HSL components from the color string.
321 preg_match( '/hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)/', $hsl, $matches );
322
323 if ( count( $matches ) !== 4 ) {
324 // Invalid HSL string format.
325 return null;
326 }
327
328 // Extract HSL values.
329 $h = (int) $matches[1];
330 $s = (int) $matches[2] / 100;
331 $l = (int) $matches[3] / 100;
332
333 // Calculate RGB values.
334 $c = ( 1 - abs( 2 * $l - 1 ) ) * $s;
335 $x = $c * ( 1 - abs( ( (int) ( $h / 60 ) % 2 ) - 1 ) );
336 $m = $l - $c / 2;
337 $r = 0;
338 $g = 0;
339 $b = 0;
340
341 if ( $h >= 0 && $h < 60 ) {
342 $r = $c;
343 $g = $x;
344 } elseif ( $h >= 60 && $h < 120 ) {
345 $r = $x;
346 $g = $c;
347 } elseif ( $h >= 120 && $h < 180 ) {
348 $g = $c;
349 $b = $x;
350 } elseif ( $h >= 180 && $h < 240 ) {
351 $g = $x;
352 $b = $c;
353 } elseif ( $h >= 240 && $h < 300 ) {
354 $r = $x;
355 $b = $c;
356 } elseif ( $h >= 300 && $h < 360 ) {
357 $r = $c;
358 $b = $x;
359 }//end if
360
361 // Convert RGB to 8-bit values
362 $r = round( ( $r + $m ) * 255 );
363 $g = round( ( $g + $m ) * 255 );
364 $b = round( ( $b + $m ) * 255 );
365
366 // Convert RGB to hex
367 $hex = sprintf( '%02x%02x%02x', $r, $g, $b );
368
369 return $hex;
370 }
371
372 /**
373 * @since 2.3
374 *
375 * @param string $hex string The original color in hex format #ffffff.
376 * @param int $steps integer Should be between -255 and 255. Negative = darker, positive = lighter.
377 *
378 * @return string
379 */
380 public static function adjust_brightness( $hex, $steps ) {
381 $steps = max( - 255, min( 255, $steps ) );
382
383 if ( 0 === strpos( $hex, 'rgba(' ) ) {
384 $rgba = str_replace( ')', '', str_replace( 'rgba(', '', $hex ) );
385 list ( $r, $g, $b, $a ) = array_map( 'trim', explode( ',', $rgba ) );
386 $r = max( 0, min( 255, $r + $steps ) );
387 $g = max( 0, min( 255, $g + $steps ) );
388 $b = max( 0, min( 255, $b + $steps ) );
389 return 'rgba(' . $r . ',' . $g . ',' . $b . ',' . $a . ')';
390 }
391
392 // Normalize into a six character long hex string
393 $hex = str_replace( '#', '', $hex );
394 self::fill_hex( $hex );
395
396 // Split into three parts: R, G and B
397 $color_parts = str_split( $hex, 2 );
398 $return = '#';
399
400 foreach ( $color_parts as $color ) {
401 // Convert to decimal.
402 $color = hexdec( $color );
403 // Adjust color.
404 $color = max( 0, min( 255, $color + $steps ) );
405
406 // Make two char hex code.
407 $return .= str_pad( dechex( $color ), 2, '0', STR_PAD_LEFT );
408 }
409
410 return $return;
411 }
412
413 /**
414 * @since 6.0
415 *
416 * @param string $color
417 *
418 * @return int
419 */
420 public static function get_color_brightness( $color ) {
421 if ( 0 === strpos( $color, 'rgb' ) ) {
422 $color = self::rgb_to_hex( $color );
423 }
424
425 if ( 0 === strpos( $color, 'hsl' ) ) {
426 $hsl_to_hex = self::hsl_to_hex( $color );
427
428 if ( is_null( $hsl_to_hex ) ) {
429 // Fallback if we cannot convert the HSL value.
430 return 0;
431 }
432
433 $color = $hsl_to_hex;
434 }
435
436 self::fill_hex( $color );
437
438 $c_r = hexdec( substr( $color, 0, 2 ) );
439 $c_g = hexdec( substr( $color, 2, 2 ) );
440 $c_b = hexdec( substr( $color, 4, 2 ) );
441 $brightness = ( ( $c_r * 299 ) + ( $c_g * 587 ) + ( $c_b * 114 ) ) / 1000;
442
443 return $brightness;
444 }
445
446 /**
447 * Change a 3 character hex color to a 6 character one.
448 *
449 * @since 6.0
450 *
451 * @param string $color Color value, passed by reference.
452 *
453 * @return void
454 */
455 private static function fill_hex( &$color ) {
456 if ( 3 === strlen( $color ) ) {
457 $color = $color[0] . $color[0] . $color[1] . $color[1] . $color[2] . $color[2];
458 }
459 }
460
461 /**
462 * @since 4.05.02
463 *
464 * @param array $vars CSS variable keys.
465 *
466 * @return array
467 */
468 public static function get_css_vars( $vars = array() ) {
469 $vars = apply_filters( 'frm_css_vars', $vars );
470 return array_unique( $vars );
471 }
472
473 /**
474 * @since 4.05.02
475 *
476 * @param array $settings Style settings.
477 * @param array $defaults Default style settings.
478 * @param array $vars CSS variable keys to output.
479 *
480 * @return void
481 */
482 public static function output_vars( $settings, $defaults = array(), $vars = array() ) {
483 if ( empty( $vars ) ) {
484 $vars = self::get_css_vars( array_keys( $settings ) );
485 }
486
487 $vars = array_diff( $vars, self::get_style_keys_to_remove_from_output_vars() );
488
489 foreach ( $vars as $var ) {
490 if ( ! isset( $settings[ $var ] ) || ! self::css_key_is_valid( $var ) ) {
491 continue;
492 }
493
494 if ( ! isset( $defaults[ $var ] ) ) {
495 $defaults[ $var ] = '';
496 }
497
498 $prepared_value = '';
499
500 if ( self::should_add_css_var( $settings, $defaults, $var, $prepared_value ) ) {
501 echo '--' . esc_html( self::clean_var_name( str_replace( '_', '-', $var ) ) ) . ':' . $prepared_value . ';'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
502 }
503 }
504 }
505
506 /**
507 * None of these style settings are used as CSS variables, so we want to exclude them to keep the CSS output clean.
508 *
509 * @since 6.26.1
510 *
511 * @return array<string>
512 */
513 private static function get_style_keys_to_remove_from_output_vars() {
514 return array(
515 'remove_box_shadow',
516 'remove_box_shadow_active',
517 'theme_css',
518 'theme_name',
519 'theme_selector',
520 'important_style',
521 'submit_style',
522 'collapse_icon',
523 'center_form',
524 'custom_css',
525 'style_class',
526 'submit_bg_img',
527 'change_margin',
528 'repeat_icon',
529 'use_base_font_size',
530 'field_shape_type',
531 'bg_image_id',
532 );
533 }
534
535 /**
536 * Check if a CSS variable setting is not blank, doesn't match the default, and doesn't include invalid substrings.
537 *
538 * @since 6.24
539 *
540 * @param array $settings Array of setting values.
541 * @param array $defaults Array of default values.
542 * @param string $var The setting key name.
543 * @param string $prepared_value The value from calling css_var_prepare_value. This is set by reference so it can be used after this function is called.
544 *
545 * @return bool True if the CSS value should be printed.
546 */
547 private static function should_add_css_var( $settings, $defaults, $var, &$prepared_value ) {
548 $prepared_value = self::css_var_prepare_value( $settings, $var );
549
550 if ( $prepared_value === '' ) {
551 return false;
552 }
553
554 if ( $defaults && $defaults[ $var ] === $prepared_value ) {
555 return false;
556 }
557
558 return self::css_value_is_valid( $prepared_value );
559 }
560
561 /**
562 * Prevent invalid CSS keys from getting added to the generated CSS.
563 *
564 * @since 6.20
565 *
566 * @param string $key
567 *
568 * @return bool
569 */
570 private static function css_key_is_valid( $key ) {
571 // Any key that is abnormally large is not valid.
572 // Any key that contains a '{' is not valid.
573 return strlen( $key ) < 100 && false === strpos( $key, '{' );
574 }
575
576 /**
577 * Confirm a CSS value is valid.
578 * If it appears to contain JavaScript, it will not be added.
579 *
580 * @since 6.20
581 *
582 * @param string $var
583 *
584 * @return bool
585 */
586 private static function css_value_is_valid( $var ) {
587 if ( is_numeric( $var ) ) {
588 return true;
589 }
590
591 // None of these substrings should be present in any CSS value.
592 $invalid_substrings = array(
593 'function(',
594 ';userAgent',
595 ';stopPropagation',
596 '{const',
597 'window[',
598 'navigator[',
599 'Array;',
600 );
601
602 foreach ( $invalid_substrings as $substring ) {
603 if ( strpos( $var, $substring ) !== false ) {
604 return false;
605 }
606 }
607
608 return true;
609 }
610
611 /**
612 * Remove anything that isn't used as a CSS variable name.
613 *
614 * @param string $var_name
615 *
616 * @return string
617 */
618 private static function clean_var_name( $var_name ) {
619 return preg_replace( '/[^a-zA-Z0-9_-]/', '', $var_name );
620 }
621
622 /**
623 * Prepare the value for a CSS variable.
624 *
625 * @since 6.14
626 *
627 * @param array $settings An array of css style.
628 * @param string $key
629 *
630 * @return string
631 */
632 private static function css_var_prepare_value( $settings, $key ) {
633 $value = $settings[ $key ];
634
635 if ( ! is_string( $value ) && ! is_numeric( $value ) ) {
636 return '';
637 }
638
639 switch ( $key ) {
640 case 'font':
641 return safecss_filter_attr( $value );
642
643 case 'border_width_error':
644 case 'field_border_width':
645 if ( ! empty( $settings['field_shape_type'] ) && 'underline' === $settings['field_shape_type'] ) {
646 return safecss_filter_attr( '0px 0px ' . $value . ' 0px' );
647 }
648 break;
649
650 case 'box_shadow':
651 if ( ! empty( $settings['field_shape_type'] ) && 'underline' === $settings['field_shape_type'] ) {
652 return safecss_filter_attr( 'none' );
653 }
654 break;
655
656 case 'border_radius':
657 if ( ! empty( $settings['field_shape_type'] ) ) {
658 switch ( $settings['field_shape_type'] ) {
659 case 'underline':
660 case 'regular':
661 return safecss_filter_attr( '0px' );
662 case 'circle':
663 return safecss_filter_attr( '30px' );
664 }
665 }
666 break;
667 }//end switch
668
669 return esc_html( $value );
670 }
671
672 /**
673 * @since 2.3
674 *
675 * @param WP_Post $style
676 *
677 * @return array
678 */
679 public static function get_settings_for_output( $style ) {
680 if ( self::previewing_style() ) {
681 $frm_style = new FrmStyle();
682
683 if ( isset( $_POST['frm_style_setting'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
684
685 // Sanitizing is done later.
686 $posted = wp_unslash( $_POST['frm_style_setting'] ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
687
688 if ( ! is_array( $posted ) ) {
689 $posted = json_decode( $posted, true );
690 FrmAppHelper::format_form_data( $posted );
691 $settings = $frm_style->sanitize_post_content( $posted['frm_style_setting']['post_content'] );
692 $style_name = sanitize_title( $posted['style_name'] );
693 } else {
694 $settings = $frm_style->sanitize_post_content( $posted['post_content'] );
695 $style_name = FrmAppHelper::get_post_param( 'style_name', '', 'sanitize_title' );
696 }
697 } else {
698 $settings = $frm_style->sanitize_post_content( wp_unslash( $_GET ) );
699 $style_name = FrmAppHelper::get_param( 'style_name', '', 'get', 'sanitize_title' );
700 }
701
702 $settings = self::update_base_font_size( $settings, $frm_style->get_defaults() );
703
704 FrmAppHelper::sanitize_value( 'sanitize_text_field', $settings );
705
706 $settings['style_class'] = '';
707
708 if ( ! empty( $style_name ) ) {
709 $settings['style_class'] = $style_name . '.';
710 }
711 } else {
712 $settings = $style->post_content;
713 $settings['style_class'] = 'frm_style_' . $style->post_name . '.';
714 }//end if
715
716 $settings['style_class'] .= 'with_frm_style';
717 $settings['font'] = stripslashes( $settings['font'] );
718 $settings['change_margin'] = self::description_margin_for_screensize( $settings['width'] );
719
720 $checkbox_opts = array( 'important_style', 'auto_width', 'submit_style', 'collapse_icon', 'center_form' );
721
722 foreach ( $checkbox_opts as $opt ) {
723 if ( ! isset( $settings[ $opt ] ) ) {
724 $settings[ $opt ] = 0;
725 }
726 }
727
728 self::prepare_color_output( $settings );
729
730 $settings['field_height'] = $settings['field_height'] === '' ? 'auto' : $settings['field_height'];
731 $settings['field_width'] = $settings['field_width'] === '' ? 'auto' : $settings['field_width'];
732 $settings['auto_width'] = $settings['auto_width'] ? 'auto' : $settings['field_width'];
733 $settings['box_shadow'] = ! empty( $settings['remove_box_shadow'] ) ? 'none' : '0 1px 2px 0 rgba(18, 18, 23, 0.05)';
734
735 if ( ! isset( $settings['repeat_icon'] ) ) {
736 $settings['repeat_icon'] = 1;
737 }
738
739 return $settings;
740 }
741
742 /**
743 * Update the "Base Font Size" value from "Quick Settings across multiple settings values".
744 *
745 * @since 6.14
746 *
747 * @param array $settings An array of css style.
748 * @param array $defaults Default style settings.
749 *
750 * @return array
751 */
752 public static function update_base_font_size( $settings, $defaults ) {
753 if ( empty( $settings['base_font_size'] ) || empty( $settings['use_base_font_size'] ) || 'false' === $settings['use_base_font_size'] ) {
754 return $settings;
755 }
756
757 $base_font_size = (int) $settings['base_font_size'];
758 $font_size = $defaults['font_size'];
759 $font_sizes_to_update = array(
760 'font_size',
761 'field_font_size',
762 'check_font_size',
763 'title_size',
764 'form_desc_size',
765 'description_font_size',
766 'section_font_size',
767 'submit_font_size',
768 'success_font_size',
769 'error_font_size',
770 'progress_size',
771 );
772
773 array_map(
774 function ( $key ) use ( $defaults, $font_size, $base_font_size, &$settings ) {
775 if ( isset( $settings[ $key ] ) ) {
776 $settings[ $key ] = round( self::get_base_font_size_scale( $key, $font_size, $defaults ) * $base_font_size ) . 'px';
777 }
778 },
779 $font_sizes_to_update
780 );
781
782 return $settings;
783 }
784
785 /**
786 * Get style font size scale value.
787 *
788 * @since 6.14
789 *
790 * @param string $key Setting key.
791 * @param int|string $value Base font size value.
792 * @param array $defaults Default style settings.
793 *
794 * @return float
795 */
796 private static function get_base_font_size_scale( $key, $value, $defaults ) {
797 if ( empty( $defaults[ $key ] ) || ! is_numeric( (int) $defaults[ $key ] ) || ! is_numeric( (int) $value ) || 0 === (int) $value ) {
798 return 1;
799 }
800
801 return round( (int) $defaults[ $key ] / (int) $value, 2 );
802 }
803
804 /**
805 * @since 2.3
806 *
807 * @param array $settings Style settings, passed by reference.
808 * @param bool $allow_transparent Whether transparent colors are allowed.
809 *
810 * @return void
811 */
812 public static function prepare_color_output( &$settings, $allow_transparent = true ) {
813 $colors = self::allow_color_override();
814
815 foreach ( $colors as $css => $opts ) {
816 if ( $css === 'transparent' && ! $allow_transparent ) {
817 $css = '';
818 }
819
820 foreach ( $opts as $opt ) {
821 self::get_color_output( $css, $settings[ $opt ] );
822 }
823 }
824 }
825
826 /**
827 * @since 2.3
828 *
829 * @return array
830 */
831 private static function allow_color_override() {
832 $frm_style = new FrmStyle();
833 $colors = $frm_style->get_color_settings();
834
835 $transparent = array(
836 'fieldset_color',
837 'fieldset_bg_color',
838 'bg_color',
839 'bg_color_active',
840 'bg_color_disabled',
841 'section_bg_color',
842 'error_bg',
843 'success_bg_color',
844 'progress_bg_color',
845 'progress_active_bg_color',
846 'submit_border_color',
847 'submit_hover_border_color',
848 'submit_active_border_color',
849 'submit_hover_bg_color',
850 'submit_active_bg_color',
851 );
852
853 return array(
854 'transparent' => $transparent,
855 '' => array_diff( $colors, $transparent ),
856 );
857 }
858
859 /**
860 * @since 2.3
861 *
862 * @param string $default Default color value.
863 * @param string $color Color value, passed by reference.
864 *
865 * @return void
866 */
867 private static function get_color_output( $default, &$color ) {
868 $color = trim( $color );
869
870 if ( empty( $color ) ) {
871 $color = $default;
872 } elseif ( false !== strpos( $color, 'rgb(' ) ) {
873 $color = str_replace( 'rgb(', 'rgba(', $color );
874 $color = str_replace( ')', ',1)', $color );
875 } elseif ( strpos( $color, '#' ) === false && self::is_hex( $color ) ) {
876 $color = '#' . $color;
877 }
878 }
879
880 /**
881 * If a color looks like a hex code without the #, prepend the #.
882 * A color looks like a hex code if it does not contain the substrings "rgb", "rgba", "hsl", "hsla", or "hwb".
883 *
884 * @since 6.8
885 *
886 * @param string $color
887 *
888 * @return bool
889 */
890 private static function is_hex( $color ) {
891 $non_hex_substrings = array(
892 'rgba(',
893 'hsl(',
894 'hsla(',
895 'hwb(',
896 );
897
898 foreach ( $non_hex_substrings as $substring ) {
899 if ( false !== strpos( $color, $substring ) ) {
900 return false;
901 }
902 }
903
904 return true;
905 }
906
907 /**
908 * If left/right label is over a certain size,
909 * adjust the field description margin at a different screen size
910 *
911 * @since 2.3
912 *
913 * @param string $width Label width value.
914 *
915 * @return false|string
916 */
917 private static function description_margin_for_screensize( $width ) {
918 $temp_label_width = str_replace( 'px', '', $width );
919 $change_margin = false;
920
921 if ( $temp_label_width >= 230 ) {
922 $change_margin = '800px';
923 } elseif ( $width >= 215 ) {
924 $change_margin = '700px';
925 } elseif ( $width >= 180 ) {
926 $change_margin = '650px';
927 }
928
929 return $change_margin;
930 }
931
932 /**
933 * @since 2.3
934 *
935 * @return bool
936 */
937 public static function previewing_style() {
938 $ajax_change = isset( $_POST['action'] ) && $_POST['action'] === 'frm_change_styling' && isset( $_POST['frm_style_setting'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
939
940 return $ajax_change || isset( $_GET['flat'] );
941 }
942
943 /**
944 * Get the URL for the Styler page list view (where you can assign styles to a form and view style templates) for a target form.
945 *
946 * @since 6.0
947 *
948 * @param int|string $form_id
949 *
950 * @return string
951 */
952 public static function get_list_url( $form_id ) {
953 return admin_url( 'admin.php?page=formidable-styles&form=' . absint( $form_id ) );
954 }
955
956 /**
957 * Get the back button args from Style settings.
958 *
959 * @since 6.14
960 *
961 * @param stdClass|WP_Post $style
962 * @param int $form_id
963 *
964 * @return array
965 */
966 public static function get_style_options_back_button_args( $style, $form_id ) {
967 if ( self::is_advanced_settings() ) {
968 return array(
969 'title' => __( 'Quick Settings', 'formidable' ),
970 'id' => 'frm_style_back_to_quick_settings',
971 );
972 }
973 return array(
974 'href' => self::get_list_url( $form_id ),
975 'title' => $style->post_title,
976 );
977 }
978
979 /**
980 * Get a link to edit a target style post object in the visual styler.
981 *
982 * @param stdClass|WP_Post $style
983 * @param int|string $form_id Used for the back button and preview form target.
984 * @param string $section The url param section.
985 *
986 * @return string
987 */
988 public static function get_edit_url( $style, $form_id = 0, $section = '' ) {
989 $query_args = array(
990 'page' => 'formidable-styles',
991 'frm_action' => 'edit',
992 'id' => $style->ID,
993 'section' => $section,
994 );
995
996 if ( $form_id ) {
997 // We include &form_id for the back button to know where to point to.
998 $query_args['form'] = $form_id;
999 }
1000
1001 return add_query_arg( $query_args, admin_url( 'admin.php' ) );
1002 }
1003
1004 /**
1005 * Get a count of the number of forms assigned to a target style ID.
1006 * All unassigned forms are included in the count for the default style.
1007 *
1008 * @since 6.0
1009 *
1010 * @param int|string $style_id
1011 * @param bool $is_default
1012 *
1013 * @return int
1014 */
1015 public static function get_form_count_for_style( $style_id, $is_default ) {
1016 $serialized = serialize( array( 'custom_style' => (string) $style_id ) );
1017 // Chop off the "a:1:{" from the front and the "}" from the back.
1018 $substring = substr( $serialized, 5, -1 );
1019 $number_of_forms = FrmDb::get_count(
1020 'frm_forms',
1021 array(
1022 'status' => 'published',
1023 'options LIKE' => $substring,
1024 )
1025 );
1026
1027 if ( ! $is_default ) {
1028 // Exit early as the rest of the code is about including the default count.
1029 return $number_of_forms;
1030 }
1031
1032 $conversational_style_id = FrmDb::get_var( 'posts', array( 'post_name' => 'lines-no-boxes' ), 'ID' );
1033 $number_of_forms += self::get_default_style_count( $style_id, $conversational_style_id );
1034
1035 return $number_of_forms;
1036 }
1037
1038 /**
1039 * Get the number of forms that use the default style.
1040 *
1041 * @since 6.0
1042 *
1043 * @param int|string $style_id
1044 * @param mixed $conversational_style_id
1045 *
1046 * @return int
1047 */
1048 private static function get_default_style_count( $style_id, $conversational_style_id ) {
1049 $substrings = array_map(
1050 function ( $value ) {
1051 $substring = serialize( array( 'custom_style' => $value ) );
1052 return substr( $substring, 5, -1 );
1053 },
1054 array( '1', 1 )
1055 );
1056 $where = array(
1057 'status' => 'published',
1058 0 => array(
1059 'options NOT LIKE' => 'custom_style',
1060 'or' => 1,
1061 'options LIKE' => $substrings,
1062 ),
1063 );
1064
1065 if ( $conversational_style_id ) {
1066 // When a conversational style is set, check for it in the query by wrapping the where and adding a conversational option check.
1067 $is_conversational_style = (int) $style_id === (int) $conversational_style_id;
1068 $where[0] = array(
1069 // The chat option doesn't exist if it isn't on.
1070 ( $is_conversational_style ? 'options LIKE' : 'options NOT LIKE' ) => ';s:4:"chat";',
1071 $where[0],
1072 );
1073 }
1074
1075 return FrmDb::get_count( 'frm_forms', $where );
1076 }
1077
1078 /**
1079 * Check if the current page is the advanced settings page.
1080 *
1081 * @since 6.14
1082 *
1083 * @return bool True if is advanced settings, false otherwise.
1084 */
1085 public static function is_advanced_settings() {
1086 return FrmAppHelper::get_param( 'section' ) === 'advanced-settings' && FrmAppHelper::get_param( 'page' ) === 'formidable-styles';
1087 }
1088
1089 /**
1090 * Get wrapper classname for style editor sections.
1091 *
1092 * @since 6.16
1093 *
1094 * @param string $section_type
1095 *
1096 * @return string The style editor wrapper classname.
1097 */
1098 public static function style_editor_get_wrapper_classname( $section_type ) {
1099 $is_quick_settings = ( 'quick-settings' === $section_type );
1100 $classname = 'frm-style-editor-form';
1101 $classname .= ( ! self::is_advanced_settings() xor $is_quick_settings ) ? ' frm_hidden' : '';
1102 $classname .= FrmAppHelper::pro_is_installed() ? ' frm-pro' : '';
1103
1104 return $classname;
1105 }
1106
1107 /**
1108 * Retrieve the background image URL of the submit button.
1109 * It may be either a full URL string (used in versions prior to 6.14) or a numeric attachment ID (introduced in version 6.14).
1110 *
1111 * @since 6.14
1112 *
1113 * @param array $settings
1114 *
1115 * @return false|string Return image url or false.
1116 */
1117 public static function get_submit_image_bg_url( $settings ) {
1118 $background_image = $settings['submit_bg_img'];
1119
1120 if ( empty( $background_image ) ) {
1121 return false;
1122 }
1123
1124 // Handle the case where the submit_bg_img is a full URL string. If the settings were saved with the older styler version prior to 6.14, the submit_bg_img will be a full URL string.
1125 if ( ! is_numeric( $background_image ) ) {
1126 return $background_image;
1127 }
1128
1129 return wp_get_attachment_url( (int) $background_image );
1130 }
1131
1132 /**
1133 * Determines if the chosen JavaScript library should be used.
1134 *
1135 * @since 6.13
1136 *
1137 * @return bool
1138 */
1139 public static function use_chosen_js() {
1140 if ( ! FrmAppHelper::pro_is_installed() ) {
1141 return false;
1142 }
1143
1144 return is_callable( 'FrmProAppHelper::use_chosen_js' )
1145 ? FrmProAppHelper::use_chosen_js()
1146 : true;
1147 }
1148
1149 /**
1150 * Returns the bottom part from a margin/padding value.
1151 *
1152 * @since 6.17
1153 *
1154 * @param string $value The margin/padding value.
1155 *
1156 * @return string
1157 */
1158 public static function get_bottom_value( $value ) {
1159 if ( ! $value ) {
1160 return $value;
1161 }
1162
1163 $parts = explode( ' ', $value );
1164
1165 if ( count( $parts ) < 3 ) {
1166 return $parts[0];
1167 }
1168 return $parts[2];
1169 }
1170 }
1171