diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php index de079357fb860..7045aa1963644 100644 --- a/src/wp-includes/class-wp-image-editor-gd.php +++ b/src/wp-includes/class-wp-image-editor-gd.php @@ -153,6 +153,30 @@ protected function update_size( $width = false, $height = false ) { return parent::update_size( $width, $height ); } + /** + * Generates an hash for the current image. + * + * Concatenates crop coordinates, destination width and destination + * height into a string, calculates an MD5 hash, and returns first 8 characters. + * + * @since 6.4.0 + * + * @param bool|array $crop Boolean or array with crop coordinates (e.g., array( x, y )). + * @param int $dst_w The destination width. + * @param int $dst_h The destination height. + * @return false|string 8-char MD5 hash. False for non-array $crop. + */ + protected function create_crop_hash( $crop, $dst_w, $dst_h ) { + if ( ! is_array( $crop ) ) { + return false; + } + + $str = $crop[0] . $crop[1] . $dst_w . $dst_h; + $hash = substr( md5( $str ), 0, 8 ); + + return parent::update_crop_hash( $hash ); + } + /** * Resizes current image. * @@ -220,6 +244,7 @@ protected function _resize( $max_w, $max_h, $crop = false ) { imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ); if ( is_gd_image( $resized ) ) { + $this->create_crop_hash( $crop, $dst_w, $dst_h ); $this->update_size( $dst_w, $dst_h ); return $resized; } @@ -534,6 +559,7 @@ protected function _save( $image, $filename = null, $mime_type = null ) { 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ), 'width' => $this->size['width'], 'height' => $this->size['height'], + 'hash' => $this->hash, 'mime-type' => $mime_type, 'filesize' => wp_filesize( $filename ), ); diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index 03fe0bca6975f..3a6a3067bf967 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -309,6 +309,30 @@ public static function set_imagick_time_limit() { } } + /** + * Generates an hash for the current image. + * + * Concatenates crop coordinates, destination width and destination + * height into a string, calculates an MD5 hash, and returns first 8 characters. + * + * @since 6.4.0 + * + * @param bool|array $crop Boolean or array with crop coordinates (e.g., array( x, y )). + * @param int $dst_w The destination width. + * @param int $dst_h The destination height. + * @return false|string 8-char MD5 hash. False for non-array $crop. + */ + protected function create_crop_hash( $crop, $dst_w, $dst_h ) { + if ( ! is_array( $crop ) ) { + return false; + } + + $str = $crop[0] . $crop[1] . $dst_w . $dst_h; + $hash = substr( md5( $str ), 0, 8 ); + + return parent::update_crop_hash( $hash ); + } + /** * Resizes current image. * @@ -343,6 +367,7 @@ public function resize( $max_w, $max_h, $crop = false ) { list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims; if ( $crop ) { + $this->create_crop_hash( $crop, $dst_w, $dst_h ); return $this->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h ); } @@ -837,6 +862,7 @@ protected function _save( $image, $filename = null, $mime_type = null ) { 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ), 'width' => $this->size['width'], 'height' => $this->size['height'], + 'hash' => $this->hash, 'mime-type' => $mime_type, 'filesize' => wp_filesize( $filename ), ); diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index 3c636dc6ba5a7..da3bb143122e2 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -15,6 +15,7 @@ abstract class WP_Image_Editor { protected $file = null; protected $size = null; + protected $hash = null; protected $mime_type = null; protected $output_mime_type = null; protected $default_mime_type = 'image/jpeg'; @@ -221,6 +222,31 @@ protected function update_size( $width = null, $height = null ) { return true; } + + /** + * Gets hash for the current image. + * + * @since 6.4.0 + * + * @return string $hash 8-character MD5 hash. + */ + public function get_crop_hash() { + return $this->hash; + } + + /** + * Sets hash for the current image. + * + * @since 6.4.0 + * + * @param string $hash 8-character MD5 hash. + * @return true + */ + protected function update_crop_hash( $hash = null ) { + $this->hash = $hash; + return true; + } + /** * Gets the Image Compression quality on a 1-100% scale. * @@ -486,7 +512,13 @@ public function get_suffix() { return false; } - return "{$this->size['width']}x{$this->size['height']}"; + $suffix = "{$this->size['width']}x{$this->size['height']}"; + + if( $this->get_crop_hash() ){ + $suffix = $suffix . "-{$this->hash}"; + } + + return $suffix; } /** diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 1ef1b93d4e91a..92fb1821463cc 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -1362,6 +1362,64 @@ public function test_media_handle_upload_expected_titles() { $this->assertSame( 'This is a test', $post->post_title ); } + /** + * @ticket 40370 + */ + public function test_media_handle_upload_add_image_size() { + global $_wp_additional_image_sizes; + + $iptc_file = DIR_TESTDATA . '/images/test-image-iptc.jpg'; + + // Make a copy of this file as it gets moved during the file upload + $tmp_name = wp_tempnam( $iptc_file ); + + copy( $iptc_file, $tmp_name ); + + // FIXME : change to correct upload method that creates thumbnails + // and resuts in the new filenames with hashes + $_FILES['async-upload'] = array( + 'tmp_name' => $tmp_name, + 'name' => 'test-image-iptc.jpg', + 'type' => 'image/jpeg', + 'error' => 0, + 'size' => filesize( $iptc_file ), + ); + + // add multiple image sizes + add_image_size( 'newscentered', 400, 400, array( 'center', 'center') ); + add_image_size( 'newstop', 400, 400, array( 'center', 'top' ) ); + add_image_size( 'newsbottom', 400, 400, array( 'center', 'bottom' ) ); + + $info = wp_upload_dir(); + + $orig = array_map('basename', glob( ABSPATH . 'wp-content/uploads' + . $info['subdir'] .'/*.png')); + + // upload file + $post_id = media_handle_upload( + 'async-upload', + 0, + array(), + array( + 'action' => 'test_iptc_upload', + 'test_form' => false, + ) + ); + + unset( $_FILES['upload'] ); + + // Clean up. + wp_delete_attachment( $post_id ); + + // 3 new files should be created + $new = array_map('basename', glob( ABSPATH . 'wp-content/uploads' + . $info['subdir'] .'/*.{jpg, png}', GLOB_BRACE)); + $new_files = array_diff($new, $orig); + + $this->assertCount(3, $new_files ); + + } + /** * @ticket 33016 */