1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;; Muse Journal Publishing
5 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7 ;; The module facilitates the keeping and publication of a journal.
8 ;; When publishing to HTML, it assumes the form of a web log, or blog.
10 ;; The input format for each entry is as follows:
12 ;; * 20040317: Title of entry
14 ;; Text for the entry.
17 ;; "You know who you are. It comes down to a simple gut check: You
18 ;; either love what you do or you don't. Period." -- P. Bronson
21 ;; The "qotd", or Quote of the Day, is entirely optional. When
22 ;; generated to HTML, this entry is rendered as:
25 ;; <div id="entry-qotd">
26 ;; <h3>Quote of the Day:</h3>
27 ;; <p>"You know who you are. It comes down to a simple gut
28 ;; check: You either love what you do or you don't. Period."
31 ;; <div id="entry-body">
32 ;; <div id="entry-head">
33 ;; <div id="entry-date">
34 ;; <span class="date">March 17, 2004</span>
36 ;; <div id="entry-title">
37 ;; <h2>Title of entry</h2>
40 ;; <div id="entry-text">
41 ;; <p>Text for the entry.</p>
46 ;; The plurality of "div" tags makes it possible to display the
47 ;; entries in any form you wish, using a CSS style.
49 ;; Also, an .RDF file can be generated from your journal by publishing
50 ;; it with the "rdf" style. It uses the first two sentences of the
51 ;; first paragraph of each entry as its "description", and
52 ;; autogenerates tags for linking to the various entries.
54 (require 'muse-publish
)
59 (defgroup muse-journal nil
60 "Rules for transforming a journal into its final form."
63 (defcustom muse-journal-heading-regexp
64 "\\(?:\\([0-9]+\\)\\(?:: \\)?\\)?\\(.+?\\)?"
65 "A regexp that match a journal heading.
66 Paren group 1 is the ISO date, group 2 is the optional category,
67 and group 3 is the optional heading for the entry."
71 (defcustom muse-journal-date-format
"%a, %e %b %Y"
72 "Date formats to use for journal entries."
76 (defcustom muse-journal-html-heading-regexp
77 (concat "^<h2[^>]*>" muse-journal-heading-regexp
"</h2>$")
78 "A regexp that match a journal heading.
79 Paren group 1 is the ISO date, group 2 is the optional category,
80 and group 3 is the optional heading for the entry."
84 (defcustom muse-journal-html-entry-template
86 <a name=\"%anchor%\" style=\"text-decoration: none\"> </a>
87 <div id=\"entry-body\">
88 <div id=\"entry-head\">
89 <div id=\"entry-date\">
90 <span class=\"date\">%date%</span>
92 <div id=\"entry-title\">
96 <div id=\"entry-text\">
97 <div id=\"entry-qotd\">
106 :group
'muse-journal
)
108 (defcustom muse-journal-latex-section
109 "\\section*{%title% \\hfill {\\normalsize %date%}}
110 \\addcontentsline{toc}{chapter}{%title%}"
113 :group
'muse-journal
)
115 (defcustom muse-journal-latex-subsection
116 "\\subsection*{%title%}
117 \\addcontentsline{toc}{section}{%title%}"
120 :group
'muse-journal
)
122 (defcustom muse-journal-latex-markup-tags
123 '(("qotd" t nil muse-journal-latex-qotd-tag
))
124 "See `muse-publish-markup-tags' for more info."
125 :type
'(repeat (list (string :tag
"Markup tag")
126 (boolean :tag
"Expect closing tag" :value t
)
127 (boolean :tag
"Parse attributes" :value nil
)
129 :group
'muse-journal
)
131 (defun muse-journal-generate-pages ()
132 (let ((output-dir (muse-style-element :path
)))
133 (goto-char (point-min))
134 (while (re-search-forward muse-journal-heading-regexp nil t
)
135 (let* ((date (match-string 1))
136 (category (match-string 1))
137 (category-file (concat output-dir category
"/index.html"))
138 (heading (match-string 1)))
141 (defcustom muse-journal-rdf-extension
".rdf"
144 :group
'muse-journal
)
146 (defcustom muse-journal-rdf-base-url
""
147 "The base URL of the website reference by the RDF file."
149 :group
'muse-journal
)
151 (defcustom muse-journal-rdf-header
152 "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"
153 xmlns=\"http://purl.org/rss/1.0/\"
154 xmlns:dc=\"http://purl.org/dc/elements/1.1/\">
155 <channel rdf:about=\"<lisp>(concat muse-journal-rdf-base-url
156 (muse-publish-output-name))</lisp>\">
157 <title><lisp>(muse-publishing-directive \"title\")</lisp></title>
158 <link><lisp>(concat muse-journal-rdf-base-url
159 (concat (muse-page-name)
160 muse-html-extension))</lisp></link>
161 <description>A Journal</description>
164 <rdf:li resource=\"<lisp>
165 (concat muse-journal-rdf-base-url
166 (concat (muse-page-name)
167 muse-html-extension))</lisp>\"/>
172 :type
'(choice string file
)
173 :group
'muse-journal
)
175 (defcustom muse-journal-rdf-footer
178 :type
'(choice string file
)
179 :group
'muse-journal
)
181 (defcustom muse-journal-rdf-date-format
185 :group
'muse-journal
)
187 (defcustom muse-journal-rdf-entry-template
188 " <item rdf:about=\"%link%#%anchor%\">
189 <title>%title%</title>
193 <link>%link%#%anchor%</link>
194 <dc:date>%date%</dc:date>
195 <dc:creator>%maintainer%</dc:creator>
199 :group
'muse-journal
)
201 (defcustom muse-journal-rdf-markup-regexps
202 '((10000 muse-link-regexp
0 "\\2"))
203 "List of markup rules for publishing a Muse journal page to RDF.
204 For more on the structure of this list, see `muse-publish-markup-regexps'."
205 :type
'(repeat (choice
206 (list :tag
"Markup rule"
208 (choice regexp symbol
)
210 (choice string function symbol
))
212 :group
'muse-journal
)
214 (defcustom muse-journal-rdf-markup-functions
218 "An alist of style types to custom functions for that kind of text.
219 For more on the structure of this list, see
220 `muse-publish-markup-functions'."
221 :type
'(alist :key-type symbol
:value-type function
)
222 :group
'muse-journal
)
224 (defun muse-journal-anchorize-title (title)
226 (if (string-match "(" title
)
227 (setq title
(substring title
0 (match-beginning 0))))
228 (if (string-match "<[^>]+>" title
)
229 (setq title
(replace-match "" nil nil title
)))
230 (downcase (replace-regexp-in-string "[^a-zA-Z0-9_]" ""
233 (defun muse-journal-sort-entries (&optional direction
)
239 (if (re-search-forward "^\\* [0-9]+" nil t
)
240 (goto-char (match-beginning 0))
241 (goto-char (point-max)))))
244 (if (re-search-forward "^\\* [0-9]+" nil t
)
245 (goto-char (1- (match-beginning 0)))
246 (goto-char (point-max)))))
254 (defun muse-journal-html-munge-buffer ()
255 (goto-char (point-min))
256 (let ((heading-regexp muse-journal-html-heading-regexp
)
257 (inhibit-read-only t
))
258 (while (re-search-forward heading-regexp nil t
)
259 (let* ((date (match-string 1))
261 (title (match-string 2))
264 (delete-region (match-beginning 0) (match-end 0))
267 (while (string-match "\\(^<[^>]+>\\|<[^>]+>$\\)" clean-title
)
268 (setq clean-title
(replace-match "" nil nil clean-title
)))))
272 (concat "\\`\\([1-9][0-9][0-9][0-9]\\)[./]?"
273 "\\([0-1][0-9]\\)[./]?\\([0-3][0-9]\\)") date
))
277 (string-to-int (match-string 3 date
))
278 (string-to-int (match-string 2 date
))
279 (string-to-int (match-string 1 date
))
281 date
(concat (format-time-string
282 muse-journal-date-format datestamp
)
283 (substring date
(match-end 0))))))
286 (point) (if (re-search-forward
287 (concat "\\(^<hr>$\\|"
288 heading-regexp
"\\)") nil t
)
291 (goto-char (point-max))
292 (while (and (not (bobp))
293 (eq ?\
(char-syntax (char-before))))
295 (goto-char (point-min))
296 (while (and (not (eobp))
297 (eq ?\
(char-syntax (char-after))))
300 (when (search-forward "<qotd>" nil t
)
301 (let ((tag-beg (match-beginning 0))
303 (re-search-forward "</qotd>\n*")
304 (setq qotd
(buffer-substring-no-properties
305 beg
(match-beginning 0)))
306 (delete-region tag-beg
(match-end 0)))))
307 (setq text
(buffer-string))
308 (delete-region (point-min) (point-max))
309 (let ((entry muse-journal-html-entry-template
))
310 (while (string-match "%date%" entry
)
311 (setq entry
(replace-match (or date
"") nil t entry
)))
312 (while (string-match "%title%" entry
)
313 (setq entry
(replace-match (or title
" ") nil t entry
)))
314 (while (string-match "%anchor%" entry
)
315 (setq entry
(replace-match
316 (muse-journal-anchorize-title
317 (or clean-title orig-date
))
319 (while (string-match "%qotd%" entry
)
320 (setq entry
(replace-match (or qotd
"") nil t entry
)))
321 (while (string-match "%text%" entry
)
322 (setq entry
(replace-match text nil t entry
)))
325 (goto-char (point-min))
326 (search-forward "<div id=\"entry-qotd\">")
327 (let ((beg (match-beginning 0)))
328 (re-search-forward "</div>\n*")
329 (delete-region beg
(point))))))))))
331 (defun muse-journal-latex-munge-buffer ()
332 (goto-char (point-min))
333 (let ((heading-regexp
334 (concat "^" (regexp-quote (muse-markup-text 'section
))
335 muse-journal-heading-regexp
336 (regexp-quote (muse-markup-text 'section-end
)) "$"))
337 (inhibit-read-only t
))
338 (when (re-search-forward heading-regexp nil t
)
339 (goto-char (match-beginning 0))
343 (if (re-search-forward heading-regexp nil t
)
344 (goto-char (match-beginning 0))
345 (goto-char (point-max)))))
348 (if (re-search-forward heading-regexp nil t
)
349 (goto-char (1- (match-beginning 0)))
350 (goto-char (point-max)))))
357 (while (re-search-forward heading-regexp nil t
)
358 (let ((date (match-string 1))
359 (title (match-string 2))
364 (concat "\\([1-9][0-9][0-9][0-9]\\)[./]?"
365 "\\([0-1][0-9]\\)[./]?\\([0-3][0-9]\\)") date
))
366 (setq date
(encode-time
368 (string-to-int (match-string 3 date
))
369 (string-to-int (match-string 2 date
))
370 (string-to-int (match-string 1 date
))
372 date
(format-time-string
373 muse-journal-date-format date
))))
375 (setq section muse-journal-latex-section
)
376 (while (string-match "%title%" section
)
377 (setq section
(replace-match (or title
"Untitled")
379 (while (string-match "%date%" section
)
380 (setq section
(replace-match (or date
"") nil t section
))))
381 (replace-match section nil t
))))
382 (goto-char (point-min))
383 (let ((subheading-regexp
384 (concat "^" (regexp-quote (muse-markup-text 'subsection
))
386 (regexp-quote (muse-markup-text 'subsection-end
)) "$"))
387 (inhibit-read-only t
))
388 (while (re-search-forward subheading-regexp nil t
)
389 (let ((subsection muse-journal-latex-subsection
))
391 (let ((title (match-string 1)))
392 (while (string-match "%title%" subsection
)
393 (setq subsection
(replace-match title nil t subsection
)))))
394 (replace-match subsection nil t
)))))
396 (defun muse-journal-latex-qotd-tag (beg end
)
398 (insert (muse-markup-text 'begin-quote
))
400 (insert (muse-markup-text 'end-quote
)))
402 (defun muse-journal-rdf-munge-buffer ()
403 (goto-char (point-min))
404 (let ((heading-regexp (concat "^\\* " muse-journal-heading-regexp
"$"))
405 (inhibit-read-only t
))
406 (while (re-search-forward heading-regexp nil t
)
407 (let* ((date (match-string 1))
409 (title (match-string 2))
414 (concat "\\([1-9][0-9][0-9][0-9]\\)[./]?"
415 "\\([0-1][0-9]\\)[./]?\\([0-3][0-9]\\)") date
))
416 (setq date
(encode-time 0 0 0
417 (string-to-int (match-string 3 date
))
418 (string-to-int (match-string 2 date
))
419 (string-to-int (match-string 1 date
))
421 date
(format-time-string
422 muse-journal-rdf-date-format date
))))
426 (if (re-search-forward heading-regexp nil t
)
429 (goto-char (point-min))
430 (delete-region (point) (line-end-position))
431 (re-search-forward "</qotd>\n+" nil t
)
432 (while (and (char-after)
433 (eq ?\
(char-syntax (char-after))))
437 (setq desc
(buffer-substring beg
(point))))
438 (delete-region (point-min) (point-max))
439 (let ((entry muse-journal-rdf-entry-template
))
440 (while (string-match "%date%" entry
)
441 (setq entry
(replace-match (or date
"") nil t entry
)))
442 (while (string-match "%title%" entry
)
443 (setq entry
(replace-match (or title
"Untitled") nil t entry
)))
444 (while (string-match "%desc%" entry
)
445 (setq entry
(replace-match desc nil t entry
)))
446 (while (string-match "%link%" entry
)
447 (setq entry
(replace-match
448 (concat muse-journal-rdf-base-url
449 (concat (muse-page-name)
450 muse-html-extension
))
452 (while (string-match "%anchor%" entry
)
453 (setq entry
(replace-match
454 (muse-journal-anchorize-title (or title orig-date
))
456 (while (string-match "%maintainer%" entry
)
457 (setq entry
(replace-match
458 (or (muse-style-element :maintainer
)
459 (concat "webmaster@" (system-name)))
463 (unless (assoc "journal-html" muse-publishing-styles
)
464 (muse-derive-style "journal-html" "html"
465 :before-end
'muse-journal-html-munge-buffer
)
467 (muse-derive-style "journal-latex" "latex"
468 :tags
'muse-journal-latex-markup-tags
469 :before-end
'muse-journal-latex-munge-buffer
)
471 (muse-derive-style "journal-pdf" "pdf"
472 :tags
'muse-journal-latex-markup-tags
473 :before-end
'muse-journal-latex-munge-buffer
)
475 (muse-derive-style "journal-book-latex" "book-latex"
477 :tags
'muse-journal-latex-markup-tags
478 :before-end
'muse-journal-latex-munge-buffer
)
480 (muse-derive-style "journal-book-pdf" "book-pdf"
482 :tags
'muse-journal-latex-markup-tags
483 :before-end
'muse-journal-latex-munge-buffer
)
485 (muse-define-style "journal-rdf"
486 :suffix
'muse-journal-rdf-extension
487 :regexps
'muse-journal-rdf-markup-regexps
488 :functions
'muse-journal-rdf-markup-functions
489 :before
'muse-journal-rdf-munge-buffer
490 :header
'muse-journal-rdf-header
491 :footer
'muse-journal-rdf-footer
))
493 (provide 'muse-journal
)
495 ;;; muse-journal.el ends here