diff --git a/wagtail/admin/rich_text/converters/editor_html.py b/wagtail/admin/rich_text/converters/editor_html.py index 124f6e7ec..4d5489140 100644 --- a/wagtail/admin/rich_text/converters/editor_html.py +++ b/wagtail/admin/rich_text/converters/editor_html.py @@ -15,6 +15,18 @@ class WhitelistRule: self.handler = handler +class EmbedTypeRule: + def __init__(self, embed_type, handler): + self.embed_type = embed_type + self.handler = handler + + +class LinkTypeRule: + def __init__(self, link_type, handler): + self.link_type = link_type + self.handler = handler + + # Whitelist rules which are always active regardless of the rich text features that are enabled BASE_WHITELIST_RULES = { @@ -40,9 +52,10 @@ class DbWhitelister(Whitelister): determined by the handler for that type defined in link_handlers, while keeping the element content intact. """ - def __init__(self, converter_rules, features): + def __init__(self, converter_rules): + self.converter_rules = converter_rules self.element_rules = BASE_WHITELIST_RULES.copy() - for rule in converter_rules: + for rule in self.converter_rules: if isinstance(rule, WhitelistRule): self.element_rules[rule.element] = rule.handler @@ -58,23 +71,19 @@ class DbWhitelister(Whitelister): for fn in construct_whitelist_hooks: self.element_rules.update(fn()) - self.features = features - @cached_property def embed_handlers(self): - embed_handlers = {} - for feature in self.features: - embed_handlers.update(feature_registry.get_embed_handler_rules(feature)) - - return embed_handlers + return { + rule.embed_type: rule.handler for rule in self.converter_rules + if isinstance(rule, EmbedTypeRule) + } @cached_property def link_handlers(self): - link_handlers = {} - for feature in self.features: - link_handlers.update(feature_registry.get_link_handler_rules(feature)) - - return link_handlers + return { + rule.link_type: rule.handler for rule in self.converter_rules + if isinstance(rule, LinkTypeRule) + } def clean_tag_node(self, doc, tag): if 'data-embedtype' in tag.attrs: @@ -120,12 +129,10 @@ class DbWhitelister(Whitelister): class EditorHTMLConverter: def __init__(self, features=None): if features is None: - self.features = feature_registry.get_default_features() - else: - self.features = features + features = feature_registry.get_default_features() self.converter_rules = [] - for feature in self.features: + for feature in features: rule = feature_registry.get_converter_rule('editorhtml', feature) if rule is not None: # rule should be a list of WhitelistRule() instances - append this to @@ -134,7 +141,7 @@ class EditorHTMLConverter: @cached_property def whitelister(self): - return DbWhitelister(self.converter_rules, self.features) + return DbWhitelister(self.converter_rules) def to_database_format(self, html): return self.whitelister.clean(html) @@ -143,14 +150,11 @@ class EditorHTMLConverter: def html_rewriter(self): embed_rules = {} link_rules = {} - for feature in self.features: - embed_handlers = feature_registry.get_embed_handler_rules(feature) - for handler_name, handler in embed_handlers.items(): - embed_rules[handler_name] = handler.expand_db_attributes - - link_handlers = feature_registry.get_link_handler_rules(feature) - for handler_name, handler in link_handlers.items(): - link_rules[handler_name] = handler.expand_db_attributes + for rule in self.converter_rules: + if isinstance(rule, EmbedTypeRule): + embed_rules[rule.embed_type] = rule.handler.expand_db_attributes + elif isinstance(rule, LinkTypeRule): + link_rules[rule.link_type] = rule.handler.expand_db_attributes return MultiRuleRewriter([ LinkRewriter(link_rules), EmbedRewriter(embed_rules) diff --git a/wagtail/core/rich_text/feature_registry.py b/wagtail/core/rich_text/feature_registry.py index 4d454faaa..a376a83d1 100644 --- a/wagtail/core/rich_text/feature_registry.py +++ b/wagtail/core/rich_text/feature_registry.py @@ -44,14 +44,6 @@ class FeatureRegistry: # The API of that rule object is not defined here, and is specific to each converter backend. self.converter_rules_by_converter = {} - # a mapping of feature names to embed_handler rules that should be merged into the - # list of recognised embedtypes when the feature is active - self.embed_handler_rules = {} - - # a mapping of feature names to link_handler rules that should be merged into the - # list of recognised linktypes when the feature is active - self.link_handler_rules = {} - def get_default_features(self): if not self.has_scanned_for_features: self._scan_for_features() @@ -104,21 +96,3 @@ class FeatureRegistry: return self.converter_rules_by_converter[converter_name][feature_name] except KeyError: return None - - def register_embed_handler_rules(self, feature_name, ruleset): - self.embed_handler_rules[feature_name] = ruleset - - def get_embed_handler_rules(self, feature_name): - if not self.has_scanned_for_features: - self._scan_for_features() - - return self.embed_handler_rules.get(feature_name, {}) - - def register_link_handler_rules(self, feature_name, ruleset): - self.link_handler_rules[feature_name] = ruleset - - def get_link_handler_rules(self, feature_name): - if not self.has_scanned_for_features: - self._scan_for_features() - - return self.link_handler_rules.get(feature_name, {}) diff --git a/wagtail/core/wagtail_hooks.py b/wagtail/core/wagtail_hooks.py index 0556c4516..48cb5146d 100644 --- a/wagtail/core/wagtail_hooks.py +++ b/wagtail/core/wagtail_hooks.py @@ -2,7 +2,7 @@ from django.conf import settings from django.contrib.auth.views import redirect_to_login from django.urls import reverse -from wagtail.admin.rich_text.converters.editor_html import WhitelistRule +from wagtail.admin.rich_text.converters.editor_html import LinkTypeRule, WhitelistRule from wagtail.core import hooks from wagtail.core.models import PageViewRestriction from wagtail.core.rich_text.pages import PageLinkHandler, page_linktype_handler @@ -45,10 +45,10 @@ def register_core_features(features): features.default_features.append('link') features.register_converter_rule('editorhtml', 'link', [ - WhitelistRule('a', attribute_rule({'href': check_url})) + WhitelistRule('a', attribute_rule({'href': check_url})), + LinkTypeRule('page', PageLinkHandler), ]) features.register_link_type('page', page_linktype_handler) - features.register_link_handler_rules('link', {'page': PageLinkHandler}) features.default_features.append('bold') features.register_converter_rule('editorhtml', 'bold', [ diff --git a/wagtail/documents/wagtail_hooks.py b/wagtail/documents/wagtail_hooks.py index 98adfc811..de8a78837 100644 --- a/wagtail/documents/wagtail_hooks.py +++ b/wagtail/documents/wagtail_hooks.py @@ -9,6 +9,7 @@ from django.utils.translation import ungettext from wagtail.admin.menu import MenuItem from wagtail.admin.rich_text import HalloPlugin +from wagtail.admin.rich_text.converters.editor_html import LinkTypeRule from wagtail.admin.search import SearchArea from wagtail.admin.site_summary import SummaryItem from wagtail.core import hooks @@ -81,7 +82,9 @@ def register_document_feature(features): js=['wagtaildocs/js/hallo-plugins/hallo-wagtaildoclink.js'], ) ) - features.register_link_handler_rules('document-link', {'document': DocumentLinkHandler}) + features.register_converter_rule('editorhtml', 'document-link', [ + LinkTypeRule('document', DocumentLinkHandler), + ]) features.default_features.append('document-link') diff --git a/wagtail/embeds/wagtail_hooks.py b/wagtail/embeds/wagtail_hooks.py index 6e804cec6..d7a16ddf3 100644 --- a/wagtail/embeds/wagtail_hooks.py +++ b/wagtail/embeds/wagtail_hooks.py @@ -3,6 +3,7 @@ from django.urls import reverse from django.utils.html import format_html from wagtail.admin.rich_text import HalloPlugin +from wagtail.admin.rich_text.converters.editor_html import EmbedTypeRule from wagtail.core import hooks from wagtail.embeds import urls from wagtail.embeds.rich_text import MediaEmbedHandler, media_embedtype_handler @@ -29,7 +30,10 @@ def editor_js(): @hooks.register('register_rich_text_features') def register_embed_feature(features): + # define a handler for converting tags into frontend HTML features.register_embed_type('media', media_embedtype_handler) + + # define a hallo.js plugin to use when the 'embed' feature is active features.register_editor_plugin( 'hallo', 'embed', HalloPlugin( @@ -37,5 +41,12 @@ def register_embed_feature(features): js=['wagtailembeds/js/hallo-plugins/hallo-wagtailembeds.js'], ) ) - features.register_embed_handler_rules('embed', {'media': MediaEmbedHandler}) + + # define how to convert between editorhtml's representation of embeds and + # the database representation + features.register_converter_rule('editorhtml', 'embed', [ + EmbedTypeRule('media', MediaEmbedHandler) + ]) + + # add 'embed' to the set of on-by-default rich text features features.default_features.append('embed') diff --git a/wagtail/images/wagtail_hooks.py b/wagtail/images/wagtail_hooks.py index 0d5621bc0..2eb430da4 100644 --- a/wagtail/images/wagtail_hooks.py +++ b/wagtail/images/wagtail_hooks.py @@ -7,6 +7,7 @@ from django.utils.translation import ungettext from wagtail.admin.menu import MenuItem from wagtail.admin.rich_text import HalloPlugin +from wagtail.admin.rich_text.converters.editor_html import EmbedTypeRule from wagtail.admin.search import SearchArea from wagtail.admin.site_summary import SummaryItem from wagtail.core import hooks @@ -65,7 +66,10 @@ def editor_js(): @hooks.register('register_rich_text_features') def register_image_feature(features): + # define a handler for converting tags into frontend HTML features.register_embed_type('image', image_embedtype_handler) + + # define a hallo.js plugin to use when the 'image' feature is active features.register_editor_plugin( 'hallo', 'image', HalloPlugin( @@ -73,7 +77,14 @@ def register_image_feature(features): js=['wagtailimages/js/hallo-plugins/hallo-wagtailimage.js'], ) ) - features.register_embed_handler_rules('image', {'image': ImageEmbedHandler}) + + # define how to convert between editorhtml's representation of images and + # the database representation + features.register_converter_rule('editorhtml', 'image', [ + EmbedTypeRule('image', ImageEmbedHandler) + ]) + + # add 'image' to the set of on-by-default rich text features features.default_features.append('image')