diff --git a/ietf/utils/markdown.py b/ietf/utils/markdown.py index 3b7c60cae0..63d1c7a70f 100644 --- a/ietf/utils/markdown.py +++ b/ietf/utils/markdown.py @@ -6,6 +6,8 @@ the datatracker. """ import markdown as python_markdown +from markdown.extensions import Extension +from markdown.postprocessors import Postprocessor from django.utils.safestring import mark_safe @@ -13,15 +15,37 @@ from ietf.utils.text import bleach_cleaner, bleach_linker +class LinkifyExtension(Extension): + """ + Simple Markdown extension inspired by https://github.com/daGrevis/mdx_linkify, + but using our bleach_linker directly. Doing the linkification on the converted + Markdown output introduces artifacts. + """ + + def extendMarkdown(self, md): + md.postprocessors.register(LinkifyPostprocessor(md), "linkify", 50) + # disable automatic links via angle brackets for email addresses + md.inlinePatterns.deregister("automail") + # "autolink" for URLs does not seem to cause issues, so leave it on + + +class LinkifyPostprocessor(Postprocessor): + def run(self, text): + return urlize_ietf_docs(bleach_linker.linkify(text)) + + def markdown(text): return mark_safe( - bleach_linker.linkify( - urlize_ietf_docs( - bleach_cleaner.clean( - python_markdown.markdown( - text, extensions=["extra", "nl2br", "sane_lists", "toc"] - ) - ) + bleach_cleaner.clean( + python_markdown.markdown( + text, + extensions=[ + "extra", + "nl2br", + "sane_lists", + "toc", + LinkifyExtension(), + ], ) ) ) diff --git a/ietf/utils/tests_markdown.py b/ietf/utils/tests_markdown.py new file mode 100644 index 0000000000..c8c07b50c7 --- /dev/null +++ b/ietf/utils/tests_markdown.py @@ -0,0 +1,60 @@ +# Copyright The IETF Trust 2023, All Rights Reserved +"""Markdown API utilities tests""" + +from textwrap import dedent + +from ietf.utils.tests import TestCase +from ietf.utils.markdown import markdown + + +class MarkdownTests(TestCase): + SAMPLE_MARKDOWN = dedent( + """ + # IETF Markdown Test File + + This file contains a bunch of constructs to test our markdown converter in + `ietf/utils/markdown.py`. + + ## Links + + * https://example.com + * + * [Example](https://example.com) + * user@example.com + * + * [User](mailto:user@example.com) + * RFC2119 + * BCP 3 + * STD 1 + * FYI2 + * draft-ietf-opsec-indicators-of-compromise + * draft-ietf-opsec-indicators-of-compromise-01 + """ + ) + + SAMPLE_MARKDOWN_OUTPUT = dedent( + """ +

IETF Markdown Test File

+

This file contains a bunch of constructs to test our markdown converter in
+ ietf/utils/markdown.py.

+ + + """ + ).strip() + + def test_markdown(self): + result = markdown(self.SAMPLE_MARKDOWN) + self.assertEqual(result, self.SAMPLE_MARKDOWN_OUTPUT)