;;; org-icalendar.el --- iCalendar export for Org-mode
-;; Copyright (C) 2004-2011 Free Software Foundation, Inc.
+;; Copyright (C) 2004-2012 Free Software Foundation, Inc.
;; Author: Carsten Dominik <carsten at orgmode dot org>
;; Keywords: outlines, hypermedia, calendar, wp
;; Homepage: http://orgmode.org
-;; Version: 7.4
;;
;; This file is part of GNU Emacs.
;;
(require 'org-exp)
-(eval-when-compile
- (require 'cl))
+(eval-when-compile (require 'cl))
(declare-function org-bbdb-anniv-export-ical "org-bbdb" nil)
- The alarm will go off N minutes before the event
- only a DISPLAY action is defined."
:group 'org-export-icalendar
+ :version "24.1"
:type 'integer)
(defcustom org-icalendar-combined-name "OrgMode"
(defcustom org-icalendar-combined-description nil
"Calendar description for the combined iCalendar (all agenda files)."
:group 'org-export-icalendar
+ :version "24.1"
:type 'string)
(defcustom org-icalendar-use-plain-timestamp t
:group 'org-export-icalendar
:type 'boolean)
+(defcustom org-icalendar-honor-noexport-tag nil
+ "Non-nil means don't export entries with a tag in `org-export-exclude-tags'."
+ :group 'org-export-icalendar
+ :version "24.1"
+ :type 'boolean)
+
(defcustom org-icalendar-use-deadline '(event-if-not-todo todo-due)
"Contexts where iCalendar export should use a deadline time stamp.
This is a list with several symbols in it. Valid symbol are:
(defcustom org-icalendar-timezone (getenv "TZ")
"The time zone string for iCalendar export.
-When nil of the empty string, use the abbreviation retrieved from Emacs."
+When nil or the empty string, use output from \(current-time-zone\)."
:group 'org-export-icalendar
:type '(choice
(const :tag "Unspecified" nil)
(string :tag "Time zone")))
-(defcustom org-icalendar-use-UTC-date-time ()
- "Non-nil force the use of the universal time for iCalendar DATE-TIME.
-The iCalendar DATE-TIME can be expressed with local time or universal Time,
-universal time could be more compatible with some external tools."
+;; Backward compatibility with previous variable
+(defvar org-icalendar-use-UTC-date-time nil)
+(defcustom org-icalendar-date-time-format
+ (if org-icalendar-use-UTC-date-time
+ ":%Y%m%dT%H%M%SZ"
+ ":%Y%m%dT%H%M%S")
+ "Format-string for exporting icalendar DATE-TIME.
+See `format-time-string' for a full documentation. The only
+difference is that `org-icalendar-timezone' is used for %Z.
+
+Interesting value are:
+ - \":%Y%m%dT%H%M%S\" for local time
+ - \";TZID=%Z:%Y%m%dT%H%M%S\" for local time with explicit timezone
+ - \":%Y%m%dT%H%M%SZ\" for time expressed in Universal Time"
+
:group 'org-export-icalendar
- :type 'boolean)
+ :version "24.1"
+ :type '(choice
+ (const :tag "Local time" ":%Y%m%dT%H%M%S")
+ (const :tag "Explicit local time" ";TZID=%Z:%Y%m%dT%H%M%S")
+ (const :tag "Universal time" ":%Y%m%dT%H%M%SZ")
+ (string :tag "Explicit format")))
+
+(defun org-icalendar-use-UTC-date-timep ()
+ (char-equal (elt org-icalendar-date-time-format
+ (1- (length org-icalendar-date-time-format))) ?Z))
;;; iCalendar export
If COMBINE is non-nil, combine all calendar entries into a single large
file and store it under the name `org-combined-agenda-icalendar-file'."
(save-excursion
- (org-prepare-agenda-buffers files)
+ (org-agenda-prepare-buffers files)
(let* ((dir (org-export-directory
:ical (list :publishing-directory
org-export-publishing-directory)))
(let ((standard-output ical-buffer))
(if combine
(and (not started) (setq started t)
- (org-start-icalendar-file org-icalendar-combined-name))
- (org-start-icalendar-file category))
- (org-print-icalendar-entries combine)
+ (org-icalendar-start-file org-icalendar-combined-name))
+ (org-icalendar-start-file category))
+ (org-icalendar-print-entries combine)
(when (or (and combine (not files)) (not combine))
(when (and combine org-icalendar-include-bbdb-anniversaries)
(require 'org-bbdb)
(org-bbdb-anniv-export-ical))
- (org-finish-icalendar-file)
+ (org-icalendar-finish-file)
(set-buffer ical-buffer)
(run-hooks 'org-before-save-iCalendar-file-hook)
(save-buffer)
(run-hooks 'org-after-save-iCalendar-file-hook)
- (and (boundp 'org-wait) (numberp org-wait) (sit-for org-wait))
- ))))
+ (and (boundp 'org-wait) (numberp org-wait) (sit-for org-wait))))))
(org-release-buffers org-agenda-new-buffers))))
(defvar org-before-save-iCalendar-file-hook nil
the iCalendar file.")
(defvar org-agenda-default-appointment-duration) ; defined in org-agenda.el
-(defun org-print-icalendar-entries (&optional combine)
+(defun org-icalendar-print-entries (&optional combine)
"Print iCalendar entries for the current Org-mode file to `standard-output'.
When COMBINE is non nil, add the category to each line."
(require 'org-agenda)
(let ((re1 (concat org-ts-regexp "\\|<%%([^>\n]+>"))
(re2 (concat "--?-?\\(" org-ts-regexp "\\)"))
- (dts (org-ical-ts-to-string
+ (dts (org-icalendar-ts-to-string
(format-time-string (cdr org-time-stamp-formats) (current-time))
"DTSTART"))
hd ts ts2 state status (inc t) pos b sexp rrule
- scheduledp deadlinep todo prefix due start
- tmp pri categories location summary desc uid alarm
+ scheduledp deadlinep todo prefix due start tags
+ tmp pri categories location summary desc uid alarm alarm-time
(sexp-buffer (get-buffer-create "*ical-tmp*")))
(org-refresh-category-properties)
(save-excursion
(throw :skip nil)))
(setq pos (match-beginning 0)
ts (match-string 0)
+ tags (org-get-tags-at)
inc t
hd (condition-case nil
(org-icalendar-cleanup-string
(org-id-get-create)
(or (org-id-get) (org-id-new)))
categories (org-export-get-categories)
+ alarm-time (org-entry-get nil "APPT_WARNTIME")
+ alarm-time (if alarm-time (string-to-number alarm-time) 0)
alarm ""
deadlinep nil scheduledp nil)
+ (setq tmp (buffer-substring (max (point-min) (- pos org-ds-keyword-length)) pos)
+ deadlinep (string-match org-deadline-regexp tmp)
+ scheduledp (string-match org-scheduled-regexp tmp)
+ todo (org-get-todo-state))
+ ;; donep (org-entry-is-done-p)
(if (looking-at re2)
(progn
(goto-char (match-end 0))
(setq ts2 (match-string 1)
inc (not (string-match "[0-9]\\{1,2\\}:[0-9][0-9]" ts2))))
- (setq tmp (buffer-substring (max (point-min)
- (- pos org-ds-keyword-length))
- pos)
- ts2 (if (string-match "[0-9]\\{1,2\\}:[0-9][0-9]-\\([0-9]\\{1,2\\}:[0-9][0-9]\\)" ts)
+ (setq ts2 (if (string-match "[0-9]\\{1,2\\}:[0-9][0-9]-\\([0-9]\\{1,2\\}:[0-9][0-9]\\)" ts)
(progn
(setq inc nil)
(replace-match "\\1" t nil ts))
- ts)
- deadlinep (string-match org-deadline-regexp tmp)
- scheduledp (string-match org-scheduled-regexp tmp)
- todo (org-get-todo-state)
- ;; donep (org-entry-is-done-p)
- ))
+ ts)))
(when (and (not org-icalendar-use-plain-timestamp)
(not deadlinep) (not scheduledp))
(throw :skip t))
+ ;; don't export entries with a :noexport: tag
+ (when (and org-icalendar-honor-noexport-tag
+ (delq nil (mapcar (lambda(x)
+ (member x org-export-exclude-tags)) tags)))
+ (throw :skip t))
(when (and
deadlinep
(if todo
(if (or (string-match org-tr-regexp hd)
(string-match org-ts-regexp hd))
(setq hd (replace-match "" t t hd)))
- (if (string-match "\\+\\([0-9]+\\)\\([dwmy]\\)>" ts)
+ (if (string-match "\\+\\([0-9]+\\)\\([hdwmy]\\)>" ts)
(setq rrule
(concat "\nRRULE:FREQ="
(cdr (assoc
(match-string 2 ts)
- '(("d" . "DAILY")("w" . "WEEKLY")
+ '(("h" . "HOURLY")("d" . "DAILY")("w" . "WEEKLY")
("m" . "MONTHLY")("y" . "YEARLY"))))
";INTERVAL=" (match-string 1 ts)))
(setq rrule ""))
;; (c) only a DISPLAY action is defined.
;; [ESF]
(let ((t1 (ignore-errors (org-parse-time-string ts 'nodefault))))
- (if (and (> org-icalendar-alarm-time 0)
+ (if (and (or (> alarm-time 0) (> org-icalendar-alarm-time 0))
(car t1) (nth 1 t1) (nth 2 t1))
- (setq alarm (format "\nBEGIN:VALARM\nACTION:DISPLAY\nDESCRIPTION:%s\nTRIGGER:-P0D0H%dM0S\nEND:VALARM" summary org-icalendar-alarm-time))
- (setq alarm ""))
- )
+ (setq alarm (format "\nBEGIN:VALARM\nACTION:DISPLAY\nDESCRIPTION:%s\nTRIGGER:-P0DT0H%dM0S\nEND:VALARM"
+ summary (or alarm-time org-icalendar-alarm-time)))
+ (setq alarm "")))
(if (string-match org-bracket-link-regexp summary)
(setq summary
(replace-match (if (match-end 3)
(if scheduledp (setq summary (concat "S: " summary)))
(if (string-match "\\`<%%" ts)
(with-current-buffer sexp-buffer
- (insert (substring ts 1 -1) " " summary "\n"))
+ (let ((entry (substring ts 1 -1)))
+ (put-text-property 0 1 'uid
+ (concat " " prefix uid) entry)
+ (insert entry " " summary "\n")))
(princ (format "BEGIN:VEVENT
UID: %s
%s
CATEGORIES:%s%s
END:VEVENT\n"
(concat prefix uid)
- (org-ical-ts-to-string ts "DTSTART")
- (org-ical-ts-to-string ts2 "DTEND" inc)
+ (org-icalendar-ts-to-string ts "DTSTART")
+ (org-icalendar-ts-to-string ts2 "DTEND" inc)
rrule summary
(if (and desc (string-match "\\S-" desc))
(concat "\nDESCRIPTION: " desc) "")
due (and (member 'todo-due org-icalendar-use-deadline)
(org-entry-get nil "DEADLINE"))
start (and (member 'todo-start org-icalendar-use-scheduled)
- (org-entry-get nil "SCHEDULED"))
+ (org-entry-get nil "SCHEDULED"))
categories (org-export-get-categories)
uid (if org-icalendar-store-UID
(org-id-get-create)
(or (org-id-get) (org-id-new))))
- (and due (setq due (org-ical-ts-to-string due "DUE")))
- (and start (setq start (org-ical-ts-to-string start "DTSTART")))
+ (and due (setq due (org-icalendar-ts-to-string due "DUE")))
+ (and start (setq start (org-icalendar-ts-to-string start "DTSTART")))
(if (string-match org-bracket-link-regexp hd)
(setq hd (replace-match (if (match-end 3) (match-string 3 hd)
(if (not s)
nil
(if is-body
- (let ((re (concat "\\(" org-drawer-regexp "\\)[^\000]*?:END:.*\n?"))
- (re2 (concat "^[ \t]*" org-keyword-time-regexp ".*\n?")))
- (while (string-match re s) (setq s (replace-match "" t t s)))
- (while (string-match re2 s) (setq s (replace-match "" t t s))))
+ (let ((re (concat "\\(" org-drawer-regexp "\\)[^\000]*?:END:.*\n?"))
+ (re2 (concat "^[ \t]*" org-keyword-time-regexp ".*\n?")))
+ (while (string-match re s) (setq s (replace-match "" t t s)))
+ (while (string-match re2 s) (setq s (replace-match "" t t s))))
(setq s (replace-regexp-in-string "[[:space:]]+" " " s)))
(let ((start 0))
(while (string-match "\\([,;]\\)" s start)
(when (string-match "[;,:]" s) (setq s (concat "\"" s "\"")))
s))
-(defun org-start-icalendar-file (name)
+(defun org-icalendar-start-file (name)
"Start an iCalendar file by inserting the header."
(let ((user user-full-name)
(name (or name "unknown"))
X-WR-CALDESC:%s
CALSCALE:GREGORIAN\n" name user timezone description))))
-(defun org-finish-icalendar-file ()
+(defun org-icalendar-finish-file ()
"Finish an iCalendar file by inserting the END statement."
(princ "END:VCALENDAR\n"))
-(defun org-ical-ts-to-string (s keyword &optional inc)
+(defun org-icalendar-ts-to-string (s keyword &optional inc)
"Take a time string S and convert it to iCalendar format.
KEYWORD is added in front, to make a complete line like DTSTART....
When INC is non-nil, increase the hour by two (if time string contains
(setq h (+ 2 h)))
(setq d (1+ d))))
(setq time (encode-time s mi h d m y)))
- (setq fmt (if have-time (if org-icalendar-use-UTC-date-time
- ":%Y%m%dT%H%M%SZ"
- ":%Y%m%dT%H%M%S")
- ";VALUE=DATE:%Y%m%d"))
- (concat keyword (format-time-string fmt time
- (and org-icalendar-use-UTC-date-time
+ (setq fmt (if have-time
+ (replace-regexp-in-string "%Z"
+ org-icalendar-timezone
+ org-icalendar-date-time-format)
+ ";VALUE=DATE:%Y%m%d"))
+ (concat keyword (format-time-string fmt time
+ (and (org-icalendar-use-UTC-date-timep)
have-time))))))
(provide 'org-icalendar)
+;; Local variables:
+;; generated-autoload-file: "org-loaddefs.el"
+;; End:
+
;;; org-icalendar.el ends here