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
- Use a text format with CKEditor 5 and enable image upload.
- Enable the paste filter with the default filters.
- Copy an image, or content from Word that contains an image.
- 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 namesuploadIdanduploadProcessedonimgelements. - Or only replace
data.contentwhen 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
Comment #2
hanoiiComment #3
star-szrHi @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.