django-tinymce icon indicating copy to clipboard operation
django-tinymce copied to clipboard

images_upload_handler doesn't work for TinyMCE 6

Open justinqian42 opened this issue 10 months ago • 3 comments

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 https://github.com/jazzband/django-tinymce/issues/426#issuecomment-1472369152

justinqian42 avatar Apr 17 '24 01:04 justinqian42

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).

mnoah66 avatar May 03 '24 01:05 mnoah66

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

engrmumtazali0112 avatar May 12 '24 12:05 engrmumtazali0112

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.

marius-mather avatar Jul 10 '24 01:07 marius-mather