parent
562ef9af36
commit
e21a3904ff
8 changed files with 350 additions and 17 deletions
|
@ -216,7 +216,7 @@ DURATION:PT` + fmt.Sprintf("%.6f", t.Duration.Hours()) + `H` + fmt.Sprintf("%.6f
|
||||||
|
|
||||||
if t.Priority != 0 {
|
if t.Priority != 0 {
|
||||||
caldavtodos += `
|
caldavtodos += `
|
||||||
PRIORITY:` + strconv.Itoa(int(t.Priority))
|
PRIORITY:` + strconv.Itoa(mapPriorityToCaldav(t.Priority))
|
||||||
}
|
}
|
||||||
|
|
||||||
caldavtodos += `
|
caldavtodos += `
|
||||||
|
|
|
@ -375,6 +375,39 @@ COMPLETED:20181201T013024
|
||||||
STATUS:COMPLETED
|
STATUS:COMPLETED
|
||||||
LAST-MODIFIED:00010101T000000
|
LAST-MODIFIED:00010101T000000
|
||||||
END:VTODO
|
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`,
|
END:VCALENDAR`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,21 +21,20 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/caldav"
|
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"github.com/laurent22/ical-go"
|
"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
|
// Make caldav todos from Vikunja todos
|
||||||
var caldavtodos []*caldav.Todo
|
var caldavtodos []*Todo
|
||||||
for _, t := range listTasks {
|
for _, t := range listTasks {
|
||||||
|
|
||||||
duration := t.EndDate.Sub(t.StartDate)
|
duration := t.EndDate.Sub(t.StartDate)
|
||||||
|
|
||||||
caldavtodos = append(caldavtodos, &caldav.Todo{
|
caldavtodos = append(caldavtodos, &Todo{
|
||||||
Timestamp: t.Updated,
|
Timestamp: t.Updated,
|
||||||
UID: t.UID,
|
UID: t.UID,
|
||||||
Summary: t.Title,
|
Summary: t.Title,
|
||||||
|
@ -52,15 +51,15 @@ func getCaldavTodosForTasks(list *models.List, listTasks []*models.Task) string
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
caldavConfig := &caldav.Config{
|
caldavConfig := &Config{
|
||||||
Name: list.Title,
|
Name: list.Title,
|
||||||
ProdID: "Vikunja Todo App",
|
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)
|
parsed, err := ical.ParseCalendar(content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
var priority int64
|
||||||
if _, ok := task["PRIORITY"]; ok {
|
if _, ok := task["PRIORITY"]; ok {
|
||||||
priority, err = strconv.ParseInt(task["PRIORITY"], 10, 64)
|
priorityParsed, err := strconv.ParseInt(task["PRIORITY"], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
priority = parseVTODOPriority(priorityParsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the enddate
|
// Parse the enddate
|
||||||
|
@ -118,7 +119,7 @@ func caldavTimeToTimestamp(tstring string) time.Time {
|
||||||
return time.Time{}
|
return time.Time{}
|
||||||
}
|
}
|
||||||
|
|
||||||
format := caldav.DateFormat
|
format := DateFormat
|
||||||
|
|
||||||
if strings.HasSuffix(tstring, "Z") {
|
if strings.HasSuffix(tstring, "Z") {
|
||||||
format = `20060102T150405Z`
|
format = `20060102T150405Z`
|
101
pkg/caldav/parsing_test.go
Normal file
101
pkg/caldav/parsing_test.go
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
66
pkg/caldav/priority.go
Normal file
66
pkg/caldav/priority.go
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
131
pkg/caldav/priority_test.go
Normal file
131
pkg/caldav/priority_test.go
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
caldav2 "code.vikunja.io/api/pkg/caldav"
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
@ -66,7 +67,7 @@ func ListHandler(c echo.Context) error {
|
||||||
// Parse it
|
// Parse it
|
||||||
vtodo := string(body)
|
vtodo := string(body)
|
||||||
if vtodo != "" && strings.HasPrefix(vtodo, `BEGIN:VCALENDAR`) {
|
if vtodo != "" && strings.HasPrefix(vtodo, `BEGIN:VCALENDAR`) {
|
||||||
storage.task, err = parseTaskFromVTODO(vtodo)
|
storage.task, err = caldav2.ParseTaskFromVTODO(vtodo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return echo.ErrInternalServerError
|
return echo.ErrInternalServerError
|
||||||
|
|
|
@ -21,8 +21,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.vikunja.io/api/pkg/caldav"
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
user2 "code.vikunja.io/api/pkg/user"
|
user2 "code.vikunja.io/api/pkg/user"
|
||||||
|
@ -256,7 +256,7 @@ func (vcls *VikunjaCaldavListStorage) CreateResource(rpath, content string) (*da
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
vTask, err := parseTaskFromVTODO(content)
|
vTask, err := caldav.ParseTaskFromVTODO(content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -295,7 +295,7 @@ func (vcls *VikunjaCaldavListStorage) CreateResource(rpath, content string) (*da
|
||||||
// UpdateResource updates a resource
|
// UpdateResource updates a resource
|
||||||
func (vcls *VikunjaCaldavListStorage) UpdateResource(rpath, content string) (*data.Resource, error) {
|
func (vcls *VikunjaCaldavListStorage) UpdateResource(rpath, content string) (*data.Resource, error) {
|
||||||
|
|
||||||
vTask, err := parseTaskFromVTODO(content)
|
vTask, err := caldav.ParseTaskFromVTODO(content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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)
|
// GetContent returns the content string of a resource (a task in our case)
|
||||||
func (vlra *VikunjaListResourceAdapter) GetContent() string {
|
func (vlra *VikunjaListResourceAdapter) GetContent() string {
|
||||||
if vlra.list != nil && vlra.list.Tasks != nil {
|
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 {
|
if vlra.task != nil {
|
||||||
list := models.List{Tasks: []*models.Task{vlra.task}}
|
list := models.List{Tasks: []*models.Task{vlra.task}}
|
||||||
return getCaldavTodosForTasks(&list, list.Tasks)
|
return caldav.GetCaldavTodosForTasks(&list, list.Tasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
Loading…
Reference in a new issue