@@ -111,7 +111,13 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig:
111111 return config
112112
113113 # Fail if any export option is enabled
114- if not any ([self .config .json_feed_enabled , self .config .rss_feed_enabled ]):
114+ if not any (
115+ [
116+ self .config .atom_feed_enabled ,
117+ self .config .json_feed_enabled ,
118+ self .config .rss_feed_enabled ,
119+ ]
120+ ):
115121 logger .error (
116122 "At least one export option has to be enabled. Plugin is disabled."
117123 )
@@ -254,18 +260,24 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig:
254260 # final feed url
255261 if base_feed .html_url :
256262 # concatenate both URLs
257- self .feed_created .rss_url = (
258- base_feed .html_url + self .config .feeds_filenames .rss_created
263+ self .feed_created .atom_url = (
264+ base_feed .html_url + self .config .feeds_filenames .atom_created
259265 )
260- self .feed_updated .rss_url = (
261- base_feed .html_url + self .config .feeds_filenames .rss_updated
266+ self .feed_updated .atom_url = (
267+ base_feed .html_url + self .config .feeds_filenames .atom_updated
262268 )
263269 self .feed_created .json_url = (
264270 base_feed .html_url + self .config .feeds_filenames .json_created
265271 )
266272 self .feed_updated .json_url = (
267273 base_feed .html_url + self .config .feeds_filenames .json_updated
268274 )
275+ self .feed_created .rss_url = (
276+ base_feed .html_url + self .config .feeds_filenames .rss_created
277+ )
278+ self .feed_updated .rss_url = (
279+ base_feed .html_url + self .config .feeds_filenames .rss_updated
280+ )
269281 else :
270282 logger .error (
271283 "The variable `site_url` is not set in the MkDocs "
@@ -274,7 +286,9 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig:
274286 )
275287 self .feed_created .rss_url = self .feed_updated .json_url = (
276288 self .feed_updated .rss_url
277- ) = self .feed_updated .json_url = None
289+ ) = self .feed_updated .json_url = self .feed_created .atom_url = (
290+ self .feed_updated .atom_url
291+ ) = None
278292
279293 # ending event
280294 return config
@@ -340,6 +354,11 @@ def on_page_content(
340354 else :
341355 page_url_comments = None
342356
357+ # Store full HTML content if needed for Atom feed
358+ page_content : str | None = None
359+ if self .config .abstract_chars_count == - 1 :
360+ page_content = html
361+
343362 # append to list to be filtered later
344363 self .pages_to_filter .append (
345364 PageInformation (
@@ -349,6 +368,7 @@ def on_page_content(
349368 in_page = page , categories_labels = self .config .categories
350369 ),
351370 comments_url = page_url_comments ,
371+ html_content = page_content ,
352372 created = page_dates [0 ],
353373 description = self .util .get_description_or_abstract (
354374 in_page = page ,
@@ -379,13 +399,13 @@ def on_post_build(self, config: config_options.Config) -> None:
379399 return
380400
381401 # pretty print or not
382- pretty_print = self .config .pretty_print
402+ pretty_print : bool = self .config .pretty_print
383403
384404 # output filepaths
385- out_feed_created = Path (config .site_dir ).joinpath (
405+ out_rss_created = Path (config .site_dir ).joinpath (
386406 self .config .feeds_filenames .rss_created
387407 )
388- out_feed_updated = Path (config .site_dir ).joinpath (
408+ out_rss_updated = Path (config .site_dir ).joinpath (
389409 self .config .feeds_filenames .rss_updated
390410 )
391411 out_json_created = Path (config .site_dir ).joinpath (
@@ -394,6 +414,12 @@ def on_post_build(self, config: config_options.Config) -> None:
394414 out_json_updated = Path (config .site_dir ).joinpath (
395415 self .config .feeds_filenames .json_updated
396416 )
417+ out_atom_created : Path = Path (config .site_dir ).joinpath (
418+ self .config .feeds_filenames .atom_created
419+ )
420+ out_atom_updated : Path = Path (config .site_dir ).joinpath (
421+ self .config .feeds_filenames .atom_updated
422+ )
397423
398424 # stylesheet for RSS feed
399425 if self .config .stylesheet == "auto" :
@@ -458,7 +484,7 @@ def on_post_build(self, config: config_options.Config) -> None:
458484 page .pub_date = format_datetime (dt = page .created )
459485
460486 # write file
461- with out_feed_created .open (mode = "w" , encoding = "UTF8" ) as fifeed_created :
487+ with out_rss_created .open (mode = "w" , encoding = "UTF8" ) as fifeed_created :
462488 if pretty_print :
463489 fifeed_created .write (template .render (feed = self .feed_created ))
464490 else :
@@ -480,7 +506,7 @@ def on_post_build(self, config: config_options.Config) -> None:
480506 page .pub_date = format_datetime (dt = page .updated )
481507
482508 # write file
483- with out_feed_updated .open (mode = "w" , encoding = "UTF8" ) as fifeed_updated :
509+ with out_rss_updated .open (mode = "w" , encoding = "UTF8" ) as fifeed_updated :
484510 if pretty_print :
485511 fifeed_updated .write (template .render (feed = self .feed_updated ))
486512 else :
@@ -510,3 +536,88 @@ def on_post_build(self, config: config_options.Config) -> None:
510536 fp ,
511537 indent = 4 if self .config .pretty_print else None ,
512538 )
539+
540+ # ATOM FEED
541+ if self .config .atom_feed_enabled :
542+ # Jinja environment depending on the pretty print option
543+ if pretty_print :
544+ env = Environment (
545+ autoescape = select_autoescape (["html" , "xml" ]),
546+ loader = FileSystemLoader (self .tpl_folder ),
547+ )
548+ else :
549+ env = Environment (
550+ autoescape = select_autoescape (["html" , "xml" ]),
551+ loader = FileSystemLoader (self .tpl_folder ),
552+ lstrip_blocks = True ,
553+ trim_blocks = True ,
554+ )
555+
556+ template = env .get_template ("atom.xml.jinja2" )
557+
558+ # -- Feed sorted by creation date
559+ logger .debug (
560+ "Fill creation dates and dump created feed into Atom template."
561+ )
562+
563+ # Format dates for Atom (ISO 8601)
564+ for page in self .feed_created .entries :
565+ page .atom_published = page .created .isoformat ()
566+ page .atom_updated = page .updated .isoformat ()
567+
568+ # Format feed buildDate for Atom (ISO 8601)
569+ build_date_atom : str = datetime .fromtimestamp (
570+ get_build_timestamp ()
571+ ).isoformat ()
572+
573+ # Temporarily store the ISO format
574+ original_build_date : str = self .feed_created .buildDate
575+ self .feed_created .buildDate = build_date_atom
576+
577+ # write file
578+ with out_atom_created .open (mode = "w" , encoding = "UTF8" ) as fiatom_created :
579+ if pretty_print :
580+ fiatom_created .write (template .render (feed = self .feed_created ))
581+ else :
582+ prev_char = ""
583+ for char in template .render (feed = asdict (self .feed_created )):
584+ if char == "\n " :
585+ # convert new lines to spaces to preserve sentence structure
586+ char = " "
587+ if char == " " and prev_char == " " :
588+ prev_char = char
589+ continue
590+ prev_char = char
591+ fiatom_created .write (char )
592+
593+ # Restore original buildDate
594+ self .feed_created .buildDate = original_build_date
595+
596+ # -- Feed sorted by update date
597+ logger .debug ("Fill update dates and dump updated feed into Atom template." )
598+
599+ for page in self .feed_updated .entries :
600+ page .atom_published = page .created .isoformat ()
601+ page .atom_updated = page .updated .isoformat ()
602+
603+ original_build_date = self .feed_updated .buildDate
604+ self .feed_updated .buildDate = build_date_atom
605+
606+ # write file
607+ with out_atom_updated .open (mode = "w" , encoding = "UTF8" ) as fiatom_updated :
608+ if pretty_print :
609+ fiatom_updated .write (template .render (feed = self .feed_updated ))
610+ else :
611+ prev_char = ""
612+ for char in template .render (feed = asdict (self .feed_updated )):
613+ if char == "\n " :
614+ # convert new lines to spaces to preserve sentence structure
615+ char = " "
616+ if char == " " and prev_char == " " :
617+ prev_char = char
618+ continue
619+ prev_char = char
620+ fiatom_updated .write (char )
621+
622+ # Restore original buildDate
623+ self .feed_updated .buildDate = original_build_date
0 commit comments