2019-12-04 20:39:56 +01:00
|
|
|
// Vikunja is a todo-list application to facilitate your life.
|
2020-01-09 18:33:22 +01:00
|
|
|
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
|
2018-11-26 21:17:33 +01:00
|
|
|
//
|
2019-12-04 20:39:56 +01:00
|
|
|
// This program 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.
|
2018-11-26 21:17:33 +01:00
|
|
|
//
|
2019-12-04 20:39:56 +01:00
|
|
|
// 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 General Public License for more details.
|
2018-11-26 21:17:33 +01:00
|
|
|
//
|
2019-12-04 20:39:56 +01:00
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2018-11-26 21:17:33 +01:00
|
|
|
|
2018-11-03 16:05:45 +01:00
|
|
|
package caldav
|
|
|
|
|
|
|
|
import (
|
2019-05-22 19:48:48 +02:00
|
|
|
"code.vikunja.io/api/pkg/models"
|
2018-11-03 16:05:45 +01:00
|
|
|
"code.vikunja.io/api/pkg/utils"
|
2019-05-22 19:48:48 +02:00
|
|
|
"fmt"
|
2018-11-03 16:05:45 +01:00
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2019-05-22 19:48:48 +02:00
|
|
|
// DateFormat ist the caldav date format
|
|
|
|
const DateFormat = `20060102T150405`
|
|
|
|
|
2018-11-03 16:05:45 +01:00
|
|
|
// Event holds a single caldav event
|
|
|
|
type Event struct {
|
|
|
|
Summary string
|
|
|
|
Description string
|
|
|
|
UID string
|
|
|
|
Alarms []Alarm
|
|
|
|
|
|
|
|
TimestampUnix int64
|
|
|
|
StartUnix int64
|
|
|
|
EndUnix int64
|
|
|
|
}
|
|
|
|
|
2019-05-22 19:48:48 +02:00
|
|
|
// Todo holds a single VTODO
|
|
|
|
type Todo struct {
|
|
|
|
// Required
|
|
|
|
TimestampUnix int64
|
|
|
|
UID string
|
|
|
|
|
|
|
|
// Optional
|
|
|
|
Summary string
|
|
|
|
Description string
|
|
|
|
CompletedUnix int64
|
|
|
|
Organizer *models.User
|
|
|
|
Priority int64 // 0-9, 1 is highest
|
|
|
|
RelatedToUID string
|
|
|
|
|
|
|
|
StartUnix int64
|
|
|
|
EndUnix int64
|
|
|
|
DueDateUnix int64
|
|
|
|
Duration time.Duration
|
|
|
|
|
|
|
|
CreatedUnix int64
|
|
|
|
UpdatedUnix int64 // last-mod
|
|
|
|
}
|
|
|
|
|
2018-11-03 16:05:45 +01:00
|
|
|
// Alarm holds infos about an alarm from a caldav event
|
|
|
|
type Alarm struct {
|
|
|
|
TimeUnix int64
|
|
|
|
Description string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Config is the caldav calendar config
|
|
|
|
type Config struct {
|
|
|
|
Name string
|
|
|
|
ProdID string
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseEvents parses an array of caldav events and gives them back as string
|
|
|
|
func ParseEvents(config *Config, events []*Event) (caldavevents string) {
|
|
|
|
caldavevents += `BEGIN:VCALENDAR
|
|
|
|
VERSION:2.0
|
|
|
|
METHOD:PUBLISH
|
|
|
|
X-PUBLISHED-TTL:PT4H
|
|
|
|
X-WR-CALNAME:` + config.Name + `
|
|
|
|
PRODID:-//` + config.ProdID + `//EN`
|
|
|
|
|
|
|
|
for _, e := range events {
|
|
|
|
|
|
|
|
if e.UID == "" {
|
|
|
|
e.UID = makeCalDavTimeFromUnixTime(e.TimestampUnix) + utils.Sha256(e.Summary)
|
|
|
|
}
|
|
|
|
|
|
|
|
caldavevents += `
|
|
|
|
BEGIN:VEVENT
|
|
|
|
UID:` + e.UID + `
|
|
|
|
SUMMARY:` + e.Summary + `
|
|
|
|
DESCRIPTION:` + e.Description + `
|
|
|
|
DTSTAMP:` + makeCalDavTimeFromUnixTime(e.TimestampUnix) + `
|
|
|
|
DTSTART:` + makeCalDavTimeFromUnixTime(e.StartUnix) + `
|
|
|
|
DTEND:` + makeCalDavTimeFromUnixTime(e.EndUnix)
|
|
|
|
|
|
|
|
for _, a := range e.Alarms {
|
|
|
|
if a.Description == "" {
|
|
|
|
a.Description = e.Summary
|
|
|
|
}
|
|
|
|
|
|
|
|
caldavevents += `
|
|
|
|
BEGIN:VALARM
|
|
|
|
TRIGGER:` + calcAlarmDateFromReminder(e.StartUnix, a.TimeUnix) + `
|
|
|
|
ACTION:DISPLAY
|
|
|
|
DESCRIPTION:` + a.Description + `
|
|
|
|
END:VALARM`
|
|
|
|
}
|
|
|
|
caldavevents += `
|
|
|
|
END:VEVENT`
|
|
|
|
}
|
|
|
|
|
|
|
|
caldavevents += `
|
|
|
|
END:VCALENDAR` // Need a line break
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-05-22 19:48:48 +02:00
|
|
|
// ParseTodos returns a caldav vcalendar string with todos
|
|
|
|
func ParseTodos(config *Config, todos []*Todo) (caldavtodos string) {
|
|
|
|
caldavtodos = `BEGIN:VCALENDAR
|
|
|
|
VERSION:2.0
|
|
|
|
METHOD:PUBLISH
|
|
|
|
X-PUBLISHED-TTL:PT4H
|
|
|
|
X-WR-CALNAME:` + config.Name + `
|
|
|
|
PRODID:-//` + config.ProdID + `//EN`
|
|
|
|
|
|
|
|
for _, t := range todos {
|
|
|
|
if t.UID == "" {
|
|
|
|
t.UID = makeCalDavTimeFromUnixTime(t.TimestampUnix) + utils.Sha256(t.Summary)
|
|
|
|
}
|
|
|
|
|
|
|
|
caldavtodos += `
|
|
|
|
BEGIN:VTODO
|
|
|
|
UID:` + t.UID + `
|
|
|
|
DTSTAMP:` + makeCalDavTimeFromUnixTime(t.TimestampUnix) + `
|
|
|
|
SUMMARY:` + t.Summary
|
|
|
|
|
|
|
|
if t.StartUnix != 0 {
|
|
|
|
caldavtodos += `
|
|
|
|
DTSTART: ` + makeCalDavTimeFromUnixTime(t.StartUnix)
|
|
|
|
}
|
|
|
|
if t.EndUnix != 0 {
|
|
|
|
caldavtodos += `
|
|
|
|
DTEND: ` + makeCalDavTimeFromUnixTime(t.EndUnix)
|
|
|
|
}
|
|
|
|
if t.Description != "" {
|
|
|
|
caldavtodos += `
|
|
|
|
DESCRIPTION:` + t.Description
|
|
|
|
}
|
|
|
|
if t.CompletedUnix != 0 {
|
|
|
|
caldavtodos += `
|
|
|
|
COMPLETED: ` + makeCalDavTimeFromUnixTime(t.CompletedUnix)
|
|
|
|
}
|
|
|
|
if t.Organizer != nil {
|
|
|
|
caldavtodos += `
|
|
|
|
ORGANIZER;CN=:` + t.Organizer.Username
|
|
|
|
}
|
|
|
|
|
|
|
|
if t.RelatedToUID != "" {
|
|
|
|
caldavtodos += `
|
|
|
|
RELATED-TO:` + t.RelatedToUID
|
|
|
|
}
|
|
|
|
|
|
|
|
if t.DueDateUnix != 0 {
|
|
|
|
caldavtodos += `
|
|
|
|
DUE:` + makeCalDavTimeFromUnixTime(t.DueDateUnix)
|
|
|
|
}
|
|
|
|
|
|
|
|
if t.CreatedUnix != 0 {
|
|
|
|
caldavtodos += `
|
|
|
|
CREATED:` + makeCalDavTimeFromUnixTime(t.CreatedUnix)
|
|
|
|
}
|
|
|
|
|
|
|
|
if t.Duration != 0 {
|
|
|
|
caldavtodos += `
|
|
|
|
DURATION:PT` + fmt.Sprintf("%.6f", t.Duration.Hours()) + `H` + fmt.Sprintf("%.6f", t.Duration.Minutes()) + `M` + fmt.Sprintf("%.6f", t.Duration.Seconds()) + `S`
|
|
|
|
}
|
|
|
|
|
|
|
|
if t.Priority != 0 {
|
|
|
|
caldavtodos += `
|
|
|
|
PRIORITY:` + strconv.Itoa(int(t.Priority))
|
|
|
|
}
|
|
|
|
|
|
|
|
caldavtodos += `
|
|
|
|
LAST-MODIFIED:` + makeCalDavTimeFromUnixTime(t.UpdatedUnix)
|
|
|
|
|
|
|
|
caldavtodos += `
|
|
|
|
END:VTODO`
|
|
|
|
}
|
|
|
|
|
|
|
|
caldavtodos += `
|
|
|
|
END:VCALENDAR` // Need a line break
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-11-03 16:05:45 +01:00
|
|
|
func makeCalDavTimeFromUnixTime(unixtime int64) (caldavtime string) {
|
2018-12-16 14:21:32 +01:00
|
|
|
tz, _ := time.LoadLocation("UTC")
|
|
|
|
tm := time.Unix(unixtime, 0).In(tz)
|
2019-05-22 19:48:48 +02:00
|
|
|
return tm.Format(DateFormat)
|
2018-11-03 16:05:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func calcAlarmDateFromReminder(eventStartUnix, reminderUnix int64) (alarmTime string) {
|
|
|
|
if eventStartUnix > reminderUnix {
|
|
|
|
alarmTime += `-`
|
|
|
|
}
|
|
|
|
alarmTime += `PT`
|
|
|
|
diff := eventStartUnix - reminderUnix
|
|
|
|
if diff < 0 { // Make it positive
|
|
|
|
diff = diff * -1
|
|
|
|
}
|
|
|
|
alarmTime += strconv.Itoa(int(diff/60)) + "M"
|
|
|
|
return
|
|
|
|
}
|