diff --git a/pkg/models/list.go b/pkg/models/list.go
index f4e3fb560..fe5e5a8c5 100644
--- a/pkg/models/list.go
+++ b/pkg/models/list.go
@@ -151,6 +151,26 @@ func (l *List) GetSimpleByID() (err error) {
return
}
+// GetListSimplByTaskID gets a list by a task id
+func GetListSimplByTaskID(taskID int64) (l *List, err error) {
+ // We need to re-init our list object, because otherwise xorm creates a "where for every item in that list object,
+ // leading to not finding anything if the id is good, but for example the title is different.
+ exists, err := x.
+ Select("lists.*").
+ Join("INNER", "tasks", "list.id = tasks.list_id").
+ Where("tasks.id = ?", taskID).
+ Get(l)
+ if err != nil {
+ return
+ }
+
+ if !exists {
+ return &List{}, ErrListDoesNotExist{ID: l.ID}
+ }
+
+ return
+}
+
// Gets the lists only, without any tasks or so
func getRawListsForUser(search string, u *User, page int) (lists []*List, err error) {
fullUser, err := GetUserByID(u.ID)
diff --git a/pkg/models/list_task_assignees.go b/pkg/models/list_task_assignees.go
new file mode 100644
index 000000000..9c64fb45c
--- /dev/null
+++ b/pkg/models/list_task_assignees.go
@@ -0,0 +1,162 @@
+// Vikunja is a todo-list application to facilitate your life.
+// Copyright 2019 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 .
+
+package models
+
+import "code.vikunja.io/web"
+
+// ListTaskAssginee represents an assignment of a user to a task
+type ListTaskAssginee struct {
+ ID int64 `xorm:"int(11) autoincr not null unique pk" json:"-"`
+ TaskID int64 `xorm:"int(11) INDEX not null" json:"-" param:"listtask"`
+ UserID int64 `xorm:"int(11) INDEX not null" json:"user_id" param:"user"`
+ Created int64 `xorm:"created"`
+
+ web.CRUDable `xorm:"-" json:"-"`
+ web.Rights `xorm:"-" json:"-"`
+}
+
+// TableName makes a pretty table name
+func (ListTaskAssginee) TableName() string {
+ return "task_assignees"
+}
+
+// ListTaskAssigneeWithUser is a helper type to deal with user joins
+type ListTaskAssigneeWithUser struct {
+ TaskID int64
+ User `xorm:"extends"`
+}
+
+func getRawTaskAssigneesForTasks(taskIDs []int64) (taskAssignees []*ListTaskAssigneeWithUser, err error) {
+ taskAssignees = []*ListTaskAssigneeWithUser{nil}
+ err = x.Table("task_assignees").
+ Select("task_id, users.*").
+ In("task_id", taskIDs).
+ Join("INNER", "users", "task_assignees.user_id = users.id").
+ Find(&taskAssignees)
+ return
+}
+
+// Create or update a bunch of task assignees
+func (t *ListTask) updateTaskAssignees(assignees []*User) (err error) {
+
+ // If we don't have any new assignees, delete everything right away. Saves us some hassle.
+ if len(assignees) == 0 && len(t.Assignees) > 0 {
+ _, err = x.Where("task_id = ?", t.ID).
+ Delete(ListTaskAssginee{})
+ return err
+ }
+
+ // If we didn't change anything (from 0 to zero) don't do anything.
+ if len(assignees) == 0 && len(t.Assignees) == 0 {
+ return nil
+ }
+
+ // Make a hashmap of the new assignees for easier comparison
+ newAssignees := make(map[int64]*User, len(assignees))
+ for _, newAssignee := range assignees {
+ newAssignees[newAssignee.ID] = newAssignee
+ }
+
+ // Get old assignees to delete
+ var found bool
+ var assigneesToDelete []int64
+ oldAssignees := make(map[int64]*User, len(t.Assignees))
+ for _, oldAssignee := range t.Assignees {
+ found = false
+ if newAssignees[oldAssignee.ID] != nil {
+ found = true // If a new assignee is already in the list with old assignees
+ }
+
+ // Put all assignees which are only on the old list to the trash
+ if !found {
+ assigneesToDelete = append(assigneesToDelete, oldAssignee.ID)
+ }
+
+ oldAssignees[oldAssignee.ID] = oldAssignee
+ }
+
+ // Delete all assignees not passed
+ if len(assigneesToDelete) > 0 {
+ _, err = x.In("user_id", assigneesToDelete).
+ And("task_id = ?", t.ID).
+ Delete(ListTaskAssginee{})
+ if err != nil {
+ return err
+ }
+ }
+
+ // Get the list to perform later checks
+ list := List{ID: t.ListID}
+ err = list.GetSimpleByID()
+ if err != nil {
+ return
+ }
+
+ // Loop through our users and add them
+ for _, u := range assignees {
+ // Check if the user is already assigned and assign him only if not
+ if oldAssignees[u.ID] != nil {
+ // continue outer loop
+ continue
+ }
+
+ // Add the new assignee
+ err = t.addNewAssigneeByID(u.ID, &list)
+ if err != nil {
+ return err
+ }
+ }
+
+ return
+}
+
+// Delete a task assignee
+func (la *ListTaskAssginee) Delete() (err error) {
+ _, err = x.Delete(&ListTaskAssginee{TaskID: la.TaskID, UserID: la.UserID})
+ return
+}
+
+// Create adds a new assignee to a task
+func (la *ListTaskAssginee) Create(a web.Auth) (err error) {
+
+ // Get the list to perform later checks
+ list, err := GetListSimplByTaskID(la.TaskID)
+ if err != nil {
+ return
+ }
+
+ task := &ListTask{ID: la.TaskID}
+ return task.addNewAssigneeByID(la.UserID, list)
+}
+
+func (t *ListTask) addNewAssigneeByID(newAssigneeID int64, list *List) (err error) {
+ // Check if the user exists and has access to the list
+ newAssignee, err := GetUserByID(newAssigneeID)
+ if err != nil {
+ return err
+ }
+ if !list.CanRead(&newAssignee) {
+ return ErrUserDoesNotHaveAccessToList{list.ID, newAssigneeID}
+ }
+
+ _, err = x.Insert(ListTaskAssginee{
+ TaskID: t.ID,
+ UserID: newAssigneeID,
+ })
+
+ return
+}
diff --git a/pkg/models/list_task_assignees_rights.go b/pkg/models/list_task_assignees_rights.go
new file mode 100644
index 000000000..a5f03f77c
--- /dev/null
+++ b/pkg/models/list_task_assignees_rights.go
@@ -0,0 +1,42 @@
+// Vikunja is a todo-list application to facilitate your life.
+// Copyright 2019 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 .
+
+package models
+
+import (
+ "code.vikunja.io/api/pkg/log"
+ "code.vikunja.io/web"
+)
+
+// CanCreate checks if a user can add a new assignee
+func (la *ListTaskAssginee) CanCreate(a web.Auth) bool {
+ return la.canDoListTaskAssingee(a)
+}
+
+// CanDelete checks if a user can delete an assignee
+func (la *ListTaskAssginee) CanDelete(a web.Auth) bool {
+ return la.canDoListTaskAssingee(a)
+}
+
+func (la *ListTaskAssginee) canDoListTaskAssingee(a web.Auth) bool {
+ // Check if the current user can edit the list
+ list, err := GetListSimplByTaskID(la.TaskID)
+ if err != nil {
+ log.Log.Errorf("Error during canDoListTaskAssingee for ListTaskAssginee: %v", err)
+ return false
+ }
+ return list.CanCreate(a)
+}
diff --git a/pkg/models/list_tasks.go b/pkg/models/list_tasks.go
index 586e21231..85a486a1d 100644
--- a/pkg/models/list_tasks.go
+++ b/pkg/models/list_tasks.go
@@ -76,25 +76,6 @@ func (ListTask) TableName() string {
return "tasks"
}
-// ListTaskAssginee represents an assignment of a user to a task
-type ListTaskAssginee struct {
- ID int64 `xorm:"int(11) autoincr not null unique pk"`
- TaskID int64 `xorm:"int(11) INDEX not null"`
- UserID int64 `xorm:"int(11) INDEX not null"`
- Created int64 `xorm:"created"`
-}
-
-// TableName makes a pretty table name
-func (ListTaskAssginee) TableName() string {
- return "task_assignees"
-}
-
-// ListTaskAssigneeWithUser is a helper type to deal with user joins
-type ListTaskAssigneeWithUser struct {
- TaskID int64
- User `xorm:"extends"`
-}
-
// GetTasksByListID gets all todotasks for a list
func GetTasksByListID(listID int64) (tasks []*ListTask, err error) {
// make a map so we can put in a lot of other stuff more easily
@@ -175,16 +156,6 @@ func GetTasksByListID(listID int64) (tasks []*ListTask, err error) {
return
}
-func getRawTaskAssigneesForTasks(taskIDs []int64) (taskAssignees []*ListTaskAssigneeWithUser, err error) {
- taskAssignees = []*ListTaskAssigneeWithUser{nil}
- err = x.Table("task_assignees").
- Select("task_id, users.*").
- In("task_id", taskIDs).
- Join("INNER", "users", "task_assignees.user_id = users.id").
- Find(&taskAssignees)
- return
-}
-
func getTaskByIDSimple(taskID int64) (task ListTask, err error) {
if taskID < 1 {
return ListTask{}, ErrListTaskDoesNotExist{taskID}
diff --git a/pkg/models/list_tasks_create_update.go b/pkg/models/list_tasks_create_update.go
index 5cfbdda99..c1259cd66 100644
--- a/pkg/models/list_tasks_create_update.go
+++ b/pkg/models/list_tasks_create_update.go
@@ -143,88 +143,3 @@ func updateDone(oldTask *ListTask, newTask *ListTask) {
newTask.Done = false
}
}
-
-// Create a bunch of task assignees
-func (t *ListTask) updateTaskAssignees(assignees []*User) (err error) {
-
- // If we don't have any new assignees, delete everything right away. Saves us some hassle.
- if len(assignees) == 0 && len(t.Assignees) > 0 {
- _, err = x.Where("task_id = ?", t.ID).
- Delete(ListTaskAssginee{})
- return err
- }
-
- // If we didn't change anything (from 0 to zero) don't do anything.
- if len(assignees) == 0 && len(t.Assignees) == 0 {
- return nil
- }
-
- // Make a hashmap of the new assignees for easier comparison
- newAssignees := make(map[int64]*User, len(assignees))
- for _, newAssignee := range assignees {
- newAssignees[newAssignee.ID] = newAssignee
- }
-
- // Get old assignees to delete
- var found bool
- var assigneesToDelete []int64
- oldAssignees := make(map[int64]*User, len(t.Assignees))
- for _, oldAssignee := range t.Assignees {
- found = false
- if newAssignees[oldAssignee.ID] != nil {
- found = true // If a new assignee is already in the list with old assignees
- }
-
- // Put all assignees which are only on the old list to the trash
- if !found {
- assigneesToDelete = append(assigneesToDelete, oldAssignee.ID)
- }
-
- oldAssignees[oldAssignee.ID] = oldAssignee
- }
-
- // Delete all assignees not passed
- if len(assigneesToDelete) > 0 {
- _, err = x.In("user_id", assigneesToDelete).
- And("task_id = ?", t.ID).
- Delete(ListTaskAssginee{})
- if err != nil {
- return err
- }
- }
-
- // Get the list to perform later checks
- list := List{ID: t.ListID}
- err = list.GetSimpleByID()
- if err != nil {
- return
- }
-
- // Loop through our users and add them
- for _, u := range assignees {
- // Check if the user is already assigned and assign him only if not
- if oldAssignees[u.ID] != nil {
- // continue outer loop
- continue
- }
-
- // Check if the user exists and has access to the list
- newAssignee, err := GetUserByID(u.ID)
- if err != nil {
- return err
- }
- if !list.CanRead(&newAssignee) {
- return ErrUserDoesNotHaveAccessToList{list.ID, u.ID}
- }
-
- _, err = x.Insert(ListTaskAssginee{
- TaskID: t.ID,
- UserID: u.ID,
- })
- if err != nil {
- return err
- }
- }
-
- return
-}
diff --git a/pkg/routes/routes.go b/pkg/routes/routes.go
index 3711a1611..e233520b3 100644
--- a/pkg/routes/routes.go
+++ b/pkg/routes/routes.go
@@ -241,6 +241,15 @@ func RegisterRoutes(e *echo.Echo) {
}
a.POST("/tasks/bulk", bulkTaskHandler.UpdateWeb)
+ assigneeTaskHandler := &handler.WebHandler{
+ EmptyStruct: func() handler.CObject {
+ return &models.ListTaskAssginee{}
+ },
+ }
+ a.PUT("/tasks/:listtask/assignees", assigneeTaskHandler.CreateWeb)
+ a.DELETE("/tasks/:listtask/assignees/:user", assigneeTaskHandler.DeleteWeb)
+ a.GET("/tasks/:listtask/labelassigneess", assigneeTaskHandler.ReadAllWeb)
+
labelTaskHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.LabelTask{}