diff --git a/pelican/contents.py b/pelican/contents.py index c0a3e775..11459f77 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -584,7 +584,7 @@ class Page(Content): class Article(Content): - mandatory_properties = ("title", "date", "category") + mandatory_properties = ("title", "date") allowed_statuses = ("published", "hidden", "draft", "skip") default_status = "published" default_template = "article" diff --git a/pelican/generators.py b/pelican/generators.py index b7eef104..515257c4 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -513,7 +513,7 @@ class ArticlesGenerator(CachingGenerator): self.get_template(article.template), self.context, article=article, - category=article.category, + category=getattr(article, "category", None), override_output=hasattr(article, "override_save_as"), url=article.url, blog=True, @@ -764,7 +764,8 @@ class ArticlesGenerator(CachingGenerator): for article in self.articles: # only main articles are listed in categories and tags # not translations or hidden articles - self.categories[article.category].append(article) + if hasattr(article, "category"): + self.categories[article.category].append(article) if hasattr(article, "tags"): for tag in article.tags: self.tags[tag].append(article) diff --git a/pelican/readers.py b/pelican/readers.py index dfa76638..508d655f 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -746,7 +746,7 @@ def default_metadata(settings=None, process=None): if process: value = process(name, value) metadata[name] = value - if "DEFAULT_CATEGORY" in settings: + if "DEFAULT_CATEGORY" in settings and settings.get("CATEGORY_SAVE_AS"): value = settings["DEFAULT_CATEGORY"] if process: value = process("category", value) @@ -811,7 +811,7 @@ def parse_path_metadata(source_path, settings=None, process=None): checks = [] for key, data in [("FILENAME_METADATA", base), ("PATH_METADATA", source_path)]: checks.append((settings.get(key, None), data)) - if settings.get("USE_FOLDER_AS_CATEGORY", None): + if settings.get("USE_FOLDER_AS_CATEGORY") and settings.get("CATEGORY_SAVE_AS"): checks.append(("(?P.*)", subdir)) for regexp, data in checks: if regexp and data: diff --git a/pelican/tests/test_theme.py b/pelican/tests/test_theme.py index 25d23b97..aa996081 100644 --- a/pelican/tests/test_theme.py +++ b/pelican/tests/test_theme.py @@ -133,6 +133,44 @@ class TestTemplateInheritance(LoggedTestCase): self.assertNotIn("Proudly powered by", content) self.assertIn("New footer", content) + def test_disabled_categories_no_links(self): + """When CATEGORY_SAVE_AS is empty, articles without an explicit + category should not get a default category or show category links.""" + + # Create an article without a Category metadata line + content_dir = mkdtemp(prefix="pelican_test_content.") + with open(os.path.join(content_dir, "no_category.md"), "w") as f: + f.write( + "Title: No Category Article\n" + "Date: 2024-01-01\n\n" + "Article without a category.\n" + ) + + try: + settings = read_settings( + path=None, + override={ + "THEME": "simple", + "PATH": content_dir, + "OUTPUT_PATH": self.temp_output, + "CACHE_PATH": self.temp_cache, + "SITEURL": "http://example.com", + "CATEGORY_SAVE_AS": None, + "CATEGORY_URL": None, + "CATEGORIES_SAVE_AS": None, + }, + ) + + pelican = Pelican(settings=settings) + mute(True)(pelican.run)() + + with open(os.path.join(self.temp_output, "no-category-article.html")) as f: + content = f.read() + + self.assertNotIn("Category:", content) + finally: + rmtree(content_dir) + def test_category_and_tag_feed_titles_use_slug(self): """Feed link titles on category/tag pages should have unique titles."""