diff --git a/docs/content.rst b/docs/content.rst index 7d7e2cfa..6c58aa11 100644 --- a/docs/content.rst +++ b/docs/content.rst @@ -89,7 +89,7 @@ contains a list of reserved metadata keywords: ``summary`` Brief description of content for index pages ``lang`` Content language ID (``en``, ``fr``, etc.) ``translation`` If content is a translation of another (``true`` or ``false``) -``status`` Content status: ``draft``, ``hidden``, or ``published`` +``status`` Content status: ``draft``, ``hidden``, ``skip``, or ``published`` ``template`` Name of template to use to generate content (without extension) ``save_as`` Save content to this relative file path ``url`` URL to use for this article/page @@ -633,6 +633,13 @@ attribute. Hidden posts will be output to ``ARTICLE_SAVE_AS`` as expected, but are not included by default in tag, category, and author indexes, nor in the main article feed. This has the effect of creating an "unlisted" post. +Skip Posts +========== + +Posts marked with ``skip`` status are ignored entirely. They are not processed +nor output to the ``ARTICLE_SAVE_AS`` path. Such posts will similarly not be +included in indexes or feeds. + .. _W3C ISO 8601: https://www.w3.org/TR/NOTE-datetime .. _AsciiDoc: https://asciidoc.org .. _Pelican Plugins: https://github.com/pelican-plugins diff --git a/pelican/contents.py b/pelican/contents.py index 5a403261..0769f875 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -547,9 +547,29 @@ class Content: self._summary = self.metadata["summary"] +class SkipStub(Content): + """Stub class representing content that should not be processed in any way.""" + + def __init__( + self, content, metadata=None, settings=None, source_path=None, context=None + ): + self.source_path = source_path + + def is_valid(self): + return False + + @property + def content(self): + raise NotImplementedError("Stub content should not be read") + + @property + def save_as(self): + raise NotImplementedError("Stub content cannot be saved") + + class Page(Content): mandatory_properties = ("title",) - allowed_statuses = ("published", "hidden", "draft") + allowed_statuses = ("published", "hidden", "draft", "skip") default_status = "published" default_template = "page" @@ -560,7 +580,7 @@ class Page(Content): class Article(Content): mandatory_properties = ("title", "date", "category") - allowed_statuses = ("published", "hidden", "draft") + allowed_statuses = ("published", "hidden", "draft", "skip") default_status = "published" default_template = "article" diff --git a/pelican/generators.py b/pelican/generators.py index 548c494f..6b44d79e 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -19,7 +19,7 @@ from jinja2 import ( ) from pelican.cache import FileStampDataCacher -from pelican.contents import Article, Page, Static +from pelican.contents import Article, Page, SkipStub, Static from pelican.plugins import signals from pelican.plugins._utils import plugin_enabled from pelican.readers import Readers @@ -690,6 +690,10 @@ class ArticlesGenerator(CachingGenerator): self._add_failed_source_path(f) continue + if isinstance(article, SkipStub): + logger.debug("Safely skipping %s", f) + continue + if not article.is_valid(): self._add_failed_source_path(f) continue @@ -702,6 +706,8 @@ class ArticlesGenerator(CachingGenerator): all_drafts.append(article) elif article.status == "hidden": hidden_articles.append(article) + elif article.status == "skip": + raise AssertionError("Documents with 'skip' status should be skipped") self.add_source_path(article) self.add_static_links(article) @@ -899,6 +905,10 @@ class PagesGenerator(CachingGenerator): self._add_failed_source_path(f) continue + if isinstance(page, SkipStub): + logger.debug("Safely skipping %s", f) + continue + if not page.is_valid(): self._add_failed_source_path(f) continue @@ -911,6 +921,9 @@ class PagesGenerator(CachingGenerator): hidden_pages.append(page) elif page.status == "draft": draft_pages.append(page) + elif page.status == "skip": + raise AssertionError("Documents with 'skip' status should be skipped") + self.add_source_path(page) self.add_static_links(page) diff --git a/pelican/readers.py b/pelican/readers.py index 3d0e8d58..59aa7ca3 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -15,7 +15,7 @@ from docutils.writers.html4css1 import HTMLTranslator, Writer from pelican import rstdirectives # NOQA from pelican.cache import FileStampDataCacher -from pelican.contents import Author, Category, Page, Tag +from pelican.contents import Author, Category, Page, SkipStub, Tag from pelican.plugins import signals from pelican.utils import file_suffix, get_date, pelican_open, posixize_path @@ -669,6 +669,9 @@ class Readers(FileStampDataCacher): ) context_signal.send(context_sender, metadata=metadata) + if metadata.get("status") == "skip": + content_class = SkipStub + return content_class( content=content, metadata=metadata, diff --git a/pelican/tests/content/article_skip.md b/pelican/tests/content/article_skip.md new file mode 100644 index 00000000..02cd5a52 --- /dev/null +++ b/pelican/tests/content/article_skip.md @@ -0,0 +1,5 @@ +Title: Skipped article +Date: 2024-06-30 +Status: skip + +This content will not be rendered. diff --git a/pelican/tests/test_cache.py b/pelican/tests/test_cache.py index a1bbc559..8876a8a5 100644 --- a/pelican/tests/test_cache.py +++ b/pelican/tests/test_cache.py @@ -183,15 +183,16 @@ class TestCache(unittest.TestCase): generator.readers.read_file = MagicMock() generator.generate_context() """ - 6 files don't get cached because they were not valid + 7 files don't get cached because they were not valid - article_with_attributes_containing_double_quotes.html - article_with_comments.html - article_with_null_attributes.html - 2012-11-30_md_w_filename_meta#foo-bar.md - empty.md - empty_with_bom.md + - article_skip.md """ - self.assertEqual(generator.readers.read_file.call_count, 6) + self.assertEqual(generator.readers.read_file.call_count, 7) def test_article_reader_content_caching(self): """Test raw article content caching at the reader level"""