Cropped images are not shown / generated

Created on 23 January 2024, 11 months ago
Updated 26 January 2024, 11 months ago

Problem/Motivation

Cropped images are not generated / shown

Steps to reproduce

- Have: Drupal 10.2.2 with drupal/crop_image:2.0.2, drupal/media_contextual_crop:2.0.3, drupal/media_contextual_crop_field_formatter:2.0.2, drupal/media_contextual_crop_fp_adapter:2.0.0, drupal/media_contextual_crop_iwc_adapter:dev-2.0.x
- Create a '16:9' Crop type
- Create a 'Cropped - 16:9' Image Style that uses the 16:9 Crop type
- Add a 'Media with contextual modifications' field to a Node type referencing the Image media type
- Set Form Display to Media Library Extra for that field. Form mode: default
- Edit the Image media type: Form Display: Default: Use the Contextual Crop Image widget
- Set Manage Display of the 'Media with contextual modifications' field to Rendered Entity: Default
- Edit the Image media type: Manage Display: Default: Use Contextual Crop Image. Image style: Cropped - 16:9
- Create / Edit a Node
- Observe that you can select an image from the Media Library
- Observe that you can click the edit button
- Observe that you see a working cropping widget and you can adjust / set the crop
- Save the node
- The crop is not displayed
- In sites/default/files there is no 'contextual' folder. There is no generated cropped image to be found anywhere
- Inspect the URL of the missing / broken image: /sites/default/files/contextual/styles/crop_16_9/279/public/uploads/crop-duplicate-1-for-adobestock_599962628%20%281%29.jpeg?itok=jjTbP3cM
- Manually opening that url into a new window redirects to: https://mywebsite.com/nl/sites/default/files/contextual/styles/crop_16_9/279/public?itok=jjTbP3cM which is a 404 page not found

Proposed resolution

I observe that in media_contextual_crop/src/PathProcessor/PathProcessorImageStyles.php inside the processInbound function 'file' and 'contextual_crop' are set to the request object:

      $request->query->set('file', $file);
      $request->query->set('contextual_crop', $context);

But I see that in media_contextual_crop/src/Controller/ContextualImageStyleDownloadController.php inside the process function, those are missing from that Request object. I can see this if I add a line at line 50 like: dpm($request);.
If, as a test, I manually add these parameters here to the Request like this:

    $request->query->set('file', 'uploads/crop-duplicate-1-for-adobestock_599962628 (1).jpeg');
    $request->query->set('contextual_crop', '279');

Then I see a working cropped image. And then I see that the sites/default/files/contextual folder now exists, and a cropped image is placed inside that folder: sites/default/files/contextual/styles/crop_hero/279/public/uploads/crop-duplicate-1-for-adobestock_599962628 (1).jpeg

So the problem seems to boil down to missing file and contextual crop query parameters inside the process() function.

Remaining tasks

Fix it.

🐛 Bug report
Status

Closed: works as designed

Version

2.0

Component

Code

Created by

