Skip to content

0.2.6 #28

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

Merged
merged 5 commits into from
Nov 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,12 @@ EDITORJS_DEFAULT_CONFIG_TOOLS = {
'Image': {
'class': 'ImageTool',
'inlineToolbar': True,
"config": {"endpoints": {
"config": {
"endpoints": {
"byFile": reverse_lazy('editorjs_image_upload'),
"byUrl": reverse_lazy('editorjs_image_by_url')
}},
}
},
},
'Header': {
'class': 'Header',
Expand Down Expand Up @@ -291,15 +293,16 @@ plugin use css property [prefers-color-scheme](https://developer.mozilla.org/en-
The application can be configured by editing the project's `settings.py`
file.

| Key | Description | Default | Type |
| --------------------------------- | ---------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `EDITORJS_DEFAULT_PLUGINS` | List of plugins names Editor.js from npm | [See above](#plugins) | `list[str]`, `tuple[str]` |
| `EDITORJS_DEFAULT_CONFIG_TOOLS` | Map of Tools to use | [See above](#plugins) | `dict[str, dict]` |
| `EDITORJS_IMAGE_UPLOAD_PATH` | Path uploads images | `uploads/images/` | `str` |
| `EDITORJS_IMAGE_UPLOAD_PATH_DATE` | Subdirectories | `%Y/%m/` | `str` |
| `EDITORJS_IMAGE_NAME_ORIGINAL` | To use the original name of the image file? | `False` | `bool` |
| `EDITORJS_IMAGE_NAME` | Image file name. Ignored when `EDITORJS_IMAGE_NAME_ORIGINAL` is `True` | `token_urlsafe(8)` | `callable(filename: str, file: InMemoryUploadedFile)` ([docs](https://docs.djangoproject.com/en/3.0/ref/files/uploads/)) |
| `EDITORJS_VERSION` | Version Editor.js | `2.22.3` | `str` |
| Key | Description | Default | Type |
| --------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `EDITORJS_DEFAULT_PLUGINS` | List of plugins names Editor.js from npm | [See above](#plugins) | `list[str]`, `tuple[str]` |
| `EDITORJS_DEFAULT_CONFIG_TOOLS` | Map of Tools to use | [See above](#plugins) | `dict[str, dict]` |
| `EDITORJS_IMAGE_UPLOAD_PATH` | Path uploads images | `uploads/images/` | `str` |
| `EDITORJS_IMAGE_UPLOAD_PATH_DATE` | Subdirectories | `%Y/%m/` | `str` |
| `EDITORJS_IMAGE_NAME_ORIGINAL` | To use the original name of the image file? | `False` | `bool` |
| `EDITORJS_IMAGE_NAME` | Image file name. Ignored when `EDITORJS_IMAGE_NAME_ORIGINAL` is `True` | `token_urlsafe(8)` | `callable(filename: str, file: InMemoryUploadedFile)` ([docs](https://docs.djangoproject.com/en/3.0/ref/files/uploads/)) |
| `EDITORJS_EMBED_HOSTNAME_ALLOWED` | List of allowed hostname for embed | `('player.vimeo.com','www.youtube.com','coub.com','vine.co','imgur.com','gfycat.com','player.twitch.tv','player.twitch.tv','music.yandex.ru','codepen.io','www.instagram.com','twitframe.com','assets.pinterest.com','www.facebook.com','www.aparat.com'),` | `list[str]`, `tuple[str]` |
| `EDITORJS_VERSION` | Version Editor.js | `2.22.3` | `str` |

For `EDITORJS_IMAGE_NAME` was used `from secrets import token_urlsafe`

Expand Down
2 changes: 1 addition & 1 deletion django_editorjs_fields/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.2.5"
__version__ = "0.2.6"

from .fields import EditorJsJSONField, EditorJsTextField
from .widgets import EditorJsWidget
Expand Down
34 changes: 30 additions & 4 deletions django_editorjs_fields/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,30 @@

VERSION = getattr(settings, "EDITORJS_VERSION", '2.22.2')

# ATTACHMENT_REQUIRE_AUTHENTICATION = str(
# getattr(settings, "EDITORJS_ATTACHMENT_REQUIRE_AUTHENTICATION", True)
# )

EMBED_HOSTNAME_ALLOWED = str(
getattr(settings, "EDITORJS_EMBED_HOSTNAME_ALLOWED", (
'player.vimeo.com',
'www.youtube.com',
'coub.com',
'vine.co',
'imgur.com',
'gfycat.com',
'player.twitch.tv',
'player.twitch.tv',
'music.yandex.ru',
'codepen.io',
'www.instagram.com',
'twitframe.com',
'assets.pinterest.com',
'www.facebook.com',
'www.aparat.com',
))
)

IMAGE_UPLOAD_PATH = str(
getattr(settings, "EDITORJS_IMAGE_UPLOAD_PATH", 'uploads/images/')
)
Expand Down Expand Up @@ -45,10 +69,12 @@
'Image': {
'class': 'ImageTool',
'inlineToolbar': True,
"config": {"endpoints": {
"byFile": reverse_lazy('editorjs_image_upload'),
"byUrl": reverse_lazy('editorjs_image_by_url')
}},
"config": {
"endpoints": {
"byFile": reverse_lazy('editorjs_image_upload'),
"byUrl": reverse_lazy('editorjs_image_by_url')
}
},
},
'Header': {
'class': 'Header',
Expand Down
34 changes: 33 additions & 1 deletion django_editorjs_fields/fields.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import json

from django.core import checks
from django.core.exceptions import ValidationError
from django.db.models import Field
from django.forms import Textarea

from .config import DEBUG
from .config import DEBUG, EMBED_HOSTNAME_ALLOWED
from .utils import get_hostname_from_url
from .widgets import EditorJsWidget

try:
Expand Down Expand Up @@ -51,6 +55,34 @@ def __init__(self, plugins, tools, **kwargs):

super().__init__(**kwargs)

def validate_embed(self, value):
for item in value.get('blocks', []):
type = item.get('type', '').lower()
if type == 'embed':
embed = item['data']['embed']
hostname = get_hostname_from_url(embed)

if hostname not in EMBED_HOSTNAME_ALLOWED:
raise ValidationError(
hostname + ' is not allowed in EDITORJS_EMBED_HOSTNAME_ALLOWED')

def clean(self, value, model_instance):
if value and value != 'null':
if not isinstance(value, dict):
try:
value = json.loads(value)
except ValueError:
pass
except TypeError:
pass
else:
self.validate_embed(value)
value = json.dumps(value)
else:
self.validate_embed(value)

return super().clean(value, model_instance)

def formfield(self, **kwargs):
if self.use_editorjs:
kwargs['widget'] = EditorJsWidget(
Expand Down
2 changes: 1 addition & 1 deletion django_editorjs_fields/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.contrib.admin.views.decorators import staff_member_required
from django.urls import path

from .views import ImageUploadView, LinkToolView, ImageByUrl
from .views import ImageByUrl, ImageUploadView, LinkToolView

urlpatterns = [
path(
Expand Down
7 changes: 7 additions & 0 deletions django_editorjs_fields/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import urllib.parse

from django.conf import settings
from django.utils.module_loading import import_string

Expand All @@ -12,4 +14,9 @@ def get_storage_class():
)()


def get_hostname_from_url(url):
obj_url = urllib.parse.urlsplit(url)
return obj_url.hostname


storage = get_storage_class()
50 changes: 32 additions & 18 deletions django_editorjs_fields/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def default(self, obj):

class EditorJsWidget(widgets.Textarea):
def __init__(self, plugins=None, tools=None, config=None, **kwargs):
self.plugins = PLUGINS if plugins is None else plugins
self.plugins = plugins
self.tools = tools
self.config = config

Expand All @@ -39,28 +39,37 @@ def __init__(self, plugins=None, tools=None, config=None, **kwargs):
def configuration(self):
tools = {}
config = self.config or {}
custom_tools = self.tools or {}
# get name packages without version
plugins = ['@'.join(p.split('@')[:2]) for p in self.plugins]

for plugin in plugins:
plugin_key = PLUGINS_KEYS.get(plugin)
plugin_tools = custom_tools.get(
plugin_key) or CONFIG_TOOLS.get(plugin_key) or {}
plugin_class = plugin_tools.get('class')
if self.plugins or self.tools:
custom_tools = self.tools or {}
# get name packages without version
plugins = ['@'.join(p.split('@')[:2])
for p in self.plugins or PLUGINS]

if plugin_class:
for plugin in plugins:
plugin_key = PLUGINS_KEYS.get(plugin)

tools[plugin_key] = custom_tools.get(
plugin_key, CONFIG_TOOLS.get(plugin_key)
)
if not plugin_key:
continue

tools[plugin_key]['class'] = plugin_class
plugin_tools = custom_tools.get(
plugin_key) or CONFIG_TOOLS.get(plugin_key) or {}
plugin_class = plugin_tools.get('class')

custom_tools.pop(plugin_key, None)
if plugin_class:

if custom_tools:
tools.update(custom_tools)
tools[plugin_key] = custom_tools.get(
plugin_key, CONFIG_TOOLS.get(plugin_key)
)

tools[plugin_key]['class'] = plugin_class

custom_tools.pop(plugin_key, None)

if custom_tools:
tools.update(custom_tools)
else: # default
tools.update(CONFIG_TOOLS)

config.update(tools=tools)
return config
Expand All @@ -70,7 +79,12 @@ def media(self):
js_list = [
'//cdn.jsdelivr.net/npm/@editorjs/editorjs@' + VERSION # lib
]
js_list += ['//cdn.jsdelivr.net/npm/' + p for p in self.plugins or []]

plugins = self.plugins or PLUGINS

if plugins:
js_list += ['//cdn.jsdelivr.net/npm/' + p for p in plugins]

js_list.append('django-editorjs-fields/js/django-editorjs-fields.js')

return Media(
Expand Down
2 changes: 1 addition & 1 deletion example/blog/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Post(models.Model):
blank=True,
)
body_textfield = EditorJsTextField( # only images and paragraph (default)
plugins=["@editorjs/image"], null=True, blank=True,
plugins=["@editorjs/image", "@editorjs/embed"], null=True, blank=True,
i18n={
'messages': {
'blockTunes': {
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "django-editorjs-fields"
version = "0.2.5"
version = "0.2.6"
description = "Django plugin for using Editor.js"
authors = ["Ilya Kotlyakov <[email protected]>"]
license = "MIT"
Expand Down