aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfpi2024-08-28 18:33:20 +0200
committerRobert Pluim2024-09-11 10:04:23 +0200
commit8332b4dd07a43d09ff8eed7097873d9ac4d5afc8 (patch)
tree0d70823b286166bb17f082f1ab912c5a03581acc
parent69e1aca041c57ba425425d31471e1c8f86d3bf04 (diff)
downloademacs-8332b4dd07a43d09ff8eed7097873d9ac4d5afc8.tar.gz
emacs-8332b4dd07a43d09ff8eed7097873d9ac4d5afc8.zip
Allow comments to organizer in icalendar event replies (Bug#72831)
* 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. * test/lisp/gnus/gnus-icalendar-tests.el (gnus-icalendar-accept-with-comment, gnus-icalendar-decline-without-changing-comment): New tests.
-rw-r--r--etc/NEWS8
-rw-r--r--lisp/gnus/gnus-icalendar.el64
-rw-r--r--test/lisp/gnus/gnus-icalendar-tests.el72
3 files changed, 126 insertions, 18 deletions
diff --git a/etc/NEWS b/etc/NEWS
index c6f8b0062e4..8589931684f 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -139,6 +139,14 @@ exactly below the text after the prefix on the first line.
139If 'whitespace-style' includes 'missing-newline-at-eof' (which is the 139If 'whitespace-style' includes 'missing-newline-at-eof' (which is the
140default), the 'whitespace-cleanup' function will now add the newline. 140default), the 'whitespace-cleanup' function will now add the newline.
141 141
142** Gnus
143
144---
145*** Replying to icalendar events now supports specifying a comment.
146When called with a prefix argument, accepting, declining, or tentatively
147accepting an icalendar event will prompt for a comment to add to the
148response.
149
142** Eshell 150** Eshell
143 151
144--- 152---
diff --git a/lisp/gnus/gnus-icalendar.el b/lisp/gnus/gnus-icalendar.el
index af7284b88e8..0d0827b3890 100644
--- a/lisp/gnus/gnus-icalendar.el
+++ b/lisp/gnus/gnus-icalendar.el
@@ -309,7 +309,7 @@ status will be retrieved from the first matching attendee record."
309;;; gnus-icalendar-event-reply 309;;; gnus-icalendar-event-reply
310;;; 310;;;
311 311
312(defun gnus-icalendar-event--build-reply-event-body (ical-request status identities) 312(defun gnus-icalendar-event--build-reply-event-body (ical-request status identities &optional comment)
313 (let ((summary-status (capitalize (symbol-name status))) 313 (let ((summary-status (capitalize (symbol-name status)))
314 (attendee-status (upcase (symbol-name status))) 314 (attendee-status (upcase (symbol-name status)))
315 reply-event-lines) 315 reply-event-lines)
@@ -319,6 +319,10 @@ status will be retrieved from the first matching attendee record."
319 (if (string-match "^[^:]+:" line) 319 (if (string-match "^[^:]+:" line)
320 (replace-match (format "\\&%s: " summary-status) t nil line) 320 (replace-match (format "\\&%s: " summary-status) t nil line)
321 line)) 321 line))
322 (update-comment
323 (line)
324 (if comment (format "COMMENT:%s" comment)
325 line))
322 (update-dtstamp () 326 (update-dtstamp ()
323 (format-time-string "DTSTAMP:%Y%m%dT%H%M%SZ" nil t)) 327 (format-time-string "DTSTAMP:%Y%m%dT%H%M%SZ" nil t))
324 (attendee-matches-identity 328 (attendee-matches-identity
@@ -341,6 +345,7 @@ status will be retrieved from the first matching attendee record."
341 (cond 345 (cond
342 ((string= key "ATTENDEE") (update-attendee-status line)) 346 ((string= key "ATTENDEE") (update-attendee-status line))
343 ((string= key "SUMMARY") (update-summary line)) 347 ((string= key "SUMMARY") (update-summary line))
348 ((string= key "COMMENT") (update-comment line))
344 ((string= key "DTSTAMP") (update-dtstamp)) 349 ((string= key "DTSTAMP") (update-dtstamp))
345 ((member key '("ORGANIZER" "DTSTART" "DTEND" 350 ((member key '("ORGANIZER" "DTSTART" "DTEND"
346 "LOCATION" "DURATION" "SEQUENCE" 351 "LOCATION" "DURATION" "SEQUENCE"
@@ -363,16 +368,27 @@ status will be retrieved from the first matching attendee record."
363 attendee-status user-full-name user-mail-address) 368 attendee-status user-full-name user-mail-address)
364 reply-event-lines)) 369 reply-event-lines))
365 370
371 ;; add comment line if not existing
372 (when (and comment
373 (not (gnus-icalendar-find-if
374 (lambda (x)
375 (string-match "^COMMENT" x))
376 reply-event-lines)))
377 (push (format "COMMENT:%s" comment) reply-event-lines))
378
366 (mapconcat #'identity `("BEGIN:VEVENT" 379 (mapconcat #'identity `("BEGIN:VEVENT"
367 ,@(nreverse reply-event-lines) 380 ,@(nreverse reply-event-lines)
368 "END:VEVENT") 381 "END:VEVENT")
369 "\n")))) 382 "\n"))))
370 383
371(defun gnus-icalendar-event-reply-from-buffer (buf status identities) 384(defun gnus-icalendar-event-reply-from-buffer (buf status identities &optional comment)
372 "Build a calendar event reply for request contained in BUF. 385 "Build a calendar event reply for request contained in BUF.
373The reply will have STATUS (`accepted', `tentative' or `declined'). 386The reply will have STATUS (`accepted', `tentative' or `declined').
374The reply will be composed for attendees matching any entry 387The reply will be composed for attendees matching any entry
375on the IDENTITIES list." 388on the IDENTITIES list.
389Optional argument COMMENT will be placed in the comment field of the
390reply.
391"
376 (cl-labels 392 (cl-labels
377 ((extract-block 393 ((extract-block
378 (blockname) 394 (blockname)
@@ -396,7 +412,7 @@ on the IDENTITIES list."
396 "PRODID:Gnus" 412 "PRODID:Gnus"
397 "VERSION:2.0" 413 "VERSION:2.0"
398 zone 414 zone
399 (gnus-icalendar-event--build-reply-event-body event status identities) 415 (gnus-icalendar-event--build-reply-event-body event status identities comment)
400 "END:VCALENDAR"))) 416 "END:VCALENDAR")))
401 417
402 (mapconcat #'identity (delq nil contents) "\n")))))) 418 (mapconcat #'identity (delq nil contents) "\n"))))))
@@ -878,13 +894,13 @@ These will be used to retrieve the RSVP information from ical events."
878 (insert "Subject: " subject) 894 (insert "Subject: " subject)
879 (message-send-and-exit)))) 895 (message-send-and-exit))))
880 896
881(defun gnus-icalendar-reply (data) 897(defun gnus-icalendar-reply (data &optional comment)
882 (let* ((handle (car data)) 898 (let* ((handle (car data))
883 (status (cadr data)) 899 (status (cadr data))
884 (event (caddr data)) 900 (event (caddr data))
885 (reply (gnus-icalendar-with-decoded-handle handle 901 (reply (gnus-icalendar-with-decoded-handle handle
886 (gnus-icalendar-event-reply-from-buffer 902 (gnus-icalendar-event-reply-from-buffer
887 (current-buffer) status (gnus-icalendar-identities)))) 903 (current-buffer) status (gnus-icalendar-identities) comment)))
888 (organizer (gnus-icalendar-event:organizer event))) 904 (organizer (gnus-icalendar-event:organizer event)))
889 905
890 (when reply 906 (when reply
@@ -1009,25 +1025,37 @@ These will be used to retrieve the RSVP information from ical events."
1009 (when data 1025 (when data
1010 (gnus-icalendar-save-part data)))) 1026 (gnus-icalendar-save-part data))))
1011 1027
1012(defun gnus-icalendar-reply-accept () 1028(defun gnus-icalendar-reply-accept (&optional comment-p)
1013 "Accept invitation in the current article." 1029 "Accept invitation in the current article.
1014 (interactive nil gnus-article-mode gnus-summary-mode) 1030
1031Optional argument COMMENT-P non-nil (interactively `\\[universal-argument]')
1032means prompt for a comment to include in the reply."
1033 (interactive "P" gnus-article-mode gnus-summary-mode)
1015 (with-current-buffer gnus-article-buffer 1034 (with-current-buffer gnus-article-buffer
1016 (gnus-icalendar-reply (list gnus-icalendar-handle 'accepted gnus-icalendar-event)) 1035 (gnus-icalendar-reply (list gnus-icalendar-handle 'accepted gnus-icalendar-event)
1036 (when comment-p (read-string "Comment: ")))
1017 (setq-local gnus-icalendar-reply-status 'accepted))) 1037 (setq-local gnus-icalendar-reply-status 'accepted)))
1018 1038
1019(defun gnus-icalendar-reply-tentative () 1039(defun gnus-icalendar-reply-tentative (&optional comment-p)
1020 "Send tentative response to invitation in the current article." 1040 "Send tentative response to invitation in the current article.
1021 (interactive nil gnus-article-mode gnus-summary-mode) 1041
1042Optional argument COMMENT-P non-nil (interactively `\\[universal-argument]')
1043means prompt for a comment to include in the reply."
1044 (interactive "P" gnus-article-mode gnus-summary-mode)
1022 (with-current-buffer gnus-article-buffer 1045 (with-current-buffer gnus-article-buffer
1023 (gnus-icalendar-reply (list gnus-icalendar-handle 'tentative gnus-icalendar-event)) 1046 (gnus-icalendar-reply (list gnus-icalendar-handle 'tentative gnus-icalendar-event)
1047 (when comment-p (read-string "Comment: ")))
1024 (setq-local gnus-icalendar-reply-status 'tentative))) 1048 (setq-local gnus-icalendar-reply-status 'tentative)))
1025 1049
1026(defun gnus-icalendar-reply-decline () 1050(defun gnus-icalendar-reply-decline (&optional comment-p)
1027 "Decline invitation in the current article." 1051 "Decline invitation in the current article.
1028 (interactive nil gnus-article-mode gnus-summary-mode) 1052
1053Optional argument COMMENT-P non-nil (interactively `\\[universal-argument]')
1054means prompt for a comment to include in the reply."
1055 (interactive "P" gnus-article-mode gnus-summary-mode)
1029 (with-current-buffer gnus-article-buffer 1056 (with-current-buffer gnus-article-buffer
1030 (gnus-icalendar-reply (list gnus-icalendar-handle 'declined gnus-icalendar-event)) 1057 (gnus-icalendar-reply (list gnus-icalendar-handle 'declined gnus-icalendar-event)
1058 (when comment-p (read-string "Comment: ")))
1031 (setq-local gnus-icalendar-reply-status 'declined))) 1059 (setq-local gnus-icalendar-reply-status 'declined)))
1032 1060
1033(defun gnus-icalendar-event-export () 1061(defun gnus-icalendar-event-export ()
diff --git a/test/lisp/gnus/gnus-icalendar-tests.el b/test/lisp/gnus/gnus-icalendar-tests.el
index 08c85013d17..72f1e660077 100644
--- a/test/lisp/gnus/gnus-icalendar-tests.el
+++ b/test/lisp/gnus/gnus-icalendar-tests.el
@@ -255,5 +255,77 @@ END:VCALENDAR" (list "participant@anoncompany.com"))))
255<2020-09-21 14:00-14:30 +1w>"))) 255<2020-09-21 14:00-14:30 +1w>")))
256 (setenv "TZ" tz)))) 256 (setenv "TZ" tz))))
257 257
258(ert-deftest gnus-icalendar-accept-with-comment ()
259 ""
260 (let ((event "BEGIN:VEVENT
261DTSTART;TZID=Europe/Berlin:20200915T140000
262DTEND;TZID=Europe/Berlin:20200915T143000
263RRULE:FREQ=WEEKLY;BYDAY=FR,MO,TH,TU,WE
264DTSTAMP:20200915T120627Z
265ORGANIZER;CN=anon@anoncompany.com:mailto:anon@anoncompany.com
266UID:7b6g3m7iftuo90ei4ul00feqn_R20200915T120000@google.com
267ATTENDEE;CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;RSVP=TRUE
268 ;CN=participant@anoncompany.com;X-NUM-GUESTS=0:mailto:participant@anoncompany.com
269CREATED:20200325T095723Z
270DESCRIPTION:Coffee talk
271LAST-MODIFIED:20200915T120623Z
272LOCATION:
273SEQUENCE:0
274STATUS:CONFIRMED
275SUMMARY:Casual coffee talk
276TRANSP:OPAQUE
277END:VEVENT")
278 (icalendar-identities '("participant@anoncompany.com")))
279 (unwind-protect
280 (progn
281 (let* ((reply (with-temp-buffer
282 (insert event)
283 (gnus-icalendar-event-reply-from-buffer
284 (current-buffer)
285 'accepted
286 icalendar-identities
287 "Can not stay long."))))
288 (should (string-match "^ATTENDEE;.*?\\(PARTSTAT=[^;]+\\)" reply))
289 (should (string-equal (match-string 1 reply) "PARTSTAT=ACCEPTED"))
290 (should (string-match "^COMMENT:\\(.*\\)$" reply))
291 (should (string-equal (match-string 1 reply) "Can not stay long.")))))))
292
293(ert-deftest gnus-icalendar-decline-without-changing-comment ()
294 ""
295 (let ((event "BEGIN:VEVENT
296DTSTART;TZID=Europe/Berlin:20200915T140000
297DTEND;TZID=Europe/Berlin:20200915T143000
298RRULE:FREQ=WEEKLY;BYDAY=FR,MO,TH,TU,WE
299DTSTAMP:20200915T120627Z
300ORGANIZER;CN=anon@anoncompany.com:mailto:anon@anoncompany.com
301UID:7b6g3m7iftuo90ei4ul00feqn_R20200915T120000@google.com
302ATTENDEE;CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;RSVP=TRUE
303 ;CN=participant@anoncompany.com;X-NUM-GUESTS=0:mailto:participant@anoncompany.com
304CREATED:20200325T095723Z
305DESCRIPTION:Coffee talk
306LAST-MODIFIED:20200915T120623Z
307COMMENT:Only available at 2pm
308LOCATION:
309SEQUENCE:0
310STATUS:CONFIRMED
311SUMMARY:Casual coffee talk
312TRANSP:OPAQUE
313END:VEVENT")
314 (icalendar-identities '("participant@anoncompany.com")))
315 (unwind-protect
316 (progn
317 (let* ((reply (with-temp-buffer
318 (insert event)
319 (gnus-icalendar-event-reply-from-buffer
320 (current-buffer)
321 'declined
322 icalendar-identities
323 nil))))
324 (should (string-match "^ATTENDEE;.*?\\(PARTSTAT=[^;]+\\)" reply))
325 (should (string-equal (match-string 1 reply) "PARTSTAT=DECLINED"))
326 (should (string-match "^COMMENT:\\(.*\\)$" reply))
327 (should (string-equal (match-string 1 reply) "Only available at 2pm"))
328 )))))
329
258(provide 'gnus-icalendar-tests) 330(provide 'gnus-icalendar-tests)
259;;; gnus-icalendar-tests.el ends here 331;;; gnus-icalendar-tests.el ends here