🇧🇪Belgium flyke

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • Issue created by @flyke
  • 🇫🇷France DrDam

    It seems the bug is caused by the end of the image name " (1)" which are encoded to "%20%281%29".

  • 🇧🇪Belgium flyke

    Yes, If I upload a new image to the Media library with a simple filename like 'Naamloos.png' and use that, still the same problem.
    Broken image src = https://mywebsite.com/sites/default/files/contextual/styles/crop_hero/public/uploads/Naamloos.png?itok=zbqO4E0b
    manually opening that in browser redirects to: https://mywebsite.com/nl/sites/default/files/contextual/styles/crop_hero/public/uploads?itok=zbqO4E0b but thats a 404 page not found. No image is generated. There is a sites/default/files/contextual folder now (from the previous test where I forced the parameters insite the Controller) but its empty.

  • 🇫🇷France DrDam

    You have a multilanguage site ? (i see the /nl/ )

  • 🇧🇪Belgium flyke

    Yes, it is a multilingual website.

  • 🇫🇷France DrDam

    Can you confirm me Images URL ? (as they are written in sources)

    because :

    https://mywebsite.com/sites/default/files/contextual/styles/crop_hero/pu...

    are broken due the fact there is no "contextual id" in URL.
    A good URL would be :

    https://mywebsite.com/sites/default/files/contextual/styles/crop_hero/[I...

    => if it's the case, the bug are in the formater

    In the first message, the error may are in the PathProcessor ...

    so i'm confused...

  • 🇧🇪Belgium flyke

    I can confirm that the Image URLs I pasted here are real and unmodified exept for the 'mywebsite.com' part.
    If you check to proposed resolution part of the issue, you will see that I already noticed that 'file' and 'contextual_crop' query parameters are missing inside the process() function, and that if I manually set something there for those parameters, then everything works.
    So the problem is that the 'file' and 'contextual_crop' parameters are missing inside the process() function. They are available inside the processInbound() function, but they are missing from the process() function of the Controller.

  • 🇧🇪Belgium flyke

    I think images might make it clearer.

  • 🇧🇪Belgium flyke

    Extra screenshots.

  • 🇫🇷France DrDam

    Ok, I does not understand why the parameters in the request not passe to the controller...

    Can you make a dsm on other parameters of the controller ?

  • 🇧🇪Belgium flyke

    Here you go.

  • 🇧🇪Belgium flyke

    More info from that first request object if you need it.

  • 🇫🇷France DrDam

    That seem logic.

    So, in the PathProcessorImageStyles, can you put some dsm :

        // Get the image style, scheme and path.
        if (substr_count($rest, '/') >= 4) {
    => dsm($rest);
          [$style_separator, $image_style, $context, $scheme, $file] = explode('/', $rest, 5);
    => dsm([$style_separator, $image_style, $context, $scheme, $file]);
          // Set the file & context as query parameter.
          $request->query->set('file', $file);
          $request->query->set('contextual_crop', $context);
    => dsm($path_prefix . $style_separator . '/' . $image_style . '/' . $context . '/' . $scheme);
          return $path_prefix . $style_separator . '/' . $image_style . '/' . $context . '/' . $scheme;
        }
    
  • 🇧🇪Belgium flyke

    Here you go.

  • 🇧🇪Belgium flyke

    I refreshed the page, and then you see the expected number of output / dpms. That is probably less confusing.

  • 🇫🇷France DrDam

    There is the problem, the "rest" url will be "styles/[style_name]/[context_id]/[scheme]/[file path] and in your case, the context_id are absent.

    can you add another dsm at the line 33

     else {
          return $path;
        }
    => dsm($path)
        // Strip out path prefix.
        $rest = preg_replace('|^' . preg_quote($path_prefix, '|') . '|', '', $path);
    
  • 🇧🇪Belgium flyke

    Here you go.

  • 🇫🇷France DrDam

    can you confirme that is the URL in the HTML source ?
    If it's the case, the error are in the URL generation

  • 🇧🇪Belgium flyke

    Yes, looks the same to me. Where might the URL generation be wrong ? any way to trouble shoot that further ?

  • 🇧🇪Belgium flyke

    Testing around, I find this. Will try to investigate more.

  • 🇧🇪Belgium flyke

    It seems that inside generateContextualizedImage() function, $plugin->saveCrop returns NULL.

  • 🇫🇷France DrDam

    So, can you add dsm in Drupal\media_contextual_crop_iwc_adapter\Plugin\MediaContextualCrop/ImageWidgetCrop::save_crop

        foreach ($style_dependencies as $dep) {
          if (strstr($dep[0], 'crop.type')) {
            $croptype = str_replace('crop.type.', '', $dep[0]);
          }
        }
    => dsm($croptype);
    => dsm($crop_settings['crop_wrapper']);
        // Filter to the concerned crop setting.
        foreach ($crop_settings['crop_wrapper'] as $crop_type => $container) {
          // Get Crop value.
          if ($croptype != $crop_type) {
            continue;
          }
    
  • 🇧🇪Belgium flyke

    I will in a moment. I am now debugging media_contextual_crop_iwc_adapter/src/Plugin/MediaContextualCrop/ImageWidgetCrop.php.
    I can see there that $croptype gets the value 'hero' (which is indeed the applied crop style).
    But inside $crop_settings['crop_wrapper'], there is only the '16_9' crop type, which is the first of the available crop types, but not the correct one.
    So this part of the code:

          if ($croptype != $crop_type) {
            continue;
          }

    never yields a result, because 'hero' is never equal to '16_9'.
    That causes the code to never retrieve or create a crop, and thus returning NULL instead of a crop ID.

  • 🇧🇪Belgium flyke

    added screenshot with arrow in image 1 for the part that we want to reach but never reach.

  • 🇫🇷France DrDam

    In fact, the $crop_settings['crop_wrapper'] contain only the 16_9 and no hero here. (see #21)

    ok, so go to the media_contextual_crop.mode in the "template_preprocess_image_contextual_formatter" function, Line 241

    function template_preprocess_image_contextual_formatter(&$variables) {
      \Drupal::moduleHandler()->loadInclude('image', 'field.inc');
      template_preprocess_image_formatter($variables);
    
    => dsm($variables['item'])
    
      // Find the competent plugin to process this image.
      $use_case_plugin_manager = \Drupal::service('plugin.manager.media_contextual_crop_use_case');
      /** @var \Drupal\media_contextual_crop\MediaContextualCropUseCasePluginBase $use_case_plugin */
      $use_case_plugin = $use_case_plugin_manager->findCompetent($variables['item']);
      

    Sorry for the "debuging method"

  • 🇧🇪Belgium flyke

    No problem at all, I am actually glad I can be of assistance and that you are willing to help troubleshoot this.

  • 🇫🇷France DrDam

    Ok, and in the dsm($variables['item']['values']['image_crop']['crop_wrapper'])

  • 🇧🇪Belgium flyke

    And here is the more detailed output from dpm($variables['item']->image_crop);

  • 🇫🇷France DrDam

    And if you modifiy this crop, and resave the content ? the missing value are added ? (I imagine not ... )

  • 🇫🇷France DrDam

    The bug can came from Drupal\media_contextual_crop_iwc_adapter\Plugin\MediaContextualCrop\ImageWidgetCrop::processFieldData

    Can you add a dsm Line 114 & 158

      public static function processFieldData($field_data) {
    => dsm($field_data)
        // Filter to the concerned crop setting.
        foreach ($field_data['crop_wrapper'] as $crop_type => $container) {
          // Get Crop value.
          $crop_values = $container['crop_container']['values'];
          if ($crop_values['crop_applied'] == 0) {
            unset($field_data['crop_wrapper'][$crop_type]);
          }
        }
    
        if (count($field_data['crop_wrapper']) == 0) {
          return NULL;
        }
        else {
    =>dsm($field_data)
          return $field_data;
        }
    
      }
  • 🇧🇪Belgium flyke

    Here you go.

  • 🇧🇪Belgium flyke

    I offer you my apologies, It seems that I have in all that testing switched from applying a hero crop to actually applying a 16_9 crop.
    So the crop_applied = 1 is correct for the 16_9 crop at the moment.

  • 🇫🇷France DrDam

    Can you test, to be sure the crop is applied. I don't know how IWC really work...

    And, if it's the case, an image must be processed ... even if no crop are provided...

  • 🇧🇪Belgium flyke

    I can edit the image, set a crop, save the page.
    The image is not shown as we already saw in numerous previous screenshots.
    I can then edit the page and the image again, and I can see while editing that the crop is in exactly the same place as when I previously saved it. So on the Edit side, eveything works ?

    But on the page itself, the image is not working because in media_contextual_crop_iwc_adapter/src/Plugin/MediaContextualCrop/ImageWidgetCrop.php in saveCrop it compare $croptype ('hero') to $crop_type (16_9);
    Maybe because of some old crop data or something ? Can I clear all existing crops somehow to test this ?

  • 🇫🇷France DrDam

    now, in Drupal\media_contextual_crop_iwc_adapter\Plugin\MediaContextualCrop\ImageWidgetCrop::processFieldData

    public static function processFieldData($field_data) {

    // Filter to the concerned crop setting.
    foreach ($field_data['crop_wrapper'] as $crop_type => $container) {
    // Get Crop value.
    $crop_values = $container['crop_container']['values'];
    =>dsm($crop_values)
    if ($crop_values['crop_applied'] == 0) {
    unset($field_data['crop_wrapper'][$crop_type]);
    }
    }

    if (count($field_data['crop_wrapper']) == 0) {
    return NULL;
    }
    else {

    return $field_data;
    }

    }

    the "crop_values['crop_applied']" for the "hero" crop still have 0 as value ?

  • 🇧🇪Belgium flyke

    I found the reason why it did not work, where the 'hero' comes from and so also how to fix it.
    I am sorry for your time. The frustrating thing about current implementation of cropping is that while you can select multiple possible cropping types on the Form Display, you are still required to set a single crop image style in the Manage display settings of the 'Contextual Crop Image' widget.
    And apparently the crop I was trying to apply did not match the crop image style of the Manage display settings of the 'Contextual Crop Image' widget.
    So everything works now, cropped image is generated and visible. A thousand times thank you for your time.

    I have a way around this problem in other projects, I just removed it because it did no longer apply for Drupal 10. But I will try to refactor it for D10.
    - Create a new 'Automatic crop' image style. It is empty without any operations.
    - use preprocessing for this image style, get the crop type and hardwire a corresponding crop image style for each crop type
    - apply that image style. this means that applying 'automatic_crop' image style for a 16_9 crop will actually apply the '16_9_crop' image style, while applying 'automatic_crop' image style for a 'hero' crop will actually apply the 'hero_crop' image style.

  • 🇫🇷France DrDam

    No worry, there is this kinde of "issues" that show there really have something broken.

    The frustrating thing about current implementation of cropping is that while you can select multiple possible cropping types on the Form Display, you are still required to set a single crop image style in the Manage display settings of the 'Contextual Crop Image' widget.

    I don't understand the use_case here ...

    In the media, you have a view_mode, and for the field_image you want have multiple style to be applied on the image ? Did you tried to use a Responsive_Image_Style instead of an "image_style" ?

  • Status changed to Closed: works as designed 11 months ago
  • 🇧🇪Belgium flyke

    Hi DrDam: Please look at the screenshot about what the use case is:
    - User can apply a 16:9 crop
    - OR user can apply a 16:9 small image crop
    - OR user can apply a 80:39 crop
    - OR user can aaply a Hero crop
    - OR user can apply a square crop
    BUT
    only when het applies the 16:9 crop it will work, because that is the image style that is selected for the Contextual Crop Image in Manage Display. It is that fact that you have to choose an image style there that screws up the possibility of giving the user the choice of what crop type he applies to the image.

  • 🇫🇷France DrDam

    It is that fact that you have to choose an image style there that screws up the possibility of giving the user the choice of what crop type he applies to the image.

    If I understood correctly, you use CROP to impose some "image formats" (and the user is free to select the area), but the user "in addition" has the ability to choose which format he wants. want for its image (from the node form)?

    Something like this module but applied to media with crop ?

  • 🇧🇪Belgium flyke

    Hi, what I mean is: make the screenshot 'multiple possible crops.png' work, so user can choose any of the crops on the left side of that screenshot. But thats not possible because of screenshot 'manage display - set contextual crop image style.png' because we are forced to set a crop image style there. In this example, the 'Crop 16:9' image style is selected.

    Only if the user chooses to use the 16:9 crop it will work because of that image style.
    But if the user chooses to use any other crop (see the left side with possible crop options available to him), the image will be broken because it does not match that 'Crop 16:9' image style.

    Therefor, I would like that that the manage display - Contextual Crop Image - Image style option is removed completely. So we dont need to set it. Instead the code from the Contextual Crop Image widget could figer out which image style to apply based on the selected crop type.

  • 🇫🇷France DrDam

    OK,

    The scenario you want for the use case :
    Step 1 : Insert Media in content
    Step 2 : crop the media "what ever you want"
    Step 3 : the image are display "as she is croped"

    In fact, you don't even care about the type of crop : "display image as the user crop her"

  • 🇧🇪Belgium flyke

    Yes, I think thats what I mean :-). So If the user crops a Media with a 1:1 crop or a 16:9 crop or a 4:4 crop, It will work and the crop will be generated and displayed without me having to bother to set an image style in the Manage Display.
    Of course, If I have a 1:1 crop type, a 1:1 crop image style needs to exist. And if I have a 16:9 crop type, a 16:9 image style needs to exist. But I should not have to explicitly set a crop image style in Manage Display. It should automatically shoose the correct one.

  • 🇧🇪Belgium flyke

    Sorry to bother again, Its not working on a fresh try :-(

    I tried to crop from a clean start, So I deleted all existing crops:
    drush php-eval '$crops = \Drupal::entityTypeManager()->getStorage("crop")->loadMultiple(); foreach ($crops as $crop) { $crop->delete(); }'

    I went to sites/default/files and deleted all cropped and crop duplicate images I could find.

    I deleted the existing image blocks. I added 2 image blocks next to each other, both showing the same Media image, no cropping applied.
    See 01 - before cropping.png

    I then edited image 1 and added cropping to that image, see:
    02 - cropping only 1 image.png

    The cropped image is broken unfortunatly, see image:
    03 - broken image after cropping.png

    But, the image src now does contain a contextual id I think (289): https://mywebsite.com/sites/default/files/contextual/styles/crop_16_9/28...

    Also, it passes the if ($croptype != $crop_type) { statement (because '16_9' == '16_9') which is also good.
    A Crop entity is created and the function does return a valid crop ID.

    I can DPM the Crop entity, see 04 - crop entity.png

    So I think more is working than at the beginning of this issue, but somehow still no working image on this fresh try.

  • Status changed to Active 11 months ago
  • 🇫🇷France DrDam

    Did you have any error in drupal watchdog ?

    The URL in 03 are correct, so can you make a dsm for parameters of MediaContextualCropService::createContextualizedDerivativePath

  • 🇧🇪Belgium flyke

    I will look further into this tomorrow if I find the time, new urgent stuff for other clients have came up.

    But in the meantime I created a fix for the process function for when the necessary request query parameters are missing:

      public function process(Request $request, $context, ImageStyleInterface $image_style, $scheme) {
    
        // Set 'contextual_crop' parameter if its missing.
        if (is_null($request->query->get('contextual_crop')) && is_numeric($context)) {
          $request->query->set('contextual_crop', $context);
        }
    
        // Set 'file' parameter if its missing.
        if (is_null($request->query->get('file')) && is_numeric($context)) {
          // Load Crop entity.
          $crop = \Drupal::service('entity_type.manager')->getStorage('crop')->load($context);
          // Get value for the file parameter.
          $crop_uri = $crop->uri->value;
          $crop_uri = str_replace($scheme . '://', '', $crop_uri);
          $request->query->set('file', $crop_uri);
        }
        
        $target = $request->query->get('file');
        ...
  • 🇧🇪Belgium flyke

    It might not be pretty code but it's working for now with this code so I can focus on other urgent assignments.
    I do hope to find the actual cause and I do hope I find the time to check MediaContextualCropService::createContextualizedDerivativePath for you.
    Will report back if I do.

  • 🇫🇷France DrDam

    In fact, you are right, if I have the context, the file are not mandatory...

    • 87da6991 committed on 2.0.x
      Q&D fix for #3416473
      
    • cddba003 committed on 2.0.x
      Cleaner fix for #3416473
      
  • Status changed to Closed: works as designed 11 months ago
  • 🇫🇷France DrDam

    Released in 2.0.4

Production build 0.71.5 2024