1;;; muse-journal.el --- keep and publish a journal 2 3;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 4;; Free Software Foundation, Inc. 5 6;; This file is part of Emacs Muse. It is not part of GNU Emacs. 7 8;; Emacs Muse is free software; you can redistribute it and/or modify 9;; it under the terms of the GNU General Public License as published 10;; by the Free Software Foundation; either version 3, or (at your 11;; option) any later version. 12 13;; Emacs Muse is distributed in the hope that it will be useful, but 14;; WITHOUT ANY WARRANTY; without even the implied warranty of 15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16;; General Public License for more details. 17 18;; You should have received a copy of the GNU General Public License 19;; along with Emacs Muse; see the file COPYING. If not, write to the 20;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21;; Boston, MA 02110-1301, USA. 22 23;;; Commentary: 24 25;; The module facilitates the keeping and publication of a journal. 26;; When publishing to HTML, it assumes the form of a web log, or blog. 27;; 28;; The input format for each entry is as follows: 29;; 30;; * 20040317: Title of entry 31;; 32;; Text for the entry. 33;; 34;; <qotd> 35;; "You know who you are. It comes down to a simple gut check: You 36;; either love what you do or you don't. Period." -- P. Bronson 37;; </qotd> 38;; 39;; The "qotd", or Quote of the Day, is entirely optional. When 40;; generated to HTML, this entry is rendered as: 41;; 42;; <div class="entry"> 43;; <div class="entry-qotd"> 44;; <h3>Quote of the Day:</h3> 45;; <p>"You know who you are. It comes down to a simple gut 46;; check: You either love what you do or you don't. Period." 47;; -- P. Bronson</p> 48;; </div> 49;; <div class="entry-body"> 50;; <div class="entry-head"> 51;; <div class="entry-date"> 52;; <span class="date">March 17, 2004</span> 53;; </div> 54;; <div class="entry-title"> 55;; <h2>Title of entry</h2> 56;; </div> 57;; </div> 58;; <div class="entry-text"> 59;; <p>Text for the entry.</p> 60;; </div> 61;; </div> 62;; </div> 63;; 64;; The plurality of "div" tags makes it possible to display the 65;; entries in any form you wish, using a CSS style. 66;; 67;; Also, an .RDF file can be generated from your journal by publishing 68;; it with the "rdf" style. It uses the first two sentences of the 69;; first paragraph of each entry as its "description", and 70;; autogenerates tags for linking to the various entries. 71 72;;; Contributors: 73 74;; René Stadler (mail AT renestadler DOT de) provided a patch that 75;; causes dates in RSS feeds to be generated in a format that RSS 76;; readers can parse. 77 78;;; Code: 79 80;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 81;; 82;; Muse Journal Publishing 83;; 84;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 85 86(require 'muse-publish) 87(require 'muse-html) 88(require 'muse-latex) 89(require 'muse-book) 90 91(defgroup muse-journal nil 92 "Rules for transforming a journal into its final form." 93 :group 'muse-publish) 94 95(defcustom muse-journal-heading-regexp 96 "\\(?:\\([0-9]+\\)\\(?:: \\)?\\)?\\(.+?\\)?" 97 "A regexp that matches a journal heading. 98Paren group 1 is the ISO date, group 2 is the optional category, 99and group 3 is the optional heading for the entry." 100 :type 'regexp 101 :group 'muse-journal) 102 103(defcustom muse-journal-date-format "%a, %e %b %Y" 104 "Date format to use for journal entries." 105 :type 'string 106 :group 'muse-journal) 107 108(defcustom muse-journal-html-heading-regexp 109 (concat "^<h2[^>\n]*>" muse-journal-heading-regexp "</h2>$") 110 "A regexp that matches a journal heading from an HTML document. 111Paren group 1 is the ISO date, group 2 is the optional category, 112and group 3 is the optional heading for the entry." 113 :type 'regexp 114 :group 'muse-journal) 115 116(defcustom muse-journal-rss-heading-regexp 117 (concat "^\\* " muse-journal-heading-regexp "$") 118 "A regexp that matches a journal heading from an HTML document. 119Paren group 1 is the ISO date, group 2 is the optional category, 120and group 3 is the optional heading for the entry." 121 :type 'regexp 122 :group 'muse-journal) 123 124(defcustom muse-journal-html-entry-template 125 "<div class=\"entry\"> 126 <a name=\"%anchor%\" style=\"text-decoration: none\"> </a> 127 <div class=\"entry-body\"> 128 <div class=\"entry-head\"> 129 <div class=\"entry-date\"> 130 <span class=\"date\">%date%</span> 131 </div> 132 <div class=\"entry-title\"> 133 <h2>%title%</h2> 134 </div> 135 </div> 136 <div class=\"entry-text\"> 137 <div class=\"entry-qotd\"> 138 <p>%qotd%</p> 139 </div> 140%text% 141 </div> 142 </div> 143</div>\n\n" 144 "Template used to publish individual journal entries as HTML. 145This may be text or a filename." 146 :type 'string 147 :group 'muse-journal) 148 149(defcustom muse-journal-latex-section 150 "\\section*{%title% \\hfill {\\normalsize %date%}} 151\\addcontentsline{toc}{chapter}{%title%}" 152 "Template used to publish a LaTeX section." 153 :type 'string 154 :group 'muse-journal) 155 156(defcustom muse-journal-latex-subsection 157 "\\subsection*{%title%} 158\\addcontentsline{toc}{section}{%title%}" 159 "Template used to publish a LaTeX subsection." 160 :type 'string 161 :group 'muse-journal) 162 163(defcustom muse-journal-markup-tags 164 '(("qotd" t nil nil muse-journal-qotd-tag)) 165 "A list of tag specifications, for specially marking up Journal entries. 166See `muse-publish-markup-tags' for more info. 167 168This is used by journal-latex and its related styles, as well as 169the journal-rss-entry style, which both journal-rdf and 170journal-rss use." 171 :type '(repeat (list (string :tag "Markup tag") 172 (boolean :tag "Expect closing tag" :value t) 173 (boolean :tag "Parse attributes" :value nil) 174 (boolean :tag "Nestable" :value nil) 175 function)) 176 :group 'muse-journal) 177 178;; FIXME: This doesn't appear to be used. 179(defun muse-journal-generate-pages () 180 (let ((output-dir (muse-style-element :path))) 181 (goto-char (point-min)) 182 (while (re-search-forward muse-journal-heading-regexp nil t) 183 (let* ((date (match-string 1)) 184 (category (match-string 1)) 185 (category-file (concat output-dir category "/index.html")) 186 (heading (match-string 1))) 187 t)))) 188 189(defcustom muse-journal-rdf-extension ".rdf" 190 "Default file extension for publishing RDF (RSS 1.0) files." 191 :type 'string 192 :group 'muse-journal) 193 194(defcustom muse-journal-rdf-base-url "" 195 "The base URL of the website referenced by the RDF file." 196 :type 'string 197 :group 'muse-journal) 198 199(defcustom muse-journal-rdf-header 200 "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" 201 xmlns=\"http://purl.org/rss/1.0/\" 202 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"> 203 <channel rdf:about=\"<lisp>(concat (muse-style-element :base-url) 204 (muse-publish-link-name))</lisp>\"> 205 <title><lisp>(muse-publishing-directive \"title\")</lisp></title> 206 <link><lisp>(concat (muse-style-element :base-url) 207 (concat (muse-page-name) 208 muse-html-extension))</lisp></link> 209 <description><lisp>(muse-publishing-directive \"desc\")</lisp></description> 210 <items> 211 <rdf:Seq> 212 <rdf:li resource=\"<lisp> 213 (concat (muse-style-element :base-url) 214 (concat (muse-page-name) 215 muse-html-extension))</lisp>\"/> 216 </rdf:Seq> 217 </items> 218 </channel>\n" 219 "Header used for publishing RDF (RSS 1.0) files. 220This may be text or a filename." 221 :type 'string 222 :group 'muse-journal) 223 224(defcustom muse-journal-rdf-footer 225 "</rdf:RDF>\n" 226 "Footer used for publishing RDF (RSS 1.0) files. 227This may be text or a filename." 228 :type 'string 229 :group 'muse-journal) 230 231(defcustom muse-journal-rdf-date-format 232 "%Y-%m-%dT%H:%M:%S" 233 "Date format to use for RDF entries." 234 :type 'string 235 :group 'muse-journal) 236 237(defcustom muse-journal-rdf-entry-template 238 "\n <item rdf:about=\"%link%#%anchor%\"> 239 <title>%title%</title> 240 <description> 241 %desc% 242 </description> 243 <link>%link%#%anchor%</link> 244 <dc:date>%date%</dc:date> 245 <dc:creator>%maintainer%</dc:creator> 246 </item>\n" 247 "Template used to publish individual journal entries as RDF. 248This may be text or a filename." 249 :type 'string 250 :group 'muse-journal) 251 252(defcustom muse-journal-rdf-summarize-entries nil 253 "If non-nil, include only summaries in the RDF file, not the full data. 254 255The default is nil, because this annoys some subscribers." 256 :type 'boolean 257 :group 'muse-journal) 258 259(defcustom muse-journal-rss-extension ".xml" 260 "Default file extension for publishing RSS 2.0 files." 261 :type 'string 262 :group 'muse-journal) 263 264(defcustom muse-journal-rss-base-url "" 265 "The base URL of the website referenced by the RSS file." 266 :type 'string 267 :group 'muse-journal) 268 269(defcustom muse-journal-rss-header 270 "<\?xml version=\"1.0\" encoding=\"<lisp> 271 (muse-html-encoding)</lisp>\"?> 272<rss version=\"2.0\"> 273 <channel> 274 <title><lisp>(muse-publishing-directive \"title\")</lisp></title> 275 <link><lisp>(concat (muse-style-element :base-url) 276 (concat (muse-page-name) 277 muse-html-extension))</lisp></link> 278 <description><lisp>(muse-publishing-directive \"desc\")</lisp></description> 279 <language>en-us</language> 280 <generator>Emacs Muse</generator>\n\n" 281 "Header used for publishing RSS 2.0 files. This may be text or a filename." 282 :type 'string 283 :group 'muse-journal) 284 285(defcustom muse-journal-rss-footer 286 "\n\n </channel> 287</rss>\n" 288 "Footer used for publishing RSS 2.0 files. This may be text or a filename." 289 :type 'string 290 :group 'muse-journal) 291 292(defcustom muse-journal-rss-date-format 293 "%a, %d %b %Y %H:%M:%S %Z" 294 "Date format to use for RSS 2.0 entries." 295 :type 'string 296 :group 'muse-journal) 297 298(defcustom muse-journal-rss-entry-template 299 "\n <item> 300 <title>%title%</title> 301 <link>%link%#%anchor%</link> 302 <description>%desc%</description> 303 <author><lisp>(muse-publishing-directive \"author\")</lisp></author> 304 <pubDate>%date%</pubDate> 305 <guid>%link%#%anchor%</guid> 306 %enclosure% 307 </item>\n" 308 "Template used to publish individual journal entries as RSS 2.0. 309This may be text or a filename." 310 :type 'string 311 :group 'muse-journal) 312 313(defcustom muse-journal-rss-enclosure-types-alist 314 '(("mp3" . "audio/mpeg")) 315 "File types that are accepted as RSS enclosures. 316This is an alist that maps file extension to content type. 317Useful for podcasting." 318 :type '(alist :key-type string :value-type string) 319 :group 'muse-journal) 320 321(defcustom muse-journal-rss-summarize-entries nil 322 "If non-nil, include only summaries in the RSS file, not the full data. 323 324The default is nil, because this annoys some subscribers." 325 :type 'boolean 326 :group 'muse-journal) 327 328(defcustom muse-journal-rss-markup-regexps 329 '((10000 muse-explicit-link-regexp 0 "\\2")) 330 "List of markup rules for publishing a Muse journal page to RSS 2.0. 331For more information on the structure of this list, see 332`muse-publish-markup-regexps'." 333 :type '(repeat (choice 334 (list :tag "Markup rule" 335 integer 336 (choice regexp symbol) 337 integer 338 (choice string function symbol)) 339 function)) 340 :group 'muse-journal) 341 342(defcustom muse-journal-rss-markup-functions 343 '((email . ignore) 344 (link . ignore) 345 (url . ignore)) 346 "An alist of style types to custom functions for that kind of text. 347For more on the structure of this list, see 348`muse-publish-markup-functions'." 349 :type '(alist :key-type symbol :value-type function) 350 :group 'muse-journal) 351 352(defun muse-journal-anchorize-title (title) 353 "This strips tags from TITLE, truncates TITLE at begin parenthesis, 354and escapes any remaining non-alphanumeric characters." 355 (save-match-data 356 (if (string-match "(" title) 357 (setq title (substring title 0 (match-beginning 0)))) 358 (if (string-match "<[^>]+>" title) 359 (setq title (replace-match "" nil nil title))) 360 (let (pos code len ch) 361 (while (setq pos (string-match (concat "[^" muse-regexp-alnum "_]") 362 title pos)) 363 (setq ch (aref title pos) 364 code (format "%%%02X" (cond ((fboundp 'char-to-ucs) 365 (char-to-ucs ch)) 366 ((fboundp 'char-to-int) 367 (char-to-int ch)) 368 (t ch))) 369 len (length code) 370 title (concat (substring title 0 pos) 371 code 372 (when (< pos (length title)) 373 (substring title (1+ pos) nil))) 374 pos (+ len pos))) 375 title))) 376 377(defun muse-journal-sort-entries (&optional direction) 378 (interactive "P") 379 (sort-subr 380 direction 381 (function 382 (lambda () 383 (if (re-search-forward "^\\* [0-9]+" nil t) 384 (goto-char (match-beginning 0)) 385 (goto-char (point-max))))) 386 (function 387 (lambda () 388 (if (re-search-forward "^\\* [0-9]+" nil t) 389 (goto-char (1- (match-beginning 0))) 390 (goto-char (point-max))))) 391 (function 392 (lambda () 393 (forward-char 2))) 394 (function 395 (lambda () 396 (end-of-line))))) 397 398(defun muse-journal-qotd-tag (beg end) 399 (muse-publish-ensure-block beg end) 400 (muse-insert-markup (muse-markup-text 'begin-quote)) 401 (muse-insert-markup (muse-markup-text 'begin-quote-item)) 402 (goto-char end) 403 (muse-insert-markup (muse-markup-text 'end-quote-item)) 404 (muse-insert-markup (muse-markup-text 'end-quote))) 405 406(defun muse-journal-html-munge-buffer () 407 (goto-char (point-min)) 408 (let ((heading-regexp muse-journal-html-heading-regexp) 409 (inhibit-read-only t)) 410 (while (re-search-forward heading-regexp nil t) 411 (let* ((date (match-string 1)) 412 (orig-date date) 413 (title (match-string 2)) 414 (clean-title title) 415 datestamp qotd text) 416 (delete-region (match-beginning 0) (match-end 0)) 417 (if clean-title 418 (save-match-data 419 (while (string-match "\\(^<[^>]+>\\|<[^>]+>$\\)" clean-title) 420 (setq clean-title (replace-match "" nil nil clean-title))))) 421 (save-match-data 422 (when (and date 423 (string-match 424 (concat "\\`\\([1-9][0-9][0-9][0-9]\\)[./]?" 425 "\\([0-1][0-9]\\)[./]?\\([0-3][0-9]\\)") date)) 426 (setq datestamp 427 (encode-time 428 0 0 0 429 (string-to-number (match-string 3 date)) 430 (string-to-number (match-string 2 date)) 431 (string-to-number (match-string 1 date)) 432 nil) 433 date (concat (format-time-string 434 muse-journal-date-format datestamp) 435 (substring date (match-end 0)))))) 436 (save-restriction 437 (narrow-to-region 438 (point) (if (re-search-forward 439 (concat "\\(^<hr>$\\|" 440 heading-regexp "\\)") nil t) 441 (match-beginning 0) 442 (point-max))) 443 (goto-char (point-max)) 444 (while (and (not (bobp)) 445 (eq ?\ (char-syntax (char-before)))) 446 (delete-char -1)) 447 (goto-char (point-min)) 448 (while (and (not (eobp)) 449 (eq ?\ (char-syntax (char-after)))) 450 (delete-char 1)) 451 (save-excursion 452 (when (search-forward "<qotd>" nil t) 453 (let ((tag-beg (match-beginning 0)) 454 (beg (match-end 0)) 455 end) 456 (re-search-forward "</qotd>\n*") 457 (setq end (point-marker)) 458 (save-restriction 459 (narrow-to-region beg (match-beginning 0)) 460 (muse-publish-escape-specials (point-min) (point-max) 461 nil 'document) 462 (setq qotd (buffer-substring-no-properties 463 (point-min) (point-max)))) 464 (delete-region tag-beg end) 465 (set-marker end nil)))) 466 (setq text (buffer-string)) 467 (delete-region (point-min) (point-max)) 468 (let ((entry muse-journal-html-entry-template)) 469 (muse-insert-file-or-string entry) 470 (muse-publish-mark-read-only (point-min) (point-max)) 471 (goto-char (point-min)) 472 (while (search-forward "%date%" nil t) 473 (remove-text-properties (match-beginning 0) (match-end 0) 474 '(read-only nil rear-nonsticky nil)) 475 (replace-match (or date "") nil t)) 476 (goto-char (point-min)) 477 (while (search-forward "%title%" nil t) 478 (remove-text-properties (match-beginning 0) (match-end 0) 479 '(read-only nil rear-nonsticky nil)) 480 (replace-match (or title " ") nil t)) 481 (goto-char (point-min)) 482 (while (search-forward "%anchor%" nil t) 483 (replace-match (muse-journal-anchorize-title 484 (or clean-title orig-date)) 485 nil t)) 486 (goto-char (point-min)) 487 (while (search-forward "%qotd%" nil t) 488 (save-restriction 489 (narrow-to-region (match-beginning 0) (match-end 0)) 490 (delete-region (point-min) (point-max)) 491 (when qotd (muse-insert-markup qotd)))) 492 (goto-char (point-min)) 493 (while (search-forward "%text%" nil t) 494 (remove-text-properties (match-beginning 0) (match-end 0) 495 '(read-only nil rear-nonsticky nil)) 496 (replace-match text nil t)) 497 (when (null qotd) 498 (goto-char (point-min)) 499 (when (search-forward "<div class=\"entry-qotd\">" nil t) 500 (let ((beg (match-beginning 0))) 501 (re-search-forward "</div>\n*" nil t) 502 (delete-region beg (point)))))))))) 503 ;; indicate that we are to continue the :before-end processing 504 nil) 505 506(defun muse-journal-latex-munge-buffer () 507 (goto-char (point-min)) 508 (let ((heading-regexp 509 (concat "^" (regexp-quote (muse-markup-text 'section)) 510 muse-journal-heading-regexp 511 (regexp-quote (muse-markup-text 'section-end)) "$")) 512 (inhibit-read-only t)) 513 (when (re-search-forward heading-regexp nil t) 514 (goto-char (match-beginning 0)) 515 (sort-subr nil 516 (function 517 (lambda () 518 (if (re-search-forward heading-regexp nil t) 519 (goto-char (match-beginning 0)) 520 (goto-char (point-max))))) 521 (function 522 (lambda () 523 (if (re-search-forward heading-regexp nil t) 524 (goto-char (1- (match-beginning 0))) 525 (goto-char (point-max))))) 526 (function 527 (lambda () 528 (forward-char 2))) 529 (function 530 (lambda () 531 (end-of-line))))) 532 (while (re-search-forward heading-regexp nil t) 533 (let ((date (match-string 1)) 534 (title (match-string 2)) 535 ;; FIXME: Nothing is done with qotd 536 qotd section) 537 (save-match-data 538 (when (and date 539 (string-match 540 (concat "\\([1-9][0-9][0-9][0-9]\\)[./]?" 541 "\\([0-1][0-9]\\)[./]?\\([0-3][0-9]\\)") date)) 542 (setq date (encode-time 543 0 0 0 544 (string-to-number (match-string 3 date)) 545 (string-to-number (match-string 2 date)) 546 (string-to-number (match-string 1 date)) 547 nil) 548 date (format-time-string 549 muse-journal-date-format date)))) 550 (save-restriction 551 (narrow-to-region (match-beginning 0) (match-end 0)) 552 (delete-region (point-min) (point-max)) 553 (muse-insert-markup muse-journal-latex-section) 554 (goto-char (point-min)) 555 (while (search-forward "%title%" nil t) 556 (replace-match (or title "Untitled") nil t)) 557 (goto-char (point-min)) 558 (while (search-forward "%date%" nil t) 559 (replace-match (or date "") nil t)))))) 560 (goto-char (point-min)) 561 (let ((subheading-regexp 562 (concat "^" (regexp-quote (muse-markup-text 'subsection)) 563 "\\([^\n}]+\\)" 564 (regexp-quote (muse-markup-text 'subsection-end)) "$")) 565 (inhibit-read-only t)) 566 (while (re-search-forward subheading-regexp nil t) 567 (let ((title (match-string 1))) 568 (save-restriction 569 (narrow-to-region (match-beginning 0) (match-end 0)) 570 (delete-region (point-min) (point-max)) 571 (muse-insert-markup muse-journal-latex-subsection) 572 (goto-char (point-min)) 573 (while (search-forward "%title%" nil t) 574 (replace-match title nil t)))))) 575 ;; indicate that we are to continue the :before-end processing 576 nil) 577 578(defun muse-journal-rss-munge-buffer () 579 (goto-char (point-min)) 580 (let ((heading-regexp muse-journal-rss-heading-regexp) 581 (inhibit-read-only t)) 582 (while (re-search-forward heading-regexp nil t) 583 (let* ((date (match-string 1)) 584 (orig-date date) 585 (title (match-string 2)) 586 ;; FIXME: Nothing is done with qotd 587 enclosure qotd desc) 588 (if title 589 (save-match-data 590 (if (string-match muse-explicit-link-regexp title) 591 (setq enclosure (muse-get-link title) 592 title (muse-get-link-desc title))))) 593 (save-match-data 594 (when (and date 595 (string-match 596 (concat "\\([1-9][0-9][0-9][0-9]\\)[./]?" 597 "\\([0-1][0-9]\\)[./]?\\([0-3][0-9]\\)") date)) 598 (setq date (encode-time 0 0 0 599 (string-to-number (match-string 3 date)) 600 (string-to-number (match-string 2 date)) 601 (string-to-number (match-string 1 date)) 602 nil) 603 ;; make sure that date is in a format that RSS 604 ;; readers can handle 605 date (let ((system-time-locale "C")) 606 (format-time-string 607 (muse-style-element :date-format) date))))) 608 (save-restriction 609 (narrow-to-region 610 (match-beginning 0) 611 (if (re-search-forward heading-regexp nil t) 612 (match-beginning 0) 613 (if (re-search-forward "^Footnotes:" nil t) 614 (match-beginning 0) 615 (point-max)))) 616 (goto-char (point-min)) 617 (delete-region (point) (muse-line-end-position)) 618 (re-search-forward "</qotd>\n+" nil t) 619 (while (and (char-after) 620 (eq ?\ (char-syntax (char-after)))) 621 (delete-char 1)) 622 (let ((beg (point))) 623 (if (muse-style-element :summarize) 624 (progn 625 (forward-sentence 2) 626 (setq desc (concat (buffer-substring beg (point)) "..."))) 627 (save-restriction 628 (muse-publish-markup-buffer "rss-entry" "journal-rss-entry") 629 (goto-char (point-min)) 630 (if (re-search-forward "Page published by Emacs Muse" nil t) 631 (goto-char (muse-line-end-position)) 632 (muse-display-warning 633 (concat 634 "Cannot find 'Page published by Emacs Muse begins here'.\n" 635 "You will probably need this text in your header.")) 636 (goto-char (point-min))) 637 (setq beg (point)) 638 (if (re-search-forward "Page published by Emacs Muse" nil t) 639 (goto-char (muse-line-beginning-position)) 640 (muse-display-warning 641 (concat 642 "Cannot find 'Page published by Emacs Muse ends here'.\n" 643 "You will probably need this text in your footer.")) 644 (goto-char (point-max))) 645 (setq desc (buffer-substring beg (point)))))) 646 (unless (string= desc "") 647 (setq desc (concat "<![CDATA[" desc "]]>"))) 648 (delete-region (point-min) (point-max)) 649 (let ((entry (muse-style-element :entry-template))) 650 (muse-insert-file-or-string entry) 651 (goto-char (point-min)) 652 (while (search-forward "%date%" nil t) 653 (replace-match (or date "") nil t)) 654 (goto-char (point-min)) 655 (while (search-forward "%title%" nil t) 656 (replace-match "") 657 (save-restriction 658 (narrow-to-region (point) (point)) 659 (insert (or title "Untitled")) 660 (remove-text-properties (match-beginning 0) (match-end 0) 661 '(read-only nil rear-nonsticky nil)) 662 (let ((muse-publishing-current-style (muse-style "html"))) 663 (muse-publish-escape-specials (point-min) (point-max) 664 nil 'document)))) 665 (goto-char (point-min)) 666 (while (search-forward "%desc%" nil t) 667 (replace-match desc nil t)) 668 (goto-char (point-min)) 669 (while (search-forward "%enclosure%" nil t) 670 (replace-match 671 (if (null enclosure) 672 "" 673 (save-match-data 674 (format 675 "<enclosure url=\"%s\" %stype=\"%s\"/>" 676 (if (string-match "//" enclosure) 677 enclosure 678 (concat (muse-style-element :base-url) 679 enclosure)) 680 (let ((file 681 (expand-file-name enclosure 682 (muse-style-element :path)))) 683 (if (file-readable-p file) 684 (format "length=\"%d\" " 685 (nth 7 (file-attributes file))) 686 "")) 687 (if (string-match "\\.\\([^.]+\\)$" enclosure) 688 (let* ((ext (match-string 1 enclosure)) 689 (type 690 (assoc 691 ext muse-journal-rss-enclosure-types-alist))) 692 (if type 693 (cdr type) 694 "application/octet-stream")))))) 695 nil t)) 696 (goto-char (point-min)) 697 (while (search-forward "%link%" nil t) 698 (replace-match 699 (concat (muse-style-element :base-url) 700 (concat (muse-page-name) 701 muse-html-extension)) 702 nil t)) 703 (goto-char (point-min)) 704 (while (search-forward "%anchor%" nil t) 705 (replace-match 706 (muse-journal-anchorize-title (or title orig-date)) 707 nil t)) 708 (goto-char (point-min)) 709 (while (search-forward "%maintainer%" nil t) 710 (replace-match 711 (or (muse-style-element :maintainer) 712 (concat "webmaster@" (system-name))) 713 nil t))))))) 714 ;; indicate that we are to continue the :before-end processing 715 nil) 716 717 718;;; Register the Muse Journal Publishers 719 720(muse-derive-style "journal-html" "html" 721 :before-end 'muse-journal-html-munge-buffer) 722 723(muse-derive-style "journal-xhtml" "xhtml" 724 :before-end 'muse-journal-html-munge-buffer) 725 726(muse-derive-style "journal-latex" "latex" 727 :tags 'muse-journal-markup-tags 728 :before-end 'muse-journal-latex-munge-buffer) 729 730(muse-derive-style "journal-pdf" "pdf" 731 :tags 'muse-journal-markup-tags 732 :before-end 'muse-journal-latex-munge-buffer) 733 734(muse-derive-style "journal-book-latex" "book-latex" 735 ;;:nochapters 736 :tags 'muse-journal-markup-tags 737 :before-end 'muse-journal-latex-munge-buffer) 738 739(muse-derive-style "journal-book-pdf" "book-pdf" 740 ;;:nochapters 741 :tags 'muse-journal-markup-tags 742 :before-end 'muse-journal-latex-munge-buffer) 743 744(muse-define-style "journal-rdf" 745 :suffix 'muse-journal-rdf-extension 746 :regexps 'muse-journal-rss-markup-regexps 747 :functions 'muse-journal-rss-markup-functions 748 :before 'muse-journal-rss-munge-buffer 749 :header 'muse-journal-rdf-header 750 :footer 'muse-journal-rdf-footer 751 :date-format 'muse-journal-rdf-date-format 752 :entry-template 'muse-journal-rdf-entry-template 753 :base-url 'muse-journal-rdf-base-url 754 :summarize 'muse-journal-rdf-summarize-entries) 755 756(muse-define-style "journal-rss" 757 :suffix 'muse-journal-rss-extension 758 :regexps 'muse-journal-rss-markup-regexps 759 :functions 'muse-journal-rss-markup-functions 760 :before 'muse-journal-rss-munge-buffer 761 :header 'muse-journal-rss-header 762 :footer 'muse-journal-rss-footer 763 :date-format 'muse-journal-rss-date-format 764 :entry-template 'muse-journal-rss-entry-template 765 :base-url 'muse-journal-rss-base-url 766 :summarize 'muse-journal-rss-summarize-entries) 767 768;; Used by `muse-journal-rss-munge-buffer' to mark up individual entries 769(muse-derive-style "journal-rss-entry" "html" 770 :tags 'muse-journal-markup-tags) 771 772(provide 'muse-journal) 773 774;;; muse-journal.el ends here 775