diff --git a/wagtail/core/rich_text/__init__.py b/wagtail/core/rich_text/__init__.py index 507b873e25..4a8ba79fd1 100644 --- a/wagtail/core/rich_text/__init__.py +++ b/wagtail/core/rich_text/__init__.py @@ -21,17 +21,8 @@ def expand_db_html(html): global FRONTEND_REWRITER 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() - } - + embed_rules = features.get_embed_types() + link_rules = features.get_link_types() FRONTEND_REWRITER = 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 a690b999a7..cb78a06b41 100644 --- a/wagtail/core/rich_text/feature_registry.py +++ b/wagtail/core/rich_text/feature_registry.py @@ -26,6 +26,17 @@ class FeatureRegistry: # an explicit `feature` list. self.default_features = [] + # a mapping of linktype names to rewriter functions for converting database representations + # of links (e.g. ) into front-end HTML. Each rewriter function + # takes a dict of attributes, and returns the rewritten opening tag as a string + self.link_types = {} + + # a mapping of embedtype names to rewriter functions for converting database representations + # of embedded content (e.g. ) + # into front-end HTML. Each rewriter function takes a dict of attributes, and returns an + # HTML fragment to replace it with + self.embed_types = {} + # a mapping of feature names to whitelister element rules that should be merged into # the whitelister element_rules config when the feature is active self.whitelister_element_rules = {} @@ -62,6 +73,22 @@ class FeatureRegistry: except KeyError: return None + def register_link_type(self, link_type, handler): + self.link_types[link_type] = handler + + def get_link_types(self): + if not self.has_scanned_for_features: + self._scan_for_features() + return self.link_types + + def register_embed_type(self, embed_type, handler): + self.embed_types[embed_type] = handler + + def get_embed_types(self): + if not self.has_scanned_for_features: + self._scan_for_features() + return self.embed_types + def register_whitelister_element_rules(self, feature_name, ruleset): self.whitelister_element_rules[feature_name] = ruleset @@ -80,20 +107,6 @@ class FeatureRegistry: return self.embed_handler_rules.get(feature_name, {}) - def get_all_embed_handler_rules(self): - """ - Return a dictionary of embedtypes to embed handlers, collated from all the - registered embed handler rules - """ - if not self.has_scanned_for_features: - self._scan_for_features() - - collated_ruleset = {} - for ruleset in self.embed_handler_rules.values(): - collated_ruleset.update(ruleset) - - return collated_ruleset - def register_link_handler_rules(self, feature_name, ruleset): self.link_handler_rules[feature_name] = ruleset @@ -102,17 +115,3 @@ class FeatureRegistry: self._scan_for_features() return self.link_handler_rules.get(feature_name, {}) - - def get_all_link_handler_rules(self): - """ - Return a dictionary of linktypes to link handlers, collated from all the - registered link handler rules - """ - if not self.has_scanned_for_features: - self._scan_for_features() - - collated_ruleset = {} - for ruleset in self.link_handler_rules.values(): - collated_ruleset.update(ruleset) - - return collated_ruleset diff --git a/wagtail/core/rich_text/pages.py b/wagtail/core/rich_text/pages.py index 5c48a81d21..8bb0de671f 100644 --- a/wagtail/core/rich_text/pages.py +++ b/wagtail/core/rich_text/pages.py @@ -19,14 +19,6 @@ class PageLinkHandler: """ return {'id': tag['data-id']} - @staticmethod - def expand_db_attributes(attrs): - try: - page = Page.objects.get(id=attrs['id']) - return '' % escape(page.specific.url) - except Page.DoesNotExist: - return "" - @staticmethod def expand_db_attributes_for_editor(attrs): try: @@ -40,3 +32,11 @@ class PageLinkHandler: return '' % (attrs, escape(page.specific.url)) except Page.DoesNotExist: return "" + + +def page_linktype_handler(attrs): + try: + page = Page.objects.get(id=attrs['id']) + return '' % escape(page.specific.url) + except Page.DoesNotExist: + return "" diff --git a/wagtail/core/tests/test_rich_text.py b/wagtail/core/tests/test_rich_text.py index e7e555b7e7..57d8ee7be3 100644 --- a/wagtail/core/tests/test_rich_text.py +++ b/wagtail/core/tests/test_rich_text.py @@ -5,7 +5,7 @@ from mock import patch from wagtail.core.models import Page from wagtail.core.rich_text import RichText, expand_db_html from wagtail.core.rich_text.feature_registry import FeatureRegistry -from wagtail.core.rich_text.pages import PageLinkHandler +from wagtail.core.rich_text.pages import PageLinkHandler, page_linktype_handler from wagtail.core.rich_text.rewriters import extract_attrs @@ -20,7 +20,7 @@ class TestPageLinkHandler(TestCase): {'id': 'test-id'}) def test_expand_db_attributes_page_does_not_exist(self): - result = PageLinkHandler.expand_db_attributes({'id': 0}) + result = page_linktype_handler({'id': 0}) self.assertEqual(result, '') def test_expand_db_attributes_for_editor(self): @@ -38,7 +38,7 @@ class TestPageLinkHandler(TestCase): ) def test_expand_db_attributes_not_for_editor(self): - result = PageLinkHandler.expand_db_attributes({'id': 1}) + result = page_linktype_handler({'id': 1}) self.assertEqual(result, '') diff --git a/wagtail/core/wagtail_hooks.py b/wagtail/core/wagtail_hooks.py index 5562b85b1a..3547ee1d07 100644 --- a/wagtail/core/wagtail_hooks.py +++ b/wagtail/core/wagtail_hooks.py @@ -4,7 +4,7 @@ from django.urls import reverse from wagtail.core import hooks from wagtail.core.models import PageViewRestriction -from wagtail.core.rich_text.pages import PageLinkHandler +from wagtail.core.rich_text.pages import PageLinkHandler, page_linktype_handler from wagtail.core.whitelist import allow_without_attributes, attribute_rule, check_url @@ -42,6 +42,7 @@ def register_core_features(features): features.default_features.append('link') features.register_whitelister_element_rules('link', {'a': attribute_rule({'href': check_url})}) + features.register_link_type('page', page_linktype_handler) features.register_link_handler_rules('link', {'page': PageLinkHandler}) features.default_features.append('bold') diff --git a/wagtail/documents/rich_text.py b/wagtail/documents/rich_text.py index 7aeef7306c..b1bf7496ba 100644 --- a/wagtail/documents/rich_text.py +++ b/wagtail/documents/rich_text.py @@ -8,15 +8,6 @@ class DocumentLinkHandler: def get_db_attributes(tag): return {'id': tag['data-id']} - @staticmethod - def expand_db_attributes(attrs): - Document = get_document_model() - try: - doc = Document.objects.get(id=attrs['id']) - return '' % escape(doc.url) - except Document.DoesNotExist: - return "" - @staticmethod def expand_db_attributes_for_editor(attrs): Document = get_document_model() @@ -25,3 +16,12 @@ class DocumentLinkHandler: return '' % (doc.id, escape(doc.url)) except Document.DoesNotExist: return "" + + +def document_linktype_handler(attrs): + Document = get_document_model() + try: + doc = Document.objects.get(id=attrs['id']) + return '' % escape(doc.url) + except Document.DoesNotExist: + return "" diff --git a/wagtail/documents/tests/test_rich_text.py b/wagtail/documents/tests/test_rich_text.py index 1b51e444a3..9e32a78970 100644 --- a/wagtail/documents/tests/test_rich_text.py +++ b/wagtail/documents/tests/test_rich_text.py @@ -1,7 +1,7 @@ from bs4 import BeautifulSoup from django.test import TestCase -from wagtail.documents.rich_text import DocumentLinkHandler +from wagtail.documents.rich_text import DocumentLinkHandler, document_linktype_handler class TestDocumentRichTextLinkHandler(TestCase): @@ -15,7 +15,7 @@ class TestDocumentRichTextLinkHandler(TestCase): {'id': 'test-id'}) def test_expand_db_attributes_document_does_not_exist(self): - result = DocumentLinkHandler.expand_db_attributes({'id': 0}) + result = document_linktype_handler({'id': 0}) self.assertEqual(result, '') def test_expand_db_attributes_for_editor(self): @@ -24,6 +24,6 @@ class TestDocumentRichTextLinkHandler(TestCase): '') def test_expand_db_attributes_not_for_editor(self): - result = DocumentLinkHandler.expand_db_attributes({'id': 1}) + result = document_linktype_handler({'id': 1}) self.assertEqual(result, '') diff --git a/wagtail/documents/wagtail_hooks.py b/wagtail/documents/wagtail_hooks.py index d58dd7e3e6..98adfc8115 100644 --- a/wagtail/documents/wagtail_hooks.py +++ b/wagtail/documents/wagtail_hooks.py @@ -19,7 +19,7 @@ from wagtail.documents.api.admin.endpoints import DocumentsAdminAPIEndpoint from wagtail.documents.forms import GroupDocumentPermissionFormSet from wagtail.documents.models import get_document_model from wagtail.documents.permissions import permission_policy -from wagtail.documents.rich_text import DocumentLinkHandler +from wagtail.documents.rich_text import DocumentLinkHandler, document_linktype_handler @hooks.register('register_admin_urls') @@ -73,6 +73,7 @@ def editor_js(): @hooks.register('register_rich_text_features') def register_document_feature(features): + features.register_link_type('document', document_linktype_handler) features.register_editor_plugin( 'hallo', 'document-link', HalloPlugin( diff --git a/wagtail/embeds/rich_text.py b/wagtail/embeds/rich_text.py index b559cfbb94..9ec58c78bf 100644 --- a/wagtail/embeds/rich_text.py +++ b/wagtail/embeds/rich_text.py @@ -20,14 +20,6 @@ class MediaEmbedHandler: 'url': tag['data-url'], } - @staticmethod - def expand_db_attributes(attrs): - """ - Given a dict of attributes from the tag, return the real HTML - representation for use on the front-end. - """ - return format.embed_to_frontend_html(attrs['url']) - @staticmethod def expand_db_attributes_for_editor(attrs): """ @@ -39,3 +31,11 @@ class MediaEmbedHandler: except EmbedException: # Could be replaced with a nice error message return '' + + +def media_embedtype_handler(attrs): + """ + Given a dict of attributes from the tag, return the real HTML + representation for use on the front-end. + """ + return format.embed_to_frontend_html(attrs['url']) diff --git a/wagtail/embeds/tests.py b/wagtail/embeds/tests.py index d8a1c81c30..5300ae40c0 100644 --- a/wagtail/embeds/tests.py +++ b/wagtail/embeds/tests.py @@ -22,7 +22,7 @@ from wagtail.embeds.finders.embedly import EmbedlyFinder as EmbedlyFinder from wagtail.embeds.finders.embedly import AccessDeniedEmbedlyException, EmbedlyException from wagtail.embeds.finders.oembed import OEmbedFinder as OEmbedFinder from wagtail.embeds.models import Embed -from wagtail.embeds.rich_text import MediaEmbedHandler +from wagtail.embeds.rich_text import MediaEmbedHandler, media_embedtype_handler from wagtail.embeds.templatetags.wagtailembeds_tags import embed_tag try: @@ -598,7 +598,7 @@ class TestMediaEmbedHandler(TestCase): height=1000, ) - result = MediaEmbedHandler.expand_db_attributes( + result = media_embedtype_handler( {'url': 'http://www.youtube.com/watch/'} ) self.assertIn('test html', result) @@ -607,7 +607,7 @@ class TestMediaEmbedHandler(TestCase): def test_expand_db_attributes_catches_embed_not_found(self, get_embed): get_embed.side_effect = EmbedNotFoundException - result = MediaEmbedHandler.expand_db_attributes( + result = media_embedtype_handler( {'url': 'http://www.youtube.com/watch/'} ) diff --git a/wagtail/embeds/wagtail_hooks.py b/wagtail/embeds/wagtail_hooks.py index a23a70a99c..6e804cec64 100644 --- a/wagtail/embeds/wagtail_hooks.py +++ b/wagtail/embeds/wagtail_hooks.py @@ -5,7 +5,7 @@ from django.utils.html import format_html from wagtail.admin.rich_text import HalloPlugin from wagtail.core import hooks from wagtail.embeds import urls -from wagtail.embeds.rich_text import MediaEmbedHandler +from wagtail.embeds.rich_text import MediaEmbedHandler, media_embedtype_handler @hooks.register('register_admin_urls') @@ -29,6 +29,7 @@ def editor_js(): @hooks.register('register_rich_text_features') def register_embed_feature(features): + features.register_embed_type('media', media_embedtype_handler) features.register_editor_plugin( 'hallo', 'embed', HalloPlugin( diff --git a/wagtail/images/rich_text.py b/wagtail/images/rich_text.py index d3c7c73375..62dc78d7f8 100644 --- a/wagtail/images/rich_text.py +++ b/wagtail/images/rich_text.py @@ -22,21 +22,6 @@ class ImageEmbedHandler: 'alt': tag['data-alt'], } - @staticmethod - def expand_db_attributes(attrs): - """ - Given a dict of attributes from the tag, return the real HTML - representation for use on the front-end. - """ - Image = get_image_model() - try: - image = Image.objects.get(id=attrs['id']) - except Image.DoesNotExist: - return "" - - image_format = get_image_format(attrs['format']) - return image_format.image_to_html(image, attrs.get('alt', '')) - @staticmethod def expand_db_attributes_for_editor(attrs): """ @@ -52,3 +37,18 @@ class ImageEmbedHandler: image_format = get_image_format(attrs['format']) return image_format.image_to_editor_html(image, attrs.get('alt', '')) + + +def image_embedtype_handler(attrs): + """ + Given a dict of attributes from the tag, return the real HTML + representation for use on the front-end. + """ + Image = get_image_model() + try: + image = Image.objects.get(id=attrs['id']) + except Image.DoesNotExist: + return "" + + image_format = get_image_format(attrs['format']) + return image_format.image_to_html(image, attrs.get('alt', '')) diff --git a/wagtail/images/tests/test_rich_text.py b/wagtail/images/tests/test_rich_text.py index 5c461f109b..a5c938f6af 100644 --- a/wagtail/images/tests/test_rich_text.py +++ b/wagtail/images/tests/test_rich_text.py @@ -1,7 +1,7 @@ from bs4 import BeautifulSoup from django.test import TestCase -from wagtail.images.rich_text import ImageEmbedHandler +from wagtail.images.rich_text import ImageEmbedHandler, image_embedtype_handler from .utils import Image, get_test_image_file @@ -20,12 +20,12 @@ class TestImageEmbedHandler(TestCase): 'format': 'test-format'}) def test_expand_db_attributes_image_does_not_exist(self): - result = ImageEmbedHandler.expand_db_attributes({'id': 0}) + result = image_embedtype_handler({'id': 0}) self.assertEqual(result, '') def test_expand_db_attributes_not_for_editor(self): Image.objects.create(id=1, title='Test', file=get_test_image_file()) - result = ImageEmbedHandler.expand_db_attributes( + result = image_embedtype_handler( {'id': 1, 'alt': 'test-alt', 'format': 'left'} @@ -34,7 +34,7 @@ class TestImageEmbedHandler(TestCase): def test_expand_db_attributes_escapes_alt_text(self): Image.objects.create(id=1, title='Test', file=get_test_image_file()) - result = ImageEmbedHandler.expand_db_attributes( + result = image_embedtype_handler( {'id': 1, 'alt': 'Arthur "two sheds" Jackson', 'format': 'left'}, @@ -43,7 +43,7 @@ class TestImageEmbedHandler(TestCase): def test_expand_db_attributes_with_missing_alt(self): Image.objects.create(id=1, title='Test', file=get_test_image_file()) - result = ImageEmbedHandler.expand_db_attributes( + result = image_embedtype_handler( {'id': 1, 'format': 'left'}, ) diff --git a/wagtail/images/wagtail_hooks.py b/wagtail/images/wagtail_hooks.py index d1c2f115b3..0d5621bc01 100644 --- a/wagtail/images/wagtail_hooks.py +++ b/wagtail/images/wagtail_hooks.py @@ -14,7 +14,7 @@ from wagtail.images import admin_urls, get_image_model, image_operations from wagtail.images.api.admin.endpoints import ImagesAdminAPIEndpoint from wagtail.images.forms import GroupImagePermissionFormSet from wagtail.images.permissions import permission_policy -from wagtail.images.rich_text import ImageEmbedHandler +from wagtail.images.rich_text import ImageEmbedHandler, image_embedtype_handler @hooks.register('register_admin_urls') @@ -65,6 +65,7 @@ def editor_js(): @hooks.register('register_rich_text_features') def register_image_feature(features): + features.register_embed_type('image', image_embedtype_handler) features.register_editor_plugin( 'hallo', 'image', HalloPlugin(