vollyimnetz / crop-thumbnails

"Crop Thumbnails" made it easy to get exacly that specific image-detail you want to show. Crop your images the simple way.
GNU General Public License v3.0
45 stars 20 forks source link

Original file is deleted when cropping an image not yet cropped by WP #95

Closed kurtrank closed 17 hours ago

kurtrank commented 1 week ago

I've gotten reports of some image files breaking, and in trying to reproduce their issue I found that cropping a size that WordPress has not created a separate file for seems to delete the original image. Note that this appears to only be an issue when uploading an image smaller than one of your defined sizes and then cropping one of those larger sizes. I think it doesn't really make sense to crop a larger size (as the "size too small for good crop quality!" warning indicates), but I think it's happening because people are grouping by ratio and cropping all at once.

Custom sizes defined:

function theme_setup() {
    // 3:2
    add_image_size( '3_2_xlarge', 1602, 1068, true );
    add_image_size( '3_2_large', 1026, 684, true );
    add_image_size( '3_2_medium', 768, 512, true );
    add_image_size( '3_2_small', 300, 200, true );
    // ...other sizes
}
add_action( 'after_setup_theme', 'theme_setup' );

When an image is uploaded I guess WordPress only creates separate image files if the image is large enough for each size. In my test I uploaded a 640x640 square image, so it created a new file for 3_2_small and 3_2_medium but not the other two.

When I crop one that doesn't exist (like 3_2_xlarge) or group by same ratio and crop all 3_2, the debug says that it has deleted the original square-3.jpg.

[
  "validated input data",
  {
    "selection": {
      "x": 0,
      "y": 0,
      "x2": 640,
      "y2": 427,
      "w": 640,
      "h": 427
    },
    "sourceImageId": 3331,
    "activeImageSizes": [
      {
        "name": "3_2_xlarge",
        "width": 1602,
        "height": 1068,
        "ratio": 1.5,
        "crop": 1
      }
    ]
  },
  "filename: /Users/me/Local Sites/taco/app/public/wp-content/uploads/2024/09/square-3-1602x1068.jpg",
  "delete old image:square-3.jpg" // <---- original removed
]

I guess this happens because: WP didn't create a separate file so it lists the original as the "file" for all of those sizes, and it looks like whatever is listed there is marked for deletion https://github.com/vollyimnetz/crop-thumbnails/blob/e9e55568ad31f7043173f4261f257c62036b5389/functions/save.php#L92

I am wondering if this is something that can be fixed, or if it's meant to work like this and I need to configure something differently about my custom image sizes? Maybe it should have a check to never delete the original, or only delete if notYetCropped is false.

