1 ;;; planner-rss.el --- RSS export for the Emacs Planner (planner.el)
3 ;; Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
4 ;; Parts copyright (C) 2005 David D. Smith
6 ;; Emacs Lisp Archive Entry
7 ;; Filename: planner-rss.el
8 ;; Keywords: hypermedia
9 ;; Author: Sacha Chua <sacha@free.net.ph>
10 ;; Description: Export planner entries as an RSS feed
11 ;; URL: http://www.plannerlove.com/
12 ;; Compatibility: Emacs20, Emacs21
14 ;; This file is part of Planner. It is not part of GNU Emacs.
16 ;; Planner is free software; you can redistribute it and/or modify it
17 ;; under the terms of the GNU General Public License as published by
18 ;; the Free Software Foundation; either version 2, or (at your option)
21 ;; Planner is distributed in the hope that it will be useful, but
22 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
23 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 ;; General Public License for more details.
26 ;; You should have received a copy of the GNU General Public License
27 ;; along with Planner; see the file COPYING. If not, write to the
28 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29 ;; Boston, MA 02110-1301, USA.
33 ;; If you use `remember-to-planner' to keep daily notes, you can
34 ;; automatically publish remembered notes as an RSS feed by adding the
35 ;; following code to your .emacs:
37 ;; (add-to-list 'remember-planner-append-hook 'planner-rss-add-note t)
39 ;; You can also invoke `planner-rss-add-note' on any note you would
44 ;; David Smith (davidsmith AT acm DOT org) ported this to work with
50 (require 'planner-publish
)
52 (defgroup planner-rss nil
53 "Planner options for RSS feeds."
54 :prefix
"planner-rss-"
57 (defcustom planner-rss-base-url
59 "Base URL for blog entries. Should include trailing /.
60 Example: http://sacha.free.net.ph/notebook/wiki/"
64 ;; On my system, this is set to
66 ;; "/home/sacha/public_html/notebook/wiki/blog.rdf"
67 ;; "<?xml version=\"1.0\"?><rss version=\"2.0\"><channel>
68 ;;<title>sachachua's blog</title>
69 ;;<link>http://sacha.free.net.ph/notebook/wiki/today.php</link>
70 ;;<description>Random notes</description>
71 ;;</channel></rss>\n"))
72 (defcustom planner-rss-category-feeds
74 "List of (CONDITION FILENAME INITIAL-CONTENTS).
76 If CONDITION is a regexp, all entries that match the regexp in
77 either title or body will be included in FILENAME. If CONDITION
78 is a function with one argument, it will be called with the
79 marked-up text, and a non-nil return value means include this
82 If INITIAL-CONTENTS is non-nil, it is used to initialize the file if
83 the file is not found or is corrupted.
87 \"/home/sacha/public_html/notebook/wiki/blog.rdf\"
88 \"<?xml version=\\\"1.0\\\"?><rss version=\\\"2.0\\\"><channel>
89 <title>sachachua's blog</title>
90 <link>http://sacha.free.net.ph/notebook/wiki/today.php</link>
91 <description>Random notes</description>
92 </channel></rss>\n\"))"
93 :type
'(repeat (group (choice regexp function
) file string
))
96 (defcustom planner-rss-feed-limits nil
97 "A list of (REGEX SIZE-LIMIT ITEM-LIMIT).
99 REGEX is a regular expression that matches the filename.
100 SIZE-LIMIT, if non-nil, is the upper limit in characters.
101 ITEM-LIMIT, if non-nil, is the upper limit in items. If the feed
102 exceeds the stated limits, older items are deleted."
103 :type
'(alist :key-type regexp
104 :value-type
(group (choice
106 (const :tag
"No limit" nil
)
110 (const :tag
"No limit" nil
)
114 ;; Determined from planner-rss-category-feeds.
115 ;; You don't need to set this.
116 (defvar planner-rss-file-name nil
"Filename of current RSS feed.")
117 (defvar planner-rss-initial-contents nil
"Initial contents.")
119 (defun planner-rss-add-item (item)
120 "Add an item to the top of the items list in `planner-rss-file-name'.
121 It will have TITLE, LINK, DESCRIPTION, PUBDATE and CATEGORIES.
122 `planner-rss-initialize' is called if necessary."
124 (save-window-excursion
125 (find-file planner-rss-file-name
)
126 (goto-char (point-min))
127 (unless (re-search-forward "<item>\\|</channel>" nil t
)
130 (insert planner-rss-initial-contents
)
131 (muse-publish-markup-region (point-min) (point-max) "*rss" "planner-rss")
132 (goto-char (point-max))
133 (re-search-backward "</channel>")))
134 (goto-char (match-beginning 0))
139 (defun planner-rss-strip-tags (string)
140 "Remove all tags from STRING."
141 (planner-replace-regexp-in-string "<[^>]+>" "" string
))
144 (defun planner-rss-add-note (&optional feed
)
145 "Export the current note using `planner-rss-add-item'.
146 If FEED is non-nil, add the note to the specified feed only.
147 Call with the interactive prefix in order to be prompted for FEED."
148 (interactive (list (when current-prefix-arg
149 (read-file-name "Feed: "))))
150 (save-window-excursion
153 (when (planner-narrow-to-note)
155 (text (buffer-substring-no-properties (point-min) (point-max)))
156 (muse-publishing-current-file (buffer-file-name))
157 (entry (with-temp-buffer
159 (muse-publish-markup-buffer "*rss*" "planner-rss")
161 (dolist (feed planner-rss-category-feeds nil
)
162 (let ((condition (elt feed
0))
163 (planner-rss-file-name (elt feed
1))
164 (planner-rss-initial-contents (elt feed
2)))
165 (when (cond ((functionp condition
)
166 (funcall condition text
))
168 (string-match condition text
))
170 (unless (member planner-rss-file-name seen
)
171 (add-to-list 'seen planner-rss-file-name
)
172 (planner-rss-add-item entry
)))))))))))
174 (defun planner-rss-limit ()
175 "Apply limits specified in `planner-rss-feed-limits'."
176 (let ((filename (expand-file-name (planner-current-file))))
179 (when (string-match (elt item
0) filename
)
180 (planner-rss-limit-size (elt item
1))
181 (planner-rss-limit-items (elt item
2))))
182 planner-rss-feed-limits
)))
184 (defun planner-rss-limit-size (limit)
185 "Delete RSS items that cause this file to go over LIMIT characters."
190 (re-search-backward "<item>" nil t
)
191 (let ((start (match-beginning 0)))
192 (re-search-forward "</channel>" nil t
)
193 (delete-region start
(match-beginning 0))))))
195 (defun planner-rss-limit-items (limit)
196 "Delete RSS items past the LIMIT-th item."
199 (goto-char (point-min))
200 (while (and (> limit -
1) (re-search-forward "<item>" nil t
))
201 (setq limit
(1- limit
)))
203 (let ((start (match-beginning 0)))
204 (re-search-forward "</channel>" nil t
)
205 (delete-region start
(match-beginning 0))))))
207 (defun planner-publish-markup-note-rss ()
208 "Replace note with RSS 2.0 representation of note data. Borrowed
209 heavily from Sacha's personal configs."
212 (save-excursion (beginning-of-line) (point))
213 (or (save-excursion (and (re-search-forward "<item>\\|</channel>" nil t
)
214 (match-beginning 0)))
216 (let ((info (planner-current-note-info t
)))
217 (delete-region (point-min) (point-max))
219 (insert "<title><verbatim>"
220 (muse-publish-escape-specials-in-string (planner-note-title info
))
221 "</verbatim></title>\n")
222 (insert "<link><verbatim>"
223 (concat planner-rss-base-url
(muse-page-name) ".html#"
224 (planner-note-anchor info
))
225 "</verbatim></link>\n")
226 (insert "<guid><verbatim>"
227 (concat planner-rss-base-url
(muse-page-name) ".html#"
228 (planner-note-anchor info
))
229 "</verbatim></guid>\n")
230 (when (planner-note-body info
)
231 (insert "<description><![CDATA["
233 (insert (planner-note-body info
))
234 (muse-publish-markup-buffer "*title*" "planner-rss-info")
236 "]]></description>\n"))
237 (when (planner-note-date info
)
239 (let ((system-time-locale "C")
240 (timestamp (planner-note-timestamp info
))
241 (date (planner-filename-to-calendar-date
242 (planner-note-date info
)))
243 (minutes) (hour) (day) (month) (year))
246 (when (string-match "\\([0-9]+\\):\\([0-9]+\\)" timestamp
)
247 (let ((hour (string-to-number (match-string 1 timestamp
)))
248 (minutes (string-to-number
249 (match-string 2 timestamp
)))
253 (encode-time 0 minutes hour day month year
)))))
255 (insert "</item>\n"))))
257 (defcustom planner-publish-markup-rss-functions
258 '((note . planner-publish-markup-note-rss
))
259 "An alist of style types to custom functions for that kind of text for RSS.
260 For more on the structure of this list, see
261 `muse-publish-markup-functions'."
262 :type
'(alist :key-type symbol
:value-type function
)
263 :group
'planner-publish
)
265 (unless (assoc "planner-rss" muse-publishing-styles
)
266 (muse-derive-style "planner-rss" "planner-xml"
267 :functions
'planner-publish-markup-rss-functions
270 :prefix planner-rss-base-url
)
271 (muse-derive-style "planner-rss-info" "planner-html"
274 :prefix planner-rss-base-url
))
276 (provide 'planner-rss
)
278 ;;; planner-rss.el ends here