diff --git a/README.md b/README.md index 9a34ced..74cf4e7 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,8 @@ Plugin for using [Editor.js](https://editorjs.io/) in django admin. pip install django-editorjs ``` +Add django_editorjs to your INSTALLED_APPS setting. + # Simple example ```python @@ -87,6 +89,50 @@ class Post(models.Model): - `Attaches` - (`dict`) configuration for tool `AttachesTool`. (_For more info see official documentation for tool_). - `Table` - (`dict`) configuration for tool `Table`. (_For more info see official documentation for tool_). +## For file upload + +1. Add `EditorjsImageUploaderView` view to your project's `urls.py` file: + +```python +from django_editorjs.views import EditorjsImageUploader + +... + +urlpatterns += [ + path("editorjs/image/", EditorjsImageUploaderView.as_view()) +] +``` + +2. Setting Configurations `settings.py` + +```python +# EditorjsImageUploaderView absolute url +EDITORJS_UPLOAD_IMAGE_URL = "/editorjs/image/" + +# Relative path to your editorjs media upload directory +EDITORJS_UPLOAD_IMAGE_PATH = "editorjs/images/" + +# Images types that available for upload +EDITORJS_IMAGE_TYPES = ( + "image/png", + "image/jpg", + "image/jpeg", + "image/pjpeg", + "image/gif", +) + +# Maximum Upload Image +# 2.5MB - 2621440 +# 5MB - 5242880 +# 10MB - 10485760 +# 20MB - 20971520 +# 50MB - 5242880 +# 100MB 104857600 +# 250MB - 214958080 +# 500MB - 429916160 +EDITORJS_MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024 # Default 10MB +``` + # API - `EditorJsField` @@ -102,5 +148,4 @@ class Post(models.Model): - load tool on demand - more examples in README.md - view-function for file uploading -- view-function for image uploading - view-function for link info crawler diff --git a/django_editorjs/app_settings.py b/django_editorjs/app_settings.py new file mode 100644 index 0000000..d995f83 --- /dev/null +++ b/django_editorjs/app_settings.py @@ -0,0 +1,18 @@ +from django.conf import settings + +UPLOAD_IMAGE_ENDPOINT = ( + getattr(settings, "EDITORJS_UPLOAD_IMAGE_URL", None) or "/editorjs/image/" +) +UPLOAD_IMAGE_PATH = ( + getattr(settings, "EDITORJS_UPLOAD_IMAGE_PATH", None) or "editorjs/images/" +) + +IMAGE_AVAILABLE_TYPES = getattr(settings, "EDITORJS_IMAGE_TYPES", None) or ( + "image/png", + "image/jpg", + "image/jpeg", + "image/pjpeg", + "image/gif", +) + +MAX_IMAGE_UPLOAD_SIZE = getattr(settings, "EDITORJS_MAX_IMAGE_UPLOAD_SIZE", None) or 1048576 diff --git a/django_editorjs/fields.py b/django_editorjs/fields.py index cfdf381..1320e40 100644 --- a/django_editorjs/fields.py +++ b/django_editorjs/fields.py @@ -5,7 +5,8 @@ class EditorJsField(Field): - def __init__(self, editorjs_config=None, *args, **kwargs): + def __init__(self, *args, **kwargs): + editorjs_config = kwargs.pop("editorjs_config", None) super().__init__(*args, **kwargs) self._editorjs_config = editorjs_config diff --git a/django_editorjs/static/django-editorjs.js b/django_editorjs/static/django-editorjs.js index fe21be8..9b8a241 100644 --- a/django_editorjs/static/django-editorjs.js +++ b/django_editorjs/static/django-editorjs.js @@ -1,4 +1,24 @@ (function () { + /** + * @param {String} name + */ + var getCookie = function(name) { + var cookieValue = null; + var i = 0; + if (document.cookie && document.cookie !== '') { + var cookies = document.cookie.split(';'); + for (i; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) === (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + }; + /** * @param {Object} config * @param {String} tool @@ -36,12 +56,15 @@ var input_el = field_wrapper.querySelector("[data-editorjs-input]"); var config_el = field_wrapper.querySelector("[data-editorjs-config]"); var config = JSON.parse(config_el.innerHTML.trim()); + var upload_image_endpoint = config.UPLOAD_IMAGE_ENDPOINT; var tools = {}; if (!isDisabled(config, "Image")) { tools.Image = extractToolConfig(config, "Image", { class: ImageTool, inlineToolbar: true, + config: { endpoints: { byFile: upload_image_endpoint } }, }); + tools.Image.config.additionalRequestHeaders = {"X-CSRFToken": getCookie("csrftoken")}; } if (!isDisabled(config, "Header")) { tools.Header = extractToolConfig(config, "Header", { diff --git a/django_editorjs/views.py b/django_editorjs/views.py new file mode 100644 index 0000000..bee7a0b --- /dev/null +++ b/django_editorjs/views.py @@ -0,0 +1,56 @@ +import os +import json +import uuid + +from django.conf import settings +from django.views import View +from django.http import HttpResponse +from django.core.files.base import ContentFile +from django.core.files.storage import default_storage + +from .app_settings import ( + IMAGE_AVAILABLE_TYPES, + MAX_IMAGE_UPLOAD_SIZE, + UPLOAD_IMAGE_PATH, +) + + +class EditorjsImageUploaderView(View): + def post(self, request, *args, **kwargs): + if "image" in request.FILES: + image = request.FILES["image"] + if image.content_type not in IMAGE_AVAILABLE_TYPES: + data = json.dumps({"success": 0, "error": "Bad image format."}) + return HttpResponse(data, content_type="application/json", status=405) + if image.size > MAX_IMAGE_UPLOAD_SIZE: + to_MB = MAX_IMAGE_UPLOAD_SIZE / (1024 * 1024) + data = json.dumps( + { + "success": 0, + "error": "Maximum image file is %(size)s MB." % {"size": to_MB}, + }, + ) + return HttpResponse(data, content_type="application/json", status=405) + + img_uuid = "{0}-{1}".format( + uuid.uuid4().hex[:10], image.name.replace(" ", "-") + ) + tmp_file = os.path.join(UPLOAD_IMAGE_PATH, img_uuid) + def_path = default_storage.save(tmp_file, ContentFile(image.read())) + img_url = os.path.join(settings.MEDIA_URL, def_path) + + data = json.dumps( + { + "success": 1, + "file": { + "url": request.build_absolute_uri(img_url), + "name": image.name, + "size": image.size, + "content_type": image.content_type, + }, + } + ) + return HttpResponse(data, content_type="application/json", status=201) + + data = json.dumps({"success": 0, "error": _("No 'image' in files")}) + return HttpResponse(data, content_type="application/json", status=400) diff --git a/django_editorjs/widgets.py b/django_editorjs/widgets.py index 047e293..cf8909c 100644 --- a/django_editorjs/widgets.py +++ b/django_editorjs/widgets.py @@ -2,11 +2,16 @@ from django.forms import widgets, Media from django.template.loader import render_to_string +from .app_settings import UPLOAD_IMAGE_ENDPOINT + class EditorJsWidget(widgets.Textarea): - def __init__(self, editorjs_config, *args, **kwargs): + def __init__(self, *args, **kwargs): + self._editorjs_config = kwargs.pop("editorjs_config", None) + if self._editorjs_config is None: + self._editorjs_config = {} + self._editorjs_config["UPLOAD_IMAGE_ENDPOINT"] = UPLOAD_IMAGE_ENDPOINT super(EditorJsWidget, self).__init__(*args, **kwargs) - self._editorjs_config = editorjs_config @property def media(self):