Problem/Motivation

When the paste filter is enabled on a text editor that also has image upload enabled, pasted images are never uploaded.

Symptoms:

  • The image appears in the editor as an empty box.
  • No request is sent to /ckeditor5/upload-image/{editor}.
  • No file is created.
  • The Source button stays disabled, because CKEditor 5 has a pending upload that never completes.

Disabling the paste filter on the text editor makes pasted images upload again.

Tested with Drupal core 10.6.17 (CKEditor 5 47.6) and ckeditor5_paste_filter 1.1.1.

Possibly related core issue: #3447491: CKEditor 5 Paste from Word with "Full HTML": image disappeared when reopening Edit tab

Steps to reproduce

  1. Use a text format with CKEditor 5 and enable image upload.
  2. Enable the paste filter with the default filters.
  3. Copy an image, or content from Word that contains an image.
  4. Paste it into the editor.

Expected: the image is uploaded and displayed.
Actual: an empty image is inserted, nothing is uploaded, and the Source button is disabled.

Cause

The ImageUploadEditing plugin of CKEditor 5 handles pasted images in the inputTransformation event. For each local image it creates an upload loader, empties the src attribute, and sets the attributes uploadId and uploadProcessed on the view element.

The paste filter listens to the same event at normal priority. It serializes data.content with htmlProcessor.toData(), runs the regular expressions, and parses the result with htmlProcessor.toView().

HTML parsing lowercases attribute names, so uploadId becomes uploadid and uploadProcessed becomes uploadprocessed. The upcast converter of the image upload plugin matches on uploadId, so the image is no longer recognized as an upload. The loader was already created, which is why the pending action remains and source editing stays disabled.

Proposed resolution

Options:

  • After toView(), restore the attribute names uploadId and uploadProcessed on img elements.
  • Or only replace data.content when a filter changed the HTML, and restore the attributes in that case.
  • Or skip the string round trip for view elements that carry upload attributes.

Workaround

A small CKEditor 5 plugin that listens to inputTransformation at a priority just below normal and renames the attributes back:

editor.plugins.get('ClipboardPipeline').on(
  'inputTransformation',
  (evt, data) => {
    const writer = new ViewUpcastWriter(editor.editing.view.document);
    const images = Array.from(
      writer.createRangeIn(data.content).getItems(),
    ).filter((item) => item.is('element', 'img'));
    images.forEach((image) => {
      [['uploadid', 'uploadId'], ['uploadprocessed', 'uploadProcessed']]
        .forEach(([lowercase, name]) => {
          if (image.hasAttribute(lowercase)) {
            const value = image.getAttribute(lowercase);
            writer.removeAttribute(lowercase, image);
            writer.setAttribute(name, value, image);
          }
        });
    });
  },
  { priority: -1 },
);

Remaining tasks

  • Confirm on Drupal 11.
  • Decide on the approach.
  • Add test coverage for pasting an image with image upload enabled.

User interface changes

None.

API changes

None.

Data model changes

None.

Comments

hanoii created an issue. See original summary.

hanoii’s picture

Issue summary: View changes
star-szr’s picture

Hi @hanoii, thanks for the report. And apologies for the delayed reply, I have been sick recently.

I may not have time to look into this soon but it seems there is enough information here to dig into. I wonder if changing the priority of our plugin might be worth looking into.