Subject: [PATCH] gnus-icalendar: Allow comments in event replies

From: Ferdinand Pieper <[email-masked]>
Date: Tue, 27 Aug 2024 11:58:32 +0200

Tags: patch

The iCalendar RFC allows comments in an event reply to e.g. indicate a reason for declining. This patch implements sending an additional comment if the usual functions `gnus-icalendar-reply-[accept,decline,tentative]` (bound to `i a`, `i d`, `i t`) are called with a prefix argument.

Please let me know any feedback regarding the patch and commit message formatting.

Notably this only implements the comment functionality for the functions mentioned above and does not include the buttons defined in `gnus-icalendar-event:inline-reply-buttons`, as I am not sure how the `read-string` for the user's comment should be implemented for them.

Best,

>From 8ff07723b407d56de72ddfea98b8931951bb7ad8 Mon Sep 17 00:00:00 2001
From: fpi <[email-masked]>
Date: Mon, 19 Aug 2024 17:18:11 +0200
Subject: [PATCH] Allow comments to organizer in ical event replies

* lisp/gnus/gnus-icalendar.el
(gnus-icalendar-event--build-reply-event-body): Add optional COMMENT
argument to be inserted into the reply.
(gnus-icalendar-event-reply-from-buffer): Add COMMENT argument to be
passed through to gnus-icalendar-event--build-reply-event-body
(gnus-icalendar-reply-accept):
(gnus-icalendar-reply-tentative):
(gnus-icalendar-reply-decline): If interactively called with a prefix
argument ask user for a COMMENT to add to the reply.
---
 lisp/gnus/gnus-icalendar.el | 52 ++++++++++++++++++++++++++-----------
 1 file changed, 37 insertions(+), 15 deletions(-)

diff --git a/lisp/gnus/gnus-icalendar.el b/lisp/gnus/gnus-icalendar.el
index af7284b88e8..7a295fb9ed2 100644
--- a/lisp/gnus/gnus-icalendar.el
+++ b/lisp/gnus/gnus-icalendar.el
@@ -309,7 +309,7 @@ gnus-icalendar-event-from-buffer
 ;;; gnus-icalendar-event-reply
 ;;;
 
-(defun gnus-icalendar-event--build-reply-event-body (ical-request status identities)
+(defun gnus-icalendar-event--build-reply-event-body (ical-request status identities &optional comment)
   (let ((summary-status (capitalize (symbol-name status)))
         (attendee-status (upcase (symbol-name status)))
         reply-event-lines)
@@ -319,6 +319,10 @@ gnus-icalendar-event--build-reply-event-body
 	  (if (string-match "^[^:]+:" line)
 	      (replace-match (format "\\&%s: " summary-status) t nil line)
 	    line))
+         (update-comment
+           (line)
+           (if comment (format "COMMENT:%s" comment)
+             line))
 	 (update-dtstamp ()
 			 (format-time-string "DTSTAMP:%Y%m%dT%H%M%SZ" nil t))
 	 (attendee-matches-identity
@@ -341,6 +345,7 @@ gnus-icalendar-event--build-reply-event-body
 		    (cond
 		     ((string= key "ATTENDEE") (update-attendee-status line))
 		     ((string= key "SUMMARY") (update-summary line))
+		     ((string= key "COMMENT") (update-comment line))
 		     ((string= key "DTSTAMP") (update-dtstamp))
 		     ((member key '("ORGANIZER" "DTSTART" "DTEND"
 				    "LOCATION" "DURATION" "SEQUENCE"
@@ -363,12 +368,17 @@ gnus-icalendar-event--build-reply-event-body
                       attendee-status user-full-name user-mail-address)
               reply-event-lines))
 
+      ;; add comment line if not existing
+      (when (and comment (not (gnus-icalendar-find-if (lambda (x) (string-match "^COMMENT" x))
+                                                      reply-event-lines)))
+        (push (format "COMMENT:%s" comment) reply-event-lines))
+
       (mapconcat #'identity `("BEGIN:VEVENT"
                               ,@(nreverse reply-event-lines)
                               "END:VEVENT")
                  "\n"))))
 
