Skip to content

Commit 123450c

Browse files
westonrutervipul0425
andauthored
Merge pull request #2121 from vipul0425/fix/issue-2111
Adds function to process BG image for cover and group block. Co-authored-by: vipul0425 <[email protected]> Co-authored-by: westonruter <[email protected]>
2 parents 3cec065 + ca2a61f commit 123450c

File tree

3 files changed

+289
-6
lines changed

3 files changed

+289
-6
lines changed

plugins/webp-uploads/helper.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -432,10 +432,10 @@ function webp_uploads_should_generate_all_fallback_sizes(): bool {
432432
*
433433
* @since 2.2.0
434434
*
435-
* @param int $attachment_id The ID of the attachment.
436-
* @param string $src The original image src url.
437-
* @param string $mime A mime type we are looking to get image url.
438-
* @return string|null Returns mime type image if available.
435+
* @param int $attachment_id The ID of the attachment.
436+
* @param string $src The original image src url.
437+
* @param string $mime A mime type we are looking to get image url.
438+
* @return non-empty-string|null Returns mime type image if available.
439439
*/
440440
function webp_uploads_get_mime_type_image( int $attachment_id, string $src, string $mime ): ?string {
441441
$metadata = wp_get_attachment_metadata( $attachment_id );
@@ -444,11 +444,12 @@ function webp_uploads_get_mime_type_image( int $attachment_id, string $src, stri
444444
$basename = wp_basename( $metadata['file'] );
445445

446446
if ( $src_basename === $basename ) {
447-
return str_replace(
447+
$result = str_replace(
448448
$basename,
449449
$metadata['sources'][ $mime ]['file'],
450450
$src
451451
);
452+
return '' === $result ? null : $result;
452453
}
453454
}
454455

@@ -471,11 +472,12 @@ function webp_uploads_get_mime_type_image( int $attachment_id, string $src, stri
471472
continue;
472473
}
473474

474-
return str_replace(
475+
$result = str_replace(
475476
$size_data['file'],
476477
$size_data['sources'][ $mime ]['file'],
477478
$src
478479
);
480+
return '' === $result ? null : $result;
479481
}
480482
}
481483

plugins/webp-uploads/hooks.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,13 +747,111 @@ function webp_uploads_render_generator(): void {
747747
}
748748
add_action( 'wp_head', 'webp_uploads_render_generator' );
749749

750+
/**
751+
* Process a block's content to handle background images for specific block types.
752+
*
753+
* This function targets blocks like Cover and Group that may use background images,
754+
* converting them to modern image formats when appropriate.
755+
*
756+
* @since n.e.x.t
757+
*
758+
* @phpstan-param array{
759+
* blockName: string|null,
760+
* attrs: array{
761+
* id?: positive-int,
762+
* url?: string,
763+
* style?: array{
764+
* background?: array{
765+
* backgroundImage?: string
766+
* }
767+
* }
768+
* }
769+
* } $block
770+
*
771+
* @param string|mixed $block_content The block content.
772+
* @param array<string, mixed> $block The block.
773+
* @return string The filtered block content.
774+
*/
775+
function webp_uploads_filter_block_background_images( $block_content, array $block ): string {
776+
// Because plugins can do bad things.
777+
if ( ! is_string( $block_content ) ) {
778+
$block_content = '';
779+
}
780+
781+
// Only run on frontend.
782+
if ( ! webp_uploads_in_frontend_body() || '' === $block_content ) {
783+
return $block_content;
784+
}
785+
786+
$attachment_id = null;
787+
$image_url = null;
788+
789+
if ( 'core/cover' === $block['blockName'] ) {
790+
if ( isset( $block['attrs']['id'], $block['attrs']['url'] ) ) {
791+
$attachment_id = $block['attrs']['id'];
792+
$image_url = $block['attrs']['url'];
793+
}
794+
} elseif ( 'core/group' === $block['blockName'] ) {
795+
if ( isset( $block['attrs']['style']['background']['backgroundImage']['id'], $block['attrs']['style']['background']['backgroundImage']['url'] ) ) {
796+
$attachment_id = $block['attrs']['style']['background']['backgroundImage']['id'];
797+
$image_url = $block['attrs']['style']['background']['backgroundImage']['url'];
798+
}
799+
}
800+
801+
// Abort if there is no associated background image.
802+
if (
803+
! isset( $attachment_id, $image_url ) ||
804+
$attachment_id <= 0 ||
805+
'' === $image_url ||
806+
! is_array( wp_get_attachment_metadata( $attachment_id ) )
807+
) {
808+
return $block_content;
809+
}
810+
811+
$original_mime = get_post_mime_type( $attachment_id );
812+
if ( ! is_string( $original_mime ) ) {
813+
return $block_content;
814+
}
815+
816+
$target_mimes = webp_uploads_get_content_image_mimes( $attachment_id, 'background_image' );
817+
818+
foreach ( $target_mimes as $target_mime ) {
819+
if ( $target_mime === $original_mime ) {
820+
continue;
821+
}
822+
823+
$new_url = webp_uploads_get_mime_type_image( $attachment_id, $image_url, $target_mime );
824+
if ( ! is_string( $new_url ) ) {
825+
continue;
826+
}
827+
828+
$processor = new WP_HTML_Tag_Processor( $block_content );
829+
while ( $processor->next_tag() ) {
830+
$style = $processor->get_attribute( 'style' );
831+
if ( is_string( $style ) && str_contains( $style, 'background-image:' ) && str_contains( $style, $image_url ) ) {
832+
$updated_style = str_replace( $image_url, $new_url, $style );
833+
$processor->set_attribute( 'style', $updated_style );
834+
$block_content = $processor->get_updated_html();
835+
break 2;
836+
}
837+
}
838+
}
839+
840+
return $block_content;
841+
}
842+
750843
/**
751844
* Initializes custom functionality for handling image uploads and content filters.
752845
*
753846
* @since 2.1.0
754847
*/
755848
function webp_uploads_init(): void {
849+
// Filter regular image tags.
756850
add_filter( 'wp_content_img_tag', webp_uploads_is_picture_element_enabled() ? 'webp_uploads_wrap_image_in_picture' : 'webp_uploads_filter_image_tag', 10, 3 );
851+
852+
// Filter blocks that may contain background images.
853+
add_filter( 'render_block_core/cover', 'webp_uploads_filter_block_background_images', 10, 2 );
854+
add_filter( 'render_block_core/group', 'webp_uploads_filter_block_background_images', 10, 2 );
757855
}
758856
add_action( 'init', 'webp_uploads_init' );
759857

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<?php
2+
/**
3+
* Tests for webp_uploads_filter_block_background_images().
4+
*
5+
* @group background-image
6+
*
7+
* @package webp-uploads
8+
*/
9+
10+
use WebP_Uploads\Tests\TestCase;
11+
12+
class Test_WebP_Uploads_Block_Background_Images extends TestCase {
13+
14+
/**
15+
* Temporary attachment IDs created during tests.
16+
*
17+
* @var array<int>
18+
*/
19+
private $temp_attachment_ids = array();
20+
21+
/**
22+
* Set up test environment.
23+
*/
24+
public function set_up(): void {
25+
parent::set_up();
26+
27+
// Ensure we can generate both JPEG and WebP variants.
28+
$this->opt_in_to_jpeg_and_webp();
29+
update_option( 'perflab_modern_image_format', 'webp' );
30+
update_option( 'perflab_generate_webp_and_jpeg', '1' );
31+
32+
// Satisfy webp_uploads_in_frontend_body() conditions.
33+
$this->mock_frontend_body_hooks();
34+
}
35+
36+
/**
37+
* Tear down and clean created artifacts.
38+
*/
39+
public function tear_down(): void {
40+
foreach ( $this->temp_attachment_ids as $id ) {
41+
wp_delete_attachment( $id, true );
42+
}
43+
$this->temp_attachment_ids = array();
44+
45+
parent::tear_down();
46+
}
47+
48+
/**
49+
* Helper: Create a JPEG attachment and return its ID.
50+
*/
51+
private function create_jpeg_attachment(): int {
52+
$attachment_id = self::factory()->attachment->create_upload_object( TESTS_PLUGIN_DIR . '/tests/data/images/leaves.jpg' );
53+
$this->assertNotWPError( $attachment_id );
54+
$this->temp_attachment_ids[] = $attachment_id;
55+
return $attachment_id;
56+
}
57+
58+
/**
59+
* Generates block content markup and block array for a given block type using an attachment background image.
60+
*
61+
* @param string $block_name Block name (e.g. 'core/cover' or 'core/group').
62+
* @param int $attachment_id Attachment ID whose URL will be used as original background image.
63+
* @param string $original_url The original full size image URL.
64+
* @return array{block_content:string,block:array<string,mixed>} Array containing 'block_content' HTML and 'block' parsed block structure.
65+
*/
66+
private function generate_block_with_background( string $block_name, int $attachment_id, string $original_url ): array {
67+
if ( 'core/cover' === $block_name ) {
68+
$block_content = '<div class="wp-block-cover"><div style="background-image:url(' . esc_url( $original_url ) . ')"><span class="wp-block-cover__inner-container">Content</span></div></div>';
69+
$block = array(
70+
'blockName' => 'core/cover',
71+
'attrs' => array(
72+
'id' => $attachment_id,
73+
'url' => $original_url,
74+
),
75+
);
76+
} elseif ( 'core/group' === $block_name ) {
77+
$block_content = '<div class="wp-block-group" style="background-image:url(' . esc_url( $original_url ) . ')"><div class="wp-block-group__inner-container">Content</div></div>';
78+
$block = array(
79+
'blockName' => 'core/group',
80+
'attrs' => array(
81+
'style' => array(
82+
'background' => array(
83+
'backgroundImage' => array(
84+
'id' => $attachment_id,
85+
'url' => $original_url,
86+
),
87+
),
88+
),
89+
),
90+
);
91+
} else {
92+
$block_content = '';
93+
$block = array(
94+
'blockName' => $block_name,
95+
'attrs' => array(),
96+
);
97+
}
98+
99+
return array(
100+
'block_content' => $block_content,
101+
'block' => $block,
102+
);
103+
}
104+
105+
/**
106+
* It should replace a Cover block background image URL with a WebP variant when available.
107+
*
108+
* @covers ::webp_uploads_filter_block_background_images
109+
*/
110+
public function test_cover_block_background_image_replaced_with_webp(): void {
111+
if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
112+
$this->markTestSkipped( 'Mime type image/webp is not supported.' );
113+
}
114+
115+
$attachment_id = $this->create_jpeg_attachment();
116+
$original_url = wp_get_attachment_image_url( $attachment_id, 'full' );
117+
$this->assertIsString( $original_url );
118+
119+
$generated = $this->generate_block_with_background( 'core/cover', $attachment_id, $original_url );
120+
$filtered = webp_uploads_filter_block_background_images( $generated['block_content'], $generated['block'] );
121+
122+
$webp_url = webp_uploads_get_mime_type_image( $attachment_id, $original_url, 'image/webp' );
123+
$this->assertNotNull( $webp_url, 'Expected a generated WebP URL.' );
124+
125+
if ( $webp_url !== $original_url ) {
126+
$this->assertStringContainsString( $webp_url, $filtered, 'Filtered markup should reference WebP background image.' );
127+
}
128+
}
129+
130+
/**
131+
* It should not change background when no WebP source exists.
132+
*
133+
* @covers ::webp_uploads_filter_block_background_images
134+
*/
135+
public function test_cover_block_background_image_not_changed_without_webp_source(): void {
136+
// Remove any transform filters added during set_up so we start clean.
137+
remove_all_filters( 'webp_uploads_upload_image_mime_transforms' );
138+
139+
// Restrict transforms to only JPEG so no WebP is produced for this test.
140+
$filter = static function ( $transforms ) {
141+
$transforms['image/jpeg'] = array( 'image/jpeg' );
142+
return $transforms;
143+
};
144+
add_filter( 'webp_uploads_upload_image_mime_transforms', $filter );
145+
146+
$attachment_id = $this->create_jpeg_attachment();
147+
$original_url = wp_get_attachment_image_url( $attachment_id, 'full' );
148+
$this->assertIsString( $original_url );
149+
150+
$generated = $this->generate_block_with_background( 'core/cover', $attachment_id, $original_url );
151+
$filtered = webp_uploads_filter_block_background_images( $generated['block_content'], $generated['block'] );
152+
153+
remove_filter( 'webp_uploads_upload_image_mime_transforms', $filter );
154+
155+
$this->assertSame( $generated['block_content'], $filtered, 'Background image should remain unchanged when no WebP source exists.' );
156+
$this->assertStringNotContainsString( '-jpg.webp', $filtered );
157+
}
158+
159+
/**
160+
* It should replace a Group block background image URL with a WebP variant when available.
161+
*
162+
* @covers ::webp_uploads_filter_block_background_images
163+
*/
164+
public function test_group_block_background_image_replaced_with_webp(): void {
165+
if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
166+
$this->markTestSkipped( 'Mime type image/webp is not supported.' );
167+
}
168+
169+
$attachment_id = $this->create_jpeg_attachment();
170+
$original_url = wp_get_attachment_image_url( $attachment_id, 'full' );
171+
$this->assertIsString( $original_url );
172+
173+
$generated = $this->generate_block_with_background( 'core/group', $attachment_id, $original_url );
174+
$filtered = webp_uploads_filter_block_background_images( $generated['block_content'], $generated['block'] );
175+
176+
$webp_url = webp_uploads_get_mime_type_image( $attachment_id, $original_url, 'image/webp' );
177+
$this->assertNotNull( $webp_url, 'Expected a generated WebP URL.' );
178+
179+
if ( $webp_url !== $original_url ) {
180+
$this->assertStringContainsString( $webp_url, $filtered, 'Filtered group block should reference WebP background image.' );
181+
}
182+
}
183+
}

0 commit comments

Comments
 (0)