Skip to content

Inline Editing support #16

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 8 commits into from
Sep 23, 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
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ INSTALLED_APPS = [
]
```

## Upgrade

```bash
pip install django-editorjs-fields --upgrade
python manage.py collectstatic # upgrade js and css files
```


## Usage

Add code in your model
Expand Down Expand Up @@ -282,8 +290,8 @@ file.
| `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: django.core.files.uploadedfile.InMemoryUploadedFile)` ([docs](https://docs.djangoproject.com/en/3.0/ref/files/uploads/)) |
| `EDITORJS_VERSION` | Version Editor.js | `2.22.2` | `str` |
| `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` |

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.2"
__version__ = "0.2.3"

from .fields import EditorJsJSONField, EditorJsTextField
from .widgets import EditorJsWidget
Expand Down
6 changes: 6 additions & 0 deletions django_editorjs_fields/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ class EditorJsTextField(EditorJsFieldMixin, FieldMixin):
def __init__(self, plugins=None, tools=None, **kwargs):
super().__init__(plugins, tools, **kwargs)

def clean(self, value, model_instance):
if value == 'null':
value = None

return super().clean(value, model_instance)


class EditorJsJSONField(EditorJsFieldMixin, JSONField if HAS_JSONFIELD else FieldMixin):
# pylint: disable=useless-super-delegation
Expand Down
Original file line number Diff line number Diff line change
@@ -1,118 +1,173 @@
function initEditorJsField(field_id, config) {
var pluginName = "django_editorjs_fields - " + field_id
;(function () {
var pluginName = "django_editorjs_fields"
var pluginHelp =
"Write about the issue here: https://github.com/2ik/django-editorjs-fields/issues"
var textarea = document.getElementById(field_id)
var holder = textarea.nextElementSibling
var text = textarea.value.trim()

if (text) {
function initEditorJsPlugin() {
var fields = document.querySelectorAll("[data-editorjs-textarea]")

for (let i = 0; i < fields.length; i++) {
initEditorJsField(fields[i])
}
}

function initEditorJsField(textarea) {
if (!textarea) {
logError("bad textarea")
return false
}

var id = textarea.getAttribute("id")

if (!id) {
logError("empty field 'id'")
holder.remove()
return false
}

var holder = document.getElementById(id + "_editorjs_holder")

if (!holder) {
logError("holder not found")
holder.remove()
return false
}

if (id.indexOf("__prefix__") !== -1) return

try {
text = JSON.parse(text)
var config = JSON.parse(textarea.getAttribute("data-config"))
} catch (error) {
console.error(error)
console.error(
pluginName +
" - invalid json data from the database. Clear the field manually. " +
pluginHelp
logError(
"invalid 'data-config' on field: " + id + " . Clear the field manually"
)
holder.remove()
return false
}
}

textarea.style.display = "none" // remove old textarea
var text = textarea.value.trim()

if (text) {
try {
text = JSON.parse(text)
} catch (error) {
console.error(error)
logError(
"invalid json data from the database. Clear the field manually"
)
holder.remove()
return false
}
}

var editorConfig = {
id: field_id,
holder: holder,
data: text,
}
textarea.style.display = "none" // remove old textarea

var editorConfig = {
id: id,
holder: holder,
data: text,
}

if ("tools" in config) {
// set config
var tools = config.tools

if ("tools" in config) {
// set config
var tools = config.tools
for (var plugin in tools) {
var cls = tools[plugin].class

for (var plugin in tools) {
var cls = tools[plugin].class
if (cls && window[cls] != undefined) {
tools[plugin].class = eval(cls)
continue
}

if (cls && window[cls] != undefined) {
tools[plugin].class = eval(cls)
continue
delete tools[plugin]
logError("[" + plugin + "] Class " + cls + " Not Found")
}

delete tools[plugin]
console.error(
pluginName +
" - [" +
plugin +
"] Class " +
cls +
" Not Found. " +
pluginHelp
)
editorConfig.tools = tools
}

editorConfig.tools = tools
}
if ("autofocus" in config) {
editorConfig.autofocus = !!config.autofocus
}

if ("autofocus" in config) {
editorConfig.autofocus = !!config.autofocus
}
if ("hideToolbar" in config) {
editorConfig.hideToolbar = !!config.hideToolbar
}

if ("hideToolbar" in config) {
editorConfig.hideToolbar = !!config.hideToolbar
}
if ("inlineToolbar" in config) {
editorConfig.inlineToolbar = config.inlineToolbar
}

if ("inlineToolbar" in config) {
editorConfig.inlineToolbar = config.inlineToolbar
}
if ("readOnly" in config) {
editorConfig.readOnly = config.readOnly
}

if ("readOnly" in config) {
editorConfig.readOnly = config.readOnly
}
if ("minHeight" in config) {
editorConfig.minHeight = config.minHeight || 300
}

if ("minHeight" in config) {
editorConfig.minHeight = config.minHeight || 300
}
if ("logLevel" in config) {
editorConfig.logLevel = config.logLevel || "VERBOSE"
} else {
editorConfig.logLevel = "ERROR"
}

if ("logLevel" in config) {
editorConfig.logLevel = config.logLevel || "VERBOSE"
} else {
editorConfig.logLevel = "ERROR"
}
if ("placeholder" in config) {
editorConfig.placeholder = config.placeholder || "Type text..."
} else {
editorConfig.placeholder = "Type text..."
}

if ("placeholder" in config) {
editorConfig.placeholder = config.placeholder || "Type text..."
} else {
editorConfig.placeholder = "Type text..."
}
if ("defaultBlock" in config) {
editorConfig.defaultBlock = config.defaultBlock || "paragraph"
}

if ("defaultBlock" in config) {
editorConfig.defaultBlock = config.defaultBlock || "paragraph"
}
if ("sanitizer" in config) {
editorConfig.sanitizer = config.sanitizer || {
p: true,
b: true,
a: true,
}
}

if ("i18n" in config) {
editorConfig.i18n = config.i18n || {}
}

if ("sanitizer" in config) {
editorConfig.sanitizer = config.sanitizer || {
p: true,
b: true,
a: true,
editorConfig.onChange = function () {
editor
.save()
.then(function (data) {
if (data.blocks.length) {
textarea.value = JSON.stringify(data)
} else {
textarea.value = 'null'
}
})
.catch(function (error) {
console.log("save error: ", error)
})
}
var editor = new EditorJS(editorConfig)
holder.setAttribute("data-processed", 1)
}

if ("i18n" in config) {
editorConfig.i18n = config.i18n || {}
function logError(msg) {
console.error(pluginName + " - " + msg + ". " + pluginHelp)
}

editorConfig.onChange = function () {
editor
.save()
.then(function (data) {
textarea.value = JSON.stringify(data)
})
.catch(function (error) {
console.log("save error: ", error)
})
addEventListener("DOMContentLoaded", initEditorJsPlugin)

// Event
if (typeof django === "object" && django.jQuery) {
django.jQuery(document).on("formset:added", function (event, $row) {
var textarea = $row.find("[data-editorjs-textarea]").get(0)

if (textarea) {
initEditorJsField(textarea)
}
})
}
var editor = new EditorJS(editorConfig)
}
})()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<textarea data-editorjs-textarea name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %} data-config='{{ widget.config }}'>{% if widget.value %}{{ widget.value }}{% endif %}</textarea>
<div data-editorjs-holder id="{{ widget.attrs.id }}_editorjs_holder" class="editorjs-holder"></div>
5 changes: 4 additions & 1 deletion django_editorjs_fields/templatetags/editorjs.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def generate_quote(data):

def generate_code(data):
code = data.get('code')
return f'<code class="code">{code}</div>'
return f'<code class="code">{code}</code>'


def generate_raw(data):
Expand All @@ -101,6 +101,9 @@ def generate_embed(data):

@register.filter(is_safe=True)
def editorjs(value):
if not value or value == 'null':
return ""

if not isinstance(value, dict):
try:
value = json.loads(value)
Expand Down
45 changes: 30 additions & 15 deletions django_editorjs_fields/widgets.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
import json

from django.core.serializers.json import DjangoJSONEncoder
from django.forms import Media, widgets
from django.utils.functional import cached_property
from django.forms.renderers import get_default_renderer
from django.forms.utils import flatatt
from django.utils.encoding import force_str
from django.utils.functional import Promise, cached_property
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe

from .config import CONFIG_TOOLS, PLUGINS, PLUGINS_KEYS, VERSION


class LazyEncoder(DjangoJSONEncoder):
def default(self, obj):
if isinstance(obj, Promise):
return force_str(obj)
return super().default(obj)


json_encode = LazyEncoder().encode


class EditorJsWidget(widgets.Textarea):
def __init__(self, plugins=None, tools=None, config=None, **kwargs):
self.plugins = PLUGINS if plugins is None else plugins
Expand Down Expand Up @@ -69,17 +84,17 @@ def media(self):
)

def render(self, name, value, attrs=None, renderer=None):
html = super().render(name, value, attrs)

html += '''
<div data-editorjs-holder id="%(id)s_editorjs_holder" class="editorjs-holder"></div>
<script defer>
addEventListener('DOMContentLoaded', function () {
initEditorJsField('%(id)s', %(config)s);
})
</script>''' % {
'id': attrs.get('id'),
'config': json.dumps(self.configuration()),
}

return mark_safe(html)
if value is None:
value = ''

if renderer is None:
renderer = get_default_renderer()

return mark_safe(renderer.render("django-editorjs-fields/widget.html", {
'widget': {
'name': name,
'value': conditional_escape(force_str(value)),
'attrs': self.build_attrs(self.attrs, attrs),
'config': json_encode(self.configuration()),
}
}))
Loading