PluginProbe
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts / trunk
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts vtrunk
2.8.14 2.8.13 2.8.12 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.10 2.8.11 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 trunk 1.0.1 1.0.2 1.1.0 1.1.1 All 147 releases
← All changes | includes/custom-style.php +376 -38 2.8.5trunk View file →
@@ -131,10 +131,19 @@
131 131
132 132 // Render settings for carousels.
133 133 add_filter( 'render_block', [ $this, 'render_carousel_settings' ], 30, 3 );
134 134
135 - // Migrate CBB class for old blocks.
136 - add_filter( 'render_block', [ $this, 'migrate_cbb_class' ], 30, 3 );
135 + // Handle migration css class and custom css.
136 + add_filter( 'render_block', [ $this, 'manage_cbb_class' ], 30, 3 );
137 +
138 + // Render steps block data.
139 + add_filter( 'render_block_data', [ $this, 'render_steps_block_data' ], 10, 3 );
140 +
141 + // Render steps blocks.
142 + add_filter( 'render_block', [ $this, 'render_steps_attributes' ], 10, 2 );
143 +
144 + // Sanitize post data before saving to database.
145 + add_filter( 'wp_insert_post_data', [ $this, 'sanitize_post_data' ] );
137 146 }
138 147
139 148 /**
140 149 * Register custom setting fields
@@ -1100,8 +1109,9 @@
1100 1109
1101 1110 /**
1102 1111 * Build transform item: translate, rotate, scale, skew
1103 1112 *
1113 + * @param array $transform_item
1104 1114 * @return string
1105 1115 */
1106 1116 private function build_transform_item( $transform_item ) {
1107 1117 $style = '';
@@ -1392,8 +1402,9 @@
1392 1402 /**
1393 1403 * Build style for accordion border
1394 1404 *
1395 1405 * @param array $args
1406 + * @param array $style_array
1396 1407 * @return string
1397 1408 */
1398 1409 private function build_accordion_border_style( $args, &$style_array ) {
1399 1410 $value = $args['value'] ?? null;
@@ -1400,15 +1411,30 @@
1400 1411 if ( $value && is_array( $value ) ) {
1401 1412 if ( ( $value['top'] ?? false ) || ( $value['right'] ?? false ) || ( $value['bottom'] ?? false ) || ( $value['left'] ?? false ) ) {
1402 1413 $border_styles = $this->build_border_style( $args );
1403 1414 } else {
1404 - $border_styles = $this->build_border_style( [ 'value' => ['top' => $value, 'right' => $value, 'bottom' => $value, 'left' => $value] ] );
1415 + $border_styles = $this->build_border_style(
1416 + [
1417 + 'value' => [
1418 + 'top' => $value,
1419 + 'right' => $value,
1420 + 'bottom' => $value,
1421 + 'left' => $value,
1422 + ],
1423 + ]
1424 + );
1405 1425 }
1406 1426
1407 1427 if ( $border_styles ) {
1408 - $border_style = implode( ';', array_map( function ($attr_key) use ($border_styles) {
1409 - return "{$attr_key}:{$border_styles[ $attr_key ]}";
1410 - }, array_keys($border_styles) ) );
1428 + $border_style = implode(
1429 + ';',
1430 + array_map(
1431 + function ( $attr_key ) use ( $border_styles ) {
1432 + return "{$attr_key}:{$border_styles[ $attr_key ]}";
1433 + },
1434 + array_keys( $border_styles )
1435 + )
1436 + );
1411 1437
1412 1438 $style_array[] = "{$args['selector']}{{$border_style};}";
1413 1439 }
1414 1440 }
@@ -1419,8 +1445,9 @@
1419 1445 /**
1420 1446 * Build style for accordion radius
1421 1447 *
1422 1448 * @param array $args
1449 + * @param array $style_array
1423 1450 * @return string
1424 1451 */
1425 1452 private function build_accordion_radius_style( $args, &$style_array ) {
1426 1453 $value = $args['value'] ?? null;
@@ -1462,9 +1489,9 @@
1462 1489
1463 1490 /**
1464 1491 * Check whether current block has custom style or not.
1465 1492 *
1466 - * @param [type] $block
1493 + * @param array $block
1467 1494 * @return boolean
1468 1495 */
1469 1496 private function get_custom_css_style( $block ) {
1470 1497 // There is no boldblocks value.
@@ -1558,10 +1585,16 @@
1558 1585 return $this->build_thumbnail_background( $background, $block_content, $block, $block_instance );
1559 1586 } elseif ( $media_url ) {
1560 1587 return $this->build_custom_image_background( $background, $block_content, $block, $block_instance );
1561 1588 }
1562 - } elseif ( 'video' === $media_type && $media_url ) {
1563 - return $this->build_video_background( $background, $block_content, $block, $block_instance );
1589 + } elseif ( 'video' === $media_type ) {
1590 + if ( $media_url ) {
1591 + return $this->build_video_background( $background, $block_content, $block, $block_instance );
1592 + }
1593 + } elseif ( 'embed' === $media_type ) {
1594 + if ( $media_url ) {
1595 + return $this->build_embed_background( $background, $block_content, $block, $block_instance );
1596 + }
1564 1597 }
1565 1598
1566 1599 return $block_content;
1567 1600 }
@@ -1954,9 +1987,9 @@
1954 1987 if ( ! $name || ! $duration ) {
1955 1988 return $accumulator;
1956 1989 }
1957 1990
1958 - $delay = $animation['delay'] ?? 0;
1991 + $delay = empty( $animation['delay'] ) ? 0 : $animation['delay'];
1959 1992 $repeat = $animation['repeat'] ?? 1;
1960 1993 $infinite = $animation['infinite'] ?? false;
1961 1994 $timing_function = $animation['timingFunction'] ?? '';
1962 1995 $animation_name = isset( $animation_names[ $name ]['name'] ) ? $animation_names[ $name ]['name'] : $name;
@@ -2138,9 +2171,10 @@
2138 2171 $block_background = sprintf( '<div class="bb:block-background bb:block-background--video"><video src="%1$s" autoplay="autoplay" muted loop playsinline preload="auto" style="%2$s"/></div>', $media['url'], $video_style );
2139 2172
2140 2173 if ( $media['isPausable'] ?? false ) {
2141 2174 $block_class .= ' cbb-has-video-controls';
2142 - $block_background .= '<button class="cbb-play-pause cbb-video-play-pause is-playing"></button>';
2175 + $position_class = ( $media['playPausePosition'] ?? '' ) ? ' is-' . esc_attr( $media['playPausePosition'] ) : '';
2176 + $block_background .= '<button class="cbb-play-pause cbb-video-play-pause is-playing' . $position_class . '"></button>';
2143 2177 }
2144 2178
2145 2179 if ( strpos( $block['innerHTML'] ?? '', $block_class ) === false ) {
2146 2180 $block_content = $this->add_class_to_block( $block_content, $block_class );
@@ -2151,8 +2185,123 @@
2151 2185 return $block_content;
2152 2186 }
2153 2187
2154 2188 /**
2189 + * Build the embed background
2190 + *
2191 + * @param array $background
2192 + * @param string $block_content
2193 + * @param array $block
2194 + * @param WP_Block $block_instance
2195 + * @return string
2196 + */
2197 + private function build_embed_background( $background, $block_content, $block, $block_instance ) {
2198 + $media = $background['embed'] ?? [];
2199 +
2200 + // Invalid data.
2201 + if ( ! is_string( $media['url'] ) ) {
2202 + return $block_content;
2203 + }
2204 +
2205 + $block_background = '';
2206 +
2207 + // Use WordPress's native oEmbed processing (includes caching).
2208 + $oembed_html = wp_oembed_get( $media['url'] );
2209 +
2210 + if ( $oembed_html ) {
2211 + // Extract iframe src from the oEmbed HTML.
2212 + preg_match( '/src=["\']([^"\']+)["\']/', $oembed_html, $src_matches );
2213 + if ( ! empty( $src_matches[1] ) ) {
2214 + $iframe_src = $src_matches[1];
2215 +
2216 + // Detect provider from iframe src URL.
2217 + $lower_src = strtolower( $iframe_src );
2218 + $provider = null;
2219 +
2220 + if ( str_contains( $lower_src, 'youtube.com' ) || str_contains( $lower_src, 'youtu.be' ) ) {
2221 + $provider = 'youtube';
2222 + } elseif ( str_contains( $lower_src, 'vimeo.com' ) ) {
2223 + $provider = 'vimeo';
2224 + } elseif ( str_contains( $lower_src, 'videopress.com' ) ) {
2225 + $provider = 'videopress';
2226 + } elseif ( str_contains( $lower_src, 'wordpress.tv' ) ) {
2227 + $provider = 'wordpress-tv';
2228 + }
2229 +
2230 + // Modify iframe src to add background video parameters based on provider.
2231 + $parsed_url = wp_parse_url( $iframe_src );
2232 + if ( $parsed_url && isset( $parsed_url['host'] ) ) {
2233 + // Parse existing query parameters.
2234 + $query_params = array();
2235 + if ( isset( $parsed_url['query'] ) ) {
2236 + parse_str( $parsed_url['query'], $query_params );
2237 + }
2238 +
2239 + // Add background video parameters based on provider.
2240 + if ( 'youtube' === $provider ) {
2241 + $query_params['autoplay'] = '1';
2242 + $query_params['mute'] = '1';
2243 + $query_params['loop'] = '1';
2244 + $query_params['controls'] = '0';
2245 + $query_params['playsinline'] = '1';
2246 + $query_params['rel'] = '1';
2247 +
2248 + // For loop to work, we need the playlist parameter.
2249 + $path = $parsed_url['path'] ?? '';
2250 + $path_segments = explode( '/', $path );
2251 + $video_id = end( $path_segments );
2252 + if ( $video_id ) {
2253 + $query_params['playlist'] = $video_id;
2254 + }
2255 + } elseif ( 'vimeo' === $provider ) {
2256 + $query_params['autoplay'] = '1';
2257 + $query_params['muted'] = '1';
2258 + $query_params['loop'] = '1';
2259 + $query_params['background'] = '1';
2260 + $query_params['controls'] = '0';
2261 + $query_params['transparent'] = '0';
2262 + } else {
2263 + $query_params['autoplay'] = '1';
2264 + $query_params['loop'] = '1';
2265 + $query_params['muted'] = '1';
2266 + }
2267 +
2268 + // Rebuild the URL with new parameters.
2269 + $iframe_src = $parsed_url['scheme'] . '://' . $parsed_url['host'];
2270 + if ( isset( $parsed_url['path'] ) ) {
2271 + $iframe_src .= $parsed_url['path'];
2272 + }
2273 + if ( ! empty( $query_params ) ) {
2274 + $iframe_src .= '?' . http_build_query( $query_params );
2275 + }
2276 + }
2277 +
2278 + // Build the iframe HTML that will replace the figure.
2279 + $iframe_html = sprintf(
2280 + '<iframe src="%s" title="Background video" frameborder="0" allow="autoplay; fullscreen"></iframe>',
2281 + esc_url( $iframe_src )
2282 + );
2283 +
2284 + $block_background = sprintf( '<div class="bb:block-background bb:block-background--embed">%s</div>', $iframe_html );
2285 + }
2286 + }
2287 +
2288 + if ( ! $block_background ) {
2289 + return $block_content;
2290 + }
2291 +
2292 + $block_class = 'bb:has-background bb:has-background--embed';
2293 + if ( strpos( $block['innerHTML'] ?? '', $block_class ) === false ) {
2294 + $block_content = $this->add_class_to_block( $block_content, $block_class );
2295 + }
2296 +
2297 + $block_content = $this->add_inner_content_to_block( $block_content, $block_background );
2298 +
2299 + return $block_content;
2300 + }
2301 +
2302 +
2303 + /**
2155 2304 * Render block overlay
2156 2305 *
2157 2306 * @param string $block_content
2158 2307 * @param array $block
@@ -2214,9 +2363,9 @@
2214 2363
2215 2364 /**
2216 2365 * Render accordions block data
2217 2366 *
2218 - * @param array $parsed_block
2367 + * @param array $parsed_block
2219 2368 * @return array
2220 2369 */
2221 2370 public function render_accordion_block_data( $parsed_block ) {
2222 2371 $block_type = \WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] );
@@ -2749,9 +2898,9 @@
2749 2898 // Build carousel settings.
2750 2899 $carousel_settings = $post_template->attributes['boldblocks']['carousel'] ?? [];
2751 2900 if ( $carousel_settings ) {
2752 2901 if ( ! $this->block_has_attribute( 'data-carousel-settings', $block_content ) ) {
2753 - $carousel_attr = $this->build_carousel_settings( $carousel_settings, true );
2902 + $carousel_attr = $this->build_carousel_settings( $carousel_settings, true, $post_template->parsed_block, $post_template );
2754 2903
2755 2904 if ( $carousel_attr ) {
2756 2905 $block_content = $this->add_data_to_block( $block_content, 'data-carousel-settings', esc_attr( $carousel_attr ) );
2757 2906 }
@@ -2924,13 +3073,15 @@
2924 3073
2925 3074 /**
2926 3075 * Build a json string of carousel settings
2927 3076 *
2928 - * @param array $carousel_settings
2929 - * @param boolean $exclude_li_role
3077 + * @param array $carousel_settings
3078 + * @param boolean $exclude_li_role
3079 + * @param array $parsed_block
3080 + * @param WP_Block $block_instance
2930 3081 * @return string
2931 3082 */
2932 - private function build_carousel_settings( $carousel_settings, $exclude_li_role = false ) {
3083 + private function build_carousel_settings( $carousel_settings, $exclude_li_role = false, $parsed_block = null, $block_instance = null ) {
2933 3084 // phpcs:disable
2934 3085 $dataset = [];
2935 3086
2936 3087 // Speed.
@@ -3024,8 +3175,14 @@
3024 3175
3025 3176 if ( in_array($effect, $slidesPerViewDependencies, true ) ) {
3026 3177 $dataset['slidesPerView'] = 1;
3027 3178 } else {
3179 + // centeredSlides.
3180 + $dataset['centeredSlides'] = $carousel_settings['centeredSlides'] ?? false;
3181 + if ($dataset['centeredSlides'] && $effect === 'slide' && ($carousel_settings['centeredSlidesSettings']['enable'] ?? '')) {
3182 + $dataset['centeredSlidesSettings'] = $carousel_settings['centeredSlidesSettings'];
3183 + }
3184 +
3028 3185 $slidesPerView = $this->carousel_refine_slides_per_view( $carousel_settings['slidesPerView'] ?? 1 );
3029 3186 $slidesPerGroup = absint( $carousel_settings['slidesPerGroup'] ?? 0 );
3030 3187
3031 3188 if ( $slidesPerView ) {
@@ -3084,9 +3241,9 @@
3084 3241 $dataset['slidesPerView'] = $first_breakpoint['slidesPerView'];
3085 3242 if ($dataset['slidesPerView'] > 1 && ($first_breakpoint['slidesPerGroup'] ?? 0)) {
3086 3243 $dataset['slidesPerGroup'] = $first_breakpoint['slidesPerGroup'];
3087 3244 }
3088 - if ( $first_breakpoint['spaceBetween'] ) {
3245 + if ( ! empty( $first_breakpoint['spaceBetween'] ) ) {
3089 3246 $dataset['spaceBetween'] = $first_breakpoint['spaceBetween'];
3090 3247 }
3091 3248
3092 3249 // Set the breakpoints.
@@ -3110,17 +3267,17 @@
3110 3267 'slidesPerGroup' => $slidesPerGroup ? $slidesPerGroup : 1,
3111 3268 ],
3112 3269 ];
3113 3270 }
3271 +
3272 + // Reset centeredSlides
3273 + if ( ! ( $slidesPerView > 1 || 'auto' === $slidesPerView ) ) {
3274 + unset( $dataset['centeredSlides'] );
3275 + unset( $dataset['centeredSlidesSettings'] );
3276 + }
3114 3277 }
3115 3278 }
3116 3279
3117 - // centeredSlides.
3118 - $dataset['centeredSlides'] = $carousel_settings['centeredSlides'] ?? false;
3119 - if ($dataset['centeredSlides'] && $effect === 'slide' && ($carousel_settings['centeredSlidesSettings']['enable'] ?? '')) {
3120 - $dataset['centeredSlidesSettings'] = $carousel_settings['centeredSlidesSettings'];
3121 - }
3122 -
3123 3280 // Pagination.
3124 3281 $pagination = $carousel_settings['pagination'] ?? [];
3125 3282 $dataset['pagination'] = $pagination['enable'] ?? false;
3126 3283 if ( $dataset['pagination'] ) {
@@ -3130,8 +3287,20 @@
3130 3287 // Navigation.
3131 3288 $navigation = $carousel_settings['navigation'] ?? [];
3132 3289 $dataset['navigation'] = $navigation['enable'] ?? false;
3133 3290 if ( $dataset['navigation'] ) {
3291 + $next_icon = trim($navigation['nextIcon']['value'] ?? '');
3292 + $prev_icon = trim($navigation['prevIcon']['value'] ?? '');
3293 + if ( $next_icon || $prev_icon ) {
3294 + $svg_sanitizer = $this->the_plugin_instance->get_component( IconLibrary::class );
3295 + if ($svg_sanitizer) {
3296 + $next_icon = $next_icon ? $svg_sanitizer->sanitize_svg( $next_icon ) : '';
3297 + $prev_icon = $prev_icon ? $svg_sanitizer->sanitize_svg( $prev_icon ) : '';
3298 +
3299 + $navigation['nextIcon']['value'] = $next_icon;
3300 + $navigation['prevIcon']['value'] = $prev_icon;
3301 + }
3302 + }
3134 3303 $dataset['navigationSettings'] = $navigation;
3135 3304 }
3136 3305
3137 3306 // Scrollbar.
@@ -3158,9 +3327,9 @@
3158 3327 if ( $exclude_li_role ) {
3159 3328 $dataset['a11y'] = ['slideRole' => '', 'slideLabelMessage' => ''];
3160 3329 }
3161 3330
3162 - return wp_json_encode( $dataset );
3331 + return wp_json_encode( apply_filters( 'cbb_get_carousel_settings', $dataset, $parsed_block, $block_instance ) );
3163 3332 // phpcs:enable
3164 3333 }
3165 3334
3166 3335 /**
@@ -3166,9 +3335,9 @@
3166 3335 /**
3167 3336 * Refine the slides per view
3168 3337 *
3169 3338 * @param mixed $raw_value
3170 - * @return void
3339 + * @return string|float 'auto' || number
3171 3340 */
3172 3341 private function carousel_refine_slides_per_view( $raw_value ) {
3173 3342 $value = $raw_value;
3174 3343 if ( is_array( $raw_value ) ) {
@@ -3506,9 +3675,9 @@
3506 3675 // Carousel settings.
3507 3676 $carousel_settings = $block['attrs']['boldblocks']['carousel'] ?? [];
3508 3677
3509 3678 // Dataset attribute.
3510 - $carousel_dataset = $this->build_carousel_settings( $carousel_settings );
3679 + $carousel_dataset = $this->build_carousel_settings( $carousel_settings, false, $block, $block_instance );
3511 3680 if ( $carousel_dataset ) {
3512 3681 $block_content = $this->add_data_to_block( $block_content, 'data-carousel-settings', esc_attr( $carousel_dataset ) );
3513 3682 }
3514 3683
@@ -3544,9 +3713,9 @@
3544 3713 return apply_filters( 'cbb_carousel_preloader', '<div class="cbb-loader"></div>', $block, $block_instance );
3545 3714 }
3546 3715
3547 3716 /**
3548 - * Migrate cbb class for old CBB blocks.
3717 + * Manage css class: migrate old class and adding custom css class.
3549 3718 *
3550 3719 * @param string $block_content
3551 3720 * @param array $block
3552 3721 * @param WP_Block $block_instance
@@ -3551,28 +3720,51 @@
3551 3720 * @param array $block
3552 3721 * @param WP_Block $block_instance
3553 3722 * @return string
3554 3723 */
3555 - public function migrate_cbb_class( $block_content, $block, $block_instance ) {
3724 + public function manage_cbb_class( $block_content, $block, $block_instance ) {
3556 3725 // Ignore admin side or empty content.
3557 3726 if ( is_admin() || ! $block_content ) {
3558 3727 return $block_content;
3559 3728 }
3560 3729
3561 - // There is no need to migrate.
3562 - $migrate_cbb_class = $block_instance->block_type->supports['migrateClass'] ?? '';
3563 - if ( ! $migrate_cbb_class ) {
3730 + // Bail if it is not a CBB block.
3731 + if ( empty( $block_instance->block_type->supports['cbb'] ?? '' ) ) {
3564 3732 return $block_content;
3565 3733 }
3566 3734
3567 - // Replace the class.
3568 - $block_reader = new \WP_HTML_Tag_Processor( $block_content );
3569 - if ( $block_reader->next_tag( [ 'class_name' => $migrate_cbb_class ] ) ) {
3570 - $new_class = str_ends_with( $migrate_cbb_class, 'custom' ) ? 'cbb-block' : 'cbb-block-parent';
3571 - $block_reader->set_attribute( 'class', str_replace( $migrate_cbb_class, $new_class, $block_reader->get_attribute( 'class' ) ) );
3735 + // Get custom classes.
3736 + $css_classes = trim( $block_instance->block_type->supports['CSSClasses'] ?? '' );
3572 3737
3738 + // Get migrate class.
3739 + $migrate_class = trim( $block_instance->block_type->supports['migrateClass'] ?? '' );
3740 +
3741 + // Bail if we don't need to process.
3742 + if ( ! $css_classes && ! $migrate_class ) {
3743 + return $block_content;
3744 + }
3745 +
3746 + // Initialize the HTML Tag Processor.
3747 + $p = new \WP_HTML_Tag_Processor( $block_content );
3748 +
3749 + // Target only the very first outer wrapping element.
3750 + if ( $p->next_tag() ) {
3751 + if ( $migrate_class ) {
3752 + $p->remove_class( $migrate_class );
3753 + $p->add_class( str_ends_with( $migrate_class, 'custom' ) ? 'cbb-block' : 'cbb-block-parent' );
3754 + }
3755 +
3756 + if ( $css_classes ) {
3757 + // Explode by space in case multiple custom classes are supplied.
3758 + foreach ( explode( ' ', $css_classes ) as $single_class ) {
3759 + if ( ! empty( $single_class ) ) {
3760 + $p->add_class( $single_class );
3761 + }
3762 + }
3763 + }
3764 +
3573 3765 // Render the updated HTML.
3574 - return $block_reader->get_updated_html();
3766 + return $p->get_updated_html();
3575 3767 }
3576 3768
3577 3769 return $block_content;
3578 3770 }
@@ -3577,8 +3769,44 @@
3577 3769 return $block_content;
3578 3770 }
3579 3771
3580 3772 /**
3773 + * Render steps block data
3774 + *
3775 + * @param array $parsed_block
3776 + * @param array $block
3777 + * @param WP_Block $parent_block
3778 + * @return array
3779 + */
3780 + public function render_steps_block_data( $parsed_block, $block, $parent_block ) {
3781 + if ( ! $parent_block || empty( $parent_block->attributes['boldblocks']['steps']['enableSteps'] ) ) {
3782 + return $parsed_block;
3783 + }
3784 +
3785 + // Mark it as a child of steps.
3786 + $parsed_block['attrs']['boldblocks']['childStep'] = true;
3787 +
3788 + return $parsed_block;
3789 + }
3790 +
3791 + /**
3792 + * Render aria attributes for steps blocks
3793 + *
3794 + * @param string $block_content
3795 + * @param array $block
3796 + * @return string
3797 + */
3798 + public function render_steps_attributes( $block_content, $block ) {
3799 + if ( ! empty( $block['attrs']['boldblocks']['steps']['enableSteps'] ) ) {
3800 + $block_content = $this->add_data_to_block( $block_content, 'role', 'list' );
3801 + } elseif ( ! empty( $block['attrs']['boldblocks']['childStep'] ) ) {
3802 + $block_content = $this->add_data_to_block( $block_content, 'role', 'listitem' );
3803 + }
3804 +
3805 + return $block_content;
3806 + }
3807 +
3808 + /**
3581 3809 * Support block style for non-cbb blocks
3582 3810 *
3583 3811 * @param string $is_supported
3584 3812 * @param array $block
@@ -3789,8 +4017,9 @@
3789 4017 /**
3790 4018 * Add data attribute to block wrapper
3791 4019 *
3792 4020 * @param string $block_content
4021 + * @param string $name
3793 4022 * @param string $value
3794 4023 * @return string
3795 4024 */
3796 4025 public function add_data_to_block( $block_content, $name, $value ) {
@@ -3822,8 +4051,46 @@
3822 4051 return $block_content;
3823 4052 }
3824 4053
3825 4054 /**
4055 + * Get inner html of a tag
4056 + *
4057 + * @param string $content
4058 + * @param string $tag_name
4059 + * @return string
4060 + */
4061 + public function get_element_inner_html( $content, $tag_name ) {
4062 + $processor = new class( $content ) extends \WP_HTML_Tag_Processor {
4063 + public function get_inner_html( $tag_name ) {
4064 + $this->set_bookmark( 'open' );
4065 + $opener = $this->bookmarks['open'];
4066 +
4067 + while ( $this->next_token() ) {
4068 + if ( $this->get_tag() === $tag_name && $this->is_tag_closer() ) {
4069 +
4070 + $this->set_bookmark( 'close' );
4071 + $closer = $this->bookmarks['close'];
4072 +
4073 + return substr(
4074 + $this->html,
4075 + $opener->start + $opener->length,
4076 + $closer->start - $opener->start - $opener->length
4077 + );
4078 + }
4079 + }
4080 +
4081 + return '';
4082 + }
4083 + };
4084 +
4085 + if ( ! $processor->next_tag( $tag_name ) ) {
4086 + return '';
4087 + }
4088 +
4089 + return $processor->get_inner_html( $tag_name );
4090 + }
4091 +
4092 + /**
3826 4093 * Detect a value is valid
3827 4094 *
3828 4095 * @param mixed $value
3829 4096 * @return boolean
@@ -3894,8 +4161,66 @@
3894 4161 return $value;
3895 4162 }
3896 4163
3897 4164 /**
4165 + * Sanitize post data
4166 + *
4167 + * @param array $data
4168 + * @return array
4169 + */
4170 + public function sanitize_post_data( $data ) {
4171 + if ( empty( $data['post_content'] ) ) {
4172 + return $data;
4173 + }
4174 +
4175 + if ( ! $this->user_can_edit_css() ) {
4176 + $blocks = parse_blocks( wp_unslash( $data['post_content'] ) );
4177 +
4178 + // Strip custom css from blocks.
4179 + $modified = false;
4180 + $blocks = $this->remove_custom_css_from_blocks( $blocks, $modified );
4181 +
4182 + if ( $modified ) {
4183 + $data['post_content'] = wp_slash( serialize_blocks( $blocks ) );
4184 + }
4185 + }
4186 +
4187 + return $data;
4188 + }
4189 +
4190 + /**
4191 + * Remove custom css if any
4192 + *
4193 + * @param array $blocks
4194 + * @param boolean $modified
4195 + * @return array
4196 + */
4197 + private function remove_custom_css_from_blocks( $blocks, &$modified = false ) {
4198 + foreach ( $blocks as &$block ) {
4199 + if ( ! empty( $block['attrs']['boldblocks']['customCSS']['value'] ) ) {
4200 + $block['attrs']['boldblocks']['customCSS']['value'] = '';
4201 + $modified = true;
4202 + }
4203 +
4204 + // Recursive for inner blocks.
4205 + if ( ! empty( $block['innerBlocks'] ) ) {
4206 + $block['innerBlocks'] = $this->remove_custom_css_from_blocks( $block['innerBlocks'], $modified );
4207 + }
4208 + }
4209 +
4210 + return $blocks;
4211 + }
4212 +
4213 + /**
4214 + * If the current user can edit css
4215 + *
4216 + * @return boolean
4217 + */
4218 + public function user_can_edit_css() {
4219 + return apply_filters( 'cbb_user_can_edit_css', current_user_can( 'manage_options' ) );
4220 + }
4221 +
4222 + /**
3898 4223 * Refine custom JS and CSS value
3899 4224 *
3900 4225 * @param string $code
3901 4226 * @param array $tokens
@@ -3903,9 +4228,22 @@
3903 4228 * @return string
3904 4229 */
3905 4230 public function refine_custom_value( $code, $tokens = [], $type = 'CSS' ) {
3906 4231 // Remove tags.
3907 - // $code = preg_replace( '/(<([\w-]+)>)/mi', '', $code );
4232 + if ( 'CSS' === $type ) {
4233 + $unsafe_tags = [ 'script', 'style', 'iframe', 'object', 'embed', 'img', 'video', 'audio', 'math', 'form' ];
4234 +
4235 + // Build a regex pattern that matches:
4236 + // 1. <tag ...> ... </tag> (full tag with content)
4237 + // 2. <tag ... /> or <tag ...> (self-closing or empty).
4238 + $pattern = sprintf(
4239 + '/<(%s)\b[^>]*>(?:.*?<\/\1\s*>)?/is',
4240 + implode( '|', $unsafe_tags )
4241 + );
4242 +
4243 + // Remove all unsafe tags in one go.
4244 + $code = preg_replace( $pattern, '', $code );
4245 + }
3908 4246
3909 4247 // Replace breakpoint tokens.
3910 4248 $breakpoints = $this->get_breakpoints();
3911 4249