@@ -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 )
@@ -255,18 +261,24 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig:
255261 # final feed url
256262 if base_feed .html_url :
257263 # concatenate both URLs
258- self .feed_created .rss_url = (
259- base_feed .html_url + self .config .feeds_filenames .rss_created
264+ self .feed_created .atom_url = (
265+ base_feed .html_url + self .config .feeds_filenames .atom_created
260266 )
261- self .feed_updated .rss_url = (
262- base_feed .html_url + self .config .feeds_filenames .rss_updated
267+ self .feed_updated .atom_url = (
268+ base_feed .html_url + self .config .feeds_filenames .atom_updated
263269 )
264270 self .feed_created .json_url = (
265271 base_feed .html_url + self .config .feeds_filenames .json_created
266272 )
267273 self .feed_updated .json_url = (
268274 base_feed .html_url + self .config .feeds_filenames .json_updated
269275 )
276+ self .feed_created .rss_url = (
277+ base_feed .html_url + self .config .feeds_filenames .rss_created
278+ )
279+ self .feed_updated .rss_url = (
280+ base_feed .html_url + self .config .feeds_filenames .rss_updated
281+ )
270282 else :
271283 logger .error (
272284 "The variable `site_url` is not set in the MkDocs "
@@ -275,7 +287,9 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig:
275287 )
276288 self .feed_created .rss_url = self .feed_updated .json_url = (
277289 self .feed_updated .rss_url
278- ) = self .feed_updated .json_url = None
290+ ) = self .feed_updated .json_url = self .feed_created .atom_url = (
291+ self .feed_updated .atom_url
292+ ) = None
279293
280294 # ending event
281295 return config
@@ -341,6 +355,11 @@ def on_page_content(
341355 else :
342356 page_url_comments = None
343357
358+ # Store full HTML content if needed for Atom feed
359+ page_content : str | None = None
360+ if self .config .abstract_chars_count == - 1 :
361+ page_content = html
362+
344363 # append to list to be filtered later
345364 self .pages_to_filter .append (
346365 PageInformation (
@@ -350,6 +369,7 @@ def on_page_content(
350369 in_page = page , categories_labels = self .config .categories
351370 ),
352371 comments_url = page_url_comments ,
372+ html_content = page_content ,
353373 created = page_dates [0 ],
354374 description = self .util .get_description_or_abstract (
355375 in_page = page ,
@@ -380,13 +400,13 @@ def on_post_build(self, config: config_options.Config) -> None:
380400 return
381401
382402 # pretty print or not
383- pretty_print = self .config .pretty_print
403+ pretty_print : bool = self .config .pretty_print
384404
385405 # output filepaths
386- out_feed_created = Path (config .site_dir ).joinpath (
406+ out_rss_created = Path (config .site_dir ).joinpath (
387407 self .config .feeds_filenames .rss_created
388408 )
389- out_feed_updated = Path (config .site_dir ).joinpath (
409+ out_rss_updated = Path (config .site_dir ).joinpath (
390410 self .config .feeds_filenames .rss_updated
391411 )
392412 out_json_created = Path (config .site_dir ).joinpath (
@@ -395,6 +415,12 @@ def on_post_build(self, config: config_options.Config) -> None:
395415 out_json_updated = Path (config .site_dir ).joinpath (
396416 self .config .feeds_filenames .json_updated
397417 )
418+ out_atom_created : Path = Path (config .site_dir ).joinpath (
419+ self .config .feeds_filenames .atom_created
420+ )
421+ out_atom_updated : Path = Path (config .site_dir ).joinpath (
422+ self .config .feeds_filenames .atom_updated
423+ )
398424
399425 # stylesheet for RSS feed
400426 if self .config .stylesheet == "auto" :
@@ -459,7 +485,7 @@ def on_post_build(self, config: config_options.Config) -> None:
459485 page .pub_date = format_datetime (dt = page .created )
460486
461487 # write file
462- with out_feed_created .open (mode = "w" , encoding = "UTF8" ) as fifeed_created :
488+ with out_rss_created .open (mode = "w" , encoding = "UTF8" ) as fifeed_created :
463489 if pretty_print :
464490 fifeed_created .write (template .render (feed = self .feed_created ))
465491 else :
@@ -481,7 +507,7 @@ def on_post_build(self, config: config_options.Config) -> None:
481507 page .pub_date = format_datetime (dt = page .updated )
482508
483509 # write file
484- with out_feed_updated .open (mode = "w" , encoding = "UTF8" ) as fifeed_updated :
510+ with out_rss_updated .open (mode = "w" , encoding = "UTF8" ) as fifeed_updated :
485511 if pretty_print :
486512 fifeed_updated .write (template .render (feed = self .feed_updated ))
487513 else :
@@ -511,3 +537,88 @@ def on_post_build(self, config: config_options.Config) -> None:
511537 fp ,
512538 indent = 4 if self .config .pretty_print else None ,
513539 )
540+
541+ # ATOM FEED
542+ if self .config .atom_feed_enabled :
543+ # Jinja environment depending on the pretty print option
544+ if pretty_print :
545+ env = Environment (
546+ autoescape = select_autoescape (["html" , "xml" ]),
547+ loader = FileSystemLoader (self .tpl_folder ),
548+ )
549+ else :
550+ env = Environment (
551+ autoescape = select_autoescape (["html" , "xml" ]),
552+ loader = FileSystemLoader (self .tpl_folder ),
553+ lstrip_blocks = True ,
554+ trim_blocks = True ,
555+ )
556+
557+ template = env .get_template ("atom.xml.jinja2" )
558+
559+ # -- Feed sorted by creation date
560+ logger .debug (
561+ "Fill creation dates and dump created feed into Atom template."
562+ )
563+
564+ # Format dates for Atom (ISO 8601)
565+ for page in self .feed_created .entries :
566+ page .atom_published = page .created .isoformat ()
567+ page .atom_updated = page .updated .isoformat ()
568+
569+ # Format feed buildDate for Atom (ISO 8601)
570+ build_date_atom : str = datetime .fromtimestamp (
571+ get_build_timestamp ()
572+ ).isoformat ()
573+
574+ # Temporarily store the ISO format
575+ original_build_date : str = self .feed_created .buildDate
576+ self .feed_created .buildDate = build_date_atom
577+
578+ # write file
579+ with out_atom_created .open (mode = "w" , encoding = "UTF8" ) as fiatom_created :
580+ if pretty_print :
581+ fiatom_created .write (template .render (feed = self .feed_created ))
582+ else :
583+ prev_char = ""
584+ for char in template .render (feed = asdict (self .feed_created )):
585+ if char == "\n " :
586+ # convert new lines to spaces to preserve sentence structure
587+ char = " "
588+ if char == " " and prev_char == " " :
589+ prev_char = char
590+ continue
591+ prev_char = char
592+ fiatom_created .write (char )
593+
594+ # Restore original buildDate
595+ self .feed_created .buildDate = original_build_date
596+
597+ # -- Feed sorted by update date
598+ logger .debug ("Fill update dates and dump updated feed into Atom template." )
599+
600+ for page in self .feed_updated .entries :
601+ page .atom_published = page .created .isoformat ()
602+ page .atom_updated = page .updated .isoformat ()
603+
604+ original_build_date = self .feed_updated .buildDate
605+ self .feed_updated .buildDate = build_date_atom
606+
607+ # write file
608+ with out_atom_updated .open (mode = "w" , encoding = "UTF8" ) as fiatom_updated :
609+ if pretty_print :
610+ fiatom_updated .write (template .render (feed = self .feed_updated ))
611+ else :
612+ prev_char = ""
613+ for char in template .render (feed = asdict (self .feed_updated )):
614+ if char == "\n " :
615+ # convert new lines to spaces to preserve sentence structure
616+ char = " "
617+ if char == " " and prev_char == " " :
618+ prev_char = char
619+ continue
620+ prev_char = char
621+ fiatom_updated .write (char )
622+
623+ # Restore original buildDate
624+ self .feed_updated .buildDate = original_build_date
0 commit comments