-(defun gnus-icalendar-event-reply-from-buffer (buf status identities)
+(defun gnus-icalendar-event-reply-from-buffer (buf status identities &optional comment)
   "Build a calendar event reply for request contained in BUF.
 The reply will have STATUS (`accepted', `tentative' or  `declined').
 The reply will be composed for attendees matching any entry
@@ -396,7 +406,7 @@ gnus-icalendar-event-reply-from-buffer
                               "PRODID:Gnus"
                               "VERSION:2.0"
                               zone
-                              (gnus-icalendar-event--build-reply-event-body event status identities)
+                              (gnus-icalendar-event--build-reply-event-body event status identities comment)
                               "END:VCALENDAR")))
 
           (mapconcat #'identity (delq nil contents) "\n"))))))
@@ -878,13 +888,13 @@ gnus-icalendar-send-buffer-by-mail
       (insert "Subject: " subject)
       (message-send-and-exit))))
 
-(defun gnus-icalendar-reply (data)
+(defun gnus-icalendar-reply (data &optional comment)
   (let* ((handle (car data))
          (status (cadr data))
          (event (caddr data))
          (reply (gnus-icalendar-with-decoded-handle handle
                   (gnus-icalendar-event-reply-from-buffer
-                   (current-buffer) status (gnus-icalendar-identities))))
+                   (current-buffer) status (gnus-icalendar-identities) comment)))
          (organizer (gnus-icalendar-event:organizer event)))
 
     (when reply
@@ -1009,25 +1019,37 @@ gnus-icalendar-save-event
     (when data
       (gnus-icalendar-save-part data))))
 
-(defun gnus-icalendar-reply-accept ()
-  "Accept invitation in the current article."
+(defun gnus-icalendar-reply-accept (&optional comment-p)
+  "Accept invitation in the current article.
+
+With a prefix `\\[universal-argument]' prompt for a comment to include
+in the reply."
   (interactive nil gnus-article-mode gnus-summary-mode)
   (with-current-buffer gnus-article-buffer
-    (gnus-icalendar-reply (list gnus-icalendar-handle 'accepted gnus-icalendar-event))
+    (gnus-icalendar-reply (list gnus-icalendar-handle 'accepted gnus-icalendar-event)
+                          (when comment-p (read-string "Comment: ")))
     (setq-local gnus-icalendar-reply-status 'accepted)))
 
-(defun gnus-icalendar-reply-tentative ()
-  "Send tentative response to invitation in the current article."
+(defun gnus-icalendar-reply-tentative (&optional comment-p)
+  "Send tentative response to invitation in the current article.
+
+With a prefix `\\[universal-argument]' prompt for a comment to include
+in the reply."
   (interactive nil gnus-article-mode gnus-summary-mode)
   (with-current-buffer gnus-article-buffer
-    (gnus-icalendar-reply (list gnus-icalendar-handle 'tentative gnus-icalendar-event))
+    (gnus-icalendar-reply (list gnus-icalendar-handle 'tentative gnus-icalendar-event)
+                          (when comment-p (read-string "Comment: ")))
     (setq-local gnus-icalendar-reply-status 'tentative)))
 
-(defun gnus-icalendar-reply-decline ()
-  "Decline invitation in the current article."
-  (interactive nil gnus-article-mode gnus-summary-mode)
+(defun gnus-icalendar-reply-decline (&optional comment-p)
+  "Decline invitation in the current article.
+
+With a prefix `\\[universal-argument]' prompt for a comment to include
+in the reply."
+  (interactive "P" gnus-article-mode gnus-summary-mode)
   (with-current-buffer gnus-article-buffer
-    (gnus-icalendar-reply (list gnus-icalendar-handle 'declined gnus-icalendar-event))
+    (gnus-icalendar-reply (list gnus-icalendar-handle 'declined gnus-icalendar-event)
+                          (when comment-p (read-string "Comment: ")))
     (setq-local gnus-icalendar-reply-status 'declined)))
 
 (defun gnus-icalendar-event-export ()
-- 
2.30.2