Full JS-Debug
cropImage:{
  "url": "http://taco.loc/wp-content/uploads/2024/09/square-3.jpg",
  "width": 640,
  "height": 640,
  "gcd": 640,
  "ratio": 1,
  "printRatio": "1:1",
  "image_size": "full"
}
cropData:{
  "options": {
    "debug_js": 1,
    "debug_data": 1
  },
  "sourceImageId": 3331,
  "sourceImage": {
    "full": {
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3.jpg",
      "width": 640,
      "height": 640,
      "gcd": 640,
      "ratio": 1,
      "printRatio": "1:1",
      "image_size": "full"
    },
    "large": {
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3.jpg",
      "width": 640,
      "height": 640,
      "gcd": 640,
      "ratio": 1,
      "printRatio": "1:1",
      "image_size": "large"
    },
    "medium_large": {
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3.jpg",
      "width": 640,
      "height": 640,
      "gcd": 640,
      "ratio": 1,
      "printRatio": "1:1",
      "image_size": "medium_large"
    }
  },
  "sourceImageMeta": {
    "aperture": "0",
    "credit": "",
    "camera": "",
    "caption": "",
    "created_timestamp": "0",
    "copyright": "",
    "focal_length": "0",
    "iso": "0",
    "shutter_speed": "0",
    "title": "",
    "orientation": "0",
    "keywords": []
  },
  "postTypeFilter": null,
  "imageSizes": [
    {
      "name": "thumbnail",
      "nameLabel": "thumbnail",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-150x150.jpg",
      "width": 150,
      "height": 150,
      "gcd": 150,
      "ratio": 1,
      "printRatio": "1:1",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": false
    },
    {
      "name": "post-thumbnail",
      "nameLabel": "post-thumbnail",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-125x125.jpg",
      "width": 125,
      "height": 125,
      "gcd": 125,
      "ratio": 1,
      "printRatio": "1:1",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": false
    },
    {
      "name": "large_square",
      "nameLabel": "large_square",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3.jpg",
      "width": 1024,
      "height": 1024,
      "gcd": 1024,
      "ratio": 1,
      "printRatio": "1:1",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": true
    },
    {
      "name": "medium_large_square",
      "nameLabel": "medium_large_square",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-512x512.jpg",
      "width": 512,
      "height": 512,
      "gcd": 512,
      "ratio": 1,
      "printRatio": "1:1",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": false
    },
    {
      "name": "3_2_xlarge",
      "nameLabel": "3_2_xlarge",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-1602x1068.jpg",
      "width": 1602,
      "height": 1068,
      "gcd": 534,
      "ratio": 1.5,
      "printRatio": "3:2",
      "hideByPostType": false,
      "crop": true,
      "active": true,
      "lowResWarning": false,
      "cacheBreak": 1726771742844,
      "notYetCropped": true
    },
    {
      "name": "3_2_large",
      "nameLabel": "3_2_large",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3.jpg",
      "width": 1026,
      "height": 684,
      "gcd": 342,
      "ratio": 1.5,
      "printRatio": "3:2",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": true
    },
    {
      "name": "3_2_medium",
      "nameLabel": "3_2_medium",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-640x512.jpg",
      "width": 768,
      "height": 512,
      "gcd": 256,
      "ratio": 1.5,
      "printRatio": "3:2",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": false
    },
    {
      "name": "3_2_small",
      "nameLabel": "3_2_small",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-300x200.jpg",
      "width": 300,
      "height": 200,
      "gcd": 100,
      "ratio": 1.5,
      "printRatio": "3:2",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": false
    },
    {
      "name": "2_1_xlarge",
      "nameLabel": "2_1_xlarge",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3.jpg",
      "width": 1600,
      "height": 800,
      "gcd": 800,
      "ratio": 2,
      "printRatio": "2:1",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": true
    },
    {
      "name": "2_1_large",
      "nameLabel": "2_1_large",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-640x512.jpg",
      "width": 1024,
      "height": 512,
      "gcd": 512,
      "ratio": 2,
      "printRatio": "2:1",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": false
    },
    {
      "name": "2_1_medium",
      "nameLabel": "2_1_medium",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-640x384.jpg",
      "width": 768,
      "height": 384,
      "gcd": 384,
      "ratio": 2,
      "printRatio": "2:1",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": false
    },
    {
      "name": "2_1_small",
      "nameLabel": "2_1_small",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-300x150.jpg",
      "width": 300,
      "height": 150,
      "gcd": 150,
      "ratio": 2,
      "printRatio": "2:1",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": false
    },
    {
      "name": "3_1_xlarge",
      "nameLabel": "3_1_xlarge",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-640x534.jpg",
      "width": 1602,
      "height": 534,
      "gcd": 534,
      "ratio": 3,
      "printRatio": "3:1",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": false
    },
    {
      "name": "3_1_large",
      "nameLabel": "3_1_large",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-640x342.jpg",
      "width": 1026,
      "height": 342,
      "gcd": 342,
      "ratio": 3,
      "printRatio": "3:1",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": false
    },
    {
      "name": "3_1_medium",
      "nameLabel": "3_1_medium",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-640x256.jpg",
      "width": 768,
      "height": 256,
      "gcd": 256,
      "ratio": 3,
      "printRatio": "3:1",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": false
    },
    {
      "name": "3_1_small",
      "nameLabel": "3_1_small",
      "url": "http://taco.loc/wp-content/uploads/2024/09/square-3-300x100.jpg",
      "width": 300,
      "height": 100,
      "gcd": 100,
      "ratio": 3,
      "printRatio": "3:1",
      "hideByPostType": false,
      "crop": true,
      "active": false,
      "lowResWarning": false,
      "cacheBreak": 1726771731548,
      "notYetCropped": false
    }
  ],
  "lang": {
    "warningOriginalToSmall": "Warning: the original image is too small to be cropped in good quality with this thumbnail size.",
    "cropDisabled": "Cropping is disabled for this post-type.",
    "waiting": "Please wait until the images are cropped.",
    "rawImage": "Raw",
    "pixel": "pixel",
    "instructions_overlay_text": "Choose an image size.",
    "instructions_header": "Quick Instructions",
    "instructions_step_1": "Step 1: Choose an image-size from the list.",
    "instructions_step_2": "Step 2: Change the selection of the image above.",
    "instructions_step_3": "Step 3: Click on \"Save Crop\".",
    "label_crop": "Save Crop",
    "label_same_ratio_mode": "Images with same ratio",
    "label_same_ratio_mode_nothing": "Do nothing",
    "label_same_ratio_mode_select": "Select together",
    "label_same_ratio_mode_group": "Group together",
    "label_deselect_all": "deselect all",
    "label_large_handles": "use large handles",
    "dimensions": "Dimensions:",
    "ratio": "Ratio:",
    "cropped": "cropped",
    "lowResWarning": "Original image size too small for good crop quality!",
    "notYetCropped": "Not yet cropped by WordPress.",
    "message_image_orientation": "This image has an image orientation value in its exif-metadata. Be aware that this may result in rotatated or mirrored images on safari ipad / iphone.",
    "script_connection_error": "The plugin can not correctly connect to the server.",
    "noPermission": "You are not permitted to crop the thumbnails.",
    "infoNoImageSizesAvailable": "No image sizes for cropping available.",
    "headline_selected_image_sizes": "Selected image sizes"
  },
  "hiddenOnPostType": false
}
Image attachment metadata from database
Array
(
    [width] => 640
    [height] => 640
    [file] => 2024/09/square-3.jpg
    [filesize] => 135100
    [sizes] => Array
        (
            [thumbnail] => Array
                (
                    [file] => square-3-150x150.jpg
                    [width] => 150
                    [height] => 150
                    [mime-type] => image/jpeg
                    [filesize] => 7208
                )

            [medium] => Array
                (
                    [file] => square-3-300x300.jpg
                    [width] => 300
                    [height] => 300
                    [mime-type] => image/jpeg
                    [filesize] => 24250
                )

            [medium_large] => Array
                (
                    [file] => square-3.jpg
                    [width] => 640
                    [height] => 640
                    [mime] => image/jpeg
                )

            [large] => Array
                (
                    [file] => square-3.jpg
                    [width] => 640
                    [height] => 640
                    [mime] => image/jpeg
                )

            [1536x1536] => Array
                (
                    [file] => square-3.jpg
                    [width] => 640
                    [height] => 640
                    [mime] => image/jpeg
                )

            [2048x2048] => Array
                (
                    [file] => square-3.jpg
                    [width] => 640
                    [height] => 640
                    [mime] => image/jpeg
                )

            [post-thumbnail] => Array
                (
                    [file] => square-3-125x125.jpg
                    [width] => 125
                    [height] => 125
                    [mime-type] => image/jpeg
                    [filesize] => 5200
                )

            [large_square] => Array
                (
                    [file] => square-3.jpg
                    [width] => 640
                    [height] => 640
                    [mime] => image/jpeg
                )

            [medium_large_square] => Array
                (
                    [file] => square-3-512x512.jpg
                    [width] => 512
                    [height] => 512
                    [mime-type] => image/jpeg
                    [filesize] => 59815
                )

            [3_2_xlarge] => Array
                (
                    [file] => square-3-1602x1068.jpg
                    [width] => 1602
                    [height] => 1068
                    [mime] => image/jpeg
                    [mime-type] => image/jpeg
                    [cpt_last_cropping_data] => Array
                        (
                            [x] => 0
                            [y] => 0
                            [x2] => 640
                            [y2] => 427
                            [original_width] => 640
                            [original_height] => 640
                        )

                )

            [3_2_large] => Array
                (
                    [file] => square-3.jpg
                    [width] => 640
                    [height] => 640
                    [mime] => image/jpeg
                )

            [3_2_medium] => Array
                (
                    [file] => square-3-640x512.jpg
                    [width] => 640
                    [height] => 512
                    [mime-type] => image/jpeg
                    [filesize] => 115891
                )

            [3_2_small] => Array
                (
                    [file] => square-3-300x200.jpg
                    [width] => 300
                    [height] => 200
                    [mime-type] => image/jpeg
                    [filesize] => 24538
                )

            [2_1_xlarge] => Array
                (
                    [file] => square-3.jpg
                    [width] => 640
                    [height] => 640
                    [mime] => image/jpeg
                )

            [2_1_large] => Array
                (
                    [file] => square-3-640x512.jpg
                    [width] => 640
                    [height] => 512
                    [mime-type] => image/jpeg
                    [filesize] => 115891
                )

            [2_1_medium] => Array
                (
                    [file] => square-3-640x384.jpg
                    [width] => 640
                    [height] => 384
                    [mime-type] => image/jpeg
                    [filesize] => 94218
                )

            [2_1_small] => Array
                (
                    [file] => square-3-300x150.jpg
                    [width] => 300
                    [height] => 150
                    [mime-type] => image/jpeg
                    [filesize] => 19478
                )

            [3_1_xlarge] => Array
                (
                    [file] => square-3-640x534.jpg
                    [width] => 640
                    [height] => 534
                    [mime-type] => image/jpeg
                    [filesize] => 121876
                )

            [3_1_large] => Array
                (
                    [file] => square-3-640x342.jpg
                    [width] => 640
                    [height] => 342
                    [mime-type] => image/jpeg
                    [filesize] => 88802
                )

            [3_1_medium] => Array
                (
                    [file] => square-3-640x256.jpg
                    [width] => 640
                    [height] => 256
                    [mime-type] => image/jpeg
                    [filesize] => 67968
                )

            [3_1_small] => Array
                (
                    [file] => square-3-300x100.jpg
                    [width] => 300
                    [height] => 100
                    [mime-type] => image/jpeg
                    [filesize] => 14358
                )

        )

    [image_meta] => Array
        (
            [aperture] => 0
            [credit] => 
            [camera] => 
            [caption] => 
            [created_timestamp] => 0
            [copyright] => 
            [focal_length] => 0
            [iso] => 0
            [shutter_speed] => 0
            [title] => 
            [orientation] => 0
            [keywords] => Array
                (
                )

        )

)
vollyimnetz commented 3 days ago

