diff --git a/docs/configuration.md b/docs/configuration.md index 0a27f527..c799b338 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -426,6 +426,18 @@ Will result in: Default: `None`. +### Category feeds + +Enables generating separate feeds for each category. + +Default: `false`. + +### Category feeds directory + +Directory to put category feeds into + +Default: `rss_dir`. + ---- ## Integration diff --git a/docs/schema.json b/docs/schema.json index 00145280..0ad85e0f 100644 --- a/docs/schema.json +++ b/docs/schema.json @@ -127,6 +127,18 @@ "markdownDescription": "https://guts.github.io/mkdocs-rss-plugin/configuration/#url-parameters", "type": "object", "default": null + }, + "category_feeds": { + "title": "Separate feeds for each category", + "markdownDescription": "https://guts.github.io/mkdocs-rss-plugin/configuration/#category_feeds", + "type": "boolean", + "default": false + }, + "category_feeds_dir": { + "title": "Directory to put category feeds into", + "markdownDescription": "https://guts.github.io/mkdocs-rss-plugin/configuration/#category_feeds_directory", + "type": "string", + "default": "rss" } }, "additionalProperties": false diff --git a/mkdocs.yml b/mkdocs.yml index 848ea8c3..39b90004 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -32,6 +32,8 @@ plugins: utm_source: "documentation" utm_medium: "RSS" utm_campaign: "feed-syndication" + category_feeds: false + category_feeds_dir: rss - search theme: diff --git a/mkdocs_rss_plugin/plugin.py b/mkdocs_rss_plugin/plugin.py index 92ac064f..f7a058e7 100644 --- a/mkdocs_rss_plugin/plugin.py +++ b/mkdocs_rss_plugin/plugin.py @@ -5,6 +5,7 @@ # ################################## # standard library +import os import logging from copy import deepcopy from datetime import datetime @@ -58,6 +59,8 @@ class GitRssPlugin(BasePlugin): ("match_path", config_options.Type(str, default=".*")), ("pretty_print", config_options.Type(bool, default=False)), ("url_parameters", config_options.Type(dict, default=None)), + ("category_feeds", config_options.Type(bool, default=False)), + ("category_feeds_dir", config_options.Type(str, default="rss")) ) def __init__(self): @@ -71,9 +74,13 @@ def __init__(self): self.meta_default_time = None # pages storage self.pages_to_filter = [] + # config + self.category_feeds = False + self.category_feeds_dir = "rss" # prepare output feeds self.feed_created = dict self.feed_updated = dict + self.category_feed = dict def on_config(self, config: config_options.Config) -> dict: """The config event is the first event called on build and @@ -123,6 +130,9 @@ def on_config(self, config: config_options.Config) -> dict: # pattern to match pages included in output self.match_path_pattern = compile(self.config.get("match_path")) + self.category_feeds = self.config.get("category_feeds") + self.category_feeds_dir = self.config.get("category_feeds_dir") + # date handling if self.config.get("date_from_meta") is not None: self.src_date_created = self.config.get("date_from_meta").get( @@ -162,6 +172,7 @@ def on_config(self, config: config_options.Config) -> dict: # create 2 final dicts self.feed_created = deepcopy(base_feed) self.feed_updated = deepcopy(base_feed) + self.category_feed = deepcopy(base_feed) # final feed url if base_feed.get("html_url"): @@ -272,6 +283,41 @@ def on_page_content( ) ) + def render_feed(self, pretty_print: bool, feed_name: str, feed: dict): + if pretty_print: + # load Jinja environment and template + env = Environment( + autoescape=select_autoescape(["html", "xml"]), + loader=FileSystemLoader(self.tpl_folder), + ) + + template = env.get_template(self.tpl_file.name) + + # write feed to file + with feed_name.open(mode="w", encoding="UTF8") as fifeed: + fifeed.write(template.render(feed=feed)) + else: + # load Jinja environment and template + env = Environment( + autoescape=select_autoescape(["html", "xml"]), + loader=FileSystemLoader(self.tpl_folder), + lstrip_blocks=True, + trim_blocks=True, + ) + template = env.get_template(self.tpl_file.name) + + # write feed to file stripping out spaces and new lines + with feed_name.open(mode="w", encoding="UTF8") as fifeed: + prev_char = "" + for char in template.render(feed=feed): + if char == "\n": + continue + if char == " " and prev_char == " ": + prev_char = char + continue + prev_char = char + fifeed.write(char) + def on_post_build(self, config: config_options.Config) -> dict: """The post_build event does not alter any variables. \ Use this event to call post-build scripts. \ @@ -312,52 +358,32 @@ def on_post_build(self, config: config_options.Config) -> dict: ) ) - # write feeds according to the pretty print option - if pretty_print: - # load Jinja environment and template - env = Environment( - autoescape=select_autoescape(["html", "xml"]), - loader=FileSystemLoader(self.tpl_folder), - ) - - template = env.get_template(self.tpl_file.name) - - # write feeds to files - with out_feed_created.open(mode="w", encoding="UTF8") as fifeed_created: - fifeed_created.write(template.render(feed=self.feed_created)) - - with out_feed_updated.open(mode="w", encoding="UTF8") as fifeed_updated: - fifeed_updated.write(template.render(feed=self.feed_updated)) - - else: - # load Jinja environment and template - env = Environment( - autoescape=select_autoescape(["html", "xml"]), - loader=FileSystemLoader(self.tpl_folder), - lstrip_blocks=True, - trim_blocks=True, - ) - template = env.get_template(self.tpl_file.name) + # Render main feeds + self.render_feed(pretty_print, out_feed_created, self.feed_created) + self.render_feed(pretty_print, out_feed_updated, self.feed_updated) + + # Render category feeds if enabled + if self.category_feeds: + feeds = {} + # collect feeds of pages per category + for page in self.pages_to_filter: + for category in page.categories: + feeds.setdefault(category, []).append(page) + + # Ensure target directory exists + path = Path(config.get("site_dir")) / self.category_feeds_dir + os.makedirs(path, exist_ok=True) + + for category, pages in feeds.items(): + # Create a feed per category + filename = f"{category}.xml" + feed = deepcopy(self.category_feed) + feed.get("entries").extend( + self.util.filter_pages( + pages=pages, + length = self.config.get("length", 20), + attribute = "created" + ) + ) + self.render_feed(pretty_print, path / filename, feed) - # write feeds to files stripping out spaces and new lines - with out_feed_created.open(mode="w", encoding="UTF8") as fifeed_created: - prev_char = "" - for char in template.render(feed=self.feed_created): - if char == "\n": - continue - if char == " " and prev_char == " ": - prev_char = char - continue - prev_char = char - fifeed_created.write(char) - - with out_feed_updated.open(mode="w", encoding="UTF8") as fifeed_updated: - for char in template.render(feed=self.feed_updated): - if char == "\n": - prev_char = char - continue - if char == " " and prev_char == " ": - prev_char = char - continue - prev_char = char - fifeed_updated.write(char) diff --git a/tests/test_config.py b/tests/test_config.py index 7309977e..838fce03 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -68,6 +68,8 @@ def test_plugin_config_defaults(self): "pretty_print": False, "match_path": ".*", "url_parameters": None, + "category_feeds": False, + "category_feeds_dir": "rss", } # load @@ -92,6 +94,8 @@ def test_plugin_config_image(self): "pretty_print": False, "match_path": ".*", "url_parameters": None, + "category_feeds": False, + "category_feeds_dir": "rss", } # custom config