diff --git a/pelican/contents.py b/pelican/contents.py index 11459f77..1c2fc4c6 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -198,6 +198,18 @@ class Content: ) return False + # "{slug}.html" with an empty slug becomes ".html", which the + # development server then treats as the site root. + name = os.path.basename(str(self.save_as).replace("\\", "/")) + if name in {".html", ".", ""}: + logger.warning( + "Skipping %s: slug %r produces an invalid output filename %r", + self, + getattr(self, "slug", ""), + self.save_as, + ) + return False + return True def _has_valid_status(self) -> bool: diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 4e48e594..ea6b8937 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -811,6 +811,18 @@ class TestArticle(TestBase): article = Article(**article_kwargs) self.assertTrue(article._has_valid_save_as()) + def test_symbol_only_title_does_not_write_dot_html(self): + settings = get_settings() + article_kwargs = self._copy_page_kwargs() + article_kwargs["metadata"]["title"] = "~" + article_kwargs["metadata"]["date"] = datetime.datetime(2005, 2, 4, 18, 59) + article_kwargs["settings"] = settings + article = Article(**article_kwargs) + self.assertEqual(article.slug, "") + self.assertEqual(article.save_as, ".html") + self.assertFalse(article._has_valid_save_as()) + self.assertFalse(article.is_valid()) + class TestStatic(LoggedTestCase): def setUp(self):