Problem/Motivation

The module does its job perfect. It would be great if it also did the core work ;)

Current solution from #2644468: Multiple image upload breaks image dimensions:

+++ b/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php
@@ -212,6 +212,12 @@ public static function process($element, FormStateInterface $form_state, $form)
+      // In the case of a multiple upload, do not retrieve the image dimensions
+      // as it can lead to all images having the same dimensions. The correct
+      // dimensions will be calculated in ImageItem::preSave().
+      elseif (count($element['#files']) > 1) {
+        $variables['width'] = $variables['height'] = NULL;
+      }

I spent a chunk of time looking for something better and did not find it. Because the implementation of support multiple upload is very clever (and controversial). There may also be delays with fixing in the core.

Proposed resolution

To take this fix in this module via decorating of ImageWidget.

Remaining tasks

User interface changes

API changes

Data model changes

CommentFileSizeAuthor
#5 interdiff-2-5.txt5.65 KBAnonymous (not verified)
#5 2962779-5.patch7.59 KBAnonymous (not verified)
#3 multiple-upload-without-rebuildForm.png11.49 KBAnonymous (not verified)
#3 interdiff-2-3.txt2.13 KBAnonymous (not verified)
#3 2962779-3.patch10.26 KBAnonymous (not verified)
#2 2962779-2.patch10.1 KBAnonymous (not verified)

Comments

Anonymous’s picture

vaplas created an issue. See original summary.

Anonymous’s picture

Status: Active » Needs review
StatusFileSize
new10.1 KB

In fact, the decorator prevents the '#default_value' of image field in multiple upload case. But this keeps the width / height of each of the images (yes, with multiple upload all very tricky). This can be verified if you put a breakpoint in ImageItem::preSave(). So there will be no extra costs for getting sizes. Woot!

Added two tests, for checking via BTB/JTB. But surely enough one, at your choice :) (if you like this idea, of course).

Anonymous’s picture

StatusFileSize
new10.26 KB
new2.13 KB
new11.49 KB

Deeper digging...

It turns out the image field widget is being processed twice:

  1. First, for an empty image field (which is present for adding new files).
  2. Second, for uploaded images.

When only one image is uploaded, it is not noticeable. But if we upload multiple images, then initially the empty field will take the data from the first image. And then will use them for all the other images by default.

For example, if we comment next line:

- $form = $this->rebuildForm($form_id, $form_state, $form);
+ //$form = $this->rebuildForm($form_id, $form_state, $form);

And than upload multiple images, we get next result:

All images were not proccesed, but the widget grabbed the first image instead of empty field widget.

Therefore, there is another suggestion, just ignore the standard image processing while uploading multiple images.

fietserwin’s picture

It's an interesting idea to also solve the bug while waiting for core to get the fix committed. However I am not too deep in the cause and current/previous solution of the bug to see if this patch will do what it promises.

Therefore I am willing to commit the patch as is, but hope that I can contact you when issues are reported in the issue queue of this module.

What I will do before committing this patch is review and update the project page. Furthermore, I think that adding the repair part to core will undoubtedly add another "year" before it gets committed, so I think we should strive for the core fix to get committed as is, thus without update hook and refer people to this module for repair, hopefully with an admin message presented by an update hook.

Is this OK for you?

Anonymous’s picture

StatusFileSize
new7.59 KB
new5.65 KB

Thank you @fietserwin for good points!

It seems to me that #3 works more correctly, and I will push this variant to the core tracker until I am convinced.

However, after a series of the critical security fixes for core, #2 variant looks better for the contrib module. Because this variant completely preserves the behavior of the core.

You can ping me on this issue at any time. We can also provide the ability to disable this fix via settings.php + clear cache:
$settings['image_field_repair_disable_fix_2644468'] = TRUE;

I deleted the BTB test, because the JTB works better as a black-box test.

Furthermore, I think that adding the repair part to core will undoubtedly add another "year" before it gets committed, so I think we should strive for the core fix to get committed as is, thus without update hook and refer people to this module for repair, hopefully with an admin message presented by an update hook.

Nice idea! I will propose this to the core issue in the next few days.

Anonymous’s picture

About update project page, I suggest moving to the top of the "Usage" section (to users could immediately understand what to do):

Usage

After installing and enabling the module, it will fix the problem for next uploads of images.

To repair already damaged images, go to Admin -> Configuration -> Media -> Image Field Repair (admin/config/media/image_file_repair/dimensions) and press the "Start" button.

Note: If for some reason you want only repair images, and do not want to fix the bug (for example, you use a special patch or your own solution to fix the problem, or drupal version without this problem), you can disable fix. Just do add next line to /sites/default/settings.php:
$settings['image_field_repair_disable_fix_2644468'] = TRUE;

After having repaired your image fields you can uninstall and remove this module.

fietserwin’s picture

I had a look at the code. It looks all good, the only remark I have is about this line:

+++ b/src/ImageFieldRepairServiceProvider.php
@@ -0,0 +1,27 @@
+    if (!Settings::get('image_field_repair_disable_fix_2644468')) {

I haven't done much D8 programming, but shouldn't we put this in a config yml file of our own, which then can easily be edited by users? If not, how would you change this setting? Should we describe it? I am fine with not creating a UI of our own for this setting, but isn't there something to male it more accessible?

Anonymous’s picture

Yes, I thought about it, too. But to turn off decoration completely, we must intervene at the ServiceProvider::alert() stage. Unfortunately, at this stage the \Drupal::$container is not exists. So, inside of ImageFieldRepairServiceProvider we cannot use something like:

\Drupal::config('image_field_repair.settings')->get('disable_fix_2644468')

I looked at how other contrib modules solved this problem, and found solution only via Settings. Examples:

Also many other modules use settings.php for their tweaks in other places. So looks like this is normal practice.

I tried to describe example of usage it in the end of #6:

add next line to /sites/default/settings.php:
$settings['image_field_repair_disable_fix_2644468'] = TRUE;

Maybe it's even more convenient than own yml file. Not all users are familiar with YML, but everything is editing settings.php (for set trusted_host_patterns, etc).

Of course, we can turn off the fix at a later stage, but still the decoration will remain. Or write custom method for read config. But in general, there should be quite specific reasons why the user will want to stop fixing, so its disable is a bit specific :)

Anonymous’s picture

I forgot to add that after settings.php change, we need to clear the cache too. (for run \Drupal::service('kernel')->invalidateContainer();)

  • fietserwin committed 16fa71f on 8.x-1.x authored by vaplas
    Issue #2962779 by vaplas, fietserwin: Decorate ImageWidget to fix the...
fietserwin’s picture

Status: Needs review » Fixed

Thanks for the explanation. committed.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.