diff --git a/modules/images/webp-uploads/image-edit.php b/modules/images/webp-uploads/image-edit.php index 8a8b22241f..c3b8e9621d 100644 --- a/modules/images/webp-uploads/image-edit.php +++ b/modules/images/webp-uploads/image-edit.php @@ -19,18 +19,41 @@ */ function webp_uploads_update_sources( $metadata, $valid_mime_transforms, $main_images, $subsized_images ) { foreach ( $valid_mime_transforms as $targeted_mime ) { - // Make sure the path and file exists as those values are being accessed. - if ( ! isset( $main_images[ $targeted_mime ]['path'], $main_images[ $targeted_mime ]['file'] ) || ! file_exists( $main_images[ $targeted_mime ]['path'] ) ) { - continue; + // Make sure the path and file exists as those values are required. + $image_directory = null; + if ( isset( $main_images[ $targeted_mime ]['path'], $main_images[ $targeted_mime ]['file'] ) && file_exists( $main_images[ $targeted_mime ]['path'] ) ) { + // Add sources to original image metadata. + $metadata['sources'][ $targeted_mime ] = array( + 'file' => $main_images[ $targeted_mime ]['file'], + 'filesize' => filesize( $main_images[ $targeted_mime ]['path'] ), + ); + $image_directory = pathinfo( $main_images[ $targeted_mime ]['path'], PATHINFO_DIRNAME ); } - $image_directory = pathinfo( $main_images[ $targeted_mime ]['path'], PATHINFO_DIRNAME ); + /** + * If no original image was provided the image_directory can't be determined, in that scenario try to + * find it from the `file` property. + * + * @see get_attached_file() + */ + if ( + null === $image_directory + && isset( $metadata['file'] ) + && 0 !== strpos( $metadata['file'], '/' ) + && ':\\' !== substr( $metadata['file'], 1, 2 ) + ) { + $uploads = wp_get_upload_dir(); + if ( false === $uploads['error'] && isset( $uploads['basedir'] ) ) { + $file = path_join( $uploads['basedir'], $metadata['file'] ); + if ( file_exists( $file ) ) { + $image_directory = pathinfo( $file, PATHINFO_DIRNAME ); + } + } + } - // Add sources to original image metadata. - $metadata['sources'][ $targeted_mime ] = array( - 'file' => $main_images[ $targeted_mime ]['file'], - 'filesize' => filesize( $main_images[ $targeted_mime ]['path'] ), - ); + if ( null === $image_directory ) { + continue; + } foreach ( $metadata['sizes'] as $size_name => $size_details ) { if ( empty( $subsized_images[ $targeted_mime ][ $size_name ]['file'] ) ) { @@ -39,7 +62,6 @@ function webp_uploads_update_sources( $metadata, $valid_mime_transforms, $main_i // Add sources to resized image metadata. $subsize_path = path_join( $image_directory, $subsized_images[ $targeted_mime ][ $size_name ]['file'] ); - if ( ! file_exists( $subsize_path ) ) { continue; } @@ -91,7 +113,6 @@ function ( $metadata, $post_meta_id ) use ( $post_id, $file_path, $mime_type, $e return $metadata; } $callback_executed = true; - // No sizes to be created. if ( empty( $metadata['sizes'] ) ) { return $metadata; @@ -99,7 +120,14 @@ function ( $metadata, $post_meta_id ) use ( $post_id, $file_path, $mime_type, $e $old_metadata = wp_get_attachment_metadata( $post_id ); $resize_sizes = array(); + $target = isset( $_REQUEST['target'] ) ? $_REQUEST['target'] : 'all'; + foreach ( $old_metadata['sizes'] as $size_name => $size_details ) { + // If the target is 'nothumb', skip generating the 'thumbnail' size. + if ( 'nothumb' === $target && 'thumbnail' === $size_name ) { + continue; + } + if ( isset( $metadata['sizes'][ $size_name ] ) && ! empty( $metadata['sizes'][ $size_name ] ) && $metadata['sizes'][ $size_name ]['file'] !== $old_metadata['sizes'][ $size_name ]['file'] ) { $resize_sizes[ $size_name ] = $metadata['sizes'][ $size_name ]; @@ -111,8 +139,18 @@ function ( $metadata, $post_meta_id ) use ( $post_id, $file_path, $mime_type, $e $filename = pathinfo( $file_path, PATHINFO_FILENAME ); $main_images = array(); $subsized_images = array(); + foreach ( $mime_transforms as $targeted_mime ) { if ( $targeted_mime === $mime_type ) { + // If the target is `thumbnail` make sure it is the only selected size. + if ( 'thumbnail' === $target ) { + if ( isset( $metadata['sizes']['thumbnail'] ) ) { + $subsized_images[ $targeted_mime ] = array( 'thumbnail' => $metadata['sizes']['thumbnail'] ); + } + // When the targeted thumbnail is selected no additional size and subsize is set. + continue; + } + $main_images[ $targeted_mime ] = array( 'path' => $file_path, 'file' => pathinfo( $file_path, PATHINFO_BASENAME ), @@ -129,16 +167,44 @@ function ( $metadata, $post_meta_id ) use ( $post_id, $file_path, $mime_type, $e continue; } - $extension = explode( '|', $allowed_mimes[ $targeted_mime ] ); - $destination = trailingslashit( $original_directory ) . "{$filename}.{$extension[0]}"; - $result = $editor->save( $destination, $targeted_mime ); - - if ( is_wp_error( $result ) ) { - continue; + $extension = explode( '|', $allowed_mimes[ $targeted_mime ] ); + $extension = $extension[0]; + + // If the target is `thumbnail` make sure only that size is generated. + if ( 'thumbnail' === $target ) { + if ( ! isset( $subsized_images[ $mime_type ]['thumbnail']['file'] ) ) { + continue; + } + $thumbnail_file = $subsized_images[ $mime_type ]['thumbnail']['file']; + $image_path = path_join( $original_directory, $thumbnail_file ); + $editor = wp_get_image_editor( $image_path, array( 'mime_type' => $targeted_mime ) ); + + if ( is_wp_error( $editor ) ) { + continue; + } + + $current_extension = pathinfo( $thumbnail_file, PATHINFO_EXTENSION ); + // Create a file with then new extension out of the targeted file. + $target_file_name = preg_replace( "/\.$current_extension$/", ".$extension", $thumbnail_file ); + $target_file_location = path_join( $original_directory, $target_file_name ); + $result = $editor->save( $target_file_location, $targeted_mime ); + + if ( is_wp_error( $result ) ) { + continue; + } + + $subsized_images[ $targeted_mime ] = array( 'thumbnail' => $result ); + } else { + $destination = trailingslashit( $original_directory ) . "{$filename}.{$extension}"; + $result = $editor->save( $destination, $targeted_mime ); + + if ( is_wp_error( $result ) ) { + continue; + } + + $main_images[ $targeted_mime ] = $result; + $subsized_images[ $targeted_mime ] = $editor->multi_resize( $resize_sizes ); } - - $main_images[ $targeted_mime ] = $result; - $subsized_images[ $targeted_mime ] = $editor->multi_resize( $resize_sizes ); } return webp_uploads_update_sources( $metadata, $mime_transforms, $main_images, $subsized_images ); diff --git a/tests/modules/images/webp-uploads/image-edit-tests.php b/tests/modules/images/webp-uploads/image-edit-tests.php index a9cee9705a..9e247ea250 100644 --- a/tests/modules/images/webp-uploads/image-edit-tests.php +++ b/tests/modules/images/webp-uploads/image-edit-tests.php @@ -3,7 +3,7 @@ * Tests for webp-uploads module image-edit.php. * * @package performance-lab - * @group webp-uploads + * @group image-edit */ use PerformanceLab\Tests\TestCase\ImagesTestCase; @@ -138,31 +138,38 @@ public function it_should_prevent_to_backup_the_full_size_image_if_only_the_thum $attachment_id = $this->factory->attachment->create_upload_object( TESTS_PLUGIN_DIR . '/tests/testdata/modules/images/leafs.jpg' ); $metadata = wp_get_attachment_metadata( $attachment_id ); $this->assertArrayHasKey( 'sources', $metadata ); - $editor = new WP_Image_Edit( $attachment_id ); $editor->flip_vertical()->only_thumbnail()->save(); $this->assertTrue( $editor->success() ); - $backup_sources = get_post_meta( $attachment_id, '_wp_attachment_backup_sources', true ); $this->assertEmpty( $backup_sources ); - $backup_sizes = get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true ); $this->assertIsArray( $backup_sizes ); $this->assertCount( 1, $backup_sizes ); $this->assertArrayHasKey( 'thumbnail-orig', $backup_sizes ); $this->assertArrayHasKey( 'sources', $backup_sizes['thumbnail-orig'] ); - $metadata = wp_get_attachment_metadata( $attachment_id ); - $this->assertImageHasSource( $attachment_id, 'image/jpeg' ); $this->assertImageHasSource( $attachment_id, 'image/webp' ); - $this->assertImageHasSizeSource( $attachment_id, 'thumbnail', 'image/jpeg' ); $this->assertImageHasSizeSource( $attachment_id, 'thumbnail', 'image/webp' ); + $this->assertFileNameIsNotEdited( $metadata['sources']['image/jpeg']['file'] ); + $this->assertFileNameIsNotEdited( $metadata['sources']['image/webp']['file'] ); + foreach ( $metadata['sizes'] as $size_name => $properties ) { $this->assertImageHasSizeSource( $attachment_id, $size_name, 'image/jpeg' ); $this->assertImageHasSizeSource( $attachment_id, $size_name, 'image/webp' ); + + if ( 'thumbnail' === $size_name ) { + $this->assertSame( $properties['file'], $properties['sources']['image/jpeg']['file'] ); + $this->assertSame( str_replace( '.jpg', '.webp', $properties['file'] ), $properties['sources']['image/webp']['file'] ); + $this->assertFileNameIsEdited( $properties['sources']['image/jpeg']['file'] ); + $this->assertFileNameIsEdited( $properties['sources']['image/webp']['file'] ); + } else { + $this->assertFileNameIsNotEdited( $properties['sources']['image/jpeg']['file'] ); + $this->assertFileNameIsNotEdited( $properties['sources']['image/webp']['file'] ); + } } } @@ -191,6 +198,10 @@ public function it_should_backup_the_image_when_all_images_except_the_thumbnail_ $this->assertImageHasSource( $attachment_id, 'image/jpeg' ); $this->assertImageHasSource( $attachment_id, 'image/webp' ); + foreach ( $updated_metadata['sources'] as $properties ) { + $this->assertFileNameIsEdited( $properties['file'] ); + } + $backup_sizes = get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true ); $this->assertIsArray( $backup_sizes ); $this->assertArrayNotHasKey( 'thumbnail-orig', $backup_sizes, 'The thumbnail-orig was stored in the back up' ); @@ -203,6 +214,60 @@ public function it_should_backup_the_image_when_all_images_except_the_thumbnail_ } } + /** + * Use the attached image when updating subsequent images not the original version + * + * @test + */ + public function it_should_use_the_attached_image_when_updating_subsequent_images_not_the_original_version() { + // The leafs image is 1080 pixels wide with this filter we ensure a -scaled version is created for this test. + add_filter( + 'big_image_size_threshold', + function () { + // Due to the largest image size is 1024 and the image is 1080x720, 1050 is a good spot to create a scaled size for all images sizes. + return 1050; + } + ); + + $attachment_id = $this->factory->attachment->create_upload_object( + TESTS_PLUGIN_DIR . '/tests/testdata/modules/images/leafs.jpg' + ); + + $this->assertNotSame( wp_get_original_image_path( $attachment_id ), get_attached_file( $attachment_id ) ); + + $editor = new WP_Image_Edit( $attachment_id ); + $editor->flip_right()->all_except_thumbnail()->save(); + + $this->assertTrue( $editor->success() ); + + $metadata = wp_get_attachment_metadata( $attachment_id ); + + $this->assertArrayHasKey( 'original_image', $metadata ); + $this->assertArrayHasKey( 'sources', $metadata ); + $this->assertImageHasSource( $attachment_id, 'image/jpeg' ); + $this->assertImageHasSource( $attachment_id, 'image/webp' ); + + foreach ( $metadata['sources'] as $properties ) { + $this->assertFileNameIsEdited( $properties['file'] ); + $this->assertStringContainsString( '-scaled-', $properties['file'] ); + } + + $this->assertArrayHasKey( 'sizes', $metadata ); + foreach ( $metadata['sizes'] as $size_name => $properties ) { + $this->assertImageHasSizeSource( $attachment_id, $size_name, 'image/jpeg' ); + $this->assertImageHasSizeSource( $attachment_id, $size_name, 'image/webp' ); + foreach ( $properties['sources'] as $mime_type => $values ) { + if ( 'thumbnail' === $size_name ) { + $this->assertFileNameIsNotEdited( $values['file'], "'{$size_name}' is not valid." ); + $this->assertStringNotContainsString( '-scaled-', $values['file'] ); + } else { + $this->assertFileNameIsEdited( $values['file'], "'{$size_name}' is not valid." ); + $this->assertStringContainsString( '-scaled-', $values['file'] ); + } + } + } + } + /** * Update source attributes when webp is edited. *