Entry
[planner-el.git] / planner-rdf.el
blob58a3188d0581047dbc5a2fb53af80e045ffef6b9
1 ;;; planner-rdf.el --- RDF export for Emacs PlannerMode
3 ;; Copyright (C) 2004, 2008 Rainer Volz
4 ;; Parts copyright (C) 2005, 2008 Free Software Foundation, Inc.
5 ;; Parts copyright (C) 2006, 2007 Software Freedom Law Center
7 ;; Author: Rainer Volz, http://www.rainervolz.de
8 ;; Version: 0.1
9 ;; Keywords: PlannerMode OWL RDF
10 ;; X-URL: not distributed yet
12 ;; This file is part of Planner. It is not part of GNU Emacs.
14 ;; Planner is free software; you can redistribute it and/or modify it
15 ;; under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 3, or (at your option)
17 ;; any later version.
19 ;; Planner is distributed in the hope that it will be useful, but
20 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 ;; General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with Planner; see the file COPYING. If not, write to the
26 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27 ;; Boston, MA 02110-1301, USA.
29 ;;; Commentary:
31 ;; planner-rdf is an add-on to Planner. It provides functions to
32 ;; publish Planner information in RDF format, so that the Planner
33 ;; information can be processed in an automated way by RDF-aware
34 ;; tools. To do this planner-rdf adds itself to the hook
35 ;; muse-after-publish-hook, which is run whenever Planner pages are
36 ;; being published.
38 ;; The source distribution and the HTML edition of the documentation is
39 ;; available at http://www.rainervolz.de/planner-rdf/
41 ;; To use planner-rdf put this file into your load-path and the following into
42 ;; your ~/.emacs file:
43 ;; (require 'planner-rdf)
44 ;; (add-hook 'muse-after-publish-hook 'planner-rdf-publish-file)
45 ;; (add-hook 'muse-after-publish-hook 'planner-rdf-publish-index)
47 ;; To minimise interference with the publication of other (non-Planner)
48 ;; Muse projects, planner-rdf-publish-file checks whether the current
49 ;; major mode is planner-mode. If yes, RDF publication is started, if not, the
50 ;; file is ignored.
52 ;; planner-rdf creates two files for every published Planner file: a .rdf and
53 ;; a .owl file. The .owl file contains
54 ;; - tasks
55 ;; - notes
56 ;; of the respective Planner page in OWL format. The OWL schema used is
57 ;; documented in planner-rdf.owl, which is part of this distribution.
58 ;; For a detailed explanation of the format please look at the planner-rdf
59 ;; documentation included.
61 ;; The .rdf file contains Dublin Core metadata about the source file, the HTML
62 ;; file, and the OWL file produced. It is mainly used to provide information
63 ;; about the relation of the different page versions to various tools.
65 ;; Please note: Currently the .rdf/.owl files are meant for local processing,
66 ;; not Internet publishing. Therefore they contain file-URIs pointing to the
67 ;; local file system.
69 ;; Tested with GNU Emacs 21.3.50...
71 ;; Customisation
73 ;; By default the RDF files are stored in the
74 ;; planner-publishing-directory. For a different location change variable
75 ;; planner-rdf-directory.
78 (eval-when-compile
79 (require 'cl))
80 (require 'planner)
82 (defvar planner-rdf-version "0.1"
83 "Module Version")
85 (defvar planner-rdf-directory planner-publishing-directory
86 "Directory where the RDF files are stored. Defaults to planner-publishing-directory.")
88 (defvar planner-rdf-schema-planner
89 "http://www.rainervolz.de/schema/2004/11/planner-rdf#"
90 "The Planner schema.")
92 (defvar planner-rdf-base
93 "http://localhost/planner/"
94 "The base URI for Planner info")
95 (defvar planner-rdf-task-prefix (concat planner-rdf-base "task-"))
96 (defvar planner-rdf-note-prefix (concat planner-rdf-base "note-"))
97 (defvar planner-rdf-tag-prefix (concat planner-rdf-base "tag-"))
98 (defvar planner-rdf-link-prefix (concat planner-rdf-base "link-"))
99 (defvar planner-rdf-page-prefix (concat planner-rdf-base "page-"))
100 (defvar planner-rdf-pagetype-prefix (concat planner-rdf-base "page-type-"))
101 (defvar planner-rdf-pagetype-day (concat planner-rdf-base "day-page"))
102 (defvar planner-rdf-pagetype-project (concat planner-rdf-base "topic-page"))
103 (defvar planner-rdf-pagetype-other (concat planner-rdf-base "other-page"))
105 ;; common match strings - tasks
106 (defvar planner-rdf-matchstr-ptaskids
107 "{{Tasks:\\([0-9]+\\)}}"
108 "Finds task ids of the form {{Tasks:<Number>}} in Planner tasks.")
109 (defvar planner-rdf-matchstr-ptask
110 "^#\\([A-C]\\)\\([0-9]*\\)\\s-*\\([_oXDCP]\\)\\s-*\\(.+\\)$"
111 "Finds a Planner task in a buffer.")
113 ;; common match strings - notes
114 (defvar planner-rdf-matchstr-pnote
115 "^.#\\([0-9]+\\)\\s-+\\(.+\\)"
116 "Finds the beginning of a Planner note in a buffer. Matches id.")
117 (defvar planner-rdf-matchstr-pnote1
118 "^\\(.+\\)\\s-+[0-9][0-9]:[0-9][0-9]\\s-+(\\[\\[[0-9]+.[0-9]+.[0-9]+#[0-9]+\\]\\])$"
119 "Note title with time and day page link. Match text.")
120 (defvar planner-rdf-matchstr-pnote2
121 "^\\(.+\\)\\s-+[0-9][0-9]:[0-9][0-9]\\s-+(\\[\\[.+#[0-9]+\\]\\])$"
122 "Note title with time and project page link. Match text.")
123 (defvar planner-rdf-matchstr-pnote3
124 "^\\(.+\\)\\s-+[0-9][0-9]:[0-9][0-9]$"
125 "Note title with time, no page link. Match text.")
126 (defvar planner-rdf-matchstr-pnote4
127 "^.+\\s-+[0-9][0-9]:[0-9][0-9]\\s-+(\\[\\[\\([0-9]+.[0-9]+.[0-9]+#[0-9]+\\)\\]\\])$"
128 "Note title with time and day page link. Match page link.")
129 (defvar planner-rdf-matchstr-pnote5
130 "^.+\\s-+[0-9][0-9]:[0-9][0-9]\\s-+(\\[\\[\\(.+#[0-9]+\\)\\]\\])$"
131 "Note title with time and project page link. Match page link.")
133 ;; common match strings - links
134 (defvar planner-rdf-matchstr-plink1
135 "\\[\\[\\([^[\n]+?\\)\\]\\[\\(.+?\\)\\]\\]"
136 "Link like [[<link>][<text>]]. Match link and text.")
137 (defvar planner-rdf-matchstr-plink2
138 "\\[\\[\\([^[\n]+?\\)\\]\\]"
139 "Link like [[<link>]]. Match link.")
141 (defvar planner-rdf-matchstr-pagenotelink
142 "\\(.+\\)#[0-9]+"
143 "Matches the page part of a page/note reference.")
145 (defvar planner-rdf-matchstr-pagenotelink1
146 "\\(.+\\)#note[0-9]+"
147 "Matches the page part of a page/note reference.")
149 ;; common match strings - tags
150 (defvar planner-rdf-matchstr-ptag
151 "{{\\(.+?\\)}}"
152 "Tag like {{<type>:<id>}}. Match everything.")
154 ;; hooks
155 (defvar planner-rdf-analysis-note-functions nil
156 "A hook called when the standard note content has been saved.
157 Hook functions must take one parameter, the vector with the note content.
158 Their return value is ignored. Output must be to the current buffer, and in
159 Turtle syntax.")
161 (defvar planner-rdf-analysis-task-functions nil
162 "A hook called when the standard task content has been saved.
163 Hook functions must take one parameter, the vector with the task content.
164 Their return value is ignored. Output must be to the current buffer, and in
165 Turtle syntax.")
167 (defvar planner-rdf-prolog-hook nil
168 "A hook called when the standard turtle file prolog has been saved.
169 Output must be to the current buffer, and in Turtle syntax.")
171 (defvar planner-rdf-epilog-hook nil
172 "A hook called when all standard content has been saved.
173 Output must be to the current buffer, and in Turtle syntax.")
176 (defun planner-rdf-curtz ()
177 "Return a string representation of the current time zone."
178 (let* ((tz (current-time-zone))
179 (secs (car tz))
180 (mins (/ (abs secs) 60))
181 (tzstr "")
182 (hours (/ mins 60))
183 (rmins (% mins 60)))
184 (if (< secs 0)
185 (setq tzstr "-")
186 (setq tzstr "+"))
187 (format "%s%02d:%02d" tzstr hours rmins)))
191 (defun planner-rdf-replace-sth (input replace with)
192 "Replace <replace> with <with> in <input>."
193 (let ((input2 input)
194 (start 0))
195 (while (not (null (string-match (regexp-opt (list replace)) input2 start)))
196 (setq input2 (replace-match with t t input2))
197 (setq start (match-end 0)))
198 input2))
200 (defun planner-rdf-get-taskid (taskdesc)
201 "Retrieve or generate a task id from the description."
202 (if (string-match planner-rdf-matchstr-ptaskids taskdesc)
203 (planner-match-string-no-properties 1 taskdesc)
204 (concat "-" (number-to-string(abs (sxhash taskdesc))))))
206 (defun planner-rdf-get-task-info ()
207 "Retrieve the task information from the current buffer."
208 (let ((results '()))
209 (goto-char (point-min))
210 (while (not (eobp))
211 (when (looking-at planner-rdf-matchstr-ptask)
212 (let ((info (planner-task-info-from-string
213 (buffer-name)
214 (match-string 0))))
215 (add-to-list
216 'results
217 (vector (buffer-name)
218 (planner-task-priority info)
219 (planner-task-status info)
220 (planner-task-date info)
221 (planner-task-plan info)
222 (planner-task-description info)
223 (planner-task-number info)
224 (planner-rdf-get-taskid (planner-task-description info))
225 ))))
226 (forward-line))
227 results))
229 (defun planner-rdf-get-note-info ()
230 "Retrieve the note information from the current buffer."
231 (interactive)
232 (let ((results '())(in-notes 'nil)
233 (curr-note-id 0)
234 (curr-note-headline "")
235 (curr-note-txt ""))
236 (goto-char (point-min))
237 (while (not (eobp))
238 (if (looking-at planner-rdf-matchstr-pnote)
239 (progn
240 (if (null in-notes)
241 (setq in-notes 't)
242 (progn ; save previous note
243 (add-to-list
244 'results
245 (vector (buffer-name)
246 curr-note-id
247 curr-note-headline
248 curr-note-txt))
249 ;; (planner-rdf-log (concat "Note " curr-note-id " " curr-note-headline))
250 (setq curr-note-txt "")
251 (setq curr-note-id 0)))
252 (setq curr-note-id (planner-match-string-no-properties 1))
253 (setq curr-note-headline (planner-match-string-no-properties 2))
254 ;; zur nächsten - sonst wird der Titel als Text mitgenommen
255 (forward-line)))
256 (if in-notes
257 (setq curr-note-txt (concat curr-note-txt (thing-at-point 'line))))
258 (forward-line))
259 ; save the last note if there
260 (if (and in-notes (> (string-to-number curr-note-id) 0))
261 (progn
262 (add-to-list
263 'results
264 (vector (buffer-name)
265 curr-note-id
266 curr-note-headline
267 curr-note-txt))
268 ;; (planner-rdf-log (concat "Note " curr-note-id " " curr-note-headline))
270 results))
272 (defun planner-rdf-insert-prolog ()
273 "Insert a file prolog, necessary schema references etc.
274 Runs the hook planner-rdf-prolog-hook at the end to allow
275 custom content."
277 (insert "<rdf:RDF\n"
278 "\txmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n"
279 "\txmlns:xsd=\"http://www.w3.org/2001/XMLSchema#\"\n"
280 "\txmlns:planner=\"http://www.rainervolz.de/schema/2004/11/planner-rdf#\"\n"
281 "\txmlns:rdfs=\"http://www.w3.org/2000/01/rdf-schema#\"\n"
282 "\txmlns=\"http://localhost/planner/\"\n"
283 "\txmlns:owl=\"http://www.w3.org/2002/07/owl#\"\n"
284 "\txml:base=\"http://localhost/planner/\"\n"
285 ">\n")
286 (run-hooks 'planner-rdf-prolog-hook))
288 (defun planner-rdf-insert-dcprolog ()
289 "Insert a file prolog, necessary schema references etc."
290 (insert
291 (concat "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n")
292 (concat "\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n")
293 ">\n"))
295 (defun planner-rdf-insert-epilog ()
296 "Insert a file epilog.
297 Runs the hook planner-rdf-epilog-hook at the end to allow
298 custom content."
299 (run-hooks 'planner-rdf-epilog-hook)
300 (insert "</rdf:RDF>\n"))
303 (defun planner-rdf-retrieve-links (id ctr input parent)
304 "Collect Planner-style links, [[link][text]] or [...]."
305 (let ((start 0)(results '()))
306 (while (not (null (string-match planner-rdf-matchstr-plink1 input start)))
307 (push (list
308 (match-string 1 input)
309 (match-string 2 input)
310 (concat id "-" (number-to-string ctr))
311 parent)
312 results)
313 (setq ctr (incf ctr))
314 (setq start (match-end 0)))
315 (setq start 0)
316 (while (not (null (string-match planner-rdf-matchstr-plink2 input start)))
317 (push (list
318 (match-string 1 input)
320 (concat id "-" (number-to-string ctr))
321 parent)
322 results)
323 (setq ctr (incf ctr))
324 (setq start (match-end 0)))
325 results))
327 (defun planner-rdf-retrieve-tags (id ctr input parent)
328 "Collect Planner-style tags, {{text:id}}."
329 (let ((start 0)(results '()))
330 (while (not (null (string-match planner-rdf-matchstr-ptag input start)))
331 (push (list
332 (match-string 1 input)
333 (concat id "-" (number-to-string ctr))
334 parent)
335 results)
336 (setq ctr (incf ctr))
337 (setq start (match-end 0)))
338 results))
340 (defun planner-rdf-replace-links (input)
341 "replace Planner-style links, [[link][text]] or [...], with text."
342 (while (not (null (string-match planner-rdf-matchstr-plink1 input 0)))
343 (setq input (replace-match (planner-match-string-no-properties 2 input) t t input)))
344 (while (not (null (string-match planner-rdf-matchstr-plink2 input 0)))
345 (setq input (replace-match "*link*" t t input)))
346 input)
348 (defun planner-rdf-link-type (uri)
349 "Retrieve the link protocol."
350 (if (string-match "\\(\\w+\\):.+" uri)
351 (match-string 1 uri)
352 "unknown"))
354 (defun planner-rdf-tag-id (tag)
355 "Retrieve the id part of a tag."
356 (if (string-match "\\w+:\\(.+\\)" tag)
357 (match-string 1 tag)
358 "unknown"))
360 (defun planner-rdf-escape-uri (uri)
361 "Apply various escapes to the URI."
362 (let* ((uri2 (planner-rdf-replace-sth uri "<" "\\u003c"))
363 (uri3 (planner-rdf-replace-sth uri2 ">" "\\u003e"))
364 (uri4 (planner-rdf-replace-sth uri3 "&" "&amp;")))
365 uri4))
367 (defun planner-rdf-escape-string (input)
368 "Apply various escapes to strings."
369 (let* ((input2 (planner-rdf-replace-sth input "\"" "\\u0022"))
370 (input3 (planner-rdf-replace-sth input2 "&" "&amp;"))
371 (input4 (planner-rdf-replace-sth input3 "<" "&lt;"))
372 (input5 (planner-rdf-replace-sth input4 ">" "&gt;"))
374 input5))
376 (defun planner-rdf-print-link (link)
377 "Output a RDF representation of a link.
378 If the link has no name, insert the defined unknown string."
379 (let ((lt (planner-rdf-link-type (elt link 0)))
380 (uri (elt link 0)))
381 (insert "<planner:Link rdf:about=\"" planner-rdf-link-prefix (elt link 2) "\">\n")
382 (if (not (null (elt link 1)))
383 (insert "\t<link-text>" (planner-rdf-escape-string(elt link 1)) "</link-text>\n")
384 (insert "\t<link-text>"
385 (planner-rdf-escape-string
386 (if (string= lt "unknown")
387 (substring uri 0 10)
388 (substring uri (+ (length lt) 3) 10)))
389 "</link-text>\n"))
390 (insert "\t<link-type>" lt "</link-type>\n")
391 (insert "\t<link-uri>"(planner-rdf-escape-uri uri) "</link-uri>\n")
392 (insert "</planner:Link>\n")))
394 (defun planner-rdf-print-link2 (link)
395 "Output a resource statement for a link."
396 (insert "\t<link rdf:resource=\"" planner-rdf-link-prefix (elt link 2) "\"/>\n"))
398 (defun planner-rdf-print-tag (tag)
399 "Output a RDF representation of a tag."
400 (insert "<planner:Tag rdf:about=\"" planner-rdf-tag-prefix (elt tag 1) "\">\n")
401 (insert "\t<tag-type>" (planner-rdf-link-type (elt tag 0)) "</tag-type>\n")
402 (insert "\t<tag-id>" (planner-rdf-escape-uri(planner-rdf-tag-id (elt tag 0))) "</tag-id>\n")
403 (insert "</planner:Tag>\n"))
405 (defun planner-rdf-print-tag2 (tag)
406 "Output a resource statement for a tag."
407 (insert "\t<tag rdf:resource=\"" planner-rdf-tag-prefix (elt tag 1) "\"/>\n"))
409 (defun planner-rdf-task-get-nice-description (input)
410 "Extract a nice, readable description.
411 Remove the task ids e.g., {{Tasks:XX}}."
412 (let ((pos (string-match planner-rdf-matchstr-ptaskids input))
413 (title ""))
414 (when (numberp pos)
415 (setq input (substring input 0 (decf pos))))
416 (setq title (planner-rdf-replace-links input))
417 (planner-rdf-replace-sth title "<nop>" "")
421 (defun planner-rdf-task (taskvector)
422 "Transform task information to turtle syntax.
423 Run the hook 'planner-rdf-task-functions' at the end
424 of the standard output to provide customised content."
425 ;; Generate a task-id
426 (let ((task-id)
427 (task-unique-id)
428 (linkes '())
429 (tags '()))
430 (setq task-id (elt taskvector 7))
431 ;; (setq task-unique-id (concat (elt taskvector 0) "-" task-id))
432 ;; (setq task-unique-id (concat
433 ;; (elt taskvector 0)
434 ;; "#"
435 ;; (elt taskvector 1)
436 ;; (elt taskvector 6)))
437 (setq task-unique-id (concat
438 (elt taskvector 0)
439 "-"
440 task-id))
441 (setq linkes (planner-rdf-retrieve-links
442 task-unique-id
444 (elt taskvector 5)
445 (concat planner-rdf-task-prefix task-unique-id)))
446 (mapcar 'planner-rdf-print-link linkes)
447 (setq tags (planner-rdf-retrieve-tags
448 task-unique-id
450 (elt taskvector 5)
451 (concat planner-rdf-task-prefix task-unique-id)))
452 (mapcar 'planner-rdf-print-tag tags)
453 (insert "<planner:Task rdf:about=\"" planner-rdf-task-prefix task-unique-id "\">\n")
454 (insert "\t<task-priority>" (elt taskvector 1) "</task-priority>\n")
455 (insert "\t<task-number>" (elt taskvector 6) "</task-number>\n")
456 (insert "\t<task-status>" (elt taskvector 2) "</task-status>\n")
457 (insert "\t<date>" (planner-rdf-replace-sth (elt taskvector 3) "." "-") "</date>\n")
458 (if (not (null (elt taskvector 4)))
459 (insert "\t<alias rdf:resource=\"" planner-rdf-task-prefix (elt taskvector 4) "-" task-id "\" />\n"))
460 (insert "\t<description>"
461 (planner-rdf-escape-string
462 (planner-rdf-task-get-nice-description
463 (elt taskvector 5)))
464 "</description>\n")
465 ;; if the task-id is negative, it is generated, not good for linking
466 (when (> (string-to-number task-id) -1)
467 (insert "\t<task-id>" task-id "</task-id>\n"))
468 (mapcar 'planner-rdf-print-link2 linkes)
469 (mapcar 'planner-rdf-print-tag2 tags)
470 (run-hook-with-args 'planner-rdf-analysis-task-functions taskvector)
471 (insert "</planner:Task>\n")))
473 (defun planner-rdf-note-get-nice-description (input)
474 "Extract a nice description, text only if possible."
475 (let ((title ""))
476 (cond
477 ;; title with time and day page link
478 ((string-match planner-rdf-matchstr-pnote1 input)
479 (setq title (planner-rdf-escape-string(planner-match-string-no-properties 1 input))))
480 ;; title with time and project page link
481 ((string-match planner-rdf-matchstr-pnote2 input)
482 (setq title (planner-rdf-escape-string(planner-match-string-no-properties 1 input))))
483 ;; title with time, no page link
484 ((string-match planner-rdf-matchstr-pnote3 input)
485 (setq title (planner-rdf-escape-string (planner-match-string-no-properties 1 input))))
486 ;; ok, take evrything
487 (t (setq title (planner-rdf-escape-string input))))
488 (setq title (planner-rdf-replace-links title))
489 (planner-rdf-replace-sth title "<nop>" "")
492 (defun planner-rdf-note-get-alias (input)
493 "Extract the alias page link from title string."
494 (let ((alias ""))
495 (cond
496 ;; title with time and day page link
497 ((string-match planner-rdf-matchstr-pnote4 input)
498 (setq alias (planner-rdf-escape-string(planner-match-string-no-properties 1 input))))
499 ;; title with time and project page link
500 ((string-match planner-rdf-matchstr-pnote5 input)
501 (setq alias (planner-rdf-escape-string(planner-match-string-no-properties 1 input))))
502 ;; ok, nothing there
503 (t (setq alias "")))
504 (planner-rdf-replace-sth alias "#" "-")))
506 (defun planner-rdf-note-get-plan (page input)
507 "Extract the alias page link from title string."
508 (let ((plan "")
509 (alias (planner-rdf-note-get-alias input)))
510 (cond
511 ;; there is something
512 ((string-match planner-rdf-matchstr-pagenotelink alias)
513 (setq plan (planner-rdf-escape-string(planner-match-string-no-properties 1 alias))))
514 ((string-match planner-rdf-matchstr-pagenotelink1 alias)
515 (setq plan (planner-rdf-escape-string(planner-match-string-no-properties 1 alias))))
516 ;; ok, nothing there
517 (t (setq plan page)))
518 plan))
520 (defun planner-rdf-note (notevector)
521 "Transform note information to RDF.
522 Run the hook 'planner-rdf-note-functions' at the end
523 of the standard output to provide customised content."
524 ;; Generate a note-id
525 (let ((note-id (concat (elt notevector 0) "-" (elt notevector 1)))
526 (title (planner-rdf-note-get-nice-description (elt notevector 2)))
527 (alias (planner-rdf-note-get-alias (elt notevector 2)))
528 (linksall '())
529 (tagsall '()))
530 ;; links from title and body
531 (let* ((linkst (planner-rdf-retrieve-links
532 note-id
534 title
535 (concat planner-rdf-note-prefix note-id)))
536 (linksb (planner-rdf-retrieve-links
537 note-id
538 (length linkst)
539 (elt notevector 3)
540 (concat planner-rdf-note-prefix note-id))))
541 (setq linksall (append linkst linksb))
542 (mapcar 'planner-rdf-print-link linksall))
543 (let* ((tagst (planner-rdf-retrieve-tags
544 note-id
546 title
547 (concat planner-rdf-note-prefix note-id)))
548 (tagsb (planner-rdf-retrieve-tags
549 note-id
550 (length tagst)
551 (elt notevector 3)
552 (concat planner-rdf-note-prefix note-id))))
553 (setq tagsall (append tagst tagsb))
554 (mapcar 'planner-rdf-print-tag tagsall))
555 (insert "<planner:Note rdf:about=\"" planner-rdf-note-prefix note-id "\">\n")
556 (insert "\t<note-id>" note-id "</note-id>\n")
557 ; find a date - first try buffer name
558 (if (string-match "^\\([0-9]+.[0-9]+.[0-9]+\\)$" (elt notevector 0))
559 (insert "\t<date>" (planner-rdf-replace-sth (elt notevector 0) "." "-") "</date>\n")
560 (progn
561 ; no luck, try title line
562 (if (string-match "(\\[\\[\\([0-9]+.[0-9]+.[0-9]+\\)#[0-9]+\\]\\])" (elt notevector 2))
563 (insert
564 "\t<date>"
565 (planner-rdf-replace-sth
566 (planner-match-string-no-properties 1 (elt notevector 2)) "." "-")
567 "</date>\n"))))
569 ;; find time
570 (if (string-match "\\s-+\\([0-9][0-9]:[0-9][0-9]\\)\\s-+\\|\\s-+\\([0-9][0-9]:[0-9][0-9]\\)$"
571 (elt notevector 2))
572 (insert
573 "\t<note-time>"
574 (if (planner-match-string-no-properties 1 (elt notevector 2))
575 (planner-match-string-no-properties 1 (elt notevector 2))
576 (planner-match-string-no-properties 2 (elt notevector 2)))
577 ":00</note-time>\n"))
578 (insert "\t<description>" title "</description>\n")
579 (when (not (string= "" alias))
580 (insert "\t<alias rdf:resource=\"" planner-rdf-note-prefix alias "\" />\n"))
581 (mapcar 'planner-rdf-print-link2 linksall)
582 (mapcar 'planner-rdf-print-tag2 tagsall)
583 (run-hook-with-args 'planner-rdf-analysis-note2turtle-functions notevector)
584 (insert "</planner:Note>\n")))
586 (defun planner-rdf-insert-page-info (name tasks notes)
587 "Create a page object."
588 (insert "<planner:Page rdf:about=\"" planner-rdf-page-prefix name "\">\n")
589 (let ((type (if (string-match "^[0-9]\\{4\\}[\\.\\-][0-9]\\{2\\}[\\.\\-][0-9]\\{2\\}" name)
590 planner-rdf-pagetype-day
591 planner-rdf-pagetype-project)))
592 (insert "\t<page-type rdf:resource=\"" type "\"/>\n")
594 (mapcar
595 (lambda (task)
596 (insert "\t<task rdf:resource=\""
597 planner-rdf-task-prefix
598 (elt task 0)
600 (elt task 7)
601 "\"/>\n"))
602 tasks)
603 (mapcar
604 (lambda (note)
605 (insert "\t<note rdf:resource=\""
606 planner-rdf-note-prefix
607 (concat (elt note 0) "-" (elt note 1))
608 "\"/>\n"))
609 notes)
610 (insert "</planner:Page>\n"))
612 (defun planner-rdf-metadata (file)
613 "Create Dublin Core metadata for the varous files involved."
614 ;; The source file
615 (let ((name (file-name-nondirectory file))
616 (rdfdir (file-name-as-directory planner-rdf-directory)))
617 (insert "<rdf:Description rdf:about=\"file:"
618 (expand-file-name file)
619 "\">\n")
620 (insert "\t<dc:Title>" (file-name-nondirectory file) "</dc:Title>\n")
621 (insert "\t<dc:Identifier>" planner-rdf-base "page-" name "</dc:Identifier>\n")
622 (insert "\t<dc:Format>text</dc:Format>\n")
623 (if (string-match "^[0-9]\\{4\\}[\\.\\-][0-9]\\{2\\}[\\.\\-][0-9]\\{2\\}" name)
624 (progn
625 (insert "\t<dc:Type>" planner-rdf-pagetype-day "</dc:Type>\n")
626 (insert
627 "\t<dc:Coverage>"
628 planner-rdf-base
629 "date/"
630 (planner-rdf-replace-sth name "." "-")
631 "</dc:Coverage>\n"))
632 (progn
633 (insert "\t<dc:Type>" planner-rdf-pagetype-project "</dc:Type>\n")
634 (insert "\t<dc:Coverage>"
635 planner-rdf-base
636 "topic/"
637 name
638 "</dc:Coverage>\n")))
639 (insert "</rdf:Description>\n")
640 ;; The HTML file
641 (insert "<rdf:Description rdf:about=\"file:"
642 (expand-file-name (concat rdfdir name ".html"))
643 "\">\n")
644 (insert "\t<dc:Title>" name "</dc:Title>\n")
645 (insert "\t<dc:Source>file:" file "</dc:Source>\n")
646 (insert "\t<dc:Format>text/html</dc:Format>\n")
647 (insert "</rdf:Description>\n")
648 ;; The OWL file
649 (insert "<rdf:Description rdf:about=\"file:"
650 (expand-file-name (concat rdfdir name ".owl"))
651 "\">\n")
652 (insert "\t<dc:Title>" name "</dc:Title>\n")
653 (insert "\t<dc:Source>file:" file "</dc:Source>\n")
654 (insert "\t<dc:Format>application/rdf+xml</dc:Format>\n")
655 (insert "</rdf:Description>\n")
659 ;;;###autoload
660 (defun planner-rdf-publish-file (file)
661 "Publish the file in RDF format, if called by PlannerMode.
662 Designed to be called via `muse-after-publish-hook'.
663 Non-Planner files, matching `muse-image-regexp' will be treated
664 differently. Currently they are simply ignored."
665 (interactive "f")
666 (when (eq major-mode 'planner-mode)
667 (if (string-match muse-image-regexp file)
668 (message (concat "planner-rdf: Ignored file " file))
669 (let ((tasks nil)
670 (notes nil))
671 (let ((mybuff (get-file-buffer file))
672 (opened nil))
673 (when (not mybuff)
674 (setq mybuff (find-file file))
675 (setq opened t))
676 (with-current-buffer mybuff
677 (setq tasks (planner-rdf-get-task-info)))
678 (with-current-buffer mybuff
679 (setq notes (planner-rdf-get-note-info)))
680 (if (and (not (buffer-modified-p mybuff)) opened)
681 (kill-buffer mybuff)))
682 (let ((mybuff2 (find-file
683 (expand-file-name
684 (concat (file-name-as-directory planner-rdf-directory)
685 (file-name-nondirectory file)
686 ".owl")))))
687 (with-current-buffer mybuff2
688 (erase-buffer)
689 (planner-rdf-insert-prolog)
690 (mapcar 'planner-rdf-task tasks)
691 (mapcar 'planner-rdf-note notes)
692 (planner-rdf-insert-page-info (file-name-nondirectory file) tasks notes)
693 (planner-rdf-insert-epilog)
694 (let ((backup-inhibited t))
695 (save-buffer)))
696 (kill-buffer mybuff2))
697 (let ((mybuff3 (find-file
698 (expand-file-name
699 (concat (file-name-as-directory planner-rdf-directory)
700 (file-name-nondirectory file)
701 ".rdf")))))
702 (with-current-buffer mybuff3
703 (erase-buffer)
704 (planner-rdf-insert-dcprolog)
705 (planner-rdf-metadata file)
706 (planner-rdf-insert-epilog)
707 (let ((backup-inhibited t))
708 (save-buffer)))
709 (kill-buffer mybuff3))
710 ))))
712 ;;;###autoload
713 (defun planner-rdf-publish-index ()
714 "Create an index for the .rdf files.
715 Will be called via `muse-after-publish-hook'.
716 Creates index.rdf, a rdf:bag, with all existing .rdf files as
717 items."
718 (interactive)
719 (let ((rdf-index (expand-file-name
720 (concat
721 (file-name-as-directory planner-rdf-directory)
722 "index.rdf"))))
723 (when (file-exists-p rdf-index)
724 (delete-file rdf-index))
725 (with-temp-buffer
726 (erase-buffer)
727 (message "Creating RDF index...")
728 (insert "<?xml version=\"1.0\"?>\n")
729 (insert "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n")
730 (insert "\t<rdf:Bag>\n")
731 (let ((rdf-files (directory-files
732 (file-name-as-directory planner-rdf-directory)
734 "\\.rdf$"
735 nil)))
736 (insert
737 (mapconcat
738 (lambda (name)
739 (concat
740 "\t\t<rdf:li rdf:resource=\"file:"
741 name
742 "\"/>"))
743 rdf-files
744 "\n")))
745 (insert "\n\t</rdf:Bag>\n")
746 (insert "</rdf:RDF>\n")
747 (let ((backup-inhibited t))
748 (write-file rdf-index nil)))))
751 (provide 'planner-rdf)