diff --git a/mkdocs_rss_plugin/plugin.py b/mkdocs_rss_plugin/plugin.py index a650280a..33ceae83 100644 --- a/mkdocs_rss_plugin/plugin.py +++ b/mkdocs_rss_plugin/plugin.py @@ -5,6 +5,7 @@ # ################################## # standard library +import html as html_module import json from copy import deepcopy from dataclasses import asdict @@ -357,7 +358,8 @@ def on_page_content( ), guid=page.canonical_url, link=page_url_full, - title=page.title, + # MkDocs keeps HTML entities in titles extracted from the H1 + title=html_module.unescape(page.title) if page.title else None, updated=page_dates[1], # for later fetch _mkdocs_page_ref=MkdocsPageSubset.from_page(page), diff --git a/tests/fixtures/docs/page_with_special_chars.md b/tests/fixtures/docs/page_with_special_chars.md new file mode 100644 index 00000000..f3c5f33e --- /dev/null +++ b/tests/fixtures/docs/page_with_special_chars.md @@ -0,0 +1,12 @@ +--- +authors: + - Test Author +date: 2024-01-15 10:00 +description: "Description with & ampersand and brackets" +--- + +# Test: A & B < C > D + +This page tests that special characters are properly encoded in the RSS feed. +The title is derived from the H1, which is the only case where MkDocs +returns it with HTML entities. diff --git a/tests/test_build.py b/tests/test_build.py index 2a1d0883..08eeb1c3 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -961,6 +961,10 @@ def test_xml_escaping_in_author(self): self.assertIn("OpenSavvy & contributors", feed_xml) self.assertNotIn("OpenSavvy & contributors", feed_xml) + # H1-derived titles arrive from MkDocs with HTML entities: + # they must be unescaped once, then escaped once for the XML + self.assertIn("Test: A & B < C > D", feed_xml) + # ############################################################################## # ##### Stand alone program ########