Commit 7eb5b456 authored by Fabien Amarger's avatar Fabien Amarger

feat(editorjs): Add editorjs implementation

parent e8d30084abbb
Pipeline #60667 failed with stages
in 20 seconds
......@@ -3,6 +3,120 @@
Add editorjs format for RichString
"""
import copy
from logilab.common.decorators import monkeypatch
from cubicweb import cwconfig, entity
from cubicweb.web import formfields, formwidgets as fw
from cubicweb.web.views import forms
from yams import constraints
NEW_PERSISTENT_OPTIONS = []
for option in cwconfig.PERSISTENT_OPTIONS:
if option[0] == "default-text-format":
option = ("default-text-format", copy.deepcopy(option[1]))
option[1]["choices"] += ("application/vnd.cubicweb.editorjs",)
NEW_PERSISTENT_OPTIONS.append(option)
formfields.EditableFileField.editable_formats += ("application/vnd.cubicweb.editorjs",)
forms.FieldsForm.needs_js += ("cubes.editorjs.js",)
constraints.FormatConstraint.regular_formats += ("application/vnd.cubicweb.editorjs",)
def use_editorjs(rich_text_field, form):
"""return True if editor.js should be used to edit entity's attribute named
`attr`
"""
return rich_text_field.format(form) == "application/vnd.cubicweb.editorjs"
class EditorJS(fw.TextArea):
"""EditorJS enabled <textarea>, will return a unicode string containing
HTML formated text.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.attrs["cubicweb:type"] = "editorjs"
def _render(self, form, field, renderer):
return super()._render(form, field, renderer)
old_get_widget = formfields.RichTextField.get_widget
@monkeypatch(formfields.RichTextField)
def get_widget(self, form):
if self.widget is None:
if use_editorjs(self, form):
return EditorJS()
return old_get_widget(self, form)
old_get_format_field = formfields.RichTextField.get_format_field
@monkeypatch(formfields.RichTextField)
def get_format_field(self, form):
if self.format_field:
return self.format_field
# we have to cache generated field since it's use as key in the
# context dictionary
req = form._cw
try:
return req.data[self]
except KeyError:
fkwargs = {"eidparam": self.eidparam, "role": self.role}
if use_editorjs(self, form):
fkwargs["widget"] = fw.HiddenInput()
fkwargs["value"] = "application/vnd.cubicweb.editorjs"
fkwargs["eidparam"] = self.eidparam
field = formfields.StringField(name=self.name + "_format", **fkwargs)
req.data[self] = field
return field
else:
return old_get_format_field(self, form)
old_printable_value = entity.Entity.printable_value
@monkeypatch(entity.Entity)
def printable_value(
self,
attr,
value=entity._marker,
attrtype=None,
format="text/html",
displaytime=True,
): # XXX cw_printable_value
"""return a displayable value (i.e. unicode string) which may contains
html tags
"""
attr = str(attr)
if value is entity._marker:
value = getattr(self, attr)
if isinstance(value, str):
value = value.strip()
if value is None or value == "": # don't use "not", 0 is an acceptable value
return ""
if attrtype is None:
attrtype = self.e_schema.destination(attr)
props = self.e_schema.rdef(attr)
if attrtype == "String":
# internalinalized *and* formatted string such as schema
# description...
if props.internationalizable:
value = self._cw._(value)
attrformat = self.cw_attr_metadata(attr, "format")
if attrformat:
if attrformat == "application/vnd.cubicweb.editorjs":
self._cw.add_js("cubes.editorjs.js")
return f'<textarea cubicweb:type="editorjs" cubicweb:mode="read">{value}</textarea>'
return old_printable_value(
self, attr, value, attrtype, format, displaytime
)
def includeme(config):
pass
tools = [
"header",
"list",
"image",
"checklist",
"link",
"quote",
"simple-image",
"code",
"table",
];
function loadScript(scriptUrl) {
const script = document.createElement("script");
script.src = scriptUrl;
document.body.appendChild(script);
return new Promise((res, rej) => {
script.onload = function () {
res();
};
script.onerror = function () {
rej();
};
});
}
function initEditorjs() {
jQuery("textarea").each((_index, node) => {
if (node.getAttribute("cubicweb:type") == "editorjs") {
const editor = document.createElement("div");
$(node).hide();
node.parentNode.appendChild(editor);
const editorJsConfig = {
/**
* Id of Element that should contain Editor instance
*/
holder: editor,
tools: {
header: Header,
list: List,
image: SimpleImage,
checklist: Checklist,
quote: Quote,
linkTool: LinkTool,
code: CodeTool,
table: Table,
},
onChange: (api) => {
api.saver.save().then((outputData) => {
node.innerHTML = JSON.stringify(outputData);
});
},
};
if (node.innerHTML !== "") {
try {
editorJsConfig.data = JSON.parse(node.innerHTML);
} catch (e) {
console.error(
"Existing content for editor.js init data is not a valid JSON. See DOM node",
node.id
);
}
}
if (node.getAttribute("cubicweb:mode") == "read") {
editorJsConfig.readOnly = true;
}
new EditorJS(editorJsConfig);
}
});
}
jQuery(document).ready(() =>
loadScript(
"https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest"
).then(() =>
Promise.all(
tools.map((tool) =>
loadScript(`https://cdn.jsdelivr.net/npm/@editorjs/${tool}@latest`)
)
).then(initEditorjs)
)
);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment