Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions mkdocs_rss_plugin/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class _DateFromMeta(Config):
class _FeedsFilenamesConfig(Config):
"""Sub configuration for feeds filenames."""

atom_created = config_options.Type(str, default="feed_atom_created.xml")
atom_updated = config_options.Type(str, default="feed_atom_updated.xml")
json_created = config_options.Type(str, default="feed_json_created.json")
json_updated = config_options.Type(str, default="feed_json_updated.json")
rss_created = config_options.Type(str, default="feed_rss_created.xml")
Expand All @@ -41,6 +43,7 @@ class RssPluginConfig(Config):

abstract_chars_count = config_options.Type(int, default=160)
abstract_delimiter = config_options.Type(str, default="<!-- more -->")
atom_feed_enabled = config_options.Type(bool, default=True)
categories = config_options.Optional(
config_options.ListOfItems(config_options.Type(str))
)
Expand Down
2 changes: 2 additions & 0 deletions mkdocs_rss_plugin/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class PageInformation:
created: datetime | None = None
description: str | None = None
guid: str | None = None
html_content: str | None = None
image: tuple[str, str, int] | None = None
link: str | None = None
pub_date: str | None = None
Expand All @@ -77,6 +78,7 @@ class PageInformation:
class RssFeedBase:
"""Object describing a feed."""

atom_url: str | None = None
author: str | None = None
buildDate: str | None = None
copyright: str | None = None
Expand Down
133 changes: 122 additions & 11 deletions mkdocs_rss_plugin/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,13 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig:
return config

# Fail if any export option is enabled
if not any([self.config.json_feed_enabled, self.config.rss_feed_enabled]):
if not any(
[
self.config.atom_feed_enabled,
self.config.json_feed_enabled,
self.config.rss_feed_enabled,
]
):
logger.error(
"At least one export option has to be enabled. Plugin is disabled."
)
Expand Down Expand Up @@ -237,18 +243,24 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig:
# final feed url
if base_feed.html_url:
# concatenate both URLs
self.feed_created.rss_url = (
base_feed.html_url + self.config.feeds_filenames.rss_created
self.feed_created.atom_url = (
base_feed.html_url + self.config.feeds_filenames.atom_created
)
self.feed_updated.rss_url = (
base_feed.html_url + self.config.feeds_filenames.rss_updated
self.feed_updated.atom_url = (
base_feed.html_url + self.config.feeds_filenames.atom_updated
)
self.feed_created.json_url = (
base_feed.html_url + self.config.feeds_filenames.json_created
)
self.feed_updated.json_url = (
base_feed.html_url + self.config.feeds_filenames.json_updated
)
self.feed_created.rss_url = (
base_feed.html_url + self.config.feeds_filenames.rss_created
)
self.feed_updated.rss_url = (
base_feed.html_url + self.config.feeds_filenames.rss_updated
)
else:
logger.error(
"The variable `site_url` is not set in the MkDocs "
Expand All @@ -257,7 +269,9 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig:
)
self.feed_created.rss_url = self.feed_updated.json_url = (
self.feed_updated.rss_url
) = self.feed_updated.json_url = None
) = self.feed_updated.json_url = self.feed_created.atom_url = (
self.feed_updated.atom_url
) = None

# ending event
return config
Expand Down Expand Up @@ -323,6 +337,11 @@ def on_page_content(
else:
page_url_comments = None

# Store full HTML content if needed for Atom feed
page_content: str | None = None
if self.config.abstract_chars_count == -1:
page_content = html

# append to list to be filtered later
self.pages_to_filter.append(
PageInformation(
Expand All @@ -332,6 +351,7 @@ def on_page_content(
in_page=page, categories_labels=self.config.categories
),
comments_url=page_url_comments,
html_content=page_content,
created=page_dates[0],
description=self.util.get_description_or_abstract(
in_page=page,
Expand Down Expand Up @@ -362,13 +382,13 @@ def on_post_build(self, config: config_options.Config) -> None:
return

# pretty print or not
pretty_print = self.config.pretty_print
pretty_print: bool = self.config.pretty_print

# output filepaths
out_feed_created = Path(config.site_dir).joinpath(
out_rss_created = Path(config.site_dir).joinpath(
self.config.feeds_filenames.rss_created
)
out_feed_updated = Path(config.site_dir).joinpath(
out_rss_updated = Path(config.site_dir).joinpath(
self.config.feeds_filenames.rss_updated
)
out_json_created = Path(config.site_dir).joinpath(
Expand All @@ -377,6 +397,12 @@ def on_post_build(self, config: config_options.Config) -> None:
out_json_updated = Path(config.site_dir).joinpath(
self.config.feeds_filenames.json_updated
)
out_atom_created: Path = Path(config.site_dir).joinpath(
self.config.feeds_filenames.atom_created
)
out_atom_updated: Path = Path(config.site_dir).joinpath(
self.config.feeds_filenames.atom_updated
)

# created items
self.feed_created.entries.extend(
Expand Down Expand Up @@ -435,7 +461,7 @@ def on_post_build(self, config: config_options.Config) -> None:
page.pub_date = format_datetime(dt=page.created)

# write file
with out_feed_created.open(mode="w", encoding="UTF8") as fifeed_created:
with out_rss_created.open(mode="w", encoding="UTF8") as fifeed_created:
if pretty_print:
fifeed_created.write(template.render(feed=self.feed_created))
else:
Expand All @@ -457,7 +483,7 @@ def on_post_build(self, config: config_options.Config) -> None:
page.pub_date = format_datetime(dt=page.updated)

# write file
with out_feed_updated.open(mode="w", encoding="UTF8") as fifeed_updated:
with out_rss_updated.open(mode="w", encoding="UTF8") as fifeed_updated:
if pretty_print:
fifeed_updated.write(template.render(feed=self.feed_updated))
else:
Expand Down Expand Up @@ -487,3 +513,88 @@ def on_post_build(self, config: config_options.Config) -> None:
fp,
indent=4 if self.config.pretty_print else None,
)

# ATOM FEED
if self.config.atom_feed_enabled:
# Jinja environment depending on the pretty print option
if pretty_print:
env = Environment(
autoescape=select_autoescape(["html", "xml"]),
loader=FileSystemLoader(self.tpl_folder),
)
else:
env = Environment(
autoescape=select_autoescape(["html", "xml"]),
loader=FileSystemLoader(self.tpl_folder),
lstrip_blocks=True,
trim_blocks=True,
)

template = env.get_template("atom.xml.jinja2")

# -- Feed sorted by creation date
logger.debug(
"Fill creation dates and dump created feed into Atom template."
)

# Format dates for Atom (ISO 8601)
for page in self.feed_created.entries:
page.atom_published = page.created.isoformat()
page.atom_updated = page.updated.isoformat()

# Format feed buildDate for Atom (ISO 8601)
build_date_atom: str = datetime.fromtimestamp(
get_build_timestamp()
).isoformat()

# Temporarily store the ISO format
original_build_date: str = self.feed_created.buildDate
self.feed_created.buildDate = build_date_atom

# write file
with out_atom_created.open(mode="w", encoding="UTF8") as fiatom_created:
if pretty_print:
fiatom_created.write(template.render(feed=self.feed_created))
else:
prev_char = ""
for char in template.render(feed=asdict(self.feed_created)):
if char == "\n":
# convert new lines to spaces to preserve sentence structure
char = " "
if char == " " and prev_char == " ":
prev_char = char
continue
prev_char = char
fiatom_created.write(char)

# Restore original buildDate
self.feed_created.buildDate = original_build_date

# -- Feed sorted by update date
logger.debug("Fill update dates and dump updated feed into Atom template.")

for page in self.feed_updated.entries:
page.atom_published = page.created.isoformat()
page.atom_updated = page.updated.isoformat()

original_build_date = self.feed_updated.buildDate
self.feed_updated.buildDate = build_date_atom

# write file
with out_atom_updated.open(mode="w", encoding="UTF8") as fiatom_updated:
if pretty_print:
fiatom_updated.write(template.render(feed=self.feed_updated))
else:
prev_char = ""
for char in template.render(feed=asdict(self.feed_updated)):
if char == "\n":
# convert new lines to spaces to preserve sentence structure
char = " "
if char == " " and prev_char == " ":
prev_char = char
continue
prev_char = char
fiatom_updated.write(char)

# Restore original buildDate
self.feed_updated.buildDate = original_build_date
67 changes: 67 additions & 0 deletions mkdocs_rss_plugin/templates/atom.xml.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" {% if feed.language is not none %} xml:lang="{{ feed.language }}"{% endif %}>
{# Mandatory elements #}
{% if feed.title is not none %}<title>{{ feed.title|e }}</title>{% endif %}
{% if feed.html_url is not none %}<link href="{{ feed.html_url }}" rel="alternate" type="text/html"/>{% endif %}
{% if feed.atom_url is not none %}<link href="{{ feed.atom_url }}" rel="self" type="application/atom+xml"/>{% endif %}
{% if feed.atom_url is not none %}<id>{{ feed.atom_url }}</id>{% endif %}

{# Optional elements #}
{% if feed.description is not none %}<subtitle>{{ feed.description|e }}</subtitle>{% endif %}
{% if feed.author is not none %}
<author>
<name>{{ feed.author|e }}</name>
</author>
{% endif %}

{# Timestamps #}
<updated>{{ feed.buildDate }}</updated>

{# Credits #}
<generator>{{ feed.generator }}</generator>

{# Feed illustration #}
{% if feed.logo_url is defined %}
<logo>{{ feed.logo_url }}</logo>
{% endif %}

{# Entries #}
{% for item in feed.entries %}
<entry>
<title>{{ item.title|e }}</title>
{% if item.link is not none %}<link href="{{ item.link|e }}" rel="alternate" type="text/html"/>{% endif %}
{% if item.guid is not none %}<id>{{ item.guid }}</id>{% endif %}
<updated>{{ item.atom_updated }}</updated>
<published>{{ item.atom_published }}</published>

{# Authors loop #}
{% if item.authors is not none %}
{% for author in item.authors %}
<author>
<name>{{ author }}</name>
</author>
{% endfor %}
{% endif %}

{# Categories loop #}
{% if item.categories is not none %}
{% for category in item.categories %}
<category term="{{ category }}"/>
{% endfor %}
{% endif %}

<summary type="html">{{ item.description|e }}</summary>

{# Full content if available - only include if not empty #}
{% if item.content is not none and item.content|trim != "" %}
<content type="html">{{ item.content|e }}</content>
{% endif %}


{# Image enclosure #}
{% if item.image is not none %}
<link rel="enclosure" href="{{ item.image[0] }}" type="{{ item.image[1] }}" length="{{ item.image[2] }}"/>
{% endif %}
</entry>
{% endfor %}
</feed>
13 changes: 13 additions & 0 deletions mkdocs_rss_plugin/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,18 @@ def get_file_dates(
tuple[datetime, datetime]: tuple of timestamps (creation date, last commit date)
"""
logger.debug(f"Extracting dates for {in_page.file.src_uri}")

# handle temporary pages (e.g. archive pages of blog plugin of Material for Mkdocs)
if in_page.file.abs_src_path.startswith("/tmp"):
logger.debug(
f"Temporary page detected ({in_page.file.abs_src_path}). "
"Falling back to build date for both creation and update dates."
)
return (
get_build_datetime(),
get_build_datetime(),
)

# empty vars
dt_created = dt_updated = None
if meta_default_time is None:
Expand Down Expand Up @@ -619,6 +631,7 @@ def get_image(
if img_local_cache_path := self.social_cards.get_social_card_cache_path_for_page(
mkdocs_page=in_page
):
print("HA", img_local_cache_path, img_url)
img_length = img_local_cache_path.stat().st_size
img_type = guess_type(url=img_local_cache_path, strict=False)[0]
elif img_local_build_path := self.social_cards.get_social_card_build_path_for_page(
Expand Down
10 changes: 10 additions & 0 deletions tests/fixtures/mkdocs_atom_disabled.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
site_name: Test RSS Plugin
site_description: Test with Atom disabled
site_url: https://guts.github.io/mkdocs-rss-plugin

plugins:
- rss:
atom_feed_enabled: false

theme:
name: mkdocs
12 changes: 12 additions & 0 deletions tests/mkdocs_custom_atom_filenames.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
site_name: Test RSS Plugin with custom Atom filenames
site_description: Test Atom with custom filenames
site_url: https://guts.github.io/mkdocs-rss-plugin

plugins:
- rss:
feeds_filenames:
atom_created: atom.xml
atom_updated: atom-updated.xml

theme:
name: mkdocs
Loading
Loading