1
1
Fork
You've already forked emacs-ox-atom
0
A general-purpose Atom feed exporter for Org-mode.
  • Emacs Lisp 99.3%
  • Makefile 0.7%
2026年04月02日 18:24:57 +04:00
.forgejo/workflows Initial commit. 2026年04月02日 18:24:57 +04:00
.gitignore Initial commit. 2026年04月02日 18:24:57 +04:00
LICENSE.org Initial commit. 2026年04月02日 18:24:57 +04:00
Makefile Initial commit. 2026年04月02日 18:24:57 +04:00
ox-atom-tests.el Initial commit. 2026年04月02日 18:24:57 +04:00
ox-atom.el Initial commit. 2026年04月02日 18:24:57 +04:00
README.org Initial commit. 2026年04月02日 18:24:57 +04:00

emacs-ox-atom

A general-purpose Atom 1.0 feed exporter for Org-mode.

Installation

Clone or copy org-atom.el somewhere on your load-path, then:

 (require 'org-atom)

Quick start

Approach A — Single file (one headline per entry)

Each top-level headline becomes one <entry>. File-level keywords supply the feed metadata.

 #+TITLE: My Blog
 #+ATOM_ID: https://example.org/feed.xml
 #+ATOM_LINK: https://example.org/
 #+ATOM_SELF: https://example.org/feed.xml
 #+ATOM_AUTHOR: Alice <alice@example.org>
 #+ATOM_LOGO: https://example.org/logo.png
 * My first post
 :PROPERTIES:
 :ATOM_ID: https://example.org/posts/first.html
 :ATOM_LINK: https://example.org/posts/first.html
 :ATOM_UPDATED: [2026年02月12日]
 :ATOM_PUBLISHED: [2025年11月23日]
 :ATOM_SUMMARY: A short teaser shown in feed readers.
 :END:
 Body text here becomes =<content type​=​"html">=.

Export via the menu (C-c C-e a) or programmatically:

 ;; To a file (e.g. feed.xml):
 (org-atom-export-to-atom)
 ;; To a buffer for inspection:
 (org-atom-export-as-atom)

Or as an ox-publish publishing function:

 (add-to-list
 'org-publish-project-alist
 '("my-feed"
 :base-directory "~/blog/"
 :publishing-directory "~/blog/public/"
 :include ("feed.org")
 :publishing-function org-atom-publish-to-atom))

Approach B — Multi-file (one file per post)

Each .org file in a directory is an independent post. After exporting the files to HTML, call org-atom-write-feed to assemble the feed:

 (org-atom-write-feed
 :file "public/feed.xml"
 :title "My Blog"
 :id "https://example.org/"
 :link "https://example.org/"
 :self "https://example.org/feed.xml"
 :authors '(:name "Alice" :email "alice@example.org")
 :entries (org-atom-entries-from-files
 (directory-files "org/posts" t "\\.org$")
 :base-url "https://example.org/posts/"
 :html-dir "public/posts/"
 :exclude-drafts t))

This is the recommended approach when using ox-publish for a blog with one post per file; call org-atom-write-feed from a :completion-function in your posts project.

Reference

File-level keywords (single-file mode)

These #+KEYWORD: lines go at the top of your feed .org file.

Keyword Feed element Notes
#+TITLE <title type​=​"text"> Standard Org keyword
#+ATOM_ID <id> Permanent IRI; falls back to #+ATOM_LINK
#+ATOM_SUBTITLE <subtitle type​=​"text">
#+ATOM_AUTHOR <author> Parsed as "Name <email>"
#+ATOM_LINK <link rel​=​"alternate"> URL of the site's homepage
#+ATOM_SELF <link rel​=​"self"> URL of the feed itself
#+ATOM_RIGHTS <rights type​=​"text">
#+ATOM_ICON <icon>
#+ATOM_LOGO <logo>

Entry properties (single-file mode)

These go in the :PROPERTIES: drawer of each top-level headline.

