[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC] ox-icalendar: Unscheduled tasks & repeating tasks
From: |
Jack Kamm |
Subject: |
[RFC] ox-icalendar: Unscheduled tasks & repeating tasks |
Date: |
Sun, 26 Mar 2023 11:56:31 -0700 |
Hello,
The attached 2 patches add support for exporting unscheduled tasks and
repeating tasks to iCalendar, respectively.
For patch 1 (unscheduled tasks):
Currently, ox-icalendar does not allow creating an iCalendar task
without a scheduled start date. If an Org TODO is missing a SCHEDULED
timestamp, then ox-icalendar sets today as the scheduled start date for
the exported task.
Patch 1 changes this by adding a new customization
org-icalendar-todo-force-scheduling. When non-nil, the start date is set
to today (same as the current behavior). When nil, unscheduled Org TODOs
are instead exported without a start date.
I also propose the default value to be nil. Note, this is
backwards-incompatible with the previous behavior!
But I think it should be the default anyways, because IMO it is the more
correct and useful behavior. An iCalendar VTODO without a DTSTART
property is valid, and has the same meaning as an Org TODO without a
SCHEDULED timestamp. Also, all the iCalendar programs I have tried
support unscheduled tasks, including Thunderbird, Evolution, Nextcloud,
and Tasks.org.
For patch 2 (repeating timestamps):
I add recurrence rule (RRULE) export for repeating SCHEDULED and
DEADLINE timestamps in TODOs, similar to how repeating non-TODO events
are currently handled.
The main complication here is that iCalendar's RRULE applies to both
DTSTART and DUE properties; by contrast, Org's SCHEDULED and DEADLINE
timestamps may have different repeaters. I am not sure the best way to
handle the case where SCHEDULED and DEADLINE have different repeaters,
so in that case I issue a warning and skip the repeater.
>From 1bd268ab260d5077d7456c0d64fea36128772f86 Mon Sep 17 00:00:00 2001
From: Jack Kamm <jackkamm@gmail.com>
Date: Sun, 26 Mar 2023 07:43:53 -0700
Subject: [PATCH 1/2] ox-icalendar: Allow exporting unscheduled VTODOs
* lisp/ox-icalendar.el (org-icalendar-todo-force-scheduling): New
option to revert to previous export behavior of unscheduled TODOs.
(org-icalendar--vtodo): Don't force unscheduled TODOs to have a
scheduled start time of today, unless
`org-icalendar-todo-force-scheduling' is set.
---
etc/ORG-NEWS | 15 +++++++++++++++
lisp/ox-icalendar.el | 32 +++++++++++++++++++++-----------
2 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index ac233a986..fb4f82b29 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -23,6 +23,15 @@ If you still want to use python-mode with ob-python, you
might
consider
[[https://gitlab.com/jackkamm/ob-python-mode-mode][ob-python-mode-mode]], where
the code to support python-mode
has been ported to.
+*** Icalendar export of TODOs no longer forces a start time
+
+For TODOs without a scheduled start time, ox-icalendar no longer
+forces them to have a scheduled start time of today when exporting.
+This makes it possible to create icalendar TODOs without a start time.
+
+To revert to the old behavior, set the new custom option
+~org-icalendar-todo-force-scheduling~ to non-nil.
+
** New and changed options
*** New ~org-cite-natbib-export-bibliography~ option defining fallback
bibliography style
@@ -111,6 +120,12 @@ backend used for evaluation of ClojureScript.
official [[https://clojure.org/guides/deps_and_cli][Clojure CLI tools]].
The command can be customized with ~ob-clojure-cli-command~.
+*** New ~org-icalendar-todo-force-scheduling~ option for old ox-icalendar TODO
scheduling behavior
+
+Set ~org-icalendar-todo-force-scheduling~ to non-nil to revert to the
+old ox-icalendar TODO export behavior, that forced all exported TODOs
+to have a scheduled start time.
+
** New features
*** Add support for ~logind~ idle time in ~org-user-idle-seconds~
diff --git a/lisp/ox-icalendar.el b/lisp/ox-icalendar.el
index 81a77a770..63aefcc84 100644
--- a/lisp/ox-icalendar.el
+++ b/lisp/ox-icalendar.el
@@ -231,6 +231,12 @@ (defcustom org-icalendar-include-todo nil
(repeat :tag "Specific TODO keywords"
(string :tag "Keyword"))))
+(defcustom org-icalendar-todo-force-scheduling nil
+ "Non-nil means unscheduled tasks are exported as scheduled.
+The current date is used as the scheduled time for such tasks."
+ :group 'org-export-icalendar
+ :type 'boolean)
+
(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."
@@ -776,21 +782,25 @@ (defun org-icalendar--vtodo
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)))))))
+ (when org-icalendar-todo-force-scheduling
+ ;; 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"
+ (when start
+ (concat (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
--
2.39.2
>From 8348f5b8c56087f0fb8cdd775a816f63cb57f38f Mon Sep 17 00:00:00 2001
From: Jack Kamm <jackkamm@gmail.com>
Date: Sun, 26 Mar 2023 10:37:47 -0700
Subject: [PATCH 2/2] ox-icalendar: Support repeating timestamps in TODOs
* lisp/ox-icalendar.el (org-icalendar--rrule): New helper function to
generate RRULE.
(org-icalendar--vevent): Use `org-icalendar--rrule' instead of
generating the RRULE directly.
(org-icalendar--vtodo): Generate RRULE for repeating scheduled and
deadline timestamps.
---
etc/ORG-NEWS | 13 ++++++++++++
lisp/ox-icalendar.el | 50 +++++++++++++++++++++++++++++++++-----------
2 files changed, 51 insertions(+), 12 deletions(-)
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index fb4f82b29..3919b240e 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -159,6 +159,19 @@ Running shell blocks with the ~:session~ header freezes
Emacs until
execution completes. The new ~:async~ header allows users to continue
editing with Emacs while a ~:session~ block executes.
+*** Add support for repeating tasks in iCalendar export
+
+Repeating Scheduled and Deadline timestamps in TODOs are now exported
+as recurring tasks in iCalendar export.
+
+Note that in Org-mode, the repeaters for the Scheduled and Deadline
+timestamps can be different; whereas in iCalendar, the recurrence rule
+applies to both the scheduled start time and the deadline due date.
+
+In case the timestamp repeaters contradict, the correct export
+behavior is not well-defined. Currently, Org-mode will issue a
+warning and skip the repeaters in this case.
+
** Miscellaneous
*** Remove undocumented ~:target~ header parameter in ~ob-clojure~
diff --git a/lisp/ox-icalendar.el b/lisp/ox-icalendar.el
index 63aefcc84..179795ac9 100644
--- a/lisp/ox-icalendar.el
+++ b/lisp/ox-icalendar.el
@@ -726,6 +726,13 @@ (defun org-icalendar-entry (entry contents info)
;; Don't forget components from inner entries.
contents))))
+(defun org-icalendar--rrule (unit value)
+ (format "RRULE:FREQ=%s;INTERVAL=%d\n"
+ (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.
@@ -752,12 +759,9 @@ (\"PUBLIC\", \"CONFIDENTIAL\", and \"PRIVATE\") are
predefined, others
(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)))
+ (org-icalendar--rrule
+ (org-element-property :repeater-unit timestamp)
+ (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))
@@ -792,7 +796,9 @@ (defun org-icalendar--vtodo
:hour-start (nth 2 now)
:day-start (nth 3 now)
:month-start (nth 4 now)
- :year-start (nth 5 now))))))))
+ :year-start (nth 5 now)))))))
+ (due (and (memq 'todo-due org-icalendar-use-deadline)
+ (org-element-property :deadline entry))))
(org-icalendar-fold-string
(concat "BEGIN:VTODO\n"
"UID:TODO-" uid "\n"
@@ -801,11 +807,31 @@ (defun org-icalendar--vtodo
(concat (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"))
+ (when due
+ (concat (org-icalendar-convert-timestamp
+ due "DUE" nil timezone)
+ "\n"))
+ ;; RRULE
+ (let ((start-repeater-unit (org-element-property
+ :repeater-unit start))
+ (start-repeater-value (org-element-property
+ :repeater-value start))
+ (due-repeater-unit (org-element-property
+ :repeater-unit due))
+ (due-repeater-value (org-element-property
+ :repeater-value due)))
+ (when (or start-repeater-value due-repeater-value)
+ (if (and start due
+ (not (and (eql start-repeater-unit
+ due-repeater-unit)
+ (eql start-repeater-value
+ due-repeater-value))))
+ (progn (warn "Scheduled and Deadline repeaters are not
equal. Skipping repeater export.")
+ nil)
+ (org-icalendar--rrule (or start-repeater-unit
+ due-repeater-unit)
+ (or start-repeater-value
+ due-repeater-value)))))
"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))
--
2.39.2
- [RFC] ox-icalendar: Unscheduled tasks & repeating tasks,
Jack Kamm <=