Formidable Forms – Form Builder for WordPress - Version 4.04

Version Description

  • New: Show a warning if a WordPress parameter is used in the redirect URL to prevent unintented results.
  • New: Added frm_is_field_type hook to set if an individual field is shown as radio or checkbox
  • New: Show the new pricing fields in the builder for product education.
Download this release

Release Info

Developer sswells
Plugin Icon 128x128 Formidable Forms – Form Builder for WordPress
Version 4.04
Comparing to
See all releases

Code changes from version 4.03.07 to 4.04

classes/controllers/FrmAppController.php CHANGED
@@ -260,6 +260,17 @@ class FrmAppController {
260
  include( FrmAppHelper::plugin_path() . '/classes/views/shared/confirm-overlay.php' );
261
  }
262
 
 
 
 
 
 
 
 
 
 
 
 
263
  /**
264
  * @since 3.04.02
265
  */
@@ -409,6 +420,7 @@ class FrmAppController {
409
 
410
  do_action( 'frm_enqueue_builder_scripts' );
411
  self::include_upgrade_overlay();
 
412
  } elseif ( FrmAppHelper::is_view_builder_page() ) {
413
  if ( isset( $_REQUEST['post_type'] ) ) {
414
  $post_type = sanitize_title( wp_unslash( $_REQUEST['post_type'] ) );
@@ -428,6 +440,7 @@ class FrmAppController {
428
  wp_enqueue_script( 'formidable_admin' );
429
  wp_enqueue_style( 'formidable-admin' );
430
  FrmAppHelper::localize_script( 'admin' );
 
431
  }
432
  } elseif ( $pagenow == 'widgets.php' ) {
433
  FrmAppHelper::load_admin_wide_js();
260
  include( FrmAppHelper::plugin_path() . '/classes/views/shared/confirm-overlay.php' );
261
  }
262
 
263
+ public static function include_info_overlay() {
264
+ wp_enqueue_script( 'jquery-ui-dialog' );
265
+ wp_enqueue_style( 'jquery-ui-dialog' );
266
+
267
+ add_action( 'admin_footer', 'FrmAppController::info_overlay_html' );
268
+ }
269
+
270
+ public static function info_overlay_html() {
271
+ include( FrmAppHelper::plugin_path() . '/classes/views/shared/info-overlay.php' );
272
+ }
273
+
274
  /**
275
  * @since 3.04.02
276
  */
420
 
421
  do_action( 'frm_enqueue_builder_scripts' );
422
  self::include_upgrade_overlay();
423
+ self::include_info_overlay();
424
  } elseif ( FrmAppHelper::is_view_builder_page() ) {
425
  if ( isset( $_REQUEST['post_type'] ) ) {
426
  $post_type = sanitize_title( wp_unslash( $_REQUEST['post_type'] ) );
440
  wp_enqueue_script( 'formidable_admin' );
441
  wp_enqueue_style( 'formidable-admin' );
442
  FrmAppHelper::localize_script( 'admin' );
443
+ self::include_info_overlay();
444
  }
445
  } elseif ( $pagenow == 'widgets.php' ) {
446
  FrmAppHelper::load_admin_wide_js();
classes/controllers/FrmFieldsController.php CHANGED
@@ -613,6 +613,31 @@ class FrmFieldsController {
613
  return $default_value;
614
  }
615
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
616
  /**
617
  * Use HMTL5 placeholder with js fallback
618
  *
613
  return $default_value;
614
  }
615
 
616
+ /**
617
+ * Maybe add a blank placeholder option before any options
618
+ * in a dropdown.
619
+ *
620
+ * @since 4.04
621
+ * @return bool True if placeholder was added.
622
+ */
623
+ public static function add_placeholder_to_select( $field ) {
624
+ $placeholder = FrmField::get_option( $field, 'placeholder' );
625
+ if ( empty( $placeholder ) ) {
626
+ $placeholder = self::get_default_value_from_name( $field );
627
+ }
628
+
629
+ if ( $placeholder !== '' ) {
630
+ ?>
631
+ <option value="">
632
+ <?php echo esc_html( FrmField::get_option( $field, 'autocom' ) ? '' : $placeholder ); ?>
633
+ </option>
634
+ <?php
635
+ return true;
636
+ }
637
+
638
+ return false;
639
+ }
640
+
641
  /**
642
  * Use HMTL5 placeholder with js fallback
643
  *
classes/controllers/FrmFormsController.php CHANGED
@@ -122,8 +122,10 @@ class FrmFormsController {
122
  $id = FrmAppHelper::get_param( 'id', '', 'get', 'absint' );
123
 
124
  $errors = FrmForm::validate( $_POST );
 
 
125
  if ( count( $errors ) > 0 ) {
126
- return self::get_settings_vars( $id, $errors );
127
  }
128
 
129
  do_action( 'frm_before_update_form_settings', $id );
@@ -132,7 +134,7 @@ class FrmFormsController {
132
 
133
  $message = __( 'Settings Successfully Updated', 'formidable' );
134
 
135
- return self::get_settings_vars( $id, array(), $message );
136
  }
137
 
138
  public static function update( $values = array() ) {
@@ -897,11 +899,26 @@ class FrmFormsController {
897
  }
898
  }
899
 
900
- public static function get_settings_vars( $id, $errors = array(), $message = '' ) {
901
  FrmAppHelper::permission_check( 'frm_edit_forms' );
902
 
903
  global $frm_vars;
904
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
905
  $form = FrmForm::getOne( $id );
906
  $fields = FrmField::get_all_for_form( $id );
907
  $values = FrmAppHelper::setup_edit_vars( $form, 'forms', $fields, true );
122
  $id = FrmAppHelper::get_param( 'id', '', 'get', 'absint' );
123
 
124
  $errors = FrmForm::validate( $_POST );
125
+ $warnings = FrmFormsHelper::check_for_warnings( $_POST );
126
+
127
  if ( count( $errors ) > 0 ) {
128
+ return self::get_settings_vars( $id, $errors, compact( 'warnings' ) );
129
  }
130
 
131
  do_action( 'frm_before_update_form_settings', $id );
134
 
135
  $message = __( 'Settings Successfully Updated', 'formidable' );
136
 
137
+ return self::get_settings_vars( $id, array(), compact( 'message', 'warnings' ) );
138
  }
139
 
140
  public static function update( $values = array() ) {
899
  }
900
  }
901
 
902
+ public static function get_settings_vars( $id, $errors = array(), $args = array() ) {
903
  FrmAppHelper::permission_check( 'frm_edit_forms' );
904
 
905
  global $frm_vars;
906
 
907
+ if ( ! is_array( $args ) ) {
908
+ // For reverse compatibility.
909
+ $args = array(
910
+ 'message' => $args,
911
+ );
912
+ }
913
+
914
+ $defaults = array(
915
+ 'message' => '',
916
+ 'warnings' => array(),
917
+ );
918
+ $args = array_merge( $defaults, $args );
919
+ $message = $args['message'];
920
+ $warnings = $args['warnings'];
921
+
922
  $form = FrmForm::getOne( $id );
923
  $fields = FrmField::get_all_for_form( $id );
924
  $values = FrmAppHelper::setup_edit_vars( $form, 'forms', $fields, true );
classes/helpers/FrmAppHelper.php CHANGED
@@ -11,7 +11,7 @@ class FrmAppHelper {
11
  /**
12
  * @since 2.0
13
  */
14
- public static $plug_version = '4.03.07';
15
 
16
  /**
17
  * @since 1.07.02
@@ -1168,9 +1168,9 @@ class FrmAppHelper {
1168
 
1169
  public static function frm_capabilities( $type = 'auto' ) {
1170
  $cap = array(
1171
- 'frm_view_forms' => __( 'View Forms and Templates', 'formidable' ),
1172
- 'frm_edit_forms' => __( 'Add/Edit Forms and Templates', 'formidable' ),
1173
- 'frm_delete_forms' => __( 'Delete Forms and Templates', 'formidable' ),
1174
  'frm_change_settings' => __( 'Access this Settings Page', 'formidable' ),
1175
  'frm_view_entries' => __( 'View Entries from Admin Area', 'formidable' ),
1176
  'frm_delete_entries' => __( 'Delete Entries from Admin Area', 'formidable' ),
@@ -2265,10 +2265,18 @@ class FrmAppHelper {
2265
  'unmatched_parens' => __( 'This calculation has at least one unmatched ( ) { } [ ].', 'formidable' ),
2266
  'view_shortcodes' => __( 'This calculation may have shortcodes that work in Views but not forms.', 'formidable' ),
2267
  'text_shortcodes' => __( 'This calculation may have shortcodes that work in text calculations but not numeric calculations.', 'formidable' ),
 
 
 
 
 
 
 
2268
  'repeat_limit_min' => __( 'Please enter a Repeat Limit that is greater than 1.', 'formidable' ),
2269
  'checkbox_limit' => __( 'Please select a limit between 0 and 200.', 'formidable' ),
2270
  'install' => __( 'Install', 'formidable' ),
2271
  'active' => __( 'Active', 'formidable' ),
 
2272
  'no_items_found' => __( 'No items found.', 'formidable' ),
2273
  );
2274
 
11
  /**
12
  * @since 2.0
13
  */
14
+ public static $plug_version = '4.04';
15
 
16
  /**
17
  * @since 1.07.02
1168
 
1169
  public static function frm_capabilities( $type = 'auto' ) {
1170
  $cap = array(
1171
+ 'frm_view_forms' => __( 'View Forms', 'formidable' ),
1172
+ 'frm_edit_forms' => __( 'Add and Edit Forms', 'formidable' ),
1173
+ 'frm_delete_forms' => __( 'Delete Forms', 'formidable' ),
1174
  'frm_change_settings' => __( 'Access this Settings Page', 'formidable' ),
1175
  'frm_view_entries' => __( 'View Entries from Admin Area', 'formidable' ),
1176
  'frm_delete_entries' => __( 'Delete Entries from Admin Area', 'formidable' ),
2265
  'unmatched_parens' => __( 'This calculation has at least one unmatched ( ) { } [ ].', 'formidable' ),
2266
  'view_shortcodes' => __( 'This calculation may have shortcodes that work in Views but not forms.', 'formidable' ),
2267
  'text_shortcodes' => __( 'This calculation may have shortcodes that work in text calculations but not numeric calculations.', 'formidable' ),
2268
+ 'only_one_action' => __( 'This form action is limited to one per form. Please edit the existing form action.', 'formidable' ),
2269
+ 'unsafe_params' => FrmFormsHelper::reserved_words(),
2270
+ /* Translators: %s is the name of a Detail Page Slug that is a reserved word.*/
2271
+ 'slug_is_reserved' => sprintf( __( 'The Detail Page Slug "%s" is reserved by WordPress. This may cause problems. Is this intentional?', 'formidable' ), '****' ),
2272
+ /* Translators: %s is the name of a parameter that is a reserved word. More than one word could be listed here, though that would not be common. */
2273
+ 'param_is_reserved' => sprintf( __( 'The parameter "%s" is reserved by WordPress. This may cause problems when included in the URL. Is this intentional? ', 'formidable' ), '****' ),
2274
+ 'reserved_words' => __( 'See the list of reserved words in WordPress.', 'formidable' ),
2275
  'repeat_limit_min' => __( 'Please enter a Repeat Limit that is greater than 1.', 'formidable' ),
2276
  'checkbox_limit' => __( 'Please select a limit between 0 and 200.', 'formidable' ),
2277
  'install' => __( 'Install', 'formidable' ),
2278
  'active' => __( 'Active', 'formidable' ),
2279
+ 'select_a_field' => __( 'Select a Field', 'formidable' ),
2280
  'no_items_found' => __( 'No items found.', 'formidable' ),
2281
  );
