From d218e9c4b6a99f94ec6d8d73308d01ded8764536 Mon Sep 17 00:00:00 2001 From: COQUARD Cyrille Date: Wed, 22 Nov 2023 12:05:16 +0100 Subject: [PATCH 1/3] Added a fix for the lightbox by not passing the wp attributes to the picture tag --- classes/Webp/Picture/Display.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/classes/Webp/Picture/Display.php b/classes/Webp/Picture/Display.php index 7a475327a..639220774 100644 --- a/classes/Webp/Picture/Display.php +++ b/classes/Webp/Picture/Display.php @@ -229,7 +229,11 @@ protected function build_picture_tag( $image ) { unset( $attributes['data-object-position'] ); } - $output = 'build_attributes( $attributes ) . ">\n"; + $picture_attributes = array_filter( $attributes, function ( $attribute ) { + return strpos( $attribute, 'data-wp' ) === false; + }, ARRAY_FILTER_USE_KEY ); + + $output = 'build_attributes( $picture_attributes ) . ">\n"; /** * Allow to add more tags to the tag. * From 2515e5698244f59142a87b4ce7ad931f1066427b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Perona?= Date: Mon, 5 Jan 2026 16:28:07 -0500 Subject: [PATCH 2/3] fix CS --- classes/Picture/Display.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/classes/Picture/Display.php b/classes/Picture/Display.php index 0374ef248..c2bc2ff49 100644 --- a/classes/Picture/Display.php +++ b/classes/Picture/Display.php @@ -218,9 +218,13 @@ protected function build_picture_tag( $image ) { unset( $attributes['data-object-position'] ); } - $picture_attributes = array_filter( $attributes, function ( $attribute ) { - return strpos( $attribute, 'data-wp' ) === false; - }, ARRAY_FILTER_USE_KEY ); + $picture_attributes = array_filter( + $attributes, + function ( $attribute ) { + return strpos( $attribute, 'data-wp' ) === false; + }, + ARRAY_FILTER_USE_KEY + ); $output = 'build_attributes( $picture_attributes ) . ">\n"; /** From 4ea5bf04fc6eeed3c2e14c739a50cc3fbdca6860 Mon Sep 17 00:00:00 2001 From: honemo Date: Mon, 13 Jul 2026 15:18:48 +0200 Subject: [PATCH 3/3] Add unit tests for data-wp attribute filtering in build_picture_tag() Covers the lightbox fix (#758): data-wp-* directive attributes must be excluded from the tag while remaining on the inner tag, without breaking the existing Gutenberg cover-background exclusion logic. Co-Authored-By: Claude Sonnet 5 --- .../classes/Picture/BuildPictureTagTest.php | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 Tests/Unit/classes/Picture/BuildPictureTagTest.php diff --git a/Tests/Unit/classes/Picture/BuildPictureTagTest.php b/Tests/Unit/classes/Picture/BuildPictureTagTest.php new file mode 100644 index 000000000..6598e8bdf --- /dev/null +++ b/Tests/Unit/classes/Picture/BuildPictureTagTest.php @@ -0,0 +1,189 @@ +returnArg(); + } + + /** + * Get an instance of Display without invoking the constructor (its only + * dependency, Imagify_Filesystem, is not needed by build_picture_tag()). + * + * @return Display + */ + private function get_display_instance(): Display { + $reflection = new ReflectionClass( Display::class ); + + return $reflection->newInstanceWithoutConstructor(); + } + + /** + * Build a minimal $image array as expected by build_picture_tag(), build_source_tag() + * and build_img_tag(). $attributes is the raw attributes array. + * + * @param array $attributes The attributes. + * + * @return array + */ + private function get_image_data( array $attributes ): array { + return [ + 'attributes' => $attributes, + 'src_attribute' => 'src', + 'srcset_attribute' => false, + 'srcset' => [], + 'src' => [], + ]; + } + + /** + * Extract the opening tag's attribute string from the built output. + * + * @param string $output The output of build_picture_tag(). + * + * @return string + */ + private function get_picture_opening_tag( string $output ): string { + preg_match( '/^]*)>/', $output, $matches ); + + return $matches[1] ?? ''; + } + + /** + * Extract the tag's attribute string from the built output. + * + * @param string $output The output of build_picture_tag(). + * + * @return string + */ + private function get_img_tag( string $output ): string { + preg_match( '/]*)\/>/', $output, $matches ); + + return $matches[1] ?? ''; + } + + /** + * `data-wp-*` directive attributes must be filtered out of the tag, + * but kept on the inner tag. + */ + public function testDataWpAttributesAreExcludedFromPictureTagButKeptOnImgTag(): void { + $attributes = [ + 'data-object-fit' => 'cover', + 'data-wp-interactive' => 'core/image', + 'data-wp-context' => '{"isOpen":false}', + 'data-wp-on--click' => 'actions.showLightbox', + ]; + + $image = $this->get_image_data( $attributes ); + + $method = $this->get_reflective_method( 'build_picture_tag', Display::class ); + $output = $method->invoke( $this->get_display_instance(), $image ); + + $picture_tag = $this->get_picture_opening_tag( $output ); + $img_tag = $this->get_img_tag( $output ); + + $this->assertStringNotContainsString( 'data-wp-interactive', $picture_tag ); + $this->assertStringNotContainsString( 'data-wp-context', $picture_tag ); + $this->assertStringNotContainsString( 'data-wp-on--click', $picture_tag ); + + $this->assertStringContainsString( 'data-wp-interactive="core/image"', $img_tag ); + $this->assertStringContainsString( 'data-wp-context="{"isOpen":false}"', $img_tag ); + $this->assertStringContainsString( 'data-wp-on--click="actions.showLightbox"', $img_tag ); + } + + /** + * Non `data-wp` attributes are unaffected by the new filter and keep behaving + * as they did before: they remain on the tag, and follow the + * pre-existing removal rules on the tag. + */ + public function testNonDataWpAttributesAreUnaffected(): void { + $attributes = [ + 'data-object-fit' => 'cover', + 'class' => 'my-image', + 'id' => 'img-1', + ]; + + $image = $this->get_image_data( $attributes ); + + $method = $this->get_reflective_method( 'build_picture_tag', Display::class ); + $output = $method->invoke( $this->get_display_instance(), $image ); + + $picture_tag = $this->get_picture_opening_tag( $output ); + $img_tag = $this->get_img_tag( $output ); + + // All 3 attributes remain on the tag, since none of them contain 'data-wp'. + $this->assertStringContainsString( 'data-object-fit="cover"', $picture_tag ); + $this->assertStringContainsString( 'class="my-image"', $picture_tag ); + $this->assertStringContainsString( 'id="img-1"', $picture_tag ); + + // Pre-existing logic (unrelated to this fix) strips class/id from the tag, + // but keeps data-object-fit. + $this->assertStringContainsString( 'data-object-fit="cover"', $img_tag ); + $this->assertStringNotContainsString( 'class=', $img_tag ); + $this->assertStringNotContainsString( 'id=', $img_tag ); + } + + /** + * The new `data-wp` filter composes correctly with the pre-existing + * 'wp-block-cover__image-background' exclusion logic: both sets of + * attributes are removed from the tag, while the tag + * (which is treated as a Gutenberg cover background image) keeps + * everything, including the `data-wp` attributes. + */ + public function testDataWpFilterComposesWithGutenbergCoverExclusion(): void { + $attributes = [ + 'class' => 'wp-block-cover__image-background', + 'style' => 'object-fit:cover', + 'data-object-fit' => 'cover', + 'data-object-position' => '50% 50%', + 'data-wp-interactive' => 'core/image', + 'data-wp-context' => '{}', + ]; + + $image = $this->get_image_data( $attributes ); + + $method = $this->get_reflective_method( 'build_picture_tag', Display::class ); + $output = $method->invoke( $this->get_display_instance(), $image ); + + $picture_tag = $this->get_picture_opening_tag( $output ); + $img_tag = $this->get_img_tag( $output ); + + // None of the Gutenberg-specific attributes, nor the data-wp attributes, remain on . + $this->assertStringNotContainsString( 'class=', $picture_tag ); + $this->assertStringNotContainsString( 'style=', $picture_tag ); + $this->assertStringNotContainsString( 'data-object-fit', $picture_tag ); + $this->assertStringNotContainsString( 'data-object-position', $picture_tag ); + $this->assertStringNotContainsString( 'data-wp-interactive', $picture_tag ); + $this->assertStringNotContainsString( 'data-wp-context', $picture_tag ); + $this->assertSame( '', trim( $picture_tag ) ); + + // The tag, being the Gutenberg cover background image, keeps everything + // (only 'id' and 'title' are ever stripped from it in that branch). + $this->assertStringContainsString( 'class="wp-block-cover__image-background"', $img_tag ); + $this->assertStringContainsString( 'style="object-fit:cover"', $img_tag ); + $this->assertStringContainsString( 'data-object-fit="cover"', $img_tag ); + $this->assertStringContainsString( 'data-object-position="50% 50%"', $img_tag ); + $this->assertStringContainsString( 'data-wp-interactive="core/image"', $img_tag ); + $this->assertStringContainsString( 'data-wp-context="{}"', $img_tag ); + } +}