Better caldav support #73

Merged
konrad merged 82 commits from feature/better-caldav-support into master 2019-05-22 17:48:49 +00:00
4 changed files with 65 additions and 11 deletions
Showing only changes of commit 0dac93ce91 - Show all commits

View File

@ -194,7 +194,7 @@ Sorry for some of them being in German, I'll tranlate them at some point.
* [ ] Cleanup the whole mess I made with the handlers and storage providers etc -> Probably a good idea to create a seperate storage provider etc for lists and tasks
* [ ] Check if only needed things are queried from the db when accessing dav (for ex. no need to get all tasks when we actually only need the title)
* [x] Fix OPTIONS Requests to the rest of the api being broken
* [ ] Parse all props defined in rfc5545
* [x] Parse all props defined in rfc5545
* [ ] COMPLETED -> Need to actually save the time the task was completed
* [ ] Tests

View File

@ -17,11 +17,13 @@
package caldav
import (
"bytes"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/web/handler"
"github.com/labstack/echo/v4"
"github.com/samedi/caldav-go"
"io/ioutil"
"strconv"
"strings"
)
@ -53,6 +55,22 @@ func ListHandler(c echo.Context) error {
user: &u,
}
// Try to parse a task from the request payload
body, _ := ioutil.ReadAll(c.Request().Body)
// Restore the io.ReadCloser to its original state
c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(body))
// Parse it
vtodo := string(body)
if vtodo != "" && strings.HasPrefix(vtodo, `BEGIN:VCALENDAR`) {
storage.task, err = parseTaskFromVTODO(vtodo)
if err != nil {
log.Log.Error(err)
return echo.ErrInternalServerError
}
}
//fmt.Printf("[CALDAV] Request Body: %v", string(body))
/*
This is the request body evolution sends to get a list with all calendars (=lists with tasks in our case):
@ -101,10 +119,6 @@ func ListHandler(c echo.Context) error {
// <D:status>HTTP/1.1 404 Not Found</D:status>
// </D:propstat>
//buf := new(bytes.Buffer)
//buf.ReadFrom(c.Request().Body)
//fmt.Printf("[CALDAV] Request Body: %v", buf.String())
/*
More debugging resulted in this:
@ -225,6 +239,36 @@ func ListHandler(c echo.Context) error {
*/
/*
Marking a task as done:
BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
VERSION:2.0
BEGIN:VTODO
UID:3ada92f28b4ceda38562ebf047c6ff05400d4c572352a
DTSTAMP:20190511T183631
DTSTART:19700101T000000
DTEND:19700101T000000
SUMMARY:sdgs
ORGANIZER;CN=:user
CREATED:20190511T183631
PRIORITY:0
LAST-MODIFIED:20190512T193428Z
COMPLETED:20190512T193428Z
PERCENT-COMPLETE:100
STATUS:COMPLETED
END:VTODO
END:VCALENDAR
*/
// Looks like when trying to update a task, the client makes a request with a different uuid.
// This would then result in Vikunja not finding the task the client wants to update (since that uid does not exist).
// I've yet to find out why.
caldav.SetupStorage(storage)
caldav.SetupUser("dav/principals/" + u.Username)
response := caldav.HandleRequest(c.Request())

View File

@ -116,7 +116,9 @@ func (vcls *VikunjaCaldavListStorage) GetResource(rpath string) (*data.Resource,
// If the task is not nil, we need to get the task and not the list
if vcls.task != nil {
task, err := models.GetTaskSimple(vcls.task)
// save and override the updated unix date to not break any later etag checks
updated := vcls.task.Updated
task, err := models.GetTaskSimple(&models.ListTask{ID: vcls.task.ID, UID: vcls.task.UID})
if err != nil {
if models.IsErrListTaskDoesNotExist(err) {
return nil, false, errs.ResourceNotFoundError
@ -125,6 +127,9 @@ func (vcls *VikunjaCaldavListStorage) GetResource(rpath string) (*data.Resource,
}
vcls.task = &task
if updated > 0 {
vcls.task.Updated = updated
}
rr := VikunjaListResourceAdapter{
list: vcls.list,
task: &task,
@ -247,12 +252,14 @@ func (vlra *VikunjaListResourceAdapter) CalculateEtag() string {
fmt.Printf("[CALDAV] CalculateEtag\n")
// Return the etag of a task if we have one
if vlra.task != nil {
// Extra prefixes... ough...
// Dirty hacks, here we come...
return `"` + strconv.FormatInt(vlra.task.ID, 10) + `-` + strconv.FormatInt(vlra.task.Updated, 10) + vlra.task.UID + `"`
return `"` + strconv.FormatInt(vlra.task.ID, 10) + `-` + strconv.FormatInt(vlra.task.Updated, 10) + `"`
}
// FIXME: because of this, we need to update the list updated timestamp everytime a task on the list is added or modified.
// For now, we randomize this to prevent client-side caching
// This also returns the etag of the list, and not of the task,
// which becomes problematic because the client uses this etag (= the one from the list) to make
// Requests to update a task. These do not match and thus updating a task fails.
return strconv.FormatInt(vlra.list.ID, 10) + `-` + strconv.FormatInt(vlra.list.Updated, 10) + utils.MakeRandomString(10)
}

View File

@ -20,7 +20,6 @@ import (
"code.vikunja.io/api/pkg/caldav"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/utils"
"github.com/laurent22/ical-go/ical"
"strconv"
"time"
@ -37,7 +36,7 @@ func getCaldavTodosForTasks(tasks []*models.ListTask) string {
caldavtodos = append(caldavtodos, &caldav.Todo{
TimestampUnix: t.Updated,
UID: utils.Sha256(strconv.FormatInt(t.ID, 10)),
UID: t.UID,
Summary: t.Text,
Description: t.Description,
CompletedUnix: 0,
@ -109,6 +108,10 @@ func parseTaskFromVTODO(content string) (vTask *models.ListTask, err error) {
}
func caldavTimeToUnixTimestamp(tstring string) int64 {
if tstring == "" {
return 0
}
t, err := time.Parse(caldav.DateFormat, tstring)
if err != nil {
log.Log.Errorf("Error while parsing caldav time %s to unix time: %s", tstring, err)