2282
 
classes/helpers/FrmFieldsHelper.php CHANGED
@@ -1703,6 +1703,57 @@ class FrmFieldsHelper {
1703
  return $field_array + $field_options;
1704
  }
1705
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1706
  /**
1707
  * @deprecated 4.0
1708
  */
1703
  return $field_array + $field_options;
1704
  }
1705
 
1706
+ /**
1707
+ * @since 4.04
1708
+ */
1709
+ public static function show_add_field_buttons( $args ) {
1710
+ $field_key = $args['field_key'];
1711
+ $field_type = $args['field_type'];
1712
+ $field_label = FrmAppHelper::icon_by_class( FrmFormsHelper::get_field_link_icon( $field_type ), array( 'echo' => false ) );
1713
+ $field_name = FrmFormsHelper::get_field_link_name( $field_type );
1714
+ $field_label .= ' <span>' . $field_name . '</span>';
1715
+
1716
+ /* translators: %s: Field name */
1717
+ $upgrade_label = sprintf( esc_html__( '%s fields', 'formidable' ), $field_name );
1718
+
1719
+ // If the individual field isn't allowed, disable it.
1720
+ $run_filter = true;
1721
+ $single_no_allow = ' ';
1722
+ $install_data = '';
1723
+ $requires = '';
1724
+ $upgrade_message = '';
1725
+ $link = isset( $field_type['link'] ) ? esc_url_raw( $field_type['link'] ) : '';
1726
+ if ( strpos( $field_type['icon'], ' frm_show_upgrade' ) ) {
1727
+ $single_no_allow .= 'frm_show_upgrade';
1728
+ $field_type['icon'] = str_replace( ' frm_show_upgrade', '', $field_type['icon'] );
1729
+ $run_filter = false;
1730
+ if ( isset( $field_type['addon'] ) ) {
1731
+ $upgrading = FrmAddonsController::install_link( $field_type['addon'] );
1732
+ if ( isset( $upgrading['url'] ) ) {
1733
+ $install_data = json_encode( $upgrading );
1734
+ }
1735
+ $requires = FrmFormsHelper::get_plan_required( $upgrading );
1736
+ } elseif ( isset( $field_type['require'] ) ) {
1737
+ $requires = $field_type['require'];
1738
+ }
1739
+ }
1740
+
1741
+ if ( isset( $field_type['message'] ) ) {
1742
+ $upgrade_message = FrmAppHelper::kses( $field_type['message'], array( 'a', 'img' ) );
1743
+ }
1744
+
1745
+ ?>
1746
+ <li class="frmbutton <?php echo esc_attr( $args['no_allow_class'] . $single_no_allow . ' frm_t' . str_replace( '|', '-', $field_key ) ); ?>" id="<?php echo esc_attr( $field_key ); ?>" data-upgrade="<?php echo esc_attr( $upgrade_label ); ?>" data-message="<?php echo esc_attr( $upgrade_message ); ?>" data-link="<?php echo esc_attr( $link ); ?>" data-medium="builder" data-oneclick="<?php echo esc_attr( $install_data ); ?>" data-content="<?php echo esc_attr( $field_key ); ?>" data-requires="<?php echo esc_attr( $requires ); ?>">
1747
+ <?php
1748
+ if ( $run_filter ) {
1749
+ $field_label = apply_filters( 'frmpro_field_links', $field_label, $args['id'], $field_key );
1750
+ }
1751
+ echo FrmAppHelper::kses( $field_label, array( 'a', 'i', 'span', 'use', 'svg' ) ); // WPCS: XSS ok.
1752
+ ?>
1753
+ </li>
1754
+ <?php
1755
+ }
1756
+
1757
  /**
1758
  * @deprecated 4.0
1759
  */
classes/helpers/FrmFormsHelper.php CHANGED
@@ -1360,4 +1360,157 @@ BEFORE_HTML;
1360
 
1361
  return false;
1362
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1363
  }
1360
 
1361
  return false;
1362
  }
1363
+
1364
+ /**
1365
+ * Checks for warnings to be displayed after form settings are saved.
1366
+ *
1367
+ * @since 4.04
1368
+ *
1369
+ * @param array $values The $_POST array, which contains values submitted in a form.
1370
+ *
1371
+ * @return array An array of warnings or an empty array.
1372
+ */
1373
+ public static function check_for_warnings( $values ) {
1374
+ $warnings = array();
1375
+
1376
+ $redirect_warning = self::check_redirect_url_for_unsafe_params( $values );
1377
+
1378
+ if ( $redirect_warning ) {
1379
+ $warnings[] = $redirect_warning;
1380
+ }
1381
+
1382
+ return apply_filters( 'frm_check_for_warnings', $warnings, $values );
1383
+ }
1384
+
1385
+ /**
1386
+ * Checks the redirect URL for params whose names are reserved words.
1387
+ *
1388
+ * @since 4.04
1389
+ *
1390
+ * @param array $values The $_POST array, which contains the values submitted in a form.
1391
+ *
1392
+ * @return bool|string A warning message about unsafe params or false.
1393
+ */
1394
+ private static function check_redirect_url_for_unsafe_params( $values ) {
1395
+ if ( ! isset( $values['options'] ) ) {
1396
+ return false;
1397
+ }
1398
+
1399
+ $options = $values['options'];
1400
+ FrmAppHelper::sanitize_with_html( $options );
1401
+
1402
+ if ( ( ! isset( $options['success_action'] ) ) || $options['success_action'] !== 'redirect' || ! isset( $options['success_url'] ) ) {
1403
+ return false;
1404
+ }
1405
+
1406
+ $unsafe_params_in_redirect = self::get_unsafe_params( $options['success_url'] );
1407
+
1408
+ return self::create_unsafe_param_warning( $unsafe_params_in_redirect );
1409
+ }
1410
+
1411
+ /**
1412
+ * Returns an array of params whose names are reserved words in the specified URL.
1413
+ *
1414
+ * @since 4.04
1415
+ *
1416
+ * @param string $url The URL whose params are being checked.
1417
+ *
1418
+ * @return array An array of params whose names are reserved words or an empty array.
1419
+ */
1420
+ private static function get_unsafe_params( $url ) {
1421
+ $redirect_components = parse_url( $url );
1422
+ parse_str( $redirect_components['query'], $redirect_params );
1423
+ $redirect_param_names = array_keys( $redirect_params );
1424
+ $reserved_words = self::reserved_words();
1425
+ $unsafe_params_in_redirect = array_intersect( $redirect_param_names, $reserved_words );
1426
+
1427
+ return array_values( $unsafe_params_in_redirect );
1428
+ }
1429
+
1430
+ /**
1431
+ * Returns a warning if reserved words have been used as param names in the redirect URL.
1432
+ *
1433
+ * @since 4.04
1434
+ *
1435
+ * @param array $unsafe_params_in_redirect Array of params from the redirect URL whose names are reserved words.
1436
+ *
1437
+ * @return bool|string A string with an unsafe param message or false.
1438
+ */
1439
+ private static function create_unsafe_param_warning( $unsafe_params_in_redirect ) {
1440
+ $count = count( $unsafe_params_in_redirect );
1441
+ $caution = esc_html__( 'Is this intentional?', 'formidable' );
1442
+ $reserved_words_intro = esc_html__( 'See the list of reserved words in WordPress.', 'formidable' );
1443
+ $reserved_words_link = '<a href="https://codex.wordpress.org/WordPress_Query_Vars" target="_blank"> ' . $reserved_words_intro . '</a>';
1444
+
1445
+ if ( $count === 0 ) {
1446
+ return false;
1447
+ }
1448
+
1449
+ if ( $count == 1 ) {
1450
+ /* translators: %s: the name of a single parameter in the redirect URL */
1451
+ return sprintf( esc_html__( 'The redirect URL is using the parameter "%s", which is reserved by WordPress. ', 'formidable' ), $unsafe_params_in_redirect[0] ) . $caution . $reserved_words_link;
1452
+ }
1453
+
1454
+ $unsafe_params_string = implode( '", "', $unsafe_params_in_redirect );
1455
+
1456
+ /* translators: %s: the names of two or more parameters in the redirect URL, separated by commas */
1457
+ return sprintf( esc_html__( 'The redirect URL is using the parameters "%s", which are reserved by WordPress. ', 'formidable' ), $unsafe_params_string ) . $caution . $reserved_words_link;
1458
+ }
1459
+
1460
+ /**
1461
+ * Returns an array of common reserved words in WordPress.
1462
+ *
1463
+ * An edited list of reserved terms from the Codex.
1464
+ * https://codex.wordpress.org/Reserved_Terms
1465
+ *
1466
+ * @since 4.04
1467
+ *
1468
+ * @return array Array of WordPress reserved words.
1469
+ */
1470
+ public static function reserved_words() {
1471
+ return array(
1472
+ 'id',
1473
+ 'attachment',
1474
+ 'author',
1475
+ 'author_name',
1476
+ 'calendar',
1477
+ 'cat',
1478
+ 'category',
1479
+ 'category_name',
1480
+ 'cpage',
1481
+ 'custom',
1482
+ 'day',
1483
+ 'date',
1484
+ 'error',
1485
+ 'feed',
1486
+ 'hour',
1487
+ 'm',
1488
+ 'minute',
1489
+ 'more',
1490
+ 'name',
1491
+ 'order',
1492
+ 'p',
1493
+ 'page',
1494
+ 'page_id',
1495
+ 'paged',
1496
+ 'pb',
1497
+ 'post',
1498
+ 'posts',
1499
+ 'preview',
1500
+ 's',
1501
+ 'search',
1502
+ 'second',
1503
+ 'sentence',
1504
+ 'tag',
1505
+ 'taxonomy',
1506
+ 'tb',
1507
+ 'term',
1508
+ 'terms',
1509
+ 'theme',
1510
+ 'title',
1511
+ 'type',
1512
+ 'w',
1513
+ 'year',
1514
+ );
1515
+ }
1516
  }
classes/models/FrmField.php CHANGED
@@ -175,6 +175,21 @@ class FrmField {
175
  <img src="https://s3.amazonaws.com/fp.strategy11.com/images/appointments/appointments.png" alt="Scheduling" />',
176
  'link' => 'https://simplyscheduleappointments.com/meet/formidable/',
177
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  );
179
 
180
  // Since the signature field may be in a different section, don't show it twice.
@@ -944,7 +959,16 @@ class FrmField {
944
  }
945
 
946
  public static function get_option_in_array( $field, $option ) {
947
- return isset( $field[ $option ] ) ? $field[ $option ] : '';
 
 
 
 
 
 
 
 
 
948
  }
949
 
950
  public static function get_option_in_object( $field, $option ) {
@@ -1030,10 +1054,19 @@ class FrmField {
1030
  $field_type = self::get_original_field_type( $field );
1031
  $data_type = self::get_option( $field, 'data_type' );
1032
 
1033
- return (
1034
  $is_type === $field_type ||
1035
  ( 'data' === $field_type && $is_type === $data_type ) ||
1036
- ( 'lookup' === $field_type && $is_type === $data_type )
 
1037
  );
 
 
 
 
 
 
 
 
1038
  }
1039
  }
175
  <img src="https://s3.amazonaws.com/fp.strategy11.com/images/appointments/appointments.png" alt="Scheduling" />',
176
  'link' => 'https://simplyscheduleappointments.com/meet/formidable/',
177
  ),
178
+ 'product' => array(
179
+ 'name' => __( 'Product', 'formidable' ),
180
+ 'icon' => 'frm_icon_font frm_product_icon',
181
+ 'section' => 'pricing',
182
+ ),
183
+ 'quantity' => array(
184
+ 'name' => __( 'Quantity', 'formidable' ),
185
+ 'icon' => 'frm_icon_font frm_quantity_icon',
186
+ 'section' => 'pricing',
187
+ ),
188
+ 'total' => array(
189
+ 'name' => __( 'Total', 'formidable' ),
190
+ 'icon' => 'frm_icon_font frm_total_icon',
191
+ 'section' => 'pricing',
192
+ ),
193
  );
