vikunja/pkg/models/list_duplicate.go

150 lines
3.9 KiB
Go

// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 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 General Public License 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package models
import (
"code.vikunja.io/web"
)
// ListDuplicate holds everything needed to duplicate a list
type ListDuplicate struct {
// The list id of the list to duplicate
ListID int64 `json:"list_id"`
// The target namespace
NamespaceID int64 `json:"namespace_id"`
List *List `json:"-"`
web.Rights `json:"-"`
web.CRUDable `json:"-"`
}
// CanCreate checks if a user has the right to duplicate a list
func (ld *ListDuplicate) CanCreate(a web.Auth) (canCreate bool, err error) {
// List Exists + user has read access to list
ld.List = &List{ID: ld.ListID}
canRead, err := ld.List.CanRead(a)
if err != nil || !canRead {
return canRead, err
}
// Namespace exists + user has write access to is (-> can create new lists)
ld.List.NamespaceID = ld.NamespaceID
return ld.List.CanCreate(a)
}
// Create duplicates a list
func (ld *ListDuplicate) Create(a web.Auth) (err error) {
ld.List.ID = 0
ld.List.Identifier = "" // Reset the identifier to trigger regenerating a new one
// Set the owner to the current user
ld.List.OwnerID = a.GetID()
if err := CreateOrUpdateList(ld.List); err != nil {
return err
}
// Duplicate kanban buckets
// Old bucket ID as key, new id as value
// Used to map the newly created tasks to their new buckets
bucketMap := make(map[int64]int64)
buckets := []*Bucket{}
err = x.Where("list_id = ?", ld.ListID).Find(&buckets)
if err != nil {
return
}
for _, b := range buckets {
oldID := b.ID
b.ID = 0
b.ListID = ld.List.ID
if err := b.Create(a); err != nil {
return err
}
bucketMap[oldID] = b.ID
}
// Get all tasks + all task details
tasks, _, _, err := getTasksForLists([]*List{{ID: ld.ListID}}, &taskOptions{})
if err != nil {
return err
}
taskMap := make(map[int64]int64)
// Create + update all tasks
oldTaskIDs := make([]int64, len(tasks))
for _, t := range tasks {
oldID := t.ID
t.ID = 0
t.ListID = ld.List.ID
t.BucketID = bucketMap[t.ID]
t.UID = ""
err := createTask(t, a, false)
if err != nil {
return err
}
taskMap[oldID] = t.ID
oldTaskIDs = append(oldTaskIDs, oldID)
}
// Save all attachments
// We also duplicate all underlying files since they could be modified in one list which would result in
// file changes in the other list which is not something we want.
attachments, err := getTaskAttachmentsByTaskIDs(oldTaskIDs)
if err != nil {
return err
}
for _, attachment := range attachments {
attachment.TaskID = oldTaskIDs[attachment.TaskID]
if err := attachment.File.LoadFileByID(); err != nil {
return err
}
err := attachment.NewAttachment(attachment.File.File, attachment.File.Name, attachment.File.Size, a)
if err != nil {
return err
}
_ = attachment.File.File.Close()
}
// Copy label tasks (not the labels)
labelTasks := []*LabelTask{}
err = x.In("task_id", oldTaskIDs).Find(&labelTasks)
if err != nil {
return
}
for _, lt := range labelTasks {
lt.ID = 0
lt.TaskID = taskMap[lt.TaskID]
if _, err := x.Insert(lt); err != nil {
return err
}
}
// * assignees
// * comments
// * relations in that list
// * reminders
// * Background files + unsplash info
// * rights / shares
// * Generate new link shares if any are available
return
}