django-tinymce
django-tinymce copied to clipboard
images_upload_handler doesn't work for TinyMCE 6
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 -
- 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.
- 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
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).
i have an issues when i create tinymce in model it give error and can not make data base
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.