194
 
195
  // Since the signature field may be in a different section, don't show it twice.
959
  }
960
 
961
  public static function get_option_in_array( $field, $option ) {
962
+
963
+ if ( isset( $field[ $option ] ) ) {
964
+ $this_option = $field[ $option ];
965
+ } elseif ( isset( $field['field_options'] ) && is_array( $field['field_options'] ) && isset( $field['field_options'][ $option ] ) ) {
966
+ $this_option = $field['field_options'][ $option ];
967
+ } else {
968
+ $this_option = '';
969
+ }
970
+
971
+ return $this_option;
972
  }
973
 
974
  public static function get_option_in_object( $field, $option ) {
1054
  $field_type = self::get_original_field_type( $field );
1055
  $data_type = self::get_option( $field, 'data_type' );
1056
 
1057
+ $is_field_type = (
1058
  $is_type === $field_type ||
1059
  ( 'data' === $field_type && $is_type === $data_type ) ||
1060
+ ( 'lookup' === $field_type && $is_type === $data_type ) ||
1061
+ ( 'product' === $field_type && $is_type === $data_type )
1062
  );
1063
+
1064
+ /**
1065
+ * When a field type is checked, allow individual fields
1066
+ * to set the type.
1067
+ *
1068
+ * @since 4.04
1069
+ */
1070
+ return apply_filters( 'frm_is_field_type', $is_field_type, compact( 'field', 'is_type' ) );
1071
  }
1072
  }
classes/models/fields/FrmFieldType.php CHANGED
@@ -369,12 +369,65 @@ DEFAULT_HTML;
369
  }
370
 
371
  $this->field_choices_heading( $args );
372
- echo '<div class="frm_grid_container frm-collapse-me">';
 
373
  include( FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/field-choices.php' );
374
  $this->show_extra_field_choices( $args );
375
  echo '</div>';
376
  }
377
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  /**
379
  * Should the section for adding choices show for this field?
380
  *
@@ -407,7 +460,7 @@ DEFAULT_HTML;
407
  protected function field_choices_heading( $args ) {
408
  $all_field_types = array_merge( FrmField::pro_field_selection(), FrmField::field_selection() );
409
  ?>
410
- <h3>
411
  <?php
412
  printf(
413
  /* translators: %s: Field type */
@@ -420,6 +473,13 @@ DEFAULT_HTML;
420
  <?php
421
  }
422
 
 
 
 
 
 
 
 
423
  /**
424
  * This is called for any fields with set options (radio, checkbox, select, dynamic, lookup).
425
  *
369
  }
370
 
371
  $this->field_choices_heading( $args );
372
+
373
+ echo '<div class="frm_grid_container frm-collapse-me' . esc_attr( $this->extra_field_choices_class() ) . '">';
374
  include( FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/field-choices.php' );
375
  $this->show_extra_field_choices( $args );
376
  echo '</div>';
377
  }
378
 
379
+ /**
380
+ * @since 4.04
381
+ */
382
+ public function show_field_options( $args ) {
383
+ if ( ! $this->should_continue_to_field_options( $args ) ) {
384
+ return;
385
+ }
386
+
387
+ $has_options = ! empty( $args['field']['options'] );
388
+ $short_name = FrmAppHelper::truncate( strip_tags( str_replace( '"', '&quot;', $args['field']['name'] ) ), 20 );
389
+
390
+ /* translators: %s: Field name */
391
+ $option_title = sprintf( __( '%s Options', 'formidable' ), $short_name );
392
+
393
+ include( FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/field-options.php' );
394
+ }
395
+
396
+ /**
397
+ * @since 4.04
398
+ */
399
+ protected function should_continue_to_field_options( $args ) {
400
+ return in_array( $args['field']['type'], array( 'select', 'radio', 'checkbox' ) );
401
+ }
402
+
403
+ /**
404
+ * @since 4.04
405
+ */
406
+ protected function get_bulk_edit_string() {
407
+ return __( 'Bulk Edit Options', 'formidable' );
408
+ }
409
+
410
+ /**
411
+ * @since 4.04
412
+ */
413
+ protected function get_add_option_string() {
414
+ return __( 'Add Option', 'formidable' );
415
+ }
416
+
417
+ /**
418
+ * @since 4.04
419
+ */
420
+ protected function show_single_option( $args ) {
421
+ FrmFieldsHelper::show_single_option( $args['field'] );
422
+ }
423
+
424
+ /**
425
+ * @since 4.04
426
+ */
427
+ protected function extra_field_choices_class() {
428
+ return '';
429
+ }
430
+
431
  /**
432
  * Should the section for adding choices show for this field?
433
  *
460
  protected function field_choices_heading( $args ) {
461
  $all_field_types = array_merge( FrmField::pro_field_selection(), FrmField::field_selection() );
462
  ?>
463
+ <h3 <?php $this->field_choices_heading_attrs( $args ); ?>>
464
  <?php
465
  printf(
466
  /* translators: %s: Field type */
473
  <?php
474
  }
475
 
476
+ /**
477
+ * @since 4.04
478
+ */
479
+ protected function field_choices_heading_attrs( $args ) {
480
+ return;
481
+ }
482
+
483
  /**
484
  * This is called for any fields with set options (radio, checkbox, select, dynamic, lookup).
485
  *
classes/views/frm-fields/back-end/field-choices.php CHANGED
@@ -5,29 +5,6 @@ if ( isset( $args['field']['post_field'] ) && $args['field']['post_field'] == 'p
5
  <?php echo FrmFieldsHelper::get_term_link( $args['field']['taxonomy'] ); // WPCS: XSS ok. ?>
6
  </div>
7
  <?php
8
- } elseif ( in_array( $args['field']['type'], array( 'select', 'radio', 'checkbox' ) ) ) {
9
- $has_options = ! empty( $args['field']['options'] );
10
- $short_name = FrmAppHelper::truncate( strip_tags( str_replace( '"', '&quot;', $args['field']['name'] ) ), 20 );
11
-
12
- /* translators: %s: Field name */
13
- $option_title = sprintf( __( '%s Options', 'formidable' ), $short_name );
14
-
15
- ?>
16
- <span class="frm-bulk-edit-link">
17
- <a href="#" title="<?php echo esc_attr( $option_title ); ?>" class="frm-bulk-edit-link">
18
- <?php esc_html_e( 'Bulk Edit Options', 'formidable' ); ?>
19
- </a>
20
- </span>
21
- <?php do_action( 'frm_add_multiple_opts_labels', $args['field'] ); ?>
22
- <ul id="frm_field_<?php echo esc_attr( $args['field']['id'] ); ?>_opts" class="frm_sortable_field_opts frm_clear<?php echo ( count( $args['field']['options'] ) > 10 ) ? ' frm_field_opts_list' : ''; ?> frm_add_remove" data-key="<?php echo esc_attr( $args['field']['field_key'] ); ?>">
23
- <?php FrmFieldsHelper::show_single_option( $args['field'] ); ?>
24
- </ul>
25
- <div class="frm6 frm_form_field">
26
- <a href="javascript:void(0);" data-opttype="single" class="frm_cb_button frm-small-add frm_add_opt frm6 frm_form_field" id="frm_add_opt_<?php echo esc_attr( $args['field']['id'] ); ?>">
27
- <span class="frm_icon_font frm_add_tag"></span>
28
- <?php esc_html_e( 'Add Option', 'formidable' ); ?>
29
- </a>
30
- </div>
31
-
32
- <?php
33
  }
5
  <?php echo FrmFieldsHelper::get_term_link( $args['field']['taxonomy'] ); // WPCS: XSS ok. ?>
6
  </div>
7
  <?php
8
+ } else {
9
+ $this->show_field_options( $args );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  }
classes/views/frm-fields/back-end/field-options.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <span class="frm-bulk-edit-link">
2
+ <a href="#" title="<?php echo esc_attr( $option_title ); ?>" class="frm-bulk-edit-link">
3
+ <?php echo esc_html( $this->get_bulk_edit_string() ); ?>
4
+ </a>
5
+ </span>
6
+
7
+ <?php do_action( 'frm_add_multiple_opts_labels', $args['field'] ); ?>
8
+
9
+ <ul id="frm_field_<?php echo esc_attr( $args['field']['id'] ); ?>_opts" class="frm_sortable_field_opts frm_clear<?php echo ( count( $args['field']['options'] ) > 10 ) ? ' frm_field_opts_list' : ''; ?> frm_add_remove" data-key="<?php echo esc_attr( $args['field']['field_key'] ); ?>">
10
+ <?php $this->show_single_option( $args ); ?>
11
+ </ul>
12
+
13
+ <div class="frm6 frm_form_field">
14
+ <a href="javascript:void(0);" data-opttype="single" class="frm_cb_button frm-small-add frm_add_opt frm6 frm_form_field" id="frm_add_opt_<?php echo esc_attr( $args['field']['id'] ); ?>">
15
+ <span class="frm_icon_font frm_add_tag"></span>
16
+ <?php echo esc_html( $this->get_add_option_string() ); ?>
17
+ </a>
18
+ </div>
classes/views/frm-fields/back-end/number-range.php CHANGED
@@ -11,7 +11,7 @@
11
  </span>
12
  </span>
13
  </p>
14
- <p class="frm3 frm_last frm_form_field">
15
  <label for="frm_step_<?php echo esc_attr( $field['field_key'] ); ?>">
16
  <?php esc_html_e( 'Step', 'formidable' ); ?>
17
  </label>
11
  </span>
12
  </span>
13
  </p>
14
+ <p class="frm3 frm_last frm_form_field frm-step">
15
  <label for="frm_step_<?php echo esc_attr( $field['field_key'] ); ?>">
16
  <?php esc_html_e( 'Step', 'formidable' ); ?>
17
  </label>
classes/views/frm-fields/back-end/settings.php CHANGED
@@ -1,4 +1,4 @@
1
- <div class="frm-single-settings frm_hidden frm-fields" id="frm-single-settings-<?php echo esc_attr( $field['id'] ); ?>" data-fid="<?php echo esc_attr( $field['id'] ); ?>">
2
  <input type="hidden" name="frm_fields_submitted[]" value="<?php echo esc_attr( $field['id'] ); ?>" />
3
  <input type="hidden" name="field_options[field_order_<?php echo esc_attr( $field['id'] ); ?>]" value="<?php echo esc_attr( $field['field_order'] ); ?>"/>
4
 
1
+ <div class="frm-single-settings frm_hidden frm-fields frm-type-<?php echo esc_attr( $field['type'] ); ?>" id="frm-single-settings-<?php echo esc_attr( $field['id'] ); ?>" data-fid="<?php echo esc_attr( $field['id'] ); ?>">
2
  <input type="hidden" name="frm_fields_submitted[]" value="<?php echo esc_attr( $field['id'] ); ?>" />
3
  <input type="hidden" name="field_options[field_order_<?php echo esc_attr( $field['id'] ); ?>]" value="<?php echo esc_attr( $field['field_order'] ); ?>"/>
4
 
classes/views/frm-fields/front-end/dropdown-field.php CHANGED
@@ -18,20 +18,9 @@ if ( isset( $field['post_field'] ) && $field['post_field'] == 'post_category' &&
18
  <?php
19
  }
20
 
21
- $placeholder = FrmField::get_option( $field, 'placeholder' );
22
- if ( empty( $placeholder ) ) {
23
- $placeholder = FrmFieldsController::get_default_value_from_name( $field );
24
- }
25
 
