diff --git a/pelican-upstream-patch.diff b/pelican-upstream-patch.diff new file mode 100644 index 0000000..7774c69 --- /dev/null +++ b/pelican-upstream-patch.diff @@ -0,0 +1,142 @@ +diff --git a/pelican/contents.py b/pelican/contents.py +index a06a1a6a..82da413a 100644 +--- a/pelican/contents.py ++++ b/pelican/contents.py +@@ -27,6 +27,7 @@ from pelican.utils import ( + sanitised_join, + set_date_tzinfo, + slugify, ++ strip_toc_elements_from_html, + truncate_html_paragraphs, + truncate_html_words, + ) +@@ -446,14 +447,20 @@ class Content: + content = truncate_html_paragraphs(self.content, max_paragraphs) + + if self.settings["SUMMARY_MAX_LENGTH"] is None: +- return content +- +- return truncate_html_words( ++ summary = content ++ else: ++ summary = truncate_html_words( + content, + self.settings["SUMMARY_MAX_LENGTH"], + self.settings["SUMMARY_END_SUFFIX"], + ) + ++ # Strip TOC elements that would contain broken links in summary context ++ # TOC anchors only work in full article view, not in summaries/excerpts ++ summary = strip_toc_elements_from_html(summary) ++ ++ return summary ++ + @property + def summary(self) -> str: + return self.get_summary(self.get_siteurl()) +diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py +index 95bf197a..f0b9c32b 100644 +--- a/pelican/tests/test_utils.py ++++ b/pelican/tests/test_utils.py +@@ -420,6 +420,60 @@ class TestUtils(LoggedTestCase): + self.assertEqual(utils.truncate_html_paragraphs(three, 3), three) + self.assertEqual(utils.truncate_html_paragraphs(three, 4), three) + ++ def test_strip_toc_elements_from_html(self): ++ # Test removing TOC div with various class names ++ html_with_toc = ( ++ '
' ++ '

Table of Contents

' ++ '' ++ '
' ++ '

Some content here

' ++ ) ++ result = utils.strip_toc_elements_from_html(html_with_toc) ++ self.assertNotIn('
Section Heading' ++ '

Some content

' ++ ) ++ result = utils.strip_toc_elements_from_html(html_with_backref) ++ self.assertNotIn('toc-backref', result) ++ self.assertNotIn('Section Heading', result) ++ ++ # Test combined - remove both TOC div and backrefs ++ html_combined = ( ++ '
' ++ '

TOC here

' ++ '
' ++ '

the design

' ++ '

Article content

' ++ '

key features

' ++ '

More content

' ++ ) ++ result = utils.strip_toc_elements_from_html(html_combined) ++ self.assertNotIn('

TOC

Content

' ++ result = utils.strip_toc_elements_from_html(html_mixed_case) ++ self.assertNotIn('CONTENTS', result) ++ self.assertIn('

Content

', result) ++ + def test_process_translations(self): + fr_articles = [] + en_articles = [] +diff --git a/pelican/utils.py b/pelican/utils.py +index eb281ce9..50213bc0 100644 +--- a/pelican/utils.py ++++ b/pelican/utils.py +@@ -643,6 +643,36 @@ def truncate_html_paragraphs(s, count): + return "".join(paragraphs) + + ++def strip_toc_elements_from_html(html: str) -> str: ++ """Strip table of contents elements from HTML summaries. ++ ++ Removes TOC divs and toc-backref anchor links that would be broken ++ when article summaries are displayed outside of the full article context ++ (e.g., on homepage, in RSS feeds). ++ ++ :param html: HTML content to process ++ :return: Cleaned HTML with TOC elements removed ++ """ ++ # Remove the entire
...
block ++ html = re.sub( ++ r']*>.*?
', ++ '', ++ html, ++ flags=re.DOTALL | re.IGNORECASE ++ ) ++ ++ # Remove anchor links from headings (e.g., text) ++ # These links point to anchors that don't exist in summary context ++ html = re.sub( ++ r']*class="[^"]*toc-backref[^"]*"[^>]*>(.*?)', ++ r'\1', ++ html, ++ flags=re.DOTALL | re.IGNORECASE ++ ) ++ ++ return html ++ ++ + def process_translations( + content_list: list[Content], + translation_id: str | Collection[str] | None = None,