diff --git a/pkg/models/export.go b/pkg/models/export.go index 99a1d88c2..fe3a0e5e8 100644 --- a/pkg/models/export.go +++ b/pkg/models/export.go @@ -155,6 +155,21 @@ func exportProjectsAndTasks(s *xorm.Session, u *user.User, wr *zip.Writer) (task projectIDs = append(projectIDs, p.ID) } + views := map[int64]*ProjectView{} + err = s.In("project_id", projectIDs).Find(&views) + if err != nil { + return + } + + viewIDs := []int64{} + for _, v := range views { + if projectsMap[v.ProjectID].Views == nil { + projectsMap[v.ProjectID].Views = []*ProjectView{} + } + projectsMap[v.ProjectID].Views = append(projectsMap[v.ProjectID].Views, v) + viewIDs = append(viewIDs, v.ID) + } + tasks, _, _, err := getTasksForProjects(s, rawProjects, u, &taskSearchOptions{ page: 0, perPage: -1, @@ -194,17 +209,75 @@ func exportProjectsAndTasks(s *xorm.Session, u *user.User, wr *zip.Writer) (task } buckets := []*Bucket{} - err = s.In("project_id", projectIDs).Find(&buckets) + err = s.In("project_view_id", viewIDs).Find(&buckets) if err != nil { return } + bucketIDs := []int64{} for _, b := range buckets { - if _, exists := projectsMap[b.ProjectID]; !exists { - log.Debugf("[User Data Export] Project %d does not exist for bucket %d, omitting", b.ProjectID, b.ID) + view, exists := views[b.ProjectViewID] + if !exists { + log.Debugf("[User Data Export] Project view %d does not exist for bucket %d, omitting", b.ProjectViewID, b.ID) continue } - projectsMap[b.ProjectID].Buckets = append(projectsMap[b.ProjectID].Buckets, b) + _, exists = projectsMap[view.ProjectID] + if !exists { + log.Debugf("[User Data Export] Project %d does not exist for bucket %d, omitting", view.ProjectID, b.ID) + continue + } + projectsMap[view.ProjectID].Buckets = append(projectsMap[view.ProjectID].Buckets, b) + bucketIDs = append(bucketIDs, b.ID) + } + + taskBuckets := []*TaskBucket{} + err = s.In("bucket_id", bucketIDs).Find(&taskBuckets) + if err != nil { + return + } + + for _, tb := range taskBuckets { + view, exists := views[tb.ProjectViewID] + if !exists { + log.Debugf("[User Data Export] Project view %d does not exist, omitting", tb.ProjectViewID) + continue + } + _, exists = projectsMap[view.ProjectID] + if !exists { + log.Debugf("[User Data Export] Project %d does not exist, omitting", view.ProjectID) + continue + } + + if projectsMap[view.ProjectID].TaskBuckets == nil { + projectsMap[view.ProjectID].TaskBuckets = []*TaskBucket{} + } + + projectsMap[view.ProjectID].TaskBuckets = append(projectsMap[view.ProjectID].TaskBuckets, tb) + } + + taskPositions := []*TaskPosition{} + err = s.In("project_view_id", viewIDs).Find(&taskPositions) + if err != nil { + return + } + + for _, p := range taskPositions { + view, exists := views[p.ProjectViewID] + if !exists { + log.Debugf("[User Data Export] Project view %d does not exist, omitting", p.ProjectViewID) + continue + } + _, exists = projectsMap[view.ProjectID] + if !exists { + log.Debugf("[User Data Export] Project %d does not exist, omitting", view.ProjectID) + continue + } + + if projectsMap[view.ProjectID].Positions == nil { + projectsMap[view.ProjectID].Positions = []*TaskPosition{} + } + + projectsMap[view.ProjectID].Positions = append(projectsMap[view.ProjectID].Positions, p) } data, err := json.Marshal(projects) diff --git a/pkg/models/project.go b/pkg/models/project.go index 5abdd82f2..00e99630b 100644 --- a/pkg/models/project.go +++ b/pkg/models/project.go @@ -93,8 +93,10 @@ type ProjectWithTasksAndBuckets struct { // An array of tasks which belong to the project. Tasks []*TaskWithComments `xorm:"-" json:"tasks"` // Only used for migration. - Buckets []*Bucket `xorm:"-" json:"buckets"` - BackgroundFileID int64 `xorm:"null" json:"background_file_id"` + Buckets []*Bucket `xorm:"-" json:"buckets"` + TaskBuckets []*TaskBucket `xorm:"-" json:"task_buckets"` + Positions []*TaskPosition `xorm:"-" json:"positions"` + BackgroundFileID int64 `xorm:"null" json:"background_file_id"` } // TableName returns a better name for the projects table diff --git a/pkg/modules/migration/create_from_structure.go b/pkg/modules/migration/create_from_structure.go index d4f7c50ec..9515399ce 100644 --- a/pkg/modules/migration/create_from_structure.go +++ b/pkg/modules/migration/create_from_structure.go @@ -64,6 +64,11 @@ func insertFromStructure(s *xorm.Session, str []*models.ProjectWithTasksAndBucke } p.ID = 0 + + for _, view := range p.Views { + view.ProjectID = 0 + } + err = createProject(s, p, &archivedProjects, labels, user) if err != nil { return err @@ -167,7 +172,7 @@ func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTas } // Create all buckets - buckets := make(map[int64]*models.Bucket) // old bucket id is the key + bucketsByOldID := make(map[int64]*models.Bucket) // old bucket id is the key if len(project.Buckets) > 0 { log.Debugf("[creating structure] Creating %d buckets", len(project.Buckets)) } @@ -179,40 +184,45 @@ func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTas if err != nil { return } - buckets[oldID] = bucket + bucketsByOldID[oldID] = bucket log.Debugf("[creating structure] Created bucket %d, old ID was %d", bucket.ID, oldID) } // Create all views, create default views if we don't have any + viewsByOldIDs := make(map[int64]*models.ProjectView, len(oldViews)) if len(oldViews) > 0 { for _, view := range oldViews { + oldID := view.ID view.ID = 0 if view.DefaultBucketID != 0 { - bucket, has := buckets[view.DefaultBucketID] + bucket, has := bucketsByOldID[view.DefaultBucketID] if has { view.DefaultBucketID = bucket.ID } } if view.DoneBucketID != 0 { - bucket, has := buckets[view.DoneBucketID] + bucket, has := bucketsByOldID[view.DoneBucketID] if has { view.DoneBucketID = bucket.ID } } + view.ProjectID = project.ID + err = view.Create(s, user) if err != nil { return } + viewsByOldIDs[oldID] = view } } else { // Only using the default views // Add all buckets to the default kanban view for _, view := range project.Views { if view.ViewKind == models.ProjectViewKindKanban { - for _, b := range buckets { + for _, b := range bucketsByOldID { b.ProjectViewID = view.ID err = b.Update(s, user) if err != nil { @@ -227,7 +237,7 @@ func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTas log.Debugf("[creating structure] Creating %d tasks", len(tasks)) setBucketOrDefault := func(task *models.Task) { - bucket, exists := buckets[task.BucketID] + bucket, exists := bucketsByOldID[task.BucketID] if exists { task.BucketID = bucket.ID } else if task.BucketID > 0 { @@ -240,6 +250,7 @@ func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTas } tasksByOldID := make(map[int64]*models.TaskWithComments, len(tasks)) + newTaskIDs := []int64{} // Create all tasks for i, t := range tasks { setBucketOrDefault(&tasks[i].Task) @@ -251,6 +262,8 @@ func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTas continue } + newTaskIDs = append(newTaskIDs, t.ID) + if err != nil { return } @@ -401,6 +414,58 @@ func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTas } } + if len(viewsByOldIDs) > 0 { + newPositions := []*models.TaskPosition{} + for _, pos := range project.Positions { + _, hasTask := tasksByOldID[pos.TaskID] + _, hasView := viewsByOldIDs[pos.ProjectViewID] + if !hasTask || !hasView { + continue + } + newPositions = append(newPositions, &models.TaskPosition{ + TaskID: tasksByOldID[pos.TaskID].ID, + ProjectViewID: viewsByOldIDs[pos.ProjectViewID].ID, + Position: pos.Position, + }) + } + + if len(newPositions) > 0 { + _, err = s.In("task_id", newTaskIDs).Delete(&models.TaskPosition{}) + if err != nil { + return + } + _, err = s.Insert(newPositions) + if err != nil { + return + } + } + + newTaskBuckets := make([]*models.TaskBucket, 0, len(project.TaskBuckets)) + for _, tb := range project.TaskBuckets { + _, hasTask := tasksByOldID[tb.TaskID] + _, hasBucket := bucketsByOldID[tb.BucketID] + if !hasTask || !hasBucket { + continue + } + newTaskBuckets = append(newTaskBuckets, &models.TaskBucket{ + TaskID: tasksByOldID[tb.TaskID].ID, + BucketID: bucketsByOldID[tb.BucketID].ID, + ProjectViewID: bucketsByOldID[tb.BucketID].ProjectViewID, + }) + } + + if len(newTaskBuckets) > 0 { + _, err = s.In("task_id", newTaskIDs).Delete(&models.TaskBucket{}) + if err != nil { + return + } + _, err = s.Insert(newTaskBuckets) + if err != nil { + return + } + } + } + project.Tasks = tasks project.Buckets = originalBuckets