ckeditor5 icon indicating copy to clipboard operation
ckeditor5 copied to clipboard

Custom Adapter for upload files

Open felek000 opened this issue 4 years ago • 5 comments

Provide a description of the task

I want to upload custom adapter for handle upload files.

function ulpoadAdapterDrop(editor) {
    editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
        return new UploadDrop(loader);
    };
}

And it fires when i upload image files. Now i want to either edit my adapter to handle example pdf or create custom adapter. Still in my UploadDrop adapter events only works when i upload images.

export default class UploadDrop {
    constructor(loader) {
        // The file loader instance to use during the upload.
        this.loader = loader;
    }

    // Starts the upload process.
    upload() {
        console.log('aaaa', this.loader);

How can i allow adapter works also in other files types ?

Also i try solution to return other adapter. like found on github:

const fileName = loader.file.name;

    if ( fileName.endsWith( '.mov' ) ) {
        return new FileUploadAdapter( ... );
    } else {
        return new GenericUploadAdapter( ... );
    }

but loader.file is a promise so that will no work. Any suggestions how to handle that ?

What steps should be taken to fulfill the task?

📃 Other details

  • Browser: chrome
  • OS: windows
  • CKEditor version: 5
  • Installed CKEditor plugins: …

felek000 avatar Dec 18 '19 10:12 felek000

It seems that we missed this use case somehow, or something changed in the API which makes solving this easily harder now.

What I'd do now is implementing an adapter class which will serve as a proxy to two other adapters. It seems to be the simplest option right now.

But it's a valid ticket – we should at least document how to do this.

Reinmar avatar Jan 14 '20 10:01 Reinmar

I don't know if is valid solution. in ClassicEditor.builtinPlugins = [dropEvent]

function dropEvent(editor) {
    editor.editing.view.document.on('drop',async (evt, data) => {
        // Stop execute next callbacks.
        evt.stop();
        console.log('dropppppp',data);
        /**@TODO API*/
       
        const content = `<a href=""/>link file</a>`;
        const viewFragment = editor.data.processor.toView(content);
        const modelFragment = editor.data.toModel(viewFragment);

        editor.model.insertContent(modelFragment, editor.model.document.selection);

        // Stop the default event action.
        data.preventDefault();
    }, {priority: 'high'});

    editor.editing.view.document.on('dragover', (evt, data) => {
        evt.stop();
        data.preventDefault();
    }, {priority: 'high'});
}

felek000 avatar Jan 14 '20 13:01 felek000

For those interested this is my variation of @felek000's solution above, it uploads the file and inserts an anchor tag if the file is not an image (e.g. PDF), otherwise it just uses whatever file adapter you are currently using with CKeditor, in my case the Simple Upload Adapter for images.

function InsertFile(editor) {
  editor.editing.view.document.on(
    "drop",
    async (event, data) => {
      if (
        data.dataTransfer.files &&
        !data.dataTransfer.files[0].type.includes("image")
      ) {
        event.stop();
        data.preventDefault();
        const file = data.dataTransfer.files[0];
        const form = new FormData();
        form.append("Content-Type", file.type);
        form.append("upload", file);
        let httpRequest;
        if (window.XMLHttpRequest) {
          httpRequest = new XMLHttpRequest();
        } else if (window.ActiveXObject) {
          httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
        }
        httpRequest.upload.onprogress = (evt) => {
          let progress = undefined;
          progress = (evt.loaded / evt.total) * 100;
          // Update progress UI...
        };
        httpRequest.open("POST", "/images", true);
        httpRequest.onload = () => {
          if (httpRequest.status === 200) {
            const body = JSON.parse(httpRequest.responseText);
            const content = `<a href="${body.url}"/>${data.dataTransfer.files[0].name}</a>`;
            const viewFragment = editor.data.processor.toView(content);
            const modelFragment = editor.data.toModel(viewFragment);
            editor.model.insertContent(
              modelFragment,
              editor.model.document.selection
            );
          } else {
            alert(
              "Something went wrong while uploading your file, please check your \
              network connection and try again."
            );
            console.error(
              "Request failed.  Returned status of " + httpRequest.status
            );
          }
        };
        httpRequest.send(form);
      }
    },
    { priority: "high" }
  );

  editor.editing.view.document.on(
    "dragover",
    (event, data) => {
      event.stop();
      data.preventDefault();
    },
    { priority: "high" }
  );
}

SimonBrazell avatar Apr 28 '21 06:04 SimonBrazell

A more complete solution, my project includes bootstrap so I've made use of some of components from it, but those can easily be replaced with something else.

import Plugin from "@ckeditor/ckeditor5-core/src/plugin";
import ButtonView from "@ckeditor/ckeditor5-ui/src/button/buttonview";
import icon from "@ckeditor/ckeditor5-ckfinder/theme/icons/browse-files.svg";

const progress = `
<div class="modal fade" id="progress-modal" tabindex="-1" role="dialog">
  <div class="modal-dialog modal-lg modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title">Uploading file, please wait...</h4>
      </div>
      <div class="modal-body">
        <div class="progress" style="width: 100%; max-width: 100%">
          <div
            class="progress-bar progress-bar-success progress-bar-striped active"
            role="progressbar"
            aria-valuenow="0"
            aria-valuemin="0"
            aria-valuemax="100"
            style="width: 0%"
          ></div>
        </div>
      </div>
    </div>
  </div>
</div>`;

export default class InsertFile extends Plugin {
  init() {
    const editor = this.editor;
    editor.editing.view.document.on(
      "drop",
      async (event, data) => {
        if (
          data.dataTransfer.files &&
          !data.dataTransfer.files[0].type.includes("image")
        ) {
          event.stop();
          data.preventDefault();
          this.insert(data.dataTransfer.files[0], editor);
        }
      },
      { priority: "high" }
    );

    editor.editing.view.document.on(
      "dragover",
      (event, data) => {
        event.stop();
        data.preventDefault();
      },
      { priority: "high" }
    );

    editor.ui.componentFactory.add("insertFile", (locale) => {
      const inputElement = document.createElement("input");
      inputElement.type = "file";
      inputElement.accept =
        ".doc,.docx,.pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf";
      inputElement.addEventListener("change", (event) => {
        this.insert(event.target.files[0], editor);
      });

      const view = new ButtonView(locale);

      view.set({
        label: "Insert file",
        icon: icon,
        tooltip: true,
      });

      view.on("execute", () => {
        inputElement.dispatchEvent(new MouseEvent("click"));
      });

      return view;
    });
  }

  insert(file, editor) {
    if (file) {
      $("body").append(progress);
      $("#progress-modal").modal({
        backdrop: "static",
        keyboard: false,
      });
      const form = new FormData();
      form.append("Content-Type", file.type);
      form.append("upload", file);
      let httpRequest;
      if (window.XMLHttpRequest) {
        httpRequest = new XMLHttpRequest();
      } else if (window.ActiveXObject) {
        httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
      }
      httpRequest.upload.onprogress = (ev) => {
        let progress = undefined;
        progress = (ev.loaded / ev.total) * 100;
        $(".progress-bar")
          .css("width", progress + "%")
          .attr("aria-valuenow", progress);
      };
      httpRequest.open("POST", "/images", true);
      httpRequest.onload = () => {
        if (httpRequest.status === 200) {
          const body = JSON.parse(httpRequest.responseText);
          const content = `<a href="${body.url}"/>${file.name}</a>`;
          const viewFragment = editor.data.processor.toView(content);
          const modelFragment = editor.data.toModel(viewFragment);
          editor.model.insertContent(
            modelFragment,
            editor.model.document.selection
          );
          $("#progress-modal").modal("hide");
        } else {
          $("#progress-modal").modal("hide");
          alert(
            `Something went wrong while uploading your file, please check your network connection and try again.
          (${httpRequest.status} ${httpRequest.statusText})`
          );
          console.error(
            "Request failed.  Returned status of " + httpRequest.status,
            httpRequest
          );
        }
      };
      httpRequest.send(form);
    }
  }
}

SimonBrazell avatar Apr 29 '21 01:04 SimonBrazell

Is there any progress now? Can I customize multiple upload adapters?

zgpnuaa avatar Sep 19 '22 03:09 zgpnuaa