From 78c9298375942d73543aaa86b8f2af29ff76a9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Cadilhac?= Date: Thu, 15 Aug 2019 10:01:48 -0400 Subject: [PATCH] Allow bumping late tasks and warning deadlines in iCal export * lisp/ox-icalendar.el (org-icalendar-bump-todos): New variable. (org-icalendar-warn-deadlines): New variable. (org-icalendar-today-timestamp): New function. (org-icalendar-days-until-timestamp): New function. (org-icalendar-entry): Implement bumping late tasks and warning deadlines. * doc/org-manual.org (iCalendar Export): Document this. --- doc/org-manual.org | 12 +++++ lisp/ox-icalendar.el | 114 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 113 insertions(+), 13 deletions(-) diff --git a/doc/org-manual.org b/doc/org-manual.org index c0a91ab3e..9dc58247b 100644 --- a/doc/org-manual.org +++ b/doc/org-manual.org @@ -15329,6 +15329,18 @@ capabilities of the destination application. Some are more lenient than others. Consult the Org mode FAQ for advice on specific applications. +#+vindex: org-icalendar-bump-todos +#+vindex: org-icalendar-warn-deadlines +An important difference between the Org Agenda and the exported +calendar is that overdue tasks and deadline warnings are not treated +in any special way by default. To change this, configure the +variables ~org-icalendar-bump-todos~ and +~org-icalendar-warn-deadlines~. Setting the first variable will move +overdue TODOs to the current day, their titles being changed to +reflect how late the task is. Setting the second variable adds an +event on the current day if a deadline event is due in the next +~org-deadline-warning-days~ days. + ** Other Built-in Back-ends :PROPERTIES: :DESCRIPTION: Exporting to a man page. diff --git a/lisp/ox-icalendar.el b/lisp/ox-icalendar.el index 779051cb0..3a291ee2b 100644 --- a/lisp/ox-icalendar.el +++ b/lisp/ox-icalendar.el @@ -119,6 +119,24 @@ This is a list with possibly several symbols in it. Valid symbols are: (const :tag "SCHEDULED in TODO entries become start date" todo-start))) +(defcustom org-icalendar-bump-todos nil + "Non-nil means that pending TODO VEVENTs are bumped to today. +In addition, if non-nil, the number of late days is indicated in the summary." + :group 'org-export-icalendar + :type 'boolean + :package-version '(Org . "9.3") + :safe #'booleanp) + +(defcustom org-icalendar-warn-deadlines nil + "Non-nil means that a new VEVENT is created today to warn for deadlines. +This only applies to TODOs that are not done. Relies on +`org-deadline-warning-days' for the number of days during which the warning +is created." + :group 'org-export-icalendar + :type 'boolean + :package-version '(Org . "9.3") + :safe #'booleanp) + (defcustom org-icalendar-categories '(local-tags category) "Items that should be entered into the \"categories\" field. @@ -481,6 +499,28 @@ or subject for the event." (concat folded-line "\r\n " (substring line chunk-start)))))) (org-split-string s "\n") "\r\n"))) +(defun org-icalendar-today-timestamp () + "Return a TIMESTAMP object for today, at 00:00." + (let ((dt (decode-time))) + (list 'timestamp + (nconc (list :year-start (nth 5 dt) + :year-end (nth 5 dt) + :month-start (nth 4 dt) + :month-end (nth 4 dt) + :day-start (nth 3 dt) + :day-end (nth 3 dt)))))) + +(defun org-icalendar-days-until-timestamp (timestamp) + "Return the number of days until TIMESTAMP. + +If TIMESTAMP occurs today, return 0. +If TIMESTAMP occurs yesterday, return -1." + (floor + (/ (float-time + (time-subtract (org-timestamp-to-time timestamp) + (apply 'encode-time + (append '(0 0 0) (nthcdr 3 (decode-time)))))) + (* 60 60 24)))) ;;; Filters @@ -567,19 +607,67 @@ inlinetask within the section." ;; "VEVENT" component from scheduled, deadline, or any ;; timestamp in the entry. (let ((deadline (org-element-property :deadline entry))) - (and deadline - (memq (if todo-type 'event-if-todo 'event-if-not-todo) - org-icalendar-use-deadline) - (org-icalendar--vevent - entry deadline (concat "DL-" uid) - (concat "DL: " summary) loc desc cat tz class))) - (let ((scheduled (org-element-property :scheduled entry))) - (and scheduled - (memq (if todo-type 'event-if-todo 'event-if-not-todo) - org-icalendar-use-scheduled) - (org-icalendar--vevent - entry scheduled (concat "SC-" uid) - (concat "S: " summary) loc desc cat tz class))) + (when (and deadline + (memq (if todo-type 'event-if-todo 'event-if-not-todo) + org-icalendar-use-deadline)) + (let ((days-until-deadline + (org-icalendar-days-until-timestamp deadline))) + (cond + ;; Case 1: Just export the event if... + ((or + ;; Not a pending TODO. + (not (eq todo-type 'todo)) + ;; Is due today. + (= days-until-deadline 0) + ;; Overdue but no bumping. + (and (not org-icalendar-bump-todos) + (< days-until-deadline 0)) + ;; In the future but no warning. + (and (not org-icalendar-warn-deadlines) + (> days-until-deadline 0))) + (org-icalendar--vevent + entry deadline (concat "DL-" uid) + (concat "DL: " summary) loc desc cat tz class)) + ;; Case 2: pending TODO overdue and should bump. + ((and org-icalendar-bump-todos + (< days-until-deadline 0)) + (org-icalendar--vevent + entry (org-icalendar-today-timestamp) (concat "DL-" uid) + (format "DL (%dx): %s" (- days-until-deadline) summary) + loc desc cat tz class)) + ;; Case 3: in the future and should warn. + (t + (concat + ;; If in the warning zone. + (when (<= days-until-deadline org-deadline-warning-days) + (org-icalendar--vevent + entry (org-icalendar-today-timestamp) + (format "DL-%s-%d" uid days-until-deadline) + (format "DL (in %dd.): %s" days-until-deadline summary) + loc desc cat tz class)) + (org-icalendar--vevent + entry deadline (concat "DL-" uid) + (concat "DL: " summary) loc desc cat tz class))))))) + (let ((scheduled (org-element-property :scheduled entry))) + (when (and scheduled + (memq (if todo-type 'event-if-todo 'event-if-not-todo) + org-icalendar-use-scheduled)) + (let ((days-until-scheduled + (org-icalendar-days-until-timestamp scheduled))) + (cond + ;; Already done, due in the future or today, or no bumps. + ((or (not (eq todo-type 'todo)) + (>= days-until-scheduled 0) + (not org-icalendar-bump-todos)) + (org-icalendar--vevent + entry scheduled (concat "SC-" uid) + (concat "S: " summary) loc desc cat tz class)) + ;; Overdue and should bump. + (t + (org-icalendar--vevent + entry (org-icalendar-today-timestamp) (concat "SC-" uid) + (format "S (%dx): %s" (- days-until-scheduled) summary) + loc desc cat tz class)))))) ;; When collecting plain timestamps from a headline and its ;; title, skip inlinetasks since collection will happen once ;; ENTRY is one of them. -- 2.22.0