diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 1437726345..ed7dc68e88 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -28,6 +28,7 @@ Changelog
* Added name attributes to all built-in page action menu items (LB (Ben Johnston))
* Added validation on the filter string to the Jinja2 image template tag (Jonny Scholes)
* Changed the pages reodering UI toggle to make it easier to find (Katie Locke, Thibaud Colas)
+ * Added support for rich text link rewrite handlers for `external` and `email` links (Md Arifin Ibne Matin)
* Fix: Set `SERVER_PORT` to 443 in `Page.dummy_request()` for HTTPS sites (Sergey Fedoseev)
* Fix: Include port number in `Host` header of `Page.dummy_request()` (Sergey Fedoseev)
* Fix: Validation error messages in `InlinePanel` no longer count towards `max_num` when disabling the 'add' button (Todd Dembrey, Thibaud Colas)
diff --git a/docs/advanced_topics/customisation/rich_text_internals.rst b/docs/advanced_topics/customisation/rich_text_internals.rst
index 75529afe90..318f83720f 100644
--- a/docs/advanced_topics/customisation/rich_text_internals.rst
+++ b/docs/advanced_topics/customisation/rich_text_internals.rst
@@ -97,6 +97,22 @@ The ``register_link_type`` method allows you to define a function to be called w
def register_report_link(features):
features.register_link_type('report', report_link_handler)
+It is also possible to define link rewrite handler for Wagtail’s built-in ``external`` and ``email`` links, even though they do not have a predefined ``linktype``. For example, if you want external links to have a ``rel="nofollow"`` attribute for SEO purposes:
+
+.. code-block:: python
+
+ from django.utils.html import escape
+ from wagtail.core import hooks
+
+ def external_link_handler(attrs):
+ href = attrs["href"]
+ return '' % escape(href)
+
+ @hooks.register('register_rich_text_features')
+ def register_external_link(features):
+ features.register_link_type('external', external_link_handler)
+
+Similarly you can use ``email`` linktype to add a custom rewrite handler for email links (e.g. to obfuscate emails in rich text).
Embed rewrite handlers
----------------------
diff --git a/docs/releases/2.5.rst b/docs/releases/2.5.rst
index 4a8b9a8bb6..cb20a5f6b1 100644
--- a/docs/releases/2.5.rst
+++ b/docs/releases/2.5.rst
@@ -54,6 +54,7 @@ Other features
* Added name attributes to all built-in page action menu items (LB (Ben Johnston))
* Added validation on the filter string to the Jinja2 image template tag (Jonny Scholes)
* Changed the pages reodering UI toggle to make it easier to find (Katie Locke, Thibaud Colas)
+ * Added support for rich text link rewrite handlers for ``external`` and ``email`` links (Md Arifin Ibne Matin)
Bug fixes
diff --git a/wagtail/core/rich_text/rewriters.py b/wagtail/core/rich_text/rewriters.py
index b690810027..904eaad661 100644
--- a/wagtail/core/rich_text/rewriters.py
+++ b/wagtail/core/rich_text/rewriters.py
@@ -56,12 +56,28 @@ class LinkRewriter:
try:
link_type = attrs['linktype']
except KeyError:
- # return ordinary links without a linktype unchanged
- return match.group(0)
+ link_type = None
+ href = attrs.get('href', None)
+ if href:
+ # From href attribute we try to detect only the linktypes that we
+ # currently support (`external` & `email`, `page` has a default handler)
+ # from the link chooser.
+ if href.startswith(('http:', 'https:')):
+ link_type = 'external'
+ elif href.startswith('mailto:'):
+ link_type = 'email'
+
+ if not link_type:
+ # otherwise return ordinary links without a linktype unchanged
+ return match.group(0)
try:
rule = self.link_rules[link_type]
except KeyError:
+ if link_type in ['email', 'external']:
+ # If no rule is registered for supported types
+ # return ordinary links without a linktype unchanged
+ return match.group(0)
# unrecognised link type
return ''
diff --git a/wagtail/core/tests/test_rich_text.py b/wagtail/core/tests/test_rich_text.py
index 2546ac2135..d6e6254e37 100644
--- a/wagtail/core/tests/test_rich_text.py
+++ b/wagtail/core/tests/test_rich_text.py
@@ -7,7 +7,7 @@ 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, page_linktype_handler
-from wagtail.core.rich_text.rewriters import extract_attrs
+from wagtail.core.rich_text.rewriters import LinkRewriter, extract_attrs
class TestPageLinkHandler(TestCase):
@@ -117,3 +117,63 @@ class TestFeatureRegistry(TestCase):
self.assertIsNone(
features.get_editor_plugin('hallo', 'made_up_feature')
)
+
+
+class TestLinkRewriterTagReplacing(TestCase):
+ def test_should_follow_default_behaviour(self):
+ # we always have default `page` rules registered.
+ rules = {
+ 'page': lambda attrs: ''.format(attrs['id'])
+ }
+ rewriter = LinkRewriter(rules)
+
+ page_type_link = rewriter('')
+ self.assertEqual(page_type_link, '')
+
+ # but it should also be able to handle other supported
+ # link types (email, external) even if no rules is provided
+ external_type_link = rewriter('')
+ self.assertEqual(external_type_link, '')
+ email_type_link = rewriter('')
+ self.assertEqual(email_type_link, '')
+
+ # As well as link which don't have any linktypes
+ link_without_linktype = rewriter('')
+ self.assertEqual(link_without_linktype, '')
+
+ # But should not handle if a custom linktype is mentioned but no
+ # associate rules are registered.
+ link_with_custom_linktype = rewriter('')
+ self.assertNotEqual(link_with_custom_linktype, '')
+ self.assertEqual(link_with_custom_linktype, '')
+
+
+ def test_supported_type_should_follow_given_rules(self):
+ # we always have `page` rules by default
+ rules = {
+ 'page': lambda attrs: ''.format(attrs['id']),
+ 'external': lambda attrs: ''.format(attrs['href']),
+ 'email': lambda attrs: ''.format(attrs['href']),
+ 'custom': lambda attrs: ''.format(attrs['href']),
+ }
+ rewriter = LinkRewriter(rules)
+
+ page_type_link = rewriter('')
+ self.assertEqual(page_type_link, '')
+
+ # It should call appropriate rule supported linktypes (external or email)
+ # based on the href value
+ external_type_link = rewriter('')
+ self.assertEqual(external_type_link, '')
+ external_type_link_http = rewriter('')
+ self.assertEqual(external_type_link_http, '')
+ email_type_link = rewriter('')
+ self.assertEqual(email_type_link, '')
+
+ # But not the unsupported ones.
+ link_with_no_linktype = rewriter('')
+ self.assertEqual(link_with_no_linktype, '')
+
+ # Also call the rule if a custom linktype is mentioned.
+ link_with_custom_linktype = rewriter('')
+ self.assertEqual(link_with_custom_linktype, '')