26
  $skipped = false;
27
- if ( $placeholder !== '' ) {
28
- ?>
29
- <option value="">
30
- <?php echo esc_html( FrmField::get_option( $field, 'autocom' ) ? '' : $placeholder ); ?>
31
- </option>
32
- <?php
33
- }
34
-
35
  $other_opt = false;
36
  $other_checked = false;
37
  if ( empty( $field['options'] ) ) {
@@ -48,7 +37,7 @@ if ( isset( $field['post_field'] ) && $field['post_field'] == 'post_category' &&
48
  }
49
  }
50
 
51
- if ( ! empty( $placeholder ) && $opt == '' && ! $skipped ) {
52
  $skipped = true;
53
  continue;
54
  }
18
  <?php
19
  }
20
 
21
+ $placeholder = FrmFieldsController::add_placeholder_to_select( $field );
 
 
 
22
 
23
  $skipped = false;
 
 
 
 
 
 
 
 
24
  $other_opt = false;
25
  $other_checked = false;
26
  if ( empty( $field['options'] ) ) {
37
  }
38
  }
39
 
40
+ if ( $placeholder && $opt == '' && ! $skipped ) {
41
  $skipped = true;
42
  continue;
43
  }
classes/views/frm-forms/add_field_links.php CHANGED
@@ -44,7 +44,17 @@ if ( $no_allow_class === 'frm_noallow' ) {
44
  $no_allow_class .= ' frm_show_upgrade';
45
  }
46
 
47
- foreach ( FrmField::pro_field_selection() as $field_key => $field_type ) {
 
 
 
 
 
 
 
 
 
 
48
 
49
  if ( is_array( $field_type ) && isset( $field_type['switch_from'] ) ) {
50
  continue;
@@ -74,48 +84,7 @@ foreach ( FrmField::pro_field_selection() as $field_key => $field_type ) {
74
  </li>
75
  <?php
76
  } else {
77
- $field_label = FrmAppHelper::icon_by_class( FrmFormsHelper::get_field_link_icon( $field_type ), array( 'echo' => false ) );
78
- $field_name = FrmFormsHelper::get_field_link_name( $field_type );
79
- $field_label .= ' <span>' . $field_name . '</span>';
80
-
81
- /* translators: %s: Field name */
82
- $upgrade_label = sprintf( esc_html__( '%s fields', 'formidable' ), $field_name );
83
-
84
- // If the individual field isn't allowed, disable it.
85
- $run_filter = true;
86
- $single_no_allow = ' ';
87
- $install_data = '';
88
- $requires = '';
89
- $upgrade_message = '';
90
- $link = isset( $field_type['link'] ) ? esc_url_raw( $field_type['link'] ) : '';
91
- if ( strpos( $field_type['icon'], ' frm_show_upgrade' ) ) {
92
- $single_no_allow .= 'frm_show_upgrade';
93
- $field_type['icon'] = str_replace( ' frm_show_upgrade', '', $field_type['icon'] );
94
- $run_filter = false;
95
- if ( isset( $field_type['addon'] ) ) {
96
- $upgrading = FrmAddonsController::install_link( $field_type['addon'] );
97
- if ( isset( $upgrading['url'] ) ) {
98
- $install_data = json_encode( $upgrading );
99
- }
100
- $requires = FrmFormsHelper::get_plan_required( $upgrading );
101
- } elseif ( isset( $field_type['require'] ) ) {
102
- $requires = $field_type['require'];
103
- }
104
- }
105
-
106
- if ( isset( $field_type['message'] ) ) {
107
- $upgrade_message = FrmAppHelper::kses( $field_type['message'], array( 'a', 'img' ) );
108
- }
109
- ?>
110
- <li class="frmbutton <?php echo esc_attr( $no_allow_class . $single_no_allow . ' frm_t' . str_replace( '|', '-', $field_key ) ); ?>" id="<?php echo esc_attr( $field_key ); ?>" data-upgrade="<?php echo esc_attr( $upgrade_label ); ?>" data-message="<?php echo esc_attr( $upgrade_message ); ?>" data-link="<?php echo esc_attr( $link ); ?>" data-medium="builder" data-oneclick="<?php echo esc_attr( $install_data ); ?>" data-content="<?php echo esc_attr( $field_key ); ?>" data-requires="<?php echo esc_attr( $requires ); ?>">
111
- <?php
112
- if ( $run_filter ) {
113
- $field_label = apply_filters( 'frmpro_field_links', $field_label, $id, $field_key );
114
- }
115
- echo FrmAppHelper::kses( $field_label, array( 'a', 'i', 'span', 'use', 'svg' ) ); // WPCS: XSS ok.
116
- ?>
117
- </li>
118
- <?php
119
  }
120
 
121
  unset( $field_key, $field_type, $field_label );
@@ -123,6 +92,21 @@ foreach ( FrmField::pro_field_selection() as $field_key => $field_type ) {
123
  ?>
124
  </ul>
125
  <div class="clear"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  </div>
127
  <?php do_action( 'frm_extra_form_instructions' ); ?>
128
 
44
  $no_allow_class .= ' frm_show_upgrade';
45
  }
46
 
47
+ $pro_fields = FrmField::pro_field_selection();
48
+ $field_sections = array();
49
+ foreach ( $pro_fields as $field_key => $field_type ) {
50
+
51
+ if ( isset( $field_type['section'] ) ) {
52
+ if ( ! isset( $field_sections[ $field_type['section'] ] ) ) {
53
+ $field_sections[ $field_type['section'] ] = array();
54
+ }
55
+ $field_sections[ $field_type['section'] ][ $field_key ] = $field_type;
56
+ continue;
57
+ }
58
 
59
  if ( is_array( $field_type ) && isset( $field_type['switch_from'] ) ) {
60
  continue;
84
  </li>
85
  <?php
86
  } else {
87
+ FrmFieldsHelper::show_add_field_buttons( compact( 'field_key', 'field_type', 'id', 'no_allow_class' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
 
90
  unset( $field_key, $field_type, $field_label );
92
  ?>
93
  </ul>
94
  <div class="clear"></div>
95
+
96
+ <?php foreach ( $field_sections as $section => $section_fields ) { ?>
97
+ <h3 class="frm-with-line">
98
+ <span><?php esc_html_e( 'Pricing Fields', 'formidable' ); ?></span>
99
+ </h3>
100
+ <ul class="field_type_list">
101
+ <?php
102
+ foreach ( $section_fields as $field_key => $field_type ) {
103
+ FrmFieldsHelper::show_add_field_buttons( compact( 'field_key', 'field_type', 'id', 'no_allow_class' ) );
104
+ unset( $field_key, $field_type );
105
+ }
106
+ ?>
107
+ </ul>
108
+ <div class="clear"></div>
109
+ <?php } ?>
110
  </div>
111
  <?php do_action( 'frm_extra_form_instructions' ); ?>
112
 
classes/views/shared/errors.php CHANGED
@@ -9,6 +9,21 @@ if ( isset( $message ) && '' !== $message ) {
9
  }
10
  }
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  if ( isset( $errors ) && is_array( $errors ) && count( $errors ) > 0 ) {
13
  ?>
14
  <div class="frm_error_style inline" role="alert">
9
  }
10
  }
11
 
12
+ if ( isset( $warnings ) && is_array( $warnings ) && count( $warnings ) > 0 ) {
13
+ ?>
14
+ <div class="frm_warning_style inline" role="alert">
15
+ <div class="frm_warning_heading"> <?php echo esc_html__( 'Warning:', 'formidable' ); ?></div>
16
+ <ul id="frm_warnings">
17
+ <?php
18
+ foreach ( $warnings as $warning ) {
19
+ echo '<li>' . FrmAppHelper::kses( $warning, array( 'a', 'br' ) ) . '</li>'; // WPCS: XSS ok.
20
+ }
21
+ ?>
22
+ </ul>
23
+ </div>
24
+ <?php
25
+ }
26
+
27
  if ( isset( $errors ) && is_array( $errors ) && count( $errors ) > 0 ) {
28
  ?>
29
  <div class="frm_error_style inline" role="alert">
classes/views/shared/info-overlay.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="frm_info_modal" class="frm_hidden frm-inline-modal frm-info-modal">
2
+ <div class="metabox-holder">
3
+ <div class="postbox">
4
+ <a href="#" class="dismiss alignright" title="<?php esc_attr_e( 'Dismiss this message', 'formidable' ); ?>">
5
+ <?php FrmAppHelper::icon_by_class( 'frmfont frm_close_icon', array( 'aria-label' => 'Dismiss' ) ); ?>
6
+ </a>
7
+ <div class="inside">
8
+ <div class="info-modal-inside frmcenter">
9
+ <p class="frm-info-msg">
10
+ <?php esc_html_e( 'Are you sure?', 'formidable' ); ?>
11
+ </p>
12
+ <br/>
13
+ <a href="#" id="frm-info-click" class="button button-primary frm-button-primary dismiss">
14
+ <?php esc_html_e( 'Got it!', 'formidable' ); ?>
15
+ </a>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ </div>
css/frm_admin.css CHANGED
@@ -14,7 +14,6 @@
14
  --sidebar-hover: rgb(227, 232, 235);
15
  --primary-color: rgb(65, 153, 253);
16
  --primary-hover: rgb(49, 119, 199);
17
- --tooltip-font-size: 14px;
18
  --green: rgb(63, 172, 37);
19
  --orange: #F15A24;
20
  --pink: rgb(226, 42, 110);
@@ -594,8 +593,10 @@ p.frm_has_shortcodes {
594
  text-align: center;
595
  }
596
 
 
597
  #frm_confirm_modal .dismiss .frmsvg,
598
  #frm_upgrade_modal .dismiss .frmsvg,
 
599
  #frm_confirm_modal .dismiss i,
600
  #frm_upgrade_modal .dismiss i {
601
  top: 7px;
@@ -746,7 +747,7 @@ p.frm_has_shortcodes {
746
  position: sticky;
747
  top: 0;
748
  background-color: var(--sidebar-color);
749
- z-index: 1000;
750
  margin-top: 0;
751
  padding-top: 5px;
752
  }
@@ -1018,6 +1019,7 @@ h2 .frm-button-primary {
1018
  transition: all .2s ease;
1019
  padding: 0 15px;
1020
  height: 28px;
 
1021
  outline: none;
1022
  line-height: 26px;
1023
  }
@@ -1501,6 +1503,7 @@ form .frm_blank_field label {
1501
  }
1502
 
1503
  .frm_error_style,
 
1504
  .frm_message,
1505
  #post-body-content .frm_updated_message,
1506
  div.frm_updated_message {
@@ -1528,8 +1531,24 @@ div.frm_updated_message {
1528
  background-color: #F2DEDE;
1529
  }
1530
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1531
  .frm_updated_message ul,
1532
  .frm_updated_message li,
 
 
1533
  .frm_error_style ul,
1534
  .frm_error_style li {
1535
  margin: 0;
@@ -3294,6 +3313,7 @@ li.ui-state-default.edit_field_type_divider .frm-show-hover {
3294
  padding-bottom: 5px;
3295
  }
3296
 
 
3297
  #new_fields .frm_single_option select,
3298
  #new_fields .frm_single_option input[type=text] {
3299
  width: calc(100% - 73px);
@@ -3311,7 +3331,7 @@ li.ui-state-default.edit_field_type_divider .frm-show-hover {
3311
  .frm_single_option .frm-drag {
3312
  font-size: 23px;
3313
  width: 22px;
3314
- height: 22px;
3315
  margin-left: -6px;
3316
  color: var(--grey);
3317
  }
@@ -5137,6 +5157,46 @@ tr.frm_options_heading td {
5137
  font-size: 15px;
5138
  }
5139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5140
  #frm-preview-block {
5141
  height: 400px;
5142
  overflow: scroll;
@@ -5688,6 +5748,11 @@ select[name="m"] {
5688
  line-height: 30px;
5689
  }
5690
 
 
 
 
 
 
5691
  /*hide elements if js is avail*/
5692
  select.texture {
5693
  display: none;
@@ -5746,6 +5811,10 @@ ul .frm_col_two {
5746
  margin-right: 0;
5747
  }
5748
 
 
 
 
 
5749
  .settings-lite-cta a,
5750
  .settings-lite-cta ul,
5751
  .settings-lite-cta p,
@@ -5755,6 +5824,7 @@ ul .frm_col_two {
5755
  box-shadow: none !important;
5756
  }
5757
 
 
5758
  .settings-lite-cta h3 {
5759
  font-size: 17px !important;
5760
  }
@@ -5763,6 +5833,7 @@ ul .frm_col_two {
5763
  color: var(--green);
5764
  }
5765
 
 
5766
  .settings-lite-cta .postbox .inside {
5767
  padding: 20px 40px;
5768
  }
@@ -5771,6 +5842,14 @@ ul .frm_col_two {
5771
  color: var(--orange)
5772
  }
5773
 
 
 
 
 
 
 
 
 
5774
  .frm-caution {
5775
  text-transform: uppercase;
5776
  font-weight: bold;
@@ -6188,7 +6267,7 @@ iframe#dyncontent_ifr {
6188
  z-index: 9999999;
6189
  display: block;
6190
  visibility: visible;
6191
- font-size: var(--tooltip-font-size);
6192
  line-height: 1.4;
6193
  opacity: 0;
6194
  filter: alpha(opacity=0);
14
  --sidebar-hover: rgb(227, 232, 235);
15
  --primary-color: rgb(65, 153, 253);
16
  --primary-hover: rgb(49, 119, 199);
 
17
  --green: rgb(63, 172, 37);
18
  --orange: #F15A24;
19
  --pink: rgb(226, 42, 110);
593
  text-align: center;
594
  }
595
 
596
+ #frm_info_modal .dismiss .frmsvg,
597
  #frm_confirm_modal .dismiss .frmsvg,
598
  #frm_upgrade_modal .dismiss .frmsvg,
599
+ #frm_info_modal .dismiss i,
600
  #frm_confirm_modal .dismiss i,
601
  #frm_upgrade_modal .dismiss i {
602
  top: 7px;
747
  position: sticky;
748
  top: 0;
749
  background-color: var(--sidebar-color);
750
+ z-index: 98; /* must be < 99 */
751
  margin-top: 0;
752
  padding-top: 5px;
753
  }
1019
  transition: all .2s ease;
1020
  padding: 0 15px;
1021
  height: 28px;
1022
+ min-height: 28px;
1023
  outline: none;
1024
  line-height: 26px;
1025
  }
1503
  }
1504
 
1505
  .frm_error_style,
1506
+ .frm_warning_style,
1507
  .frm_message,
1508
  #post-body-content .frm_updated_message,
1509
  div.frm_updated_message {
1531
  background-color: #F2DEDE;
1532
  }
1533
 
1534
+ .frm_warning_style {
1535
+ color: #8B6E3C;
1536
+ border-color: #FBECCD;
1537
+ background-color: #FDF9E3;
1538
+ border-radius: 10px;
1539
+ text-align: left;
1540
+ padding-top: 5px;
1541
+ }
1542
+
1543
+ .frm_warning_heading{
1544
+ font-weight: bold;
1545
+ margin-bottom:4px;
1546
+ }
1547
+
1548
  .frm_updated_message ul,
1549
  .frm_updated_message li,
1550
+ .frm_warning_style ul,
1551
+ .frm_warning_style li,
1552
  .frm_error_style ul,
1553
  .frm_error_style li {
1554
  margin: 0;
3313
  padding-bottom: 5px;
3314
  }
3315
 
3316
+ .frm_product_price_wrapper,
3317
  #new_fields .frm_single_option select,
3318
  #new_fields .frm_single_option input[type=text] {
3319
  width: calc(100% - 73px);
3331
  .frm_single_option .frm-drag {
3332
  font-size: 23px;
3333
  width: 22px;
3334
+ height: 19px;
3335
  margin-left: -6px;
3336
  color: var(--grey);
3337
  }
5157
  font-size: 15px;
5158
  }
5159
 
5160
+ .frm_product_price_wrapper {
5161
+ display: inline-block;
5162
+ vertical-align: middle;
5163
+ }
5164
+
5165
+ .frm_prod_type_single .frm_product_price_wrapper {
5166
+ width: 100%;
5167
+ }
5168
+
5169
+ .frm-type-quantity .frm-step {
5170
+ display: none;
5171
+ }
5172
+
5173
+ #new_fields .frm_product_price_wrapper input:first-child {
5174
+ width: 60%;
5175
+ float: left;
5176
+ }
5177
+
5178
+ #new_fields .frm_product_price_wrapper input:last-child {
5179
+ width: 37%;
5180
+ float: right;
5181
+ }
5182
+
5183
+ li.frm_single_option:not(:last-child) {
5184
+ padding-bottom: 11px;
5185
+ margin-bottom: 10px;
5186
+ border-bottom: 1px solid var(--sidebar-hover);
5187
+ }
5188
+
5189
+ .frm_prod_type_single input[type=radio],
5190
+ .frm_prod_type_single .frm_drag_icon,
5191
+ .frm_prod_type_single .frm_sortable_field_opts li:nth-child( n + 3 ),
5192
+ .frm_prod_type_single .frm_sortable_field_opts .frm_remove_tag,
5193
+ .frm_prod_type_single .frm_form_field:not(.frm_product_type):not(.frm_sep_val_product),
5194
+ .frm_prod_type_single .frm-bulk-edit-link,
5195
+ .frm_prod_type_user_def.frm_grid_container,
5196
+ .frm_prod_options_heading.frm_prod_user_def {
5197
+ display: none;
5198
+ }
5199
+
5200
  #frm-preview-block {
5201
  height: 400px;
5202
  overflow: scroll;
5748
  line-height: 30px;
5749
  }
5750
 
5751
+ .frm-single-settings .frm_prod_field_opt_cont {
5752
+ margin-top: initial;
5753
+ margin-bottom: 20px;
5754
+ }
5755
+
5756
  /*hide elements if js is avail*/
5757
  select.texture {
5758
  display: none;
5811
  margin-right: 0;
5812
  }
5813
 
5814
+ .frm-info-modal a,
5815
+ .frm-info-modal ul,
5816
+ .frm-info-modal p,
5817
+ .frm-info-modal,
5818
  .settings-lite-cta a,
5819
  .settings-lite-cta ul,
5820
  .settings-lite-cta p,
5824
  box-shadow: none !important;
5825
  }
5826
 
5827
+ .frm-info-modal h3,
5828
  .settings-lite-cta h3 {
5829
  font-size: 17px !important;
5830
  }
5833
  color: var(--green);
5834
  }
