775 lines
30 KiB
EmacsLisp
775 lines
30 KiB
EmacsLisp
;;; muse-journal.el --- keep and publish a journal
|
|
|
|
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
|
;; Free Software Foundation, Inc.
|
|
|
|
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
|
|
|
|
;; Emacs Muse 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, or (at your
|
|
;; option) any later version.
|
|
|
|
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
|
|
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
;; Boston, MA 02110-1301, USA.
|
|
|
|
;;; Commentary:
|
|
|
|
;; The module facilitates the keeping and publication of a journal.
|
|
;; When publishing to HTML, it assumes the form of a web log, or blog.
|
|
;;
|
|
;; The input format for each entry is as follows:
|
|
;;
|
|
;; * 20040317: Title of entry
|
|
;;
|
|
;; Text for the entry.
|
|
;;
|
|
;; <qotd>
|
|
;; "You know who you are. It comes down to a simple gut check: You
|
|
;; either love what you do or you don't. Period." -- P. Bronson
|
|
;; </qotd>
|
|
;;
|
|
;; The "qotd", or Quote of the Day, is entirely optional. When
|
|
;; generated to HTML, this entry is rendered as:
|
|
;;
|
|
;; <div class="entry">
|
|
;; <div class="entry-qotd">
|
|
;; <h3>Quote of the Day:</h3>
|
|
;; <p>"You know who you are. It comes down to a simple gut
|
|
;; check: You either love what you do or you don't. Period."
|
|
;; -- P. Bronson</p>
|
|
;; </div>
|
|
;; <div class="entry-body">
|
|
;; <div class="entry-head">
|
|
;; <div class="entry-date">
|
|
;; <span class="date">March 17, 2004</span>
|
|
;; </div>
|
|
;; <div class="entry-title">
|
|
;; <h2>Title of entry</h2>
|
|
;; </div>
|
|
;; </div>
|
|
;; <div class="entry-text">
|
|
;; <p>Text for the entry.</p>
|
|
;; </div>
|
|
;; </div>
|
|
;; </div>
|
|
;;
|
|
;; The plurality of "div" tags makes it possible to display the
|
|
;; entries in any form you wish, using a CSS style.
|
|
;;
|
|
;; Also, an .RDF file can be generated from your journal by publishing
|
|
;; it with the "rdf" style. It uses the first two sentences of the
|
|
;; first paragraph of each entry as its "description", and
|
|
;; autogenerates tags for linking to the various entries.
|
|
|
|
;;; Contributors:
|
|
|
|
;; René Stadler (mail AT renestadler DOT de) provided a patch that
|
|
;; causes dates in RSS feeds to be generated in a format that RSS
|
|
;; readers can parse.
|
|
|
|
;;; Code:
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; Muse Journal Publishing
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(require 'muse-publish)
|
|
(require 'muse-html)
|
|
(require 'muse-latex)
|
|
(require 'muse-book)
|
|
|
|
(defgroup muse-journal nil
|
|
"Rules for transforming a journal into its final form."
|
|
:group 'muse-publish)
|
|
|
|
(defcustom muse-journal-heading-regexp
|
|
"\\(?:\\([0-9]+\\)\\(?:: \\)?\\)?\\(.+?\\)?"
|
|
"A regexp that matches a journal heading.
|
|
Paren group 1 is the ISO date, group 2 is the optional category,
|
|
and group 3 is the optional heading for the entry."
|
|
:type 'regexp
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-date-format "%a, %e %b %Y"
|
|
"Date format to use for journal entries."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-html-heading-regexp
|
|
(concat "^<h2[^>\n]*>" muse-journal-heading-regexp "</h2>$")
|
|
"A regexp that matches a journal heading from an HTML document.
|
|
Paren group 1 is the ISO date, group 2 is the optional category,
|
|
and group 3 is the optional heading for the entry."
|
|
:type 'regexp
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rss-heading-regexp
|
|
(concat "^\\* " muse-journal-heading-regexp "$")
|
|
"A regexp that matches a journal heading from an HTML document.
|
|
Paren group 1 is the ISO date, group 2 is the optional category,
|
|
and group 3 is the optional heading for the entry."
|
|
:type 'regexp
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-html-entry-template
|
|
"<div class=\"entry\">
|
|
<a name=\"%anchor%\" style=\"text-decoration: none\"> </a>
|
|
<div class=\"entry-body\">
|
|
<div class=\"entry-head\">
|
|
<div class=\"entry-date\">
|
|
<span class=\"date\">%date%</span>
|
|
</div>
|
|
<div class=\"entry-title\">
|
|
<h2>%title%</h2>
|
|
</div>
|
|
</div>
|
|
<div class=\"entry-text\">
|
|
<div class=\"entry-qotd\">
|
|
<p>%qotd%</p>
|
|
</div>
|
|
%text%
|
|
</div>
|
|
</div>
|
|
</div>\n\n"
|
|
"Template used to publish individual journal entries as HTML.
|
|
This may be text or a filename."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-latex-section
|
|
"\\section*{%title% \\hfill {\\normalsize %date%}}
|
|
\\addcontentsline{toc}{chapter}{%title%}"
|
|
"Template used to publish a LaTeX section."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-latex-subsection
|
|
"\\subsection*{%title%}
|
|
\\addcontentsline{toc}{section}{%title%}"
|
|
"Template used to publish a LaTeX subsection."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-markup-tags
|
|
'(("qotd" t nil nil muse-journal-qotd-tag))
|
|
"A list of tag specifications, for specially marking up Journal entries.
|
|
See `muse-publish-markup-tags' for more info.
|
|
|
|
This is used by journal-latex and its related styles, as well as
|
|
the journal-rss-entry style, which both journal-rdf and
|
|
journal-rss use."
|
|
:type '(repeat (list (string :tag "Markup tag")
|
|
(boolean :tag "Expect closing tag" :value t)
|
|
(boolean :tag "Parse attributes" :value nil)
|
|
(boolean :tag "Nestable" :value nil)
|
|
function))
|
|
:group 'muse-journal)
|
|
|
|
;; FIXME: This doesn't appear to be used.
|
|
(defun muse-journal-generate-pages ()
|
|
(let ((output-dir (muse-style-element :path)))
|
|
(goto-char (point-min))
|
|
(while (re-search-forward muse-journal-heading-regexp nil t)
|
|
(let* ((date (match-string 1))
|
|
(category (match-string 1))
|
|
(category-file (concat output-dir category "/index.html"))
|
|
(heading (match-string 1)))
|
|
t))))
|
|
|
|
(defcustom muse-journal-rdf-extension ".rdf"
|
|
"Default file extension for publishing RDF (RSS 1.0) files."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rdf-base-url ""
|
|
"The base URL of the website referenced by the RDF file."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rdf-header
|
|
"<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"
|
|
xmlns=\"http://purl.org/rss/1.0/\"
|
|
xmlns:dc=\"http://purl.org/dc/elements/1.1/\">
|
|
<channel rdf:about=\"<lisp>(concat (muse-style-element :base-url)
|
|
(muse-publish-link-name))</lisp>\">
|
|
<title><lisp>(muse-publishing-directive \"title\")</lisp></title>
|
|
<link><lisp>(concat (muse-style-element :base-url)
|
|
(concat (muse-page-name)
|
|
muse-html-extension))</lisp></link>
|
|
<description><lisp>(muse-publishing-directive \"desc\")</lisp></description>
|
|
<items>
|
|
<rdf:Seq>
|
|
<rdf:li resource=\"<lisp>
|
|
(concat (muse-style-element :base-url)
|
|
(concat (muse-page-name)
|
|
muse-html-extension))</lisp>\"/>
|
|
</rdf:Seq>
|
|
</items>
|
|
</channel>\n"
|
|
"Header used for publishing RDF (RSS 1.0) files.
|
|
This may be text or a filename."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rdf-footer
|
|
"</rdf:RDF>\n"
|
|
"Footer used for publishing RDF (RSS 1.0) files.
|
|
This may be text or a filename."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rdf-date-format
|
|
"%Y-%m-%dT%H:%M:%S"
|
|
"Date format to use for RDF entries."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rdf-entry-template
|
|
"\n <item rdf:about=\"%link%#%anchor%\">
|
|
<title>%title%</title>
|
|
<description>
|
|
%desc%
|
|
</description>
|
|
<link>%link%#%anchor%</link>
|
|
<dc:date>%date%</dc:date>
|
|
<dc:creator>%maintainer%</dc:creator>
|
|
</item>\n"
|
|
"Template used to publish individual journal entries as RDF.
|
|
This may be text or a filename."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rdf-summarize-entries nil
|
|
"If non-nil, include only summaries in the RDF file, not the full data.
|
|
|
|
The default is nil, because this annoys some subscribers."
|
|
:type 'boolean
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rss-extension ".xml"
|
|
"Default file extension for publishing RSS 2.0 files."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rss-base-url ""
|
|
"The base URL of the website referenced by the RSS file."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rss-header
|
|
"<\?xml version=\"1.0\" encoding=\"<lisp>
|
|
(muse-html-encoding)</lisp>\"?>
|
|
<rss version=\"2.0\">
|
|
<channel>
|
|
<title><lisp>(muse-publishing-directive \"title\")</lisp></title>
|
|
<link><lisp>(concat (muse-style-element :base-url)
|
|
(concat (muse-page-name)
|
|
muse-html-extension))</lisp></link>
|
|
<description><lisp>(muse-publishing-directive \"desc\")</lisp></description>
|
|
<language>en-us</language>
|
|
<generator>Emacs Muse</generator>\n\n"
|
|
"Header used for publishing RSS 2.0 files. This may be text or a filename."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rss-footer
|
|
"\n\n </channel>
|
|
</rss>\n"
|
|
"Footer used for publishing RSS 2.0 files. This may be text or a filename."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rss-date-format
|
|
"%a, %d %b %Y %H:%M:%S %Z"
|
|
"Date format to use for RSS 2.0 entries."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rss-entry-template
|
|
"\n <item>
|
|
<title>%title%</title>
|
|
<link>%link%#%anchor%</link>
|
|
<description>%desc%</description>
|
|
<author><lisp>(muse-publishing-directive \"author\")</lisp></author>
|
|
<pubDate>%date%</pubDate>
|
|
<guid>%link%#%anchor%</guid>
|
|
%enclosure%
|
|
</item>\n"
|
|
"Template used to publish individual journal entries as RSS 2.0.
|
|
This may be text or a filename."
|
|
:type 'string
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rss-enclosure-types-alist
|
|
'(("mp3" . "audio/mpeg"))
|
|
"File types that are accepted as RSS enclosures.
|
|
This is an alist that maps file extension to content type.
|
|
Useful for podcasting."
|
|
:type '(alist :key-type string :value-type string)
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rss-summarize-entries nil
|
|
"If non-nil, include only summaries in the RSS file, not the full data.
|
|
|
|
The default is nil, because this annoys some subscribers."
|
|
:type 'boolean
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rss-markup-regexps
|
|
'((10000 muse-explicit-link-regexp 0 "\\2"))
|
|
"List of markup rules for publishing a Muse journal page to RSS 2.0.
|
|
For more information on the structure of this list, see
|
|
`muse-publish-markup-regexps'."
|
|
:type '(repeat (choice
|
|
(list :tag "Markup rule"
|
|
integer
|
|
(choice regexp symbol)
|
|
integer
|
|
(choice string function symbol))
|
|
function))
|
|
:group 'muse-journal)
|
|
|
|
(defcustom muse-journal-rss-markup-functions
|
|
'((email . ignore)
|
|
(link . ignore)
|
|
(url . ignore))
|
|
"An alist of style types to custom functions for that kind of text.
|
|
For more on the structure of this list, see
|
|
`muse-publish-markup-functions'."
|
|
:type '(alist :key-type symbol :value-type function)
|
|
:group 'muse-journal)
|
|
|
|
(defun muse-journal-anchorize-title (title)
|
|
"This strips tags from TITLE, truncates TITLE at begin parenthesis,
|
|
and escapes any remaining non-alphanumeric characters."
|
|
(save-match-data
|
|
(if (string-match "(" title)
|
|
(setq title (substring title 0 (match-beginning 0))))
|
|
(if (string-match "<[^>]+>" title)
|
|
(setq title (replace-match "" nil nil title)))
|
|
(let (pos code len ch)
|
|
(while (setq pos (string-match (concat "[^" muse-regexp-alnum "_]")
|
|
title pos))
|
|
(setq ch (aref title pos)
|
|
code (format "%%%02X" (cond ((fboundp 'char-to-ucs)
|
|
(char-to-ucs ch))
|
|
((fboundp 'char-to-int)
|
|
(char-to-int ch))
|
|
(t ch)))
|
|
len (length code)
|
|
title (concat (substring title 0 pos)
|
|
code
|
|
(when (< pos (length title))
|
|
(substring title (1+ pos) nil)))
|
|
pos (+ len pos)))
|
|
title)))
|
|
|
|
(defun muse-journal-sort-entries (&optional direction)
|
|
(interactive "P")
|
|
(sort-subr
|
|
direction
|
|
(function
|
|
(lambda ()
|
|
(if (re-search-forward "^\\* [0-9]+" nil t)
|
|
(goto-char (match-beginning 0))
|
|
(goto-char (point-max)))))
|
|
(function
|
|
(lambda ()
|
|
(if (re-search-forward "^\\* [0-9]+" nil t)
|
|
(goto-char (1- (match-beginning 0)))
|
|
(goto-char (point-max)))))
|
|
(function
|
|
(lambda ()
|
|
(forward-char 2)))
|
|
(function
|
|
(lambda ()
|
|
(end-of-line)))))
|
|
|
|
(defun muse-journal-qotd-tag (beg end)
|
|
(muse-publish-ensure-block beg end)
|
|
(muse-insert-markup (muse-markup-text 'begin-quote))
|
|
(muse-insert-markup (muse-markup-text 'begin-quote-item))
|
|
(goto-char end)
|
|
(muse-insert-markup (muse-markup-text 'end-quote-item))
|
|
(muse-insert-markup (muse-markup-text 'end-quote)))
|
|
|
|
(defun muse-journal-html-munge-buffer ()
|
|
(goto-char (point-min))
|
|
(let ((heading-regexp muse-journal-html-heading-regexp)
|
|
(inhibit-read-only t))
|
|
(while (re-search-forward heading-regexp nil t)
|
|
(let* ((date (match-string 1))
|
|
(orig-date date)
|
|
(title (match-string 2))
|
|
(clean-title title)
|
|
datestamp qotd text)
|
|
(delete-region (match-beginning 0) (match-end 0))
|
|
(if clean-title
|
|
(save-match-data
|
|
(while (string-match "\\(^<[^>]+>\\|<[^>]+>$\\)" clean-title)
|
|
(setq clean-title (replace-match "" nil nil clean-title)))))
|
|
(save-match-data
|
|
(when (and date
|
|
(string-match
|
|
(concat "\\`\\([1-9][0-9][0-9][0-9]\\)[./]?"
|
|
"\\([0-1][0-9]\\)[./]?\\([0-3][0-9]\\)") date))
|
|
(setq datestamp
|
|
(encode-time
|
|
0 0 0
|
|
(string-to-number (match-string 3 date))
|
|
(string-to-number (match-string 2 date))
|
|
(string-to-number (match-string 1 date))
|
|
nil)
|
|
date (concat (format-time-string
|
|
muse-journal-date-format datestamp)
|
|
(substring date (match-end 0))))))
|
|
(save-restriction
|
|
(narrow-to-region
|
|
(point) (if (re-search-forward
|
|
(concat "\\(^<hr>$\\|"
|
|
heading-regexp "\\)") nil t)
|
|
(match-beginning 0)
|
|
(point-max)))
|
|
(goto-char (point-max))
|
|
(while (and (not (bobp))
|
|
(eq ?\ (char-syntax (char-before))))
|
|
(delete-char -1))
|
|
(goto-char (point-min))
|
|
(while (and (not (eobp))
|
|
(eq ?\ (char-syntax (char-after))))
|
|
(delete-char 1))
|
|
(save-excursion
|
|
(when (search-forward "<qotd>" nil t)
|
|
(let ((tag-beg (match-beginning 0))
|
|
(beg (match-end 0))
|
|
end)
|
|
(re-search-forward "</qotd>\n*")
|
|
(setq end (point-marker))
|
|
(save-restriction
|
|
(narrow-to-region beg (match-beginning 0))
|
|
(muse-publish-escape-specials (point-min) (point-max)
|
|
nil 'document)
|
|
(setq qotd (buffer-substring-no-properties
|
|
(point-min) (point-max))))
|
|
(delete-region tag-beg end)
|
|
(set-marker end nil))))
|
|
(setq text (buffer-string))
|
|
(delete-region (point-min) (point-max))
|
|
(let ((entry muse-journal-html-entry-template))
|
|
(muse-insert-file-or-string entry)
|
|
(muse-publish-mark-read-only (point-min) (point-max))
|
|
(goto-char (point-min))
|
|
(while (search-forward "%date%" nil t)
|
|
(remove-text-properties (match-beginning 0) (match-end 0)
|
|
'(read-only nil rear-nonsticky nil))
|
|
(replace-match (or date "") nil t))
|
|
(goto-char (point-min))
|
|
(while (search-forward "%title%" nil t)
|
|
(remove-text-properties (match-beginning 0) (match-end 0)
|
|
'(read-only nil rear-nonsticky nil))
|
|
(replace-match (or title " ") nil t))
|
|
(goto-char (point-min))
|
|
(while (search-forward "%anchor%" nil t)
|
|
(replace-match (muse-journal-anchorize-title
|
|
(or clean-title orig-date))
|
|
nil t))
|
|
(goto-char (point-min))
|
|
(while (search-forward "%qotd%" nil t)
|
|
(save-restriction
|
|
(narrow-to-region (match-beginning 0) (match-end 0))
|
|
(delete-region (point-min) (point-max))
|
|
(when qotd (muse-insert-markup qotd))))
|
|
(goto-char (point-min))
|
|
(while (search-forward "%text%" nil t)
|
|
(remove-text-properties (match-beginning 0) (match-end 0)
|
|
'(read-only nil rear-nonsticky nil))
|
|
(replace-match text nil t))
|
|
(when (null qotd)
|
|
(goto-char (point-min))
|
|
(when (search-forward "<div class=\"entry-qotd\">" nil t)
|
|
(let ((beg (match-beginning 0)))
|
|
(re-search-forward "</div>\n*" nil t)
|
|
(delete-region beg (point))))))))))
|
|
;; indicate that we are to continue the :before-end processing
|
|
nil)
|
|
|
|
(defun muse-journal-latex-munge-buffer ()
|
|
(goto-char (point-min))
|
|
(let ((heading-regexp
|
|
(concat "^" (regexp-quote (muse-markup-text 'section))
|
|
muse-journal-heading-regexp
|
|
(regexp-quote (muse-markup-text 'section-end)) "$"))
|
|
(inhibit-read-only t))
|
|
(when (re-search-forward heading-regexp nil t)
|
|
(goto-char (match-beginning 0))
|
|
(sort-subr nil
|
|
(function
|
|
(lambda ()
|
|
(if (re-search-forward heading-regexp nil t)
|
|
(goto-char (match-beginning 0))
|
|
(goto-char (point-max)))))
|
|
(function
|
|
(lambda ()
|
|
(if (re-search-forward heading-regexp nil t)
|
|
(goto-char (1- (match-beginning 0)))
|
|
(goto-char (point-max)))))
|
|
(function
|
|
(lambda ()
|
|
(forward-char 2)))
|
|
(function
|
|
(lambda ()
|
|
(end-of-line)))))
|
|
(while (re-search-forward heading-regexp nil t)
|
|
(let ((date (match-string 1))
|
|
(title (match-string 2))
|
|
;; FIXME: Nothing is done with qotd
|
|
qotd section)
|
|
(save-match-data
|
|
(when (and date
|
|
(string-match
|
|
(concat "\\([1-9][0-9][0-9][0-9]\\)[./]?"
|
|
"\\([0-1][0-9]\\)[./]?\\([0-3][0-9]\\)") date))
|
|
(setq date (encode-time
|
|
0 0 0
|
|
(string-to-number (match-string 3 date))
|
|
(string-to-number (match-string 2 date))
|
|
(string-to-number (match-string 1 date))
|
|
nil)
|
|
date (format-time-string
|
|
muse-journal-date-format date))))
|
|
(save-restriction
|
|
(narrow-to-region (match-beginning 0) (match-end 0))
|
|
(delete-region (point-min) (point-max))
|
|
(muse-insert-markup muse-journal-latex-section)
|
|
(goto-char (point-min))
|
|
(while (search-forward "%title%" nil t)
|
|
(replace-match (or title "Untitled") nil t))
|
|
(goto-char (point-min))
|
|
(while (search-forward "%date%" nil t)
|
|
(replace-match (or date "") nil t))))))
|
|
(goto-char (point-min))
|
|
(let ((subheading-regexp
|
|
(concat "^" (regexp-quote (muse-markup-text 'subsection))
|
|
"\\([^\n}]+\\)"
|
|
(regexp-quote (muse-markup-text 'subsection-end)) "$"))
|
|
(inhibit-read-only t))
|
|
(while (re-search-forward subheading-regexp nil t)
|
|
(let ((title (match-string 1)))
|
|
(save-restriction
|
|
(narrow-to-region (match-beginning 0) (match-end 0))
|
|
(delete-region (point-min) (point-max))
|
|
(muse-insert-markup muse-journal-latex-subsection)
|
|
(goto-char (point-min))
|
|
(while (search-forward "%title%" nil t)
|
|
(replace-match title nil t))))))
|
|
;; indicate that we are to continue the :before-end processing
|
|
nil)
|
|
|
|
(defun muse-journal-rss-munge-buffer ()
|
|
(goto-char (point-min))
|
|
(let ((heading-regexp muse-journal-rss-heading-regexp)
|
|
(inhibit-read-only t))
|
|
(while (re-search-forward heading-regexp nil t)
|
|
(let* ((date (match-string 1))
|
|
(orig-date date)
|
|
(title (match-string 2))
|
|
;; FIXME: Nothing is done with qotd
|
|
enclosure qotd desc)
|
|
(if title
|
|
(save-match-data
|
|
(if (string-match muse-explicit-link-regexp title)
|
|
(setq enclosure (muse-get-link title)
|
|
title (muse-get-link-desc title)))))
|
|
(save-match-data
|
|
(when (and date
|
|
(string-match
|
|
(concat "\\([1-9][0-9][0-9][0-9]\\)[./]?"
|
|
"\\([0-1][0-9]\\)[./]?\\([0-3][0-9]\\)") date))
|
|
(setq date (encode-time 0 0 0
|
|
(string-to-number (match-string 3 date))
|
|
(string-to-number (match-string 2 date))
|
|
(string-to-number (match-string 1 date))
|
|
nil)
|
|
;; make sure that date is in a format that RSS
|
|
;; readers can handle
|
|
date (let ((system-time-locale "C"))
|
|
(format-time-string
|
|
(muse-style-element :date-format) date)))))
|
|
(save-restriction
|
|
(narrow-to-region
|
|
(match-beginning 0)
|
|
(if (re-search-forward heading-regexp nil t)
|
|
(match-beginning 0)
|
|
(if (re-search-forward "^Footnotes:" nil t)
|
|
(match-beginning 0)
|
|
(point-max))))
|
|
(goto-char (point-min))
|
|
(delete-region (point) (muse-line-end-position))
|
|
(re-search-forward "</qotd>\n+" nil t)
|
|
(while (and (char-after)
|
|
(eq ?\ (char-syntax (char-after))))
|
|
(delete-char 1))
|
|
(let ((beg (point)))
|
|
(if (muse-style-element :summarize)
|
|
(progn
|
|
(forward-sentence 2)
|
|
(setq desc (concat (buffer-substring beg (point)) "...")))
|
|
(save-restriction
|
|
(muse-publish-markup-buffer "rss-entry" "journal-rss-entry")
|
|
(goto-char (point-min))
|
|
(if (re-search-forward "Page published by Emacs Muse" nil t)
|
|
(goto-char (muse-line-end-position))
|
|
(muse-display-warning
|
|
(concat
|
|
"Cannot find 'Page published by Emacs Muse begins here'.\n"
|
|
"You will probably need this text in your header."))
|
|
(goto-char (point-min)))
|
|
(setq beg (point))
|
|
(if (re-search-forward "Page published by Emacs Muse" nil t)
|
|
(goto-char (muse-line-beginning-position))
|
|
(muse-display-warning
|
|
(concat
|
|
"Cannot find 'Page published by Emacs Muse ends here'.\n"
|
|
"You will probably need this text in your footer."))
|
|
(goto-char (point-max)))
|
|
(setq desc (buffer-substring beg (point))))))
|
|
(unless (string= desc "")
|
|
(setq desc (concat "<![CDATA[" desc "]]>")))
|
|
(delete-region (point-min) (point-max))
|
|
(let ((entry (muse-style-element :entry-template)))
|
|
(muse-insert-file-or-string entry)
|
|
(goto-char (point-min))
|
|
(while (search-forward "%date%" nil t)
|
|
(replace-match (or date "") nil t))
|
|
(goto-char (point-min))
|
|
(while (search-forward "%title%" nil t)
|
|
(replace-match "")
|
|
(save-restriction
|
|
(narrow-to-region (point) (point))
|
|
(insert (or title "Untitled"))
|
|
(remove-text-properties (match-beginning 0) (match-end 0)
|
|
'(read-only nil rear-nonsticky nil))
|
|
(let ((muse-publishing-current-style (muse-style "html")))
|
|
(muse-publish-escape-specials (point-min) (point-max)
|
|
nil 'document))))
|
|
(goto-char (point-min))
|
|
(while (search-forward "%desc%" nil t)
|
|
(replace-match desc nil t))
|
|
(goto-char (point-min))
|
|
(while (search-forward "%enclosure%" nil t)
|
|
(replace-match
|
|
(if (null enclosure)
|
|
""
|
|
(save-match-data
|
|
(format
|
|
"<enclosure url=\"%s\" %stype=\"%s\"/>"
|
|
(if (string-match "//" enclosure)
|
|
enclosure
|
|
(concat (muse-style-element :base-url)
|
|
enclosure))
|
|
(let ((file
|
|
(expand-file-name enclosure
|
|
(muse-style-element :path))))
|
|
(if (file-readable-p file)
|
|
(format "length=\"%d\" "
|
|
(nth 7 (file-attributes file)))
|
|
""))
|
|
(if (string-match "\\.\\([^.]+\\)$" enclosure)
|
|
(let* ((ext (match-string 1 enclosure))
|
|
(type
|
|
(assoc
|
|
ext muse-journal-rss-enclosure-types-alist)))
|
|
(if type
|
|
(cdr type)
|
|
"application/octet-stream"))))))
|
|
nil t))
|
|
(goto-char (point-min))
|
|
(while (search-forward "%link%" nil t)
|
|
(replace-match
|
|
(concat (muse-style-element :base-url)
|
|
(concat (muse-page-name)
|
|
muse-html-extension))
|
|
nil t))
|
|
(goto-char (point-min))
|
|
(while (search-forward "%anchor%" nil t)
|
|
(replace-match
|
|
(muse-journal-anchorize-title (or title orig-date))
|
|
nil t))
|
|
(goto-char (point-min))
|
|
(while (search-forward "%maintainer%" nil t)
|
|
(replace-match
|
|
(or (muse-style-element :maintainer)
|
|
(concat "webmaster@" (system-name)))
|
|
nil t)))))))
|
|
;; indicate that we are to continue the :before-end processing
|
|
nil)
|
|
|
|
|
|
;;; Register the Muse Journal Publishers
|
|
|
|
(muse-derive-style "journal-html" "html"
|
|
:before-end 'muse-journal-html-munge-buffer)
|
|
|
|
(muse-derive-style "journal-xhtml" "xhtml"
|
|
:before-end 'muse-journal-html-munge-buffer)
|
|
|
|
(muse-derive-style "journal-latex" "latex"
|
|
:tags 'muse-journal-markup-tags
|
|
:before-end 'muse-journal-latex-munge-buffer)
|
|
|
|
(muse-derive-style "journal-pdf" "pdf"
|
|
:tags 'muse-journal-markup-tags
|
|
:before-end 'muse-journal-latex-munge-buffer)
|
|
|
|
(muse-derive-style "journal-book-latex" "book-latex"
|
|
;;:nochapters
|
|
:tags 'muse-journal-markup-tags
|
|
:before-end 'muse-journal-latex-munge-buffer)
|
|
|
|
(muse-derive-style "journal-book-pdf" "book-pdf"
|
|
;;:nochapters
|
|
:tags 'muse-journal-markup-tags
|
|
:before-end 'muse-journal-latex-munge-buffer)
|
|
|
|
(muse-define-style "journal-rdf"
|
|
:suffix 'muse-journal-rdf-extension
|
|
:regexps 'muse-journal-rss-markup-regexps
|
|
:functions 'muse-journal-rss-markup-functions
|
|
:before 'muse-journal-rss-munge-buffer
|
|
:header 'muse-journal-rdf-header
|
|
:footer 'muse-journal-rdf-footer
|
|
:date-format 'muse-journal-rdf-date-format
|
|
:entry-template 'muse-journal-rdf-entry-template
|
|
:base-url 'muse-journal-rdf-base-url
|
|
:summarize 'muse-journal-rdf-summarize-entries)
|
|
|
|
(muse-define-style "journal-rss"
|
|
:suffix 'muse-journal-rss-extension
|
|
:regexps 'muse-journal-rss-markup-regexps
|
|
:functions 'muse-journal-rss-markup-functions
|
|
:before 'muse-journal-rss-munge-buffer
|
|
:header 'muse-journal-rss-header
|
|
:footer 'muse-journal-rss-footer
|
|
:date-format 'muse-journal-rss-date-format
|
|
:entry-template 'muse-journal-rss-entry-template
|
|
:base-url 'muse-journal-rss-base-url
|
|
:summarize 'muse-journal-rss-summarize-entries)
|
|
|
|
;; Used by `muse-journal-rss-munge-buffer' to mark up individual entries
|
|
(muse-derive-style "journal-rss-entry" "html"
|
|
:tags 'muse-journal-markup-tags)
|
|
|
|
(provide 'muse-journal)
|
|
|
|
;;; muse-journal.el ends here
|