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/FrmAppHelper.php +369 -205 6.27 → trunk View file →
@@ -9,9 +9,9 @@
9 9 * Version of the database we are moving to.
10 10 *
11 11 * @var int
12 12 */
13 - public static $db_version = 104;
13 + public static $db_version = 106;
14 14
15 15 /**
16 16 * Used by the API add-on.
17 17 *
@@ -28,9 +28,9 @@
28 28 * @since 2.0
29 29 *
30 30 * @var string
31 31 */
32 - public static $plug_version = '6.27';
32 + public static $plug_version = '6.35';
33 33
34 34 /**
35 35 * @var bool
36 36 */
@@ -202,9 +202,9 @@
202 202 }
203 203
204 204 $query_args = wp_parse_args( $parsed['query'] );
205 205
206 - return empty( $query_args['utm_medium'] ) ? '' : $query_args['utm_medium'];
206 + return ! empty( $query_args['utm_medium'] ) ? $query_args['utm_medium'] : '';
207 207 }
208 208
209 209 /**
210 210 * @since 6.25.1
@@ -270,14 +270,14 @@
270 270 * @since 2.0
271 271 *
272 272 * @param array $args - May include the form id when values need translation.
273 273 *
274 - * @return FrmSettings $frm_settings
274 + * @return FrmSettings
275 275 */
276 276 public static function get_settings( $args = array() ) {
277 277 global $frm_settings;
278 278
279 - if ( empty( $frm_settings ) ) {
279 + if ( ! $frm_settings ) {
280 280 $frm_settings = new FrmSettings( $args );
281 281 } elseif ( isset( $args['current_form'] ) ) {
282 282 // If the global has already been set, allow strings to be filtered.
283 283 $frm_settings->maybe_filter_for_form( $args );
@@ -289,10 +289,13 @@
289 289 /**
290 290 * @return string
291 291 */
292 292 public static function get_menu_name() {
293 - $frm_settings = self::get_settings();
294 - return FrmAddonsController::is_license_expired() || ! self::pro_is_installed() ? 'Formidable' : $frm_settings->menu;
293 + if ( ! self::pro_is_installed() || FrmAddonsController::is_license_expired() ) {
294 + return 'Formidable';
295 + }
296 +
297 + return self::get_settings()->menu;
295 298 }
296 299
297 300 /**
298 301 * Determine if the current branding is set to 'formidable'.
@@ -306,10 +309,9 @@
306 309 if ( ! self::pro_is_installed() ) {
307 310 return true;
308 311 }
309 312
310 - $menu_icon = self::get_menu_icon_class();
311 - return str_contains( $menu_icon, 'frm_logo_icon' );
313 + return str_contains( self::get_menu_icon_class(), 'frm_logo_icon' );
312 314 }
313 315
314 316 /**
315 317 * @since 3.05
@@ -494,9 +496,9 @@
494 496 global $pagenow;
495 497 $get_page = self::simple_get( 'page', 'sanitize_title' );
496 498
497 499 if ( $pagenow ) {
498 - // allow this to be true during ajax load i.e. ajax form builder loading
500 + // Allow this to be true during ajax load i.e. ajax form builder loading
499 501 $is_page = ( $pagenow === 'admin.php' || $pagenow === 'admin-ajax.php' ) && $get_page === $page;
500 502
501 503 if ( $is_page ) {
502 504 return true;
@@ -756,17 +758,19 @@
756 758 )
757 759 );
758 760 }
759 761
760 - if ( isset( $params ) && is_array( $value ) && $value ) {
761 - foreach ( $params as $k => $p ) {
762 - if ( ! $k || ! is_array( $value ) ) {
763 - continue;
764 - }
762 + if ( ! isset( $params ) || ! is_array( $value ) || ! $value ) {
763 + return $value;
764 + }
765 765
766 - $p = trim( $p, ']' );
767 - $value = $value[ $p ] ?? $default;
766 + foreach ( $params as $k => $p ) {
767 + if ( ! $k || ! is_array( $value ) ) {
768 + continue;
768 769 }
770 +
771 + $p = trim( $p, ']' );
772 + $value = $value[ $p ] ?? $default;
769 773 }
770 774
771 775 return $value;
772 776 }
@@ -799,9 +803,9 @@
799 803 * @param string $param
800 804 * @param string $sanitize
801 805 * @param string $default
802 806 *
803 - * @return array|string
807 + * @return array|int|string
804 808 */
805 809 public static function simple_get( $param, $sanitize = 'sanitize_text_field', $default = '' ) {
806 810 return self::get_simple_request(
807 811 array(
@@ -847,11 +851,10 @@
847 851 self::unserialize_or_decode( $value );
848 852 }
849 853 }
850 854 } elseif ( isset( $_REQUEST[ $args['param'] ] ) ) {
851 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
852 - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
853 - $value = wp_unslash( $_REQUEST[ $args['param'] ] );
855 + // phpcs:ignore phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
856 + $value = wp_unslash( $_REQUEST[ $args['param'] ] );
854 857 }
855 858
856 859 self::sanitize_value( $args['sanitize'], $value );
857 860
@@ -864,17 +867,13 @@
864 867 * @since 2.0.8
865 868 *
866 869 * @param string $value
867 870 *
868 - * @return string $value
871 + * @return string Value.
869 872 */
870 873 public static function preserve_backslashes( $value ) {
871 874 // If backslashes have already been added, don't add them again
872 - if ( ! str_contains( $value, '\\\\' ) ) {
873 - $value = addslashes( $value );
874 - }
875 -
876 - return $value;
875 + return str_contains( $value, '\\\\' ) ? $value : addslashes( $value );
877 876 }
878 877
879 878 /**
880 879 * Sanitize a value in-place.
@@ -938,8 +937,9 @@
938 937 } else {
939 938 self::sanitize_value( self::class . '::strip_most_html', $value );
940 939 }
941 940 self::decode_specialchars( $value );
941 + self::sanitize_value( 'FrmHtmlSanitizer::sanitize_url_attributes', $value );
942 942 }
943 943
944 944 /**
945 945 * Allow only a small set of very basic HTML for unprivileged users.
@@ -948,8 +948,12 @@
948 948 *
949 949 * @param string $value
950 950 */
951 951 public static function strip_most_html( $value ) {
952 + if ( '' === $value ) {
953 + return $value;
954 + }
955 +
952 956 $allowed_html = array(
953 957 'b' => array(),
954 958 'br' => array(),
955 959 'strong' => array(),
@@ -1046,10 +1050,9 @@
1046 1050 *
1047 1051 * @return string
1048 1052 */
1049 1053 public static function kses( $value, $allowed = array() ) {
1050 - $allowed_html = self::allowed_html( $allowed );
1051 - return wp_kses( $value, $allowed_html );
1054 + return wp_kses( $value, self::allowed_html( $allowed ) );
1052 1055 }
1053 1056
1054 1057 /**
1055 1058 * Sanitizes and echoes a given value.
@@ -1098,9 +1101,9 @@
1098 1101 $html = str_replace( 'class="frm_prev_page"', 'class="frm_prev_page" [back_hook]', $html );
1099 1102 }
1100 1103
1101 1104 if ( $included_draft_hook ) {
1102 - $html = str_replace( 'class="frm_save_draft"', 'class="frm_save_draft" [draft_hook]', $html );
1105 + return str_replace( 'class="frm_save_draft"', 'class="frm_save_draft" [draft_hook]', $html );
1103 1106 }
1104 1107
1105 1108 return $html;
1106 1109 }
@@ -1161,8 +1164,10 @@
1161 1164 }
1162 1165
1163 1166 /**
1164 1167 * @since 2.05.03
1168 + *
1169 + * @return array
1165 1170 */
1166 1171 private static function safe_html() {
1167 1172 $allow_class = array(
1168 1173 'class' => true,
@@ -1344,13 +1349,9 @@
1344 1349 }
1345 1350
1346 1351 global $wp_query;
1347 1352
1348 - if ( isset( $wp_query->query_vars[ $param ] ) ) {
1349 - $value = $wp_query->query_vars[ $param ];
1350 - }
1351 -
1352 - return $value;
1353 + return $wp_query->query_vars[ $param ] ?? $value;
1353 1354 }
1354 1355
1355 1356 /**
1356 1357 * Try to show the SVG if possible. Otherwise, use the font icon.
@@ -1368,10 +1369,9 @@
1368 1369 if ( isset( $atts['echo'] ) ) {
1369 1370 unset( $atts['echo'] );
1370 1371 }
1371 1372
1372 - $html_atts = self::array_to_html_params( $atts );
1373 - $icon = trim( str_replace( array( 'frm_icon_font', 'frmfont ' ), '', $class ) );
1373 + $icon = trim( str_replace( array( 'frm_icon_font', 'frmfont ' ), '', $class ) );
1374 1374
1375 1375 // Replace icons that have been removed or renamed.
1376 1376 $deprecated = array(
1377 1377 'frm_clone_solid_icon' => 'frm_clone_icon',
@@ -1383,27 +1383,55 @@
1383 1383 $icon = $deprecated[ $icon ];
1384 1384 $class = str_replace( $icon, $deprecated[ $icon ], $class );
1385 1385 }
1386 1386
1387 - if ( $icon === $class ) {
1388 - $icon = '<i class="' . esc_attr( $class ) . '"' . $html_atts . '></i>';
1389 - } else {
1390 - $class = ! str_contains( $icon, ' ' ) ? '' : ' ' . $icon;
1387 + $is_font_icon = $icon === $class;
1391 1388
1389 + if ( ! $is_font_icon ) {
1390 + $class = str_contains( $icon, ' ' ) ? ' ' . $icon : '';
1391 +
1392 1392 if ( str_contains( $icon, ' ' ) ) {
1393 1393 $icon = explode( ' ', $icon );
1394 1394 $icon = reset( $icon );
1395 1395 }
1396 + }
1396 1397
1397 - $icon = '<svg class="frmsvg' . esc_attr( $class ) . '"' . $html_atts . '><use href="#' . esc_attr( $icon ) . '" /></svg>';
1398 - }
1398 + if ( $atts ) {
1399 + // A caller passed attributes, so kses still has to decide which of them survive. Its
1400 + // allowlist comes from safe_html() through the frm_striphtml_allowed_tags filter, and
1401 + // add-ons widen it around their own icons, so there is no fixed list to check against.
1402 + $html_atts = self::array_to_html_params( $atts );
1403 + $markup = $is_font_icon
1404 + ? '<i class="' . esc_attr( $class ) . '"' . $html_atts . '></i>'
1405 + : '<svg class="frmsvg' . esc_attr( $class ) . '"' . $html_atts . '><use href="#' . esc_attr( $icon ) . '" /></svg>';
1399 1406
1400 - if ( $echo ) {
1401 - echo self::kses_icon( $icon ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1407 + if ( ! $echo ) {
1408 + return $markup;
1409 + }
1410 +
1411 + echo self::kses_icon( $markup ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1402 1412 return null;
1403 1413 }
1404 1414
1405 - return $icon;
1415 + /**
1416 + * With no attributes from the caller, the tag is nothing but this method's own markup
1417 + * around an escaped class and icon id, so there is nothing left for kses to decide and it
1418 + * can be skipped. Echoing the pieces rather than a finished string keeps that safe to the
1419 + * escaping sniff without an annotation.
1420 + *
1421 + * This is the path the form builder takes for most of the tens of thousands of icons it
1422 + * renders on a large form, and the kses pass was most of what each one cost.
1423 + */
1424 + $callback = function () use ( $is_font_icon, $class, $icon ) {
1425 + if ( $is_font_icon ) {
1426 + echo '<i class="' . esc_attr( $class ) . '"></i>';
1427 + return;
1428 + }
1429 +
1430 + echo '<svg class="frmsvg' . esc_attr( $class ) . '"><use href="#' . esc_attr( $icon ) . '" /></svg>';
1431 + };
1432 +
1433 + return self::clip( $callback, $echo );
1406 1434 }
1407 1435
1408 1436 /**
1409 1437 * Run kses for icons. It needs to add a few filters first in order to preserve some custom style values.
@@ -1515,13 +1543,15 @@
1515 1543 * @return string|void
1516 1544 */
1517 1545 public static function array_to_html_params( $atts, $echo = false ) {
1518 1546 $callback = function () use ( $atts ) {
1519 - if ( $atts ) {
1520 - foreach ( $atts as $key => $value ) {
1521 - echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
1522 - }
1547 + if ( ! $atts ) {
1548 + return;
1523 1549 }
1550 +
1551 + foreach ( $atts as $key => $value ) {
1552 + echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
1553 + }
1524 1554 };
1525 1555 return self::clip( $callback, $echo );
1526 1556 }
1527 1557
@@ -1533,8 +1563,10 @@
1533 1563 * @param Closure $echo_function
1534 1564 * @param bool $echo
1535 1565 *
1536 1566 * @return string|null
1567 + *
1568 + * @psalm-return ($echo is true ? null : string)
1537 1569 */
1538 1570 public static function clip( $echo_function, $echo = false ) {
1539 1571 if ( ! $echo ) {
1540 1572 ob_start();
@@ -1575,13 +1607,15 @@
1575 1607 *
1576 1608 * @return void
1577 1609 */
1578 1610 public static function import_link( $type = 'secondary' ) {
1611 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1579 1612 ?>
1580 1613 <a href="<?php echo esc_url( admin_url( 'admin.php?page=formidable-import' ) ); ?>" class="button frm-button-<?php echo esc_attr( $type ); ?> frm_animate_bg">
1581 1614 <?php esc_html_e( 'Import', 'formidable' ); ?>
1582 1615 </a>
1583 1616 <?php
1617 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1584 1618 }
1585 1619
1586 1620 /**
1587 1621 * Print applicable admin banner.
@@ -1602,8 +1636,9 @@
1602 1636 // Print license warning or inbox banner and exit if either prints.
1603 1637 // And exit before printing the upgrade bar if it shouldn't be shown.
1604 1638 return;
1605 1639 }
1640 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1606 1641 ?>
1607 1642 <div class="frm-upgrade-bar">
1608 1643 <div class="frm-upgrade-bar-inner">
1609 1644 <?php
@@ -1623,9 +1658,9 @@
1623 1658
1624 1659 printf(
1625 1660 /* translators: %1$s: Start link HTML, %2$s: CTA text ("upgrading to PRO" by default), %3$s: End link HTML */
1626 1661 esc_html__( 'You\'re using Formidable Forms Lite. To unlock more features consider %1$s%2$s%3$s.', 'formidable' ),
1627 - '<a href="' . esc_url( $upgrade_link ) . '">',
1662 + '<a href="' . esc_url( $upgrade_link ) . '" target="_blank" rel="noopener">',
1628 1663 esc_html( $cta_text ),
1629 1664 '</a>'
1630 1665 );
1631 1666 ?>
@@ -1631,8 +1666,9 @@
1631 1666 ?>
1632 1667 </div>
1633 1668 </div>
1634 1669 <?php
1670 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1635 1671 }
1636 1672
1637 1673 /**
1638 1674 * @since 5.4.2
@@ -1730,8 +1766,9 @@
1730 1766
1731 1767 if ( ! empty( $atts['tosearch'] ) ) {
1732 1768 $input_atts['autocomplete'] = 'off';
1733 1769 }
1770 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1734 1771 ?>
1735 1772 <p class="frm-search <?php echo esc_attr( $atts['class'] ); ?>">
1736 1773 <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>">
1737 1774 <?php echo esc_html( $atts['text'] ); ?>:
@@ -1744,8 +1781,9 @@
1744 1781 }
1745 1782 ?>
1746 1783 </p>
1747 1784 <?php
1785 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1748 1786 }
1749 1787
1750 1788 /**
1751 1789 * @param string $type Hook type slug.
@@ -1816,9 +1854,9 @@
1816 1854 * True when value is 1, true, 'true', 'yes'
1817 1855 *
1818 1856 * @since 1.07.10
1819 1857 *
1820 - * @param int|string $value The value to compare.
1858 + * @param bool|int|string $value The value to compare.
1821 1859 *
1822 1860 * @return bool
1823 1861 */
1824 1862 public static function is_true( $value ) {
@@ -1939,8 +1977,9 @@
1939 1977 if ( ! empty( $args['id'] ) ) {
1940 1978 $html_attrs['id'] = $args['id'];
1941 1979 }
1942 1980
1981 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1943 1982 if ( count( $args['source'] ) <= $args['dropdown_limit'] ) {
1944 1983 ?>
1945 1984 <select <?php self::array_to_html_params( $html_attrs, true ); ?>>
1946 1985 <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
@@ -1955,33 +1994,35 @@
1955 1994 <option value="<?php echo esc_attr( $value_label['value'] ); ?>" <?php selected( $value_label['value'], $args['selected'] ); ?>><?php echo esc_html( $value_label['label'] ); ?></option><?php // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong ?>
1956 1995 <?php endforeach; ?>
1957 1996 </select>
1958 1997 <?php
1959 - } else {
1960 - $options = array();
1961 - $autocomplete_value = '';
1998 + return;
1999 + }
2000 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1962 2001
1963 - foreach ( $args['source'] as $key => $source ) {
1964 - $value_label = self::get_dropdown_value_and_label_from_option( $source, $key, $args );
2002 + $options = array();
2003 + $autocomplete_value = '';
1965 2004
1966 - if ( $value_label['value'] === $args['selected'] ) {
1967 - $autocomplete_value = $value_label['label'];
1968 - }
2005 + foreach ( $args['source'] as $key => $source ) {
2006 + $value_label = self::get_dropdown_value_and_label_from_option( $source, $key, $args );
1969 2007
1970 - $options[] = $value_label;
2008 + if ( $value_label['value'] === $args['selected'] ) {
2009 + $autocomplete_value = $value_label['label'];
1971 2010 }
1972 2011
1973 - $html_attrs['type'] = 'hidden';
1974 - $html_attrs['class'] = 'frm_autocomplete_value_input';
1975 - $html_attrs['value'] = $args['selected'];
1976 - ?>
1977 - <input type="text" class="frm-custom-search"
1978 - data-source="<?php echo esc_attr( wp_json_encode( $options ) ); ?>"
1979 - placeholder="<?php echo esc_attr( $args['autocomplete_placeholder'] ); ?>"
1980 - value="<?php echo esc_attr( $autocomplete_value ); ?>" />
1981 - <input <?php self::array_to_html_params( $html_attrs, true ); ?> />
1982 - <?php
1983 - }//end if
2012 + $options[] = $value_label;
2013 + }
2014 +
2015 + $html_attrs['type'] = 'hidden';
2016 + $html_attrs['class'] = 'frm_autocomplete_value_input';
2017 + $html_attrs['value'] = $args['selected'];
2018 + ?>
2019 + <input type="text" class="frm-custom-search"
2020 + data-source="<?php echo esc_attr( wp_json_encode( $options ) ); ?>"
2021 + placeholder="<?php echo esc_attr( $args['autocomplete_placeholder'] ); ?>"
2022 + value="<?php echo esc_attr( $autocomplete_value ); ?>" />
2023 + <input <?php self::array_to_html_params( $html_attrs, true ); ?> />
2024 + <?php
1984 2025 }
1985 2026
1986 2027 /**
1987 2028 * Gets dropdown value and label from autodropdown option.
@@ -2015,8 +2056,9 @@
2015 2056 self::prep_page_dropdown_params( $page_id, $truncate, $args );
2016 2057
2017 2058 $pages = self::get_post_ids_and_titles( $args['post_type'] );
2018 2059 $selected = self::get_post_param( $args['field_name'], $args['page_id'], 'absint' );
2060 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
2019 2061 ?>
2020 2062 <select name="<?php echo esc_attr( $args['field_name'] ); ?>" id="<?php echo esc_attr( $args['field_name'] ); ?>" class="frm-pages-dropdown">
2021 2063 <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
2022 2064 <?php foreach ( $pages as $page ) { ?>
@@ -2025,8 +2067,9 @@
2025 2067 </option>
2026 2068 <?php } ?>
2027 2069 </select>
2028 2070 <?php
2071 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
2029 2072 }
2030 2073
2031 2074 /**
2032 2075 * Fill in missing parameters passed to wp_pages_dropdown().
@@ -2098,11 +2141,9 @@
2098 2141 *
2099 2142 * @return bool
2100 2143 */
2101 2144 public static function is_full_screen() {
2102 - return self::is_form_builder_page() ||
2103 - self::is_style_editor_page() ||
2104 - self::is_full_screen_view_builder_page();
2145 + return self::is_form_builder_page() || self::is_style_editor_page() || self::is_full_screen_view_builder_page();
2105 2146 }
2106 2147
2107 2148 /**
2108 2149 * Check if user is on the style editor or its alternative URL.
@@ -2151,8 +2192,9 @@
2151 2192 * @param array|string $capability
2152 2193 * @param string $multiple 'single' and 'multiple'.
2153 2194 */
2154 2195 public static function wp_roles_dropdown( $field_name, $capability, $multiple = 'single' ) {
2196 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
2155 2197 ?>
2156 2198 <select name="<?php echo esc_attr( $field_name ); ?>" id="<?php echo esc_attr( $field_name ); ?>"
2157 2199 <?php echo 'multiple' === $multiple ? 'multiple="multiple"' : ''; ?>
2158 2200 class="frm_multiselect">
@@ -2158,8 +2200,9 @@
2158 2200 class="frm_multiselect">
2159 2201 <?php self::roles_options( $capability ); ?>
2160 2202 </select>
2161 2203 <?php
2204 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
2162 2205 }
2163 2206
2164 2207 /**
2165 2208 * @since 4.07
@@ -2325,10 +2368,9 @@
2325 2368 if ( ! current_user_can( 'administrator' ) || current_user_can( 'frm_view_forms' ) ) {
2326 2369 return;
2327 2370 }
2328 2371
2329 - $user_id = get_current_user_id();
2330 - $user = new WP_User( $user_id );
2372 + $user = new WP_User( get_current_user_id() );
2331 2373 $frm_roles = self::frm_capabilities();
2332 2374
2333 2375 foreach ( $frm_roles as $frm_role => $frm_role_description ) {
2334 2376 $user->add_cap( $frm_role );
@@ -2345,15 +2387,17 @@
2345 2387 *
2346 2388 * @return void
2347 2389 */
2348 2390 public static function force_capability( $cap = 'frm_change_settings' ) {
2349 - if ( current_user_can( 'administrator' ) && ! current_user_can( $cap ) ) {
2350 - $role = get_role( 'administrator' );
2351 - $frm_roles = self::frm_capabilities();
2391 + if ( ! current_user_can( 'administrator' ) || current_user_can( $cap ) ) {
2392 + return;
2393 + }
2352 2394
2353 - foreach ( $frm_roles as $frm_role => $frm_role_description ) {
2354 - $role->add_cap( $frm_role );
2355 - }
2395 + $role = get_role( 'administrator' );
2396 + $frm_roles = self::frm_capabilities();
2397 +
2398 + foreach ( $frm_roles as $frm_role => $frm_role_description ) {
2399 + $role->add_cap( $frm_role );
2356 2400 }
2357 2401 }
2358 2402
2359 2403 /**
@@ -2367,15 +2411,17 @@
2367 2411 */
2368 2412 public static function permission_check( $permission, $show_message = 'show' ) {
2369 2413 $permission_error = self::permission_nonce_error( $permission );
2370 2414
2371 - if ( $permission_error !== false ) {
2372 - if ( 'hide' === $show_message ) {
2373 - $permission_error = '';
2374 - }
2415 + if ( $permission_error === false ) {
2416 + return;
2417 + }
2375 2418
2376 - wp_die( esc_html( $permission_error ) );
2419 + if ( 'hide' === $show_message ) {
2420 + $permission_error = '';
2377 2421 }
2422 +
2423 + wp_die( esc_html( $permission_error ) );
2378 2424 }
2379 2425
2380 2426 /**
2381 2427 * Check user permission and nonce
@@ -2423,9 +2469,9 @@
2423 2469 }
2424 2470
2425 2471 /**
2426 2472 * @param array|string $values
2427 - * @param string $current
2473 + * @param string|null $current
2428 2474 *
2429 2475 * @return bool
2430 2476 */
2431 2477 public static function check_selected( $values, $current ) {
@@ -2433,9 +2479,9 @@
2433 2479 $values = self::recursive_function_map( $values, 'htmlspecialchars_decode' );
2434 2480 $current = is_null( $current ) ? '' : htmlspecialchars_decode( trim( $current ) );
2435 2481
2436 2482 // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict, Universal.Operators.StrictComparisons
2437 - return ( is_array( $values ) && in_array( $current, $values ) ) || ( ! is_array( $values ) && $values == $current );
2483 + return is_array( $values ) ? in_array( $current, $values ) : $values == $current;
2438 2484 }
2439 2485
2440 2486 /**
2441 2487 * @param array|string $value
@@ -2447,16 +2493,16 @@
2447 2493 if ( is_array( $value ) ) {
2448 2494 $original_function = $function;
2449 2495 $function = count( $value ) ? explode( ', ', FrmDb::prepare_array_values( $value, $function ) ) : array( $function );
2450 2496
2451 - if ( ! self::is_assoc( $value ) ) {
2452 - $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
2453 - } else {
2497 + if ( self::is_assoc( $value ) ) {
2454 2498 foreach ( $value as $k => $v ) {
2455 2499 if ( ! is_array( $v ) ) {
2456 2500 $value[ $k ] = call_user_func( $original_function, $v );
2457 2501 }
2458 2502 }
2503 + } else {
2504 + $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
2459 2505 }
2460 2506 } else {
2461 2507 $value = self::maybe_update_value_if_null( $value, $function );
2462 2508 $value = call_user_func( $function, $value );
@@ -2476,9 +2522,9 @@
2476 2522 * @return mixed
2477 2523 */
2478 2524 private static function maybe_update_value_if_null( $value, $function ) {
2479 2525 if ( null === $value && in_array( $function, array( 'trim', 'strlen' ), true ) ) {
2480 - $value = '';
2526 + return '';
2481 2527 }
2482 2528
2483 2529 return $value;
2484 2530 }
@@ -2506,9 +2552,9 @@
2506 2552 foreach ( $array as $key => $value ) {
2507 2553 if ( is_array( $value ) ) {
2508 2554 $return = array_merge( $return, self::array_flatten( $value, $keys ) );
2509 2555 } elseif ( $keys === 'keep' ) {
2510 - $return[ $key ] = $value;
2556 + $return[ $key ] = $value;
2511 2557 } else {
2512 2558 $return[] = $value;
2513 2559 }
2514 2560 }
@@ -2563,9 +2609,9 @@
2563 2609 * @return mixed
2564 2610 */
2565 2611 public static function use_wpautop( $content ) {
2566 2612 if ( apply_filters( 'frm_use_wpautop', true ) && is_string( $content ) ) {
2567 - $content = wpautop( str_replace( '<br>', '<br />', $content ) );
2613 + return wpautop( str_replace( '<br>', '<br />', $content ) );
2568 2614 }
2569 2615
2570 2616 return $content;
2571 2617 }
@@ -2606,9 +2652,9 @@
2606 2652
2607 2653 $query = $wp_scripts->registered[ $handle ];
2608 2654
2609 2655 if ( is_object( $query ) && ! empty( $query->ver ) ) {
2610 - $ver = $query->ver;
2656 + return $query->ver;
2611 2657 }
2612 2658
2613 2659 return $ver;
2614 2660 }
@@ -2701,31 +2747,31 @@
2701 2747 $column
2702 2748 );
2703 2749
2704 2750 // Create a unique field id if it has already been used.
2705 - if ( in_array( $key, $similar_keys, true ) ) {
2706 - $key = self::maybe_truncate_key_before_appending( $column, $key );
2751 + if ( ! in_array( $key, $similar_keys, true ) ) {
2752 + return $key;
2753 + }
2707 2754
2708 - /**
2709 - * Allow for a custom separator between the attempted key and the generated suffix.
2710 - *
2711 - * @since 5.2.03
2712 - *
2713 - * @param string $separator. Default empty.
2714 - * @param string $key the key without the added suffix.
2715 - */
2716 - $separator = apply_filters( 'frm_unique_' . $column . '_separator', '', $key );
2755 + $key = self::maybe_truncate_key_before_appending( $column, $key );
2717 2756
2718 - $suffix = 2;
2719 - do {
2720 - $key_check = $key . $separator . $suffix;
2721 - ++$suffix;
2722 - } while ( in_array( $key_check, $similar_keys, true ) );
2757 + /**
2758 + * Allow for a custom separator between the attempted key and the generated suffix.
2759 + *
2760 + * @since 5.2.03
2761 + *
2762 + * @param string $separator. Default empty.
2763 + * @param string $key the key without the added suffix.
2764 + */
2765 + $separator = apply_filters( 'frm_unique_' . $column . '_separator', '', $key );
2723 2766
2724 - $key = $key_check;
2725 - }//end if
2767 + $suffix = 2;
2768 + do {
2769 + $key_check = $key . $separator . $suffix;
2770 + ++$suffix;
2771 + } while ( in_array( $key_check, $similar_keys, true ) );
2726 2772
2727 - return $key;
2773 + return $key_check;
2728 2774 }
2729 2775
2730 2776 /**
2731 2777 * Avoid trying to append to a really long key,
@@ -2736,17 +2782,19 @@
2736 2782 *
2737 2783 * @return string
2738 2784 */
2739 2785 private static function maybe_truncate_key_before_appending( $column, $key ) {
2740 - if ( in_array( $column, array( 'form_key', 'field_key' ), true ) ) {
2741 - $max_key_length_before_truncating = 60;
2786 + if ( ! in_array( $column, array( 'form_key', 'field_key' ), true ) ) {
2787 + return $key;
2788 + }
2742 2789
2743 - if ( strlen( $key ) > $max_key_length_before_truncating ) {
2744 - $key = substr( $key, 0, $max_key_length_before_truncating );
2790 + $max_key_length_before_truncating = 60;
2745 2791
2746 - if ( is_numeric( $key ) ) {
2747 - $key .= 'a';
2748 - }
2792 + if ( strlen( $key ) > $max_key_length_before_truncating ) {
2793 + $key = substr( $key, 0, $max_key_length_before_truncating );
2794 +
2795 + if ( is_numeric( $key ) ) {
2796 + $key .= 'a';
2749 2797 }
2750 2798 }
2751 2799
2752 2800 return $key;
@@ -2761,9 +2809,9 @@
2761 2809 * @return string either the original key value, or an empty string if the key was too long.
2762 2810 */
2763 2811 private static function maybe_clear_long_key( $key, $column ) {
2764 2812 if ( 'field_key' === $column && strlen( $key ) >= 70 ) {
2765 - $key = '';
2813 + return '';
2766 2814 }
2767 2815 return $key;
2768 2816 }
2769 2817
@@ -2864,17 +2912,20 @@
2864 2912 *
2865 2913 * @return void
2866 2914 */
2867 2915 private static function prepare_field_arrays( $fields, $record, array &$values, $args ) {
2868 - if ( $fields ) {
2869 - foreach ( (array) $fields as $field ) {
2870 - if ( ! self::is_admin_page() ) {
2871 - // Don't prep default values on the form settings page.
2872 - $field->default_value = apply_filters( 'frm_get_default_value', $field->default_value, $field, true );
2873 - }
2874 - $args['parent_form_id'] = $args['parent_form_id'] ?? $field->form_id;
2875 - self::fill_field_defaults( $field, $record, $values, $args );
2916 + if ( ! $fields ) {
2917 + return;
2918 + }
2919 +
2920 + foreach ( (array) $fields as $field ) {
2921 + if ( ! self::is_admin_page() ) {
2922 + // Don't prep default values on the form settings page.
2923 + $field->default_value = apply_filters( 'frm_get_default_value', $field->default_value, $field, true );
2876 2924 }
2925 +
2926 + $args['parent_form_id'] = $args['parent_form_id'] ?? $field->form_id;
2927 + self::fill_field_defaults( $field, $record, $values, $args );
2877 2928 }
2878 2929 }
2879 2930
2880 2931 /**
@@ -2894,19 +2945,19 @@
2894 2945 if ( ! isset( $field->field_options['custom_field'] ) ) {
2895 2946 $field->field_options['custom_field'] = '';
2896 2947 }
2897 2948
2898 - $meta_value = FrmProEntryMetaHelper::get_post_value(
2899 - $record->post_id,
2900 - $field->field_options['post_field'],
2901 - $field->field_options['custom_field'],
2902 - array(
2903 - 'truncate' => false,
2904 - 'type' => $field->type,
2905 - 'form_id' => $field->form_id,
2906 - 'field' => $field,
2907 - )
2908 - );
2949 + $meta_value = FrmProEntryMetaHelper::get_post_value(
2950 + $record->post_id,
2951 + $field->field_options['post_field'],
2952 + $field->field_options['custom_field'],
2953 + array(
2954 + 'truncate' => false,
2955 + 'type' => $field->type,
2956 + 'form_id' => $field->form_id,
2957 + 'field' => $field,
2958 + )
2959 + );
2909 2960 } else {
2910 2961 $meta_value = FrmEntryMeta::get_meta_value( $record, $field->id );
2911 2962 }//end if
2912 2963
@@ -3043,30 +3094,43 @@
3043 3094 return $frm_settings->load_style !== 'none';
3044 3095 }
3045 3096
3046 3097 /**
3098 + * Truncate a string.
3099 + *
3100 + * Note: By default, this function will allow for a few additional characters more than $length.
3101 + * If a string has no spaces, it allows up to 50 additional characters. To force a true length limit,
3102 + * use $force_length_limit = true.
3103 + *
3047 3104 * @param mixed $original_string
3048 3105 * @param int|string $length
3049 3106 * @param int $minword
3050 3107 * @param string $continue
3108 + * @param bool $force_length_limit Force the final string to never exceed the length limit.
3051 3109 *
3052 3110 * @return string
3053 3111 */
3054 - public static function truncate( $original_string, $length, $minword = 3, $continue = '...' ) {
3112 + public static function truncate( $original_string, $length, $minword = 3, $continue = '...', $force_length_limit = false ) {
3055 3113 if ( ! is_string( $original_string ) && ! is_int( $original_string ) ) {
3056 3114 return '';
3057 3115 }
3058 3116
3059 - $length = (int) $length;
3060 - $str = wp_strip_all_tags( (string) $original_string );
3061 - $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) );
3117 + $length = (int) $length;
3062 3118
3063 3119 if ( $length === 0 ) {
3064 3120 return '';
3065 3121 }
3066 3122
3123 + $str = wp_strip_all_tags( (string) $original_string );
3124 + $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) );
3125 +
3067 3126 if ( $length <= 10 ) {
3068 3127 $sub = self::mb_function( array( 'mb_substr', 'substr' ), array( $str, 0, $length ) );
3128 +
3129 + if ( $force_length_limit ) {
3130 + return $sub;
3131 + }
3132 +
3069 3133 return $sub . ( $length < $original_len ? $continue : '' );
3070 3134 }
3071 3135
3072 3136 $sub = '';
@@ -3094,10 +3158,21 @@
3094 3158
3095 3159 unset( $total_len, $word );
3096 3160 }
3097 3161
3098 - $sub = self::maybe_force_truncate_on_string_with_no_spaces( $sub, $length );
3162 + $sub = self::maybe_force_truncate_on_string_with_no_spaces( $sub, $length, $force_length_limit );
3099 3163
3164 + if ( $force_length_limit ) {
3165 + // Ensure the final string doesn't exceed the length limit.
3166 + $final_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $sub ) );
3167 +
3168 + if ( $final_len > $length ) {
3169 + return self::mb_function( array( 'mb_substr', 'substr' ), array( $sub, 0, $length ) );
3170 + }
3171 +
3172 + return $sub;
3173 + }
3174 +
3100 3175 return $sub . ( $len < $original_len ? $continue : '' );
3101 3176 }
3102 3177
3103 3178 /**
@@ -3104,27 +3179,39 @@
3104 3179 * If the string is still too long because there may not have been any spaces, force truncate.
3105 3180 *
3106 3181 * @since 6.5.4
3107 3182 *
3108 - * @param string $sub Current substring.
3109 - * @param int $length The length limit.
3183 + * @param string $sub Current substring.
3184 + * @param int $length The length limit.
3185 + * @param bool $force_length_limit Force the string to not exceed the length limit.
3110 3186 *
3111 3187 * @return string
3112 3188 */
3113 - private static function maybe_force_truncate_on_string_with_no_spaces( $sub, $length ) {
3114 - if ( strlen( $sub ) < $length + 50 ) {
3115 - // If the string isn't way over the limit, leave it.
3116 - return $sub;
3189 + private static function maybe_force_truncate_on_string_with_no_spaces( $sub, $length, $force_length_limit = false ) {
3190 + if ( ! $force_length_limit ) {
3191 + if ( strlen( $sub ) < $length + 50 ) {
3192 + // If the string isn't way over the limit, leave it.
3193 + return $sub;
3194 + }
3195 +
3196 + $first_space = strpos( $sub, ' ', $length );
3197 +
3198 + if ( false !== $first_space ) {
3199 + // Ignore anything with spaces.
3200 + return $sub;
3201 + }
3202 +
3203 + return substr( $sub, 0, $length + 10 );
3117 3204 }
3118 3205
3119 - $first_space = strpos( $sub, ' ', $length );
3206 + // When force_length_limit is true, ensure the string doesn't exceed the length.
3207 + $final_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $sub ) );
3120 3208
3121 - if ( false !== $first_space ) {
3122 - // Ignore anything with spaces.
3123 - return $sub;
3209 + if ( $final_len > $length ) {
3210 + return self::mb_function( array( 'mb_substr', 'substr' ), array( $sub, 0, $length ) );
3124 3211 }
3125 3212
3126 - return substr( $sub, 0, $length + 10 );
3213 + return $sub;
3127 3214 }
3128 3215
3129 3216 /**
3130 3217 * @param array $function_names
@@ -3213,12 +3300,12 @@
3213 3300 * @param int $from In seconds.
3214 3301 * @param int|string $to In seconds.
3215 3302 * @param int|string $levels Number of time units to include or a specific unit.
3216 3303 *
3217 - * @return string $time_ago
3304 + * @return string Time ago.
3218 3305 */
3219 3306 public static function human_time_diff( $from, $to = '', $levels = 1 ) {
3220 - $now = empty( $to ) && 0 !== $to ? new DateTime() : new DateTime( '@' . $to );
3307 + $now = ! $to && 0 !== $to ? new DateTime() : new DateTime( '@' . $to );
3221 3308 $ago = new DateTime( '@' . $from );
3222 3309
3223 3310 // Get the time difference
3224 3311 $diff_object = $now->diff( $ago );
@@ -3397,9 +3484,9 @@
3397 3484 *
3398 3485 * @return int
3399 3486 */
3400 3487 public static function get_last_record_num( $r_count, $current_p, $p_size ) {
3401 - return ( $r_count < $current_p * $p_size ? $r_count : $current_p * $p_size );
3488 + return $r_count < $current_p * $p_size ? $r_count : $current_p * $p_size;
3402 3489 }
3403 3490
3404 3491 /**
3405 3492 * @param int $r_count
@@ -3581,9 +3668,9 @@
3581 3668
3582 3669 // Add extra slashes for \r\n since WP strips them.
3583 3670 $post_content = str_replace( array( '\\r', '\\n', '\\u', '\\t' ), array( '\\\\r', '\\\\n', '\\\\u', '\\\\t' ), $post_content );
3584 3671
3585 - // allow for &quot
3672 + // Allow for &quot
3586 3673 return str_replace( '&quot;', '\\"', $post_content );
3587 3674 }
3588 3675
3589 3676 /**
@@ -3602,15 +3689,17 @@
3602 3689 foreach ( $val as $k1 => $v1 ) {
3603 3690 self::prepare_action_slashes( $v1, $k1, $post_content[ $key ] );
3604 3691 unset( $k1, $v1 );
3605 3692 }
3606 - } else {
3607 - // Strip all slashes so everything is the same, no matter where the value is coming from
3608 - $val = stripslashes( $val );
3609 3693
3610 - // Add backslashes before double quotes and forward slashes only
3611 - $post_content[ $key ] = addcslashes( $val, '"\\/' );
3694 + return;
3612 3695 }
3696 +
3697 + // Strip all slashes so everything is the same, no matter where the value is coming from
3698 + $val = stripslashes( $val );
3699 +
3700 + // Add backslashes before double quotes and forward slashes only
3701 + $post_content[ $key ] = addcslashes( $val, '"\\/' );
3613 3702 }
3614 3703
3615 3704 /**
3616 3705 * Check for either json or serialized data. This is temporary while transitioning
@@ -3651,13 +3740,9 @@
3651 3740 }
3652 3741
3653 3742 $parsed = FrmSerializedStringParserHelper::get()->parse( $value );
3654 3743
3655 - if ( is_array( $parsed ) ) {
3656 - $value = $parsed;
3657 - }
3658 -
3659 - return $value;
3744 + return is_array( $parsed ) ? $parsed : $value;
3660 3745 }
3661 3746
3662 3747 /**
3663 3748 * Decode a JSON string.
@@ -3680,9 +3765,9 @@
3680 3765 $single_value = is_array( $new_string ) && count( $new_string ) === 1 && isset( $new_string[0] );
3681 3766 }
3682 3767
3683 3768 if ( json_last_error() === JSON_ERROR_NONE && is_array( $new_string ) && ! $single_value ) {
3684 - $string = $new_string;
3769 + return $new_string;
3685 3770 }
3686 3771
3687 3772 return $string;
3688 3773 }
@@ -3736,16 +3821,17 @@
3736 3821 }
3737 3822
3738 3823 $key = $input['name'];
3739 3824
3740 - if ( isset( $formatted[ $key ] ) ) {
3741 - if ( is_array( $formatted[ $key ] ) ) {
3742 - $formatted[ $key ][] = $input['value'];
3743 - } else {
3744 - $formatted[ $key ] = array( $formatted[ $key ], $input['value'] );
3745 - }
3825 + if ( ! isset( $formatted[ $key ] ) ) {
3826 + $formatted[ $key ] = $input['value'];
3827 + continue;
3828 + }
3829 +
3830 + if ( is_array( $formatted[ $key ] ) ) {
3831 + $formatted[ $key ][] = $input['value'];
3746 3832 } else {
3747 - $formatted[ $key ] = $input['value'];
3833 + $formatted[ $key ] = array( $formatted[ $key ], $input['value'] );
3748 3834 }
3749 3835 }
3750 3836
3751 3837 parse_str( http_build_query( $formatted ), $form );
@@ -3758,12 +3844,9 @@
3758 3844 *
3759 3845 * @return string
3760 3846 */
3761 3847 public static function maybe_json_encode( $value ) {
3762 - if ( is_array( $value ) ) {
3763 - $value = wp_json_encode( $value );
3764 - }
3765 - return $value;
3848 + return is_array( $value ) ? wp_json_encode( $value ) : $value;
3766 3849 }
3767 3850
3768 3851 /**
3769 3852 * Echo The javascript to open and highlight the Formidable menu
@@ -3798,10 +3881,9 @@
3798 3881 *
3799 3882 * @return void
3800 3883 */
3801 3884 public static function load_admin_wide_js( $load = true ) {
3802 - $version = self::plugin_version();
3803 - wp_register_script( 'formidable_admin_global', self::plugin_url() . '/js/formidable_admin_global.js', array( 'jquery' ), $version );
3885 + wp_register_script( 'formidable_admin_global', self::plugin_url() . '/js/formidable_admin_global.js', array( 'jquery' ), self::plugin_version() );
3804 3886
3805 3887 $global_strings = array(
3806 3888 'updating_msg' => __( 'Please wait while your site updates.', 'formidable' ),
3807 3889 'deauthorize' => __( 'Are you sure you want to deauthorize Formidable Forms on this site?', 'formidable' ),
@@ -3899,8 +3981,10 @@
3899 3981 'text_shortcodes' => __( 'This calculation may have shortcodes that work in text calculations but not numeric calculations.', 'formidable' ),
3900 3982 /* translators: %d is the number of allowed actions per form */
3901 3983 'only_one_action' => sprintf( __( 'This form action is limited to %d per form.', 'formidable' ), 1 ),
3902 3984 'edit_action_text' => __( 'Please edit the existing form action.', 'formidable' ),
3985 + 'only_one_payment_action' => __( 'This form already has a payment action, and is currently limited to a single payment action.', 'formidable' ),
3986 + 'only_one_stripe_action' => __( 'This form already has a payment action. Multiple Stripe actions are available when using the Stripe add-on, available for Formidable Business licenses and higher.', 'formidable' ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3903 3987 'unsafe_params' => FrmFormsHelper::reserved_words(),
3904 3988 /* Translators: %s is the name of a Detail Page Slug that is a reserved word.*/
3905 3989 'slug_is_reserved' => sprintf( __( 'The Detail Page Slug "%s" is reserved by WordPress. This may cause problems. Is this intentional?', 'formidable' ), '****' ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3906 3990 /* 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. */
@@ -3926,11 +4010,14 @@
3926 4010 'holdShiftMsg' => esc_html__( 'You can hold %1$sShift%2$s on your keyboard to select multiple fields', 'formidable' ),
3927 4011 'noTitleText' => FrmFormsHelper::get_no_title_text(),
3928 4012
3929 4013 // In older versions this event listener causes the section to immediately close again
3930 - // when the h3 element is clicked. It's only required in WP 6.7+.
4014 + // When the h3 element is clicked. It's only required in WP 6.7+.
3931 4015 'requireAccordionTitleClickListener' => version_compare( $wp_version, '6.7', '>=' ),
3932 4016 );
4017 +
4018 + self::add_form_builder_modal_data( $admin_script_strings );
4019 +
3933 4020 /**
3934 4021 * @param array $admin_script_strings
3935 4022 */
3936 4023 $admin_script_strings = apply_filters( 'frm_admin_script_strings', $admin_script_strings );
@@ -3943,8 +4030,80 @@
3943 4030 }//end if
3944 4031 }
3945 4032
3946 4033 /**
4034 + * @param array $admin_script_strings
4035 + *
4036 + * @return void
4037 + */
4038 + private static function add_form_builder_modal_data( &$admin_script_strings ) {
4039 + if ( ! self::is_form_builder_page() || self::pro_is_installed() ) {
4040 + return;
4041 + }
4042 +
4043 + $stripe_connected = FrmStrpLiteConnectHelper::at_least_one_mode_is_setup();
4044 + $square_connected = FrmSquareLiteConnectHelper::at_least_one_mode_is_setup();
4045 + $paypal_connected = FrmPayPalLiteConnectHelper::at_least_one_mode_is_setup();
4046 + $gateway_connected = $stripe_connected || $square_connected || $paypal_connected;
4047 + $payments_settings_url = FrmStrpLiteAppController::get_payments_settings_url();
4048 +
4049 + if ( ! $gateway_connected ) {
4050 + // This modal shows when user clicks on one of the pricing fields and no payment gateways configured.
4051 + $admin_script_strings['paymentsSettingsModal'] = array(
4052 + 'title' => __( 'Setup a Payment Gateway first', 'formidable' ),
4053 + 'msg' => __( 'To use the payment fields, please install and configure a payment gateway in your account settings.', 'formidable' ),
4054 + 'closeText' => __( 'Close', 'formidable' ),
4055 + 'actionUrl' => $payments_settings_url,
4056 + 'actionText' => __( 'Go to Payment Settings', 'formidable' ),
4057 + 'noCenter' => true,
4058 + );
4059 + }
4060 +
4061 + // This modal shows on load once after upgrading the plugin.
4062 + $show_pricing_fields_modal = get_option( 'frm_show_pricing_fields_modal' );
4063 +
4064 + if ( ! $show_pricing_fields_modal ) {
4065 + return;
4066 + }
4067 +
4068 + $admin_script_strings['pricingFieldsModal'] = array(
4069 + 'title' => esc_html__( 'Start Accepting Payments Today!', 'formidable' ),
4070 + 'img' => esc_url( self::plugin_url() . '/images/upsell/pricing-fields.png' ),
4071 + 'noCenter' => true,
4072 + );
4073 +
4074 + if ( $gateway_connected ) {
4075 + $gateway_texts = array();
4076 +
4077 + if ( $stripe_connected ) {
4078 + $gateway_texts['stripe'] = esc_html__( 'Stripe', 'formidable' );
4079 + }
4080 +
4081 + if ( $square_connected ) {
4082 + $gateway_texts['square'] = esc_html__( 'Square', 'formidable' );
4083 + }
4084 +
4085 + if ( $paypal_connected ) {
4086 + $gateway_texts['paypal'] = esc_html__( 'PayPal', 'formidable' );
4087 + }
4088 +
4089 + $admin_script_strings['pricingFieldsModal']['msg'] = sprintf(
4090 + // translators: %s: Stripe, Square, or PayPal.
4091 + esc_html__( 'You already have %s connected, so these have already been unlocked.', 'formidable' ),
4092 + esc_html( implode( ' ' . esc_html__( 'and', 'formidable' ) . ' ', $gateway_texts ) )
4093 + );
4094 + } else {
4095 + $admin_script_strings['pricingFieldsModal']['closeText'] = __( 'I\'ll do it later!', 'formidable' );
4096 + $admin_script_strings['pricingFieldsModal']['actionText'] = __( 'Setup Payments Now', 'formidable' );
4097 + $admin_script_strings['pricingFieldsModal']['actionUrl'] = $payments_settings_url;
4098 + // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
4099 + $admin_script_strings['pricingFieldsModal']['msg'] = __( 'We\'ve unlocked Product, Quantity, and Total fields for Lite users! You can now transform your forms into checkout pages. To start collecting revenue, simply connect your preferred payment gateway (Stripe, Square, or PayPal) in your settings.', 'formidable' );
4100 + }//end if
4101 +
4102 + delete_option( 'frm_show_pricing_fields_modal' );
4103 + }
4104 +
4105 + /**
3947 4106 * Get the no label text.
3948 4107 *
3949 4108 * @since 6.25.1
3950 4109 *
@@ -4010,10 +4169,10 @@
4010 4169 }
4011 4170
4012 4171 $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
4013 4172 echo '<tr class="plugin-update-tr active"><th colspan="' . absint( $wp_list_table->get_column_count() ) . '" class="check-column plugin-update colspanchange"><div class="update-message">' . // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
4014 - esc_html__( 'You are running an outdated version of Formidable. This plugin may not work correctly if you do not update Formidable.', 'formidable' ) .
4015 - '</div></td></tr>';
4173 + esc_html__( 'You are running an outdated version of Formidable. This plugin may not work correctly if you do not update Formidable.', 'formidable' ) .
4174 + '</div></td></tr>';
4016 4175 }
4017 4176
4018 4177 /**
4019 4178 * If Pro is far outdated, show a message.
@@ -4067,8 +4226,9 @@
4067 4226 if ( version_compare( phpversion(), '7.0', '<' ) ) {
4068 4227 $message[] = __( 'The version of PHP on your server is too low. If this is not corrected, you may see issues with Formidable Forms. Please contact your web host and ask to be updated to PHP 7.0+.', 'formidable' ); // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
4069 4228 }
4070 4229
4230 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
4071 4231 foreach ( $message as $m ) {
4072 4232 ?>
4073 4233 <div class="frm-banner-alert frm_error_style frm_previous_install">
4074 4234 <?php echo esc_html( $m ); ?>
@@ -4074,8 +4234,9 @@
4074 4234 <?php echo esc_html( $m ); ?>
4075 4235 </div>
4076 4236 <?php
4077 4237 }
4238 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
4078 4239 }
4079 4240
4080 4241 /**
4081 4242 * @param string $type
@@ -4174,12 +4335,12 @@
4174 4335 'zu' => __( 'Zulu', 'formidable' ),
4175 4336 );
4176 4337
4177 4338 if ( $type === 'captcha' ) {
4178 - // remove the languages unavailable for the captcha
4339 + // Remove the languages unavailable for the captcha
4179 4340 $unset = array( 'sq', 'bs', 'eo', 'fo', 'fr-CH', 'sr-SR', 'ar-DZ', 'be', 'cy-GB', 'kk', 'km', 'ky', 'lb', 'mk', 'nb', 'nn', 'rm', 'tj' );
4180 4341 } else {
4181 - // remove the languages unavailable for the datepicker
4342 + // Remove the languages unavailable for the datepicker
4182 4343 $unset = array( 'fil', 'fr-CA', 'de-AT', 'de-CH', 'iw', 'hi', 'pt', 'pt-PT', 'es-419', 'mr', 'lo', 'kn', 'si', 'gu', 'bn', 'zu', 'ur', 'te', 'sw', 'am' );
4183 4344 }
4184 4345
4185 4346 $locales = array_diff_key( $locales, array_flip( $unset ) );
@@ -4465,11 +4626,9 @@
4465 4626 *
4466 4627 * @return array
4467 4628 */
4468 4629 public static function maybe_filter_array( $values, $keys ) {
4469 - $allow_unfiltered_html = self::allow_unfiltered_html();
4470 -
4471 - if ( $allow_unfiltered_html ) {
4630 + if ( self::allow_unfiltered_html() ) {
4472 4631 return $values;
4473 4632 }
4474 4633
4475 4634 foreach ( $keys as $key ) {
@@ -4493,9 +4652,9 @@
4493 4652 * @return string
4494 4653 */
4495 4654 public static function maybe_kses( $value, $allowed = 'all' ) {
4496 4655 if ( self::should_never_allow_unfiltered_html() ) {
4497 - $value = self::kses( $value, $allowed );
4656 + return self::kses( $value, $allowed );
4498 4657 }
4499 4658 return $value;
4500 4659 }
4501 4660
@@ -4582,14 +4741,9 @@
4582 4741 * @return bool
4583 4742 */
4584 4743 public static function show_new_feature( $feature ) {
4585 4744 $link = FrmAddonsController::install_link( $feature );
4586 -
4587 - if ( array_key_exists( 'status', $link ) || array_key_exists( 'class', $link ) ) {
4588 - return true;
4589 - }
4590 -
4591 - return 'coupons' === $feature && class_exists( 'FrmCouponsAppController' );
4745 + return array_key_exists( 'status', $link ) || array_key_exists( 'class', $link );
4592 4746 }
4593 4747
4594 4748 /**
4595 4749 * Enhances upgrade data parameters with installation link and plan requirement information.
@@ -4664,9 +4818,9 @@
4664 4818 * Shows pill text.
4665 4819 *
4666 4820 * @since 5.2.02
4667 4821 *
4668 - * @param string $text Text in the pill. Default is NEW.
4822 + * @param string|null $text Text in the pill. Default is NEW.
4669 4823 */
4670 4824 public static function show_pill_text( $text = null ) {
4671 4825 if ( null === $text ) {
4672 4826 $text = __( 'NEW', 'formidable' );
@@ -4856,9 +5010,9 @@
4856 5010 self::permission_check( 'frm_change_settings' );
4857 5011 check_ajax_referer( 'frm_ajax', 'nonce' );
4858 5012
4859 5013 if ( $option ) {
4860 - update_option( $option, true, 'no' );
5014 + update_option( $option, true, false );
4861 5015 }
4862 5016
4863 5017 wp_send_json_success();
4864 5018 }
@@ -4905,13 +5059,15 @@
4905 5059 $atts['class'] .= ' frm_help';
4906 5060 } else {
4907 5061 $atts['class'] = 'frm_help';
4908 5062 }
5063 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
4909 5064 ?>
4910 5065 <span <?php self::array_to_html_params( $atts, true ); ?>>
4911 5066 <?php self::icon_by_class( 'frmfont frm_tooltip_icon' ); ?>
4912 5067 </span>
4913 5068 <?php
5069 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
4914 5070 }
4915 5071
4916 5072 /**
4917 5073 * Prints errors for settings in onboarding wizard or template settings.
@@ -4932,8 +5088,10 @@
4932 5088 )
4933 5089 );
4934 5090
4935 5091 $args['class'] .= ' frm-validation-error frm-mt-xs frm_hidden';
5092 +
5093 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
4936 5094 ?>
4937 5095 <span id="<?php echo esc_attr( $args['id'] ); ?>" class="<?php echo esc_attr( $args['class'] ); ?>">
4938 5096 <?php
4939 5097 if ( is_array( $args['errors'] ) ) {
@@ -4947,8 +5105,9 @@
4947 5105 }
4948 5106 ?>
4949 5107 </span>
4950 5108 <?php
5109 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
4951 5110 }
4952 5111
4953 5112 /**
4954 5113 * Check if GDPR is enabled.
@@ -4978,13 +5137,18 @@
4978 5137 * Check if a string is valid UTF-8.
4979 5138 *
4980 5139 * @since 6.24
4981 5140 *
4982 - * @param string $string The string to check.
5141 + * @param string|null $string The string to check.
4983 5142 *
4984 5143 * @return bool
4985 5144 */
4986 5145 public static function is_valid_utf8( $string ) {
5146 + if ( is_null( $string ) ) {
5147 + // Return true so we do not attempt to change encoding on a null value.
5148 + return true;
5149 + }
5150 +
4987 5151 // wp_is_valid_utf8 is added in WP 6.9.
4988 5152 if ( function_exists( 'wp_is_valid_utf8' ) ) {
4989 5153 return wp_is_valid_utf8( $string );
4990 5154 }