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/controllers/FrmStylesController.php +172 -75 6.27 → trunk View file →
@@ -20,8 +20,17 @@
20 20 */
21 21 private static $message;
22 22
23 23 /**
24 + * Cache of the active style object keyed by form ID.
25 + *
26 + * @since 6.32
27 + *
28 + * @var array
29 + */
30 + private static $active_style = array();
31 +
32 + /**
24 33 * @return void
25 34 */
26 35 public static function load_pro_hooks() {
27 36 if ( FrmAppHelper::pro_is_installed() ) {
@@ -134,9 +143,9 @@
134 143 if ( FrmAppHelper::is_style_editor_page( 'edit' ) ) {
135 144 wp_enqueue_style( 'wp-color-picker' );
136 145 }
137 146
138 - wp_enqueue_style( 'frm-custom-theme', admin_url( 'admin-ajax.php?action=frmpro_css' ), array(), $version );
147 + wp_enqueue_style( 'frm-custom-theme', admin_url( 'admin-ajax.php?action=frmpro_css&frm_scope_custom_css=1' ), array(), $version );
139 148
140 149 $style = apply_filters( 'frm_style_head', false );
141 150
142 151 if ( $style ) {
@@ -237,17 +246,18 @@
237 246 *
238 247 * @return void
239 248 */
240 249 private static function get_url_to_custom_style( &$stylesheet_urls ) {
241 - $file_name = '/css/' . self::get_file_name();
250 + $add_css_to_uploads_dir = FrmStyle::add_css_to_uploads_dir();
251 + $file_path = FrmStyle::get_generated_css_file_path( $add_css_to_uploads_dir ) . '/' . self::get_file_name();
242 252
243 - if ( is_readable( FrmAppHelper::plugin_path() . $file_name ) ) {
244 - $url = FrmAppHelper::plugin_url() . $file_name;
245 - } else {
246 - $url = admin_url( 'admin-ajax.php?action=frmpro_css' );
253 + if ( ! is_readable( $file_path ) ) {
254 + $stylesheet_urls['formidable'] = admin_url( 'admin-ajax.php?action=frmpro_css' );
255 + return;
247 256 }
248 257
249 - $stylesheet_urls['formidable'] = $url;
258 + $base_url = $add_css_to_uploads_dir ? wp_upload_dir()['baseurl'] . '/formidable/css/' : FrmAppHelper::plugin_url() . '/css/';
259 + $stylesheet_urls['formidable'] = $base_url . self::get_file_name();
250 260 }
251 261
252 262 /**
253 263 * Use a different stylesheet per site in a multisite install
@@ -257,10 +267,9 @@
257 267 * @return string
258 268 */
259 269 public static function get_file_name() {
260 270 if ( is_multisite() ) {
261 - $blog_id = get_current_blog_id();
262 - return 'formidableforms' . absint( $blog_id ) . '.css';
271 + return 'formidableforms' . absint( get_current_blog_id() ) . '.css';
263 272 }
264 273
265 274 return 'formidableforms.css';
266 275 }
@@ -271,19 +280,15 @@
271 280 *
272 281 * @return string
273 282 */
274 283 private static function get_css_version( $css_key, $version ) {
275 - if ( 'formidable' === $css_key ) {
276 - $this_version = get_option( 'frm_last_style_update' );
284 + if ( 'formidable' !== $css_key ) {
285 + return $version;
286 + }
277 287
278 - if ( ! $this_version ) {
279 - $this_version = $version;
280 - }
281 - } else {
282 - $this_version = $version;
283 - }
288 + $this_version = get_option( 'frm_last_style_update' );
284 289
285 - return $this_version;
290 + return $this_version ? $this_version : $version;
286 291 }
287 292
288 293 /**
289 294 * @param string $tag
@@ -292,9 +297,9 @@
292 297 * @return string
293 298 */
294 299 public static function add_tags_to_css( $tag, $handle ) {
295 300 if ( ( 'formidable' === $handle || 'jquery-theme' === $handle ) && ! str_contains( $tag, ' property=' ) ) {
296 - $tag = str_replace( ' type="', ' property="stylesheet" type="', $tag );
301 + return str_replace( ' type="', ' property="stylesheet" type="', $tag );
297 302 }
298 303
299 304 return $tag;
300 305 }
@@ -371,11 +376,10 @@
371 376 FrmAppController::show_error_modal( $error_args );
372 377 return;
373 378 }
374 379
375 - $frm_style = new FrmStyle( $style_id );
376 - $active_style = $frm_style->get_one();
377 - $default_style = self::get_default_style();
380 + $frm_style = new FrmStyle( $style_id );
381 + $active_style = $frm_style->get_one();
378 382
379 383 self::disable_admin_page_styling_on_submit_buttons();
380 384
381 385 /**
@@ -384,9 +388,9 @@
384 388 * @param array{form:\stdClass} $data
385 389 */
386 390 do_action( 'frm_before_render_style_page', compact( 'form' ) );
387 391
388 - self::render_style_page( $active_style, $form, $default_style );
392 + self::render_style_page( $active_style, $form, self::get_default_style() );
389 393 }
390 394
391 395 /**
392 396 * @since 6.0
@@ -529,11 +533,10 @@
529 533
530 534 // If the default style is selected, use the "Always use default" legacy option instead of the default style.
531 535 // There's also a check here for conversational forms.
532 536 // Without the check it isn't possible to select "Default" because "Always use default" will convert to "Lines" dynamically.
533 - $default_style = self::get_default_style();
534 537
535 - if ( $style_id === $default_style->ID && empty( $form->options['chat'] ) ) {
538 + if ( $style_id === self::get_default_style()->ID && empty( $form->options['chat'] ) ) {
536 539 $style_id = 1;
537 540 }
538 541
539 542 // We want to save a string for consistency. FrmStylesHelper::get_form_count_for_style expects the custom style ID is a string.
@@ -630,22 +633,44 @@
630 633 $style = $active_style;
631 634 break;
632 635
633 636 case 'duplicate':
634 - $style = clone $active_style;
635 - $new_style = $frm_style->get_new();
637 + $style = clone $active_style;
638 + $new_style = $frm_style->get_new();
639 + $new_name = self::get_new_style_post_name( $new_style->post_name );
640 +
641 + // The single style custom CSS is nested under the old style's scope. Re-scope it to the
642 + // new style's scope so it unnests correctly for display and re-nests correctly on save.
643 + if ( ! empty( $style->post_content['single_style_custom_css'] ) ) {
644 + $css_scope_helper = new FrmCssScopeHelper();
645 + $unnested_css = $css_scope_helper->unnest( $style->post_content['single_style_custom_css'], 'frm_style_' . $style->post_name ); // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
646 + $style->post_content['single_style_custom_css'] = $css_scope_helper->nest( $unnested_css, 'frm_style_' . $new_name );
647 + }
648 +
636 649 $style->ID = $new_style->ID;
637 - $style->post_name = $new_style->post_name;
638 - unset( $new_style );
650 + $style->post_name = $new_name;
651 + unset( $new_style, $new_name );
639 652 break;
640 653
641 654 case 'new_style':
642 - $style = $frm_style->get_new();
655 + $style = $frm_style->get_new();
656 + $style->post_name = self::get_new_style_post_name( $style->post_name );
643 657 break;
644 - }
658 + }//end switch
645 659
646 660 if ( in_array( $view, array( 'duplicate', 'new_style' ), true ) ) {
647 - $style->post_title = FrmAppHelper::simple_get( 'style_name' );
661 + // A new or duplicated style has no ID yet, so the Rename modal updates the post_title input
662 + // instead of calling an endpoint. Prefer that posted title over $_GET['style_name'] when present.
663 + $posted_title = '';
664 +
665 + // phpcs:ignore WordPress.Security.NonceVerification.Missing
666 + if ( isset( $_POST['frm_style_setting']['post_title'] ) ) {
667 + // The nonce is verified in FrmStylesController::save_style before this renders.
668 + // phpcs:ignore WordPress.Security.NonceVerification.Missing
669 + $posted_title = sanitize_text_field( wp_unslash( $_POST['frm_style_setting']['post_title'] ) );
670 + }
671 +
672 + $style->post_title = '' !== $posted_title ? $posted_title : FrmAppHelper::simple_get( 'style_name' );
648 673 $style->menu_order = 0;
649 674 }
650 675
651 676 if ( ! isset( $style ) ) {
@@ -669,8 +694,27 @@
669 694 include $style_views_path . 'show.php';
670 695 }
671 696
672 697 /**
698 + * Derive the temporary post_name (CSS scope slug) for a new or duplicated style from the chosen
699 + * style name so the displayed scope matches the slug WordPress will store on save.
700 + *
701 + * Falls back to the auto-generated key when no style name was provided so the scope is never empty.
702 + *
703 + * @since 6.32
704 + *
705 + * @param string $fallback The auto-generated post_name to use when no style name is chosen.
706 + *
707 + * @return string
708 + */
709 + private static function get_new_style_post_name( $fallback ) {
710 + $style_name = FrmAppHelper::simple_get( 'style_name' );
711 + $slug = $style_name ? sanitize_title( $style_name ) : '';
712 +
713 + return $slug ? $slug : $fallback;
714 + }
715 +
716 + /**
673 717 * @since 6.0
674 718 *
675 719 * @return string
676 720 */
@@ -713,8 +757,10 @@
713 757 *
714 758 * @return void
715 759 */
716 760 public static function save_style() {
761 + FrmAppHelper::permission_check( 'frm_change_settings' );
762 +
717 763 $frm_style = new FrmStyle();
718 764 $post_id = FrmAppHelper::get_post_param( 'ID', false, 'sanitize_title' );
719 765 $style_nonce = FrmAppHelper::get_post_param( 'frm_style', '', 'sanitize_text_field' );
720 766
@@ -988,10 +1034,9 @@
988 1034 if ( ! $style_id ) {
989 1035 // A style ID is not sent when resetting on the edit page.
990 1036 // Instead of resetting the style, send the defaults back so the inputs can be updated with JavaScript.
991 1037 $frm_style = new FrmStyle();
992 - $defaults = $frm_style->get_defaults();
993 - echo json_encode( $defaults );
1038 + echo json_encode( $frm_style->get_defaults() );
994 1039 wp_die();
995 1040 }
996 1041
997 1042 $frm_style = new FrmStyle();
@@ -1044,9 +1089,9 @@
1044 1089 /**
1045 1090 * @return void
1046 1091 */
1047 1092 public static function add_meta_boxes() {
1048 - // setup meta boxes
1093 + // Setup meta boxes
1049 1094 $meta_boxes = array(
1050 1095 'general' => __( 'General', 'formidable' ),
1051 1096 'form-title' => __( 'Form Title', 'formidable' ),
1052 1097 'form-description' => __( 'Form Description', 'formidable' ),
@@ -1199,12 +1244,14 @@
1199 1244 }
1200 1245
1201 1246 $frm_settings = FrmAppHelper::get_settings();
1202 1247
1203 - if ( $frm_settings->load_style !== 'none' ) {
1204 - wp_enqueue_style( 'formidable' );
1205 - $frm_vars['css_loaded'] = true;
1248 + if ( $frm_settings->load_style === 'none' ) {
1249 + return;
1206 1250 }
1251 +
1252 + wp_enqueue_style( 'formidable' );
1253 + $frm_vars['css_loaded'] = true;
1207 1254 }
1208 1255
1209 1256 /**
1210 1257 * Get the stylesheets for the form settings page
@@ -1235,8 +1282,52 @@
1235 1282 return $frm_style->get_one();
1236 1283 }
1237 1284
1238 1285 /**
1286 + * Get the active style object for a field's form.
1287 + *
1288 + * @since 6.32
1289 + *
1290 + * @param array|int $field The 'field' array.
1291 + *
1292 + * @return object
1293 + */
1294 + public static function get_active_style( $field ) {
1295 + if ( ! is_array( $field ) ) {
1296 + return new stdClass();
1297 + }
1298 +
1299 + $form_id = $field['parent_form_id'] ?? $field['form_id'];
1300 +
1301 + if ( isset( self::$active_style[ $form_id ] ) ) {
1302 + return self::$active_style[ $form_id ];
1303 + }
1304 +
1305 + $active_style = self::get_form_style( $form_id );
1306 +
1307 + if ( ! is_object( $active_style ) ) {
1308 + $active_style = new stdClass();
1309 + }
1310 +
1311 + self::$active_style[ $form_id ] = $active_style;
1312 +
1313 + return self::$active_style[ $form_id ];
1314 + }
1315 +
1316 + /**
1317 + * Get the style setting key that stores the alignment for a field type.
1318 + *
1319 + * @since 6.32
1320 + *
1321 + * @param string $field_type
1322 + *
1323 + * @return string
1324 + */
1325 + public static function get_align_key_for_style_settings( $field_type ) {
1326 + return 'checkbox' === $field_type ? 'check_align' : 'radio_align';
1327 + }
1328 +
1329 + /**
1239 1330 * @param string $class
1240 1331 * @param string $style
1241 1332 *
1242 1333 * @return string
@@ -1324,11 +1415,10 @@
1324 1415 * @param array $field
1325 1416 *
1326 1417 * @return string
1327 1418 */
1328 - public static function &important_style( $important, $field ) {
1329 - $important = self::get_style_val( 'important_style', $field['form_id'] );
1330 - return $important;
1419 + public static function important_style( $important, $field ) {
1420 + return self::get_style_val( 'important_style', $field['form_id'] );
1331 1421 }
1332 1422
1333 1423 /**
1334 1424 * Duplicate of WordPress do_accordion_section function, it adds an additional svg icon support.
@@ -1343,9 +1433,9 @@
1343 1433 */
1344 1434 public static function do_accordion_sections( $screen, $context, $data_object ) { // phpcs:ignore SlevomatCodingStandard.Complexity.Cognitive.ComplexityTooHigh
1345 1435 global $wp_meta_boxes;
1346 1436
1347 - // the symbol id from icons.svg
1437 + // The symbol id from icons.svg
1348 1438 $icon_ids = array(
1349 1439 'ranking-fields-style' => 'frm_chart_bar_icon',
1350 1440 'section-fields-style' => 'frm-form-title-style',
1351 1441 );
@@ -1359,8 +1449,9 @@
1359 1449 }
1360 1450
1361 1451 $page = $screen->id;
1362 1452
1453 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1363 1454 ?>
1364 1455 <div id="side-sortables" class="accordion-container">
1365 1456 <ul class="outer-border">
1366 1457 <?php
@@ -1368,44 +1459,46 @@
1368 1459 $first_open = false;
1369 1460
1370 1461 if ( isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
1371 1462 foreach ( array( 'high', 'core', 'default', 'low' ) as $priority ) {
1372 - if ( isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) {
1373 - foreach ( $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) {
1374 - if ( false === $box || ! $box['title'] ) {
1375 - continue;
1376 - }
1463 + if ( ! isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) {
1464 + continue;
1465 + }
1377 1466
1378 - ++$i;
1379 - $icon_id = array_key_exists( $box['id'], $icon_ids ) ? $icon_ids[ $box['id'] ] : 'frm-' . $box['id'];
1380 - $open_class = '';
1467 + foreach ( $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) {
1468 + if ( false === $box || ! $box['title'] ) {
1469 + continue;
1470 + }
1381 1471
1382 - if ( ! $first_open ) {
1383 - $first_open = true;
1384 - $open_class = 'open';
1385 - }
1472 + ++$i;
1473 + $icon_id = array_key_exists( $box['id'], $icon_ids ) ? $icon_ids[ $box['id'] ] : 'frm-' . $box['id'];
1474 + $open_class = '';
1386 1475
1387 - $accordion_content_id = 'frm_style_section_' . $box['id'];
1388 - ?>
1389 - <li class="control-section accordion-section <?php echo esc_attr( $open_class ); ?> <?php echo esc_attr( $box['id'] ); ?>" id="<?php echo esc_attr( $box['id'] ); ?>"><?php // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong ?>
1390 - <h3 class="accordion-section-title hndle">
1391 - <?php
1392 - FrmAppHelper::icon_by_class( 'frmfont ' . $icon_id . ' frm_svg24' );
1393 - echo esc_html( $box['title'] );
1394 - ?>
1395 - <button type="button" aria-expanded="<?php echo esc_attr( 'open' === $open_class ? 'true' : 'false' ); ?>" aria-controls="<?php echo esc_attr( $accordion_content_id ); ?>" aria-label="<?php echo esc_attr( $box['title'] ); ?>"><?php // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong ?>
1396 - <?php FrmAppHelper::icon_by_class( 'frmfont frm_arrowdown8_icon' ); ?>
1397 - </button>
1398 - </h3>
1399 - <div class="accordion-section-content <?php postbox_classes( $box['id'], $page ); ?>" id="<?php echo esc_attr( $accordion_content_id ); ?>">
1400 - <div class="inside">
1401 - <?php call_user_func( $box['callback'], $data_object, $box ); ?>
1402 - </div><!-- .inside -->
1403 - </div><!-- .accordion-section-content -->
1404 - </li><!-- .accordion-section -->
1405 - <?php
1406 - }//end foreach
1407 - }//end if
1476 + if ( ! $first_open ) {
1477 + $first_open = true;
1478 + $open_class = 'open';
1479 + }
1480 +
1481 + $accordion_content_id = 'frm_style_section_' . $box['id'];
1482 + ?>
1483 + <li class="control-section accordion-section <?php echo esc_attr( $open_class ); ?> <?php echo esc_attr( $box['id'] ); ?>" id="<?php echo esc_attr( $box['id'] ); ?>"><?php // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong ?>
1484 + <h3 class="accordion-section-title hndle">
1485 + <?php
1486 + FrmAppHelper::icon_by_class( 'frmfont ' . $icon_id . ' frm_svg24' );
1487 + echo esc_html( $box['title'] );
1488 + ?>
1489 + <button type="button" aria-expanded="<?php echo esc_attr( 'open' === $open_class ? 'true' : 'false' ); ?>" aria-controls="<?php echo esc_attr( $accordion_content_id ); ?>" aria-label="<?php echo esc_attr( $box['title'] ); ?>"><?php // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong ?>
1490 + <?php FrmAppHelper::icon_by_class( 'frmfont frm_arrowdown8_icon' ); ?>
1491 + </button>
1492 + </h3>
1493 + <div class="accordion-section-content <?php postbox_classes( $box['id'], $page ); ?>" id="<?php echo esc_attr( $accordion_content_id ); ?>">
1494 + <div class="inside">
1495 + <?php call_user_func( $box['callback'], $data_object, $box ); ?>
1496 + </div><!-- .inside -->
1497 + </div><!-- .accordion-section-content -->
1498 + </li><!-- .accordion-section -->
1499 + <?php
1500 + }//end foreach
1408 1501 }//end foreach
1409 1502 }//end if
1410 1503 ?>
1411 1504 </ul><!-- .outer-border -->
@@ -1410,8 +1503,9 @@
1410 1503 ?>
1411 1504 </ul><!-- .outer-border -->
1412 1505 </div><!-- .accordion-container -->
1413 1506 <?php
1507 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1414 1508 return $i;
1415 1509 }
1416 1510
1417 1511 /**
@@ -1442,8 +1536,11 @@
1442 1536 wp_send_json_error( $data, 400 );
1443 1537 die();
1444 1538 }
1445 1539
1540 + /**
1541 + * @var WP_Post|null $post
1542 + */
1446 1543 $post = get_post( $style_id );
1447 1544
1448 1545 if ( ! $post || $post->post_type !== self::$post_type ) {
1449 1546 $data = array(