Add a feature-aware HTMLConverter object to handle editorHTML / dbHTML conversion in both directions

pull/4079/merge
Matt Westcott 2017-11-29 14:50:40 +00:00
rodzic e65c5a6106
commit 3e1ab7e74f
4 zmienionych plików z 92 dodań i 49 usunięć

Wyświetl plik

@ -7,8 +7,8 @@ from django.utils.module_loading import import_string
from wagtail.utils.widgets import WidgetWithScript
from wagtail.admin.edit_handlers import RichTextFieldPanel
from wagtail.admin.rich_text.converters.editor_html import DbWhitelister
from wagtail.core.rich_text import expand_db_html, features
from wagtail.admin.rich_text.converters.editor_html import EditorHTMLConverter
from wagtail.core.rich_text import features
class HalloPlugin:
@ -91,9 +91,9 @@ class HalloRichTextArea(WidgetWithScript, widgets.Textarea):
self.features = kwargs.pop('features', None)
if self.features is None:
self.features = features.get_default_features()
self.whitelister = DbWhitelister()
self.converter = EditorHTMLConverter()
else:
self.whitelister = DbWhitelister(self.features)
self.converter = EditorHTMLConverter(self.features)
# construct a list of plugin objects, by querying the feature registry
# and keeping the non-null responses from get_editor_plugin
@ -109,7 +109,7 @@ class HalloRichTextArea(WidgetWithScript, widgets.Textarea):
if value is None:
translated_value = None
else:
translated_value = expand_db_html(value, for_editor=True)
translated_value = self.converter.from_database_format(value)
return super().render(name, translated_value, attrs)
def render_js_init(self, id_, name, value):
@ -129,7 +129,7 @@ class HalloRichTextArea(WidgetWithScript, widgets.Textarea):
original_value = super().value_from_datadict(data, files, name)
if original_value is None:
return None
return self.whitelister.clean(original_value)
return self.converter.to_database_format(original_value)
@property
def media(self):

Wyświetl plik

@ -2,6 +2,7 @@ from django.utils.functional import cached_property
from wagtail.core import hooks
from wagtail.core.rich_text import features
from wagtail.core.rich_text.rewriters import EmbedRewriter, LinkRewriter, MultiRuleRewriter
from wagtail.core.whitelist import allow_without_attributes, Whitelister, DEFAULT_ELEMENT_RULES
@ -108,3 +109,37 @@ class DbWhitelister(Whitelister):
tag.name = 'p'
super(DbWhitelister, self).clean_tag_node(doc, tag)
class EditorHTMLConverter:
def __init__(self, features=None):
self.features = features
self.whitelister = DbWhitelister(features)
def to_database_format(self, html):
return self.whitelister.clean(html)
@cached_property
def html_rewriter(self):
if self.features is None:
feature_list = features.get_default_features()
else:
feature_list = self.features
embed_rules = {}
link_rules = {}
for feature in feature_list:
embed_handlers = features.get_embed_handler_rules(feature)
for handler_name, handler in embed_handlers.items():
embed_rules[handler_name] = handler.expand_db_attributes_for_editor
link_handlers = features.get_link_handler_rules(feature)
for handler_name, handler in link_handlers.items():
link_rules[handler_name] = handler.expand_db_attributes_for_editor
return MultiRuleRewriter([
LinkRewriter(link_rules), EmbedRewriter(embed_rules)
])
def from_database_format(self, html):
return self.html_rewriter(html)

Wyświetl plik

