diff --git a/pkg/caldav/caldav.go b/pkg/caldav/caldav.go
index 59c1149a0e..7c79d614e5 100644
--- a/pkg/caldav/caldav.go
+++ b/pkg/caldav/caldav.go
@@ -216,7 +216,7 @@ DURATION:PT` + fmt.Sprintf("%.6f", t.Duration.Hours()) + `H` + fmt.Sprintf("%.6f
if t.Priority != 0 {
caldavtodos += `
-PRIORITY:` + strconv.Itoa(int(t.Priority))
+PRIORITY:` + strconv.Itoa(mapPriorityToCaldav(t.Priority))
}
caldavtodos += `
diff --git a/pkg/caldav/caldav_test.go b/pkg/caldav/caldav_test.go
index 7c9a526033..b78e8bed06 100644
--- a/pkg/caldav/caldav_test.go
+++ b/pkg/caldav/caldav_test.go
@@ -375,6 +375,39 @@ COMPLETED:20181201T013024
STATUS:COMPLETED
LAST-MODIFIED:00010101T000000
END:VTODO
+END:VCALENDAR`,
+ },
+ {
+ name: "with priority",
+ args: args{
+ config: &Config{
+ Name: "test",
+ ProdID: "RandomProdID which is not random",
+ },
+ todos: []*Todo{
+ {
+ Summary: "Todo #1",
+ Description: "Lorem Ipsum",
+ UID: "randommduid",
+ Priority: 1,
+ Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()),
+ },
+ },
+ },
+ wantCaldavtasks: `BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:PUBLISH
+X-PUBLISHED-TTL:PT4H
+X-WR-CALNAME:test
+PRODID:-//RandomProdID which is not random//EN
+BEGIN:VTODO
+UID:randommduid
+DTSTAMP:20181201T011204
+SUMMARY:Todo #1
+DESCRIPTION:Lorem Ipsum
+PRIORITY:9
+LAST-MODIFIED:00010101T000000
+END:VTODO
END:VCALENDAR`,
},
}
diff --git a/pkg/routes/caldav/parsing.go b/pkg/caldav/parsing.go
similarity index 87%
rename from pkg/routes/caldav/parsing.go
rename to pkg/caldav/parsing.go
index 083762db6b..0b56674344 100644
--- a/pkg/routes/caldav/parsing.go
+++ b/pkg/caldav/parsing.go
@@ -21,21 +21,20 @@ import (
"strings"
"time"
- "code.vikunja.io/api/pkg/caldav"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"github.com/laurent22/ical-go"
)
-func getCaldavTodosForTasks(list *models.List, listTasks []*models.Task) string {
+func GetCaldavTodosForTasks(list *models.List, listTasks []*models.Task) string {
// Make caldav todos from Vikunja todos
- var caldavtodos []*caldav.Todo
+ var caldavtodos []*Todo
for _, t := range listTasks {
duration := t.EndDate.Sub(t.StartDate)
- caldavtodos = append(caldavtodos, &caldav.Todo{
+ caldavtodos = append(caldavtodos, &Todo{
Timestamp: t.Updated,
UID: t.UID,
Summary: t.Title,
@@ -52,15 +51,15 @@ func getCaldavTodosForTasks(list *models.List, listTasks []*models.Task) string
})
}
- caldavConfig := &caldav.Config{
+ caldavConfig := &Config{
Name: list.Title,
ProdID: "Vikunja Todo App",
}
- return caldav.ParseTodos(caldavConfig, caldavtodos)
+ return ParseTodos(caldavConfig, caldavtodos)
}
-func parseTaskFromVTODO(content string) (vTask *models.Task, err error) {
+func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) {
parsed, err := ical.ParseCalendar(content)
if err != nil {
return nil, err
@@ -78,13 +77,15 @@ func parseTaskFromVTODO(content string) (vTask *models.Task, err error) {
}
}
- // Parse the UID
+ // Parse the priority
var priority int64
if _, ok := task["PRIORITY"]; ok {
- priority, err = strconv.ParseInt(task["PRIORITY"], 10, 64)
+ priorityParsed, err := strconv.ParseInt(task["PRIORITY"], 10, 64)
if err != nil {
return nil, err
}
+
+ priority = parseVTODOPriority(priorityParsed)
}
// Parse the enddate
@@ -118,7 +119,7 @@ func caldavTimeToTimestamp(tstring string) time.Time {
return time.Time{}
}
- format := caldav.DateFormat
+ format := DateFormat
if strings.HasSuffix(tstring, "Z") {
format = `20060102T150405Z`
diff --git a/pkg/caldav/parsing_test.go b/pkg/caldav/parsing_test.go
new file mode 100644
index 0000000000..99bb61a626
--- /dev/null
+++ b/pkg/caldav/parsing_test.go
@@ -0,0 +1,101 @@
+// Vikunja is a to-do list application to facilitate your life.
+// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public Licensee 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 Licensee for more details.
+//
+// You should have received a copy of the GNU Affero General Public Licensee
+// along with this program. If not, see .
+
+package caldav
+
+import (
+ "testing"
+ "time"
+
+ "code.vikunja.io/api/pkg/config"
+ "code.vikunja.io/api/pkg/models"
+ "gopkg.in/d4l3k/messagediff.v1"
+)
+
+func TestParseTaskFromVTODO(t *testing.T) {
+ type args struct {
+ content string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantVTask *models.Task
+ wantErr bool
+ }{
+ {
+ name: "normal",
+ args: args{content: `BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:PUBLISH
+X-PUBLISHED-TTL:PT4H
+X-WR-CALNAME:test
+PRODID:-//RandomProdID which is not random//EN
+BEGIN:VTODO
+UID:randomuid
+DTSTAMP:20181201T011204
+SUMMARY:Todo #1
+DESCRIPTION:Lorem Ipsum
+LAST-MODIFIED:00010101T000000
+END:VTODO
+END:VCALENDAR`,
+ },
+ wantVTask: &models.Task{
+ Title: "Todo #1",
+ UID: "randomuid",
+ Description: "Lorem Ipsum",
+ Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
+ },
+ },
+ {
+ name: "With priority",
+ args: args{content: `BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:PUBLISH
+X-PUBLISHED-TTL:PT4H
+X-WR-CALNAME:test
+PRODID:-//RandomProdID which is not random//EN
+BEGIN:VTODO
+UID:randomuid
+DTSTAMP:20181201T011204
+SUMMARY:Todo #1
+DESCRIPTION:Lorem Ipsum
+PRIORITY:9
+LAST-MODIFIED:00010101T000000
+END:VTODO
+END:VCALENDAR`,
+ },
+ wantVTask: &models.Task{
+ Title: "Todo #1",
+ UID: "randomuid",
+ Description: "Lorem Ipsum",
+ Priority: 1,
+ Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := ParseTaskFromVTODO(tt.args.content)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ParseTaskFromVTODO() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if diff, equal := messagediff.PrettyDiff(got, tt.wantVTask); !equal {
+ t.Errorf("ParseTaskFromVTODO() gotVTask = %v, want %v, diff = %s", got, tt.wantVTask, diff)
+ }
+ })
+ }
+}
diff --git a/pkg/caldav/priority.go b/pkg/caldav/priority.go
new file mode 100644
index 0000000000..0e39c749b9
--- /dev/null
+++ b/pkg/caldav/priority.go
@@ -0,0 +1,66 @@
+// Vikunja is a to-do list application to facilitate your life.
+// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public Licensee 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 Licensee for more details.
+//
+// You should have received a copy of the GNU Affero General Public Licensee
+// along with this program. If not, see .
+
+package caldav
+
+// In caldav, priority values are an int from 0 to 9 where 1 is the highest priority and 9 the lowest. 0 is "unset".
+// Vikunja only has priorites from 0 to 5 where 0 is unset and 5 is the highest
+// See https://icalendar.org/iCalendar-RFC-5545/3-8-1-9-priority.html
+func mapPriorityToCaldav(priority int64) (caldavPriority int) {
+ switch priority {
+ case 0:
+ return 0
+ case 1: // Low
+ return 9
+ case 2: // Medium
+ return 5
+ case 3: // High
+ return 3
+ case 4: // Urgent
+ return 2
+ case 5: // DO NOW
+ return 1
+ }
+ return 0
+}
+
+// See mapPriorityToCaldav
+func parseVTODOPriority(priority int64) (vikunjaPriority int64) {
+ switch priority {
+ case 0:
+ return 0
+ case 1:
+ return 5
+ case 2:
+ return 4
+ case 3:
+ return 3
+ case 4:
+ return 3
+ case 5:
+ return 2
+ case 6:
+ return 1
+ case 7:
+ return 1
+ case 8:
+ return 1
+ case 9:
+ return 1
+ }
+
+ return 0
+}
diff --git a/pkg/caldav/priority_test.go b/pkg/caldav/priority_test.go
new file mode 100644
index 0000000000..6e59f58ff2
--- /dev/null
+++ b/pkg/caldav/priority_test.go
@@ -0,0 +1,131 @@
+// Vikunja is a to-do list application to facilitate your life.
+// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public Licensee 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 Licensee for more details.
+//
+// You should have received a copy of the GNU Affero General Public Licensee
+// along with this program. If not, see .
+
+package caldav
+
+import "testing"
+
+func Test_parseVTODOPriority(t *testing.T) {
+ tests := []struct {
+ name string
+ priority int64
+ want int64
+ }{
+ {
+ name: "unset",
+ priority: 0,
+ want: 0,
+ },
+ {
+ name: "DO NOW",
+ priority: 1,
+ want: 5,
+ },
+ {
+ name: "urgent",
+ priority: 2,
+ want: 4,
+ },
+ {
+ name: "high 1",
+ priority: 3,
+ want: 3,
+ },
+ {
+ name: "high 2",
+ priority: 4,
+ want: 3,
+ },
+ {
+ name: "medium",
+ priority: 5,
+ want: 2,
+ },
+ {
+ name: "low 1",
+ priority: 6,
+ want: 1,
+ },
+ {
+ name: "low 2",
+ priority: 7,
+ want: 1,
+ },
+ {
+ name: "low 3",
+ priority: 8,
+ want: 1,
+ },
+ {
+ name: "low 4",
+ priority: 9,
+ want: 1,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if gotVikunjaPriority := parseVTODOPriority(tt.priority); gotVikunjaPriority != tt.want {
+ t.Errorf("parseVTODOPriority() = %v, want %v", gotVikunjaPriority, tt.want)
+ }
+ })
+ }
+}
+
+func Test_mapPriorityToCaldav(t *testing.T) {
+ tests := []struct {
+ name string
+ priority int64
+ wantCaldavPriority int
+ }{
+ {
+ name: "unset",
+ priority: 0,
+ wantCaldavPriority: 0,
+ },
+ {
+ name: "low",
+ priority: 1,
+ wantCaldavPriority: 9,
+ },
+ {
+ name: "medium",
+ priority: 2,
+ wantCaldavPriority: 5,
+ },
+ {
+ name: "high",
+ priority: 3,
+ wantCaldavPriority: 3,
+ },
+ {
+ name: "urgent",
+ priority: 4,
+ wantCaldavPriority: 2,
+ },
+ {
+ name: "DO NOW",
+ priority: 5,
+ wantCaldavPriority: 1,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if gotCaldavPriority := mapPriorityToCaldav(tt.priority); gotCaldavPriority != tt.wantCaldavPriority {
+ t.Errorf("mapPriorityToCaldav() = %v, want %v", gotCaldavPriority, tt.wantCaldavPriority)
+ }
+ })
+ }
+}
diff --git a/pkg/routes/caldav/handler.go b/pkg/routes/caldav/handler.go
index 3d413f2859..8d3c4bb765 100644
--- a/pkg/routes/caldav/handler.go
+++ b/pkg/routes/caldav/handler.go
@@ -24,6 +24,7 @@ import (
"strconv"
"strings"
+ caldav2 "code.vikunja.io/api/pkg/caldav"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
@@ -66,7 +67,7 @@ func ListHandler(c echo.Context) error {
// Parse it
vtodo := string(body)
if vtodo != "" && strings.HasPrefix(vtodo, `BEGIN:VCALENDAR`) {
- storage.task, err = parseTaskFromVTODO(vtodo)
+ storage.task, err = caldav2.ParseTaskFromVTODO(vtodo)
if err != nil {
log.Error(err)
return echo.ErrInternalServerError
diff --git a/pkg/routes/caldav/listStorageProvider.go b/pkg/routes/caldav/listStorageProvider.go
index 1171aa47d6..9abe034142 100644
--- a/pkg/routes/caldav/listStorageProvider.go
+++ b/pkg/routes/caldav/listStorageProvider.go
@@ -21,8 +21,8 @@ import (
"strings"
"time"
+ "code.vikunja.io/api/pkg/caldav"
"code.vikunja.io/api/pkg/db"
-
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
user2 "code.vikunja.io/api/pkg/user"
@@ -256,7 +256,7 @@ func (vcls *VikunjaCaldavListStorage) CreateResource(rpath, content string) (*da
s := db.NewSession()
defer s.Close()
- vTask, err := parseTaskFromVTODO(content)
+ vTask, err := caldav.ParseTaskFromVTODO(content)
if err != nil {
return nil, err
}
@@ -295,7 +295,7 @@ func (vcls *VikunjaCaldavListStorage) CreateResource(rpath, content string) (*da
// UpdateResource updates a resource
func (vcls *VikunjaCaldavListStorage) UpdateResource(rpath, content string) (*data.Resource, error) {
- vTask, err := parseTaskFromVTODO(content)
+ vTask, err := caldav.ParseTaskFromVTODO(content)
if err != nil {
return nil, err
}
@@ -411,12 +411,12 @@ func (vlra *VikunjaListResourceAdapter) CalculateEtag() string {
// GetContent returns the content string of a resource (a task in our case)
func (vlra *VikunjaListResourceAdapter) GetContent() string {
if vlra.list != nil && vlra.list.Tasks != nil {
- return getCaldavTodosForTasks(vlra.list, vlra.listTasks)
+ return caldav.GetCaldavTodosForTasks(vlra.list, vlra.listTasks)
}
if vlra.task != nil {
list := models.List{Tasks: []*models.Task{vlra.task}}
- return getCaldavTodosForTasks(&list, list.Tasks)
+ return caldav.GetCaldavTodosForTasks(&list, list.Tasks)
}
return ""