mirror of
https://github.com/getpelican/pelican.git
synced 2026-10-16 04:36:55 +02:00
fix: Always assign DEFAULT_CATEGORY
PR #3589 tied category assignment to `CATEGORY_SAVE_AS`, so disabling category pages also removed article categories and broke `{category}` in URL patterns (#3596). PR #3597 restored folder-based categories, but articles outside a subfolder still lost `DEFAULT_CATEGORY`. This change assigns it again, so `CATEGORY_SAVE_AS` only controls whether category pages are generated. Setting `DEFAULT_CATEGORY = None` is now the documented way to have no categories. This also stops the Simple and NotMyIdea themes from linking to category pages that don't exist.
This commit is contained in:
parent
e54689de4e
commit
edd1f37cd4
9 changed files with 101 additions and 19 deletions
|
|
@ -65,6 +65,10 @@ Basic settings
|
|||
|
||||
The default category to fall back on. The default is ``"misc"``.
|
||||
|
||||
To publish articles without any category, set ``DEFAULT_CATEGORY = None``
|
||||
and ``USE_FOLDER_AS_CATEGORY = False``. Articles will then only have a
|
||||
category if one is specified in their metadata.
|
||||
|
||||
.. data:: DISPLAY_PAGES_ON_MENU
|
||||
|
||||
Whether to display pages on the menu of the template. Templates may or may
|
||||
|
|
@ -590,6 +594,8 @@ This would save your articles into something like
|
|||
.. data:: CATEGORY_SAVE_AS
|
||||
|
||||
The location to save a category. The default is ``"category/{slug}.html"``.
|
||||
Set this to ``""`` to disable category pages. Articles still keep their
|
||||
categories, which can be used in URL patterns such as ``ARTICLE_SAVE_AS``.
|
||||
|
||||
.. data:: TAG_URL
|
||||
|
||||
|
|
|
|||
|
|
@ -82,9 +82,9 @@ def ensure_metadata_list(text):
|
|||
|
||||
def _process_if_nonempty(processor, name, settings):
|
||||
"""Removes extra whitespace from name and applies a metadata processor.
|
||||
If name is empty or all whitespace, returns _DISCARD instead.
|
||||
If name is None, empty, or all whitespace, returns _DISCARD instead.
|
||||
"""
|
||||
name = name.strip()
|
||||
name = (name or "").strip()
|
||||
return processor(name, settings) if name else _DISCARD
|
||||
|
||||
|
||||
|
|
@ -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 and settings.get("CATEGORY_SAVE_AS"):
|
||||
if "DEFAULT_CATEGORY" in settings:
|
||||
value = settings["DEFAULT_CATEGORY"]
|
||||
if process:
|
||||
value = process("category", value)
|
||||
|
|
|
|||
|
|
@ -405,7 +405,7 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
settings = get_settings(PATH=content_dir)
|
||||
settings["CACHE_PATH"] = self.temp_cache
|
||||
settings["USE_FOLDER_AS_CATEGORY"] = False
|
||||
settings["CATEGORY_SAVE_AS"] = None # No default category fallback
|
||||
settings["DEFAULT_CATEGORY"] = None # No default category fallback
|
||||
context = get_context(settings)
|
||||
|
||||
generator = ArticlesGenerator(
|
||||
|
|
|
|||
|
|
@ -161,16 +161,44 @@ class DefaultReaderTest(ReaderTest):
|
|||
)
|
||||
|
||||
def test_path_metadata_categories(self):
|
||||
"""Ensure folder names become categories even when category pages are
|
||||
disabled, so {category} still works in URL patterns."""
|
||||
nested_content_path = os.path.join(CUR_DIR, "nested_content")
|
||||
r = readers.Readers(
|
||||
settings=get_settings(USE_FOLDER_AS_CATEGORY=True, CATEGORY_SAVE_AS=None)
|
||||
)
|
||||
mainpage = r.read_file(base_path=nested_content_path, path="maindir/maindir.md")
|
||||
self.assertDictHasSubset(mainpage.metadata, {"category": "maindir"})
|
||||
subpage = r.read_file(
|
||||
base_path=nested_content_path, path="maindir/subdir/subdir.md"
|
||||
)
|
||||
self.assertDictHasSubset(subpage.metadata, {"category": "subdir"})
|
||||
for category_save_as in ("", None, False):
|
||||
with self.subTest(CATEGORY_SAVE_AS=category_save_as):
|
||||
r = readers.Readers(
|
||||
settings=get_settings(
|
||||
USE_FOLDER_AS_CATEGORY=True, CATEGORY_SAVE_AS=category_save_as
|
||||
)
|
||||
)
|
||||
mainpage = r.read_file(
|
||||
base_path=nested_content_path, path="maindir/maindir.md"
|
||||
)
|
||||
self.assertDictHasSubset(mainpage.metadata, {"category": "maindir"})
|
||||
subpage = r.read_file(
|
||||
base_path=nested_content_path, path="maindir/subdir/subdir.md"
|
||||
)
|
||||
self.assertDictHasSubset(subpage.metadata, {"category": "subdir"})
|
||||
|
||||
def test_default_category_when_category_pages_disabled(self):
|
||||
for category_save_as in ("", None, False):
|
||||
with self.subTest(CATEGORY_SAVE_AS=category_save_as):
|
||||
page = self.read_file(
|
||||
path="article.rst",
|
||||
DEFAULT_CATEGORY="misc",
|
||||
CATEGORY_SAVE_AS=category_save_as,
|
||||
)
|
||||
self.assertEqual(page.metadata["category"], "misc")
|
||||
|
||||
def test_empty_default_category_omits_category(self):
|
||||
for default_category in ("", None):
|
||||
with self.subTest(DEFAULT_CATEGORY=default_category):
|
||||
page = self.read_file(
|
||||
path="article.rst",
|
||||
DEFAULT_CATEGORY=default_category,
|
||||
USE_FOLDER_AS_CATEGORY=False,
|
||||
)
|
||||
self.assertNotIn("category", page.metadata)
|
||||
|
||||
|
||||
class RstReaderTest(ReaderTest):
|
||||
|
|
|
|||
|
|
@ -134,8 +134,8 @@ class TestTemplateInheritance(LoggedTestCase):
|
|||
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."""
|
||||
"""When DEFAULT_CATEGORY is empty, articles without an explicit
|
||||
category should not show a category."""
|
||||
|
||||
# Create an article without a Category metadata line
|
||||
content_dir = mkdtemp(prefix="pelican_test_content.")
|
||||
|
|
@ -155,6 +155,8 @@ class TestTemplateInheritance(LoggedTestCase):
|
|||
"OUTPUT_PATH": self.temp_output,
|
||||
"CACHE_PATH": self.temp_cache,
|
||||
"SITEURL": "http://example.com",
|
||||
"DEFAULT_CATEGORY": "",
|
||||
"USE_FOLDER_AS_CATEGORY": False,
|
||||
"CATEGORY_SAVE_AS": None,
|
||||
"CATEGORY_URL": None,
|
||||
"CATEGORIES_SAVE_AS": None,
|
||||
|
|
@ -171,6 +173,45 @@ class TestTemplateInheritance(LoggedTestCase):
|
|||
finally:
|
||||
rmtree(content_dir)
|
||||
|
||||
def test_disabled_category_pages_no_links(self):
|
||||
"""When CATEGORY_SAVE_AS is empty, articles keep their category, but
|
||||
themes should not link to category pages that are not generated."""
|
||||
|
||||
content_dir = mkdtemp(prefix="pelican_test_content.")
|
||||
os.makedirs(os.path.join(content_dir, "blog"))
|
||||
with open(os.path.join(content_dir, "blog", "in_blog.md"), "w") as f:
|
||||
f.write("Title: In Blog\nDate: 2024-01-01\n\nArticle in a folder.\n")
|
||||
|
||||
try:
|
||||
for theme in ("simple", "notmyidea"):
|
||||
with self.subTest(theme=theme):
|
||||
settings = read_settings(
|
||||
path=None,
|
||||
override={
|
||||
"THEME": theme,
|
||||
"PATH": content_dir,
|
||||
"OUTPUT_PATH": self.temp_output,
|
||||
"CACHE_PATH": self.temp_cache,
|
||||
"SITEURL": "http://example.com",
|
||||
"ARTICLE_SAVE_AS": "{category}/{slug}.html",
|
||||
"CATEGORY_SAVE_AS": False,
|
||||
"CATEGORIES_SAVE_AS": False,
|
||||
},
|
||||
)
|
||||
|
||||
pelican = Pelican(settings=settings)
|
||||
mute(True)(pelican.run)()
|
||||
|
||||
with open(
|
||||
os.path.join(self.temp_output, "blog", "in-blog.html")
|
||||
) as f:
|
||||
content = f.read()
|
||||
|
||||
self.assertIn("blog", content)
|
||||
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."""
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@
|
|||
{% endfor %}
|
||||
</address>
|
||||
{% endif %}
|
||||
<p>In <a href="{{ SITEURL }}/{{ article.category.url }}">{{ article.category }}</a>.</p>
|
||||
{% if article.category %}
|
||||
<p>In {% if CATEGORY_SAVE_AS %}<a href="{{ SITEURL }}/{{ article.category.url }}">{{ article.category }}</a>{% else %}{{ article.category }}{% endif %}.</p>
|
||||
{% endif %}
|
||||
{% include 'taglist.html' %}
|
||||
{% import 'translations.html' as translations with context %}
|
||||
{{ translations.translations_for(article) }}
|
||||
|
|
|
|||
2
pelican/themes/notmyidea/templates/base.html
vendored
2
pelican/themes/notmyidea/templates/base.html
vendored
|
|
@ -30,7 +30,7 @@
|
|||
<li{% if pg == page %} class="active"{% endif %}><a href="{{ SITEURL }}/{{ pg.url }}">{{ pg.title }}</a></li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if DISPLAY_CATEGORIES_ON_MENU -%}
|
||||
{% if DISPLAY_CATEGORIES_ON_MENU and CATEGORY_SAVE_AS -%}
|
||||
{% for cat, null in categories %}
|
||||
<li{% if cat == category %} class="active"{% endif %}><a href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a></li>
|
||||
{% endfor %}
|
||||
|
|
|
|||
7
pelican/themes/simple/templates/article.html
vendored
7
pelican/themes/simple/templates/article.html
vendored
|
|
@ -49,7 +49,12 @@
|
|||
{% endif %}
|
||||
{% if article.category %}
|
||||
<p>
|
||||
Category: <a href="{{ SITEURL }}/{{ article.category.url }}">{{ article.category }}</a>
|
||||
Category:
|
||||
{% if CATEGORY_SAVE_AS %}
|
||||
<a href="{{ SITEURL }}/{{ article.category.url }}">{{ article.category }}</a>
|
||||
{% else %}
|
||||
{{ article.category }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if article.tags %}
|
||||
|
|
|
|||
2
pelican/themes/simple/templates/base.html
vendored
2
pelican/themes/simple/templates/base.html
vendored
|
|
@ -58,7 +58,7 @@
|
|||
<li><a href="{{ SITEURL }}/{{ p.url }}" {% if p==page %} aria-current="page" {% endif %}>{{ p.title }}</a></li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if DISPLAY_CATEGORIES_ON_MENU %}
|
||||
{% if DISPLAY_CATEGORIES_ON_MENU and CATEGORY_SAVE_AS %}
|
||||
{% for cat, null in categories %}
|
||||
<li><a href="{{ SITEURL }}/{{ cat.url }}" {% if cat==category %} aria-current="page" {% endif %}>{{ cat}}</a></li>
|
||||
{% endfor %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue