Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.pdm-python
pdm.lock
__pycache__/
13 changes: 10 additions & 3 deletions pelican/plugins/sitemap/sitemap.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,14 @@ def is_excluded(item):
or any(re.search(pattern, url) for pattern in excluded)
)

page_queue = [(clean_url(to_url(path)), obj) for path, obj in self.page_queue]
# Use obj.url for articles/pages to respect custom URL settings
# (e.g., ARTICLE_URL). Fall back to to_url(path) for index pages
# (archives, tags, etc.) which don't have a .url property as they're
# not Article or Page objects.
page_queue = [
(clean_url(obj.url if obj else to_url(path)), obj)
for path, obj in self.page_queue
]
page_queue = [page for page in page_queue if not is_excluded(page)]
page_queue.sort(key=lambda i: i[0])

Expand Down Expand Up @@ -174,12 +181,12 @@ def is_excluded(item):
)
priority = priorities[content_type]

# Use trans.url to respect custom URL settings for translations too
translations = "".join(
XML_TRANSLATION.format(
trans.lang,
siteurl,
# save_as path is already output-relative
clean_url(pathname2url(trans.save_as)),
clean_url(trans.url),
)
for trans in getattr(obj, "translations", ())
)
Expand Down
4 changes: 4 additions & 0 deletions pelican/plugins/sitemap/test_data/pages/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Title: About Us
Status: published

This is a test page for the about section.
7 changes: 7 additions & 0 deletions pelican/plugins/sitemap/test_data/translated.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Title: Translated Post
Date: 2023-07-12 13:00:00
Category: test
Lang: en
Slug: translated-post

This is an English article with a French translation.
7 changes: 7 additions & 0 deletions pelican/plugins/sitemap/test_data/translated_fr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Title: Article Traduit
Date: 2023-07-12 13:00:00
Category: test
Lang: fr
Slug: translated-post

Ceci est un article français.
107 changes: 107 additions & 0 deletions pelican/plugins/sitemap/test_sitemap.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,15 @@ def test_txt(self):
http://localhost/authors.html
http://localhost/categories.html
http://localhost/category/test.html
http://localhost/pages/about-us.html
http://localhost/tag/bar.html
http://localhost/tag/foo.html
http://localhost/tag/foobar.html
http://localhost/tags.html
http://localhost/test-post-daily.html
http://localhost/test-post.html
http://localhost/translated-post-fr.html
http://localhost/translated-post.html
"""
self.assertEqual(expected, contents)

Expand All @@ -80,3 +83,107 @@ def test_xml(self):
</url>
"""
self.assertIn(needle, contents)

def test_custom_article_url(self):
"""Test that custom ARTICLE_URL settings are respected in sitemap."""
settings = read_settings(
override={
"PATH": TEST_DATA,
"CACHE_CONTENT": False,
"SITEURL": "http://localhost",
"OUTPUT_PATH": self.output_path,
"PLUGINS": [sitemap],
"SITEMAP": {
"format": "txt",
},
# Custom URL settings like a user might have
"ARTICLE_URL": "blog/{slug}/",
"ARTICLE_SAVE_AS": "{slug}/index.html",
}
)
pelican = Pelican(settings=settings)
pelican.run()

with open(Path(self.output_path) / "sitemap.txt") as fd:
contents = fd.read()

# Articles should use ARTICLE_URL (blog/{slug}/),
# not ARTICLE_SAVE_AS ({slug}/index.html)
self.assertIn("http://localhost/blog/test-post/", contents)
self.assertIn("http://localhost/blog/test-post-daily/", contents)
# Should NOT contain the filesystem paths
self.assertNotIn("http://localhost/test-post/", contents)
self.assertNotIn("http://localhost/test-post-daily/", contents)

def test_translations_in_sitemap(self):
"""Test that translation links use trans.url and appear in sitemap."""
self._run_pelican(sitemap_format="xml")
with open(Path(self.output_path) / "sitemap.xml") as fd:
contents = fd.read()

# Verify translation alternate links exist
self.assertIn('<xhtml:link rel="alternate"', contents)
# Verify they reference the correct translated article URLs
self.assertIn('hreflang="en"', contents)
self.assertIn('hreflang="fr"', contents)
# Verify translated articles are in the sitemap
self.assertIn("http://localhost/translated-post.html", contents)
self.assertIn("http://localhost/translated-post-fr.html", contents)

def test_translations_with_custom_article_url(self):
"""Test translation links respect custom ARTICLE_URL settings."""
settings = read_settings(
override={
"PATH": TEST_DATA,
"CACHE_CONTENT": False,
"SITEURL": "http://localhost",
"OUTPUT_PATH": self.output_path,
"PLUGINS": [sitemap],
"SITEMAP": {
"format": "xml",
},
# Custom URL settings
"ARTICLE_URL": "blog/{slug}/",
"ARTICLE_SAVE_AS": "{slug}/index.html",
}
)
pelican = Pelican(settings=settings)
pelican.run()

with open(Path(self.output_path) / "sitemap.xml") as fd:
contents = fd.read()

# Verify translation link for English uses custom ARTICLE_URL
self.assertIn('ref="http://localhost/blog/translated-post/"', contents)
# Verify the French translation is also in sitemap
# (French uses ARTICLE_LANG_URL which defaults differently)
self.assertIn("translated-post-fr", contents)

def test_custom_page_url(self):
"""Test that custom PAGE_URL settings are respected in sitemap."""
settings = read_settings(
override={
"PATH": TEST_DATA,
"CACHE_CONTENT": False,
"SITEURL": "http://localhost",
"OUTPUT_PATH": self.output_path,
"PLUGINS": [sitemap],
"SITEMAP": {
"format": "txt",
},
# Custom URL settings for pages
"PAGE_URL": "pages/{slug}/",
"PAGE_SAVE_AS": "p/{slug}/index.html",
}
)
pelican = Pelican(settings=settings)
pelican.run()

with open(Path(self.output_path) / "sitemap.txt") as fd:
contents = fd.read()

# Pages should use PAGE_URL (pages/{slug}/),
# not PAGE_SAVE_AS (p/{slug}/index.html)
self.assertIn("http://localhost/pages/about-us/", contents)
# Should NOT contain the filesystem path
self.assertNotIn("http://localhost/p/about-us/", contents)