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