Property Entry element Notes
:ATOM_ID: <id> Falls back to :CUSTOM_ID:, then a generated URN
:ATOM_LINK: <link rel​=​"alternate">
:ATOM_UPDATED: <updated> Falls back to #+DATE
:ATOM_PUBLISHED: <published> Falls back to :ATOM_UPDATED:
:ATOM_SUMMARY: <summary type​=​"text">
:ATOM_AUTHOR: <author> Parsed as "Name <email>"; overrides feed author
:ATOM_RIGHTS: <rights type​=​"text">

Org tags on the headline (* My post :tag1:tag2:) become <category term​=​"..."/> elements automatically.

Sub-headlines are delegated to the HTML back-end and included in <content type​=​"html">.

Post keywords (multi-file mode)

These keywords are read from each individual post .org file by org-atom-entries-from-files.

Keyword Entry element Notes
#+TITLE <title>
#+DATE <updated>, <published>
#+DESCRIPTION <summary type"text">=
#+AUTHOR <author> Parsed as "Name <email>"
#+DRAFT: t (entry excluded) Honored when :exclude-drafts t
#+ATOM_ID <id> Overrides the default URL-based id
#+ATOM_UPDATED <updated> Overrides #+DATE
#+ATOM_RIGHTS <rights>

Top-level Org tags in the file become <category term​=​"..."/> elements.

org-atom-write-feed keyword arguments

Argument Type Description
:file string (required) Output path; parent directories are created
:title text construct Feed <title>
:subtitle text construct Feed <subtitle>
:id IRI string Feed <id> (permanent, globally unique)
:link string or link plist Alternate link to the site homepage
:self string or link plist Self link to the feed URL
:authors person plist or list Feed-level <author> element(s)
:contributors person plist or list Feed-level <contributor> element(s)
:categories category plist or list Feed-level <category> element(s)
:links link plist or list Additional <link> elements
:generator generator plist Overrides org-atom-generator; set to nil to omit
:icon IRI string Feed <icon>
:logo IRI string Feed <logo>
:rights text construct Feed <rights>
:updated time value Feed <updated>; derived from entries if omitted
:entries list of entry plists The feed entries; see below

org-atom-entries-from-files keyword arguments

Argument Type Description
:base-url string Prepended to each HTML filename to form <link> and <id>
:html-dir string Directory of exported HTML files; enables <content> population
:exclude-drafts boolean Skip files with #+DRAFT: t
:author-fallback person plist/list Used when a file has no #+AUTHOR
:id-fn function Called with the entry plist; returns the <id> IRI

Text constructs

Wherever a text construct is accepted (:title, :subtitle, :summary, :rights, and feed-level equivalents), you may pass:

Value Rendered as
"plain string" <... type​=​"text">
'(:text "...") <... type​=​"text">
'(:html "...") <... type​=​"html">
'(:xhtml "...") <... type​=​"xhtml"> (wrapped in <div xmlns=...>)

Person plists

Wherever a person construct is accepted (:authors, :contributors), pass a plist with:

Key Required Description
:name yes Human-readable name
:email no Email address
:uri no Homepage URI

A list of such plists produces multiple <author> / <contributor> elements. Author strings of the form "Name <email>" are parsed automatically when read from Org keywords and properties.

Link plists

Wherever a link is accepted (:link, :self, :links), pass a plist with:

Key Description
:href The IRI (required)
:rel Relation type (alternate, self, ...)
:type Media type
:hreflang Language of the referenced resource
:title Human-readable label
:length Content length in bytes

The :link and :self shorthand arguments also accept a plain string, which is wrapped in the appropriate rel and type attributes automatically.

Content values

The :content key of an entry plist accepts:

Value RFC 4287 rendering
"plain string" <content type​=​"text">
'(:text "...") <content type​=​"text">
'(:html "...") <content type​=​"html">
'(:xhtml "...") <content type​=​"xhtml">
'(:media "mime/type" "src") Out-of-line: <content type​=​"..." src​=​"..."/>
'(:inline "mime/type" "b64") Inline base64: <content type​=​"...">...</content>

Customisation

M-x customize-group RET org-atom RET

Variable Default Description
org-atom-extension "xml" File extension for exported feeds
org-atom-generator Org-mode name, URI, and version <generator> element; set to nil to omit