Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

images_upload_handler doesn't work for TinyMCE 6 #464

Open
justinqian42 opened this issue Apr 17, 2024 · 4 comments
Open

images_upload_handler doesn't work for TinyMCE 6 #464

justinqian42 opened this issue Apr 17, 2024 · 4 comments

Comments

@justinqian42
Copy link

As you may know, the feature has changed a bit in version 6.

Kindly check the comment below for details.

Thanks to @rsevs3.

The outcome is -

  1. If using image_upload_url, the code doesn't embed the csrf token in the request headers so the corresponding django view has to be csrf exempted, which is insecure.
  2. If using images_upload_handler, the new const wouldn't be called.

Just some additional information for this, the way that images_upload_handler works is different.

See: https://www.tiny.cloud/docs/tinymce/6/image/#images_upload_handler

In the new example code, example_image_upload_handler is declared as a const, so is not a property of the window object.

See for more info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const#description

I am fairly new to coding and have virtually no JS experience, so I do no know how to rewrite this to make it work. I have played around with putting the example code into init_tinymce.js and modifying to suit, which works, but isn't easily user changeable, so not a good solution. I have spent all day trying to find a nice solution, but it is beyond my skill levels currently.

Originally posted by @rsevs3 in #426 (comment)

@mnoah66
Copy link

mnoah66 commented May 3, 2024

Yeah I cannot figure out how to get my images_upload_handler to work. And I don't want to use images_upload_url as there are hidden form elements I am appending to the XHR request in order to handle some bits in the view (without constructing crazy URLs).

@engrmumtazali0112
Copy link

i have an issues when i create tinymce in model it give error and can not make data base

@marius-mather
Copy link
Contributor

I have image uploads working in my project, so I will post my solution here in case it helps, but note that I have currently hardcoded my image upload URL into the javascript function - for security reasons, I don't want to be using inline javascript, and I don't want to use eval.

Anyway, I have created my own init_tinymce.js script that overrides the default one, and in it I have:

"use strict";
// We need to override init_tinymce because it uses eval, which means we
// would need to allow unsafe_inline in our Content Security Policy.
// We need a custom images_upload_handler which works with Django (sets
// the right CSRF header), so include that in the init here
// Solution adapated from https://github.com/tinymce/tinymce/issues/3942
// NOTE: upload URL currently hardcoded into this JS
const images_upload_handler = (blobInfo, progress) =>
  new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest();
    xhr.open("POST", "/backend_api/content/upload/image/");
    const csrftoken = document.querySelector(
      "[name=csrfmiddlewaretoken]"
    ).value;
    xhr.setRequestHeader("X-CSRFTOKEN", csrftoken); // manually set header

    xhr.onload = function () {
      if (xhr.status === 403) {
        reject({ message: "HTTP Error: " + xhr.status, remove: true });
        return;
      }
      if (xhr.status !== 200) {
        reject({ message: "HTTP Error: " + xhr.status });
        return;
      }

      let json = JSON.parse(xhr.responseText);

      if (!json || typeof json.location !== "string") {
        reject("Invalid JSON: " + xhr.responseText);
        return;
      }

      resolve(json.location);
    };

    let formData = new FormData();
    formData.append("file", blobInfo.blob(), blobInfo.filename());

    xhr.send(formData);
  });

{
  function initTinyMCE(el) {
    if (el.closest(".empty-form") === null) {
      // Don't do empty inlines
      var mce_conf = JSON.parse(el.dataset.mceConf);

      mce_conf["images_upload_handler"] = images_upload_handler;

      const id = el.id;
      if ("elements" in mce_conf && mce_conf["mode"] == "exact") {
        mce_conf["elements"] = id;
      }
      if (el.dataset.mceGzConf) {
        tinyMCE_GZ.init(JSON.parse(el.dataset.mceGzConf));
      }
      if (!tinyMCE.get(id)) {
        tinyMCE.init(mce_conf);
      }
    }
  }
  
// FIle continues with ready(), initializeTinyMCE(), etc.

@four4four
Copy link

I also had this issue, tried using stuff proposed in #356 but images_upload_handler didn't work. So after checking tinymce documentation I rewrote their const declaration like this and it worked:

function image_upload_handler(blobInfo, progress) {
    return new Promise((resolve, reject) => {
          const xhr = new XMLHttpRequest();
          xhr.withCredentials = false;
          xhr.open('POST', '/example_upload_api');
          xhr.setRequestHeader('X-CSRFToken', Cookies.get("csrftoken")); // add csrf token
          // rest of the code from tinymce documentation
          xhr.upload.onprogress = (e) => {
              progress(e.loaded / e.total * 100);
          };
          xhr.onload = () => {
            if (xhr.status === 403) {
              reject({ message: 'HTTP Error: ' + xhr.status, remove: true });
              return;
            }
          
            if (xhr.status < 200 || xhr.status >= 300) {
              reject('HTTP Error: ' + xhr.status);
              return;
            }
          
            const json = JSON.parse(xhr.responseText);
          
            if (!json || typeof json.location != 'string') {
              reject('Invalid JSON: ' + xhr.responseText);
              return;
            }
          
            resolve(json.location);
          };
          
          xhr.onerror = () => {
            reject('Image upload failed due to a XHR Transport error. Code: ' + xhr.status);
          };
          
          const formData = new FormData();
          formData.append('file', blobInfo.blob(), blobInfo.filename());
          
          xhr.send(formData);
    })
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants