summaryrefslogtreecommitdiff
path: root/lisp/gnus/gnus-icalendar.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/gnus/gnus-icalendar.el')
-rw-r--r--lisp/gnus/gnus-icalendar.el108
1 files changed, 82 insertions, 26 deletions
diff --git a/lisp/gnus/gnus-icalendar.el b/lisp/gnus/gnus-icalendar.el
index ee556a32080..389bce85e8b 100644
--- a/lisp/gnus/gnus-icalendar.el
+++ b/lisp/gnus/gnus-icalendar.el
@@ -5,18 +5,20 @@
;; Author: Jan Tatarik <Jan.Tatarik@gmail.com>
;; Keywords: mail, icalendar, org
-;; This program is free software; you can redistribute it and/or modify
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
-;; This program is distributed in the hope that it will be useful,
+;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
@@ -132,11 +134,27 @@
(cl-defmethod gnus-icalendar-event:recurring-interval ((event gnus-icalendar-event))
"Return recurring interval of EVENT."
(let ((rrule (gnus-icalendar-event:recur event))
- (default-interval 1))
+ (default-interval "1"))
+
+ (if (string-match "INTERVAL=\\([[:digit:]]+\\)" rrule)
+ (match-string 1 rrule)
+ default-interval)))
- (string-match "INTERVAL=\\([[:digit:]]+\\)" rrule)
- (or (match-string 1 rrule)
- default-interval)))
+(cl-defmethod gnus-icalendar-event:recurring-days ((event gnus-icalendar-event))
+ "Return, when available, the week day numbers on which the EVENT recurs."
+ (let ((rrule (gnus-icalendar-event:recur event))
+ (weekday-map '(("SU" . 0)
+ ("MO" . 1)
+ ("TU" . 2)
+ ("WE" . 3)
+ ("TH" . 4)
+ ("FR" . 5)
+ ("SA" . 6))))
+ (when (and rrule (string-match "BYDAY=\\([^;]+\\)" rrule))
+ (let ((bydays (split-string (match-string 1 rrule) ",")))
+ (seq-map
+ (lambda (x) (cdr (assoc x weekday-map)))
+ (seq-filter (lambda (x) (string-match "^[A-Z]\\{2\\}$" x)) bydays))))))
(cl-defmethod gnus-icalendar-event:start ((event gnus-icalendar-event))
(format-time-string "%Y-%m-%d %H:%M" (gnus-icalendar-event:start-time event)))
@@ -162,8 +180,10 @@
(or (member (attendee-name prop) name-or-email)
(let ((att-email (attendee-email prop)))
(gnus-icalendar-find-if
- (lambda (email)
- (string-match email att-email))
+ (lambda (str-or-fun)
+ (if (functionp str-or-fun)
+ (funcall str-or-fun att-email)
+ (string-match str-or-fun att-email)))
name-or-email))))))
(gnus-icalendar-find-if #'attendee-prop-matches-p event-props))))
@@ -244,7 +264,14 @@
(map-property ical-property))
args)))))
(mapc #'accumulate-args prop-map)
- (apply #'make-instance event-class args))))
+ (apply
+ #'make-instance
+ event-class
+ (cl-loop for slot in (eieio-class-slots event-class)
+ for keyword = (intern
+ (format ":%s" (eieio-slot-descriptor-name slot)))
+ when (plist-member args keyword)
+ append (list keyword (plist-get args keyword)))))))
(defun gnus-icalendar-event-from-buffer (buf &optional attendee-name-or-email)
"Parse RFC5545 iCalendar in buffer BUF and return an event object.
@@ -312,7 +339,8 @@ status will be retrieved from the first matching attendee record."
(unless (gnus-icalendar-find-if (lambda (x) (string-match "^ATTENDEE" x))
reply-event-lines)
- (error "Could not find an event attendee matching given identity"))
+ (lwarn 'gnus-icalendar :warning
+ "Could not find an event attendee matching given identity"))
(mapconcat #'identity `("BEGIN:VEVENT"
,@(nreverse reply-event-lines)
@@ -400,21 +428,26 @@ Return nil for non-recurring EVENT."
(when org-freq
(format "+%s%s" (gnus-icalendar-event:recurring-interval event) org-freq)))))
-(cl-defmethod gnus-icalendar-event:org-timestamp ((event gnus-icalendar-event))
- "Build `org-mode' timestamp from EVENT start/end dates and recurrence info."
- (let* ((start (gnus-icalendar-event:start-time event))
- (end (gnus-icalendar-event:end-time event))
- (start-date (format-time-string "%Y-%m-%d" start))
+(defun gnus-icalendar--find-day (start-date end-date day)
+ (let ((time-1-day 86400))
+ (if (= (decoded-time-weekday (decode-time start-date))
+ day)
+ (list start-date end-date)
+ (gnus-icalendar--find-day (time-add start-date time-1-day)
+ (time-add end-date time-1-day)
+ day))))
+
+(defun gnus-icalendar-event--org-timestamp (start end org-repeat)
+ (let* ((start-date (format-time-string "%Y-%m-%d" start))
(start-time (format-time-string "%H:%M" start))
(start-at-midnight (string= start-time "00:00"))
(end-date (format-time-string "%Y-%m-%d" end))
(end-time (format-time-string "%H:%M" end))
(end-at-midnight (string= end-time "00:00"))
(start-end-date-diff
- (time-to-number-of-days (time-subtract
- (org-time-string-to-time end-date)
- (org-time-string-to-time start-date))))
- (org-repeat (gnus-icalendar-event:org-repeat event))
+ (time-to-number-of-days
+ (time-subtract (org-time-string-to-time end-date)
+ (org-time-string-to-time start-date))))
(repeat (if org-repeat (concat " " org-repeat) ""))
(time-1-day 86400))
@@ -445,7 +478,31 @@ Return nil for non-recurring EVENT."
;; A .:. - A .:. -> A .:.-.:.
;; A .:. - B .:.
((zerop start-end-date-diff) (format "<%s %s-%s%s>" start-date start-time end-time repeat))
- (t (format "<%s %s>--<%s %s>" start-date start-time end-date end-time)))))
+ (t (format "<%s %s>--<%s %s>" start-date start-time end-date end-time))))
+ )
+
+(cl-defmethod gnus-icalendar-event:org-timestamp ((event gnus-icalendar-event))
+ "Build `org-mode' timestamp from EVENT start/end dates and recurrence info."
+ ;; if org-repeat +1d or +1w and byday: generate one timestamp per
+ ;; byday, starting at start-date. Change +1d to +7d.
+ (let ((start (gnus-icalendar-event:start-time event))
+ (end (gnus-icalendar-event:end-time event))
+ (org-repeat (gnus-icalendar-event:org-repeat event))
+ (recurring-days (gnus-icalendar-event:recurring-days event)))
+ (if (and (or (string= org-repeat "+1d")
+ (string= org-repeat "+1w"))
+ recurring-days)
+ (let ((repeat "+1w")
+ (dates (seq-sort-by
+ 'car
+ 'time-less-p
+ (seq-map (lambda (x)
+ (gnus-icalendar--find-day start end x))
+ recurring-days))))
+ (mapconcat (lambda (x)
+ (gnus-icalendar-event--org-timestamp (car x) (cadr x)
+ repeat)) dates "\n"))
+ (gnus-icalendar-event--org-timestamp start end org-repeat))))
(defun gnus-icalendar--format-summary-line (summary &optional location)
(if location
@@ -715,9 +772,8 @@ These will be used to retrieve the RSVP information from ical events."
(lambda (x) (if (listp x) x (list x)))
(list user-full-name (regexp-quote user-mail-address)
;; NOTE: these can be lists
- gnus-ignored-from-addresses ; already regexp-quoted
- (unless (functionp message-alternative-emails) ; String or function.
- message-alternative-emails)
+ gnus-ignored-from-addresses ; String or function.
+ message-alternative-emails ; String or function.
(mapcar #'regexp-quote gnus-icalendar-additional-identities)))))
;; TODO: make the template customizable
@@ -756,7 +812,7 @@ These will be used to retrieve the RSVP information from ical events."
`(let ((,charset (cdr (assoc 'charset (mm-handle-type ,handle)))))
(with-temp-buffer
(mm-insert-part ,handle)
- (when (string= (downcase ,charset) "utf-8")
+ (when (and ,charset (string= (downcase ,charset) "utf-8"))
(decode-coding-region (point-min) (point-max) 'utf-8))
,@body))))
@@ -814,7 +870,7 @@ These will be used to retrieve the RSVP information from ical events."
(let ((subject (concat (capitalize (symbol-name status))
": " (gnus-icalendar-event:summary event))))
- (with-current-buffer (get-buffer-create gnus-icalendar-reply-bufname)
+ (with-current-buffer (gnus-get-buffer-create gnus-icalendar-reply-bufname)
(delete-region (point-min) (point-max))
(insert reply)
(fold-icalendar-buffer)