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 +201 -57 6.28 → 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.28';
32 + public static $plug_version = '6.35';
33 33
34 34 /**
35 35 * @var bool
36 36 */
@@ -309,10 +309,9 @@
309 309 if ( ! self::pro_is_installed() ) {
310 310 return true;
311 311 }
312 312
313 - $menu_icon = self::get_menu_icon_class();
314 - return str_contains( $menu_icon, 'frm_logo_icon' );
313 + return str_contains( self::get_menu_icon_class(), 'frm_logo_icon' );
315 314 }
316 315
317 316 /**
318 317 * @since 3.05
@@ -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.
@@ -1366,10 +1369,9 @@
1366 1369 if ( isset( $atts['echo'] ) ) {
1367 1370 unset( $atts['echo'] );
1368 1371 }
1369 1372
1370 - $html_atts = self::array_to_html_params( $atts );
1371 - $icon = trim( str_replace( array( 'frm_icon_font', 'frmfont ' ), '', $class ) );
1373 + $icon = trim( str_replace( array( 'frm_icon_font', 'frmfont ' ), '', $class ) );
1372 1374
1373 1375 // Replace icons that have been removed or renamed.
1374 1376 $deprecated = array(
1375 1377 'frm_clone_solid_icon' => 'frm_clone_icon',
@@ -1381,11 +1383,11 @@
1381 1383 $icon = $deprecated[ $icon ];
1382 1384 $class = str_replace( $icon, $deprecated[ $icon ], $class );
1383 1385 }
1384 1386
1385 - if ( $icon === $class ) {
1386 - $icon = '<i class="' . esc_attr( $class ) . '"' . $html_atts . '></i>';
1387 - } else {
1387 + $is_font_icon = $icon === $class;
1388 +
1389 + if ( ! $is_font_icon ) {
1388 1390 $class = str_contains( $icon, ' ' ) ? ' ' . $icon : '';
1389 1391
1390 1392 if ( str_contains( $icon, ' ' ) ) {
1391 1393 $icon = explode( ' ', $icon );
@@ -1390,18 +1392,46 @@
1390 1392 if ( str_contains( $icon, ' ' ) ) {
1391 1393 $icon = explode( ' ', $icon );
1392 1394 $icon = reset( $icon );
1393 1395 }
1396 + }
1394 1397
1395 - $icon = '<svg class="frmsvg' . esc_attr( $class ) . '"' . $html_atts . '><use href="#' . esc_attr( $icon ) . '" /></svg>';
1396 - }
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>';
1397 1406
1398 - if ( $echo ) {
1399 - 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
1400 1412 return null;
1401 1413 }
1402 1414
1403 - 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 );
1404 1434 }
1405 1435
1406 1436 /**
1407 1437 * Run kses for icons. It needs to add a few filters first in order to preserve some custom style values.
@@ -1513,13 +1543,15 @@
1513 1543 * @return string|void
1514 1544 */
1515 1545 public static function array_to_html_params( $atts, $echo = false ) {
1516 1546 $callback = function () use ( $atts ) {
1517 - if ( $atts ) {
1518 - foreach ( $atts as $key => $value ) {
1519 - echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
1520 - }
1547 + if ( ! $atts ) {
1548 + return;
1521 1549 }
1550 +
1551 + foreach ( $atts as $key => $value ) {
1552 + echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
1553 + }
1522 1554 };
1523 1555 return self::clip( $callback, $echo );
1524 1556 }
1525 1557
@@ -1531,8 +1563,10 @@
1531 1563 * @param Closure $echo_function
1532 1564 * @param bool $echo
1533 1565 *
1534 1566 * @return string|null
1567 + *
1568 + * @psalm-return ($echo is true ? null : string)
1535 1569 */
1536 1570 public static function clip( $echo_function, $echo = false ) {
1537 1571 if ( ! $echo ) {
1538 1572 ob_start();
@@ -1624,9 +1658,9 @@
1624 1658
1625 1659 printf(
1626 1660 /* translators: %1$s: Start link HTML, %2$s: CTA text ("upgrading to PRO" by default), %3$s: End link HTML */
1627 1661 esc_html__( 'You\'re using Formidable Forms Lite. To unlock more features consider %1$s%2$s%3$s.', 'formidable' ),
1628 - '<a href="' . esc_url( $upgrade_link ) . '">',
1662 + '<a href="' . esc_url( $upgrade_link ) . '" target="_blank" rel="noopener">',
1629 1663 esc_html( $cta_text ),
1630 1664 '</a>'
1631 1665 );
1632 1666 ?>
@@ -2334,10 +2368,9 @@
2334 2368 if ( ! current_user_can( 'administrator' ) || current_user_can( 'frm_view_forms' ) ) {
2335 2369 return;
2336 2370 }
2337 2371
2338 - $user_id = get_current_user_id();
2339 - $user = new WP_User( $user_id );
2372 + $user = new WP_User( get_current_user_id() );
2340 2373 $frm_roles = self::frm_capabilities();
2341 2374
2342 2375 foreach ( $frm_roles as $frm_role => $frm_role_description ) {
2343 2376 $user->add_cap( $frm_role );
@@ -2354,15 +2387,17 @@
2354 2387 *
2355 2388 * @return void
2356 2389 */
2357 2390 public static function force_capability( $cap = 'frm_change_settings' ) {
2358 - if ( current_user_can( 'administrator' ) && ! current_user_can( $cap ) ) {
2359 - $role = get_role( 'administrator' );
2360 - $frm_roles = self::frm_capabilities();
2391 + if ( ! current_user_can( 'administrator' ) || current_user_can( $cap ) ) {
2392 + return;
2393 + }
2361 2394
2362 - foreach ( $frm_roles as $frm_role => $frm_role_description ) {
2363 - $role->add_cap( $frm_role );
2364 - }
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 );
2365 2400 }
2366 2401 }
2367 2402
2368 2403 /**
@@ -2458,16 +2493,16 @@
2458 2493 if ( is_array( $value ) ) {
2459 2494 $original_function = $function;
2460 2495 $function = count( $value ) ? explode( ', ', FrmDb::prepare_array_values( $value, $function ) ) : array( $function );
2461 2496
2462 - if ( ! self::is_assoc( $value ) ) {
2463 - $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
2464 - } else {
2497 + if ( self::is_assoc( $value ) ) {
2465 2498 foreach ( $value as $k => $v ) {
2466 2499 if ( ! is_array( $v ) ) {
2467 2500 $value[ $k ] = call_user_func( $original_function, $v );
2468 2501 }
2469 2502 }
2503 + } else {
2504 + $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
2470 2505 }
2471 2506 } else {
2472 2507 $value = self::maybe_update_value_if_null( $value, $function );
2473 2508 $value = call_user_func( $function, $value );
@@ -3059,16 +3094,23 @@
3059 3094 return $frm_settings->load_style !== 'none';
3060 3095 }
3061 3096
3062 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 + *
3063 3104 * @param mixed $original_string
3064 3105 * @param int|string $length
3065 3106 * @param int $minword
3066 3107 * @param string $continue
3108 + * @param bool $force_length_limit Force the final string to never exceed the length limit.
3067 3109 *
3068 3110 * @return string
3069 3111 */
3070 - 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 ) {
3071 3113 if ( ! is_string( $original_string ) && ! is_int( $original_string ) ) {
3072 3114 return '';
3073 3115 }
3074 3116
@@ -3082,8 +3124,13 @@
3082 3124 $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) );
3083 3125
3084 3126 if ( $length <= 10 ) {
3085 3127 $sub = self::mb_function( array( 'mb_substr', 'substr' ), array( $str, 0, $length ) );
3128 +
3129 + if ( $force_length_limit ) {
3130 + return $sub;
3131 + }
3132 +
3086 3133 return $sub . ( $length < $original_len ? $continue : '' );
3087 3134 }
3088 3135
3089 3136 $sub = '';
@@ -3111,10 +3158,21 @@
3111 3158
3112 3159 unset( $total_len, $word );
3113 3160 }
3114 3161
3115 - $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 );
3116 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 +
3117 3175 return $sub . ( $len < $original_len ? $continue : '' );
3118 3176 }
3119 3177
3120 3178 /**
@@ -3121,27 +3179,39 @@
3121 3179 * If the string is still too long because there may not have been any spaces, force truncate.
3122 3180 *
3123 3181 * @since 6.5.4
3124 3182 *
3125 - * @param string $sub Current substring.
3126 - * @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.
3127 3186 *
3128 3187 * @return string
3129 3188 */
3130 - private static function maybe_force_truncate_on_string_with_no_spaces( $sub, $length ) {
3131 - if ( strlen( $sub ) < $length + 50 ) {
3132 - // If the string isn't way over the limit, leave it.
3133 - 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 );
3134 3204 }
3135 3205
3136 - $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 ) );
3137 3208
3138 - if ( false !== $first_space ) {
3139 - // Ignore anything with spaces.
3140 - return $sub;
3209 + if ( $final_len > $length ) {
3210 + return self::mb_function( array( 'mb_substr', 'substr' ), array( $sub, 0, $length ) );
3141 3211 }
3142 3212
3143 - return substr( $sub, 0, $length + 10 );
3213 + return $sub;
3144 3214 }
3145 3215
3146 3216 /**
3147 3217 * @param array $function_names
@@ -3811,10 +3881,9 @@
3811 3881 *
3812 3882 * @return void
3813 3883 */
3814 3884 public static function load_admin_wide_js( $load = true ) {
3815 - $version = self::plugin_version();
3816 - 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() );
3817 3886
3818 3887 $global_strings = array(
3819 3888 'updating_msg' => __( 'Please wait while your site updates.', 'formidable' ),
3820 3889 'deauthorize' => __( 'Are you sure you want to deauthorize Formidable Forms on this site?', 'formidable' ),
@@ -3912,8 +3981,10 @@
3912 3981 'text_shortcodes' => __( 'This calculation may have shortcodes that work in text calculations but not numeric calculations.', 'formidable' ),
3913 3982 /* translators: %d is the number of allowed actions per form */
3914 3983 'only_one_action' => sprintf( __( 'This form action is limited to %d per form.', 'formidable' ), 1 ),
3915 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
3916 3987 'unsafe_params' => FrmFormsHelper::reserved_words(),
3917 3988 /* Translators: %s is the name of a Detail Page Slug that is a reserved word.*/
3918 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
3919 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. */
@@ -3942,8 +4013,11 @@
3942 4013 // In older versions this event listener causes the section to immediately close again
3943 4014 // When the h3 element is clicked. It's only required in WP 6.7+.
3944 4015 'requireAccordionTitleClickListener' => version_compare( $wp_version, '6.7', '>=' ),
3945 4016 );
4017 +
4018 + self::add_form_builder_modal_data( $admin_script_strings );
4019 +
3946 4020 /**
3947 4021 * @param array $admin_script_strings
3948 4022 */
3949 4023 $admin_script_strings = apply_filters( 'frm_admin_script_strings', $admin_script_strings );
@@ -3956,8 +4030,80 @@
3956 4030 }//end if
3957 4031 }
3958 4032
3959 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 + /**
3960 4106 * Get the no label text.
3961 4107 *
3962 4108 * @since 6.25.1
3963 4109 *
@@ -4480,11 +4626,9 @@
4480 4626 *
4481 4627 * @return array
4482 4628 */
4483 4629 public static function maybe_filter_array( $values, $keys ) {
4484 - $allow_unfiltered_html = self::allow_unfiltered_html();
4485 -
4486 - if ( $allow_unfiltered_html ) {
4630 + if ( self::allow_unfiltered_html() ) {
4487 4631 return $values;
4488 4632 }
4489 4633
4490 4634 foreach ( $keys as $key ) {
@@ -4597,14 +4741,9 @@
4597 4741 * @return bool
4598 4742 */
4599 4743 public static function show_new_feature( $feature ) {
4600 4744 $link = FrmAddonsController::install_link( $feature );
4601 -
4602 - if ( array_key_exists( 'status', $link ) || array_key_exists( 'class', $link ) ) {
4603 - return true;
4604 - }
4605 -
4606 - return 'coupons' === $feature && class_exists( 'FrmCouponsAppController' );
4745 + return array_key_exists( 'status', $link ) || array_key_exists( 'class', $link );
4607 4746 }
4608 4747
4609 4748 /**
4610 4749 * Enhances upgrade data parameters with installation link and plan requirement information.
@@ -4998,13 +5137,18 @@
4998 5137 * Check if a string is valid UTF-8.
4999 5138 *
5000 5139 * @since 6.24
5001 5140 *
5002 - * @param string $string The string to check.
5141 + * @param string|null $string The string to check.
5003 5142 *
5004 5143 * @return bool
5005 5144 */
5006 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 +
5007 5151 // wp_is_valid_utf8 is added in WP 6.9.
5008 5152 if ( function_exists( 'wp_is_valid_utf8' ) ) {
5009 5153 return wp_is_valid_utf8( $string );
5010 5154 }