diff --git a/composer.json b/composer.json index 99042e139..d7c1b1b24 100644 --- a/composer.json +++ b/composer.json @@ -62,6 +62,7 @@ "tubalmartin/cssmin": "^4.1", "wptt/webfont-loader": "^1.1", "sabberworm/php-css-parser": "^8.4", - "stripe/stripe-php": "^13.1" + "stripe/stripe-php": "^13.1", + "enshrined/svg-sanitize": "^0.18.0" } } diff --git a/composer.lock b/composer.lock index f9a063cb5..df9be9301 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "661c5e02d7fa160de952624932f3b78d", + "content-hash": "bb0253e169852781202f8d6ca51ba70b", "packages": [ { "name": "codeinwp/themeisle-sdk", @@ -46,6 +46,51 @@ }, "time": "2024-02-01T14:10:46+00:00" }, + { + "name": "enshrined/svg-sanitize", + "version": "0.18.0", + "source": { + "type": "git", + "url": "https://github.com/darylldoyle/svg-sanitizer.git", + "reference": "6a2c069dab3843ca4d887ff09c972fc7033888d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/6a2c069dab3843ca4d887ff09c972fc7033888d0", + "reference": "6a2c069dab3843ca4d887ff09c972fc7033888d0", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^5.6 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.5 || ^8.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "enshrined\\svgSanitize\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Daryll Doyle", + "email": "daryll@enshrined.co.uk" + } + ], + "description": "An SVG sanitizer for PHP", + "support": { + "issues": "https://github.com/darylldoyle/svg-sanitizer/issues", + "source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.18.0" + }, + "time": "2024-02-22T17:51:05+00:00" + }, { "name": "masterminds/html5", "version": "2.8.1", diff --git a/inc/integrations/class-form-utils.php b/inc/integrations/class-form-utils.php index 058eb33bc..e6df86cac 100644 --- a/inc/integrations/class-form-utils.php +++ b/inc/integrations/class-form-utils.php @@ -7,6 +7,8 @@ namespace ThemeIsle\GutenbergBlocks\Integration; +use enshrined\svgSanitize\Sanitizer; + /** * Form Utils * @@ -21,7 +23,6 @@ class Form_Utils { * @since 2.0.3 */ public static function generate_test_email() { - $words = array( 'alfa', 'bravo', @@ -50,7 +51,7 @@ public static function generate_test_email() { $name_1 = $words[ wp_rand( 0, count( $words ) - 1 ) ]; $name_2 = $words[ wp_rand( 2, count( $words ) ) - 1 ]; - return "Otter-Form-successfully-connected.delete-on-confirmation.$name_1.$name_2@otter-blocks.com"; + return "Otter-Form-successfully-connected.delete-on-confirmation.$name_1.$name_2@themeisle.com"; } /** @@ -92,7 +93,6 @@ public static function save_file_from_field( $field, $files ) { 'error' => null, ); - $file_name = self::generate_file_name( $field['metadata']['name'] ); $file_data_key = $field['metadata']['data']; @@ -103,6 +103,22 @@ public static function save_file_from_field( $field, $files ) { try { $file_data = $files[ $file_data_key ]; + if ( 'svg' === pathinfo( $file_name, PATHINFO_EXTENSION ) ) { + $file_contents = file_get_contents( $file_data['tmp_name'] ); + + $sanitizer = new Sanitizer(); + $file_contents = $sanitizer->sanitize( $file_contents ); + + global $wp_filesystem; + + if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base' ) ) { + $creds = request_filesystem_credentials( site_url() ); + wp_filesystem( $creds ); + } + + $wp_filesystem->put_contents( $file_data['tmp_name'], $file_contents ); + } + // Save file to uploads folder. require_once ABSPATH . 'wp-admin/includes/file.php'; diff --git a/inc/plugins/class-limited-offers.php b/inc/plugins/class-limited-offers.php index c402743c5..527e27f19 100644 --- a/inc/plugins/class-limited-offers.php +++ b/inc/plugins/class-limited-offers.php @@ -31,31 +31,45 @@ class LimitedOffers { public $wp_option_dismiss_notification_key_base = 'dismiss_themeisle_notice_event_'; /** - * Offer Links + * Metadata for announcements. * - * @var array + * @var array */ - public $offer_metadata = array(); + public $assets = array(); /** * Timeline for the offers. * - * @var array[] + * @var array */ - public $timelines = array( - 'bf' => array( - 'start' => '2023-11-20 00:00:00', - 'end' => '2023-11-27 23:59:00', - ), - ); + public $announcements = array(); /** * LimitedOffers constructor. */ public function __construct() { + $this->announcements = apply_filters( 'themeisle_sdk_announcements', array() ); + + if ( empty( $this->announcements ) || ! is_array( $this->announcements ) ) { + return; + } + try { - if ( $this->is_deal_active( 'bf' ) ) { - $this->activate_bff(); + foreach ( $this->announcements as $announcement => $event_data ) { + if ( false !== strpos( $announcement, 'black_friday' ) ) { + if ( + empty( $event_data ) || + ! is_array( $event_data ) || + empty( $event_data['active'] ) || + empty( $event_data['otter_dashboard_url'] ) || + ! isset( $event_data['urgency_text'] ) + ) { + continue; + } + + $this->active = $announcement; + $this->prepare_black_friday_assets( $event_data ); + } } } catch ( Exception $e ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { @@ -70,6 +84,11 @@ public function __construct() { * @return void */ public function load_dashboard_hooks() { + + if ( empty( $this->assets['globalNoticeUrl'] ) ) { + return; + } + add_filter( 'themeisle_products_deal_priority', array( $this, 'add_priority' ) ); add_action( 'admin_notices', array( $this, 'render_notice' ) ); add_action( 'wp_ajax_dismiss_themeisle_event_notice_otter', array( $this, 'disable_notification_ajax' ) ); @@ -87,16 +106,19 @@ public function is_active() { /** * Activate the Black Friday deal. * + * @param array $data Event data. + * * @return void */ - public function activate_bff() { - $this->active = 'bf'; - - $this->offer_metadata = array( - 'bannerUrl' => OTTER_BLOCKS_URL . 'assets/images/black-friday-banner.png', - 'bannerAlt' => 'Otter Black Friday Sale', - 'linkDashboard' => tsdk_utmify( 'https://themeisle.com/plugins/otter-blocks/blackfriday/', 'blackfridayltd23', 'dashboard' ), - 'linkGlobal' => tsdk_utmify( 'https://themeisle.com/plugins/otter-blocks/blackfriday/', 'blackfridayltd23', 'globalnotice' ), + public function prepare_black_friday_assets( $data ) { + $this->assets = array_merge( + $this->assets, + array( + 'bannerUrl' => OTTER_BLOCKS_URL . 'assets/images/black-friday-banner.png', + 'bannerAlt' => 'Otter Black Friday Sale', + 'bannerStoreUrl' => esc_url_raw( $data['otter_dashboard_url'] ), + 'urgencyText' => esc_html( $data['urgency_text'] ), + ) ); } @@ -109,77 +131,6 @@ public function get_active_deal() { return $this->active; } - /** - * Check if the deal is active with the given slug. - * - * @param string $slug Slug of the deal. - * - * @throws Exception When date is invalid. - */ - public function is_deal_active( $slug ) { - - if ( empty( $slug ) || ! array_key_exists( $slug, $this->timelines ) ) { - return false; - } - - return $this->check_date_range( $this->timelines[ $slug ]['start'], $this->timelines[ $slug ]['end'] ); - } - - /** - * Get the remaining time for the deal in a human readable format. - * - * @param string $slug Slug of the deal. - * @return string Remaining time for the deal. - */ - public function get_remaining_time_for_deal( $slug ) { - if ( empty( $slug ) || ! array_key_exists( $slug, $this->timelines ) ) { - return ''; - } - - try { - $end_date = new DateTime( $this->timelines[ $slug ]['end'], new DateTimeZone( 'GMT' ) ); - $current_date = new DateTime( 'now', new DateTimeZone( 'GMT' ) ); - $diff = $end_date->diff( $current_date ); - - if ( 0 < $diff->days ) { - return 1 === $diff->days ? $diff->format( '%a day' ) : $diff->format( '%a days' ); - } - - if ( 0 < $diff->h ) { - return 1 === $diff->h ? $diff->format( '%h hour' ) : $diff->format( '%h hours' ); - } - - if ( 0 < $diff->i ) { - return 1 === $diff->i ? $diff->format( '%i minute' ) : $diff->format( '%i minutes' ); - } - - return 1 === $diff->s ? $diff->format( '%s second' ) : $diff->format( '%s seconds' ); - } catch ( Exception $e ) { - if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { - error_log( $e->getMessage() ); // phpcs:ignore - } - } - - return ''; - } - - /** - * Check if the current date is in the range of the offer. - * - * @param string $start Start date. - * @param string $end End date. - * - * @throws Exception When date is invalid. - */ - public function check_date_range( $start, $end ) { - - $start_date = new DateTime( $start, new DateTimeZone( 'GMT' ) ); - $end_date = new DateTime( $end, new DateTimeZone( 'GMT' ) ); - $current_date = new DateTime( 'now', new DateTimeZone( 'GMT' ) ); - - return $start_date <= $current_date && $current_date <= $end_date; - } - /** * Get the localized data for the plugin. * @@ -188,12 +139,10 @@ public function check_date_range( $start, $end ) { public function get_localized_data() { return array_merge( array( - 'active' => $this->is_active(), - 'dealSlug' => $this->get_active_deal(), - 'remainingTime' => $this->get_remaining_time_for_deal( $this->get_active_deal() ), - 'urgencyText' => 'Hurry Up! Only ' . $this->get_remaining_time_for_deal( $this->get_active_deal() ) . ' left', + 'active' => $this->is_active(), + 'dealSlug' => $this->get_active_deal(), ), - $this->offer_metadata + $this->assets ); } @@ -262,7 +211,7 @@ public function render_notice() { - + diff --git a/inc/render/class-form-multiple-choice.php b/inc/render/class-form-multiple-choice.php index 22663b1df..ca3cc7cd2 100644 --- a/inc/render/class-form-multiple-choice.php +++ b/inc/render/class-form-multiple-choice.php @@ -22,19 +22,23 @@ class Form_Multiple_Choice_Block { * @return mixed|string */ public function render( $attributes ) { - - $class_names = 'wp-block-themeisle-blocks-form-multiple-choice ' . ( isset( $attributes['className'] ) ? $attributes['className'] : '' ); - $id = isset( $attributes['id'] ) ? $attributes['id'] : ''; - $options = isset( $attributes['options'] ) ? $attributes['options'] : array(); - $field_type = isset( $attributes['type'] ) ? $attributes['type'] : 'checkbox'; - $label = isset( $attributes['label'] ) ? $attributes['label'] : __( 'Select option', 'otter-blocks' ); - $help_text = isset( $attributes['helpText'] ) ? $attributes['helpText'] : ''; + $id = isset( $attributes['id'] ) ? esc_attr( $attributes['id'] ) : ''; + $options = isset( $attributes['options'] ) ? $attributes['options'] : array(); + $field_type = isset( $attributes['type'] ) ? esc_attr( $attributes['type'] ) : 'checkbox'; + $label = isset( $attributes['label'] ) ? esc_html( $attributes['label'] ) : __( 'Select option', 'otter-blocks' ); + $help_text = isset( $attributes['helpText'] ) ? esc_html( $attributes['helpText'] ) : ''; $is_required = isset( $attributes['isRequired'] ) ? boolval( $attributes['isRequired'] ) : false; $has_multiple_selection = isset( $attributes['multipleSelection'] ) ? boolval( $attributes['multipleSelection'] ) : false; - $mapped_name = isset( $attributes['mappedName'] ) ? $attributes['mappedName'] : $id; + $mapped_name = isset( $attributes['mappedName'] ) ? esc_attr( $attributes['mappedName'] ) : $id; + + $wrapper_attributes = get_block_wrapper_attributes( + array( + 'id' => $id, + ) + ); - $output = '
'; + $output = '
'; // Compatibility with the old version of the block. if ( ! empty( $options ) && is_string( $options ) ) { @@ -53,8 +57,8 @@ public function render( $attributes ) { continue; } - $field_value = implode( '_', explode( ' ', sanitize_title( $choice['content'] ) ) ); - $field_id = 'field-' . $field_value; + $field_value = implode( '_', explode( ' ', esc_attr( $choice['content'] ) ) ); + $field_id = 'field-' . esc_attr( $field_value ); $checked = isset( $choice['isDefault'] ) && $choice['isDefault']; $output .= $this->render_field( $field_type, $choice['content'], $field_value, $mapped_name, $field_id, $checked, $is_required ); @@ -84,8 +88,8 @@ public function render( $attributes ) { public function render_field( $type, $label, $value, $name, $id, $checked = false, $is_required = false ) { $output = '
'; - $output .= ''; - $output .= ''; + $output .= ''; + $output .= ''; $output .= '
'; @@ -104,8 +108,8 @@ public function render_field( $type, $label, $value, $name, $id, $checked = fals * @return string */ public function render_select_field( $label, $options_array, $id, $name, $is_multiple, $is_required ) { - $output = ''; - $output .= ''; foreach ( $options_array as $option ) { @@ -116,7 +120,7 @@ public function render_select_field( $label, $options_array, $id, $name, $is_mul $is_selected = isset( $option['isDefault'] ) && $option['isDefault']; $field_value = implode( '_', explode( ' ', sanitize_title( $option['content'] ) ) ); - $output .= ''; + $output .= ''; } $output .= ''; diff --git a/plugins/otter-pro/inc/render/class-form-file-block.php b/plugins/otter-pro/inc/render/class-form-file-block.php index 446a5ffb5..1538c0478 100644 --- a/plugins/otter-pro/inc/render/class-form-file-block.php +++ b/plugins/otter-pro/inc/render/class-form-file-block.php @@ -29,16 +29,21 @@ public function render( $attributes ) { return ''; } - $class_names = 'wp-block-themeisle-blocks-form-file ' . ( isset( $attributes['className'] ) ? $attributes['className'] : '' ); - $id = isset( $attributes['id'] ) ? $attributes['id'] : ''; - $label = isset( $attributes['label'] ) ? $attributes['label'] : __( 'Select option', 'otter-blocks' ); - $help_text = isset( $attributes['helpText'] ) ? $attributes['helpText'] : ''; + $id = isset( $attributes['id'] ) ? esc_attr( $attributes['id'] ) : ''; + $label = isset( $attributes['label'] ) ? esc_html( $attributes['label'] ) : __( 'Select option', 'otter-blocks' ); + $help_text = isset( $attributes['helpText'] ) ? esc_html( $attributes['helpText'] ) : ''; $is_required = isset( $attributes['isRequired'] ) && boolval( $attributes['isRequired'] ); $has_multiple_files = isset( $attributes['multipleFiles'] ) && boolval( $attributes['multipleFiles'] ) && ( ! isset( $attributes['maxFilesNumber'] ) || intval( $attributes['maxFilesNumber'] ) > 1 ); $allowed_files = isset( $attributes['allowedFileTypes'] ) ? implode( ',', $attributes['allowedFileTypes'] ) : ''; - $output = '
'; - $mapped_name = isset( $attributes['mappedName'] ) ? $attributes['mappedName'] : 'field-' . $id; + $wrapper_attributes = get_block_wrapper_attributes( + array( + 'id' => $id, + ) + ); + + $output = '
'; + $mapped_name = isset( $attributes['mappedName'] ) ? esc_attr( $attributes['mappedName'] ) : 'field-' . $id; $output .= ''; @@ -46,10 +51,10 @@ public function render( $attributes ) { . $mapped_name . '" ' . ( $is_required ? 'required ' : '' ) . ' ' . ( $has_multiple_files ? 'multiple ' : '' ) - . ( isset( $attributes['allowedFileTypes'] ) ? ( ' accept="' . $allowed_files ) . '"' : '' ) - . ( isset( $attributes['maxFileSize'] ) ? ( ' data-max-file-size="' . $attributes['maxFileSize'] . '"' ) : '' ) - . ( isset( $attributes['fieldOptionName'] ) ? ( ' data-field-option-name="' . $attributes['fieldOptionName'] . '"' ) : '' ) - . ( ( isset( $attributes['multipleFiles'] ) && isset( $attributes['maxFilesNumber'] ) ) ? ( ' data-max-files-number="' . $attributes['maxFilesNumber'] . '"' ) : '' ) + . ( isset( $attributes['allowedFileTypes'] ) ? ( ' accept="' . esc_attr( $allowed_files ) ) . '"' : '' ) + . ( isset( $attributes['maxFileSize'] ) ? ( ' data-max-file-size="' . esc_attr( $attributes['maxFileSize'] ) . '"' ) : '' ) + . ( isset( $attributes['fieldOptionName'] ) ? ( ' data-field-option-name="' . esc_attr( $attributes['fieldOptionName'] ) . '"' ) : '' ) + . ( ( isset( $attributes['multipleFiles'] ) && isset( $attributes['maxFilesNumber'] ) ) ? ( ' data-max-files-number="' . esc_attr( $attributes['maxFilesNumber'] ) . '"' ) : '' ) . ' />'; $output .= '' diff --git a/plugins/otter-pro/inc/render/class-form-hidden-block.php b/plugins/otter-pro/inc/render/class-form-hidden-block.php index 4c6e2e7a2..e015c2645 100644 --- a/plugins/otter-pro/inc/render/class-form-hidden-block.php +++ b/plugins/otter-pro/inc/render/class-form-hidden-block.php @@ -29,15 +29,19 @@ public function render( $attributes ) { return ''; } - $class_names = 'wp-block-themeisle-blocks-form-hidden-field ' . ( isset( $attributes['className'] ) ? $attributes['className'] : '' ); - $id = isset( $attributes['id'] ) ? $attributes['id'] : ''; - $label = isset( $attributes['label'] ) ? $attributes['label'] : __( 'Hidden Field', 'otter-blocks' ); - $param_name = isset( $attributes['paramName'] ) ? $attributes['paramName'] : ''; - $mapped_name = isset( $attributes['mappedName'] ) ? $attributes['mappedName'] : 'field-' . $id; - $default_value = isset( $attributes['defaultValue'] ) ? $attributes['defaultValue'] : ''; + $id = isset( $attributes['id'] ) ? esc_attr( $attributes['id'] ) : ''; + $label = isset( $attributes['label'] ) ? esc_html( $attributes['label'] ) : __( 'Hidden Field', 'otter-blocks' ); + $param_name = isset( $attributes['paramName'] ) ? esc_attr( $attributes['paramName'] ) : ''; + $mapped_name = isset( $attributes['mappedName'] ) ? esc_attr( $attributes['mappedName'] ) : 'field-' . $id; + $default_value = isset( $attributes['defaultValue'] ) ? esc_attr( $attributes['defaultValue'] ) : ''; + $wrapper_attributes = get_block_wrapper_attributes( + array( + 'id' => $id, + ) + ); - $output = '