Update ChangeLog.
[planner-el.git] / planner-publish.el
blob58f2cae58420868f53138d888479d9454ec797d2
1 ;;; planner-publish.el --- planner-specific publishing
3 ;; Copyright (C) 2005, 2006 Peter K. Lee
4 ;; Parts copyright (C) 2005 Chris McMahan
5 ;; Parts copyright (C) 2005, 2006 Free Software Foundation, Inc.
6 ;; Parts copyright (C) 2005 Dale P. Smith
8 ;; Author: Peter K. Lee <saint@ c o r e n o v a .com>
9 ;; Keywords: planner publish
10 ;; Timestamp: 20 Jul 2005 10:05:29
11 ;; X-URL: http://www.corenova.com/...
13 ;; This file is part of Planner. It is not part of GNU Emacs.
15 ;; Planner is free software; you can redistribute it and/or modify it
16 ;; under the terms of the GNU General Public License as published by
17 ;; the Free Software Foundation; either version 2, or (at your option)
18 ;; any later version.
20 ;; Planner is distributed in the hope that it will be useful, but
21 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 ;; General Public License for more details.
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with Planner; see the file COPYING. If not, write to the
27 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28 ;; Boston, MA 02110-1301, USA.
30 ;;; Commentary:
32 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
34 ;;; Introduction
36 ;; Muse Styles for Planner: planner-xml, planner-html, planner-xhtml, etc.
38 ;; Handles publishing of planner files. Works with Muse to generate
39 ;; flexible markup.
41 ;;; History:
43 ;; 2005-07-15 (0.1) : creation date
44 ;; 2005-07-20 (0.2) : first public release
45 ;; 2005-07-21 (0.3) : added planner-html-style-sheet customize option
46 ;; 2005-08-09 : added to Planner, see ChangeLog for further changes
48 ;;; TODO:
50 ;; add support for various PLANNER specific sections such as Diary,
51 ;; Accomplishments, Timeclock, etc.
53 ;;; Contributors:
55 ;; Chris McMahan (cmcmahan AT one.net) helped notes to publish correctly.
57 ;; Jim Ottaway fixed several bugs.
59 ;; David Smith fixed a few bugs.
61 ;; Dale Smith implemented a new version of the "notes" tag and
62 ;; provided several patches.
64 ;; Andrew J. Korty enabled multiple links with the "categories"
65 ;; attribute.
67 (require 'planner)
69 (require 'muse-mode)
70 (require 'muse-publish)
71 (require 'muse-html) ;;; allow derive style from "html" and "xhtml"
72 (require 'muse-xml) ;;; allow derive style from "xml"
74 (defgroup planner-publish nil
75 "Options controlling the behavior of PLANNER publishing.
76 See `planner-publish' for more information."
77 :group 'planner)
79 (defcustom planner-publish-markup-regexps
80 '((1275 "^#\\([A-C]\\)\\([0-9]*\\)\\s-*\\([_oXDCP]\\)\\s-*\\(.+\\)" 0 task)
81 (1280 "^\\.#[0-9]+\\s-*" 0 note)
82 (3200 planner-date-regexp 0 link))
83 "List of markup rules for publishing PLANNER.
84 For more on the structure of this list, see `muse-publish-markup-regexps'."
85 :type '(repeat (choice
86 (list :tag "Markup rule"
87 integer
88 (choice regexp symbol)
89 integer
90 (choice string function symbol))
91 function))
92 :group 'muse-html)
94 (defcustom planner-publish-markup-functions
95 '((task . planner-publish-markup-task)
96 (note . planner-publish-markup-note))
97 "An alist of style types to custom functions for that kind of text.
98 For more on the structure of this list, see
99 `muse-publish-markup-functions'."
100 :type '(alist :key-type symbol :value-type function)
101 :group 'planner-publish)
103 (defcustom planner-publish-markup-tags
104 '(("nested-section" t nil planner-publish-nested-section-tag)
105 ("title" t t planner-publish-title-tag)
106 ("content" t nil planner-publish-content-tag)
107 ("diary-section" t nil planner-publish-diary-section-tag)
108 ("tasks-section" t nil planner-publish-tasks-section-tag)
109 ("notes-section" t nil planner-publish-notes-section-tag)
110 ("notes" nil nil planner-publish-notes-tag)
111 ("past-notes" nil t planner-publish-past-notes-tag)
112 ("task" t t planner-publish-task-tag)
113 ("note" t t planner-publish-note-tag))
114 "A list of tag specifications, for specially marking up PLANNER.
115 See `muse-publish-markup-tags' for more information."
116 :type '(repeat (list (string :tag "Markup tag")
117 (boolean :tag "Expect closing tag" :value t)
118 (boolean :tag "Parse attributes" :value nil)
119 function))
120 :group 'planner-publish)
122 ;;;_ + XML specific customizations
124 (defcustom planner-xml-markup-strings
125 '((planner-begin-nested-section . "<section>")
126 (planner-end-nested-section . "</section>")
127 (planner-begin-title . "<title>")
128 (planner-end-title . "</title>")
129 (planner-begin-content . "")
130 (planner-end-content . "")
131 (planner-begin-body . "")
132 (planner-end-body . "")
133 (planner-begin-diary-section . "<diary>")
134 (planner-end-diary-section . "</diary>")
135 (planner-begin-task-section . "<tasks>")
136 (planner-end-task-section . "</tasks>")
137 (planner-begin-task-body . "")
138 (planner-end-task-body . "")
139 (planner-begin-note-section . "<notes>")
140 (planner-end-note-section . "</notes>")
141 (planner-begin-task . "<task status=\"%s\" priority=\"%s\">")
142 (planner-end-task . "</task>")
143 (planner-begin-note . "<note number=\"%s\">")
144 (planner-end-note . "</note>")
145 (planner-begin-note-details . "<details><timestamp>%s</timestamp>")
146 (planner-end-note-details . "</details>")
147 (planner-begin-note-link . "<references>")
148 (planner-end-note-link . "</references>")
149 (planner-begin-note-categories . "<categories>")
150 (planner-end-note-categories . "</categories>"))
151 "Strings used for marking up text as XML.
152 These cover the most basic kinds of markup, the handling of which
153 differs little between the various styles.
155 If a markup rule is not found here, `muse-xml-markup-strings' is
156 searched."
157 :type '(alist :key-type symbol :value-type string)
158 :group 'planner-publish)
160 (defcustom planner-xml-header
161 "<?xml version=\"1.0\" encoding=\"<lisp>(muse-xml-encoding)</lisp>\"?>
162 <PLANNER>
163 <pageinfo>
164 <title><lisp>(muse-publishing-directive \"title\")</lisp></title>
165 <author><lisp>(muse-publishing-directive \"author\")</lisp></author>
166 <maintainer><lisp>(muse-style-element :maintainer)</lisp></maintainer>
167 <pubdate><lisp>(muse-publishing-directive \"date\")</lisp></pubdate>
168 </pageinfo>
169 <!-- Page published by Emacs Muse begins here -->\n"
170 "Header used for publishing PLANNER XML files.
171 This may be text or a filename."
172 :type 'string
173 :group 'planner-publish)
175 (defcustom planner-xml-footer "
176 <!-- Page published by Emacs Muse ends here -->
177 </PLANNER>\n"
178 "Footer used for publishing PLANNER XML files.
179 This may be text or a filename."
180 :type 'string
181 :group 'planner-publish)
183 ;;;_ + HTML specific customizations
185 (defcustom planner-html-markup-strings
186 '((planner-begin-nested-section . "<div class=\"section\">")
187 (planner-end-nested-section . "</div>")
188 (planner-begin-title . "<h%s>")
189 (planner-end-title . "</h%s>")
190 (planner-begin-content . "<div class=\"content\">")
191 (planner-end-content . "</div>")
192 (planner-begin-body . "<div class=\"body\">")
193 (planner-end-body . "</div>")
194 (planner-begin-diary-section . "<div id=\"diary\" class=\"section\">")
195 (planner-end-diary-section . "</div>")
196 (planner-begin-task-section . "<div id=\"tasks\" class=\"section\">")
197 (planner-end-task-section . "</div>")
198 (planner-begin-task-body . "<ul class=\"body\">")
199 (planner-end-task-body . "</ul>")
200 (planner-begin-note-section . "<div id=\"notes\" class=\"section\">")
201 (planner-end-note-section . "</div>")
202 (planner-begin-task . "<li class=\"task\"><span class=\"%s\"><span class=\"%s\">%s</span>")
203 (planner-end-task . "</span></li>")
204 (planner-begin-note . "<div class=\"note\"><a name=\"%s\"></a><span class=\"anchor\">%s</span>")
205 (planner-end-note . "</div>")
206 (planner-begin-note-details . "<div class=\"details\"><span class=\"timestamp\">%s</span>")
207 (planner-end-note-details . "</div>")
208 (planner-begin-note-link . " <span class=\"link\">")
209 (planner-end-note-link . "</span>")
210 (planner-begin-note-categories . " <span class=\"categories\">")
211 (planner-end-note-categories . "</span>"))
212 "Strings used for marking up text as HTML.
213 These cover the most basic kinds of markup, the handling of which
214 differs little between the various styles.
216 If a markup rule is not found here, `muse-html-markup-strings' is
217 searched."
218 :type '(alist :key-type symbol :value-type string)
219 :group 'planner-publish)
221 (defcustom planner-html-style-sheet
222 "<style type=\"text/css\">
223 body {
224 background: white; color: black;
225 margin-left: 3%; margin-right: 3%;
228 p { margin-top: 3px; margin-bottom: 3px; }
229 p.verse { margin-left: 3% }
231 h1,h2,h3,h4,h5 { margin:0; padding:0; }
233 h1 { padding: 10px; margin-bottom: 10px; }
235 table.muse-table { margin: 0; font-size: 11px;
236 border-collapse: collapse;
237 background: #e2effa;
238 border: 1px solid #aadeed; }
240 table.muse-table tbody td { border: 1px solid #ccdeed; }
242 .example { margin-left: 5px; padding: 3px;
243 background: #fffffc;
244 border: 1px solid #ccdeed; }
246 /* nested sections */
247 .section { margin: 0; padding: 10px;
248 margin-bottom: 15px;
249 font-size: 12px; }
251 .section .section { margin: 0; margin-left: 5px;
252 font-size: 11px; }
254 .title { margin: 0; padding; 0 }
256 /* optional calendar section */
257 .calendar { float: right; }
258 table.month-calendar { font-size: 9px; }
260 /* Diary section */
261 #diary p { margin-top: 1em; }
263 /* Tasks section */
264 .task .A { color: red }
265 .task .B { color: green }
266 .task .C { color: navy }
267 .task .done { color: gray; text-decoration: line-through; }
268 .task .cancelled { color: gray; text-decoration: italic; }
270 /* Notes section */
271 .note { margin-top: 1.5em; }
272 .note .anchor { float: left; margin-right: 5px; }
273 .note .details { margin-top: .5em; }
275 </style>"
276 "Store your stylesheet definitions here. The provided default
277 is for reference only. You definitely want to customize this for
278 your particular needs & wants. This is used in
279 `planner-html-header' and `planner-xhtml-header'. Refer to
280 `muse-html-style-sheet' for details on usage. You may simply
281 override the above by specifying an explicit link to a CSS file."
282 :type 'string
283 :group 'planner-publish)
285 (defcustom planner-html-header
286 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">
287 <html>
288 <head>
289 <title><lisp>
290 (concat (muse-publishing-directive \"title\")
291 (let ((author (muse-publishing-directive \"author\")))
292 (if (not (string= author (user-full-name)))
293 (concat \" (by \" author \")\"))))</lisp></title>
294 <meta name=\"generator\" content=\"muse.el\">
295 <meta http-equiv=\"<lisp>muse-html-meta-http-equiv</lisp>\"
296 content=\"<lisp>muse-html-meta-content-type</lisp>\">
297 <lisp>
298 (let ((maintainer (muse-style-element :maintainer)))
299 (when maintainer
300 (concat \"<link rev=\\\"made\\\" href=\\\"\" maintainer \"\\\">\")))
301 </lisp>
302 <lisp>planner-html-style-sheet</lisp>
303 </head>
304 <body>
305 <div id=\"content\">
306 <h1><span><lisp>
307 (concat (muse-publishing-directive \"title\")
308 (let ((author (muse-publishing-directive \"author\")))
309 (if (not (string= author (user-full-name)))
310 (concat \" (by \" author \")\"))))</lisp></span></h1>
311 <div id=\"inner-header\">
312 <lisp>planner-html-inner-header</lisp>
313 </div>
314 <div id=\"muse-sections\">
315 <!-- Page published by Emacs Muse begins here -->\n"
316 "Header used for publishing PLANNER HTML files.
317 This may be text or a filename."
318 :type 'string
319 :group 'planner-publish)
321 (defcustom planner-html-footer "
322 <!-- Page published by Emacs Muse ends here -->
323 </div>
324 <div id=\"inner-footer\">
325 <lisp>planner-html-inner-footer</lisp>
326 </div>
327 </div>
328 </body>
329 </html>\n"
330 "Footer used for publishing PLANNER HTML files.
331 This may be text or a filename."
332 :type 'string
333 :group 'planner-publish)
335 (defcustom planner-xhtml-header
336 "<?xml version=\"1.0\" encoding=\"<lisp>
337 (muse-html-encoding)</lisp>\"?>
338 <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"
339 \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">
340 <html xmlns=\"http://www.w3.org/1999/xhtml\">
341 <head>
342 <title><lisp>
343 (concat (muse-publishing-directive \"title\")
344 (let ((author (muse-publishing-directive \"author\")))
345 (if (not (string= author (user-full-name)))
346 (concat \" (by \" author \")\"))))</lisp></title>
347 <meta name=\"generator\" content=\"muse.el\" />
348 <meta http-equiv=\"<lisp>muse-html-meta-http-equiv</lisp>\"
349 content=\"<lisp>muse-html-meta-content-type</lisp>\" />
350 <lisp>
351 (let ((maintainer (muse-style-element :maintainer)))
352 (when maintainer
353 (concat \"<link rev=\\\"made\\\" href=\\\"\" maintainer \"\\\" />\")))
354 </lisp>
355 <lisp>planner-html-style-sheet</lisp>
356 </head>
357 <body>
358 <div id=\"content\">
359 <h1><span><lisp>
360 (concat (muse-publishing-directive \"title\")
361 (let ((author (muse-publishing-directive \"author\")))
362 (if (not (string= author (user-full-name)))
363 (concat \" (by \" author \")\"))))</lisp></span></h1>
364 <div id=\"inner-header\">
365 <lisp>planner-html-inner-header</lisp>
366 </div>
367 <div id=\"muse-sections\">
368 <!-- Page published by Emacs Muse begins here -->\n"
369 "Header used for publishing PLANNER XHTML files.
370 This may be text or a filename."
371 :type 'string
372 :group 'planner-publish)
374 (defcustom planner-xhtml-footer "
375 <!-- Page published by Emacs Muse ends here -->
376 </div>
377 <div id=\"inner-footer\">
378 <lisp>planner-html-inner-footer</lisp>
379 </div>
380 </div>
381 </body>
382 </html>\n"
383 "Footer used for publishing PLANNER XHTML files.
384 This may be text or a filename."
385 :type 'string
386 :group 'planner-publish)
388 (defcustom planner-html-inner-header ""
389 "Extra header section that can be embedded within
390 `planner-html-header' and `planner-xhtml-header'."
391 :type 'string
392 :group 'planner-publish)
394 (defcustom planner-html-inner-footer ""
395 "Extra footer section that can be embedded within
396 `planner-html-footer' and `planner-xhtml-footer'."
397 :type 'string
398 :group 'planner-publish)
400 ;;;_ + Publishing hooks
402 (defcustom planner-publish-prepare-regexps
403 '((100 "^\\(\\*+\\)\\s-+" 0 planner-publish-section))
404 "List of markup rules to apply before publishing a page with Planner.
405 See `muse-publish-markup-regexps' for details on the syntax used."
406 :type '(repeat (choice
407 (list :tag "Markup rule"
408 integer
409 (choice regexp symbol)
410 integer
411 (choice string function symbol))
412 function))
413 :group 'planner-publish)
415 (defcustom planner-publish-finalize-regexps
417 "List of markup rules to apply after publishing a page with Planner.
418 See `muse-publish-markup-regexps' for details on the syntax used."
419 :type '(repeat (choice
420 (list :tag "Markup rule"
421 integer
422 (choice regexp symbol)
423 integer
424 (choice string function symbol))
425 function))
426 :group 'planner-publish)
428 (defun planner-publish-prepare-buffer ()
429 (goto-char (point-min))
430 (muse-publish-markup "preparing Planner page"
431 planner-publish-prepare-regexps)
432 ;; indicate that we are to continue preparing the buffer
433 nil)
435 (defun planner-publish-finalize-buffer ()
436 (goto-char (point-min))
437 (muse-publish-markup "finalizing Planner page"
438 planner-publish-finalize-regexps)
439 ;; indicate that we are to continue finalizing the buffer
440 nil)
442 ;;;_ + Markup
444 (defun planner-publish-markup-task ()
445 "Replace tasks with XML representation of task data."
446 (save-restriction
447 (narrow-to-region
448 (planner-line-beginning-position)
449 (planner-line-end-position))
450 (let ((info (planner-current-task-info)))
451 (delete-region (point-min) (point-max))
452 (forward-line 1)
453 (insert
454 (format (concat "<task id=\"%s\" priority=\"%s\" status=\"%s\""
455 " link=\"%s\" plan=\"%s\" date=\"%s\">")
456 (or (planner-task-number info) "")
457 (or (planner-task-priority info) "")
458 (or (planner-publish-task-status-expand
459 (planner-task-status info)) "")
460 (or (planner-task-link-text info) "")
461 (or (planner-task-plan info) "")
462 (or (planner-task-date info) "")))
463 ;; mark this area read only for safety's sake
464 (planner-insert-markup (planner-task-description info))
465 (insert "</task>"))))
467 (defun planner-publish-markup-note ()
468 "Replace note with XML representation of note data. Borrowed
469 heavily from Sacha's personal configs."
470 (save-restriction
471 (narrow-to-region
472 (save-excursion (beginning-of-line) (point))
473 (or (save-excursion
474 (and (re-search-forward "^\\(\\.#\\|* \\|</notes-section>\\)" nil t)
475 (match-beginning 0)))
476 (point-max)))
477 (let ((info (planner-current-note-info t)))
478 (delete-region (point-min) (point-max))
479 (insert (format (concat "<note anchor=\"%s\" timestamp=\"%s\""
480 " link=\"%s\" categories=\"%s\">")
481 (planner-note-anchor info)
482 (or (planner-note-timestamp info) "")
483 (or (planner-note-link info) "")
484 (or (planner-note-link-text info) ""))
485 "<title level=\"3\">" (planner-note-title info) "</title>\n"
486 "<content>\n")
487 (planner-insert-markup (planner-note-body info))
488 (insert "\n\n</content>\n</note>\n"))))
491 ;;;_ + Tags
493 (defun planner-insert-markup (&rest args)
494 (if (fboundp 'muse-insert-markup)
495 (apply 'muse-insert-markup args)
496 (let ((beg (point)))
497 (apply 'insert args)
498 (muse-publish-mark-read-only beg (point)))))
500 (defun planner-publish-nested-section-tag (beg end)
501 "Generated by `planner-publish-section', the nested section tag
502 now takes in TITLE and LEVEL attributes.
504 This is related to the Muse concept of sections, but done before
505 marking up the buffer, and with special actions done on the title
506 of each section."
507 (save-excursion
508 (goto-char beg)
509 (planner-insert-markup (muse-markup-text 'planner-begin-nested-section))
510 (goto-char end)
511 (planner-insert-markup (muse-markup-text 'planner-end-nested-section))))
513 (defun planner-publish-title-tag (beg end attrs)
514 (let ((level (cdr (assoc "level" attrs))))
515 (save-excursion
516 (goto-char beg)
517 (planner-insert-markup (muse-markup-text 'planner-begin-title level))
518 (goto-char end)
519 (planner-insert-markup (muse-markup-text 'planner-end-title level)))))
521 (defun planner-publish-content-tag (beg end)
522 (save-excursion
523 (goto-char end)
524 (planner-insert-markup (muse-markup-text 'planner-end-content))
525 (goto-char beg)
526 (planner-insert-markup (muse-markup-text 'planner-begin-content))))
528 (defun planner-publish-diary-section-tag (beg end)
529 (save-excursion
530 (goto-char beg)
531 (planner-insert-markup (muse-markup-text 'planner-begin-diary-section))
532 (forward-line 1)
533 (muse-publish-verse-tag (point) end)
534 (goto-char end)
535 (planner-insert-markup (muse-markup-text 'planner-end-diary-section))))
537 (defun planner-publish-tasks-section-tag (beg end)
538 (save-excursion
539 (goto-char beg)
540 (planner-insert-markup (muse-markup-text 'planner-begin-task-section))
541 (forward-line 1)
542 (planner-insert-markup (muse-markup-text 'planner-begin-task-body))
543 (goto-char end)
544 (planner-insert-markup (muse-markup-text 'planner-end-task-body))
545 (planner-insert-markup (muse-markup-text 'planner-end-task-section))))
547 (defun planner-publish-task-tag (beg end attrs)
548 (save-excursion
549 (let ((number (cdr (assoc "id" attrs)))
550 (status (cdr (assoc "status" attrs)))
551 (priority (cdr (assoc "priority" attrs)))
552 (link (cdr (assoc "link" attrs)))
553 (plan (cdr (assoc "plan" attrs)))
554 (date (cdr (assoc "date" attrs))))
555 (remove-text-properties beg end
556 '(read-only nil rear-nonsticky nil))
557 (goto-char end)
558 (when link
559 (insert " (" (planner-make-link link) ")"))
560 (planner-insert-markup (muse-markup-text 'planner-end-task))
561 (goto-char beg)
562 (planner-insert-markup
563 (muse-markup-text 'planner-begin-task
564 status
565 priority
566 (concat priority number " "
567 (planner-publish-task-status-collapse status)
568 " "))))))
570 (defun planner-publish-notes-section-tag (beg end)
571 "Replace the region BEG to END with the notes for this page."
572 (save-excursion
573 (planner-insert-markup (muse-markup-text 'planner-begin-note-section))
574 (forward-line 1)
575 (planner-insert-markup (muse-markup-text 'planner-begin-body))
576 (insert ?\n)
577 (goto-char end)
578 (planner-insert-markup (muse-markup-text 'planner-end-body))
579 (planner-insert-markup (muse-markup-text 'planner-end-note-section))))
581 (defun planner-publish-notes-tag (beg end)
582 "Replace the region BEG to END with an index of the notes for this page."
583 (delete-region beg end)
584 (insert "\n")
585 (mapcar
586 (lambda (item)
587 (insert (format " - [[%s%s][%s]]\n"
588 (planner-page-name)
589 (car item)
590 (planner-remove-links (cdr item)))))
591 (save-excursion
592 (find-file muse-publishing-current-file)
593 (planner-notes-get-headlines)))
594 (insert "\n"))
596 (defun planner-publish-past-notes-tag (beg end attrs)
597 "Replace the region BEG to END with an index of past notes.
598 If ATTRS is non-nil, it is an alist containing values for
599 DIRECTORY and START."
600 (let ((files (save-excursion
601 (find-file muse-publishing-current-file)
602 (planner-get-day-pages nil nil t)))
603 (earliest (cdr (assoc "start" attrs))))
604 (while files
605 (when (or (null earliest)
606 (not (string-lessp (caar files) earliest)))
607 (let ((title-lines (list t)))
608 (with-temp-buffer
609 (insert-file-contents (cdar files))
610 (while (re-search-forward "^\\.#\\([0-9]+\\)\\s-+\\(.+\\)" nil t)
611 (nconc title-lines (list (cons (match-string 1)
612 (match-string 2))))))
613 (setq title-lines (cdr title-lines))
614 (when title-lines
615 (insert (planner-make-link (planner-page-name (caar files)))
616 " ::\n")
617 (planner-insert-markup " <dl class=\"contents\">\n")
618 (while title-lines
619 (planner-insert-markup " <dt class=\"contents\">")
620 (insert (format "[[%s#%s][%s]]"
621 (planner-page-name (caar files))
622 (caar title-lines) (cdar title-lines)))
623 (planner-insert-markup "</dt>\n")
624 (setq title-lines (cdr title-lines)))
625 (planner-insert-markup " </dl>\n\n"))))
626 (setq files (cdr files)))))
628 (defun planner-publish-note-tag (beg end attrs)
629 (save-excursion
630 (let ((anchor (or (cdr (assoc "anchor" attrs)) ""))
631 (timestamp (or (cdr (assoc "timestamp" attrs)) ""))
632 (link (or (cdr (assoc "link" attrs)) ""))
633 (categories (or (cdr (assoc "categories" attrs)) "")))
634 (remove-text-properties beg end
635 '(read-only nil rear-nonsticky nil))
636 (goto-char beg)
637 (planner-insert-markup (muse-markup-text 'planner-begin-note
638 anchor
639 (concat "#" anchor)))
640 (goto-char end)
641 (planner-insert-markup (muse-markup-text 'planner-begin-note-details
642 timestamp)
643 (muse-markup-text 'planner-begin-note-link))
644 (insert link)
645 (planner-insert-markup (muse-markup-text 'planner-end-note-link))
646 ;; remove link item from categories to avoid duplicates
647 (setq categories (planner-replace-regexp-in-string (regexp-quote link)
648 categories "" t t))
649 (planner-insert-markup (muse-markup-text 'planner-begin-note-categories))
650 (insert categories)
651 (planner-insert-markup (muse-markup-text 'planner-end-note-categories))
652 (insert ?\n)
653 (planner-insert-markup (muse-markup-text 'planner-end-note-details))
654 (planner-insert-markup (muse-markup-text 'planner-end-note)))))
656 ;;;_ + helper routine
658 (defun planner-publish-task-status-expand (status)
659 (cond
660 ((string= status "_") "open")
661 ((string= status "o") "in-progress")
662 ((string= status "D") "delegated")
663 ((string= status "P") "pending")
664 ((string= status "X") "done")
665 ((string= status "C") "cancelled")
666 (t "unknown")))
668 (defun planner-publish-task-status-collapse (status)
669 (cond
670 ((string= status "open") "_")
671 ((string= status "in-progress") "o")
672 ((string= status "delegated") "D")
673 ((string= status "pending") "P")
674 ((string= status "done") "X")
675 ((string= status "cancelled") "C")
676 (t "?")))
678 (defun planner-publish-section-close (depth text)
679 "Find where the closing tag of DEPTH should go, and insert TEXT."
680 (let (not-end)
681 (save-excursion
682 (while (and (setq not-end (re-search-forward
683 (concat "^\\*\\{1," (number-to-string depth)
684 "\\}\\s-+")
685 nil t))
686 (get-text-property (match-beginning 0) 'read-only)))
687 (if not-end
688 (forward-line 0)
689 (goto-char (point-max)))
690 (cond ((not (eq (char-before) ?\n))
691 (insert "\n\n"))
692 ((not (eq (char-before (1- (point))) ?\n))
693 (insert "\n")))
694 (insert text)
695 (insert "\n"))))
697 (defvar planner-section-tagnames
698 '(("Diary" . "diary-section")
699 ("Tasks" . "tasks-section")
700 ("Notes" . "notes-section"))
701 "Alist of sections and their tag name.")
703 (defun planner-publish-section-tagname (text)
704 "A routine that checks `planner-section-tagnames' for tagname."
705 (let ((tagname (cdr (assoc text planner-section-tagnames))))
706 (if tagname
707 tagname
708 "nested-section")))
710 (defun planner-publish-section ()
711 "Publish the current heading as a section."
712 (let* ((depth (length (match-string 1)))
713 (title (buffer-substring (match-end 0) (planner-line-end-position)))
714 (tagname (planner-publish-section-tagname title)))
715 (delete-region (match-beginning 0) (match-end 0))
716 (insert (format "<%s level=\"%s\"><title level=\"%s\">"
717 tagname depth (1+ depth)))
718 (end-of-line)
719 (insert "</title>")
720 (planner-publish-section-close depth (format "</%s>" tagname))))
722 ;;;_ + Planner Style Definitions
724 (unless (assoc "planner-xml" muse-publishing-styles)
725 (muse-derive-style "planner-xml" "xml"
726 :regexps 'planner-publish-markup-regexps
727 :functions 'planner-publish-markup-functions
728 :tags 'planner-publish-markup-tags
729 :strings 'planner-xml-markup-strings
730 :before 'planner-publish-prepare-buffer
731 :after 'planner-publish-finalize-buffer
732 :header 'planner-xml-header
733 :footer 'planner-xml-footer)
734 (muse-derive-style "planner-html" "html"
735 :regexps 'planner-publish-markup-regexps
736 :functions 'planner-publish-markup-functions
737 :tags 'planner-publish-markup-tags
738 :strings 'planner-html-markup-strings
739 :before 'planner-publish-prepare-buffer
740 :after 'planner-publish-finalize-buffer
741 :header 'planner-html-header
742 :footer 'planner-html-footer)
743 (muse-derive-style "planner-xhtml" "xhtml"
744 :regexps 'planner-publish-markup-regexps
745 :functions 'planner-publish-markup-functions
746 :tags 'planner-publish-markup-tags
747 :strings 'planner-html-markup-strings
748 :before 'planner-publish-prepare-buffer
749 :after 'planner-publish-finalize-buffer
750 :header 'planner-xhtml-header
751 :footer 'planner-xhtml-footer))
753 (provide 'planner-publish)
755 ;;; planner-publish.el ends here