Skip to content

Commit 5c9ad49

Browse files
committed
first commit
0 parents  commit 5c9ad49

File tree

3 files changed

+924
-0
lines changed

3 files changed

+924
-0
lines changed

README.md

Whitespace-only changes.

org-blog.el

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
;;; org-blog.el --- create and publish a blog with org-mode
2+
3+
;; Copyright (C) 2006 David O'Toole
4+
5+
;; Author: David O'Toole <[email protected]>
6+
;; Keywords: hypermedia, tools
7+
;; $Id: org-blog.el,v 1.18 2007/06/13 16:21:24 dto Exp dto $
8+
9+
;; This file is free software; you can redistribute it and/or modify
10+
;; it under the terms of the GNU General Public License as published by
11+
;; the Free Software Foundation; either version 2, or (at your option)
12+
;; any later version.
13+
14+
;; This file is distributed in the hope that it will be useful,
15+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
;; GNU General Public License for more details.
18+
19+
;; You should have received a copy of the GNU General Public License
20+
;; along with GNU Emacs; see the file COPYING. If not, write to
21+
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22+
;; Boston, MA 02110-1301, USA.
23+
24+
;;; Commentary:
25+
26+
;; This program adds simple blog publishing support to org-mode. It is
27+
;; built on top of org-publish.el.
28+
29+
;; You should read the documentation for org-publish.el before
30+
;; continuing.
31+
32+
;; The latest version of this program, and of org-publish.el, can be
33+
;; found at: http://dto.freeshell.org/notebook/OrgMode.html
34+
35+
;;;; 1. Basic configuration
36+
;;
37+
;; First add (require 'org-blog) to your emacs initialization file.
38+
;;
39+
;; Then set the variable org-blog-directory (you can also leave it
40+
;; as the default, "~/blog/"). This directory should be different
41+
;; from the directory where your normal *.org files are stored,
42+
;; otherwise they will get "posted".
43+
;;
44+
;; You should also set the variable
45+
;; org-blog-unfinished-directory. The default is
46+
;; "~/blog/unfinished". This is the directory where unfinished
47+
;; posts are stored. You can leave posts in the unfinished
48+
;; directory while you are working on them, and they won't be
49+
;; published.
50+
51+
;;;; 2. Create a post
52+
;;
53+
;; Use M-x org-blog-new-post. You'll be prompted for a
54+
;; filename. Enter a short name for this post (without the ".org")
55+
;; and press RET. You'll see a new buffer with a blank TITLE field.
56+
57+
;; You can work on more than one post at once. They'll all be
58+
;; stored in your `org-blog-unfinished-directory'. To view a list
59+
;; of posts in progress, use M-x
60+
;; org-blog-find-unfinished-posts. You'll see the directory listing
61+
;; of `org-blog-unfinished-directory', and you can use RET to
62+
;; select a post to edit.
63+
64+
;;;; 3. Finish a post
65+
;;
66+
;; When your post is ready, visit the file and hit M-x
67+
;; org-blog-finish-post. This does not mean the post is published
68+
;; on your website, only that the post is "finished" and given a
69+
;; timestamped filename. Your blog post and updated index will be
70+
;; published when you execute M-x org-publish-all.
71+
;;
72+
;; But first, let's take a detour to make sure blog publishing is
73+
;; set up properly.
74+
75+
;;;; 4. Configure blog publishing
76+
;;
77+
;; Org-blog contains an index function to publish a front page for
78+
;; your blog. This index can be configured to display the most
79+
;; recent posts, and your "blogroll" or list of links to other
80+
;; blogs. The newest post will always be at the top.
81+
;;
82+
;; You should add a project called "blog" to your
83+
;; org-publish-project-alist. Here is an example project
84+
;; configuration you can adapt to your needs:
85+
86+
;; '("blog" :base-directory "~/blog/"
87+
;; :base-extension "org"
88+
;; :publishing-directory "/protocol:user@host:~/html/blog/"
89+
;; :publishing-function org-publish-org-to-html
90+
;; :auto-index t
91+
;; :blog-base-url "http://dto.freeshell.org/blog/"
92+
;; :blog-title "dto.freeshell.org blog"
93+
;; :blog-description "David O'Toole's web log."
94+
;; :blog-export-rss t
95+
;; :index-function org-publish-blog-index
96+
;; :index-filename "index.org"
97+
;; :index-title "Title of my Blog"
98+
;; :index-posts 2
99+
;; :preamble my-blogroll-html
100+
;; :postamble my-footer-html)
101+
102+
;; Most of these keywords are documented along with
103+
;; org-publish-project-alist. Before moving on, we'll explain
104+
;; usages specific to blogging support.
105+
106+
;; The keyword :index-posts controls how many posts will be shown
107+
;; on the blog's front page. Set its value to an integer. Remaining
108+
;; posts will be shown as a list of links at the bottom of the
109+
;; page.
110+
111+
;; The :index-title should be used to set the title of your blog.
112+
;; You can use the standard :preamble and :postamble keywords to
113+
;; set the header and footer of your blog posts and front page.
114+
;; This is a great place to include your HTML blogroll and
115+
;; copyright notices.
116+
117+
;;;; 5. Now publish!
118+
;;
119+
;; After you've updated your org-publish-project-alist and created
120+
;; a post or two, hit M-x org-publish-all. Your posts should be
121+
;; uploaded, and an index frontpage generated.
122+
123+
124+
;;; Code:
125+
126+
(require 'org-publish)
127+
128+
(defgroup org-blog nil
129+
"Options for keeping and publishing a blog with org-mode."
130+
:tag "Org Blog"
131+
:group 'org-publish)
132+
133+
(defcustom org-blog-directory "~/blog/"
134+
"Directory where finished blog posts are stored."
135+
:group 'org-blog)
136+
137+
(defcustom org-blog-unfinished-directory "~/blog/unfinished"
138+
"Directory where unfinished posts are stored."
139+
:group 'org-blog)
140+
141+
(defcustom org-blog-time-format "%Y-%m-%d %I:%M%p -- "
142+
"Format string used when timestamping posts."
143+
:group 'org-blog)
144+
145+
(defun org-blog-new-post-file ()
146+
(concat (file-name-as-directory org-blog-directory) (format-time-string "blog-%Y-%m-%d-%H%M.org")))
147+
148+
(defun org-blog-new-post (filename)
149+
"Create a new post in FILENAME.
150+
Post is stored in `org-blog-unfinished-directory'."
151+
(interactive "sFilename for new post: ")
152+
(find-file (concat (file-name-as-directory org-blog-unfinished-directory)
153+
filename ".org"))
154+
(insert "#+TITLE: \n")
155+
(insert "#+DESCRIPTION: "))
156+
157+
(defun org-blog-find-unfinished-posts ()
158+
"Open `org-blog-unfinished-directory'."
159+
(interactive)
160+
(let ((dir (file-name-as-directory org-blog-unfinished-directory)))
161+
(when (not (file-exists-p dir))
162+
(make-directory dir t))
163+
(find-file dir)))
164+
165+
(defun org-blog-finish-post ()
166+
"Complete and timestamp the unfinished post in the current buffer.
167+
Follow up with org-publish-all to upload to the site."
168+
(interactive)
169+
(write-file (org-blog-new-post-file)))
170+
171+
;; pluggable index generation function for org-publish.
172+
173+
(defun org-publish-blog-index (plist &optional index-filename)
174+
"Publish an index of all finished blog posts.
175+
This function is suitable for use in the :index-function keyword
176+
of org-publish-project-alist."
177+
(let* ((posts (nreverse (sort (org-publish-get-base-files plist "*~") 'string<)))
178+
(base-directory (file-name-as-directory (or org-blog-directory (plist-get plist :base-directory))))
179+
(blog-base-url (file-name-as-directory (plist-get plist :blog-base-url)))
180+
(blog-title (plist-get plist :blog-title))
181+
(publishing-directory (file-name-as-directory
182+
(plist-get plist :publishing-directory)))
183+
(blog-description (plist-get plist :blog-description))
184+
(blog-rss-feed nil)
185+
(rss (plist-get plist :blog-export-rss))
186+
(post-content nil)
187+
(index-file (concat base-directory (or index-filename "index.org")))
188+
(index-buffer (find-buffer-visiting index-file))
189+
(num-posts (or (plist-get plist :index-posts) 5))
190+
(index-title (plist-get plist :index-title))
191+
(count 0)
192+
(p nil))
193+
194+
(message "RSS = %S" rss)
195+
;;
196+
;; if buffer is already open, kill it
197+
(if index-buffer
198+
(kill-buffer index-buffer))
199+
;;
200+
;; start the RSS feed
201+
(when rss
202+
(push (org-blog-rss-preamble blog-title blog-base-url blog-description)
203+
blog-rss-feed))
204+
;;
205+
(with-temp-buffer
206+
;;
207+
;; process each post
208+
(while (setq p (pop posts))
209+
(let ((basename (file-name-nondirectory p))
210+
(post-title nil)
211+
(post-time (format-time-string
212+
"%a, %d %b %Y %H:%M:00 %z"
213+
(nth 5 (file-attributes p))))
214+
(post-description nil))
215+
;;
216+
;; grab post details
217+
(with-temp-buffer
218+
(insert-file-contents p)
219+
;;
220+
;; make sure we are in org-mode (otherwise export won't work properly)
221+
(let ((org-inhibit-startup t)) (org-mode))
222+
(goto-char (point-min))
223+
(re-search-forward "#\\+TITLE: \\(.*\\)$" nil t)
224+
(setf post-title (match-string 1))
225+
(re-search-forward "#\\+DESCRIPTION: \\(.*\\)$" nil t)
226+
(setf post-description (match-string 1))
227+
(setf post-content (buffer-substring-no-properties
228+
(match-end 1) (point-max))))
229+
;;
230+
;; avoid inserting existing index; this would be a loop!
231+
(when (not (string= basename index-filename))
232+
;;
233+
;; add rss item
234+
(when rss
235+
(push (org-blog-rss-item post-title
236+
(concat blog-base-url
237+
(file-name-sans-extension
238+
(file-name-nondirectory p))
239+
".html")
240+
post-content
241+
post-time)
242+
blog-rss-feed))
243+
(if (< count num-posts)
244+
;;
245+
;; insert full text of post
246+
(progn (insert-file-contents p)
247+
;; permalink
248+
(goto-char (point-max))
249+
(insert (with-temp-buffer
250+
(insert (concat "\n\n [[file:" basename "][Permalink]]\n\n"))
251+
(buffer-substring-no-properties (point-min) (point-max)))))
252+
253+
;;
254+
;; or, just insert link with title
255+
(progn
256+
(goto-char (point-max))
257+
(when (= count num-posts)
258+
(insert "\n** Older posts\n"))
259+
(insert (concat " - [[file:"
260+
basename "]["
261+
post-title "]]\n")))))
262+
(setq count (+ 1 count))))
263+
;;
264+
;; finish rss feed and write
265+
(when rss
266+
(push (org-blog-rss-postamble) blog-rss-feed)
267+
(with-temp-buffer
268+
(apply 'insert (nreverse blog-rss-feed))
269+
(message "%S - %S"
270+
(concat publishing-directory "blog.rss")
271+
blog-rss-feed)
272+
273+
(write-file (concat publishing-directory "blog.xml"))))
274+
;;
275+
;; turn pasted titles into headings
276+
(goto-char (point-min))
277+
(while (search-forward "#+TITLE: " nil t)
278+
(replace-match "** " nil t))
279+
;;
280+
;; insert index title, if any
281+
(when index-title
282+
(goto-char (point-min))
283+
(insert (concat "#+TITLE: " index-title "\n\n")))
284+
(write-file index-file)
285+
(kill-buffer (current-buffer)))))
286+
287+
288+
;;;; minimal RSS 2.0 support
289+
290+
291+
(defun org-blog-rss-preamble (title link description)
292+
(format
293+
"<rss version=\"2.0\">
294+
<channel>
295+
<title>%s</title>
296+
<link>%s</link>
297+
<description>%s</description>
298+
<generator>OrgBlog</generator>"
299+
title link description))
300+
301+
302+
(defun org-blog-rss-postamble ()
303+
"</channel></rss>")
304+
305+
306+
(defun org-blog-rss-item (title permalink description pubdate)
307+
(let ((description-html (with-temp-buffer
308+
(insert description)
309+
(org-export-region-as-html (point-min) (point-max)
310+
:body-only 'string))))
311+
(format
312+
" <item>
313+
<title>%s</title>
314+
<description>%s</description>
315+
<pubDate>%s</pubDate>
316+
<guid isPermaLink=\"true\">%s</guid>
317+
</item>\n" title description-html pubdate permalink)))
318+
319+
320+
321+
322+
(provide 'org-blog)
323+
;;; org-blog.el ends here

0 commit comments

Comments
 (0)