From 740d83217415758e0f931eb0f6c6914b6979954d Mon Sep 17 00:00:00 2001 From: Patrick Fournier Date: Tue, 18 Nov 2025 10:02:25 -0500 Subject: [PATCH 1/5] Enhance Open Graph integration: add site name and fallback to plain text summary for description. --- .../seo/seo_enhancer/html_enhancer/__init__.py | 16 +++++++++++++++- .../seo/seo_enhancer/html_enhancer/open_graph.py | 4 +++- .../seo/seo_report/seo_analyzer/__init__.py | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/pelican/plugins/seo/seo_enhancer/html_enhancer/__init__.py b/pelican/plugins/seo/seo_enhancer/html_enhancer/__init__.py index 6051393..e418537 100644 --- a/pelican/plugins/seo/seo_enhancer/html_enhancer/__init__.py +++ b/pelican/plugins/seo/seo_enhancer/html_enhancer/__init__.py @@ -9,6 +9,18 @@ from .twitter_cards import TwitterCards +def _get_plain_text_summary(metadata): + """Get content from summary, without HTML tags.""" + from bs4 import BeautifulSoup + + soup = BeautifulSoup( + metadata.get("summary", ""), + "html.parser", + ) + text_summary = soup.get_text().strip() + return text_summary + + class HTMLEnhancer: """HTML Enhancer : get instances of HTML enhancements.""" @@ -63,12 +75,14 @@ def __init__(self, file, output_path, path, open_graph=False, twitter_cards=Fals if open_graph: self.open_graph = OpenGraph( + sitename=_settings.get("SITENAME"), siteurl=_settings.get("SITEURL"), fileurl=_fileurl, file_type=_file_type, title=_metadata.get("og_title") or _title, description=_metadata.get("og_description") - or _metadata.get("description"), + or _metadata.get("description") + or _get_plain_text_summary(_metadata), image=_metadata.get("og_image") or _image, locale=_settings.get("LOCALE"), ) diff --git a/pelican/plugins/seo/seo_enhancer/html_enhancer/open_graph.py b/pelican/plugins/seo/seo_enhancer/html_enhancer/open_graph.py index 58b3ac0..cd5e27e 100644 --- a/pelican/plugins/seo/seo_enhancer/html_enhancer/open_graph.py +++ b/pelican/plugins/seo/seo_enhancer/html_enhancer/open_graph.py @@ -9,8 +9,9 @@ class OpenGraph: """ def __init__( - self, siteurl, fileurl, file_type, title, description, image, locale + self, sitename, siteurl, fileurl, file_type, title, description, image, locale ) -> None: + self.sitename = sitename self.siteurl = siteurl self.fileurl = fileurl self.type = file_type @@ -57,6 +58,7 @@ def create_tags(self) -> dict: """ open_graph_tags = {} + open_graph_tags["site_name"] = self.sitename open_graph_tags["url"] = self._create_absolute_fileurl() open_graph_tags["type"] = self.type diff --git a/pelican/plugins/seo/seo_report/seo_analyzer/__init__.py b/pelican/plugins/seo/seo_report/seo_analyzer/__init__.py index 7156467..ac9a24f 100644 --- a/pelican/plugins/seo/seo_report/seo_analyzer/__init__.py +++ b/pelican/plugins/seo/seo_report/seo_analyzer/__init__.py @@ -11,7 +11,7 @@ class SEOAnalyzer: def __init__(self, article): self._title = getattr(article, "title", None) - self._description = getattr(article, "description", None) + self._description = getattr(article, "description", None) or getattr(article, "summary", None) self._content = getattr(article, "content", None) self._settings = getattr(article, "settings", None) From 60e71ea37d16be102555c868b44564eac4bfc7cf Mon Sep 17 00:00:00 2001 From: Patrick Fournier Date: Tue, 18 Nov 2025 10:37:20 -0500 Subject: [PATCH 2/5] Lint, format and update tests. --- pelican/plugins/seo/seo_enhancer/html_enhancer/__init__.py | 7 ++++--- .../plugins/seo/seo_enhancer/html_enhancer/open_graph.py | 4 +++- pelican/plugins/seo/seo_report/seo_analyzer/__init__.py | 4 +++- pelican/plugins/seo/tests/test_open_graph.py | 7 +++++++ pelican/plugins/seo/tests/test_seo_enhancer.py | 2 ++ 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/pelican/plugins/seo/seo_enhancer/html_enhancer/__init__.py b/pelican/plugins/seo/seo_enhancer/html_enhancer/__init__.py index e418537..6580aef 100644 --- a/pelican/plugins/seo/seo_enhancer/html_enhancer/__init__.py +++ b/pelican/plugins/seo/seo_enhancer/html_enhancer/__init__.py @@ -1,5 +1,7 @@ """HTML Enhancer : get instances of HTML enhancements.""" +from bs4 import BeautifulSoup + from pelican.contents import Article, Page from .article_schema_creator import ArticleSchemaCreator @@ -11,7 +13,6 @@ def _get_plain_text_summary(metadata): """Get content from summary, without HTML tags.""" - from bs4 import BeautifulSoup soup = BeautifulSoup( metadata.get("summary", ""), @@ -81,8 +82,8 @@ def __init__(self, file, output_path, path, open_graph=False, twitter_cards=Fals file_type=_file_type, title=_metadata.get("og_title") or _title, description=_metadata.get("og_description") - or _metadata.get("description") - or _get_plain_text_summary(_metadata), + or _metadata.get("description") + or _get_plain_text_summary(_metadata), image=_metadata.get("og_image") or _image, locale=_settings.get("LOCALE"), ) diff --git a/pelican/plugins/seo/seo_enhancer/html_enhancer/open_graph.py b/pelican/plugins/seo/seo_enhancer/html_enhancer/open_graph.py index cd5e27e..322879b 100644 --- a/pelican/plugins/seo/seo_enhancer/html_enhancer/open_graph.py +++ b/pelican/plugins/seo/seo_enhancer/html_enhancer/open_graph.py @@ -58,7 +58,9 @@ def create_tags(self) -> dict: """ open_graph_tags = {} - open_graph_tags["site_name"] = self.sitename + if self.sitename: + open_graph_tags["site_name"] = self.sitename + open_graph_tags["url"] = self._create_absolute_fileurl() open_graph_tags["type"] = self.type diff --git a/pelican/plugins/seo/seo_report/seo_analyzer/__init__.py b/pelican/plugins/seo/seo_report/seo_analyzer/__init__.py index ac9a24f..bbaa1a2 100644 --- a/pelican/plugins/seo/seo_report/seo_analyzer/__init__.py +++ b/pelican/plugins/seo/seo_report/seo_analyzer/__init__.py @@ -11,7 +11,9 @@ class SEOAnalyzer: def __init__(self, article): self._title = getattr(article, "title", None) - self._description = getattr(article, "description", None) or getattr(article, "summary", None) + self._description = getattr(article, "description", None) or getattr( + article, "summary", None + ) self._content = getattr(article, "content", None) self._settings = getattr(article, "settings", None) diff --git a/pelican/plugins/seo/tests/test_open_graph.py b/pelican/plugins/seo/tests/test_open_graph.py index 9e8d18a..cefc13c 100644 --- a/pelican/plugins/seo/tests/test_open_graph.py +++ b/pelican/plugins/seo/tests/test_open_graph.py @@ -17,6 +17,7 @@ def test_create_absolute_fileurl(self, fake_article): """ og = OpenGraph( + sitename=None, siteurl=fake_article.settings["SITEURL"], fileurl=fake_article.url, file_type=None, @@ -39,6 +40,7 @@ def test_create_absolute_fileurl_with_site_url_with_path(self, site_url, file_ur slash and a file URL with or without a leading slash properly. """ og = OpenGraph( + sitename=None, siteurl=site_url, fileurl=file_url, file_type=None, @@ -67,6 +69,7 @@ def test_get_locale(self, locale, expected_result): """ og = OpenGraph( + sitename=None, siteurl=None, fileurl=None, file_type=None, @@ -86,6 +89,7 @@ def test_create_tags(self, fake_article): """ og = OpenGraph( + sitename=fake_article.settings["SITENAME"], siteurl=fake_article.settings["SITEURL"], fileurl=fake_article.url, file_type="article", @@ -97,6 +101,7 @@ def test_create_tags(self, fake_article): og_tags = og.create_tags() + assert og_tags["site_name"] == "Fake Site Name" assert og_tags["url"] == "https://www.fakesite.com/fake-title.html" assert og_tags["type"] == "article" assert og_tags["title"] == "OG Title" @@ -111,6 +116,7 @@ def test_create_tags_missing_elements(self, fake_article_missing_elements): """ og = OpenGraph( + sitename=fake_article_missing_elements.settings["SITENAME"], siteurl=fake_article_missing_elements.settings["SITEURL"], fileurl=fake_article_missing_elements.url, file_type="article", @@ -122,6 +128,7 @@ def test_create_tags_missing_elements(self, fake_article_missing_elements): og_tags = og.create_tags() + assert "site_name" not in og_tags assert "title" not in og_tags assert "description" not in og_tags assert "image" not in og_tags diff --git a/pelican/plugins/seo/tests/test_seo_enhancer.py b/pelican/plugins/seo/tests/test_seo_enhancer.py index c05af56..113b24f 100644 --- a/pelican/plugins/seo/tests/test_seo_enhancer.py +++ b/pelican/plugins/seo/tests/test_seo_enhancer.py @@ -233,6 +233,7 @@ def test_add_html_enhancements_to_file_with_open_graph( + @@ -295,6 +296,7 @@ def test_add_html_enhancements_to_file_with_twitter_cards( + From d9da0b63abae172fab408804382d78f05fe966a9 Mon Sep 17 00:00:00 2001 From: Patrick Fournier Date: Tue, 18 Nov 2025 11:30:27 -0500 Subject: [PATCH 3/5] Update documentation. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index bb01d71..a45d89d 100644 --- a/README.md +++ b/README.md @@ -269,6 +269,11 @@ Image: https://www.example.com/article-image.jpg Based on [Open Graph protocol](https://ogp.me), the SEO plugin implements required properties and some aditionnals ones: +``` + +``` +`:sitename:`: The `SITENAME` from the Pelican settings. + ``` ``` @@ -293,6 +298,7 @@ og_image: https://www.example.com/og-image.jpg ``` If these metadata are not declared, `:title:`, `:description:`, `:image:` will be filled by the default `Title`, `Description` (Pelican metadata) and `Image` (plugin metadata) if they exist. +If `Description` is not defined, a plain text version of `Summary` will be used instead. ``` From 8b9fd78a548d0fd17c276181ad3074dafad64728 Mon Sep 17 00:00:00 2001 From: Patrick Fournier Date: Tue, 25 Nov 2025 11:50:16 -0500 Subject: [PATCH 4/5] Add a test: summary is used as fallback value for description. --- pelican/plugins/seo/tests/conftest.py | 1 + .../plugins/seo/tests/test_seo_enhancer.py | 64 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/pelican/plugins/seo/tests/conftest.py b/pelican/plugins/seo/tests/conftest.py index ddae16b..c0aa1aa 100644 --- a/pelican/plugins/seo/tests/conftest.py +++ b/pelican/plugins/seo/tests/conftest.py @@ -75,6 +75,7 @@ def fake_article(): "og_description": "OG Description", "og_image": "https://www.fakesite.com/og-image.jpg", "tw_account": "@TestTWCards", + "summary": "Fake summary", } title = "Fake Title" description = "Fake description" diff --git a/pelican/plugins/seo/tests/test_seo_enhancer.py b/pelican/plugins/seo/tests/test_seo_enhancer.py index 113b24f..db36c69 100644 --- a/pelican/plugins/seo/tests/test_seo_enhancer.py +++ b/pelican/plugins/seo/tests/test_seo_enhancer.py @@ -240,6 +240,70 @@ def test_add_html_enhancements_to_file_with_open_graph( + + +

Fake content title

+

Fake content 🙃

+ Fake internal link +

Fake content with inline code

+

Fake content with "Fake inline internal link"

+ + """ + ) + + def test_add_html_enhancements_to_file_with_open_graph_using_summary_for_description( + self, fake_article, fake_seo_enhancer + ): + """ + Test if add_html_to_file with open_graph setting + adds Open Graph tags to HTML files. + """ + + # Remove higher priority values for the og:description tag to force the use of + # the summary from article description. + del fake_article.metadata["og_description"] + del fake_article.description + + path = "fake_output/fake_file.html" + fake_html_enhancements = fake_seo_enhancer.launch_html_enhancer( + file=fake_article, + output_path="fake_output", + path=path, + open_graph=True, + ) + + with patch( + "seo.seo_enhancer.open", mock_open(read_data=fake_article.content) + ) as mocked_open: + mocked_file_handle = mocked_open.return_value + + fake_seo_enhancer.add_html_to_file( + enhancements=fake_html_enhancements, path=path + ) + assert len(mocked_open.call_args_list) == 2 + mocked_file_handle.read.assert_called_once() + mocked_file_handle.write.assert_called_once() + + write_args, _ = mocked_file_handle.write.call_args_list[0] + fake_html_content = write_args[0] + + # The og:description tag should now contain "Fake summary". + assert ( + fake_html_content + == """ + + Fake Title + + + + + + + + + + +

Fake content title

From 1fb26202879e56ec37cec493d2b2dab44ffe1169 Mon Sep 17 00:00:00 2001 From: Patrick Fournier Date: Tue, 25 Nov 2025 11:55:02 -0500 Subject: [PATCH 5/5] Add a test: summary is used as fallback value for description. --- pelican/plugins/seo/tests/test_seo_enhancer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/plugins/seo/tests/test_seo_enhancer.py b/pelican/plugins/seo/tests/test_seo_enhancer.py index db36c69..5687eae 100644 --- a/pelican/plugins/seo/tests/test_seo_enhancer.py +++ b/pelican/plugins/seo/tests/test_seo_enhancer.py @@ -290,7 +290,7 @@ def test_add_html_enhancements_to_file_with_open_graph_using_summary_for_descrip # The og:description tag should now contain "Fake summary". assert ( fake_html_content - == """ + == """ Fake Title