1 ;;; emacs-wiki-blosxom.el --- Publish a wiki tree for serving by Blosxom
3 ;; Copyright 2004 Gary V. Vaughan (gary AT gnu DOT org)
4 ;; Copyright 2004 Brad Collins (brad AT chenla DOT org)
6 ;; Emacs Lisp Archive Entry
7 ;; Filename: emacs-wiki-blosxom.el
9 ;; Date: Wed, 24 March 2004
10 ;; Keywords: hypermedia
11 ;; Author: Gary V. Vaughan (gary AT gnu DOT org)
12 ;; Maintainer: Gary V. Vaughan (gary AT gnu DOT org)
13 ;; Description: Publish a local Emacs Wiki tree for serving by Blosxom
14 ;; URL: http://tkd.kicks-ass.net/arch/gary@gnu.org--2004/emacs-wiki--gary--1.0
15 ;; Compatibility: Emacs21
17 ;; This file is not part of GNU Emacs.
19 ;; This is free software; you can redistribute it and/or modify it under
20 ;; the terms of the GNU General Public License as published by the Free
21 ;; Software Foundation; either version 2, or (at your option) any later
24 ;; This is distributed in the hope that it will be useful, but WITHOUT
25 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
26 ;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
29 ;; You should have received a copy of the GNU General Public License
30 ;; along with GNU Emacs; see the file COPYING. If not, write to the
31 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
32 ;; MA 02111-1307, USA.
36 ;; I maintain the hypertext parts of my website with John Wiegley's
37 ;; emacs-wiki.el, now maintained by Michael Olson at
38 ;; http://www.mwolson.org/projects/EmacsWiki.html. You will need to
39 ;; install a copy of that file before this one is of any use to you.
41 ;; Blosxom wiki publishes a tree of categorised Wiki files to a mirrored
42 ;; tree of blosxom stories to be served by blosxom.cgi.
44 ;; Each Blosxom Wiki file must include `#date yyyy-mm-dd', or optionally
45 ;; the longer `#date yyyy-mm-dd hh:mm', plus whatever normal emacs wiki
46 ;; content is desired.
48 ;; If you want to change `blosxom-directory' and some other variables,
49 ;; either use Customize or use `blosxom-option-customized'. For
52 ;; (blosxom-option-customized 'blosxom-directory "~/Blosxom")
53 ;; (blosxom-option-customized 'blosxom-publishing-directory
54 ;; "~/public_html/blog")
56 ;; and if you want to modify other emacs-wiki variables for the blosxom
59 ;; (add-to-list 'blosxom-custom-variables
60 ;; '(some-emacs-wiki-variable . "some-blosxom-wiki-value"))
61 ;; (blosxom-option-customized 'blosxom-custom-variables
62 ;; blosxom-custom-variables)
64 ;; See `emacs-wiki-update-project' and `blosxom-custom-variables' for more
69 ;; Brad Collins (brad AT chenla DOT org) ported this file (originally
70 ;; called `emacs-wiki-bloxsom.el' to Muse.
74 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
76 ;; Muse Blosxom Publishing
78 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
80 (require 'muse-publish
)
82 (defgroup muse-blosxom nil
83 "Options controlling the behaviour of Muse BLOSXOM publishing.
84 See `muse-blosxom' for more information."
87 (defcustom muse-blosxom-extension
".txt"
88 "Default file extension for publishing BLOSXOM files."
92 (defcustom muse-blosxom-header
93 "<lisp>(muse-publishing-directive \"title\")</lisp>\n"
94 "Header used for publishing BLOSXOM files."
95 :type
'(choice string file
)
98 (defcustom muse-blosxom-footer
"\n"
99 "Footer used for publishing BLOSXOM files."
100 :type
'(choice string file
)
101 :group
'muse-blosxom
)
103 (defcustom muse-blosxom-anchor-on-word nil
104 "When true, anchors surround the closest word. This allows you
105 to select them in a browser (ie, for pasting), but has the
106 side-effect of marking up headers in multiple colours if your
107 header style is different from your link style."
109 :group
'muse-blosxom
)
111 (defcustom muse-blosxom-table-attributes
112 "class=\"muse-table\" border=\"2\" cellpadding=\"5\""
113 "The attribute to be used with BLOSXOM <table> tags.
114 Note that since Muse supports direct insertion of BLOSXOM tags, you
115 can easily create any kind of table you want, as long as each
116 line begins at column 0 (to prevent it from being blockquote'd).
117 To make such a table, use this idiom:
121 [... contents of my table, in raw BLOSXOM ...]
124 It may look strange to have the tags out of sequence, but this is
125 because the Muse verbatim tag is handled during a different pass
126 than the BLOSXOM table tag."
128 :group
'muse-blosxom
)
130 (defcustom muse-blosxom-markup-regexps
133 ;;(emacs-wiki-tag-regexp 0 muse-markup-custom-tags)
135 ;; join together the parts of a list or table
136 (10000 "</\\([oud]l\\)>\\s-*<\\1>\\s-*" 0 "")
137 (10100 ,(concat " </t\\(body\\|head\\|foot\\)>\\s-*</table>\\s-*"
138 "<table[^>]*>\\s-*<t\\1>\n") 0 "")
139 (10200 "</table>\\s-*<table[^>]*>\n" 0 "")
141 ;; the beginning of the buffer begins the first paragraph
142 (10300 "\\`\n*\\([^<]\\|<\\(em\\|strong\\|code\\)>\\|<a \\)" 0
143 "<p class=\"first\">\\1")
144 ;; plain paragraph separator
145 (10400 ,(concat "\\(\n</\\(blockquote\\|center\\)>\\)?\n"
146 "\\([ \t]*\n\\)+\\(<\\(blockquote\\|center\\)>\n\\)?") 0
147 muse-blosxom-markup-paragraph
)
148 (10500 "\\([^> \n\t]\\)\\s-*\\'" 0 "\\1</p>\n")
149 (10600 "^#\\([A-C]\\)\\([0-9]*\\)\\s-*\\([_oX>CP]\\)\\s-*\\(.+\\)"
150 0 planner-markup-task
)
151 (10700 "^\\.#\\([0-9]+\\)" 0 planner-markup-note
)
152 (10800 "^#\\(date\\)\\s-+\\(.+\\)\n+" 0 muse-blosxom-markup-date-directive
))
153 "List of markup rules for publishing a Muse page to BLOSXOM.
154 For more on the structure of this list, see `muse-publish-markup-regexps'."
155 :type
'(repeat (choice
156 (list :tag
"Markup rule"
157 (choice regexp symbol
)
159 (choice string function symbol
))
161 :group
'muse-blosxom
)
163 (defcustom muse-blosxom-markup-functions
164 '((anchor . muse-blosxom-markup-anchor
)
165 (table . muse-blosxom-markup-table
)
166 (footnote . muse-blosxom-markup-footnote
))
167 "An alist of style types to custom functions for that kind of text.
168 For more on the structure of this list, see
169 `muse-publish-markup-functions'."
170 :type
'(alist :key-type symbol
:value-type function
)
171 :group
'muse-blosxom
)
173 (defcustom muse-blosxom-markup-strings
174 '((image-with-desc .
"<img src=\"%s\" alt=\"%s\">")
175 (image-link .
"<img src=\"%s\">")
176 (url-with-image .
"<a href=\"%s\"><img src=\"%s\"></a>")
177 (url-link .
"<a href=\"%s\">%s</a>")
178 (email-addr .
"<a href=\"mailto:%s\">%s</a>")
179 (emdash .
" — ")
185 (section-end .
"</h2>")
186 (subsection .
"<h3>")
187 (subsection-end .
"</h3>")
188 (subsubsection .
"<h4>")
189 (subsubsection-end .
"</h4>")
190 (begin-underline .
"<u>")
191 (end-underline .
"</u>")
192 (begin-literal .
"<code>")
193 (end-literal .
"</code>")
194 (begin-emph .
"<em>")
196 (begin-more-emph .
"<strong>")
197 (end-more-emph .
"</strong>")
198 (begin-most-emph .
"<strong><em>")
199 (end-most-emph .
"</em></strong>")
200 (begin-verse .
"<p class=\"verse\">\n")
201 (verse-space .
" ")
202 (end-verse-line .
"<br>")
203 (last-stanza-end .
"<br>")
204 (empty-verse-line .
"<br>")
206 (begin-example .
"<pre class=\"example\">")
207 (end-example .
"</pre>")
208 (begin-center .
"<center>\n")
209 (end-center .
"\n</center>")
210 (begin-quote .
"<blockquote>\n")
211 (end-quote .
"\n</blockquote>")
212 (begin-uli .
"<ul>\n<li>")
213 (end-uli .
"</li>\n</ul>")
214 (begin-oli .
"<ol>\n<li>")
215 (end-oli .
"</li>\n</ol>")
216 (begin-ddt .
"<dl>\n<dt><strong>")
217 (start-dde .
"</strong></dt>\n<dd>")
218 (end-ddt .
"</dd>\n</dl>"))
219 "Strings used for marking up text.
220 These cover the most basic kinds of markup, the handling of which
221 differs little between the various styles."
222 :type
'(alist :key-type symbol
:value-type string
)
223 :group
'muse-blosxom
)
225 (defcustom muse-blosxom-markup-specials
230 "A table of characters which must be represented specially."
231 :type
'(alist :key-type character
:value-type string
)
232 :group
'muse-blosxom
)
234 (defcustom muse-blosxom-meta-http-equiv
"Content-Type"
235 "The http-equiv attribute used for the BLOSXOM <meta> tag."
237 :group
'muse-blosxom
)
239 (defcustom muse-blosxom-meta-content-type
"text/blosxom"
240 "The content type used for the BLOSXOM <meta> tag."
242 :group
'muse-blosxom
)
244 (defcustom muse-blosxom-meta-content-encoding
(if (featurep 'mule
)
247 "If set to the symbol 'detect, use `muse-coding-map' to try
248 and determine the BLOSXOM charset from emacs's coding. If set to a string, this
249 string will be used to force a particular charset"
250 :type
'(choice string symbol
)
251 :group
'muse-blosxom
)
253 (defcustom muse-blosxom-charset-default
"iso-8859-1"
254 "The default BLOSXOM meta charset to use if no translation is found in
257 :group
'muse-blosxom
)
259 (defcustom muse-blosxom-encoding-default
'iso-8859-1
260 "The default emacs coding use if no special characters are found"
262 :group
'muse-blosxom
)
264 (defcustom muse-blosxom-encoding-map
265 '((iso-2022-jp .
"iso-2022-jp")
267 (japanese-iso-8bit .
"euc-jp")
268 (chinese-big5 .
"big5"))
269 "An alist mapping emacs coding systems to appropriate BLOSXOM charsets.
270 Use the base name of the coding system (ie, without the -unix)"
271 :type
'(alist :key-type coding-system
:value-type string
)
272 :group
'muse-blosxom
)
274 (defun muse-blosxom-transform-content-type (content-type)
275 "Using `muse-blosxom-encoding-map', try and resolve an emacs coding
276 system to an associated BLOSXOM coding system. If no match is found,
277 `muse-blosxom-charset-default' is used instead."
278 (let ((match (assoc (coding-system-base content-type
)
279 muse-blosxom-encoding-map
)))
282 muse-blosxom-charset-default
)))
284 (defun muse-blosxom-insert-anchor (anchor)
285 "Insert an anchor, either around the word at point, or within a tag."
286 (skip-chars-forward " \t\n")
287 (if (looking-at "<\\([^ />]+\\)>")
288 (let ((tag (match-string 1)))
289 (goto-char (match-end 0))
290 (insert "<a name=\"" anchor
"\" id=\"" anchor
"\">")
291 (when muse-blosxom-anchor-on-word
292 (or (and (search-forward (format "</%s>" tag
)
293 (line-end-position) t
)
294 (goto-char (match-beginning 0)))
297 (insert "<a name=\"" anchor
"\" id=\"" anchor
"\">")
298 (when muse-blosxom-anchor-on-word
302 (unless (fboundp 'looking-back
)
303 (defun looking-back (regexp &optional limit
)
305 (re-search-backward (concat "\\(?:" regexp
"\\)\\=") limit t
))))
307 (defun muse-blosxom-markup-paragraph ()
308 (let ((end (copy-marker (match-end 0) t
)))
309 (goto-char (match-beginning 0))
310 (unless (eq (char-before) ?\
>) (insert "</p>"))
312 (unless (and (eq (char-after) ?\
<)
313 (not (or (looking-at "<\\(em\\|strong\\|code\\)>")
314 (looking-at "<a "))))
316 ((looking-back "\\(</h[1-4]>\\|<hr>\\)\n\n")
317 (insert "<p class=\"first\">"))
318 ((looking-back "<\\(blockquote\\|center\\)>\n")
319 (insert "<p class=\"quoted\">"))
323 (defun muse-blosxom-markup-anchor ()
325 (muse-blosxom-insert-anchor (match-string 1))) "")
327 (defun muse-blosxom-escape-string (str)
328 "Convert to character entities any non-alphanumeric characters
329 outside a few punctuation symbols, that risk being misinterpreted
334 (while (setq pos
(string-match "[^-[:alnum:]/:._=@\\?~#]"
336 (setq code
(int-to-string (aref str pos
))
338 str
(replace-match (concat "&#" code
";") nil nil str
)
342 (defun muse-blosxom-markup-footnote ()
343 (if (/= (line-beginning-position) (match-beginning 0))
344 "<sup><a name=\"fnr.\\1\" href=\"#fn.\\1\">\\1</a></sup>"
346 "<p class=\"footnote\"><a name=\"fn.\\1\" href=\"#fnr.\\1\">\\1.</a>"
349 (let* ((beg (goto-char (match-end 0)))
350 (end (and (search-forward "\n\n" nil t
)
352 (copy-marker (match-beginning 0))
354 (while (re-search-forward "^[ \t]+\\([^\n]\\)" end t
)
355 (replace-match "\\1" t
))))))))
357 (defun muse-blosxom-markup-table ()
360 (delete-region (match-beginning 0) (match-end 0))))
361 (fields (split-string str
"\\s-*|+\\s-*"))
362 (type (and (string-match "\\s-*\\(|+\\)\\s-*" str
)
363 (length (match-string 1 str
))))
364 (part (cond ((= type
1) "tbody")
366 ((= type
3) "tfoot")))
367 (col (cond ((= type
1) "td")
371 (insert "<table " muse-blosxom-table-attributes
">\n"
374 (dolist (field fields
)
375 (insert " <" col
">" field
"</" col
">\n"))
380 ;; Handling of tags for BLOSXOM
382 (defun muse-blosxom-insert-contents (depth)
383 (let ((max-depth (or depth
2))
387 (goto-char (point-min))
388 (search-forward "Page published by Emacs Muse begins here" nil t
)
390 (while (re-search-forward "^<h\\([0-9]+\\)>\\(.+?\\)</h\\1>" nil t
)
391 (unless (get-text-property (point) 'read-only
)
392 (setq l
(1- (string-to-int (match-string 1))))
397 (when (<= l max-depth
)
398 (setq contents
(cons (cons l
(match-string-no-properties 2))
400 (goto-char (match-beginning 2))
401 (muse-blosxom-insert-anchor (concat "sec" (int-to-string index
)))
402 (setq index
(1+ index
)))))))
403 (setq index
1 contents
(reverse contents
))
404 (let ((depth 1) (sub-open 0) (p (point)))
405 (insert "<dl class=\"contents\">\n")
407 (insert "<dt class=\"contents\">\n")
408 (insert "<a href=\"#sec" (int-to-string index
) "\">"
409 (muse-publish-strip-tags (cdar contents
))
411 (setq index
(1+ index
))
413 (setq depth
(caar contents
)
414 contents
(cdr contents
))
417 ((< (caar contents
) depth
)
418 (let ((idx (caar contents
)))
420 (insert "</dl>\n</dd>\n")
421 (setq sub-open
(1- sub-open
)
423 ((> (caar contents
) depth
) ; can't jump more than one ahead
424 (insert "<dd>\n<dl class=\"contents\">\n")
425 (setq sub-open
(1+ sub-open
))))))
426 (while (> sub-open
0)
427 (insert "</dl>\n</dd>\n")
428 (setq sub-open
(1- sub-open
)))
430 (muse-publish-mark-read-only p
(point)))))
432 ;; Register the Muse BLOSXOM Publisher
434 (defun muse-blosxom-browse-file (file)
435 (browse-url (concat "file:" file
)))
437 (defun muse-blosxom-encoding ()
438 (if (stringp muse-blosxom-meta-content-encoding
)
439 muse-blosxom-meta-content-encoding
440 (muse-blosxom-transform-content-type
441 (or buffer-file-coding-system
442 muse-blosxom-encoding-default
))))
444 (defun muse-blosxom-prepare-buffer ()
445 (set (make-local-variable 'muse-publish-url-transforms
)
446 (cons 'muse-blosxom-escape-string muse-publish-url-transforms
))
447 (make-local-variable 'muse-blosxom-meta-http-equiv
)
448 (set (make-local-variable 'muse-blosxom-meta-content-type
)
449 (concat muse-blosxom-meta-content-type
"; charset="
450 (muse-blosxom-encoding))))
452 (defun muse-blosxom-finalize-buffer ()
453 (when muse-publish-generate-contents
454 (goto-char (car muse-publish-generate-contents
))
455 (muse-blosxom-insert-contents (cdr muse-publish-generate-contents
)))
456 (when (memq buffer-file-coding-system
'(no-conversion undecided-unix
))
457 ;; make it agree with the default charset
458 (setq buffer-file-coding-system muse-blosxom-encoding-default
)))
460 (unless (assoc "blosxom" muse-publishing-styles
)
461 (muse-define-style "blosxom"
462 :suffix
'muse-blosxom-extension
463 :regexps
'muse-blosxom-markup-regexps
464 :functions
'muse-blosxom-markup-functions
465 :strings
'muse-blosxom-markup-strings
466 :specials
'muse-blosxom-markup-specials
467 :before
'muse-blosxom-prepare-buffer
468 :after
'muse-blosxom-finalize-buffer
469 :header
'muse-blosxom-header
470 :footer
'muse-blosxom-footer
471 :browser
'muse-blosxom-browse-file
))
475 ;;; Maintain (published-file . date) alist
477 (defvar blosxom-page-date-alist nil
)
479 (defun muse-blosxom-markup-date-directive ()
480 "Add a date entry to `blosxom-page-date-alist' for this page."
481 (when (string= (match-string 1) "date")
482 (let ((date (match-string 2)))
485 'blosxom-page-date-alist
486 `(,(muse-published-file) .
,date
)))))
489 (defun blosxom-set-time (file)
490 "Reset the modification timestamp for published FILE.
491 Blosxom uses the modification time of a published file as its publication
492 date-time. Adding this function to `emacs-wiki-after-file-publish-hook'
493 will set the modification time of the published page according to the value
494 stored in `blosxom-page-date-alist'."
495 (let* ((page (,use-page-name file
))
496 (published (muse-published-file page
))
497 (date (cdr (assoc published blosxom-page-date-alist
))))
500 (format "touch --date='%s' %s" date published
)))))
502 (provide 'muse-blosxom
)
504 ;;; muse-blosxom.el ends here