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