5835
 
5836
+ .frm-info-modal .postbox .inside,
5837
  .settings-lite-cta .postbox .inside {
5838
  padding: 20px 40px;
5839
  }
5842
  color: var(--orange)
5843
  }
5844
 
5845
+ .info-modal-inside a.frm-standard-link {
5846
+ color: var(--primary-color);
5847
+ }
5848
+
5849
+ .info-modal-inside a.frm-standard-link:hover {
5850
+ color: var(--primary-hover);
5851
+ }
5852
+
5853
  .frm-caution {
5854
  text-transform: uppercase;
5855
  font-weight: bold;
6267
  z-index: 9999999;
6268
  display: block;
6269
  visibility: visible;
6270
+ font-size: 14px;
6271
  line-height: 1.4;
6272
  opacity: 0;
6273
  filter: alpha(opacity=0);
formidable.php CHANGED
@@ -2,7 +2,7 @@
2
  /*
3
  Plugin Name: Formidable Forms
4
  Description: Quickly and easily create drag-and-drop forms
5
- Version: 4.03.07
6
  Plugin URI: https://formidableforms.com/
7
  Author URI: https://formidableforms.com/
8
  Author: Strategy11
2
  /*
3
  Plugin Name: Formidable Forms
4
  Description: Quickly and easily create drag-and-drop forms
5
+ Version: 4.04
6
  Plugin URI: https://formidableforms.com/
7
  Author URI: https://formidableforms.com/
8
  Author: Strategy11
images/icons.svg CHANGED
@@ -872,5 +872,21 @@
872
  <title>cc_stripe</title>
873
  <path d="M17.3 10c0 .6 0 1-.2 1.2-.1.3-.3.4-.6.4a1 1 0 0 1-.4 0V9c.3-.3.5-.3.6-.3.4 0 .6.4.6 1.3zm4.8-.3h-1.2c0-.7.2-1 .6-1s.6.3.6 1zM5 11.2c0-1-.5-1.3-1.4-1.7-.5-.1-.7-.3-.7-.5s.1-.3.4-.3c.5 0 1 .2 1.3.4L5 7.9c-.2-.2-.8-.4-1.6-.4A2 2 0 0 0 2 8c-.4.3-.5.7-.5 1.2 0 .9.5 1.3 1.4 1.6.5.2.7.4.7.6 0 .2-.1.3-.5.3s-1-.2-1.5-.5l-.2 1.3c.4.2 1 .4 1.8.4.6 0 1-.1 1.4-.4.4-.3.6-.7.6-1.3zm3.3-2.4l.2-1.2h-1V6.1L6 6.4 6 7.6l-.5.1-.1 1.1h.6v2.4c0 .6.2 1 .5 1.3.3.2.7.3 1.2.3l.9-.1v-1.3l-.5.1c-.3 0-.5-.2-.5-.5V8.8h.9zm3.4.3V7.6h-.3a1 1 0 0 0-1 .6v-.6H9v5.1h1.6V9.4c.2-.2.5-.3 1-.3h.2zm.4 3.6h1.6v-5h-1.6v5zm6.7-2.7c0-.8-.1-1.5-.4-2-.3-.3-.7-.5-1.2-.5s-1 .2-1.3.6l-.1-.5h-1.4v7l1.6-.3v-1.6l.7.1c.4 0 1 0 1.5-.6.4-.5.6-1.2.6-2.2zm-5-3.8c0-.4-.4-.8-.9-.8s-.8.4-.8.8.3 1 .8 1 .9-.5.9-1zm9.7 4c0-.9-.2-1.5-.5-2-.4-.4-.9-.7-1.6-.7-1.4 0-2.2 1-2.2 2.7 0 .9.2 1.6.7 2 .4.4 1 .6 1.7.6s1.3-.1 1.7-.4l-.1-1.1c-.4.2-.9.3-1.4.3-.3 0-.5 0-.7-.2-.2-.1-.3-.4-.3-.7h2.7V10zM25 3v13.8c0 .8-.6 1.4-1.4 1.4H1.4C.6 18.3 0 17.7 0 17V3.1c0-.8.6-1.4 1.4-1.4h22.2c.8 0 1.4.6 1.4 1.4z"/>
874
  </symbol>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
875
  </defs>
876
  </svg>
872
  <title>cc_stripe</title>
873
  <path d="M17.3 10c0 .6 0 1-.2 1.2-.1.3-.3.4-.6.4a1 1 0 0 1-.4 0V9c.3-.3.5-.3.6-.3.4 0 .6.4.6 1.3zm4.8-.3h-1.2c0-.7.2-1 .6-1s.6.3.6 1zM5 11.2c0-1-.5-1.3-1.4-1.7-.5-.1-.7-.3-.7-.5s.1-.3.4-.3c.5 0 1 .2 1.3.4L5 7.9c-.2-.2-.8-.4-1.6-.4A2 2 0 0 0 2 8c-.4.3-.5.7-.5 1.2 0 .9.5 1.3 1.4 1.6.5.2.7.4.7.6 0 .2-.1.3-.5.3s-1-.2-1.5-.5l-.2 1.3c.4.2 1 .4 1.8.4.6 0 1-.1 1.4-.4.4-.3.6-.7.6-1.3zm3.3-2.4l.2-1.2h-1V6.1L6 6.4 6 7.6l-.5.1-.1 1.1h.6v2.4c0 .6.2 1 .5 1.3.3.2.7.3 1.2.3l.9-.1v-1.3l-.5.1c-.3 0-.5-.2-.5-.5V8.8h.9zm3.4.3V7.6h-.3a1 1 0 0 0-1 .6v-.6H9v5.1h1.6V9.4c.2-.2.5-.3 1-.3h.2zm.4 3.6h1.6v-5h-1.6v5zm6.7-2.7c0-.8-.1-1.5-.4-2-.3-.3-.7-.5-1.2-.5s-1 .2-1.3.6l-.1-.5h-1.4v7l1.6-.3v-1.6l.7.1c.4 0 1 0 1.5-.6.4-.5.6-1.2.6-2.2zm-5-3.8c0-.4-.4-.8-.9-.8s-.8.4-.8.8.3 1 .8 1 .9-.5.9-1zm9.7 4c0-.9-.2-1.5-.5-2-.4-.4-.9-.7-1.6-.7-1.4 0-2.2 1-2.2 2.7 0 .9.2 1.6.7 2 .4.4 1 .6 1.7.6s1.3-.1 1.7-.4l-.1-1.1c-.4.2-.9.3-1.4.3-.3 0-.5 0-.7-.2-.2-.1-.3-.4-.3-.7h2.7V10zM25 3v13.8c0 .8-.6 1.4-1.4 1.4H1.4C.6 18.3 0 17.7 0 17V3.1c0-.8.6-1.4 1.4-1.4h22.2c.8 0 1.4.6 1.4 1.4z"/>
874
  </symbol>
875
+ <symbol id="frm_price_icon" viewBox="0 0 20 20">
876
+ <title>price</title>
877
+ <path d="M.5 11.5l8 8a1.9 1.9 0 002.7 0l8.3-8.3a1.9 1.9 0 00.5-1.4v-8A1.9 1.9 0 0018.1 0h-8a1.9 1.9 0 00-1.3.5L.5 8.8a1.9 1.9 0 000 2.7zM2 10.2l8.3-8.3h8v8L9.7 18zm12.5-6.4a1.9 1.9 0 11-1.9 1.8 1.9 1.9 0 011.9-1.8z"/>
878
+ </symbol>
879
+ <symbol id="frm_product_icon" viewBox="0 0 23 23">
880
+ <title>product</title>
881
+ <path d="M22 2.9H5.8l-.4-2a1 1 0 00-1-.9h-4a.5.5 0 00-.4.5v1.1a.5.5 0 00.5.6h3.2l2.8 16a3 3 0 00-.8 2A2.7 2.7 0 008.3 23a2.7 2.7 0 002.6-2.9 3.1 3.1 0 00-.4-1.4h5.8A3.1 3.1 0 0016 20a2.7 2.7 0 002.5 2.9 2.7 2.7 0 002.6-2.9 3 3 0 00-.8-2v-.3a1 1 0 00-.9-1.3H8.1l-.3-2.1h12.4a1 1 0 001-.9L23 4.2a1 1 0 00-1-1.3zM8.3 21.2a1 1 0 010-2.2 1 1 0 010 2.2zm10.2 0a1 1 0 111-1 1 1 0 01-1 1zm1-9H7.4L6 5H21l-1.4 7.2z"/>
882
+ </symbol>
883
+ <symbol id="frm_total_icon" viewBox="0 0 23 13.8">
884
+ <title>total</title>
885
+ <path d="M21.9 0H1A1.1 1.1 0 000 1.2v11.5a1.1 1.1 0 001.1 1.1H22a1.1 1.1 0 001.1-1.1V1.2A1.1 1.1 0 0021.9 0zm-.6 9.8A2.3 2.3 0 0019 12H4a2.3 2.3 0 00-2.3-2.3V4A2.3 2.3 0 004 1.7h15A2.3 2.3 0 0021.3 4zM11.5 4a2.9 2.9 0 000 5.8 2.9 2.9 0 000-5.8z"/>
886
+ </symbol>
887
+ <symbol id="frm_quantity_icon" viewBox="0 0 17 20">
888
+ <title>quantity</title>
889
+ <g><path d="M15.6 1.3H1.9C1 1.3 0 2 0 3.1v13.6c0 1 .9 2 2 2h13.6c1 0 1.9-1 1.9-2V3.2c0-1-.9-2-2-2zm0 15.5H1.9V3.2h13.7"></path><path fill="none" stroke="currentColor" stroke-width="2" d="M9.5 4.5v10M4.5 9.5h10" transform="translate(-.7,0.4)"/></g>
890
+ </symbol>
891
  </defs>
892
  </svg>
js/formidable_admin.js CHANGED
@@ -372,6 +372,19 @@ function frmAdminBuildJS() {
372
  return false;
373
  }
374
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
  function toggleItem( e ) {
376
  /*jshint validthis:true */
377
  var toggle = this.getAttribute( 'data-frmtoggle' ),
@@ -673,6 +686,7 @@ function frmAdminBuildJS() {
673
  if ( auto !== 'auto' ) {
674
  // Hide success message on tab change.
675
  jQuery( '.frm_updated_message' ).hide();
 
676
  }
677
 
678
  if ( jQuery( link ).closest( '#frm_adv_info' ).length ) {
@@ -1001,6 +1015,7 @@ function frmAdminBuildJS() {
1001
 
1002
  initiateMultiselect();
1003
  renumberPageBreaks();
 
1004
  },
1005
  } );
1006
  }
