//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format. //Copyright (C) 2024 HTWKalender support@htwkalender.de //This program is free software: you can redistribute it and/or modify //it under the terms of the GNU Affero 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, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //GNU Affero General Public License for more details. //You should have received a copy of the GNU Affero General Public License //along with this program. If not, see . package ical import ( ics "github.com/arran4/golang-ical" "htwkalender/ical/model" "htwkalender/ical/service/functions" "log/slog" "net/url" "strings" _ "time/tzdata" ) const ( Thunderbird = "Thunderbird" GoogleCalendar = "Google-Calendar-Importer" ) // Adds a user-agent specific description and ALTREP to the calendar event. func addUserAgentSpecificDescription(vEvent *ics.VEvent, event model.Event, userAgent string) { description, altrep := generateDescription(event, userAgent) if userAgentType := identifyUserAgent(userAgent); userAgentType == Thunderbird && altrep != "" { vEvent.AddProperty(ics.ComponentPropertyDescription, description, ics.WithAlternativeRepresentation(buildDataURL(altrep))) } else { vEvent.AddProperty(ics.ComponentPropertyDescription, description) } } // Generates a description and ALTREP (alternative representation) based on the user agent. func generateDescription(event model.Event, userAgent string) (string, string) { plainDescription := buildPlainTextDescription(event) switch identifyUserAgent(userAgent) { case Thunderbird: htmlDescription := generateHTMLDescriptionForThunderbird(event) altrep := "text/html," + url.PathEscape(htmlDescription) return plainDescription, altrep case GoogleCalendar: plainDescription += generateRoomLinksForGoogleCalendar(event.Rooms) } return plainDescription, "" } // Builds a plain text description of the event details. func buildPlainTextDescription(event model.Event) string { var description strings.Builder if !functions.OnlyWhitespace(event.Prof) { description.WriteString("Profs: " + event.Prof + "\n") } if !functions.OnlyWhitespace(event.Course) { description.WriteString("Gruppen: " + event.Course + "\n") } if !functions.OnlyWhitespace(event.EventType) { description.WriteString("Typ: " + event.EventType + event.Compulsory + "\n") } if !functions.OnlyWhitespace(event.Notes) { description.WriteString("Notizen: " + event.Notes + "\n") } return description.String() } // Generates an HTML description for Thunderbird users. func generateHTMLDescriptionForThunderbird(event model.Event) string { var htmlDescription strings.Builder if !functions.OnlyWhitespace(event.Prof) { htmlDescription.WriteString("Profs: " + event.Prof + "
") } if !functions.OnlyWhitespace(event.Course) { htmlDescription.WriteString("Gruppen: " + event.Course + "
") } if !functions.OnlyWhitespace(event.EventType) { htmlDescription.WriteString("Typ: " + event.EventType + event.Compulsory + "
") } if !functions.OnlyWhitespace(event.Rooms) { htmlDescription.WriteString("Orte: ") for _, room := range functions.SeperateRoomString(event.Rooms) { roomLink := functions.MapRoom(room, true) _, err := htmlDescription.WriteString("" + room + " ") if err != nil { slog.Error("Error while writing to HTML description", "error", err) return "" } } } return htmlDescription.String() } // Generates room links formatted for Google Calendar. func generateRoomLinksForGoogleCalendar(rooms string) string { var description strings.Builder roomList := functions.SeperateRoomString(rooms) if !functions.OnlyWhitespace(rooms) { description.WriteString("Orte: ") for _, room := range roomList { roomLink := functions.MapRoom(room, true) description.WriteString(" " + room + " ") } } return description.String() } // Identifies the user agent type (e.g., Thunderbird or Google Calendar). func identifyUserAgent(userAgent string) string { if strings.Contains(userAgent, Thunderbird) { return Thunderbird } if strings.Contains(userAgent, GoogleCalendar) { return GoogleCalendar } return "" } // Constructs a data URL for the ALTREP representation. func buildDataURL(data string) *url.URL { return &url.URL{Scheme: "data", Opaque: data} }