update packages

This commit is contained in:
2025-06-22 17:08:08 +02:00
parent 54e5633369
commit 16a0a6db93
558 changed files with 68349 additions and 26568 deletions

View File

@@ -1,11 +1,11 @@
;;; ox-icalendar.el --- iCalendar Back-End for Org Export Engine -*- lexical-binding: t; -*-
;;; ox-icalendar.el --- iCalendar Backend for Org Export Engine -*- lexical-binding: t; -*-
;; Copyright (C) 2004-2023 Free Software Foundation, Inc.
;; Copyright (C) 2004-2025 Free Software Foundation, Inc.
;; Author: Carsten Dominik <carsten.dominik@gmail.com>
;; Nicolas Goaziou <mail@nicolasgoaziou.fr>
;; Maintainer: Nicolas Goaziou <mail@nicolasgoaziou.fr>
;; Keywords: outlines, hypermedia, calendar, wp
;; Maintainer: Jack Kamm <jackkamm@gmail.com>
;; Keywords: outlines, hypermedia, calendar, text
;; URL: https://orgmode.org
;; This file is part of GNU Emacs.
@@ -25,7 +25,7 @@
;;; Commentary:
;;
;; This library implements an iCalendar back-end for Org generic
;; This library implements an iCalendar backend for Org generic
;; exporter. See Org manual for more information.
;;
;; It is expected to conform to RFC 5545.
@@ -48,7 +48,7 @@
;;; User-Configurable Variables
(defgroup org-export-icalendar nil
"Options specific for iCalendar export back-end."
"Options specific for iCalendar export backend."
:tag "Org Export iCalendar"
:group 'org-export)
@@ -96,7 +96,7 @@ and will fire at the event start."
(defcustom org-icalendar-exclude-tags nil
"Tags that exclude a tree from export.
This variable allows specifying different exclude tags from other
back-ends. It can also be set with the ICALENDAR_EXCLUDE_TAGS
backends. It can also be set with the ICALENDAR_EXCLUDE_TAGS
keyword."
:group 'org-export-icalendar
:type '(repeat (string :tag "Tag")))
@@ -168,8 +168,9 @@ This is a list with possibly several symbols in it. Valid symbols are:
`todo-start'
Scheduling time stamps in TODO entries become start date. Some
calendar applications show TODO entries only after that date."
Scheduling time stamps in TODO entries become start date. (See
also `org-icalendar-todo-unscheduled-start', which controls the
start date for TODO entries without a scheduling time stamp)"
:group 'org-export-icalendar
:type
'(set :greedy t
@@ -221,7 +222,8 @@ Valid values are:
nil don't include any task.
t include tasks that are not in DONE state.
`unblocked' include all TODO items that are not blocked.
`all' include both done and not done items."
`all' include both done and not done items.
\\(\"TODO\" ...) include specific TODO keywords."
:group 'org-export-icalendar
:type '(choice
(const :tag "None" nil)
@@ -231,6 +233,38 @@ t include tasks that are not in DONE state.
(repeat :tag "Specific TODO keywords"
(string :tag "Keyword"))))
(defcustom org-icalendar-todo-unscheduled-start 'recurring-deadline-warning
"Exported start date of unscheduled TODOs.
If `org-icalendar-use-scheduled' contains `todo-start' and a task
has a \"SCHEDULED\" timestamp, that is always used as the start
date. Otherwise, this variable controls whether a start date is
exported and what its value is.
Note that the iCalendar spec RFC 5545 does not generally require
tasks to have a start date, except for repeating tasks which do
require a start date. However some iCalendar programs ignore the
requirement for repeating tasks, and allow repeating deadlines
without a matching start date.
This variable has no effect when `org-icalendar-include-todo' is nil.
Valid values are:
`recurring-deadline-warning' If deadline repeater present,
use `org-deadline-warning-days' as start.
`deadline-warning' If deadline present,
use `org-deadline-warning-days' as start.
`current-datetime' Use the current date-time as start.
nil Never add a start time for unscheduled tasks."
:group 'org-export-icalendar
:type '(choice
(const :tag "Warning days if deadline recurring" recurring-deadline-warning)
(const :tag "Warning days if deadline present" deadline-warning)
(const :tag "Now" current-datetime)
(const :tag "No start date" nil))
:package-version '(Org . "9.7")
:safe #'symbolp)
(defcustom org-icalendar-include-bbdb-anniversaries nil
"Non-nil means a combined iCalendar file should include anniversaries.
The anniversaries are defined in the BBDB database."
@@ -297,6 +331,32 @@ Interesting value are:
(const :tag "Universal time" ":%Y%m%dT%H%M%SZ")
(string :tag "Explicit format")))
(defcustom org-icalendar-ttl nil
"Time to live for the exported calendar.
Subscribing clients to the exported ics file can derive the time
interval to read the file again from the server. One example of such
client is Nextcloud calendar, which respects the setting of
X-PUBLISHED-TTL in ICS files. Setting `org-icalendar-ttl' to \"PT1H\"
would advise a server to reload the file every hour.
See https://icalendar.org/iCalendar-RFC-5545/3-8-2-5-duration.html
for a complete description of possible specifications of this
option. For example, \"PT1H\" stands for 1 hour and
\"PT0H27M34S\" stands for 0 hours, 27 minutes and 34 seconds.
The default value is nil, which means no such option is set in
the ICS file. This option can also be set on a per-document basis
with the ICAL-TTL export keyword."
:group 'org-export-icalendar
:type '(choice
(const :tag "No refresh period" nil)
(const :tag "One hour" "PT1H")
(const :tag "One day" "PT1D")
(const :tag "One week" "PT7D")
(string :tag "Other"))
:package-version '(Org . "9.7"))
(defvar org-icalendar-after-save-hook nil
"Hook run after an iCalendar file has been saved.
This hook is run with the name of the file as argument. A good
@@ -305,7 +365,7 @@ re-read the iCalendar file.")
;;; Define Back-End
;;; Define Backend
(org-export-define-derived-backend 'icalendar 'ascii
:translate-alist '((clock . nil)
@@ -334,7 +394,8 @@ re-read the iCalendar file.")
(:icalendar-use-deadline nil nil org-icalendar-use-deadline)
(:icalendar-use-scheduled nil nil org-icalendar-use-scheduled)
(:icalendar-scheduled-summary-prefix nil nil org-icalendar-scheduled-summary-prefix)
(:icalendar-deadline-summary-prefix nil nil org-icalendar-deadline-summary-prefix))
(:icalendar-deadline-summary-prefix nil nil org-icalendar-deadline-summary-prefix)
(:icalendar-ttl "ICAL-TTL" nil org-icalendar-ttl))
:filters-alist
'((:filter-headline . org-icalendar-clear-blank-lines))
:menu-entry
@@ -540,20 +601,31 @@ or subject for the event."
;; line, real contents must be split at 74 chars.
(while (< (setq chunk-end (+ chunk-start 74)) len)
(setq folded-line
(concat folded-line "\r\n "
(concat folded-line "\n "
(substring line chunk-start chunk-end))
chunk-start chunk-end))
(concat folded-line "\r\n " (substring line chunk-start))))))
(org-split-string s "\n") "\r\n")))
(concat folded-line "\n " (substring line chunk-start))))))
(org-split-string s "\n") "\n")))
(defun org-icalendar--post-process-file (file)
"Post-process the exported iCalendar FILE.
Converts line endings to dos-style CRLF as per RFC 5545, then
runs `org-icalendar-after-save-hook'."
(with-temp-buffer
(insert-file-contents file)
(let ((coding-system-for-write (coding-system-change-eol-conversion
last-coding-system-used 'dos)))
(write-region nil nil file)))
(run-hook-with-args 'org-icalendar-after-save-hook file)
nil)
;;; Filters
(defun org-icalendar-clear-blank-lines (headline _back-end _info)
(defun org-icalendar-clear-blank-lines (headline _backend _info)
"Remove blank lines in HEADLINE export.
HEADLINE is a string representing a transcoded headline.
BACK-END and INFO are ignored."
BACKEND and INFO are ignored."
(replace-regexp-in-string "^\\(?:[ \t]*\n\\)+" "" headline))
@@ -591,20 +663,22 @@ inlinetask within the section."
(if (eq type 'inlinetask)
(cons 'org-data (cons nil (org-element-contents entry)))
(let ((first (car (org-element-contents entry))))
(and (eq (org-element-type first) 'section)
(and (org-element-type-p first 'section)
(cons 'org-data
(cons nil (org-element-contents first))))))))
(concat
(let ((todo-type (org-element-property :todo-type entry))
(uid (or (org-element-property :ID entry) (org-id-new)))
(summary (org-icalendar-cleanup-string
(or (org-element-property :SUMMARY entry)
(org-export-data
(org-element-property :title entry) info))))
(loc (org-icalendar-cleanup-string
(org-export-get-node-property
:LOCATION entry
(org-property-inherit-p "LOCATION"))))
(or
(let ((org-property-separators '(("SUMMARY" . "\n"))))
(org-entry-get entry "SUMMARY" 'selective))
(org-export-data
(org-element-property :title entry) info))))
(loc
(let ((org-property-separators '(("LOCATION" . "\n"))))
(org-icalendar-cleanup-string
(org-entry-get entry "LOCATION" 'selective))))
(class (org-icalendar-cleanup-string
(org-export-get-node-property
:CLASS entry
@@ -613,7 +687,8 @@ inlinetask within the section."
;; (headline) or contents (inlinetask).
(desc
(org-icalendar-cleanup-string
(or (org-element-property :DESCRIPTION entry)
(or (let ((org-property-separators '(("DESCRIPTION" . "\n"))))
(org-entry-get entry "DESCRIPTION" 'selective))
(let ((contents (org-export-data inside info)))
(cond
((not (org-string-nw-p contents)) nil)
@@ -683,13 +758,16 @@ inlinetask within the section."
;; so, call `org-icalendar--vtodo' to transcode it into
;; a "VTODO" component.
(when (and todo-type
(cl-case (plist-get info :icalendar-include-todo)
(all t)
(unblocked
(pcase (plist-get info :icalendar-include-todo)
(`all t)
(`unblocked
(and (eq type 'headline)
(not (org-icalendar-blocked-headline-p
entry info))))
((t) (eq todo-type 'todo))))
entry info))))
;; unfinished
(`t (eq todo-type 'todo))
((and (pred listp) kwd-list)
(member (org-element-property :todo-keyword entry) kwd-list))))
(org-icalendar--vtodo entry uid summary loc desc cat tz class))
;; Diary-sexp: Collect every diary-sexp element within ENTRY
;; and its title, and transcode them. If ENTRY is
@@ -720,6 +798,15 @@ inlinetask within the section."
;; Don't forget components from inner entries.
contents))))
(defun org-icalendar--rrule (unit value)
"Format RRULE icalendar entry for UNIT frequency and VALUE interval.
UNIT is a symbol `hour', `day', `week', `month', or `year'."
(format "RRULE:FREQ=%s;INTERVAL=%d"
(cl-case unit
(hour "HOURLY") (day "DAILY") (week "WEEKLY")
(month "MONTHLY") (year "YEARLY"))
value))
(defun org-icalendar--vevent
(entry timestamp uid summary location description categories timezone class)
"Create a VEVENT component.
@@ -732,35 +819,50 @@ venue for the event. DESCRIPTION provides the complete
description of the event. CATEGORIES defines the categories the
event belongs to. TIMEZONE specifies a time zone for this event
only. CLASS contains the visibility attribute. Three of them
(\"PUBLIC\", \"CONFIDENTIAL\", and \"PRIVATE\") are predefined, others
\\(\"PUBLIC\", \"CONFIDENTIAL\", and \"PRIVATE\") are predefined, others
should be treated as \"PRIVATE\" if they are unknown to the iCalendar server.
Return VEVENT component as a string."
(org-icalendar-fold-string
(if (eq (org-element-property :type timestamp) 'diary)
(org-icalendar-transcode-diary-sexp
(org-element-property :raw-value timestamp) uid summary)
(concat "BEGIN:VEVENT\n"
(org-icalendar-dtstamp) "\n"
"UID:" uid "\n"
(org-icalendar-convert-timestamp timestamp "DTSTART" nil timezone) "\n"
(org-icalendar-convert-timestamp timestamp "DTEND" t timezone) "\n"
;; RRULE.
(when (org-element-property :repeater-type timestamp)
(format "RRULE:FREQ=%s;INTERVAL=%d\n"
(cl-case (org-element-property :repeater-unit timestamp)
(hour "HOURLY") (day "DAILY") (week "WEEKLY")
(month "MONTHLY") (year "YEARLY"))
(org-element-property :repeater-value timestamp)))
"SUMMARY:" summary "\n"
(and (org-string-nw-p location) (format "LOCATION:%s\n" location))
(and (org-string-nw-p class) (format "CLASS:%s\n" class))
(and (org-string-nw-p description)
(format "DESCRIPTION:%s\n" description))
"CATEGORIES:" categories "\n"
;; VALARM.
(org-icalendar--valarm entry timestamp summary)
"END:VEVENT"))))
(if (eq (org-element-property :type timestamp) 'diary)
(org-icalendar-transcode-diary-sexp
(org-element-property :raw-value timestamp) uid summary)
(concat "BEGIN:VEVENT\n"
(org-icalendar-dtstamp) "\n"
"UID:" uid "\n"
(org-icalendar-convert-timestamp timestamp "DTSTART" nil timezone) "\n"
(org-icalendar-convert-timestamp timestamp "DTEND" t timezone) "\n"
;; RRULE.
(when (org-element-property :repeater-type timestamp)
(concat (org-icalendar--rrule
(org-element-property :repeater-unit timestamp)
(org-element-property :repeater-value timestamp))
"\n"))
"SUMMARY:" summary "\n"
(and (org-string-nw-p location) (format "LOCATION:%s\n" location))
(and (org-string-nw-p class) (format "CLASS:%s\n" class))
(and (org-string-nw-p description)
(format "DESCRIPTION:%s\n" description))
"CATEGORIES:" categories "\n"
;; VALARM.
(org-icalendar--valarm entry timestamp summary)
"END:VEVENT\n")))
(defun org-icalendar--repeater-type (elem)
"Return ELEM's repeater-type if supported, else warn and return nil."
(let ((repeater-value (org-element-property :repeater-value elem))
(repeater-type (org-element-property :repeater-type elem)))
(cond
((not (and repeater-type
repeater-value
(> repeater-value 0)))
nil)
;; TODO Add catch-up to supported repeaters (use EXDATE to implement)
((not (memq repeater-type '(cumulate)))
(org-display-warning
(format "Repeater-type %s not currently supported by iCalendar export"
(symbol-name repeater-type)))
nil)
(repeater-type))))
(defun org-icalendar--vtodo
(entry uid summary location description categories timezone class)
@@ -769,51 +871,125 @@ Return VEVENT component as a string."
ENTRY is either a headline or an inlinetask element. UID is the
unique identifier for the task. SUMMARY defines a short summary
or subject for the task. LOCATION defines the intended venue for
the task. DESCRIPTION provides the complete description of the
task. CATEGORIES defines the categories the task belongs to.
TIMEZONE specifies a time zone for this TODO only.
the task. CLASS sets the task class (e.g. confidential). DESCRIPTION
provides the complete description of the task. CATEGORIES defines the
categories the task belongs to. TIMEZONE specifies a time zone for
this TODO only.
Return VTODO component as a string."
(let ((start (or (and (memq 'todo-start org-icalendar-use-scheduled)
(org-element-property :scheduled entry))
;; If we can't use a scheduled time for some
;; reason, start task now.
(let ((now (decode-time)))
(list 'timestamp
(list :type 'active
:minute-start (nth 1 now)
:hour-start (nth 2 now)
:day-start (nth 3 now)
:month-start (nth 4 now)
:year-start (nth 5 now)))))))
(org-icalendar-fold-string
(concat "BEGIN:VTODO\n"
"UID:TODO-" uid "\n"
(org-icalendar-dtstamp) "\n"
(org-icalendar-convert-timestamp start "DTSTART" nil timezone) "\n"
(and (memq 'todo-due org-icalendar-use-deadline)
(org-element-property :deadline entry)
(concat (org-icalendar-convert-timestamp
(org-element-property :deadline entry) "DUE" nil timezone)
"\n"))
"SUMMARY:" summary "\n"
(and (org-string-nw-p location) (format "LOCATION:%s\n" location))
(and (org-string-nw-p class) (format "CLASS:%s\n" class))
(and (org-string-nw-p description)
(format "DESCRIPTION:%s\n" description))
"CATEGORIES:" categories "\n"
"SEQUENCE:1\n"
(format "PRIORITY:%d\n"
(let ((pri (or (org-element-property :priority entry)
org-priority-default)))
(floor (- 9 (* 8. (/ (float (- org-priority-lowest pri))
(- org-priority-lowest
org-priority-highest)))))))
(format "STATUS:%s\n"
(if (eq (org-element-property :todo-type entry) 'todo)
"NEEDS-ACTION"
"COMPLETED"))
"END:VTODO"))))
(let* ((sc (and (memq 'todo-start org-icalendar-use-scheduled)
(org-element-property :scheduled entry)))
(dl (and (memq 'todo-due org-icalendar-use-deadline)
(org-element-property :deadline entry)))
(sc-repeat-p (org-icalendar--repeater-type sc))
(dl-repeat-p (org-icalendar--repeater-type dl))
(repeat-value (or (org-element-property :repeater-value sc)
(org-element-property :repeater-value dl)))
(repeat-unit (or (org-element-property :repeater-unit sc)
(org-element-property :repeater-unit dl)))
(repeat-until (and sc-repeat-p (not dl-repeat-p) dl))
(start
(cond
(sc)
((eq org-icalendar-todo-unscheduled-start 'current-datetime)
(let ((now (decode-time)))
(list 'timestamp
(list :type 'active
:minute-start (nth 1 now)
:hour-start (nth 2 now)
:day-start (nth 3 now)
:month-start (nth 4 now)
:year-start (nth 5 now)))))
((or (and (eq org-icalendar-todo-unscheduled-start
'deadline-warning)
dl)
(and (eq org-icalendar-todo-unscheduled-start
'recurring-deadline-warning)
dl-repeat-p))
(let ((dl-raw (org-element-property :raw-value dl)))
(with-temp-buffer
(insert dl-raw)
(goto-char (point-min))
(org-timestamp-down-day (org-get-wdays dl-raw))
(org-element-timestamp-parser)))))))
(concat "BEGIN:VTODO\n"
"UID:TODO-" uid "\n"
(org-icalendar-dtstamp) "\n"
(when start (concat (org-icalendar-convert-timestamp
start "DTSTART" nil timezone)
"\n"))
(when (and dl (not repeat-until))
(concat (org-icalendar-convert-timestamp
dl "DUE" nil timezone)
"\n"))
;; RRULE
(cond
;; SCHEDULED, DEADLINE have different repeaters
((and dl-repeat-p
(not (and (eq repeat-value (org-element-property
:repeater-value dl))
(eq repeat-unit (org-element-property
:repeater-unit dl)))))
;; TODO Implement via RDATE with changing DURATION
(org-display-warning "Not yet implemented: \
different repeaters on SCHEDULED and DEADLINE. Skipping.")
nil)
;; DEADLINE has repeater but SCHEDULED doesn't
((and dl-repeat-p (and sc (not sc-repeat-p)))
;; TODO SCHEDULED should only apply to first instance;
;; use RDATE with custom DURATION to implement that
(org-display-warning "Not yet implemented: \
repeater on DEADLINE but not SCHEDULED. Skipping.")
nil)
((or sc-repeat-p dl-repeat-p)
(concat
(org-icalendar--rrule repeat-unit repeat-value)
;; add UNTIL part to RRULE
(when repeat-until
(let* ((start-time
(org-element-property :minute-start start))
;; RFC5545 requires UTC iff DTSTART is not local time
(local-time-p
(and (not timezone)
(equal org-icalendar-date-time-format
":%Y%m%dT%H%M%S")))
(encoded
(org-encode-time
0
(or (org-element-property :minute-start repeat-until)
0)
(or (org-element-property :hour-start repeat-until)
0)
(org-element-property :day-start repeat-until)
(org-element-property :month-start repeat-until)
(org-element-property :year-start repeat-until))))
(concat ";UNTIL="
(cond
((not start-time)
(format-time-string "%Y%m%d" encoded))
(local-time-p
(format-time-string "%Y%m%dT%H%M%S" encoded))
((format-time-string "%Y%m%dT%H%M%SZ"
encoded t))))))
"\n")))
"SUMMARY:" summary "\n"
(and (org-string-nw-p location) (format "LOCATION:%s\n" location))
(and (org-string-nw-p class) (format "CLASS:%s\n" class))
(and (org-string-nw-p description)
(format "DESCRIPTION:%s\n" description))
"CATEGORIES:" categories "\n"
"SEQUENCE:1\n"
(format "PRIORITY:%d\n"
(let ((pri (or (org-element-property :priority entry)
org-priority-default)))
(floor (- 9 (* 8. (/ (float (- org-priority-lowest pri))
(- org-priority-lowest
org-priority-highest)))))))
(format "STATUS:%s\n"
(if (eq (org-element-property :todo-type entry) 'todo)
"NEEDS-ACTION"
"COMPLETED"))
"END:VTODO\n")))
(defun org-icalendar--valarm (entry timestamp summary)
"Create a VALARM component.
@@ -872,26 +1048,32 @@ as a communication channel."
(or (org-string-nw-p org-icalendar-timezone) (format-time-string "%Z"))
;; Description.
(org-export-data (plist-get info :title) info)
;; TTL
(plist-get info :icalendar-ttl)
contents))
(defun org-icalendar--vcalendar (name owner tz description contents)
(defun org-icalendar--vcalendar (name owner tz description ttl contents)
"Create a VCALENDAR component.
NAME, OWNER, TZ, DESCRIPTION and CONTENTS are all strings giving,
NAME, OWNER, TZ, DESCRIPTION, TTL and CONTENTS are all strings giving,
respectively, the name of the calendar, its owner, the timezone
used, a short description and the other components included."
(concat (format "BEGIN:VCALENDAR
used, a short description, time to live (refresh period) and
the other components included."
(org-icalendar-fold-string
(concat (format "BEGIN:VCALENDAR
VERSION:2.0
X-WR-CALNAME:%s
PRODID:-//%s//Emacs with Org mode//EN
X-WR-TIMEZONE:%s
X-WR-CALDESC:%s
CALSCALE:GREGORIAN\n"
(org-icalendar-cleanup-string name)
(org-icalendar-cleanup-string owner)
(org-icalendar-cleanup-string tz)
(org-icalendar-cleanup-string description))
contents
"END:VCALENDAR\n"))
X-WR-CALDESC:%s\n"
(org-icalendar-cleanup-string name)
(org-icalendar-cleanup-string owner)
(org-icalendar-cleanup-string tz)
(org-icalendar-cleanup-string description))
(when ttl (format "X-PUBLISHED-TTL:%s\n"
(org-icalendar-cleanup-string ttl)))
"CALSCALE:GREGORIAN\n"
contents
"END:VCALENDAR\n")))
@@ -926,14 +1108,13 @@ Return ICS file name."
(let ((file (buffer-file-name (buffer-base-buffer))))
(when (and file org-icalendar-store-UID)
(org-icalendar-create-uid file 'warn-user)))
;; Export part. Since this back-end is backed up by `ascii', ensure
;; Export part. Since this backend is backed up by `ascii', ensure
;; links will not be collected at the end of sections.
(let ((outfile (org-export-output-file-name ".ics" subtreep)))
(org-export-to-file 'icalendar outfile
async subtreep visible-only body-only
'(:ascii-charset utf-8 :ascii-links-to-notes nil)
'(lambda (file)
(run-hook-with-args 'org-icalendar-after-save-hook file) nil))))
#'org-icalendar--post-process-file)))
;;;###autoload
(defun org-icalendar-export-agenda-files (&optional async)
@@ -961,7 +1142,13 @@ external process."
(catch 'nextfile
(org-check-agenda-file file)
(with-current-buffer (org-get-agenda-file-buffer file)
(org-icalendar-export-to-ics))))
(condition-case err
(org-icalendar-export-to-ics)
(error
(warn "Exporting %s to icalendar failed: %s"
file
(error-message-string err))
(signal (car err) (cdr err)))))))
(org-release-buffers org-agenda-new-buffers)))))
;;;###autoload
@@ -1018,8 +1205,9 @@ This function assumes major mode for current buffer is
user-full-name
(or (org-string-nw-p org-icalendar-timezone) (format-time-string "%Z"))
org-icalendar-combined-description
org-icalendar-ttl
contents)))
(run-hook-with-args 'org-icalendar-after-save-hook file)))
(org-icalendar--post-process-file file)))
(defun org-icalendar--combine-files (&rest files)
"Combine entries from multiple files into an iCalendar file.
@@ -1042,6 +1230,8 @@ FILES is a list of files to build the calendar from."
(format-time-string "%Z"))
;; Description.
org-icalendar-combined-description
;; TTL (Refresh period)
org-icalendar-ttl
;; Contents.
(concat
;; Agenda contents.
@@ -1061,8 +1251,7 @@ FILES is a list of files to build the calendar from."
(when (and org-icalendar-include-bbdb-anniversaries
(require 'ol-bbdb nil t))
(with-output-to-string (org-bbdb-anniv-export-ical)))))))
(run-hook-with-args 'org-icalendar-after-save-hook
org-icalendar-combined-agenda-file))
(org-icalendar--post-process-file org-icalendar-combined-agenda-file))
(org-release-buffers org-agenda-new-buffers))))