@@ -1075,7 +1090,7 @@ function frmAdminBuildJS() {
1075
  }
1076
 
1077
  function maybeReenableSummaryBtnAfterAJAX( fieldType, addBtn, fieldButton, errorThrown ) {
1078
- alert( errorThrown + '. Please try again.' );
1079
  if ( 'summary' === fieldType ) {
1080
  addBtn.removeClass( 'disabled' );
1081
  fieldButton.draggable( 'enable' );
@@ -1087,6 +1102,23 @@ function frmAdminBuildJS() {
1087
  return $newFields.children( 'li.edit_field_type_summary' ).length > 0;
1088
  }
1089
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1090
  function duplicateField() {
1091
  /*jshint validthis:true */
1092
  var thisField = jQuery( this ).closest( 'li' );
@@ -1121,10 +1153,22 @@ function frmAdminBuildJS() {
1121
  field = document.getElementById( match[1] ),
1122
  section = '#' + match[1] + '.edit_field_type_divider ul.frm_sorting',
1123
  $thisSection = jQuery( section ),
 
1124
  toggled = false;
1125
 
1126
  setupSortable( section );
1127
 
 
 
 
 
 
 
 
 
 
 
 
1128
  if ( $thisSection.length ) {
1129
  $thisSection.parent( '.frm_field_box' ).children( '.frm_no_section_fields' ).addClass( 'frm_block' );
1130
  } else {
@@ -1200,10 +1244,61 @@ function frmAdminBuildJS() {
1200
  warningMessage += checkShortcodes( calculation, this );
1201
 
1202
  if ( warningMessage !== '' ) {
1203
- alert( calculation + "\n\n" + warningMessage );
1204
  }
1205
  }
1206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1207
  /**
1208
  * Checks a string for parens, brackets, and curly braces and returns a message if any unmatched are found.
1209
  * @param formula
@@ -1238,7 +1333,7 @@ function frmAdminBuildJS() {
1238
  }
1239
 
1240
  if ( stack.length > 0 || unmatchedClosing.length > 0 ) {
1241
- msg = frm_admin_js.unmatched_parens + '\n\n';
1242
  return msg;
1243
  }
1244
 
@@ -1325,13 +1420,17 @@ function frmAdminBuildJS() {
1325
  return /\[id\]|\[key\]|\[if\s\w+\]|\[foreach\s\w+\]|\[created-at(\s*)?/g;
1326
  }
1327
 
1328
- function isSummaryCalcBox( box ) {
1329
  var list = jQuery( box ).find( '.frm_code_list' );
1330
- return 1 === list.length && list.hasClass( 'frm_js_summary_list' );
1331
  }
1332
 
1333
  function extractExcludedOptions( exclude ) {
1334
  var opts = [];
 
 
 
 
1335
  for ( var i = 0; i < exclude.length; i++ ) {
1336
  if ( exclude[ i ].startsWith( '[' ) ) {
1337
  opts.push( exclude[ i ] );
@@ -1372,7 +1471,7 @@ function frmAdminBuildJS() {
1372
  return;
1373
  }
1374
 
1375
- var isSummary = isSummaryCalcBox( v );
1376
 
1377
  var form_id = jQuery( 'input[name="id"]' ).val();
1378
  var fieldId = p.find( 'input[name="frm_fields_submitted[]"]' ).val();
@@ -1391,7 +1490,7 @@ function frmAdminBuildJS() {
1391
  list.innerHTML = '';
1392
 
1393
  for ( i = 0; i < fields.length; i++ ) {
1394
- if ( exclude.includes( fields[ i ].fieldType ) ||
1395
  ( excludedOpts.length && hasExcludedOption( fields[ i ], excludedOpts ) ) ) {
1396
  continue;
1397
  }
@@ -1453,11 +1552,17 @@ function frmAdminBuildJS() {
1453
  popCalcFields( jQuery( '.frm-inline-modal.postbox:has(.frm_js_summary_list)' )[0], true );
1454
  }
1455
 
1456
- function getFieldList() {
1457
  var i, fields = [],
1458
- allFields = document.querySelectorAll( 'li.frm_field_box' );
 
1459
 
1460
  for ( i = 0; i < allFields.length; i++ ) {
 
 
 
 
 
1461
  var fieldId = allFields[ i ].getAttribute( 'data-fid' );
1462
  if ( typeof fieldId !== 'undefined' && fieldId ) {
1463
  fields.push( {
@@ -1467,10 +1572,54 @@ function frmAdminBuildJS() {
1467
  'fieldKey': getPossibleValue( 'field_options_field_key_' + fieldId )
1468
  } );
1469
  }
 
 
 
 
 
 
 
1470
 
1471
- if ( i === allFields.length - 1 ) {
1472
- return fields;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1473
  }
 
 
 
 
 
 
1474
  }
1475
  }
1476
 
@@ -1656,7 +1805,8 @@ function frmAdminBuildJS() {
1656
  fieldId = jQuery( this ).closest( '[data-fid]' ).data( 'fid' ),
1657
  separate = usingSeparateValues( fieldId ),
1658
  optList = document.getElementById( 'frm_field_' + fieldId + '_opts' ),
1659
- opts = optList.getElementsByTagName( 'li' );
 
1660
 
1661
  document.getElementById( 'bulk-field-id' ).value = fieldId;
1662
 
@@ -1669,6 +1819,9 @@ function frmAdminBuildJS() {
1669
  if ( separate ) {
1670
  content += '|' + document.getElementsByName( 'field_options[options_' + fieldId + '][' + key + '][value]' )[0].value;
1671
  }
 
 
 
1672
  content += "\r\n";
1673
  }
1674
  }
@@ -1914,14 +2067,20 @@ function frmAdminBuildJS() {
1914
  settings.remove();
1915
 
1916
  $thisField.fadeOut( 'slow', function() {
1917
- var $section = $thisField.closest( '.start_divider' );
 
1918
  $thisField.remove();
1919
- if ( $thisField.data( 'type' ) === 'break' ) {
1920
  renumberPageBreaks();
1921
  }
1922
- if ( $thisField.data( 'type' ) === 'summary' ) {
1923
  reenableAddSummaryBtn();
1924
  }
 
 
 
 
 
1925
  if ( jQuery( '#frm-show-fields li' ).length === 0 ) {
1926
  document.getElementById( 'frm_form_editor_container' ).classList.remove( 'frm-has-fields' );
1927
  } else if ( $section.length ) {
@@ -2137,6 +2296,10 @@ function frmAdminBuildJS() {
2137
  }
2138
 
2139
  function clickLabel() {
 
 
 
 
2140
  /*jshint validthis:true */
2141
  var setting = document.querySelectorAll( '[data-changeme="' + this.id + '"]' )[0],
2142
  fieldId = this.id.replace( 'field_label_', '' ),
@@ -2231,7 +2394,7 @@ function frmAdminBuildJS() {
2231
  /*jshint validthis:true */
2232
  var val = this.value;
2233
  if ( val !== '' && ( val < 2 || val > 200 ) ) {
2234
- alert( frm_admin_js.repeat_limit_min );
2235
  this.value = '';
2236
  }
2237
  }
@@ -2240,7 +2403,7 @@ function frmAdminBuildJS() {
2240
  /*jshint validthis:true */
2241
  var val = this.value;
2242
  if ( val !== '' && ( val < 1 || val > 200 ) ) {
2243
- alert( frm_admin_js.checkbox_limit );
2244
  this.value = '';
2245
  }
2246
  }
@@ -2364,29 +2527,30 @@ function frmAdminBuildJS() {
2364
  type = input.attr( 'type' );
2365
  jQuery( '#field_' + fieldId + '_inner_container > .frm_form_fields' ).html( '' );
2366
  fieldInfo = getFieldKeyFromOpt( jQuery( '#frm_delete_field_' + fieldId + '-000_container' ) );
 
 
2367
 
2368
  for ( i = 0; i < opts.length; i++ ) {
2369
- addRadioCheckboxOpt( type, opts[ i ], fieldId, fieldInfo.fieldKey );
2370
  }
2371
  }
2372
  }
2373
 
2374
- function addRadioCheckboxOpt( type, opt, fieldId, fieldKey ) {
2375
  var other, single,
2376
  isOther = opt.key.indexOf( 'other' ) !== -1,
2377
- id = 'field_' + fieldKey + '-' + opt.key,
2378
- container = jQuery( '#field_' + fieldId + '_inner_container > .frm_form_fields' );
2379
 
2380
  other = '<input type="text" id="field_' + fieldKey + '-' + opt.key + '-otext" class="frm_other_input frm_pos_none" name="item_meta[other][' + fieldId + '][' + opt.key + ']" value="" />';
2381
 
2382
  single = '<div class="frm_' + type + ' ' + type + '" id="frm_' + type + '_' + fieldId + '-' + opt.key + '"><label for="' + id +
2383
  '"><input type="' + type +
2384
  '" name="item_meta[' + fieldId + ']' + ( type === 'checkbox' ? '[]' : '' ) +
2385
- '" value="' + opt.saved + '" id="' + id + '"> ' + opt.label + '</label>' +
2386
  ( isOther ? other : '' ) +
2387
  '</div>';
2388
 
2389
- container.append( single );
2390
  }
2391
 
2392
  function fillDropdownOpts( field, atts ) {
@@ -2395,6 +2559,7 @@ function frmAdminBuildJS() {
2395
  }
2396
  var sourceID = atts.sourceID,
2397
  placeholder = atts.placeholder,
 
2398
  showOther = atts.other;
2399
 
2400
  removeDropdownOpts( field );
@@ -2416,6 +2581,11 @@ function frmAdminBuildJS() {
2416
  var opt = document.createElement( 'option' );
2417
  opt.value = opts[ i ].saved;
2418
  opt.innerHTML = label;
 
 
 
 
 
2419
  field.appendChild( opt );
2420
  }
2421
  }
@@ -2436,12 +2606,13 @@ function frmAdminBuildJS() {
2436
  }
2437
 
2438
  function getMultipleOpts( fieldId ) {
2439
- var i, saved, labelName, label, key, opts = [],
2440
  optVals = jQuery( 'input[name^="field_options[options_' + fieldId + ']"]' ),
2441
- separateValues = usingSeparateValues( fieldId );
 
2442
 
2443
  for ( i = 0; i < optVals.length; i++ ) {
2444
- if ( optVals[ i ].name.indexOf( '[000]' ) > 0 || optVals[ i ].name.indexOf( '[value]' ) > 0 ) {
2445
  continue;
2446
  }
2447
  saved = optVals[ i ].value;
@@ -2453,11 +2624,18 @@ function frmAdminBuildJS() {
2453
  saved = jQuery( 'input[name="' + labelName + '"]' ).val();
2454
  }
2455
 
2456
- opts.push( {
2457
  saved: saved,
2458
  label: label,
2459
  key: key
2460
- } );
 
 
 
 
 
 
 
2461
  }
2462
 
2463
  return opts;
@@ -2494,7 +2672,7 @@ function frmAdminBuildJS() {
2494
  var c = false;
2495
  if ( !c && jQuery( v ).attr( 'id' ) != id && jQuery( v ).html() == text ) {
2496
  c = true;
2497
- alert( 'Saved values cannot be identical.' );
2498
  }
2499
  } );
2500
  }
@@ -2686,7 +2864,7 @@ function frmAdminBuildJS() {
2686
  */
2687
  function hideEmptyEle() {
2688
  jQuery( '.frm-hide-empty' ).each( function() {
2689
- if ( jQuery( this ).text().trim().length == 0 ) {
2690
  jQuery( this ).remove();
2691
  }
2692
  });
@@ -3099,6 +3277,7 @@ function frmAdminBuildJS() {
3099
  function showFieldOptions( obj ) {
3100
  var i, singleField,
3101
  fieldId = obj.getAttribute( 'data-fid' ),
 
3102
  allFieldSettings = document.querySelectorAll( '.frm-single-settings:not(.frm_hidden)' );
3103
 
3104
  for ( i = 0; i < allFieldSettings.length; i++ ) {
@@ -3108,6 +3287,10 @@ function frmAdminBuildJS() {
3108
  singleField = document.getElementById( 'frm-single-settings-' + fieldId );
3109
  moveFieldSettings( singleField );
3110
 
 
 
 
 
3111
  singleField.classList.remove( 'frm_hidden' );
3112
  document.getElementById( 'frm-options-panel-tab' ).click();
3113
  }
@@ -3155,13 +3338,20 @@ function frmAdminBuildJS() {
3155
  function checkActiveAction( type ) {
3156
  var limit = parseInt( jQuery( '.frm_' + type + '_action' ).data( 'limit' ) );
3157
  var len = jQuery( '.frm_single_' + type + '_settings' ).length;
 
3158
  if ( len >= limit ) {
3159
- jQuery( '.frm_' + type + '_action' ).removeClass( 'frm_active_action' ).addClass( 'frm_inactive_action' );
 
 
3160
  } else {
3161
- jQuery( '.frm_' + type + '_action' ).removeClass( 'frm_inactive_action' ).addClass( 'frm_active_action' );
3162
  }
3163
  }
3164
 
 
 
 
 
3165
  function addFormLogicRow() {
3166
  /*jshint validthis:true */
3167
  var id = jQuery( this ).data( 'emailkey' );
@@ -3299,7 +3489,7 @@ function frmAdminBuildJS() {
3299
  if ( jQuery( this ).val() === v && this.name !== $t.name ) {
3300
  this.style.borderColor = 'red';
3301
  jQuery( $t ).val( '' );
3302
- alert( 'Oops. You have already used that field.' );
3303
  return false;
3304
  }
3305
  } );
@@ -4110,7 +4300,7 @@ function frmAdminBuildJS() {
4110
  // Check if there is enough space for text
4111
  var textSpace = height - size - paddingTop - paddingBottom - 3;
4112
  if ( textSpace < 0 ) {
4113
- alert( frm_admin_js.css_invalid_size );
4114
  }
4115
  }
4116
 
@@ -4652,7 +4842,7 @@ function frmAdminBuildJS() {
4652
  create: function( event, ui ) {
4653
  var $container = jQuery( this ).parent();
4654
 
4655
- if ( $container.length == 0 ) {
4656
  $container = 'body';
4657
  }
4658
 
@@ -4718,9 +4908,9 @@ function frmAdminBuildJS() {
4718
 
4719
  function installNewForm( form, action ) {
4720
  var data,
4721
- formName = form.elements['template_name'].value,
4722
- formDesc = form.elements['template_desc'].value,
4723
- link = form.elements['link'].value;
4724
 
4725
  data = {
4726
  action: action,
@@ -4931,6 +5121,32 @@ function frmAdminBuildJS() {
4931
  }
4932
  }
4933
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4934
  return {
4935
  init: function() {
4936
  s = {};
@@ -5155,9 +5371,17 @@ function frmAdminBuildJS() {
5155
  $builderForm.on( 'change', '.frm_include_extras_field', rePopCalcFieldsForSummary );
5156
  $builderForm.on( 'change', 'select[name^="field_options[form_select_"]', maybeChangeEmbedFormMsg );
5157
 
 
 
 
 
 
 
 
5158
  initBulkOptionsOverlay();
5159
  hideEmptyEle();
5160
  maybeDisableAddSummaryBtn();
 
5161
  },
5162
 
5163
  settingsInit: function() {
@@ -5195,6 +5419,7 @@ function frmAdminBuildJS() {
5195
  var formSettings = jQuery( '.frm_form_settings' );
5196
  formSettings.on( 'click', '.frm_add_form_logic', addFormLogicRow );
5197
  formSettings.on( 'blur', '.frm_email_blur', formatEmailSetting );
 
5198
 
5199
  formSettings.on( 'change', '#logic_link_submit', toggleSubmitLogic );
5200
  formSettings.on( 'click', '.frm_add_submit_logic', addSubmitLogic );
@@ -5374,6 +5599,9 @@ function frmAdminBuildJS() {
5374
  $advInfo.before( '<div id="frm_position_ele"></div>' );
5375
  setupMenuOffset();
5376
 
 
 
 
5377
  // Show loading indicator.
5378
  jQuery( '#publish' ).mousedown( function() {
5379
  this.classList.add( 'frm_loading_button' );
@@ -5564,14 +5792,14 @@ function frmAdminBuildJS() {
5564
  },
5565
 
5566
  updateOpts: function( field_id, opts, modal ) {
5567
- var separate = usingSeparateValues( field_id );
5568
- $fieldOpts = document.getElementById( 'frm_field_' + field_id + '_opts' );
5569
- empty( $fieldOpts );
5570
  jQuery.ajax( {
5571
  type: 'POST',
5572
  url: ajaxurl,
5573
  data: {
5574
- action: 'frm_import_options',
5575
  field_id: field_id,
5576
  opts: opts,
5577
  separate: separate,
372
  return false;
373
  }
374
 
375
+ function infoModal( msg ) {
376
+ var $info = initModal( '#frm_info_modal', '400px' );
377
+
378
+ if ( $info === false ) {
379
+ return false;
380
+ }
381
+
382
+ jQuery( '.frm-info-msg' ).html( msg );
383
+
384
+ $info.dialog( 'open' );
385
+ return false;
386
+ }
387
+
388
  function toggleItem( e ) {
389
  /*jshint validthis:true */
390
  var toggle = this.getAttribute( 'data-frmtoggle' ),
686
  if ( auto !== 'auto' ) {
687
  // Hide success message on tab change.
688
  jQuery( '.frm_updated_message' ).hide();
689
+ jQuery( '.frm_warning_style' ).hide();
690
  }
691
 
692
  if ( jQuery( link ).closest( '#frm_adv_info' ).length ) {
1015
 
1016
  initiateMultiselect();
1017
  renumberPageBreaks();
1018
+ maybeHideQuantityProductFieldOption();
1019
  },
1020
  } );
1021
  }
1090
  }
1091
 
1092
  function maybeReenableSummaryBtnAfterAJAX( fieldType, addBtn, fieldButton, errorThrown ) {
1093
+ infoModal( errorThrown + '. Please try again.' );
1094
  if ( 'summary' === fieldType ) {
1095
  addBtn.removeClass( 'disabled' );
1096
  fieldButton.draggable( 'enable' );
1102
  return $newFields.children( 'li.edit_field_type_summary' ).length > 0;
1103
  }
1104
 
1105
+ function maybeHideQuantityProductFieldOption() {
1106
+ var hide = true,
1107
+ opts = document.querySelectorAll( '.frmjs_prod_field_opt_cont' );
1108
+
1109
+ if ( $newFields.find( 'li.edit_field_type_product' ).length > 1 ) {
1110
+ hide = false;
1111
+ }
1112
+
1113
+ for ( var i = 0; i < opts.length; i++ ) {
1114
+ if ( hide ) {
1115
+ opts[ i ].classList.add( 'frm_hidden' );
1116
+ } else {
1117
+ opts[ i ].classList.remove( 'frm_hidden' );
1118
+ }
1119
+ }
1120
+ }
1121
+
1122
  function duplicateField() {
1123
  /*jshint validthis:true */
1124
  var thisField = jQuery( this ).closest( 'li' );
1153
  field = document.getElementById( match[1] ),
1154
  section = '#' + match[1] + '.edit_field_type_divider ul.frm_sorting',
1155
  $thisSection = jQuery( section ),
1156
+ type = field.getAttribute( 'data-type' ),
1157
  toggled = false;
1158
 
1159
  setupSortable( section );
1160
 
1161
+ if ( 'quantity' === type ) {
1162
+ // try to automatically attach a product field
1163
+ maybeSetProductField( field );
1164
+ }
1165
+
1166
+ if ( 'product' === type || 'quantity' === type ) {
1167
+ // quantity too needs to be a part of the if stmt especially cos of the very
1168
+ // 1st quantity field (or even if it's just one quantity field in the form).
1169
+ maybeHideQuantityProductFieldOption();
1170
+ }
1171
+
1172
  if ( $thisSection.length ) {
1173
  $thisSection.parent( '.frm_field_box' ).children( '.frm_no_section_fields' ).addClass( 'frm_block' );
1174
  } else {
1244
  warningMessage += checkShortcodes( calculation, this );
1245
 
1246
  if ( warningMessage !== '' ) {
1247
+ infoModal( calculation + "\n\n" + warningMessage );
1248
  }
1249
  }
1250
 
1251
+ /**
1252
+ * Checks the Detail Page slug to see if it's a reserved word and displays a message if it is.
1253
+ */
1254
+ function checkDetailPageSlug() {
1255
+ var slug = jQuery( '#param' ).val(),
1256
+ msg;
1257
+ slug = slug.trim().toLowerCase();
1258
+ if ( Array.isArray( frm_admin_js.unsafe_params ) && frm_admin_js.unsafe_params.includes( slug ) ) {
1259
+ msg = frm_admin_js.slug_is_reserved;
1260
+ msg = msg.replace( '****', addHtmlTags( slug, 'strong' ) );
1261
+ msg += '<br /><br />';
1262
+ msg += addHtmlTags( '<a href="https://codex.wordpress.org/WordPress_Query_Vars" target="_blank" class="frm-standard-link">' + frm_admin_js.reserved_words + '</a>', 'div');
1263
+ infoModal( msg );
1264
+ }
1265
+ }
1266
+
1267
+ /**
1268
+ * Checks View filter value for params named with reserved words and displays a message if any are found.
1269
+ */
1270
+ function checkFilterParamNames() {
1271
+ var regEx = /\[\s*get\s*param\s*=\s*['"]?([a-zA-Z-_]+)['"]?/ig,
1272
+ filterValue = jQuery( this ).val(),
1273
+ match = regEx.exec( filterValue ),
1274
+ unsafeParams = '';
1275
+
1276
+ while ( match != null ) {
1277
+ if ( Array.isArray( frm_admin_js.unsafe_params ) && frm_admin_js.unsafe_params.includes( match[ 1 ] ) ) {
1278
+ if ( unsafeParams !== '' ) {
1279
+ unsafeParams += '", "' + match[ 1 ];
1280
+ } else {
1281
+ unsafeParams = match[ 1 ];
1282
+ }
1283
+ }
1284
+ match = regEx.exec( filterValue );
1285
+ }
1286
+
1287
+ if ( unsafeParams !== '' ) {
1288
+ msg = frm_admin_js.param_is_reserved;
1289
+ msg = msg.replace( '****', addHtmlTags( unsafeParams, 'strong' ) );
1290
+ msg += '<br /><br />';
1291
+ msg += ' <a href="https://codex.wordpress.org/WordPress_Query_Vars" target="_blank" class="frm-standard-link">' + frm_admin_js.reserved_words + '</a>';
1292
+
1293
+ infoModal( msg );
1294
+ }
1295
+ }
1296
+
1297
+ function addHtmlTags( text, tag ){
1298
+ tag = tag ? tag : 'p';
1299
+ return '<' + tag + '>' + text + '</' + tag + '>';
1300
+ }
1301
+
1302
  /**
1303
  * Checks a string for parens, brackets, and curly braces and returns a message if any unmatched are found.
1304
  * @param formula
1333
  }
1334
 
1335
  if ( stack.length > 0 || unmatchedClosing.length > 0 ) {
1336
+ msg = frm_admin_js.unmatched_parens + "\n\n";
1337
  return msg;
1338
  }
1339
 
1420
  return /\[id\]|\[key\]|\[if\s\w+\]|\[foreach\s\w+\]|\[created-at(\s*)?/g;
1421
  }
1422
 
1423
+ function isCalcBoxType( box, listClass ) {
1424
  var list = jQuery( box ).find( '.frm_code_list' );
1425
+ return 1 === list.length && list.hasClass( listClass );
1426
  }
1427
 
1428
  function extractExcludedOptions( exclude ) {
1429
  var opts = [];
1430
+ if ( ! Array.isArray( exclude ) ) {
1431
+ return opts;
1432
+ }
1433
+
1434
  for ( var i = 0; i < exclude.length; i++ ) {
1435
  if ( exclude[ i ].startsWith( '[' ) ) {
1436
  opts.push( exclude[ i ] );
1471
  return;
1472
  }
1473
 
1474
+ var isSummary = isCalcBoxType( v, 'frm_js_summary_list' );
1475
 
1476
  var form_id = jQuery( 'input[name="id"]' ).val();
1477
  var fieldId = p.find( 'input[name="frm_fields_submitted[]"]' ).val();
1490
  list.innerHTML = '';
1491
 
1492
  for ( i = 0; i < fields.length; i++ ) {
1493
+ if ( ( exclude && exclude.includes( fields[ i ].fieldType ) ) ||
1494
  ( excludedOpts.length && hasExcludedOption( fields[ i ], excludedOpts ) ) ) {
1495
  continue;
1496
  }
1552
  popCalcFields( jQuery( '.frm-inline-modal.postbox:has(.frm_js_summary_list)' )[0], true );
1553
  }
1554
 
1555
+ function getFieldList( fieldType ) {
1556
  var i, fields = [],
1557
+ allFields = document.querySelectorAll( 'li.frm_field_box' ),
1558
+ checkType = 'undefined' !== typeof fieldType;
1559
 
1560
  for ( i = 0; i < allFields.length; i++ ) {
1561
+ // data-ftype is better (than data-type) cos of fields loaded by AJAX - which might not be ready yet
1562
+ if ( checkType && allFields[ i ].getAttribute( 'data-ftype' ) !== fieldType ) {
1563
+ continue;
1564
+ }
1565
+
1566
  var fieldId = allFields[ i ].getAttribute( 'data-fid' );
1567
  if ( typeof fieldId !== 'undefined' && fieldId ) {
1568
  fields.push( {
1572
  'fieldKey': getPossibleValue( 'field_options_field_key_' + fieldId )
1573
  } );
1574
  }
1575
+ }
1576
+
1577
+ return fields;
1578
+ }
1579
+
1580
+ function popProductFields( field ) {
1581
+ var options = [], fields, i, selected, current;
1582
 
1583
+ current = field.getAttribute( 'data-frmcurrent' );
1584
+
1585
+ fields = getFieldList( 'product' );
1586
+
1587
+ options.push( '<option value="">' + frm_admin_js.select_a_field + '</option>' );
1588
+
1589
+ for ( i = 0; i < fields.length; i++ ) {
1590
+ selected = current == fields[ i ].fieldId ? ' selected' : '';
1591
+ options.push( '<option value="'+ fields[ i ].fieldId +'"' + selected + '>'+ fields[ i ].fieldName +'</option>' );
1592
+ }
1593
+
1594
+ field.innerHTML = options.join( '' );
1595
+ }
1596
+
1597
+ function popAllProductFields() {
1598
+ var opts = document.querySelectorAll( '.frmjs_prod_field_opt' );
1599
+ for ( var i = 0; i < opts.length; i++ ) {
1600
+ popProductFields( opts[ i ] );
1601
+ }
1602
+ }
1603
+
1604
+ function maybeSetProductField( field ) {
1605
+ var productFields, quantityFields, fieldsList, productFieldOpt, fieldId;
1606
+
1607
+ fieldsList = jQuery( field ).closest( 'ul.frm_sorting' );
1608
+ productFields = fieldsList.children( '.edit_field_type_product' );
1609
+ quantityFields = fieldsList.children( '.edit_field_type_quantity' );
1610
+
1611
+ if ( 1 === quantityFields.length && 1 === productFields.length ) {
1612
+ fieldId = field.getAttribute( 'data-fid' );
1613
+ productFieldOpt = document.getElementById( 'field_options[product_field_' + fieldId + ']' );
1614
+ if ( null === productFieldOpt ) {
1615
+ return; // very unlikely though
1616
  }
1617
+
1618
+ productFieldOpt.setAttribute( 'data-frmcurrent', productFields[0].getAttribute( 'data-fid' ) );
1619
+ popProductFields( productFieldOpt );
1620
+ // in order to move its settings to that LHS panel where
1621
+ // the update form resides, else it'll lose this setting
1622
+ moveFieldSettings( document.getElementById( 'frm-single-settings-' + fieldId ) );
1623
  }
1624
  }
1625
 
1805
  fieldId = jQuery( this ).closest( '[data-fid]' ).data( 'fid' ),
1806
  separate = usingSeparateValues( fieldId ),
1807
  optList = document.getElementById( 'frm_field_' + fieldId + '_opts' ),
1808
+ opts = optList.getElementsByTagName( 'li' ),
1809
+ product = isProductField( fieldId );
1810
 
1811
  document.getElementById( 'bulk-field-id' ).value = fieldId;
1812
 
1819
  if ( separate ) {
1820
  content += '|' + document.getElementsByName( 'field_options[options_' + fieldId + '][' + key + '][value]' )[0].value;
1821
  }
1822
+ if ( product ) {
1823
+ content += '|' + document.getElementsByName( 'field_options[options_' + fieldId + '][' + key + '][price]' )[0].value;
1824
+ }
1825
  content += "\r\n";
1826
  }
1827
  }
2067
  settings.remove();
2068
 
2069
  $thisField.fadeOut( 'slow', function() {
2070