Ticket #55443: 55443-multi-mime-with-edit-and-restore-flows.diff
File 55443-multi-mime-with-edit-and-restore-flows.diff, 17.6 KB (added by , 2 years ago) |
---|
-
src/wp-admin/includes/image-edit.php
diff --git src/wp-admin/includes/image-edit.php src/wp-admin/includes/image-edit.php index e814fc47e7..b79c07ed12 100644
function stream_preview_image( $post_id ) { 735 735 */ 736 736 function wp_restore_image( $post_id ) { 737 737 $meta = wp_get_attachment_metadata( $post_id ); 738 $old_meta = $meta; 738 739 $file = get_attached_file( $post_id ); 739 740 $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true ); 740 741 $old_backup_sizes = $backup_sizes; 742 $backup_sources = get_post_meta( $post_id, '_wp_attachment_backup_sources', true ); 741 743 $restored = false; 742 744 $msg = new stdClass; 743 745 … … function wp_restore_image( $post_id ) { 799 801 } 800 802 } 801 803 804 // Get the next available `full-{orig or hash}` key on the images if the name 805 // has not been used as part of the backup sources it would be used if no size is 806 // found or backup exists `null` would be used instead. 807 $next_full_size_key_from_backup = null; 808 foreach ( array_keys( $backup_sizes ) as $size_name ) { 809 // If the target already has the sources attributes find the next one. 810 if ( isset( $backup_sources[ $size_name ] ) ) { 811 continue; 812 } 813 814 // We are only interested in the `full-` sizes. 815 if ( strpos( $size_name, 'full-' ) === false ) { 816 continue; 817 } 818 819 $next_full_size_key_from_backup = $size_name; 820 break; 821 } 822 823 if ( null !== $next_full_size_key_from_backup ) { 824 $backup_sources[ $next_full_size_key_from_backup ] = $old_meta['sources']; 825 // Store the `sources` property into the full size if present. 826 update_post_meta( $post_id, '_wp_attachment_backup_sources', $backup_sources ); 827 } 828 829 if ( isset( $backup_sources['full-orig'] ) && is_array( $backup_sources['full-orig'] ) ) { 830 $meta['sources'] = $backup_sources['full-orig']; 831 } 832 802 833 if ( ! wp_update_attachment_metadata( $post_id, $meta ) || 803 834 ( $old_backup_sizes !== $backup_sizes && ! update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes ) ) ) { 804 835 … … function wp_save_image( $post_id ) { 873 904 return $return; 874 905 } 875 906 876 $meta = wp_get_attachment_metadata( $post_id ); 877 $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true ); 907 $meta = wp_get_attachment_metadata( $post_id ); 908 $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true ); 909 $backup_sources = get_post_meta( $post->ID, '_wp_attachment_backup_sources', true ); 878 910 879 911 if ( ! is_array( $meta ) ) { 880 912 $return->error = esc_js( __( 'Image data does not exist. Please re-upload the image.' ) ); 881 913 return $return; 882 914 } 883 915 916 $old_metadata = $meta; 917 884 918 if ( ! is_array( $backup_sizes ) ) { 885 919 $backup_sizes = array(); 886 920 } 887 921 922 if ( ! is_array( $backup_sources ) ) { 923 $backup_sources = array(); 924 } 925 926 $transforms = wp_upload_image_mime_transforms( $post_id ); 927 $mime_transforms = array(); 928 929 if ( ! empty( $transforms[ $post->post_mime_type ] ) ) { 930 $mime_transforms = $transforms[ $post->post_mime_type ]; 931 } 932 888 933 // Generate new filename. 889 934 $path = get_attached_file( $post_id ); 890 935 … … function wp_save_image( $post_id ) { 982 1027 foreach ( $meta['sizes'] as $size ) { 983 1028 if ( ! empty( $size['file'] ) && preg_match( '/-e[0-9]{13}-/', $size['file'] ) ) { 984 1029 $delete_file = path_join( $dirname, $size['file'] ); 985 wp_delete_file( $delete_file ); 1030 if ( file_exists( $delete_file ) ) { 1031 wp_delete_file( $delete_file ); 1032 } 1033 1034 // Delete generated secondary mimes. 1035 foreach ( $mime_transforms as $mime ) { 1036 if ( ! empty( $size['sources'][ $mime ]['file'] ) ) { 1037 $delete_file = path_join( $dirname, $size['sources'][ $mime ]['file'] ); 1038 1039 if ( file_exists( $delete_file ) ) { 1040 wp_delete_file( $delete_file ); 1041 } 1042 } 1043 } 986 1044 } 987 1045 } 988 1046 } 989 1047 1048 $_sizes = array(); 990 1049 if ( isset( $sizes ) ) { 991 $_sizes = array();992 1050 993 1051 foreach ( $sizes as $size ) { 994 1052 $tag = false; … … function wp_save_image( $post_id ) { 1026 1084 $meta['sizes'] = array_merge( $meta['sizes'], $img->multi_resize( $_sizes ) ); 1027 1085 } 1028 1086 1087 $original_directory = pathinfo( $new_path, PATHINFO_DIRNAME ); 1088 $filename = pathinfo( $new_path, PATHINFO_FILENAME ); 1089 $main_images = array(); 1090 $subsized_images = array(); 1091 1092 foreach ( $mime_transforms as $targeted_mime ) { 1093 if ( $targeted_mime !== $post->post_mime_type ) { 1094 if ( ! $img::supports_mime_type( $targeted_mime ) ) { 1095 continue; 1096 } 1097 1098 $extension = wp_get_default_extension_for_mime_type( $targeted_mime ); 1099 if ( false === $extension ) { 1100 continue; 1101 } 1102 $img->set_output_mime_type( $targeted_mime ); 1103 1104 if ( 'thumbnail' === $target ) { 1105 if ( ! isset( $subsized_images[ $post->post_mime_type ]['thumbnail']['file'] ) ) { 1106 continue; 1107 } 1108 1109 $result = $img->make_subsize( $subsized_images[ $post->post_mime_type ]['thumbnail'] ); 1110 1111 if ( is_wp_error( $result ) ) { 1112 continue; 1113 } 1114 1115 $subsized_images[ $targeted_mime ] = array( 'thumbnail' => $result ); 1116 } else { 1117 $result = $img->save( $img->generate_filename( '', $filename, $extension ) ); 1118 1119 if ( is_wp_error( $result ) ) { 1120 continue; 1121 } 1122 1123 $main_images[ $targeted_mime ] = $result; 1124 if ( method_exists( $img, 'make_subsize' ) ) { 1125 foreach ( $_sizes as $size_name => $size_data ) { 1126 $size_meta = $img->make_subsize( $size_data ); 1127 1128 if ( ! is_wp_error( $size_meta ) ) { 1129 $subsized_images[ $targeted_mime ][ $size_name ] = $size_meta; 1130 } 1131 } 1132 } else { 1133 $subsized_images[ $targeted_mime ] = $img->multi_resize( $_sizes ); 1134 } 1135 } 1136 } else { 1137 // If the target is `thumbnail` make sure it is the only selected size. 1138 // When the targeted thumbnail is selected no additional size and subsize is set. 1139 if ( 'thumbnail' === $target && isset( $meta['sizes']['thumbnail'] ) ) { 1140 $subsized_images[ $targeted_mime ] = array( 'thumbnail' => $meta['sizes']['thumbnail'] ); 1141 } else { 1142 $main_images[ $targeted_mime ] = array( 1143 'path' => $new_path, 1144 'file' => pathinfo( $new_path, PATHINFO_BASENAME ), 1145 ); 1146 $subsized_images[ $targeted_mime ] = $meta['sizes']; 1147 } 1148 } 1149 1150 if ( isset( $main_images[ $targeted_mime ]['path'], $main_images[ $targeted_mime ]['file'] ) && file_exists( $main_images[ $targeted_mime ]['path'] ) ) { 1151 // Add sources to original image metadata. 1152 $meta['sources'][ $targeted_mime ] = array( 1153 'file' => $main_images[ $targeted_mime ]['file'], 1154 'filesize' => isset( $main_images[ $targeted_mime ]['filesize'] ) ? $main_images[ $targeted_mime ]['filesize'] : wp_filesize( $main_images[ $targeted_mime ]['path'] ), 1155 ); 1156 } 1157 1158 foreach ( $_sizes as $size_name => $size_details ) { 1159 if ( empty( $subsized_images[ $targeted_mime ][ $size_name ]['file'] ) ) { 1160 continue; 1161 } 1162 1163 // Add sources to resized image metadata. 1164 $subsize_path = path_join( $original_directory, $subsized_images[ $targeted_mime ][ $size_name ]['file'] ); 1165 if ( ! file_exists( $subsize_path ) ) { 1166 continue; 1167 } 1168 1169 $meta['sizes'][ $size_name ]['sources'][ $targeted_mime ] = array( 1170 'file' => $subsized_images[ $targeted_mime ][ $size_name ]['file'], 1171 'filesize' => wp_filesize( $subsize_path ), 1172 ); 1173 } 1174 } 1175 1029 1176 unset( $img ); 1030 1177 1031 1178 if ( $success ) { 1032 1179 wp_update_attachment_metadata( $post_id, $meta ); 1033 1180 update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes ); 1034 1181 1182 $next_target_size_name_from_source = null; 1183 // Store the provided sources for the attachment ID in the `_wp_attachment_backup_sources` with the next available target if target is `null` no source would be stored. 1184 foreach ( array_keys( $backup_sizes ) as $size_name ) { 1185 // If the target already has the sources attributes find the next one. 1186 if ( isset( $backup_sources[ $size_name ] ) ) { 1187 continue; 1188 } 1189 1190 // We are only interested in the `full-` sizes. 1191 if ( strpos( $size_name, 'full-' ) === false ) { 1192 continue; 1193 } 1194 1195 $next_target_size_name_from_source = $size_name; 1196 } 1197 1198 if ( null !== $next_target_size_name_from_source && ! empty( $old_metadata['sources'] ) ) { 1199 $backup_sources[ $next_target_size_name_from_source ] = $old_metadata['sources']; 1200 // Store the `sources` property into the full size if present. 1201 update_post_meta( $post_id, '_wp_attachment_backup_sources', $backup_sources ); 1202 } 1203 1035 1204 if ( 'thumbnail' === $target || 'all' === $target || 'full' === $target ) { 1036 1205 // Check if it's an image edit from attachment edit screen. 1037 1206 if ( ! empty( $_REQUEST['context'] ) && 'edit-attachment' === $_REQUEST['context'] ) { -
src/wp-includes/post.php
diff --git src/wp-includes/post.php src/wp-includes/post.php index df509fd114..e8abd37bfe 100644
function wp_delete_attachment( $post_id, $force_delete = false ) { 6388 6388 delete_post_meta( $post_id, '_wp_trash_meta_status' ); 6389 6389 delete_post_meta( $post_id, '_wp_trash_meta_time' ); 6390 6390 6391 $meta = wp_get_attachment_metadata( $post_id ); 6392 $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true ); 6393 $file = get_attached_file( $post_id ); 6391 $meta = wp_get_attachment_metadata( $post_id ); 6392 $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true ); 6393 $backup_sources = get_post_meta( $post->ID, '_wp_attachment_backup_sources', true ); 6394 $file = get_attached_file( $post_id ); 6394 6395 6395 6396 if ( is_multisite() && is_string( $file ) && ! empty( $file ) ) { 6396 6397 clean_dirsize_cache( $file ); … … function wp_delete_attachment( $post_id, $force_delete = false ) { 6436 6437 /** This action is documented in wp-includes/post.php */ 6437 6438 do_action( 'deleted_post', $post_id, $post ); 6438 6439 6439 wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file );6440 wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file, $backup_sources ); 6440 6441 6441 6442 clean_post_cache( $post ); 6442 6443 … … function wp_delete_attachment( $post_id, $force_delete = false ) { 6450 6451 * 6451 6452 * @global wpdb $wpdb WordPress database abstraction object. 6452 6453 * 6453 * @param int $post_id Attachment ID. 6454 * @param array $meta The attachment's meta data. 6455 * @param array $backup_sizes The meta data for the attachment's backup images. 6456 * @param string $file Absolute path to the attachment's file. 6454 * @param int $post_id Attachment ID. 6455 * @param array $meta The attachment's meta data. 6456 * @param array $backup_sizes The meta data for the attachment's backup images. 6457 * @param string $file Absolute path to the attachment's file. 6458 * @param array $backup_sources The meta data for the attachment's backup sources images. 6457 6459 * @return bool True on success, false on failure. 6458 6460 */ 6459 function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) {6461 function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file, $backup_sources ) { 6460 6462 global $wpdb; 6461 6463 6462 6464 $uploadpath = wp_get_upload_dir(); … … function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { 6580 6582 } 6581 6583 } 6582 6584 6585 // Delete full sizes backup sources mime types. 6586 if ( is_array( $backup_sources ) ) { 6587 foreach ( $backup_sources as $backup_source ) { 6588 6589 foreach ( $backup_source as $properties ) { 6590 if ( ! is_array( $properties ) || empty( $properties['file'] ) ) { 6591 continue; 6592 } 6593 6594 $source_file = str_replace( wp_basename( $file ), $properties['file'], $file ); 6595 $del_file = path_join( $uploadpath['basedir'], $source_file ); 6596 if ( ! wp_delete_file_from_directory( $del_file, $intermediate_dir ) ) { 6597 $deleted = false; 6598 } 6599 } 6600 } 6601 } 6602 6583 6603 return $deleted; 6584 6604 } 6585 6605 -
tests/phpunit/tests/image/editor.php
diff --git tests/phpunit/tests/image/editor.php tests/phpunit/tests/image/editor.php index c051ffec2b..b83eff1ae0 100644
class Tests_Image_Editor extends WP_Image_UnitTestCase { 627 627 ); 628 628 } 629 629 630 /** 631 * Test removing the backup sources files when image is removed after edited. 632 * 633 * @ticket 55443 634 */ 635 public function it_should_remove_the_backup_sizes_and_sources_if_the_attachment_is_deleted_after_edit() { 636 require_once ABSPATH . 'wp-admin/includes/image-edit.php'; 637 638 $attachment_id = $this->factory->attachment->create_upload_object( 639 DIR_TESTDATA . '/tests/testdata/modules/images/leafs.jpg' 640 ); 641 642 $file = get_attached_file( $attachment_id, true ); 643 $dirname = pathinfo( $file, PATHINFO_DIRNAME ); 644 645 $this->assertIsString( $file ); 646 $this->assertFileExists( $file ); 647 648 $_REQUEST['action'] = 'image-editor'; 649 $_REQUEST['context'] = 'edit-attachment'; 650 $_REQUEST['postid'] = $attachment_id; 651 $_REQUEST['target'] = 'all'; 652 $_REQUEST['do'] = 'save'; 653 $_REQUEST['history'] = '[{"r":-90}]'; 654 655 wp_save_image( $attachment_id ); 656 657 $backup_sources = get_post_meta( $attachment_id, '_wp_attachment_backup_sources', true ); 658 $this->assertNotEmpty( $backup_sources ); 659 $this->assertIsArray( $backup_sources ); 660 661 $backup_sizes = get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true ); 662 $this->assertNotEmpty( $backup_sizes ); 663 $this->assertIsArray( $backup_sizes ); 664 665 wp_delete_attachment( $attachment_id, true ); 666 667 $this->assertFileDoesNotExist( path_join( $dirname, $backup_sources['full-orig']['image/webp']['file'] ) ); 668 $this->assertFileDoesNotExist( path_join( $dirname, $backup_sizes['thumbnail-orig']['sources']['image/webp']['file'] ) ); 669 } 670 630 671 /** 631 672 * Test avoiding the change of URLs of images that are not part of the media library. 632 673 * -
tests/phpunit/tests/image/functions.php
diff --git tests/phpunit/tests/image/functions.php tests/phpunit/tests/image/functions.php index cb5a7971c9..95bb5cae1e 100644
class Tests_Image_Functions extends WP_UnitTestCase { 248 248 fclose( $handle ); 249 249 } 250 250 251 /** 252 * Backup the sources structure alongside the full size 253 * 254 * @ticket 55443 255 */ 256 public function test_backup_the_sources_structure_alongside_the_full_size() { 257 require_once ABSPATH . 'wp-admin/includes/image-edit.php'; 258 259 $attachment_id = self::factory()->attachment->create_upload_object( DIR_TESTDATA . '/images/canola.jpg' ); 260 261 $metadata = wp_get_attachment_metadata( $attachment_id ); 262 $this->assertEmpty( get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true ) ); 263 $this->assertEmpty( get_post_meta( $attachment_id, '_wp_attachment_backup_sources', true ) ); 264 265 $_REQUEST['action'] = 'image-editor'; 266 $_REQUEST['context'] = 'edit-attachment'; 267 $_REQUEST['postid'] = $attachment_id; 268 $_REQUEST['target'] = 'all'; 269 $_REQUEST['do'] = 'save'; 270 $_REQUEST['history'] = '[{"r":-90}]'; 271 272 $ret = wp_save_image( $attachment_id ); 273 274 $backup_sizes = get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true ); 275 276 $this->assertNotEmpty( $backup_sizes ); 277 $this->assertIsArray( $backup_sizes ); 278 279 $backup_sources = get_post_meta( $attachment_id, '_wp_attachment_backup_sources', true ); 280 $this->assertIsArray( $backup_sources ); 281 $this->assertArrayHasKey( 'full-orig', $backup_sources ); 282 $this->assertSame( $metadata['sources'], $backup_sources['full-orig'] ); 283 284 foreach ( $backup_sizes as $size => $properties ) { 285 $size_name = str_replace( '-orig', '', $size ); 286 287 if ( 'full-orig' === $size ) { 288 continue; 289 } 290 291 $this->assertArrayHasKey( 'sources', $properties ); 292 $this->assertSame( $metadata['sizes'][ $size_name ]['sources'], $properties['sources'] ); 293 } 294 } 295 296 /** 297 * Restore the sources array from the backup when an image is edited 298 * 299 * @ticket 55443 300 */ 301 public function test_restore_the_sources_array_from_the_backup_when_an_image_is_edited() { 302 require_once ABSPATH . 'wp-admin/includes/image-edit.php'; 303 304 $attachment_id = self::factory()->attachment->create_upload_object( DIR_TESTDATA . '/images/canola.jpg' ); 305 306 $metadata = wp_get_attachment_metadata( $attachment_id ); 307 308 $_REQUEST['action'] = 'image-editor'; 309 $_REQUEST['context'] = 'edit-attachment'; 310 $_REQUEST['postid'] = $attachment_id; 311 $_REQUEST['target'] = 'all'; 312 $_REQUEST['do'] = 'save'; 313 $_REQUEST['history'] = '[{"r":-90}]'; 314 315 $ret = wp_save_image( $attachment_id ); 316 317 $backup_sources = get_post_meta( $attachment_id, '_wp_attachment_backup_sources', true ); 318 319 $this->assertArrayHasKey( 'full-orig', $backup_sources ); 320 $this->assertIsArray( $backup_sources['full-orig'] ); 321 $this->assertSame( $metadata['sources'], $backup_sources['full-orig'] ); 322 323 wp_restore_image( $attachment_id ); 324 325 $metadata = wp_get_attachment_metadata( $attachment_id ); 326 $updated_backup_sources = get_post_meta( $attachment_id, '_wp_attachment_backup_sources', true ); 327 328 $this->assertSame( $backup_sources['full-orig'], $metadata['sources'] ); 329 $this->assertNotSame( $backup_sources, $updated_backup_sources ); 330 $this->assertCount( 1, $backup_sources ); 331 $this->assertCount( 2, $updated_backup_sources ); 332 333 $backup_sizes = get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true ); 334 foreach ( $backup_sizes as $size_name => $properties ) { 335 // We are only interested in the original filenames to be compared against the backup and restored values. 336 if ( false === strpos( $size_name, '-orig' ) ) { 337 continue; 338 } 339 340 $size_name = str_replace( '-orig', '', $size_name ); 341 // Full name is verified above. 342 if ( 'full' === $size_name ) { 343 continue; 344 } 345 346 $this->assertArrayHasKey( $size_name, $metadata['sizes'] ); 347 $this->assertArrayHasKey( 'sources', $metadata['sizes'][ $size_name ] ); 348 $this->assertSame( $properties['sources'], $metadata['sizes'][ $size_name ]['sources'] ); 349 } 350 } 351 251 352 /** 252 353 * Tests wp_save_image_file() and mime types. 253 354 *