diff --git a/elisp/ox-rss.el b/elisp/ox-rss.el deleted file mode 100644 index 4cdfe0e..0000000 --- a/elisp/ox-rss.el +++ /dev/null @@ -1,414 +0,0 @@ -;;; ox-rss.el --- RSS 2.0 Back-End for Org Export Engine - -;; Copyright (C) 2013-2015 Bastien Guerry - -;; Author: Bastien Guerry -;; Keywords: org, wp, blog, feed, rss - -;; This file is not yet part of GNU Emacs. - -;; This program is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see . - -;;; Commentary: - -;; This library implements a RSS 2.0 back-end for Org exporter, based on -;; the `html' back-end. -;; -;; It requires Emacs 24.1 at least. -;; -;; It provides two commands for export, depending on the desired output: -;; `org-rss-export-as-rss' (temporary buffer) and `org-rss-export-to-rss' -;; (as a ".xml" file). -;; -;; This backend understands two new option keywords: -;; -;; #+RSS_EXTENSION: xml -;; #+RSS_IMAGE_URL: http://myblog.org/mypicture.jpg -;; -;; It uses #+HTML_LINK_HOME: to set the base url of the feed. -;; -;; Exporting an Org file to RSS modifies each top-level entry by adding a -;; PUBDATE property. If `org-rss-use-entry-url-as-guid', it will also add -;; an ID property, later used as the guid for the feed's item. -;; -;; The top-level headline is used as the title of each RSS item unless -;; an RSS_TITLE property is set on the headline. -;; -;; You typically want to use it within a publishing project like this: -;; -;; (add-to-list -;; 'org-publish-project-alist -;; '("homepage_rss" -;; :base-directory "~/myhomepage/" -;; :base-extension "org" -;; :rss-image-url "http://lumiere.ens.fr/~guerry/images/faces/15.png" -;; :html-link-home "http://lumiere.ens.fr/~guerry/" -;; :html-link-use-abs-url t -;; :rss-extension "xml" -;; :publishing-directory "/home/guerry/public_html/" -;; :publishing-function (org-rss-publish-to-rss) -;; :section-numbers nil -;; :exclude ".*" ;; To exclude all files... -;; :include ("index.org") ;; ... except index.org. -;; :table-of-contents nil)) -;; -;; ... then rsync /home/guerry/public_html/ with your server. -;; -;; By default, the permalink for a blog entry points to the headline. -;; You can specify a different one by using the :RSS_PERMALINK: -;; property within an entry. - -;;; Code: - -(require 'ox-html) -(declare-function url-encode-url "url-util" (url)) - -;;; Variables and options - -(defgroup org-export-rss nil - "Options specific to RSS export back-end." - :tag "Org RSS" - :group 'org-export - :version "24.4" - :package-version '(Org . "8.0")) - -(defcustom org-rss-image-url "http://orgmode.org/img/org-mode-unicorn-logo.png" - "The URL of the an image for the RSS feed." - :group 'org-export-rss - :type 'string) - -(defcustom org-rss-extension "xml" - "File extension for the RSS 2.0 feed." - :group 'org-export-rss - :type 'string) - -(defcustom org-rss-categories 'from-tags - "Where to extract items category information from. -The default is to extract categories from the tags of the -headlines. When set to another value, extract the category -from the :CATEGORY: property of the entry." - :group 'org-export-rss - :type '(choice - (const :tag "From tags" from-tags) - (const :tag "From the category property" from-category))) - -(defcustom org-rss-use-entry-url-as-guid t - "Use the URL for the metatag? -When nil, Org will create ids using `org-icalendar-create-uid'." - :group 'org-export-rss - :type 'boolean) - -;;; Define backend - -(org-export-define-derived-backend 'rss 'html - :menu-entry - '(?r "Export to RSS" - ((?R "As RSS buffer" - (lambda (a s v b) (org-rss-export-as-rss a s v))) - (?r "As RSS file" (lambda (a s v b) (org-rss-export-to-rss a s v))) - (?o "As RSS file and open" - (lambda (a s v b) - (if a (org-rss-export-to-rss t s v) - (org-open-file (org-rss-export-to-rss nil s v))))))) - :options-alist - '((:description "DESCRIPTION" nil nil newline) - (:keywords "KEYWORDS" nil nil space) - (:with-toc nil nil nil) ;; Never include HTML's toc - (:rss-extension "RSS_EXTENSION" nil org-rss-extension) - (:rss-image-url "RSS_IMAGE_URL" nil org-rss-image-url) - (:rss-categories nil nil org-rss-categories)) - :filters-alist '((:filter-final-output . org-rss-final-function)) - :translate-alist '((headline . org-rss-headline) - (comment . (lambda (&rest args) "")) - (comment-block . (lambda (&rest args) "")) - (timestamp . (lambda (&rest args) "")) - (plain-text . org-rss-plain-text) - (section . org-rss-section) - (template . org-rss-template))) - -;;; Export functions - -;;;###autoload -(defun org-rss-export-as-rss (&optional async subtreep visible-only) - "Export current buffer to a RSS buffer. - -If narrowing is active in the current buffer, only export its -narrowed part. - -If a region is active, export that region. - -A non-nil optional argument ASYNC means the process should happen -asynchronously. The resulting buffer should be accessible -through the `org-export-stack' interface. - -When optional argument SUBTREEP is non-nil, export the sub-tree -at point, extracting information from the headline properties -first. - -When optional argument VISIBLE-ONLY is non-nil, don't export -contents of hidden elements. - -Export is done in a buffer named \"*Org RSS Export*\", which will -be displayed when `org-export-show-temporary-export-buffer' is -non-nil." - (interactive) - (let ((file (buffer-file-name (buffer-base-buffer)))) - (org-icalendar-create-uid file 'warn-user) - (org-rss-add-pubdate-property)) - (org-export-to-buffer 'rss "*Org RSS Export*" - async subtreep visible-only nil nil (lambda () (text-mode)))) - -;;;###autoload -(defun org-rss-export-to-rss (&optional async subtreep visible-only) - "Export current buffer to a RSS file. - -If narrowing is active in the current buffer, only export its -narrowed part. - -If a region is active, export that region. - -A non-nil optional argument ASYNC means the process should happen -asynchronously. The resulting file should be accessible through -the `org-export-stack' interface. - -When optional argument SUBTREEP is non-nil, export the sub-tree -at point, extracting information from the headline properties -first. - -When optional argument VISIBLE-ONLY is non-nil, don't export -contents of hidden elements. - -Return output file's name." - (interactive) - (let ((file (buffer-file-name (buffer-base-buffer)))) - (org-icalendar-create-uid file 'warn-user) - (org-rss-add-pubdate-property)) - (let ((outfile (org-export-output-file-name - (concat "." org-rss-extension) subtreep))) - (org-export-to-file 'rss outfile async subtreep visible-only))) - -;;;###autoload -(defun org-rss-publish-to-rss (plist filename pub-dir) - "Publish an org file to RSS. - -FILENAME is the filename of the Org file to be published. PLIST -is the property list for the given project. PUB-DIR is the -publishing directory. - -Return output file name." - (let ((bf (get-file-buffer filename))) - (if bf - (with-current-buffer bf - (org-icalendar-create-uid filename 'warn-user) - (org-rss-add-pubdate-property) - (write-file filename)) - (find-file filename) - (org-icalendar-create-uid filename 'warn-user) - (org-rss-add-pubdate-property) - (write-file filename) (kill-buffer))) - (org-publish-org-to - 'rss filename (concat "." org-rss-extension) plist pub-dir)) - -;;; Main transcoding functions - -(defun org-rss-headline (headline contents info) - "Transcode HEADLINE element into RSS format. -CONTENTS is the headline contents. INFO is a plist used as a -communication channel." - (unless (or (org-element-property :footnote-section-p headline) - ;; Only consider first-level headlines - (> (org-export-get-relative-level headline info) 1)) - (let* ((author (and (plist-get info :with-author) - (let ((auth (plist-get info :author))) - (and auth (org-export-data auth info))))) - (htmlext (plist-get info :html-extension)) - (hl-number (org-export-get-headline-number headline info)) - (hl-home (file-name-as-directory (plist-get info :html-link-home))) - (hl-pdir (plist-get info :publishing-directory)) - (hl-perm (org-element-property :RSS_PERMALINK headline)) - (anchor (org-export-get-reference headline info)) - (category (org-rss-plain-text - (or (org-element-property :CATEGORY headline) "") info)) - (pubdate0 (org-element-property :PUBDATE headline)) - (pubdate (let ((system-time-locale "C")) - (if pubdate0 - (format-time-string - "%a, %d %b %Y %H:%M:%S %z" - (org-time-string-to-time pubdate0))))) - (title (or (org-element-property :RSS_TITLE headline) - (replace-regexp-in-string - org-bracket-link-regexp - (lambda (m) (or (match-string 3 m) - (match-string 1 m))) - (org-element-property :raw-value headline)))) - (publink - (or (and hl-perm (concat (or hl-home hl-pdir) hl-perm)) - (concat - (or hl-home hl-pdir) - (file-name-nondirectory - (file-name-sans-extension - (plist-get info :input-file))) "." htmlext "#" anchor))) - (guid (if org-rss-use-entry-url-as-guid - publink - (org-rss-plain-text - (or (org-element-property :ID headline) - (org-element-property :CUSTOM_ID headline) - publink) - info)))) - (if (not pubdate0) "" ;; Skip entries with no PUBDATE prop - (format - (concat - "\n" - "%s\n" - "%s\n" - "%s\n" - "%s\n" - "%s\n" - (org-rss-build-categories headline info) "\n" - "\n" - "\n") - title publink author guid pubdate contents))))) - -(defun org-rss-build-categories (headline info) - "Build categories for the RSS item." - (if (eq (plist-get info :rss-categories) 'from-tags) - (mapconcat - (lambda (c) (format "" c)) - (org-element-property :tags headline) - "\n") - (let ((c (org-element-property :CATEGORY headline))) - (format "" c)))) - -(defun org-rss-template (contents info) - "Return complete document string after RSS conversion. -CONTENTS is the transcoded contents string. INFO is a plist used -as a communication channel." - (concat - (format "" - (symbol-name org-html-coding-system)) - "\n" - "" - (org-rss-build-channel-info info) "\n" - contents - "\n" - "")) - -(defun org-rss-build-channel-info (info) - "Build the RSS channel information." - (let* ((system-time-locale "C") - (title (plist-get info :title)) - (email (org-export-data (plist-get info :email) info)) - (author (and (plist-get info :with-author) - (let ((auth (plist-get info :author))) - (and auth (org-export-data auth info))))) - (date (format-time-string "%a, %d %b %Y %H:%M:%S %z")) ;; RFC 882 - (description (org-export-data (plist-get info :description) info)) - (lang (plist-get info :language)) - (keywords (plist-get info :keywords)) - (rssext (plist-get info :rss-extension)) - (blogurl (or (plist-get info :html-link-home) - (plist-get info :publishing-directory))) - (image (url-encode-url (plist-get info :rss-image-url))) - (ifile (plist-get info :input-file)) - (publink - (concat (file-name-as-directory blogurl) - (file-name-nondirectory - (file-name-sans-extension ifile)) - "." rssext))) - (format - "\n%s - -%s - -%s -%s -%s -%s -%s (%s) - -%s -%s -%s - -" - title publink blogurl description lang date date - (concat (format "Emacs %d.%d" - emacs-major-version - emacs-minor-version) - " Org-mode " (org-version)) - email author image title blogurl))) - -(defun org-rss-section (section contents info) - "Transcode SECTION element into RSS format. -CONTENTS is the section contents. INFO is a plist used as -a communication channel." - contents) - -(defun org-rss-timestamp (timestamp contents info) - "Transcode a TIMESTAMP object from Org to RSS. -CONTENTS is nil. INFO is a plist holding contextual -information." - (org-html-encode-plain-text - (org-timestamp-translate timestamp))) - -(defun org-rss-plain-text (contents info) - "Convert plain text into RSS encoded text." - (let (output) - (setq output (org-html-encode-plain-text contents) - output (org-export-activate-smart-quotes - output :html info)))) - -;;; Filters - -(defun org-rss-final-function (contents backend info) - "Prettify the RSS output." - (with-temp-buffer - (xml-mode) - (insert contents) - (indent-region (point-min) (point-max)) - (buffer-substring-no-properties (point-min) (point-max)))) - -;;; Miscellaneous - -(defun org-rss-add-pubdate-property () - "Set the PUBDATE property for top-level headlines." - (let (msg) - (org-map-entries - (lambda () - (let* ((entry (org-element-at-point)) - (level (org-element-property :level entry))) - (when (= level 1) - (unless (org-entry-get (point) "PUBDATE") - (setq msg t) - (org-set-property - "PUBDATE" (format-time-string - (cdr org-time-stamp-formats))))))) - nil nil 'comment 'archive) - (when msg - (message "Property PUBDATE added to top-level entries in %s" - (buffer-file-name)) - (sit-for 2)))) - -(provide 'ox-rss) - -;;; ox-rss.el ends here diff --git a/ha-org-publishing.org b/ha-org-publishing.org index e7b7ab3..6a13fc4 100644 --- a/ha-org-publishing.org +++ b/ha-org-publishing.org @@ -27,13 +27,26 @@ A literate programming file for publishing my website using org. * Introduction While the Emacs community have a plethora of options for generating a static website from org-formatted files, I keep my pretty simple, and use the standard =org-publish= feature. +The RSS needs UUIDs: + +#+BEGIN_SRC emacs-lisp results silent + (use-package uuidgen + :straight (:host github :repo "emacsmirror/uuidgen")) + + (defun org-icalendar-create-uid (&rest ignored) + "Returns a UUID." + (uuidgen-1)) +#+END_SRC + While the following packages come with Emacs, they aren't necessarily loaded: #+begin_src emacs-lisp :results silent + (use-package ox-rss + :straight (:host github :repo "emacsmirror/ox-rss")) + (use-package org :config (require 'ox-html) - (require 'ox-rss) (require 'ox-publish)) #+end_src