diff --git a/pkg/caldav/parsing.go b/pkg/caldav/parsing.go
index 5a7b8178b..ead9ebccf 100644
--- a/pkg/caldav/parsing.go
+++ b/pkg/caldav/parsing.go
@@ -26,7 +26,7 @@ import (
"github.com/laurent22/ical-go"
)
-func GetCaldavTodosForTasks(list *models.ListWithTasksAndBuckets, listTasks []*models.Task) string {
+func GetCaldavTodosForTasks(list *models.ListWithTasksAndBuckets, listTasks []*models.TaskWithComments) string {
// Make caldav todos from Vikunja todos
var caldavtodos []*Todo
diff --git a/pkg/models/export.go b/pkg/models/export.go
index ff7d8b79e..dd3686975 100644
--- a/pkg/models/export.go
+++ b/pkg/models/export.go
@@ -18,6 +18,7 @@
package models
import (
+ "archive/zip"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/cron"
"code.vikunja.io/api/pkg/db"
@@ -27,8 +28,6 @@ import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/utils"
"code.vikunja.io/api/pkg/version"
-
- "archive/zip"
"encoding/json"
"fmt"
"io"
@@ -146,7 +145,7 @@ func exportListsAndTasks(s *xorm.Session, u *user.User, wr *zip.Writer) (err err
nn.Lists = append(nn.Lists, &ListWithTasksAndBuckets{
List: *l,
BackgroundFileID: l.BackgroundFileID,
- Tasks: []*Task{},
+ Tasks: []*TaskWithComments{},
})
}
@@ -181,8 +180,25 @@ func exportListsAndTasks(s *xorm.Session, u *user.User, wr *zip.Writer) (err err
}
}
+ taskMap := make(map[int64]*TaskWithComments, len(tasks))
for _, t := range tasks {
- listMap[t.ListID].Tasks = append(listMap[t.ListID].Tasks, t)
+ taskMap[t.ID] = &TaskWithComments{
+ Task: *t,
+ }
+ listMap[t.ListID].Tasks = append(listMap[t.ListID].Tasks, taskMap[t.ID])
+ }
+
+ comments := []*TaskComment{}
+ err = s.
+ Join("LEFT", "tasks", "tasks.id = task_comments.task_id").
+ In("tasks.list_id", listIDs).
+ Find(&comments)
+ if err != nil {
+ return
+ }
+
+ for _, c := range comments {
+ taskMap[c.TaskID].Comments = append(taskMap[c.TaskID].Comments, c)
}
buckets := []*Bucket{}
diff --git a/pkg/models/list.go b/pkg/models/list.go
index 7ae58336d..fc899e0f6 100644
--- a/pkg/models/list.go
+++ b/pkg/models/list.go
@@ -82,7 +82,7 @@ type List struct {
type ListWithTasksAndBuckets struct {
List
// An array of tasks which belong to the list.
- Tasks []*Task `xorm:"-" json:"tasks"`
+ Tasks []*TaskWithComments `xorm:"-" json:"tasks"`
// Only used for migration.
Buckets []*Bucket `xorm:"-" json:"buckets"`
BackgroundFileID int64 `xorm:"null" json:"background_file_id"`
diff --git a/pkg/models/tasks.go b/pkg/models/tasks.go
index 840129ea1..a2e142916 100644
--- a/pkg/models/tasks.go
+++ b/pkg/models/tasks.go
@@ -129,6 +129,11 @@ type Task struct {
web.Rights `xorm:"-" json:"-"`
}
+type TaskWithComments struct {
+ Task
+ Comments []*TaskComment
+}
+
// TableName returns the table name for listtasks
func (Task) TableName() string {
return "tasks"
diff --git a/pkg/modules/migration/create_from_structure.go b/pkg/modules/migration/create_from_structure.go
index 6ae47a510..8be8304cf 100644
--- a/pkg/modules/migration/create_from_structure.go
+++ b/pkg/modules/migration/create_from_structure.go
@@ -129,7 +129,7 @@ func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTas
// Create all tasks
for _, t := range tasks {
- setBucketOrDefault(t)
+ setBucketOrDefault(&t.Task)
t.ListID = l.ID
err = t.Create(s, user)
@@ -221,6 +221,15 @@ func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTas
}
log.Debugf("[creating structure] Associated task %d with label %d", t.ID, lb.ID)
}
+
+ for _, comment := range t.Comments {
+ comment.TaskID = t.ID
+ err = comment.Create(s, user)
+ if err != nil {
+ return
+ }
+ log.Debugf("[creating structure] Created new comment %d", comment.ID)
+ }
}
// All tasks brought their own bucket with them, therefore the newly created default bucket is just extra space
diff --git a/pkg/modules/migration/microsoft-todo/microsoft_todo.go b/pkg/modules/migration/microsoft-todo/microsoft_todo.go
index 44a398075..64d20e41a 100644
--- a/pkg/modules/migration/microsoft-todo/microsoft_todo.go
+++ b/pkg/modules/migration/microsoft-todo/microsoft_todo.go
@@ -342,7 +342,7 @@ func convertMicrosoftTodoData(todoData []*list) (vikunjsStructure []*models.Name
}
}
- list.Tasks = append(list.Tasks, task)
+ list.Tasks = append(list.Tasks, &models.TaskWithComments{Task: *task})
log.Debugf("[Microsoft Todo Migration] Done converted %d tasks", len(l.Tasks))
}
diff --git a/pkg/modules/migration/todoist/todoist.go b/pkg/modules/migration/todoist/todoist.go
index 76978ab94..e56dfd2e8 100644
--- a/pkg/modules/migration/todoist/todoist.go
+++ b/pkg/modules/migration/todoist/todoist.go
@@ -264,7 +264,7 @@ func convertTodoistToVikunja(sync *sync, doneItems map[int64]*doneItem) (fullVik
lists := make(map[int64]*models.ListWithTasksAndBuckets, len(sync.Projects))
// A map for all vikunja tasks with the todoist task id as key to find them easily and add more data
- tasks := make(map[int64]*models.Task, len(sync.Items))
+ tasks := make(map[int64]*models.TaskWithComments, len(sync.Items))
// A map for all vikunja labels with the todoist id as key to find them easier
labels := make(map[int64]*models.Label, len(sync.Labels))
@@ -307,11 +307,13 @@ func convertTodoistToVikunja(sync *sync, doneItems map[int64]*doneItem) (fullVik
}
for _, i := range sync.Items {
- task := &models.Task{
- Title: i.Content,
- Created: i.DateAdded.In(config.GetTimeZone()),
- Done: i.Checked == 1,
- BucketID: i.SectionID,
+ task := &models.TaskWithComments{
+ Task: models.Task{
+ Title: i.Content,
+ Created: i.DateAdded.In(config.GetTimeZone()),
+ Done: i.Checked == 1,
+ BucketID: i.SectionID,
+ },
}
// Only try to parse the task done at date if the task is actually done
@@ -367,7 +369,7 @@ func convertTodoistToVikunja(sync *sync, doneItems map[int64]*doneItem) (fullVik
tasks[i.ParentID].RelatedTasks = make(models.RelatedTaskMap)
}
- tasks[i.ParentID].RelatedTasks[models.RelationKindSubtask] = append(tasks[i.ParentID].RelatedTasks[models.RelationKindSubtask], tasks[i.ID])
+ tasks[i.ParentID].RelatedTasks[models.RelationKindSubtask] = append(tasks[i.ParentID].RelatedTasks[models.RelationKindSubtask], &tasks[i.ID].Task)
// Remove the task from the top level structure, otherwise it is added twice
outer:
diff --git a/pkg/modules/migration/trello/trello.go b/pkg/modules/migration/trello/trello.go
index b75fa263e..57e40a3ba 100644
--- a/pkg/modules/migration/trello/trello.go
+++ b/pkg/modules/migration/trello/trello.go
@@ -271,7 +271,7 @@ func convertTrelloDataToVikunja(trelloData []*trello.Board) (fullVikunjaHierachi
log.Debugf("[Trello Migration] Downloaded card attachment %s", attachment.ID)
}
- list.Tasks = append(list.Tasks, task)
+ list.Tasks = append(list.Tasks, &models.TaskWithComments{Task: *task})
}
list.Buckets = append(list.Buckets, bucket)
diff --git a/pkg/modules/migration/vikunja-file/main_test.go b/pkg/modules/migration/vikunja-file/main_test.go
new file mode 100644
index 000000000..bd5e11348
--- /dev/null
+++ b/pkg/modules/migration/vikunja-file/main_test.go
@@ -0,0 +1,44 @@
+// 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 vikunja_file
+
+import (
+ "os"
+ "testing"
+
+ "code.vikunja.io/api/pkg/events"
+
+ "code.vikunja.io/api/pkg/config"
+ "code.vikunja.io/api/pkg/files"
+ "code.vikunja.io/api/pkg/models"
+ "code.vikunja.io/api/pkg/user"
+)
+
+// TestMain is the main test function used to bootstrap the test env
+func TestMain(m *testing.M) {
+ // Set default config
+ config.InitDefaultConfig()
+ // We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly
+ config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH"))
+
+ // Some tests use the file engine, so we'll need to initialize that
+ files.InitTests()
+ user.InitTests()
+ models.SetupTests()
+ events.Fake()
+ os.Exit(m.Run())
+}
diff --git a/pkg/modules/migration/vikunja-file/vikunja.go b/pkg/modules/migration/vikunja-file/vikunja.go
index 4f2e451ed..3df306e2d 100644
--- a/pkg/modules/migration/vikunja-file/vikunja.go
+++ b/pkg/modules/migration/vikunja-file/vikunja.go
@@ -83,12 +83,12 @@ func (v *VikunjaFileMigrator) Migrate(user *user.User, file io.ReaderAt, size in
for _, n := range namespaces {
for _, l := range n.Lists {
for _, t := range l.Tasks {
- if len(t.Labels) == 0 {
- continue
- }
for _, label := range t.Labels {
label.ID = 0
}
+ for _, comment := range t.Comments {
+ comment.ID = 0
+ }
}
}
}
diff --git a/pkg/modules/migration/vikunja-file/vikunja_test.go b/pkg/modules/migration/vikunja-file/vikunja_test.go
new file mode 100644
index 000000000..998646520
--- /dev/null
+++ b/pkg/modules/migration/vikunja-file/vikunja_test.go
@@ -0,0 +1,78 @@
+// 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 vikunja_file
+
+import (
+ "code.vikunja.io/api/pkg/config"
+ "code.vikunja.io/api/pkg/db"
+ "code.vikunja.io/api/pkg/user"
+ "github.com/stretchr/testify/assert"
+ "os"
+ "testing"
+)
+
+func TestVikunjaFileMigrator_Migrate(t *testing.T) {
+ db.LoadAndAssertFixtures(t)
+
+ m := &VikunjaFileMigrator{}
+ u := &user.User{ID: 1}
+
+ f, err := os.Open(config.ServiceRootpath.GetString() + "/pkg/modules/migration/vikunja-file/export.zip")
+ if err != nil {
+ t.Fatalf("Could not open file: %s", err)
+ }
+ defer f.Close()
+ s, err := f.Stat()
+ if err != nil {
+ t.Fatalf("Could not stat file: %s", err)
+ }
+
+ err = m.Migrate(u, f, s.Size())
+ assert.NoError(t, err)
+ db.AssertExists(t, "namespaces", map[string]interface{}{
+ "title": "test",
+ "owner_id": u.ID,
+ }, false)
+ db.AssertExists(t, "lists", map[string]interface{}{
+ "title": "Test list",
+ "owner_id": u.ID,
+ }, false)
+ db.AssertExists(t, "lists", map[string]interface{}{
+ "title": "A list with a background",
+ "owner_id": u.ID,
+ }, false)
+ db.AssertExists(t, "tasks", map[string]interface{}{
+ "title": "Some other task",
+ "created_by_id": u.ID,
+ }, false)
+ db.AssertExists(t, "task_comments", map[string]interface{}{
+ "comment": "This is a comment",
+ "author_id": u.ID,
+ }, false)
+ db.AssertExists(t, "files", map[string]interface{}{
+ "name": "cristiano-mozzillo-v3d5uBB26yA-unsplash.jpg",
+ "created_by_id": u.ID,
+ }, false)
+ db.AssertExists(t, "labels", map[string]interface{}{
+ "title": "test",
+ "created_by_id": u.ID,
+ }, false)
+ db.AssertExists(t, "buckets", map[string]interface{}{
+ "title": "Test Bucket",
+ "created_by_id": u.ID,
+ }, false)
+}
diff --git a/pkg/modules/migration/wunderlist/wunderlist.go b/pkg/modules/migration/wunderlist/wunderlist.go
index f57c1d864..fea6722e0 100644
--- a/pkg/modules/migration/wunderlist/wunderlist.go
+++ b/pkg/modules/migration/wunderlist/wunderlist.go
@@ -235,7 +235,7 @@ func convertListForFolder(listID int, list *list, content *wunderlistContents) (
}
}
- l.Tasks = append(l.Tasks, newTask)
+ l.Tasks = append(l.Tasks, &models.TaskWithComments{Task: *newTask})
}
}
return l, nil
diff --git a/pkg/routes/caldav/listStorageProvider.go b/pkg/routes/caldav/listStorageProvider.go
index 559597d66..0d1692c18 100644
--- a/pkg/routes/caldav/listStorageProvider.go
+++ b/pkg/routes/caldav/listStorageProvider.go
@@ -174,10 +174,10 @@ func (vcls *VikunjaCaldavListStorage) GetResourcesByFilters(rpath string, filter
for _, t := range vcls.list.Tasks {
rr := VikunjaListResourceAdapter{
list: vcls.list,
- task: t,
+ task: &t.Task,
isCollection: false,
}
- r := data.NewResource(getTaskURL(t), &rr)
+ r := data.NewResource(getTaskURL(&t.Task), &rr)
r.Name = t.Title
resources = append(resources, r)
}
@@ -371,7 +371,7 @@ func (vcls *VikunjaCaldavListStorage) DeleteResource(rpath string) error {
// VikunjaListResourceAdapter holds the actual resource
type VikunjaListResourceAdapter struct {
list *models.ListWithTasksAndBuckets
- listTasks []*models.Task
+ listTasks []*models.TaskWithComments
task *models.Task
isPrincipal bool
@@ -417,7 +417,7 @@ func (vlra *VikunjaListResourceAdapter) GetContent() string {
}
if vlra.task != nil {
- list := models.ListWithTasksAndBuckets{Tasks: []*models.Task{vlra.task}}
+ list := models.ListWithTasksAndBuckets{Tasks: []*models.TaskWithComments{{Task: *vlra.task}}}
return caldav.GetCaldavTodosForTasks(&list, list.Tasks)
}
@@ -481,8 +481,10 @@ func (vcls *VikunjaCaldavListStorage) getListRessource(isCollection bool) (rr Vi
panic("Tasks returned from TaskCollection.ReadAll are not []*models.Task!")
}
- listTasks = tasks
- vcls.list.Tasks = tasks
+ for _, t := range tasks {
+ listTasks = append(listTasks, &models.TaskWithComments{Task: *t})
+ }
+ vcls.list.Tasks = listTasks
}
if err := s.Commit(); err != nil {