From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp10.migadu.com ([2001:41d0:8:6d80::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms9.migadu.com with LMTPS id uIqOC52VIGSlugAASxT56A (envelope-from ) for ; Sun, 26 Mar 2023 20:57:33 +0200 Received: from aspmx1.migadu.com ([2001:41d0:8:6d80::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp10.migadu.com with LMTPS id QIXFBp2VIGTDTwEAG6o9tA (envelope-from ) for ; Sun, 26 Mar 2023 20:57:33 +0200 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 689501ED6B for ; Sun, 26 Mar 2023 20:57:32 +0200 (CEST) Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20210112 header.b=YsaGtHks; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1679857052; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=ltLx17fUVbUjhAdz0QrFuBGzulPDSPf3KQ6G0dOHZpw=; b=IR+HtS12eQtXT/Zl/re1OnCR3P03Eg+l2cJpCJy8xyfTOTl4n2F3dP3yN53Jtlc3fYZGCK 2k8s1ao45jcLkU3nUeaGbsk86ciVtqqkffu2PAU1ZQtxo8DUs0TBbtScCfjDBNqL3sjJC1 /N8s0IQQHupKsw7d/wY3tHrhqXaIuq6OqPXO8V7oN8TXOFoxw8g/GVO38wGp2iuSkvKv9I vYA0BZBKB8oeiIMBozM7VzskQiOYUdwgsJCsq16VQ19clhYnNSNZFnNNsmNqYwhCVOClTX qtNcMM9IEOJcqPjn4pmMfD0RObf+yOen0jfKc3Il2CQRE7W4j1jq6lzR5y+SEw== ARC-Seal: i=1; s=key1; d=yhetil.org; t=1679857052; a=rsa-sha256; cv=none; b=uebkLwgLDQnem4CF1qkp2GBYdVdzWE3TbAlQ44fHISusgZyY23JblLVdj4EeHN8XempNC6 cnvUjwGQeh8uSjQhcrnv7TUDBw+PS+5r7DQ2XBUgRz8TFPUDtGXkGWX3mbQHYn75Pk1YoM y/jNVq/tyA5NCxkEJ2oPUlggfAhb7Kks/JqEGw9nZJVLbzrtIkc2t2pvnH9TbAnP9mYs2L CqUQ2LuTzOLYGjCHgUqRQGVGlJHG0grEiXVpfPyRi1KO7DUFFsby4J8yZEF5nykXCszgOF 7rP4tOBWSFGHhG4c1IPNAROE5fPtiUfXBM3YznjqO7ibMJRL65SBd/s/m2CvKg== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20210112 header.b=YsaGtHks; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gmail.com Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pgVXl-0000X5-U6; Sun, 26 Mar 2023 14:56:41 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pgVXj-0000WV-9U for emacs-orgmode@gnu.org; Sun, 26 Mar 2023 14:56:39 -0400 Received: from mail-pj1-x102f.google.com ([2607:f8b0:4864:20::102f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1pgVXf-0003Gy-VJ for emacs-orgmode@gnu.org; Sun, 26 Mar 2023 14:56:38 -0400 Received: by mail-pj1-x102f.google.com with SMTP id mp3-20020a17090b190300b0023fcc8ce113so9782841pjb.4 for ; Sun, 26 Mar 2023 11:56:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; t=1679856993; h=mime-version:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=ltLx17fUVbUjhAdz0QrFuBGzulPDSPf3KQ6G0dOHZpw=; b=YsaGtHksVpDGIAbpsdyfFlOWO0oIiY80LmZ1xCeo0PPd4e6Ppm/LZapk/krn+cR+UG WWJIt3x2bVdWT9jOQW39B8T5zJ52kZBtGaAIRkkSstJpr9JhMnagSzP453uj3CU/F1fw P0gs5RxpDTgCADVge2MaJPe4j1R3DnoxhI+CtUOYqZhp2i6XxY5uqVpgfHzAj+JzQaOn dC7z5QAUJMADhkxmWJgwHny2NFIckkMq4xCMB7X5wlmjkGylw0bYhQBUFDjz3g6ors5r 9PhoZmbzjvblQJI9UP1Ky9eUUxm+GLrEsWLkMlQ9rwctAV6G9Qa8z4GBzSXXLntgZJd/ 72LQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679856993; h=mime-version:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=ltLx17fUVbUjhAdz0QrFuBGzulPDSPf3KQ6G0dOHZpw=; b=lUtyXD4+PnsqBQkWy40zKIO+ENqHQgvZv4hjvGhWxileedPBLdekC899wN3hMGb9TQ QeAk+l7a3K4dCU4LW61ya1DYfLyWRZOHrcbRF9qn+8fhEE+AcVxvcgmZQa96DiVdKsC6 RpprBloDLmolA+KIJv4Zntdypql4C+8ZqZy2Pubh24hN9yVdZc90cvIv5ZPGzFmm9QxI KmQyGStFnTbSHc57ojibYajhmFHZi0yPplFGZuoVM1W3qg3usxQFGYa9YUd44UC/dJBX 4xETKPTPBVZHmLt+yV4+BkomsFYZAP3HLkqEJx4CIJINm7CLwtBdnc2zENDFaH4MJk4c v4og== X-Gm-Message-State: AAQBX9crfM3bVAMBhtw6SyLHdLbTAPUK+2jc9BtKKYRxEBAQ2KYYiAB6 d5Lqws8nU4MI6Y2Y8LiyDgTjgwzjAus= X-Google-Smtp-Source: AKy350Z5Za5elrn7Kyi0AXUYtHEz/NpiiOe0lajUf1/ZttJZ6DpzHmU2toNsxt7CHuCCZ8I3Tgrh4Q== X-Received: by 2002:a17:902:f243:b0:19c:dedd:2ace with SMTP id j3-20020a170902f24300b0019cdedd2acemr7950394plc.18.1679856993255; Sun, 26 Mar 2023 11:56:33 -0700 (PDT) Received: from localhost (157-131-78-143.fiber.dynamic.sonic.net. [157.131.78.143]) by smtp.gmail.com with ESMTPSA id bf6-20020a170902b90600b001a1bf30cef1sm14674314plb.46.2023.03.26.11.56.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Mar 2023 11:56:32 -0700 (PDT) From: Jack Kamm To: emacs-orgmode@gnu.org Cc: mail@nicolasgoaziou.fr Subject: [RFC] ox-icalendar: Unscheduled tasks & repeating tasks Date: Sun, 26 Mar 2023 11:56:31 -0700 Message-ID: <874jq75okg.fsf@gmail.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=2607:f8b0:4864:20::102f; envelope-from=jackkamm@gmail.com; helo=mail-pj1-x102f.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: X-Migadu-Queue-Id: 689501ED6B X-Spam-Score: -9.96 X-Migadu-Spam-Score: -9.96 X-Migadu-Scanner: scn0.migadu.com List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: emacs-orgmode-bounces+larch=yhetil.org@gnu.org X-Migadu-Country: US X-Migadu-Flow: FLOW_IN X-TUID: 2gPrfq22NbD4 --=-=-= Content-Type: text/plain 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. --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0001-ox-icalendar-Allow-exporting-unscheduled-VTODOs.patch >From 1bd268ab260d5077d7456c0d64fea36128772f86 Mon Sep 17 00:00:00 2001 From: Jack Kamm 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 --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0002-ox-icalendar-Support-repeating-timestamps-in-TODOs.patch >From 8348f5b8c56087f0fb8cdd775a816f63cb57f38f Mon Sep 17 00:00:00 2001 From: Jack Kamm 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 --=-=-=--