planner-publish: Change :after to :before-end
[planner-el.git] / planner-publish.el
blob835f3137d5fa41d2cc57da1c1a45618f4a598a85
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 (if (featurep 'muse-nested-tags)
105 '(("nested-section" t nil t planner-publish-nested-section-tag)
106 ("title" t t nil planner-publish-title-tag)
107 ("content" t nil nil planner-publish-content-tag)
108 ("diary-section" t nil nil planner-publish-diary-section-tag)
109 ("tasks-section" t nil nil planner-publish-tasks-section-tag)
110 ("notes-section" t nil nil planner-publish-notes-section-tag)
111 ("notes" nil nil nil planner-publish-notes-tag)
112 ("past-notes" nil t nil planner-publish-past-notes-tag)
113 ("task" t t nil planner-publish-task-tag)
114 ("note" t t nil planner-publish-note-tag))
115 '(("nested-section" t nil planner-publish-nested-section-tag)
116 ("title" t t planner-publish-title-tag)
117 ("content" t nil planner-publish-content-tag)
118 ("diary-section" t nil planner-publish-diary-section-tag)
119 ("tasks-section" t nil planner-publish-tasks-section-tag)
120 ("notes-section" t nil planner-publish-notes-section-tag)
121 ("notes" nil nil planner-publish-notes-tag)
122 ("past-notes" nil t planner-publish-past-notes-tag)
123 ("task" t t planner-publish-task-tag)
124 ("note" t t planner-publish-note-tag)))
125 "A list of tag specifications, for specially marking up PLANNER.
126 See `muse-publish-markup-tags' for more information."
127 :type '(repeat (list (string :tag "Markup tag")
128 (boolean :tag "Expect closing tag" :value t)
129 (boolean :tag "Parse attributes" :value nil)
130 (boolean :tag "Nestable" :value nil)
131 function))
132 :group 'planner-publish)
134 ;;;_ + XML specific customizations
136 (defcustom planner-xml-markup-strings
137 '((planner-begin-nested-section . "<section>")
138 (planner-end-nested-section . "</section>")
139 (planner-begin-title . "<title>")
140 (planner-end-title . "</title>")
141 (planner-begin-content . "")
142 (planner-end-content . "")
143 (planner-begin-body . "")
144 (planner-end-body . "")
145 (planner-begin-diary-section . "<diary>")
146 (planner-end-diary-section . "</diary>")
147 (planner-begin-task-section . "<tasks>")
148 (planner-end-task-section . "</tasks>")
149 (planner-begin-task-body . "")
150 (planner-end-task-body . "")
151 (planner-begin-note-section . "<notes>")
152 (planner-end-note-section . "</notes>")
153 (planner-begin-task . "<task status=\"%s\" priority=\"%s\">")
154 (planner-end-task . "</task>")
155 (planner-begin-note . "<note number=\"%s\">")
156 (planner-end-note . "</note>")
157 (planner-begin-note-details . "<details><timestamp>%s</timestamp>")
158 (planner-end-note-details . "</details>")
159 (planner-begin-note-link . "<references>")
160 (planner-end-note-link . "</references>")
161 (planner-begin-note-categories . "<categories>")
162 (planner-end-note-categories . "</categories>"))
163 "Strings used for marking up text as XML.
164 These cover the most basic kinds of markup, the handling of which
165 differs little between the various styles.
167 If a markup rule is not found here, `muse-xml-markup-strings' is
168 searched."
169 :type '(alist :key-type symbol :value-type string)
170 :group 'planner-publish)
172 (defcustom planner-xml-header
173 "<?xml version=\"1.0\" encoding=\"<lisp>(muse-xml-encoding)</lisp>\"?>
174 <PLANNER>
175 <pageinfo>
176 <title><lisp>(muse-publishing-directive \"title\")</lisp></title>
177 <author><lisp>(muse-publishing-directive \"author\")</lisp></author>
178 <maintainer><lisp>(muse-style-element :maintainer)</lisp></maintainer>
179 <pubdate><lisp>(muse-publishing-directive \"date\")</lisp></pubdate>
180 </pageinfo>
181 <!-- Page published by Emacs Muse begins here -->\n"
182 "Header used for publishing PLANNER XML files.
183 This may be text or a filename."
184 :type 'string
185 :group 'planner-publish)
187 (defcustom planner-xml-footer "
188 <!-- Page published by Emacs Muse ends here -->
189 </PLANNER>\n"
190 "Footer used for publishing PLANNER XML files.
191 This may be text or a filename."
192 :type 'string
193 :group 'planner-publish)
195 ;;;_ + HTML specific customizations
197 (defcustom planner-html-markup-strings
198 '((planner-begin-nested-section . "<div class=\"section\">")
199 (planner-end-nested-section . "</div>")
200 (planner-begin-title . "<h%s>")
201 (planner-end-title . "</h%s>")
202 (planner-begin-content . "<div class=\"content\">")
203 (planner-end-content . "</div>")
204 (planner-begin-body . "<div class=\"body\">")
205 (planner-end-body . "</div>")
206 (planner-begin-diary-section . "<div id=\"diary\" class=\"section\">")
207 (planner-end-diary-section . "</div>")
208 (planner-begin-task-section . "<div id=\"tasks\" class=\"section\">")
209 (planner-end-task-section . "</div>")
210 (planner-begin-task-body . "<ul class=\"body\">")
211 (planner-end-task-body . "</ul>")
212 (planner-begin-note-section . "<div id=\"notes\" class=\"section\">")
213 (planner-end-note-section . "</div>")
214 (planner-begin-task . "<li class=\"task\"><span class=\"%s\"><span class=\"%s\">%s</span>")
215 (planner-end-task . "</span></li>")
216 (planner-begin-note . "<div class=\"note\"><a name=\"%s\"></a><span class=\"anchor\">%s</span>")
217 (planner-end-note . "</div>")
218 (planner-begin-note-details . "<div class=\"details\"><span class=\"timestamp\">%s</span>")
219 (planner-end-note-details . "</div>")
220 (planner-begin-note-link . " <span class=\"link\">")
221 (planner-end-note-link . "</span>")
222 (planner-begin-note-categories . " <span class=\"categories\">")
223 (planner-end-note-categories . "</span>"))
224 "Strings used for marking up text as HTML.
225 These cover the most basic kinds of markup, the handling of which
226 differs little between the various styles.
228 If a markup rule is not found here, `muse-html-markup-strings' is
229 searched."
230 :type '(alist :key-type symbol :value-type string)
231 :group 'planner-publish)
233 (defcustom planner-html-style-sheet
234 "<style type=\"text/css\">
235 body {
236 background: white; color: black;
237 margin-left: 3%; margin-right: 3%;
240 p { margin-top: 3px; margin-bottom: 3px; }
241 p.verse { margin-left: 3% }
243 h1,h2,h3,h4,h5 { margin:0; padding:0; }
245 h1 { padding: 10px; margin-bottom: 10px; }
247 table.muse-table { margin: 0; font-size: 11px;
248 border-collapse: collapse;
249 background: #e2effa;
250 border: 1px solid #aadeed; }
252 table.muse-table tbody td { border: 1px solid #ccdeed; }
254 .example { margin-left: 5px; padding: 3px;
255 background: #fffffc;
256 border: 1px solid #ccdeed; }
258 /* nested sections */
259 .section { margin: 0; padding: 10px;
260 margin-bottom: 15px;
261 font-size: 12px; }
263 .section .section { margin: 0; margin-left: 5px;
264 font-size: 11px; }
266 .title { margin: 0; padding; 0 }
268 /* optional calendar section */
269 .calendar { float: right; }
270 table.month-calendar { font-size: 9px; }
272 /* Diary section */
273 #diary p { margin-top: 1em; }
275 /* Tasks section */
276 .task .A { color: red }
277 .task .B { color: green }
278 .task .C { color: navy }
279 .task .done { color: gray; text-decoration: line-through; }
280 .task .cancelled { color: gray; text-decoration: italic; }
282 /* Notes section */
283 .note { margin-top: 1.5em; }
284 .note .anchor { float: left; margin-right: 5px; }
285 .note .details { margin-top: .5em; }
287 </style>"
288 "Store your stylesheet definitions here. The provided default
289 is for reference only. You definitely want to customize this for
290 your particular needs & wants. This is used in
291 `planner-html-header' and `planner-xhtml-header'. Refer to
292 `muse-html-style-sheet' for details on usage. You may simply
293 override the above by specifying an explicit link to a CSS file."
294 :type 'string
295 :group 'planner-publish)
297 (defcustom planner-html-header
298 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">
299 <html>
300 <head>
301 <title><lisp>
302 (concat (muse-publishing-directive \"title\")
303 (let ((author (muse-publishing-directive \"author\")))
304 (if (not (string= author (user-full-name)))
305 (concat \" (by \" author \")\"))))</lisp></title>
306 <meta name=\"generator\" content=\"muse.el\">
307 <meta http-equiv=\"<lisp>muse-html-meta-http-equiv</lisp>\"
308 content=\"<lisp>muse-html-meta-content-type</lisp>\">
309 <lisp>
310 (let ((maintainer (muse-style-element :maintainer)))
311 (when maintainer
312 (concat \"<link rev=\\\"made\\\" href=\\\"\" maintainer \"\\\">\")))
313 </lisp>
314 <lisp>planner-html-style-sheet</lisp>
315 </head>
316 <body>
317 <div id=\"content\">
318 <h1><span><lisp>
319 (concat (muse-publishing-directive \"title\")
320 (let ((author (muse-publishing-directive \"author\")))
321 (if (not (string= author (user-full-name)))
322 (concat \" (by \" author \")\"))))</lisp></span></h1>
323 <div id=\"inner-header\">
324 <lisp>planner-html-inner-header</lisp>
325 </div>
326 <div id=\"muse-sections\">
327 <!-- Page published by Emacs Muse begins here -->\n"
328 "Header used for publishing PLANNER HTML files.
329 This may be text or a filename."
330 :type 'string
331 :group 'planner-publish)
333 (defcustom planner-html-footer "
334 <!-- Page published by Emacs Muse ends here -->
335 </div>
336 <div id=\"inner-footer\">
337 <lisp>planner-html-inner-footer</lisp>
338 </div>
339 </div>
340 </body>
341 </html>\n"
342 "Footer used for publishing PLANNER HTML files.
343 This may be text or a filename."
344 :type 'string
345 :group 'planner-publish)
347 (defcustom planner-xhtml-header
348 "<?xml version=\"1.0\" encoding=\"<lisp>
349 (muse-html-encoding)</lisp>\"?>
350 <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"
351 \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">
352 <html xmlns=\"http://www.w3.org/1999/xhtml\">
353 <head>
354 <title><lisp>
355 (concat (muse-publishing-directive \"title\")
356 (let ((author (muse-publishing-directive \"author\")))
357 (if (not (string= author (user-full-name)))
358 (concat \" (by \" author \")\"))))</lisp></title>
359 <meta name=\"generator\" content=\"muse.el\" />
360 <meta http-equiv=\"<lisp>muse-html-meta-http-equiv</lisp>\"
361 content=\"<lisp>muse-html-meta-content-type</lisp>\" />
362 <lisp>
363 (let ((maintainer (muse-style-element :maintainer)))
364 (when maintainer
365 (concat \"<link rev=\\\"made\\\" href=\\\"\" maintainer \"\\\" />\")))
366 </lisp>
367 <lisp>planner-html-style-sheet</lisp>
368 </head>
369 <body>
370 <div id=\"content\">
371 <h1><span><lisp>
372 (concat (muse-publishing-directive \"title\")
373 (let ((author (muse-publishing-directive \"author\")))
374 (if (not (string= author (user-full-name)))
375 (concat \" (by \" author \")\"))))</lisp></span></h1>
376 <div id=\"inner-header\">
377 <lisp>planner-html-inner-header</lisp>
378 </div>
379 <div id=\"muse-sections\">
380 <!-- Page published by Emacs Muse begins here -->\n"
381 "Header used for publishing PLANNER XHTML files.
382 This may be text or a filename."
383 :type 'string
384 :group 'planner-publish)
386 (defcustom planner-xhtml-footer "
387 <!-- Page published by Emacs Muse ends here -->
388 </div>
389 <div id=\"inner-footer\">
390 <lisp>planner-html-inner-footer</lisp>
391 </div>
392 </div>
393 </body>
394 </html>\n"
395 "Footer used for publishing PLANNER XHTML files.
396 This may be text or a filename."
397 :type 'string
398 :group 'planner-publish)
400 (defcustom planner-html-inner-header ""
401 "Extra header section that can be embedded within
402 `planner-html-header' and `planner-xhtml-header'."
403 :type 'string
404 :group 'planner-publish)
406 (defcustom planner-html-inner-footer ""
407 "Extra footer section that can be embedded within
408 `planner-html-footer' and `planner-xhtml-footer'."
409 :type 'string
410 :group 'planner-publish)
412 ;;;_ + Publishing hooks
414 (defcustom planner-publish-prepare-regexps
415 '((100 "^\\(\\*+\\)\\s-+" 0 planner-publish-section))
416 "List of markup rules to apply before publishing a page with Planner.
417 See `muse-publish-markup-regexps' for details on the syntax used."
418 :type '(repeat (choice
419 (list :tag "Markup rule"
420 integer
421 (choice regexp symbol)
422 integer
423 (choice string function symbol))
424 function))
425 :group 'planner-publish)
427 (defcustom planner-publish-finalize-regexps
429 "List of markup rules to apply after publishing a page with Planner.
430 See `muse-publish-markup-regexps' for details on the syntax used."
431 :type '(repeat (choice
432 (list :tag "Markup rule"
433 integer
434 (choice regexp symbol)
435 integer
436 (choice string function symbol))
437 function))
438 :group 'planner-publish)
440 (defun planner-publish-prepare-buffer ()
441 (goto-char (point-min))
442 (muse-publish-markup "preparing Planner page"
443 planner-publish-prepare-regexps)
444 ;; indicate that we are to continue preparing the buffer
445 nil)
447 (defun planner-publish-finalize-buffer ()
448 (goto-char (point-min))
449 (muse-publish-markup "finalizing Planner page"
450 planner-publish-finalize-regexps)
451 ;; indicate that we are to continue finalizing the buffer
452 nil)
454 ;;;_ + Markup
456 (defun planner-publish-markup-task ()
457 "Replace tasks with XML representation of task data."
458 (save-restriction
459 (narrow-to-region
460 (planner-line-beginning-position)
461 (planner-line-end-position))
462 (let ((info (planner-current-task-info)))
463 (delete-region (point-min) (point-max))
464 (forward-line 1)
465 (insert
466 (format (concat "<task id=\"%s\" priority=\"%s\" status=\"%s\""
467 " link=\"%s\" plan=\"%s\" date=\"%s\">")
468 (or (planner-task-number info) "")
469 (or (planner-task-priority info) "")
470 (or (planner-publish-task-status-expand
471 (planner-task-status info)) "")
472 (or (planner-task-link-text info) "")
473 (or (planner-task-plan info) "")
474 (or (planner-task-date info) "")))
475 ;; mark this area read only for safety's sake
476 (planner-insert-markup (planner-task-description info))
477 (insert "</task>"))))
479 (defun planner-publish-markup-note ()
480 "Replace note with XML representation of note data. Borrowed
481 heavily from Sacha's personal configs."
482 (save-restriction
483 (narrow-to-region
484 (save-excursion (beginning-of-line) (point))
485 (or (save-excursion
486 (and (re-search-forward "^\\(\\.#\\|* \\|</notes-section>\\)" nil t)
487 (match-beginning 0)))
488 (point-max)))
489 (let ((info (planner-current-note-info t)))
490 (delete-region (point-min) (point-max))
491 (insert (format (concat "<note anchor=\"%s\" timestamp=\"%s\""
492 " link=\"%s\" categories=\"%s\">")
493 (planner-note-anchor info)
494 (or (planner-note-timestamp info) "")
495 (or (planner-note-link info) "")
496 (or (planner-note-link-text info) ""))
497 "<title level=\"3\">" (planner-note-title info) "</title>\n"
498 "<content>\n")
499 (planner-insert-markup (planner-note-body info))
500 (insert "\n\n</content>\n</note>\n"))))
503 ;;;_ + Tags
505 (defun planner-insert-markup (&rest args)
506 (if (fboundp 'muse-insert-markup)
507 (apply 'muse-insert-markup args)
508 (let ((beg (point)))
509 (apply 'insert args)
510 (muse-publish-mark-read-only beg (point)))))
512 (defun planner-publish-nested-section-tag (beg end)
513 "Generated by `planner-publish-section', the nested section tag
514 now takes in TITLE and LEVEL attributes.
516 This is related to the Muse concept of sections, but done before
517 marking up the buffer, and with special actions done on the title
518 of each section."
519 (save-excursion
520 (goto-char beg)
521 (planner-insert-markup (muse-markup-text 'planner-begin-nested-section))
522 (goto-char end)
523 (planner-insert-markup (muse-markup-text 'planner-end-nested-section))))
525 (defun planner-publish-title-tag (beg end attrs)
526 (let ((level (cdr (assoc "level" attrs))))
527 (save-excursion
528 (goto-char beg)
529 (planner-insert-markup (muse-markup-text 'planner-begin-title level))
530 (goto-char end)
531 (planner-insert-markup (muse-markup-text 'planner-end-title level)))))
533 (defun planner-publish-content-tag (beg end)
534 (save-excursion
535 (goto-char end)
536 (planner-insert-markup (muse-markup-text 'planner-end-content))
537 (goto-char beg)
538 (planner-insert-markup (muse-markup-text 'planner-begin-content))))
540 (defun planner-publish-diary-section-tag (beg end)
541 (save-excursion
542 (goto-char beg)
543 (planner-insert-markup (muse-markup-text 'planner-begin-diary-section))
544 (forward-line 1)
545 (muse-publish-verse-tag (point) end)
546 (goto-char end)
547 (planner-insert-markup (muse-markup-text 'planner-end-diary-section))))
549 (defun planner-publish-tasks-section-tag (beg end)
550 (save-excursion
551 (goto-char beg)
552 (planner-insert-markup (muse-markup-text 'planner-begin-task-section))
553 (forward-line 1)
554 (planner-insert-markup (muse-markup-text 'planner-begin-task-body))
555 (goto-char end)
556 (planner-insert-markup (muse-markup-text 'planner-end-task-body))
557 (planner-insert-markup (muse-markup-text 'planner-end-task-section))))
559 (defun planner-publish-task-tag (beg end attrs)
560 (save-excursion
561 (let ((number (cdr (assoc "id" attrs)))
562 (status (cdr (assoc "status" attrs)))
563 (priority (cdr (assoc "priority" attrs)))
564 (link (cdr (assoc "link" attrs)))
565 (plan (cdr (assoc "plan" attrs)))
566 (date (cdr (assoc "date" attrs))))
567 (remove-text-properties beg end
568 '(read-only nil rear-nonsticky nil))
569 (goto-char end)
570 (when link
571 (insert " (" (planner-make-link link) ")"))
572 (planner-insert-markup (muse-markup-text 'planner-end-task))
573 (goto-char beg)
574 (planner-insert-markup
575 (muse-markup-text 'planner-begin-task
576 status
577 priority
578 (concat priority number " "
579 (planner-publish-task-status-collapse status)
580 " "))))))
582 (defun planner-publish-notes-section-tag (beg end)
583 "Replace the region BEG to END with the notes for this page."
584 (save-excursion
585 (planner-insert-markup (muse-markup-text 'planner-begin-note-section))
586 (forward-line 1)
587 (planner-insert-markup (muse-markup-text 'planner-begin-body))
588 (insert ?\n)
589 (goto-char end)
590 (planner-insert-markup (muse-markup-text 'planner-end-body))
591 (planner-insert-markup (muse-markup-text 'planner-end-note-section))))
593 (defun planner-publish-notes-tag (beg end)
594 "Replace the region BEG to END with an index of the notes for this page."
595 (delete-region beg end)
596 (insert "\n")
597 (mapcar
598 (lambda (item)
599 (insert (format " - [[%s%s][%s]]\n"
600 (planner-page-name)
601 (car item)
602 (planner-remove-links (cdr item)))))
603 (save-excursion
604 (find-file muse-publishing-current-file)
605 (planner-notes-get-headlines)))
606 (insert "\n"))
608 (defun planner-publish-past-notes-tag (beg end attrs)
609 "Replace the region BEG to END with an index of past notes.
610 If ATTRS is non-nil, it is an alist containing values for
611 DIRECTORY and START."
612 (let ((files (save-excursion
613 (find-file muse-publishing-current-file)
614 (planner-get-day-pages nil nil t)))
615 (earliest (cdr (assoc "start" attrs))))
616 (while files
617 (when (or (null earliest)
618 (not (string-lessp (caar files) earliest)))
619 (let ((title-lines (list t)))
620 (with-temp-buffer
621 (insert-file-contents (cdar files))
622 (while (re-search-forward "^\\.#\\([0-9]+\\)\\s-+\\(.+\\)" nil t)
623 (nconc title-lines (list (cons (match-string 1)
624 (match-string 2))))))
625 (setq title-lines (cdr title-lines))
626 (when title-lines
627 (insert (planner-make-link (planner-page-name (caar files)))
628 " ::\n")
629 (planner-insert-markup " <dl class=\"contents\">\n")
630 (while title-lines
631 (planner-insert-markup " <dt class=\"contents\">")
632 (insert (format "[[%s#%s][%s]]"
633 (planner-page-name (caar files))
634 (caar title-lines) (cdar title-lines)))
635 (planner-insert-markup "</dt>\n")
636 (setq title-lines (cdr title-lines)))
637 (planner-insert-markup " </dl>\n\n"))))
638 (setq files (cdr files)))))
640 (defun planner-publish-note-tag (beg end attrs)
641 (save-excursion
642 (let ((anchor (or (cdr (assoc "anchor" attrs)) ""))
643 (timestamp (or (cdr (assoc "timestamp" attrs)) ""))
644 (link (or (cdr (assoc "link" attrs)) ""))
645 (categories (or (cdr (assoc "categories" attrs)) "")))
646 (remove-text-properties beg end
647 '(read-only nil rear-nonsticky nil))
648 (goto-char beg)
649 (planner-insert-markup (muse-markup-text 'planner-begin-note
650 anchor
651 (concat "#" anchor)))
652 (goto-char end)
653 (planner-insert-markup (muse-markup-text 'planner-begin-note-details
654 timestamp)
655 (muse-markup-text 'planner-begin-note-link))
656 (insert link)
657 (planner-insert-markup (muse-markup-text 'planner-end-note-link))
658 ;; remove link item from categories to avoid duplicates
659 (unless (or (string= link "") (string= categories ""))
660 (setq categories (planner-replace-regexp-in-string
661 (regexp-quote link) "" categories t t)))
662 (planner-insert-markup (muse-markup-text 'planner-begin-note-categories))
663 (insert categories)
664 (planner-insert-markup (muse-markup-text 'planner-end-note-categories))
665 (insert ?\n)
666 (planner-insert-markup (muse-markup-text 'planner-end-note-details))
667 (planner-insert-markup (muse-markup-text 'planner-end-note)))))
669 ;;;_ + helper routine
671 (defun planner-publish-task-status-expand (status)
672 (cond
673 ((string= status "_") "open")
674 ((string= status "o") "in-progress")
675 ((string= status "D") "delegated")
676 ((string= status "P") "pending")
677 ((string= status "X") "done")
678 ((string= status "C") "cancelled")
679 (t "unknown")))
681 (defun planner-publish-task-status-collapse (status)
682 (cond
683 ((string= status "open") "_")
684 ((string= status "in-progress") "o")
685 ((string= status "delegated") "D")
686 ((string= status "pending") "P")
687 ((string= status "done") "X")
688 ((string= status "cancelled") "C")
689 (t "?")))
691 (defun planner-publish-section-close (depth text)
692 "Find where the closing tag of DEPTH should go, and insert TEXT."
693 (let (not-end)
694 (save-excursion
695 (while (and (setq not-end (re-search-forward
696 (concat "^\\*\\{1," (number-to-string depth)
697 "\\}\\s-+")
698 nil t))
699 (get-text-property (match-beginning 0) 'read-only)))
700 (if not-end
701 (forward-line 0)
702 (goto-char (point-max)))
703 (cond ((not (eq (char-before) ?\n))
704 (insert "\n\n"))
705 ((not (eq (char-before (1- (point))) ?\n))
706 (insert "\n")))
707 (insert text)
708 (insert "\n"))))
710 (defvar planner-section-tagnames
711 '(("Diary" . "diary-section")
712 ("Tasks" . "tasks-section")
713 ("Notes" . "notes-section"))
714 "Alist of sections and their tag name.")
716 (defun planner-publish-section-tagname (text)
717 "A routine that checks `planner-section-tagnames' for tagname."
718 (let ((tagname (cdr (assoc text planner-section-tagnames))))
719 (if tagname
720 tagname
721 "nested-section")))
723 (defun planner-publish-section ()
724 "Publish the current heading as a section."
725 (let* ((depth (length (match-string 1)))
726 (title (buffer-substring (match-end 0) (planner-line-end-position)))
727 (tagname (planner-publish-section-tagname title)))
728 (delete-region (match-beginning 0) (match-end 0))
729 (insert (format "<%s level=\"%s\"><title level=\"%s\">"
730 tagname depth (1+ depth)))
731 (end-of-line)
732 (insert "</title>")
733 (planner-publish-section-close depth (format "</%s>" tagname))))
735 ;;;_ + Planner Style Definitions
737 (unless (assoc "planner-xml" muse-publishing-styles)
738 (muse-derive-style "planner-xml" "xml"
739 :regexps 'planner-publish-markup-regexps
740 :functions 'planner-publish-markup-functions
741 :tags 'planner-publish-markup-tags
742 :strings 'planner-xml-markup-strings
743 :before 'planner-publish-prepare-buffer
744 :before-end 'planner-publish-finalize-buffer
745 :header 'planner-xml-header
746 :footer 'planner-xml-footer)
747 (muse-derive-style "planner-html" "html"
748 :regexps 'planner-publish-markup-regexps
749 :functions 'planner-publish-markup-functions
750 :tags 'planner-publish-markup-tags
751 :strings 'planner-html-markup-strings
752 :before 'planner-publish-prepare-buffer
753 :before-end 'planner-publish-finalize-buffer
754 :header 'planner-html-header
755 :footer 'planner-html-footer)
756 (muse-derive-style "planner-xhtml" "xhtml"
757 :regexps 'planner-publish-markup-regexps
758 :functions 'planner-publish-markup-functions
759 :tags 'planner-publish-markup-tags
760 :strings 'planner-html-markup-strings
761 :before 'planner-publish-prepare-buffer
762 :before-end 'planner-publish-finalize-buffer
763 :header 'planner-xhtml-header
764 :footer 'planner-xhtml-footer))
766 (provide 'planner-publish)
768 ;;; planner-publish.el ends here