PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / trunk
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More vtrunk
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
← All changes | classes/helpers/FrmStylesHelper.php +177 -54 6.26 → trunk View file →
@@ -25,11 +25,13 @@
25 25 *
26 26 * @return void
27 27 */
28 28 public static function save_button() {
29 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
29 30 ?>
30 31 <input type="submit" name="submit" class="button button-primary frm-button-primary" value="<?php esc_attr_e( 'Update', 'formidable' ); ?>" />
31 32 <?php
33 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
32 34 }
33 35
34 36 /**
35 37 * @since 2.05
@@ -176,11 +178,9 @@
176 178 if ( $key ) {
177 179 $class .= $key;
178 180 }
179 181
180 - $class .= '_icon';
181 -
182 - return $class;
182 + return $class . '_icon';
183 183 }
184 184
185 185 /**
186 186 * @param WP_Post $style
@@ -194,8 +194,9 @@
194 194 $icons = self::$function_name();
195 195 unset( $function_name );
196 196
197 197 $name = 'arrow' === $type ? 'collapse_icon' : 'repeat_icon';
198 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
198 199 ?>
199 200 <div class="btn-group" id="frm_<?php echo esc_attr( $name ); ?>_select">
200 201 <button class="multiselect dropdown-toggle btn btn-default" data-toggle="dropdown" type="button">
201 202 <?php FrmAppHelper::icon_by_class( 'frmfont ' . self::icon_key_to_class( $style->post_content[ $name ], '+', $type ) ); ?>
@@ -203,12 +204,12 @@
203 204 <b class="caret"></b>
204 205 </button>
205 206 <ul class="multiselect-container frm-dropdown-menu">
206 207 <?php foreach ( $icons as $key => $icon ) { ?>
207 - <li <?php echo $style->post_content['collapse_icon'] == $key ? 'class="active"' : ''; ?>>
208 + <li <?php echo $style->post_content['collapse_icon'] == $key ? 'class="active"' : ''; // phpcs:ignore Universal.Operators.StrictComparisons ?>>
208 209 <a href="javascript:void(0);">
209 210 <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 + <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 ); ?> /><?php // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong ?>
211 212 <span>
212 213 <?php
213 214 FrmAppHelper::icon_by_class( 'frmfont ' . self::icon_key_to_class( $key, '+', $type ) );
214 215 FrmAppHelper::icon_by_class( 'frmfont ' . self::icon_key_to_class( $key, '-', $type ) );
@@ -220,8 +221,9 @@
220 221 <?php } ?>
221 222 </ul>
222 223 </div>
223 224 <?php
225 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
224 226 }
225 227
226 228 /**
227 229 * Convert a color setting to a RGB CSV (without the rgb()/rgba() wrapper).
@@ -232,13 +234,9 @@
232 234 *
233 235 * @return string RGB value without the rgb() wrapper.
234 236 */
235 237 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 - }
238 + $rgb = str_starts_with( $color, 'rgb' ) ? self::get_rgb_array_from_rgb( $color ) : self::get_rgb_array_from_hex( $color );
241 239 return implode( ',', $rgb );
242 240 }
243 241
244 242 /**
@@ -257,8 +255,9 @@
257 255 if ( 4 === count( $rgb ) ) {
258 256 // Drop the alpha. The function is expected to only return r,g,b with no alpha.
259 257 array_pop( $rgb );
260 258 }
259 +
261 260 return $rgb;
262 261 }
263 262
264 263 /**
@@ -285,9 +284,8 @@
285 284 * @return string
286 285 */
287 286 public static function hex2rgba( $hex, $a ) {
288 287 $rgb = self::hex2rgb( $hex );
289 -
290 288 return 'rgba(' . $rgb . ',' . $a . ')';
291 289 }
292 290
293 291 /**
@@ -297,9 +295,9 @@
297 295 *
298 296 * @return string Hex color value.
299 297 */
300 298 private static function rgb_to_hex( $rgba ) {
301 - if ( strpos( $rgba, '#' ) === 0 ) {
299 + if ( str_starts_with( $rgba, '#' ) ) {
302 300 // Color is already hex.
303 301 return $rgba;
304 302 }
305 303 preg_match( '/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i', $rgba, $by_color );
@@ -363,11 +361,9 @@
363 361 $g = round( ( $g + $m ) * 255 );
364 362 $b = round( ( $b + $m ) * 255 );
365 363
366 364 // Convert RGB to hex
367 - $hex = sprintf( '%02x%02x%02x', $r, $g, $b );
368 -
369 - return $hex;
365 + return sprintf( '%02x%02x%02x', $r, $g, $b );
370 366 }
371 367
372 368 /**
373 369 * @since 2.3
@@ -379,9 +375,9 @@
379 375 */
380 376 public static function adjust_brightness( $hex, $steps ) {
381 377 $steps = max( - 255, min( 255, $steps ) );
382 378
383 - if ( 0 === strpos( $hex, 'rgba(' ) ) {
379 + if ( str_starts_with( $hex, 'rgba(' ) ) {
384 380 $rgba = str_replace( ')', '', str_replace( 'rgba(', '', $hex ) );
385 381 list ( $r, $g, $b, $a ) = array_map( 'trim', explode( ',', $rgba ) );
386 382 $r = max( 0, min( 255, $r + $steps ) );
387 383 $g = max( 0, min( 255, $g + $steps ) );
@@ -417,13 +413,13 @@
417 413 *
418 414 * @return int
419 415 */
420 416 public static function get_color_brightness( $color ) {
421 - if ( 0 === strpos( $color, 'rgb' ) ) {
417 + if ( str_starts_with( $color, 'rgb' ) ) {
422 418 $color = self::rgb_to_hex( $color );
423 419 }
424 420
425 - if ( 0 === strpos( $color, 'hsl' ) ) {
421 + if ( str_starts_with( $color, 'hsl' ) ) {
426 422 $hsl_to_hex = self::hsl_to_hex( $color );
427 423
428 424 if ( is_null( $hsl_to_hex ) ) {
429 425 // Fallback if we cannot convert the HSL value.
@@ -434,14 +430,13 @@
434 430 }
435 431
436 432 self::fill_hex( $color );
437 433
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;
434 + $c_r = hexdec( substr( $color, 0, 2 ) );
435 + $c_g = hexdec( substr( $color, 2, 2 ) );
436 + $c_b = hexdec( substr( $color, 4, 2 ) );
442 437
443 - return $brightness;
438 + return ( ( $c_r * 299 ) + ( $c_g * 587 ) + ( $c_b * 114 ) ) / 1000;
444 439 }
445 440
446 441 /**
447 442 * Change a 3 character hex color to a 6 character one.
@@ -479,14 +474,13 @@
479 474 *
480 475 * @return void
481 476 */
482 477 public static function output_vars( $settings, $defaults = array(), $vars = array() ) {
483 - if ( empty( $vars ) ) {
478 + if ( ! $vars ) {
484 479 $vars = self::get_css_vars( array_keys( $settings ) );
485 480 }
486 481
487 - $remove = array( 'remove_box_shadow', 'remove_box_shadow_active', 'theme_css', 'theme_name', 'theme_selector', 'important_style', 'submit_style', 'collapse_icon', 'center_form', 'custom_css', 'style_class', 'submit_bg_img', 'change_margin', 'repeat_icon', 'use_base_font_size', 'field_shape_type' );
488 - $vars = array_diff( $vars, $remove );
482 + $vars = array_diff( $vars, self::get_style_keys_to_remove_from_output_vars() );
489 483
490 484 foreach ( $vars as $var ) {
491 485 if ( ! isset( $settings[ $var ] ) || ! self::css_key_is_valid( $var ) ) {
492 486 continue;
@@ -498,14 +492,46 @@
498 492
499 493 $prepared_value = '';
500 494
501 495 if ( self::should_add_css_var( $settings, $defaults, $var, $prepared_value ) ) {
502 - echo '--' . esc_html( self::clean_var_name( str_replace( '_', '-', $var ) ) ) . ':' . $prepared_value . ';'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
496 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
497 + echo '--' . esc_html( self::clean_var_name( str_replace( '_', '-', $var ) ) ) . ':' . $prepared_value . ';';
503 498 }
504 499 }
505 500 }
506 501
507 502 /**
503 + * None of these style settings are used as CSS variables, so we want to exclude them to keep the CSS output clean.
504 + *
505 + * @since 6.26.1
506 + *
507 + * @return array<string>
508 + */
509 + private static function get_style_keys_to_remove_from_output_vars() {
510 + return array(
511 + 'remove_box_shadow',
512 + 'remove_box_shadow_active',
513 + 'theme_css',
514 + 'theme_name',
515 + 'theme_selector',
516 + 'important_style',
517 + 'submit_style',
518 + 'collapse_icon',
519 + 'center_form',
520 + 'custom_css',
521 + 'style_class',
522 + 'submit_bg_img',
523 + 'change_margin',
524 + 'repeat_icon',
525 + 'use_base_font_size',
526 + 'field_shape_type',
527 + 'bg_image_id',
528 + 'single_style_custom_css',
529 + 'enable_style_custom_css',
530 + );
531 + }
532 +
533 + /**
508 534 * Check if a CSS variable setting is not blank, doesn't match the default, and doesn't include invalid substrings.
509 535 *
510 536 * @since 6.24
511 537 *
@@ -541,9 +567,9 @@
541 567 */
542 568 private static function css_key_is_valid( $key ) {
543 569 // Any key that is abnormally large is not valid.
544 570 // Any key that contains a '{' is not valid.
545 - return strlen( $key ) < 100 && false === strpos( $key, '{' );
571 + return strlen( $key ) < 100 && ! str_contains( $key, '{' );
546 572 }
547 573
548 574 /**
549 575 * Confirm a CSS value is valid.
@@ -571,9 +597,9 @@
571 597 'Array;',
572 598 );
573 599
574 600 foreach ( $invalid_substrings as $substring ) {
575 - if ( strpos( $var, $substring ) !== false ) {
601 + if ( str_contains( $var, $substring ) ) {
576 602 return false;
577 603 }
578 604 }
579 605
@@ -654,18 +680,19 @@
654 680
655 681 if ( isset( $_POST['frm_style_setting'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
656 682
657 683 // Sanitizing is done later.
658 - $posted = wp_unslash( $_POST['frm_style_setting'] ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
684 + //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
685 + $posted = wp_unslash( $_POST['frm_style_setting'] );
659 686
660 - if ( ! is_array( $posted ) ) {
687 + if ( is_array( $posted ) ) {
688 + $settings = $frm_style->sanitize_post_content( $posted['post_content'] );
689 + $style_name = FrmAppHelper::get_post_param( 'style_name', '', 'sanitize_title' );
690 + } else {
661 691 $posted = json_decode( $posted, true );
662 692 FrmAppHelper::format_form_data( $posted );
663 693 $settings = $frm_style->sanitize_post_content( $posted['frm_style_setting']['post_content'] );
664 694 $style_name = sanitize_title( $posted['style_name'] );
665 - } else {
666 - $settings = $frm_style->sanitize_post_content( $posted['post_content'] );
667 - $style_name = FrmAppHelper::get_post_param( 'style_name', '', 'sanitize_title' );
668 695 }
669 696 } else {
670 697 $settings = $frm_style->sanitize_post_content( wp_unslash( $_GET ) );
671 698 $style_name = FrmAppHelper::get_param( 'style_name', '', 'get', 'sanitize_title' );
@@ -838,14 +865,11 @@
838 865 */
839 866 private static function get_color_output( $default, &$color ) {
840 867 $color = trim( $color );
841 868
842 - if ( empty( $color ) ) {
869 + if ( ! $color ) {
843 870 $color = $default;
844 - } elseif ( false !== strpos( $color, 'rgb(' ) ) {
845 - $color = str_replace( 'rgb(', 'rgba(', $color );
846 - $color = str_replace( ')', ',1)', $color );
847 - } elseif ( strpos( $color, '#' ) === false && self::is_hex( $color ) ) {
871 + } elseif ( ! str_contains( $color, '#' ) && self::is_hex( $color ) ) {
848 872 $color = '#' . $color;
849 873 }
850 874 }
851 875
@@ -860,8 +884,9 @@
860 884 * @return bool
861 885 */
862 886 private static function is_hex( $color ) {
863 887 $non_hex_substrings = array(
888 + 'rgb(',
864 889 'rgba(',
865 890 'hsl(',
866 891 'hsla(',
867 892 'hwb(',
@@ -867,9 +892,9 @@
867 892 'hwb(',
868 893 );
869 894
870 895 foreach ( $non_hex_substrings as $substring ) {
871 - if ( false !== strpos( $color, $substring ) ) {
896 + if ( str_contains( $color, $substring ) ) {
872 897 return false;
873 898 }
874 899 }
875 900
@@ -901,14 +926,31 @@
901 926 return $change_margin;
902 927 }
903 928
904 929 /**
930 + * Get the raw alignment value stored in the active style for a radio or checkbox field.
931 + *
932 + * @since 6.32
933 + *
934 + * @param array|int $field The 'field' array.
935 + *
936 + * @return string
937 + */
938 + public static function get_align_from_active_style( $field ) {
939 + $field_type = FrmField::is_checkbox( $field ) ? 'checkbox' : 'radio';
940 + $key = FrmStylesController::get_align_key_for_style_settings( $field_type );
941 +
942 + return FrmStylesController::get_active_style( $field )->post_content[ $key ] ?? '';
943 + }
944 +
945 + /**
905 946 * @since 2.3
906 947 *
907 948 * @return bool
908 949 */
909 950 public static function previewing_style() {
910 - $ajax_change = isset( $_POST['action'] ) && $_POST['action'] === 'frm_change_styling' && isset( $_POST['frm_style_setting'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
951 + // phpcs:ignore WordPress.Security.NonceVerification.Missing
952 + $ajax_change = isset( $_POST['action'] ) && $_POST['action'] === 'frm_change_styling' && isset( $_POST['frm_style_setting'] );
911 953
912 954 return $ajax_change || isset( $_GET['flat'] );
913 955 }
914 956
@@ -1001,11 +1043,10 @@
1001 1043 return $number_of_forms;
1002 1044 }
1003 1045
1004 1046 $conversational_style_id = FrmDb::get_var( 'posts', array( 'post_name' => 'lines-no-boxes' ), 'ID' );
1005 - $number_of_forms += self::get_default_style_count( $style_id, $conversational_style_id );
1006 1047
1007 - return $number_of_forms;
1048 + return $number_of_forms + self::get_default_style_count( $style_id, $conversational_style_id );
1008 1049 }
1009 1050
1010 1051 /**
1011 1052 * Get the number of forms that use the default style.
@@ -1067,14 +1108,13 @@
1067 1108 *
1068 1109 * @return string The style editor wrapper classname.
1069 1110 */
1070 1111 public static function style_editor_get_wrapper_classname( $section_type ) {
1071 - $is_quick_settings = ( 'quick-settings' === $section_type );
1112 + $is_quick_settings = 'quick-settings' === $section_type;
1072 1113 $classname = 'frm-style-editor-form';
1073 1114 $classname .= ( ! self::is_advanced_settings() xor $is_quick_settings ) ? ' frm_hidden' : '';
1074 - $classname .= FrmAppHelper::pro_is_installed() ? ' frm-pro' : '';
1075 1115
1076 - return $classname;
1116 + return $classname . ( FrmAppHelper::pro_is_installed() ? ' frm-pro' : '' );
1077 1117 }
1078 1118
1079 1119 /**
1080 1120 * Retrieve the background image URL of the submit button.
@@ -1088,13 +1128,15 @@
1088 1128 */
1089 1129 public static function get_submit_image_bg_url( $settings ) {
1090 1130 $background_image = $settings['submit_bg_img'];
1091 1131
1092 - if ( empty( $background_image ) ) {
1132 + if ( ! $background_image ) {
1093 1133 return false;
1094 1134 }
1095 1135
1096 - // 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.
1136 + // Handle the case where the submit_bg_img is a full URL string. If the
1137 + // Settings were saved with the older styler version prior to 6.14, the
1138 + // submit_bg_img will be a full URL string.
1097 1139 if ( ! is_numeric( $background_image ) ) {
1098 1140 return $background_image;
1099 1141 }
1100 1142
@@ -1112,11 +1154,9 @@
1112 1154 if ( ! FrmAppHelper::pro_is_installed() ) {
1113 1155 return false;
1114 1156 }
1115 1157
1116 - return is_callable( 'FrmProAppHelper::use_chosen_js' )
1117 - ? FrmProAppHelper::use_chosen_js()
1118 - : true;
1158 + return is_callable( 'FrmProAppHelper::use_chosen_js' ) ? FrmProAppHelper::use_chosen_js() : true;
1119 1159 }
1120 1160
1121 1161 /**
1122 1162 * Returns the bottom part from a margin/padding value.
@@ -1133,10 +1173,93 @@
1133 1173 }
1134 1174
1135 1175 $parts = explode( ' ', $value );
1136 1176
1137 - if ( count( $parts ) < 3 ) {
1138 - return $parts[0];
1177 + return count( $parts ) < 3 ? $parts[0] : $parts[2];
1178 + }
1179 +
1180 + /**
1181 + * Scope CSS to .frm_forms on admin pages to prevent style conflicts.
1182 + * On front-end pages, the CSS is returned unchanged.
1183 + *
1184 + * @since 6.30
1185 + *
1186 + * @param string $css The CSS to scope.
1187 + *
1188 + * @return string
1189 + */
1190 + public static function maybe_scope_css_for_admin( $css ) {
1191 + if ( ! self::should_scope_custom_css() ) {
1192 + return $css;
1139 1193 }
1140 - return $parts[2];
1194 +
1195 + /**
1196 + * Filter the CSS selector used for @scope when scoping custom CSS on admin pages.
1197 + *
1198 + * @since 6.30
1199 + *
1200 + * @param string $selector The CSS selector to scope to. Default '.frm_forms'.
1201 + */
1202 + $selector = apply_filters( 'frm_scope_custom_css_selector', '.frm_forms' );
1203 +
1204 + return '@scope (' . $selector . ') {' . $css . '}';
1205 + }
1206 +
1207 + /**
1208 + * Scope only the custom CSS portion of the full cached stylesheet for admin pages.
1209 + * Extracts the global custom CSS from the cached CSS, outputs the theme CSS unchanged,
1210 + * and returns only the custom CSS portion scoped.
1211 + *
1212 + * @since 6.30
1213 + *
1214 + * @param string $css The full cached CSS containing both theme and custom CSS.
1215 + *
1216 + * @return string The theme CSS with only the custom CSS portion scoped.
1217 + */
1218 + public static function maybe_scope_custom_css_in_cached_output( $css ) {
1219 + if ( ! self::should_scope_custom_css() ) {
1220 + return $css;
1221 + }
1222 +
1223 + $custom_css = strip_tags( FrmStylesController::get_custom_css() );
1224 +
1225 + if ( ! $custom_css ) {
1226 + return $css;
1227 + }
1228 +
1229 + // The cached CSS is minified. Minify the custom CSS the same way to find it.
1230 + $minified_custom_css = self::minify_css( $custom_css );
1231 + $custom_css_pos = strrpos( $css, $minified_custom_css );
1232 +
1233 + if ( false !== $custom_css_pos ) {
1234 + $css = substr( $css, 0, $custom_css_pos );
1235 + }
1236 +
1237 + return $css . self::maybe_scope_css_for_admin( $custom_css );
1238 + }
1239 +
1240 + /**
1241 + * Minify CSS by stripping comments and whitespace.
1242 + * Matches the minification used when saving the cached CSS file.
1243 + *
1244 + * @since 6.30
1245 + *
1246 + * @param string $css The CSS to minify.
1247 + *
1248 + * @return string
1249 + */
1250 + private static function minify_css( $css ) {
1251 + return preg_replace( '/\/\*(.|\s)*?\*\//', '', str_replace( array( "\r\n", "\r", "\n", "\t", ' ' ), '', $css ) );
1252 + }
1253 +
1254 + /**
1255 + * Check if custom CSS should be scoped for admin context.
1256 + *
1257 + * @since 6.30
1258 + *
1259 + * @return bool
1260 + */
1261 + private static function should_scope_custom_css() {
1262 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only, used to scope CSS output for admin context.
1263 + return isset( $_GET['frm_scope_custom_css'] );
1141 1264 }
1142 1265 }