| 1 | package calendar |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "strings" |
| 6 | "time" |
| 7 | ) |
| 8 | |
| 9 | // WriteBookingICS is the booking's own writer (booking spec §5.8). It |
| 10 | // takes the public view and the event's opened UID — never an |
| 11 | // EventView — so it cannot carry the host's notes, location or other |
| 12 | // attendees. WriteICS is untouched and still says METHOD:PUBLISH for |
| 13 | // calendar exports. |
| 14 | // |
| 15 | // No CN= parameters: a parameter value has quoting rules of its own |
| 16 | // (RFC 5545 §3.2, RFC 6868) that escapeText does not implement, and a |
| 17 | // name is not needed for the invitation to work — SUMMARY names the |
| 18 | // meeting and the addresses name the people. No URL:, because a |
| 19 | // management capability inside a file that syncs everywhere is |
| 20 | // delegation, not disclosure (B6). |
| 21 | func WriteBookingICS(pb publicBooking, uid string, updated time.Time, hostLocal Event) string { |
| 22 | method, status := "REQUEST", "CONFIRMED" |
| 23 | if pb.Cancelled { |
| 24 | method, status = "CANCEL", "CANCELLED" |
| 25 | } |
| 26 | var b strings.Builder |
| 27 | line(&b, "BEGIN:VCALENDAR") |
| 28 | line(&b, "VERSION:2.0") |
| 29 | line(&b, "PRODID:"+ProdID) |
| 30 | line(&b, "CALSCALE:GREGORIAN") |
| 31 | line(&b, "METHOD:"+method) |
| 32 | line(&b, "BEGIN:VEVENT") |
| 33 | if uid == "" { |
| 34 | uid = fmt.Sprintf("%d@calendar.oficina", hostLocal.ID) |
| 35 | } |
| 36 | line(&b, "UID:"+escapeText(uid)) |
| 37 | line(&b, "DTSTAMP:"+utcStamp(updated)) |
| 38 | line(&b, fmt.Sprintf("SEQUENCE:%d", pb.Rev)) |
| 39 | line(&b, "DTSTART;TZID="+hostLocal.TZ+":"+icsDateTime(hostLocal.StartLocal)) |
| 40 | line(&b, "DTEND;TZID="+hostLocal.TZ+":"+icsDateTime(hostLocal.EndLocal)) |
| 41 | line(&b, "SUMMARY:"+escapeText(pb.Title)) |
| 42 | if pb.Description != "" { |
| 43 | line(&b, "DESCRIPTION:"+escapeText(pb.Description)) |
| 44 | } |
| 45 | line(&b, "ORGANIZER:mailto:"+escapeText(pb.HostAddress)) |
| 46 | line(&b, "ATTENDEE;PARTSTAT=ACCEPTED;RSVP=FALSE:mailto:"+escapeText(pb.Address)) |
| 47 | line(&b, "STATUS:"+status) |
| 48 | line(&b, "END:VEVENT") |
| 49 | line(&b, "END:VCALENDAR") |
| 50 | return b.String() |
| 51 | } |
| 52 | |