| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 | 
							- ;;; ox-rss.el --- RSS 2.0 Back-End for Org Export Engine
 
- ;; Copyright (C) 2013-2015  Bastien Guerry
 
- ;; Author: Bastien Guerry <bzg@gnu.org>
 
- ;; 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 <http://www.gnu.org/licenses/>.
 
- ;;; 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 <guid> 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
 
-   '((: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-solidify-link-text
 
- 	     (or (org-element-property :CUSTOM_ID headline)
 
- 		 (concat "sec-" (mapconcat 'number-to-string hl-number "-")))))
 
- 	   (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
 
- 	  "<item>\n"
 
- 	  "<title>%s</title>\n"
 
- 	  "<link>%s</link>\n"
 
- 	  "<author>%s</author>\n"
 
- 	  "<guid isPermaLink=\"false\">%s</guid>\n"
 
- 	  "<pubDate>%s</pubDate>\n"
 
- 	  (org-rss-build-categories headline info) "\n"
 
- 	  "<description><![CDATA[%s]]></description>\n"
 
- 	  "</item>\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 "<category><![CDATA[%s]]></category>" c))
 
-        (org-element-property :tags headline)
 
-        "\n")
 
-     (let ((c (org-element-property :CATEGORY headline)))
 
-       (format "<category><![CDATA[%s]]></category>" 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 "<?xml version=\"1.0\" encoding=\"%s\"?>"
 
- 	   (symbol-name org-html-coding-system))
 
-    "\n<rss version=\"2.0\"
 
- 	xmlns:content=\"http://purl.org/rss/1.0/modules/content/\"
 
- 	xmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"
 
- 	xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
 
- 	xmlns:atom=\"http://www.w3.org/2005/Atom\"
 
- 	xmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"
 
- 	xmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"
 
- 	xmlns:georss=\"http://www.georss.org/georss\"
 
-         xmlns:geo=\"http://www.w3.org/2003/01/geo/wgs84_pos#\"
 
-         xmlns:media=\"http://search.yahoo.com/mrss/\">"
 
-    "<channel>"
 
-    (org-rss-build-channel-info info) "\n"
 
-    contents
 
-    "</channel>\n"
 
-    "</rss>"))
 
- (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<title>%s</title>
 
- <atom:link href=\"%s\" rel=\"self\" type=\"application/rss+xml\" />
 
- <link>%s</link>
 
- <description><![CDATA[%s]]></description>
 
- <language>%s</language>
 
- <pubDate>%s</pubDate>
 
- <lastBuildDate>%s</lastBuildDate>
 
- <generator>%s</generator>
 
- <webMaster>%s (%s)</webMaster>
 
- <image>
 
- <url>%s</url>
 
- <title>%s</title>
 
- <link>%s</link>
 
- </image>
 
- "
 
-      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
 
 
  |