@kurtrank thank you for reporting this. I will have a look into it in the next days.

vollyimnetz commented 3 days ago

Hi @kurtrank, sorry but i cant confirm the bug you found.

I tested with the following setup:

  1. Blank, current Wordpress with Plugin Version 1.8.0 and the image_sizes you provided:
    add_action('after_setup_theme', function() {
    add_image_size( '3_2_xlarge', 1602, 1068, true );
    add_image_size( '3_2_large', 1026, 684, true );
    add_image_size( '3_2_medium', 768, 512, true );
    add_image_size( '3_2_small', 300, 200, true );
    });
  2. Uploaded an test-minimal-size.jpg with 640x640 Pixel Dimensions.
  3. CHECK - how many files are in upload folder: its 5 --> as expected
    • test-minimal-size-150x150.jpg (default thumbnail size)
    • test-minimal-size-300x200.jpg
    • test-minimal-size-300x300.jpg
    • test-minimal-size-640x512.jpg
    • test-minimal-size.jpg
  4. I cropped the sizes "3_2_xlarge" and "3_2_large" as they are not already cropped by wordpress. (i also done a separate test with grouping enabled).
  5. CHECK - how many files are in upload folder: its 7 --> as expected
    • test-minimal-size-150x150.jpg (default thumbnail size)
    • test-minimal-size-300x200.jpg
    • test-minimal-size-300x300.jpg
    • test-minimal-size-640x512.jpg
    • test-minimal-size-1026x684.jpg (the new cropped image size)
    • test-minimal-size-1602x1068.jpg (the new cropped image size)
    • test-minimal-size.jpg
  6. CHECK - is the content of the uploaded file (test-minimal-size.jpg) still the same: yes

