ckeditor / ckeditor5

Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.
https://ckeditor.com/ckeditor-5
Other
9.36k stars 3.68k forks source link

How to access images from secured back-end (e.g. access token) #11745

Open pamapa opened 2 years ago

pamapa commented 2 years ago

📝 Provide a description of requested docs changes

What is the purpose and what should be changed? Would be nice to have a documentation about the correct way to view images, which need additional authorization to access. For uploading images there is already support for this (e.g. https://ckeditor.com/docs/ckeditor5/latest/features/image-upload/simple-upload-adapter.html). When uploading images it is possible to add e.g. an authorization header containing the access token. How to achieve the same when requesting/viewing images?

I assume to achieve the same with requesting/viewing images, we need to implement an DowncastDispatcher on attribute:src (https://ckeditor.com/docs/ckeditor5/latest/framework/guides/deep-dive/conversion/conversion-preserving-custom-content.html). In this custom DowncastDispatcher we either append the token in the URL (tok=123, ugly solution) or fetch the image including authorization header and append the received blob via createObjectURL. I currently face the problem, that the second approach needs an async fetch, when the editor is initially loaded, this image is not showed, but as soon as the editor text gets touched it shows up. I guess we need to fire a special event?

Would be really nice to have this feature documented, as we are not the only one which needs that feature.

pamapa commented 2 years ago

If needed by others, we are using this something like this:

function InjectAccessTokenPlugin(editor: ClassicEditor) {
    const re = new RegExp(`${YOUR_API_URL}.+`);
    // https://ckeditor.com/docs/ckeditor5/latest/framework/guides/deep-dive/conversion/helpers/downcast.html
    // https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_conversion_conversion-Conversion.html
    editor.conversion.for("editingDowncast")
        .add((dispatcher: DowncastDispatcher) => dispatcher.on<`attribute:src${string}`>("attribute:src", (event, data, conversionApi) => {
            if (event.name !== "attribute:src:imageInline" && event.name !== "attribute:src:imageBlock") {
                return;
            }

            if (!data.attributeKey) {
                return;
            }

            conversionApi.consumable.consume(data.item, event.name);

            const viewWriter = conversionApi.writer;
            const viewElement = conversionApi.mapper.toViewElement(data.item);

            if (!viewElement) {
                console.warn("toViewElement failed", data.item);
                return;
            }

            let img = viewElement;
            // console.log("viewElement", viewElement);
            if (viewElement.name !== "img") {
                // imageBlock: <figure><img></figure>
                // imageInline: <span><img></span>
                const tmp = viewElement.getChild(0);
                if (!tmp || !tmp.is("element") || tmp.name !== "img") {
                    console.warn("figure/span has no img", viewElement, tmp);
                    return;
                }
                img = tmp;
            }

            if (data.attributeNewValue && typeof data.attributeNewValue === "string") {
                let src: string;
                if (re.exec(data.attributeNewValue)) {
                    // Fetching is async, the display is not instant. It displays the image as soon as you click on the editor
                    // custom_fetch: fetch with access token (bearer) in header and returns blob
                    custom_fetch(data.attributeNewValue, { method: "GET" }).then((response) => {
                        src = URL.createObjectURL(response.blob);
                        refImageSrc.current.push(src);
                        // view.change to update ckeditor5
                        // https://github.com/ckeditor/ckeditor5/blob/master/packages/ckeditor5-image/src/imageupload/imageuploadprogress.js
                        editor.editing.view.change(() => {
                            viewWriter.setAttribute(data.attributeKey || "", src, img);
                        });
                    }).catch((e) => {
                        console.error(e);
                    });
                } else {
                    console.log("src is no api attachment url", data.attributeNewValue, YOUR_API_URL);
                    src = data.attributeNewValue;
                }
            } else {
                viewWriter.removeAttribute(data.attributeKey, img);
            }

            // console.log("downcastImage end", img);
        }));
}
...
<CKEditor
    ...
    config={{
        extraPlugins: [
            InjectAccessTokenPlugin
        ]
    ...
ScottPony commented 1 year ago

Not sure this issue already be resolved but we're struggling it, too. The same, we have a back-end api call during editing downcasting and the result what I see is I can see the Get request has been send and got the 200 OK reply, but the image still blank but got show when I click it. @pamapa just wondering if you got any way to workaround this or some useful solution, please update here, that would be very helpful, thx!

pamapa commented 1 year ago

@ScottPony we are still using the InjectAccessTokenPlugin, which is posted above, works like a charm...

jovialhub commented 7 months ago

@pamapa do you mind if you can share the update sample code in typescript and how to use it?