1
Fork 0

Hacky "fix" for 4.1.1 support for now

This commit is contained in:
Jason Dilworth 2023-02-20 11:00:31 +00:00
parent c32a8d7498
commit ad7facef5c
4 changed files with 342 additions and 225 deletions

View file

@ -1,13 +1,13 @@
from django.apps import apps from django.apps import apps
from django.template.loader import render_to_string from django.template.loader import render_to_string
from draftjs_exporter.dom import DOM from draftjs_exporter.dom import DOM
from wagtail.admin.rich_text.converters.contentstate_models import Entity from wagtail.admin.rich_text.converters.contentstate_models import Entity
from wagtail.admin.rich_text.converters.html_to_contentstate import LinkElementHandler, AtomicBlockEntityElementHandler from wagtail.admin.rich_text.converters.html_to_contentstate import (
AtomicBlockEntityElementHandler, LinkElementHandler)
from wagtail.core.rich_text import EmbedHandler, LinkHandler from wagtail.core.rich_text import EmbedHandler, LinkHandler
from .utils import get_snippet_link_frontend_template, get_snippet_embed_frontend_template from .utils import (get_snippet_embed_frontend_template,
get_snippet_link_frontend_template)
# Snippet Link # Snippet Link

View file

@ -1,13 +1,19 @@
SNIPPET_MODEL_CHOOSER_MODAL_ONLOAD_HANDLERS = { SNIPPET_MODEL_CHOOSER_MODAL_ONLOAD_HANDLERS = {
'choose': function(modal, jsonData) { choose: function (modal, jsonData) {
function getSelectedModelMeta(context) { function getSelectedModelMeta(context) {
$('a.snippet-model-choice', modal.body).on('click', function(event) { $("a.snippet-model-choice", modal.body).on(
"click",
function (event) {
event.preventDefault(); event.preventDefault();
let modelMeta = {'appName': this.dataset.appName, 'modelName': this.dataset.modelName}; let modelMeta = {
modal.respond('snippetModelChosen', modelMeta); appName: this.dataset.appName,
modelName: this.dataset.modelName,
};
modal.respond("snippetModelChosen", modelMeta);
modal.close(); modal.close();
$(".modal-backdrop").remove(); $(".modal-backdrop").remove();
}); }
);
} }
getSelectedModelMeta(modal.body); getSelectedModelMeta(modal.body);

View file

@ -1,5 +1,5 @@
(() => { (() => {
'use strict'; "use strict";
const React = window.React; const React = window.React;
const Modifier = window.DraftJS.Modifier; const Modifier = window.DraftJS.Modifier;
@ -12,29 +12,103 @@
const global = globalThis; const global = globalThis;
const $ = global.jQuery; const $ = global.jQuery;
global.TEST_SNIPPET_CHOOSER_MODAL_ONLOAD_HANDLERS = {
choose: function (modal, jsonData) {
function ajaxifyLinks(context) {
$("a.snippet-choice", modal.body).on("click", function () {
modal.loadUrl(this.href);
return false;
});
$("[data-chooser-modal-choice]", context).on(
"click",
function (e) {
e.preventDefault();
global.TEST_SNIPPET_CHOOSER_MODAL_ONLOAD_HANDLERS.chosen(
modal,
modal.loadUrl(this.href)
);
}
);
$(".pagination a", context).on("click", function () {
loadResults(this.href);
return false;
});
}
var searchForm$ = $("form.search-bar", modal.body);
var searchUrl = searchForm$.attr("action");
var request;
function search() {
loadResults(searchUrl, searchForm$.serialize());
return false;
}
function loadResults(url, data) {
var opts = {
url: url,
success: function (data, status) {
request = null;
$("#search-results").html(data);
ajaxifyLinks($("#search-results"));
},
error: function () {
request = null;
},
};
if (data) {
opts.data = data;
}
request = $.ajax(opts);
}
searchForm$.on("submit", search);
$("#snippet-chooser-locale", modal.body).on("change", search);
$("#id_q").on("input", function () {
if (request) {
request.abort();
}
clearTimeout($.data(this, "timer"));
var wait = setTimeout(search, 200);
$(this).data("timer", wait);
});
ajaxifyLinks(modal.body);
},
chosen: function (modal, jsonData) {
// alert(`SNIPPET CHOSEN`);
if (jsonData) {
modal.respond("snippetChosen", jsonData["result"]);
modal.close();
}
},
};
const MUTABILITY = {}; const MUTABILITY = {};
MUTABILITY['SNIPPET'] = 'MUTABLE'; MUTABILITY["SNIPPET"] = "MUTABLE";
MUTABILITY['SNIPPET-EMBED'] = 'IMMUTABLE'; MUTABILITY["SNIPPET-EMBED"] = "IMMUTABLE";
const getSnippetModelChooserConfig = (entityType) => { const getSnippetModelChooserConfig = (entityType) => {
let url; let url;
let urlParams; let urlParams;
if (entityType.type === 'SNIPPET') { if (entityType.type === "SNIPPET") {
return { return {
url: global.chooserUrls.snippetLinkModelChooser, url: global.chooserUrls.snippetLinkModelChooser,
urlParams: {}, urlParams: {},
onload: global.SNIPPET_MODEL_CHOOSER_MODAL_ONLOAD_HANDLERS, onload: global.SNIPPET_MODEL_CHOOSER_MODAL_ONLOAD_HANDLERS,
}; };
} } else if (entityType.type === "SNIPPET-EMBED") {
else if (entityType.type === 'SNIPPET-EMBED') {
return { return {
url: global.chooserUrls.snippetEmbedModelChooser, url: global.chooserUrls.snippetEmbedModelChooser,
urlParams: {}, urlParams: {},
onload: global.SNIPPET_MODEL_CHOOSER_MODAL_ONLOAD_HANDLERS, onload: global.SNIPPET_MODEL_CHOOSER_MODAL_ONLOAD_HANDLERS,
}; };
} } else {
else {
return { return {
url: null, url: null,
urlParams: {}, urlParams: {},
@ -48,9 +122,14 @@
let urlParams; let urlParams;
return { return {
url: global.chooserUrls.snippetChooser.concat(window.snippetModelMeta.appName, '/', window.snippetModelMeta.modelName, '/'), url: global.chooserUrls.snippetChooser.concat(
window.snippetModelMeta.appName,
"/",
window.snippetModelMeta.modelName,
"/"
),
urlParams: {}, urlParams: {},
onload: global.SNIPPET_CHOOSER_MODAL_ONLOAD_HANDLERS, onload: TEST_SNIPPET_CHOOSER_MODAL_ONLOAD_HANDLERS,
}; };
}; };
@ -79,9 +158,10 @@
componentDidMount() { componentDidMount() {
const { onClose, entityType, entity, editorState } = this.props; const { onClose, entityType, entity, editorState } = this.props;
const { url, urlParams, onload } = getSnippetModelChooserConfig(entityType); const { url, urlParams, onload } =
getSnippetModelChooserConfig(entityType);
$(document.body).on('hidden.bs.modal', this.onClose); $(document.body).on("hidden.bs.modal", this.onClose);
// eslint-disable-next-line new-cap // eslint-disable-next-line new-cap
this.model_workflow = global.ModalWorkflow({ this.model_workflow = global.ModalWorkflow({
@ -103,12 +183,13 @@
this.model_workflow = null; this.model_workflow = null;
this.workflow = null; this.workflow = null;
$(document.body).off('hidden.bs.modal', this.onClose); $(document.body).off("hidden.bs.modal", this.onClose);
} }
onModelChosen(snippetModelMeta) { onModelChosen(snippetModelMeta) {
window.snippetModelMeta = snippetModelMeta; window.snippetModelMeta = snippetModelMeta;
const { url, urlParams, onload } = getSnippetModelObjectChooserConfig(); const { url, urlParams, onload } =
getSnippetModelObjectChooserConfig();
this.model_workflow.close(); this.model_workflow.close();
@ -119,6 +200,7 @@
onload, onload,
responses: { responses: {
snippetChosen: this.onChosen, snippetChosen: this.onChosen,
chosen: this.onChosen,
}, },
onError: () => { onError: () => {
// eslint-disable-next-line no-alert // eslint-disable-next-line no-alert
@ -135,7 +217,11 @@
const entityData = filterSnippetEntityData(entityType, data); const entityData = filterSnippetEntityData(entityType, data);
const mutability = MUTABILITY[entityType.type]; const mutability = MUTABILITY[entityType.type];
const contentWithEntity = content.createEntity(entityType.type, mutability, entityData); const contentWithEntity = content.createEntity(
entityType.type,
mutability,
entityData
);
const entityKey = contentWithEntity.getLastCreatedEntityKey(); const entityKey = contentWithEntity.getLastCreatedEntityKey();
let nextState; let nextState;
@ -144,18 +230,38 @@
// Only supports adding entities at the moment, not editing existing ones. // Only supports adding entities at the moment, not editing existing ones.
// See https://github.com/springload/draftail/blob/cdc8988fe2e3ac32374317f535a5338ab97e8637/examples/sources/ImageSource.js#L44-L62. // See https://github.com/springload/draftail/blob/cdc8988fe2e3ac32374317f535a5338ab97e8637/examples/sources/ImageSource.js#L44-L62.
// See https://github.com/springload/draftail/blob/cdc8988fe2e3ac32374317f535a5338ab97e8637/examples/sources/EmbedSource.js#L64-L91 // See https://github.com/springload/draftail/blob/cdc8988fe2e3ac32374317f535a5338ab97e8637/examples/sources/EmbedSource.js#L64-L91
nextState = AtomicBlockUtils.insertAtomicBlock(editorState, entityKey, ' '); nextState = AtomicBlockUtils.insertAtomicBlock(
editorState,
entityKey,
" "
);
} else { } else {
// Replace text if the chooser demands it, or if there is no selected text in the first place. // Replace text if the chooser demands it, or if there is no selected text in the first place.
const shouldReplaceText = data.prefer_this_title_as_link_text || selection.isCollapsed(); const shouldReplaceText =
data.prefer_this_title_as_link_text ||
selection.isCollapsed();
if (shouldReplaceText) { if (shouldReplaceText) {
// If there is a title attribute, use it. Otherwise we inject the URL. // If there is a title attribute, use it. Otherwise we inject the URL.
const newText = data.string; const newText = data.string;
const newContent = Modifier.replaceText(content, selection, newText, null, entityKey); const newContent = Modifier.replaceText(
nextState = EditorState.push(editorState, newContent, 'insert-characters'); content,
selection,
newText,
null,
entityKey
);
nextState = EditorState.push(
editorState,
newContent,
"insert-characters"
);
} else { } else {
nextState = RichUtils.toggleLink(editorState, selection, entityKey); nextState = RichUtils.toggleLink(
editorState,
selection,
entityKey
);
} }
} }
@ -181,12 +287,14 @@
} }
} }
const SnippetLink = props => { const SnippetLink = (props) => {
const { entityKey, contentState } = props; const { entityKey, contentState } = props;
const data = contentState.getEntity(entityKey).getData(); const data = contentState.getEntity(entityKey).getData();
let icon = React.createElement(window.wagtail.components.Icon, {name: 'snippet'}); let icon = React.createElement(window.wagtail.components.Icon, {
let label = data.string || ''; name: "snippet",
});
let label = data.string || "";
return React.createElement(TooltipEntity, { return React.createElement(TooltipEntity, {
entityKey: props.entityKey, entityKey: props.entityKey,
@ -194,30 +302,37 @@
onEdit: props.onEdit, onEdit: props.onEdit,
onRemove: props.onRemove, onRemove: props.onRemove,
icon: icon, icon: icon,
label: label label: label,
}); });
}; };
const SnippetEmbed = props => { const SnippetEmbed = (props) => {
const { entity, onRemoveEntity, entityKey } = props.blockProps; const { entity, onRemoveEntity, entityKey } = props.blockProps;
const data = entity.getData(); const data = entity.getData();
let icon = React.createElement(window.wagtail.components.Icon, {name: 'snippet'}); let icon = React.createElement(window.wagtail.components.Icon, {
let label = data.string || ''; name: "snippet",
});
let label = data.string || "";
return React.createElement("div", { return React.createElement(
class: "MediaBlock" "div",
}, icon, `${label}`); {
class: "MediaBlock",
},
icon,
`${label}`
);
}; };
window.draftail.registerPlugin({ window.draftail.registerPlugin({
type: 'SNIPPET', type: "SNIPPET",
source: SnippetModalWorkflowSource, source: SnippetModalWorkflowSource,
decorator: SnippetLink, decorator: SnippetLink,
}); });
window.draftail.registerPlugin({ window.draftail.registerPlugin({
type: 'SNIPPET-EMBED', type: "SNIPPET-EMBED",
source: SnippetModalWorkflowSource, source: SnippetModalWorkflowSource,
block: SnippetEmbed, block: SnippetEmbed,
}); });

View file

@ -1,18 +1,14 @@
from django.urls import include, path from django.urls import include, path, reverse
from django.urls import reverse
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.translation import gettext from django.utils.translation import gettext
from wagtail.admin.rich_text.editors.draftail import \
from wagtail.admin.rich_text.editors.draftail import features as draftail_features features as draftail_features
from wagtail.core import hooks from wagtail.core import hooks
from . import urls from . import urls
from .richtext import ( from .richtext import (ContentstateSnippetEmbedConversionRule,
ContentstateSnippetLinkConversionRule, ContentstateSnippetLinkConversionRule,
ContentstateSnippetEmbedConversionRule, SnippetEmbedHandler, SnippetLinkHandler)
SnippetLinkHandler,
SnippetEmbedHandler,
)
@hooks.register("register_rich_text_features") @hooks.register("register_rich_text_features")
@ -28,7 +24,7 @@ def register_snippet_link_feature(features):
draftail_features.EntityFeature( draftail_features.EntityFeature(
{"type": type_, "icon": "snippet", "description": gettext("Snippet Link")}, {"type": type_, "icon": "snippet", "description": gettext("Snippet Link")},
js=[ js=[
"wagtailsnippets/js/snippet-chooser-modal.js", "wagtailsnippets/js/snippet-chooser.js",
"wagtail_draftail_snippet/js/snippet-model-chooser-modal.js", "wagtail_draftail_snippet/js/snippet-model-chooser-modal.js",
"wagtail_draftail_snippet/js/wagtail-draftail-snippet.js", "wagtail_draftail_snippet/js/wagtail-draftail-snippet.js",
], ],
@ -53,7 +49,7 @@ def register_snippet_embed_feature(features):
draftail_features.EntityFeature( draftail_features.EntityFeature(
{"type": type_, "icon": "code", "description": gettext("Snippet Embed")}, {"type": type_, "icon": "code", "description": gettext("Snippet Embed")},
js=[ js=[
"wagtailsnippets/js/snippet-chooser-modal.js", "wagtailsnippets/js/snippet-chooser.js",
"wagtail_draftail_snippet/js/snippet-model-chooser-modal.js", "wagtail_draftail_snippet/js/snippet-model-chooser-modal.js",
"wagtail_draftail_snippet/js/wagtail-draftail-snippet.js", "wagtail_draftail_snippet/js/wagtail-draftail-snippet.js",
], ],