mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender.git
synced 2026-01-16 19:42:26 +01:00
182 lines
5.6 KiB
Go
182 lines
5.6 KiB
Go
//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 <https://www.gnu.org/licenses/>.
|
|
|
|
package ical
|
|
|
|
import (
|
|
ics "github.com/arran4/golang-ical"
|
|
"htwkalender/ical/model"
|
|
"htwkalender/ical/service/functions"
|
|
clock "htwkalender/ical/service/functions/time"
|
|
"htwkalender/ical/service/names"
|
|
"time"
|
|
|
|
_ "time/tzdata"
|
|
)
|
|
|
|
func GenerateIcalFeed(events model.Events, mapping map[string]model.FeedCollection, userAgent string) *ics.Calendar {
|
|
|
|
cal := ics.NewCalendarFor("HTWK Kalender")
|
|
setDefaultIcalParams(cal)
|
|
|
|
europeTime, _ := time.LoadLocation("Europe/Berlin")
|
|
internalClock := clock.RealClock{}
|
|
|
|
for _, event := range events {
|
|
mapEntry, mappingFound := mapping[event.UUID]
|
|
|
|
var eventHash = functions.HashString(time.Time(event.Start).String() + time.Time(event.End).String() + event.Course + event.Name + event.Rooms)
|
|
|
|
icalEvent := ics.NewEvent(eventHash + "@htwkalender.de")
|
|
|
|
icalEvent.SetDtStampTime(internalClock.Now().Local().In(europeTime))
|
|
icalEvent.SetStartAt(time.Time(event.Start).Local().In(europeTime))
|
|
icalEvent.SetEndAt(time.Time(event.End).Local().In(europeTime))
|
|
|
|
if mappingFound {
|
|
addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertySummary, replaceNameIfUserDefined(&event, mapEntry))
|
|
addAlarmIfSpecified(icalEvent, event, mapEntry, internalClock)
|
|
} else {
|
|
addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertySummary, event.Name)
|
|
}
|
|
|
|
addUserAgentSpecificDescription(icalEvent, event, userAgent)
|
|
addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertyLocation, event.Rooms)
|
|
|
|
cal.AddVEvent(icalEvent)
|
|
|
|
}
|
|
return cal
|
|
}
|
|
|
|
func setDefaultIcalParams(cal *ics.Calendar) {
|
|
cal.SetMethod(ics.MethodPublish)
|
|
cal.SetProductId("-//HTWK Kalender//htwkalender.de//DE")
|
|
cal.SetTzid("Europe/Berlin")
|
|
cal.SetXWRCalName("HTWK Kalender")
|
|
cal.SetXWRTimezone("Europe/Berlin")
|
|
cal.SetVersion("2.0")
|
|
cal.SetCalscale("GREGORIAN")
|
|
|
|
vTimeZone := ics.NewTimezone("Europe/Berlin")
|
|
|
|
vTimeZone.Components = []ics.Component{
|
|
&ics.Daylight{
|
|
ComponentBase: ics.ComponentBase{
|
|
Properties: []ics.IANAProperty{
|
|
{
|
|
BaseProperty: ics.BaseProperty{
|
|
IANAToken: string(ics.PropertyDtstart),
|
|
Value: "19700329T020000",
|
|
},
|
|
},
|
|
{
|
|
BaseProperty: ics.BaseProperty{
|
|
IANAToken: string(ics.PropertyRrule),
|
|
Value: "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU",
|
|
},
|
|
},
|
|
{
|
|
BaseProperty: ics.BaseProperty{
|
|
IANAToken: string(ics.PropertyTzname),
|
|
Value: "CEST",
|
|
},
|
|
},
|
|
{
|
|
BaseProperty: ics.BaseProperty{
|
|
IANAToken: string(ics.PropertyTzoffsetfrom),
|
|
Value: "+0100",
|
|
},
|
|
},
|
|
{
|
|
BaseProperty: ics.BaseProperty{
|
|
IANAToken: string(ics.PropertyTzoffsetto),
|
|
Value: "+0200",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
&ics.Standard{
|
|
ComponentBase: ics.ComponentBase{
|
|
Properties: []ics.IANAProperty{
|
|
{
|
|
BaseProperty: ics.BaseProperty{
|
|
IANAToken: string(ics.PropertyDtstart),
|
|
Value: "19701025T030000",
|
|
},
|
|
},
|
|
{
|
|
BaseProperty: ics.BaseProperty{
|
|
IANAToken: string(ics.PropertyRrule),
|
|
Value: "FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU",
|
|
},
|
|
},
|
|
{
|
|
BaseProperty: ics.BaseProperty{
|
|
IANAToken: string(ics.PropertyTzname),
|
|
Value: "CET",
|
|
},
|
|
},
|
|
{
|
|
BaseProperty: ics.BaseProperty{
|
|
IANAToken: string(ics.PropertyTzoffsetfrom),
|
|
Value: "+0200",
|
|
},
|
|
},
|
|
{
|
|
BaseProperty: ics.BaseProperty{
|
|
IANAToken: string(ics.PropertyTzoffsetto),
|
|
Value: "+0100",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
cal.AddVTimezone(vTimeZone)
|
|
}
|
|
|
|
// AddPropertyIfNotEmpty adds a property to the component if the value is not empty
|
|
// or contains only whitespaces
|
|
func addPropertyIfNotEmpty(component *ics.VEvent, key ics.ComponentProperty, value string) {
|
|
if !functions.OnlyWhitespace(value) {
|
|
component.AddProperty(key, value)
|
|
}
|
|
}
|
|
|
|
// if reminder is specified in the configuration for this event, an alarm will be added to the event
|
|
func addAlarmIfSpecified(icalEvent *ics.VEvent, event model.Event, mapping model.FeedCollection, clock clock.Clock) {
|
|
// if event.Start > now
|
|
// then add alarm
|
|
if time.Time(event.Start).Local().After(clock.Now().Local()) && mapping.Reminder {
|
|
alarm := icalEvent.AddAlarm()
|
|
alarm.AddProperty(ics.ComponentPropertyTrigger, "-P0DT0H15M0S")
|
|
alarm.AddProperty(ics.ComponentPropertyAction, "DISPLAY")
|
|
alarm.AddProperty(ics.ComponentPropertyDescription, "Next course: "+replaceNameIfUserDefined(&event, mapping)+" in "+event.Rooms)
|
|
}
|
|
}
|
|
|
|
// replaceNameIfUserDefined replaces the name of the event with the user defined name if it is not empty
|
|
// all contained template strings will be replaced with the corresponding values from the event
|
|
func replaceNameIfUserDefined(event *model.Event, mapping model.FeedCollection) string {
|
|
if !functions.OnlyWhitespace(mapping.UserDefinedName) {
|
|
return names.ReplaceTemplateSubStrings(mapping.UserDefinedName, *event)
|
|
}
|
|
return event.Name
|
|
}
|