| @@ -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 = 105; | |
| 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.32'; | |
| 32 | + public static $plug_version = '6.35'; | |
| 33 | 33 | |
| 34 | 34 | /** |
| 35 | 35 | * @var bool |
| 36 | 36 | */ |
| @@ -937,8 +937,9 @@ | ||
| 937 | 937 | } else { |
| 938 | 938 | self::sanitize_value( self::class . '::strip_most_html', $value ); |
| 939 | 939 | } |
| 940 | 940 | self::decode_specialchars( $value ); |
| 941 | + self::sanitize_value( 'FrmHtmlSanitizer::sanitize_url_attributes', $value ); | |
| 941 | 942 | } |
| 942 | 943 | |
| 943 | 944 | /** |
| 944 | 945 | * Allow only a small set of very basic HTML for unprivileged users. |
| @@ -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,11 +1383,11 @@ | ||
| 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 { | |
| 1387 | + $is_font_icon = $icon === $class; | |
| 1388 | + | |
| 1389 | + if ( ! $is_font_icon ) { | |
| 1390 | 1390 | $class = str_contains( $icon, ' ' ) ? ' ' . $icon : ''; |
| 1391 | 1391 | |
| 1392 | 1392 | if ( str_contains( $icon, ' ' ) ) { |
| 1393 | 1393 | $icon = explode( ' ', $icon ); |
| @@ -1392,18 +1392,46 @@ | ||
| 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. |
| @@ -1630,9 +1658,9 @@ | ||
| 1630 | 1658 | |
| 1631 | 1659 | printf( |
| 1632 | 1660 | /* translators: %1$s: Start link HTML, %2$s: CTA text ("upgrading to PRO" by default), %3$s: End link HTML */ |
| 1633 | 1661 | esc_html__( 'You\'re using Formidable Forms Lite. To unlock more features consider %1$s%2$s%3$s.', 'formidable' ), |
| 1634 | - '<a href="' . esc_url( $upgrade_link ) . '">', | |
| 1662 | + '<a href="' . esc_url( $upgrade_link ) . '" target="_blank" rel="noopener">', | |
| 1635 | 1663 | esc_html( $cta_text ), |
| 1636 | 1664 | '</a>' |
| 1637 | 1665 | ); |
| 1638 | 1666 | ?> |
| @@ -4053,10 +4081,14 @@ | ||
| 4053 | 4081 | if ( $square_connected ) { |
| 4054 | 4082 | $gateway_texts['square'] = esc_html__( 'Square', 'formidable' ); |
| 4055 | 4083 | } |
| 4056 | 4084 | |
| 4085 | + if ( $paypal_connected ) { | |
| 4086 | + $gateway_texts['paypal'] = esc_html__( 'PayPal', 'formidable' ); | |
| 4087 | + } | |
| 4088 | + | |
| 4057 | 4089 | $admin_script_strings['pricingFieldsModal']['msg'] = sprintf( |
| 4058 | - // translators: %s: Stripe or Square. | |
| 4090 | + // translators: %s: Stripe, Square, or PayPal. | |
| 4059 | 4091 | esc_html__( 'You already have %s connected, so these have already been unlocked.', 'formidable' ), |
| 4060 | 4092 | esc_html( implode( ' ' . esc_html__( 'and', 'formidable' ) . ' ', $gateway_texts ) ) |
| 4061 | 4093 | ); |
| 4062 | 4094 | } else { |
| @@ -4063,9 +4095,9 @@ | ||
| 4063 | 4095 | $admin_script_strings['pricingFieldsModal']['closeText'] = __( 'I\'ll do it later!', 'formidable' ); |
| 4064 | 4096 | $admin_script_strings['pricingFieldsModal']['actionText'] = __( 'Setup Payments Now', 'formidable' ); |
| 4065 | 4097 | $admin_script_strings['pricingFieldsModal']['actionUrl'] = $payments_settings_url; |
| 4066 | 4098 | // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong |
| 4067 | - $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, or Square) in your settings.', 'formidable' ); | |
| 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' ); | |
| 4068 | 4100 | }//end if |
| 4069 | 4101 | |
| 4070 | 4102 | delete_option( 'frm_show_pricing_fields_modal' ); |
| 4071 | 4103 | } |