Can you please disable all other plugins and test again?

For your Question about the default Wordpress behavior: yes wordpress will only create image-sizes if the original file is larger or equal than the image-size.

kurtrank commented 3 days ago

Ah, thanks for your detailed response. I didn't see anyone else who had this issue on here or the wp org plugin support, so I kind of got the feeling it might not be a problem with the plugin itself but I am a little lost. Someone else on my team had tried with other plugins disabled and it was still happening but I can do my own full testing and make sure and try to figure out what is going on.

kurtrank commented 17 hours ago

@vollyimnetz You were right, I did more testing with no plugins and had no issues. I discovered there is another plugin that for some reason on uploading a new item, creates an entry in the metadata in the db for every registered size even if the original image is not large enough to crop. It will just list the original image as the file, which is then marked for deletion as it doesn't match the new generated filename:

[3_2_medium] => Array
    (
        [file] => square-3-640x512.jpg
        [width] => 640
        [height] => 512
        [mime-type] => image/jpeg
        [filesize] => 115891
    )

[3_2_large] => Array
    (
        [file] => square-3.jpg <------- original file
        [width] => 640
        [height] => 640
        [mime] => image/jpeg
    )

[3_2_xlarge] => Array
    (
        [file] => square-3.jpg <------- original file
        [width] => 640
        [height] => 640
        [mime] => image/jpeg
    )

I did not realize that by default WordPress will not create a metadata entry at all for a size if it's not large enough to crop (which makes sense). I am not sure why this other plugin does it this way. I'll have to re-evaluate if the other features of that plugin are worth keeping, but for now I will use the crop_thumbnails_should_delete_old_file filter. There may be an easier way to check if it's an original file but this is what I came up with for now:

add_filter( 'crop_thumbnails_should_delete_old_file', 'my_crop_thumbnails_delete_check', 11, 4 );
function my_crop_thumbnails_delete_check( $should_delete, $image_details, $active_image_size, $current_file_path ) {
    $matches     = array();
    $upload_path = preg_match( '/(uploads|files)\/(.*)\/(.*)/', $current_file_path, $matches );

    if ( $matches[2] ) {
        // path to existing file
        $url = wp_get_upload_dir()['baseurl'] . '/' . $matches[2] . '/' . $image_details['file'];
        // check if original file
        $post_id = attachment_url_to_postid( $url );
    }
    if ( $post_id ) {
        // we have an original file, skip deleting
        $should_delete = false;
    }

    return $should_delete;
}

Seems to solve my problem. Sorry about the trouble!