@ -1,3 +1,5 @@
from bs4 import BeautifulSoup
from django.conf import settings
from django.test import TestCase
from django.test.utils import override_settings
@ -459,3 +461,33 @@ class TestWidgetWhitelisting(TestCase, WagtailTestUtils):
'body': '<p>image <img src="foo" data-embedtype="image" data-id="123" data-format="left" data-alt="test alt" /> embed <span data-embedtype="media" data-url="https://www.youtube.com/watch?v=vwyuB8QKzBI">blah</span></p>'
}, {}, 'body')
self.assertHTMLEqual(result, '<p>image <embed embedtype="image" id="123" format="left" alt="test alt" /> embed </p>')
class TestWidgetRendering(TestCase, WagtailTestUtils):
fixtures = ['test.json']
def test_default_features(self):
widget = HalloRichTextArea()
result = widget.render(
'foo',
'<p>a <a linktype="page" id="3">page</a> and a <a linktype="document" id="1">document</a></p>',
{'id': 'id_foo'},
)
soup = BeautifulSoup(result, 'html.parser')
result_value = soup.textarea.string
self.assertHTMLEqual(result_value, '<p>a <a data-linktype="page" data-id="3" data-parent-id="2" href="/events/">page</a> and a <a data-linktype="document" data-id="1" href="/documents/1/test.pdf">document</a></p>')
def test_custom_features(self):
widget = HalloRichTextArea(features=['h1', 'link', 'somethingijustmadeup'])
result = widget.render(
'foo',
'<p>a <a linktype="page" id="3">page</a> and a <a linktype="document" id="1">document</a></p>',
{'id': 'id_foo'},
)
soup = BeautifulSoup(result, 'html.parser')
result_value = soup.textarea.string
self.assertHTMLEqual(result_value, '<p>a <a data-linktype="page" data-id="3" data-parent-id="2" href="/events/">page</a> and a <a>document</a></p>')

Wyświetl plik

@ -7,60 +7,36 @@ from wagtail.core.rich_text.rewriters import EmbedRewriter, LinkRewriter, MultiR
features = FeatureRegistry()
# Rewriter functions to be built up on first call to expand_db_html, using the utility classes
# Rewriter function to be built up on first call to expand_db_html, using the utility classes
# from wagtail.core.rich_text.rewriters along with the embed handlers / link handlers registered
# with the feature registry
FRONTEND_REWRITER = None
EDITOR_REWRITER = None
def expand_db_html(html, for_editor=False):
def expand_db_html(html):
"""
Expand database-representation HTML into proper HTML usable in either
templates or the rich text editor
Expand database-representation HTML into proper HTML usable on front-end templates
"""
global FRONTEND_REWRITER, EDITOR_REWRITER
global FRONTEND_REWRITER
if for_editor:
if FRONTEND_REWRITER is None:
embed_handlers = features.get_all_embed_handler_rules()
embed_rules = {
handler_name: handler.expand_db_attributes
for handler_name, handler in embed_handlers.items()
}
link_handlers = features.get_all_link_handler_rules()
link_rules = {
handler_name: handler.expand_db_attributes
for handler_name, handler in link_handlers.items()
}
if EDITOR_REWRITER is None:
embed_handlers = features.get_all_embed_handler_rules()
embed_rules = {
handler_name: handler.expand_db_attributes_for_editor
for handler_name, handler in embed_handlers.items()
}
link_handlers = features.get_all_link_handler_rules()
link_rules = {
handler_name: handler.expand_db_attributes_for_editor
for handler_name, handler in link_handlers.items()
}
FRONTEND_REWRITER = MultiRuleRewriter([
LinkRewriter(link_rules), EmbedRewriter(embed_rules)
])
EDITOR_REWRITER = MultiRuleRewriter([
LinkRewriter(link_rules), EmbedRewriter(embed_rules)
])
return EDITOR_REWRITER(html)
else:
if FRONTEND_REWRITER is None:
embed_handlers = features.get_all_embed_handler_rules()
embed_rules = {
handler_name: handler.expand_db_attributes
for handler_name, handler in embed_handlers.items()
}
link_handlers = features.get_all_link_handler_rules()
link_rules = {
handler_name: handler.expand_db_attributes
for handler_name, handler in link_handlers.items()
}
FRONTEND_REWRITER = MultiRuleRewriter([
LinkRewriter(link_rules), EmbedRewriter(embed_rules)
])
return FRONTEND_REWRITER(html)
return FRONTEND_REWRITER(html)
class RichText: