Task assignees (#44)
This commit is contained in:
parent
37345e6bd3
commit
d39007baa0
15 changed files with 226 additions and 57 deletions
|
@ -99,8 +99,8 @@ Sorry for some of them being in German, I'll tranlate them at some point.
|
||||||
* [x] Timeline/Calendar view -> Dazu tasks die in einem Bestimmten Bereich due sind, macht dann das Frontend
|
* [x] Timeline/Calendar view -> Dazu tasks die in einem Bestimmten Bereich due sind, macht dann das Frontend
|
||||||
* [x] Tasks innerhalb eines definierbarem Bereich, sollte aber trotzdem der server machen, so à la "Gib mir alles für diesen Monat"
|
* [x] Tasks innerhalb eines definierbarem Bereich, sollte aber trotzdem der server machen, so à la "Gib mir alles für diesen Monat"
|
||||||
* [x] Bulk-edit -> Transactions
|
* [x] Bulk-edit -> Transactions
|
||||||
|
* [x] Assignees
|
||||||
* [ ] Labels
|
* [ ] Labels
|
||||||
* [ ] Assignees
|
|
||||||
* [ ] Attachments
|
* [ ] Attachments
|
||||||
* [ ] Task-Templates innerhalb namespaces und Listen (-> Mehrere, die auswählbar sind)
|
* [ ] Task-Templates innerhalb namespaces und Listen (-> Mehrere, die auswählbar sind)
|
||||||
* [ ] Ein Task muss von mehreren Assignees abgehakt werden bis er als done markiert wird
|
* [ ] Ein Task muss von mehreren Assignees abgehakt werden bis er als done markiert wird
|
||||||
|
|
|
@ -15,7 +15,7 @@ POST http://localhost:8080/api/v1/register
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"username": "user5",
|
"username": "user",
|
||||||
"password": "1234",
|
"password": "1234",
|
||||||
"email": "5@knt.li"
|
"email": "5@knt.li"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,34 @@
|
||||||
# Get all lists
|
# Get all lists
|
||||||
GET http://localhost:8080/api/v1/namespaces/1/lists
|
GET http://localhost:8080/api/v1/namespaces/1
|
||||||
Authorization: Bearer {{auth_token}}
|
Authorization: Bearer {{auth_token}}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# Get one list
|
# Get one list
|
||||||
GET http://localhost:8080/api/v1/lists/1163
|
GET http://localhost:8080/api/v1/lists/1172
|
||||||
Authorization: Bearer {{auth_token}}
|
Authorization: Bearer {{auth_token}}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# Add a new list
|
# Add a new list
|
||||||
PUT http://localhost:8080/api/v1/namespaces/6/lists
|
PUT http://localhost:8080/api/v1/namespaces/1/lists
|
||||||
Authorization: Bearer {{auth_token}}
|
Authorization: Bearer {{auth_token}}
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"title": "sffffc me only"
|
"title": "test"
|
||||||
}
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# Add a new item
|
# Add a new item
|
||||||
PUT http://localhost:8080/api/v1/lists/15
|
PUT http://localhost:8080/api/v1/lists/1
|
||||||
Authorization: Bearer {{auth_token}}
|
Authorization: Bearer {{auth_token}}
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"text": "this is a subtask 2",
|
"text": "Task",
|
||||||
"description": "Schinken",
|
"description": "Schinken"
|
||||||
"parentTaskID": 34
|
|
||||||
}
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
@ -83,11 +82,11 @@ Authorization: Bearer {{auth_token}}
|
||||||
###
|
###
|
||||||
|
|
||||||
# Give a user access to that list
|
# Give a user access to that list
|
||||||
PUT http://localhost:8080/api/v1/lists/30/users
|
PUT http://localhost:8080/api/v1/lists/1172/users
|
||||||
Authorization: Bearer {{auth_token}}
|
Authorization: Bearer {{auth_token}}
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"user_id":3, "right":1}
|
{"user_id":1, "right":1}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
|
@ -131,11 +130,13 @@ GET http://localhost:8080/api/v1/tasks/caldav
|
||||||
###
|
###
|
||||||
|
|
||||||
# Update a task
|
# Update a task
|
||||||
POST http://localhost:8080/api/v1/tasks/3491
|
POST http://localhost:8080/api/v1/tasks/1
|
||||||
Authorization: Bearer {{auth_token}}
|
Authorization: Bearer {{auth_token}}
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"startDate":1546804000, "endDate": 1546805000}
|
{"assignees":[
|
||||||
|
{"id": 1}
|
||||||
|
]}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
# Get all users
|
# Get all users
|
||||||
GET http://localhost:8080/api/v1/users
|
GET http://localhost:8080/api/v1/user
|
||||||
Authorization: Bearer {{auth_token}}
|
Authorization: Bearer {{auth_token}}
|
||||||
|
|
||||||
######
|
######
|
||||||
|
|
14
docs/docs.go
14
docs/docs.go
|
@ -1,6 +1,6 @@
|
||||||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
// This file was generated by swaggo/swag at
|
// This file was generated by swaggo/swag at
|
||||||
// 2018-12-28 01:18:16.824999107 +0100 CET m=+0.098072896
|
// 2018-12-29 15:14:06.225275112 +0100 CET m=+0.295589005
|
||||||
|
|
||||||
package docs
|
package docs
|
||||||
|
|
||||||
|
@ -3012,6 +3012,12 @@ var doc = `{
|
||||||
"models.BulkTask": {
|
"models.BulkTask": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"assignees": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.User"
|
||||||
|
}
|
||||||
|
},
|
||||||
"created": {
|
"created": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
@ -3116,6 +3122,12 @@ var doc = `{
|
||||||
"models.ListTask": {
|
"models.ListTask": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"assignees": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.User"
|
||||||
|
}
|
||||||
|
},
|
||||||
"created": {
|
"created": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
|
|
@ -2998,6 +2998,12 @@
|
||||||
"models.BulkTask": {
|
"models.BulkTask": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"assignees": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.User"
|
||||||
|
}
|
||||||
|
},
|
||||||
"created": {
|
"created": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
@ -3102,6 +3108,12 @@
|
||||||
"models.ListTask": {
|
"models.ListTask": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"assignees": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.User"
|
||||||
|
}
|
||||||
|
},
|
||||||
"created": {
|
"created": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,6 +13,10 @@ definitions:
|
||||||
type: object
|
type: object
|
||||||
models.BulkTask:
|
models.BulkTask:
|
||||||
properties:
|
properties:
|
||||||
|
assignees:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.User'
|
||||||
|
type: array
|
||||||
created:
|
created:
|
||||||
type: integer
|
type: integer
|
||||||
createdBy:
|
createdBy:
|
||||||
|
@ -82,6 +86,10 @@ definitions:
|
||||||
type: object
|
type: object
|
||||||
models.ListTask:
|
models.ListTask:
|
||||||
properties:
|
properties:
|
||||||
|
assignees:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.User'
|
||||||
|
type: array
|
||||||
created:
|
created:
|
||||||
type: integer
|
type: integer
|
||||||
createdBy:
|
createdBy:
|
||||||
|
|
|
@ -95,6 +95,11 @@ func (bt *BulkTask) Update() (err error) {
|
||||||
// When a repeating task is marked as done, we update all deadlines and reminders and set it as undone
|
// When a repeating task is marked as done, we update all deadlines and reminders and set it as undone
|
||||||
updateDone(oldtask, &bt.ListTask)
|
updateDone(oldtask, &bt.ListTask)
|
||||||
|
|
||||||
|
// Update the assignees
|
||||||
|
if err := oldtask.updateTaskAssignees(bt.Assignees); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// For whatever reason, xorm dont detect if done is updated, so we need to update this every time by hand
|
// For whatever reason, xorm dont detect if done is updated, so we need to update this every time by hand
|
||||||
// Which is why we merge the actual task struct with the one we got from the
|
// Which is why we merge the actual task struct with the one we got from the
|
||||||
// The user struct overrides values in the actual one.
|
// The user struct overrides values in the actual one.
|
||||||
|
|
|
@ -69,7 +69,12 @@ func (l *List) CanRead(a web.Auth) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return l.checkListTeamRight(user, TeamRightRead)
|
if l.checkListTeamRight(user, TeamRightRead) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Users who are able to write should also be able to read
|
||||||
|
return l.CanWrite(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanDelete checks if the user can delete a list
|
// CanDelete checks if the user can delete a list
|
||||||
|
|
|
@ -36,6 +36,7 @@ type ListTask struct {
|
||||||
Priority int64 `xorm:"int(11)" json:"priority"`
|
Priority int64 `xorm:"int(11)" json:"priority"`
|
||||||
StartDateUnix int64 `xorm:"int(11) INDEX" json:"startDate"`
|
StartDateUnix int64 `xorm:"int(11) INDEX" json:"startDate"`
|
||||||
EndDateUnix int64 `xorm:"int(11) INDEX" json:"endDate"`
|
EndDateUnix int64 `xorm:"int(11) INDEX" json:"endDate"`
|
||||||
|
Assignees []*User `xorm:"-" json:"assignees"`
|
||||||
|
|
||||||
Sorting string `xorm:"-" json:"-" param:"sort"` // Parameter to sort by
|
Sorting string `xorm:"-" json:"-" param:"sort"` // Parameter to sort by
|
||||||
StartDateSortUnix int64 `xorm:"-" json:"-" param:"startdatefilter"`
|
StartDateSortUnix int64 `xorm:"-" json:"-" param:"startdatefilter"`
|
||||||
|
@ -57,6 +58,25 @@ func (ListTask) TableName() string {
|
||||||
return "tasks"
|
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) not null"`
|
||||||
|
UserID int64 `xorm:"int(11) 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
|
// GetTasksByListID gets all todotasks for a list
|
||||||
func GetTasksByListID(listID int64) (tasks []*ListTask, err error) {
|
func GetTasksByListID(listID int64) (tasks []*ListTask, err error) {
|
||||||
err = x.Where("list_id = ?", listID).Find(&tasks)
|
err = x.Where("list_id = ?", listID).Find(&tasks)
|
||||||
|
@ -72,9 +92,11 @@ func GetTasksByListID(listID int64) (tasks []*ListTask, err error) {
|
||||||
// make a map so we can put in subtasks more easily
|
// make a map so we can put in subtasks more easily
|
||||||
taskMap := make(map[int64]*ListTask, len(tasks))
|
taskMap := make(map[int64]*ListTask, len(tasks))
|
||||||
|
|
||||||
// Get all users and put them into the array
|
// Get all users & task ids and put them into the array
|
||||||
var userIDs []int64
|
var userIDs []int64
|
||||||
|
var taskIDs []int64
|
||||||
for _, i := range tasks {
|
for _, i := range tasks {
|
||||||
|
taskIDs = append(taskIDs, i.ID)
|
||||||
found := false
|
found := false
|
||||||
for _, u := range userIDs {
|
for _, u := range userIDs {
|
||||||
if i.CreatedByID == u {
|
if i.CreatedByID == u {
|
||||||
|
@ -90,6 +112,18 @@ func GetTasksByListID(listID int64) (tasks []*ListTask, err error) {
|
||||||
taskMap[i.ID] = i
|
taskMap[i.ID] = i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get all assignees
|
||||||
|
taskAssignees, err := getRawTaskAssigneesForTasks(taskIDs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Put the assignees in the task map
|
||||||
|
for _, a := range taskAssignees {
|
||||||
|
if a != nil {
|
||||||
|
taskMap[a.TaskID].Assignees = append(taskMap[a.TaskID].Assignees, &a.User)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var users []User
|
var users []User
|
||||||
err = x.In("id", userIDs).Find(&users)
|
err = x.In("id", userIDs).Find(&users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -130,6 +164,16 @@ func GetTasksByListID(listID int64) (tasks []*ListTask, err error) {
|
||||||
return
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// GetListTaskByID returns all tasks a list has
|
// GetListTaskByID returns all tasks a list has
|
||||||
func GetListTaskByID(listTaskID int64) (listTask ListTask, err error) {
|
func GetListTaskByID(listTaskID int64) (listTask ListTask, err error) {
|
||||||
if listTaskID < 1 {
|
if listTaskID < 1 {
|
||||||
|
@ -151,6 +195,17 @@ func GetListTaskByID(listTaskID int64) (listTask ListTask, err error) {
|
||||||
}
|
}
|
||||||
listTask.CreatedBy = u
|
listTask.CreatedBy = u
|
||||||
|
|
||||||
|
// Get assignees
|
||||||
|
taskAssignees, err := getRawTaskAssigneesForTasks([]int64{listTaskID})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, u := range taskAssignees {
|
||||||
|
if u != nil {
|
||||||
|
listTask.Assignees = append(listTask.Assignees, &u.User)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,21 +36,21 @@ import (
|
||||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /lists/{id} [put]
|
// @Router /lists/{id} [put]
|
||||||
func (i *ListTask) Create(a web.Auth) (err error) {
|
func (t *ListTask) Create(a web.Auth) (err error) {
|
||||||
doer, err := getUserWithError(a)
|
doer, err := getUserWithError(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
i.ID = 0
|
t.ID = 0
|
||||||
|
|
||||||
// Check if we have at least a text
|
// Check if we have at least a text
|
||||||
if i.Text == "" {
|
if t.Text == "" {
|
||||||
return ErrListTaskCannotBeEmpty{}
|
return ErrListTaskCannotBeEmpty{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the list exists
|
// Check if the list exists
|
||||||
l := &List{ID: i.ListID}
|
l := &List{ID: t.ListID}
|
||||||
if err = l.GetSimpleByID(); err != nil {
|
if err = l.GetSimpleByID(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -60,9 +60,14 @@ func (i *ListTask) Create(a web.Auth) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
i.CreatedByID = u.ID
|
t.CreatedByID = u.ID
|
||||||
i.CreatedBy = u
|
t.CreatedBy = u
|
||||||
if _, err = x.Insert(i); err != nil {
|
if _, err = x.Insert(t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the assignees
|
||||||
|
if err := t.updateTaskAssignees(t.Assignees); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,29 +89,34 @@ func (i *ListTask) Create(a web.Auth) (err error) {
|
||||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the task (aka its list)"
|
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the task (aka its list)"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /tasks/{id} [post]
|
// @Router /tasks/{id} [post]
|
||||||
func (i *ListTask) Update() (err error) {
|
func (t *ListTask) Update() (err error) {
|
||||||
// Check if the task exists
|
// Check if the task exists
|
||||||
ot, err := GetListTaskByID(i.ID)
|
ot, err := GetListTaskByID(t.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// When a repeating task is marked as done, we update all deadlines and reminders and set it as undone
|
// When a repeating task is marked as done, we update all deadlines and reminders and set it as undone
|
||||||
updateDone(&ot, i)
|
updateDone(&ot, t)
|
||||||
|
|
||||||
|
// Update the assignees
|
||||||
|
if err := ot.updateTaskAssignees(t.Assignees); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// For whatever reason, xorm dont detect if done is updated, so we need to update this every time by hand
|
// For whatever reason, xorm dont detect if done is updated, so we need to update this every time by hand
|
||||||
// Which is why we merge the actual task struct with the one we got from the
|
// Which is why we merge the actual task struct with the one we got from the
|
||||||
// The user struct overrides values in the actual one.
|
// The user struct overrides values in the actual one.
|
||||||
if err := mergo.Merge(&ot, i, mergo.WithOverride); err != nil {
|
if err := mergo.Merge(&ot, t, mergo.WithOverride); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// And because a false is considered to be a null value, we need to explicitly check that case here.
|
// And because a false is considered to be a null value, we need to explicitly check that case here.
|
||||||
if i.Done == false {
|
if t.Done == false {
|
||||||
ot.Done = false
|
ot.Done = false
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = x.ID(i.ID).
|
_, err = x.ID(t.ID).
|
||||||
Cols("text",
|
Cols("text",
|
||||||
"description",
|
"description",
|
||||||
"done",
|
"done",
|
||||||
|
@ -118,7 +128,7 @@ func (i *ListTask) Update() (err error) {
|
||||||
"start_date_unix",
|
"start_date_unix",
|
||||||
"end_date_unix").
|
"end_date_unix").
|
||||||
Update(ot)
|
Update(ot)
|
||||||
*i = ot
|
*t = ot
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,3 +143,73 @@ func updateDone(oldTask *ListTask, newTask *ListTask) {
|
||||||
newTask.Done = false
|
newTask.Done = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a bunch of task assignees
|
||||||
|
func (t *ListTask) updateTaskAssignees(assignees []*User) (err error) {
|
||||||
|
|
||||||
|
// Get old assignees to delete
|
||||||
|
var found bool
|
||||||
|
var assigneesToDelete []int64
|
||||||
|
for _, oldAssignee := range t.Assignees {
|
||||||
|
found = false
|
||||||
|
for _, newAssignee := range assignees {
|
||||||
|
if newAssignee.ID == oldAssignee.ID {
|
||||||
|
found = true // If a new assignee is already in the list with old assignees
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put all assignees which are only on the old list to the trash
|
||||||
|
if !found {
|
||||||
|
assigneesToDelete = append(assigneesToDelete, oldAssignee.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.ReadOne()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through our users and add them
|
||||||
|
AddNewAssignee:
|
||||||
|
for _, u := range assignees {
|
||||||
|
// Check if the user is already assigned and assign him only if not
|
||||||
|
for _, oldAssignee := range t.Assignees {
|
||||||
|
if oldAssignee.ID == u.ID {
|
||||||
|
// continue outer loop
|
||||||
|
continue AddNewAssignee
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
|
@ -33,15 +33,20 @@ import (
|
||||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /tasks/{id} [delete]
|
// @Router /tasks/{id} [delete]
|
||||||
func (i *ListTask) Delete() (err error) {
|
func (t *ListTask) Delete() (err error) {
|
||||||
|
|
||||||
// Check if it exists
|
// Check if it exists
|
||||||
_, err = GetListTaskByID(i.ID)
|
_, err = GetListTaskByID(t.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = x.ID(i.ID).Delete(ListTask{}); err != nil {
|
if _, err = x.ID(t.ID).Delete(ListTask{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete assignees
|
||||||
|
if _, err = x.Where("task_id = ?", t.ID).Delete(ListTaskAssginee{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// CanDelete checks if the user can delete an task
|
// CanDelete checks if the user can delete an task
|
||||||
func (i *ListTask) CanDelete(a web.Auth) bool {
|
func (t *ListTask) CanDelete(a web.Auth) bool {
|
||||||
doer := getUserForRights(a)
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
// Get the task
|
// Get the task
|
||||||
lI, err := GetListTaskByID(i.ID)
|
lI, err := GetListTaskByID(t.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("Error occurred during CanDelete for ListTask: %s", err)
|
log.Log.Error("Error occurred during CanDelete for ListTask: %s", err)
|
||||||
return false
|
return false
|
||||||
|
@ -39,11 +39,11 @@ func (i *ListTask) CanDelete(a web.Auth) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanUpdate determines if a user has the right to update a list task
|
// CanUpdate determines if a user has the right to update a list task
|
||||||
func (i *ListTask) CanUpdate(a web.Auth) bool {
|
func (t *ListTask) CanUpdate(a web.Auth) bool {
|
||||||
doer := getUserForRights(a)
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
// Get the task
|
// Get the task
|
||||||
lI, err := GetListTaskByID(i.ID)
|
lI, err := GetListTaskByID(t.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("Error occurred during CanDelete for ListTask: %s", err)
|
log.Log.Error("Error occurred during CanDelete for ListTask: %s", err)
|
||||||
return false
|
return false
|
||||||
|
@ -56,11 +56,11 @@ func (i *ListTask) CanUpdate(a web.Auth) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanCreate determines if a user has the right to create a list task
|
// CanCreate determines if a user has the right to create a list task
|
||||||
func (i *ListTask) CanCreate(a web.Auth) bool {
|
func (t *ListTask) CanCreate(a web.Auth) bool {
|
||||||
doer := getUserForRights(a)
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
// A user can create an task if he has write acces to its list
|
// A user can create an task if he has write acces to its list
|
||||||
l := &List{ID: i.ListID}
|
l := &List{ID: t.ListID}
|
||||||
l.ReadOne()
|
l.ReadOne()
|
||||||
return l.CanWrite(doer)
|
return l.CanWrite(doer)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,22 +14,6 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Vikunja is a todo-list application to facilitate your life.
|
|
||||||
// Copyright 2018 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
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -68,6 +68,7 @@ func init() {
|
||||||
new(Namespace),
|
new(Namespace),
|
||||||
new(ListUser),
|
new(ListUser),
|
||||||
new(NamespaceUser),
|
new(NamespaceUser),
|
||||||
|
new(ListTaskAssginee),
|
||||||
)
|
)
|
||||||
|
|
||||||
tablesWithPointer = append(tables,
|
tablesWithPointer = append(tables,
|
||||||
|
@ -81,6 +82,7 @@ func init() {
|
||||||
&Namespace{},
|
&Namespace{},
|
||||||
&ListUser{},
|
&ListUser{},
|
||||||
&NamespaceUser{},
|
&NamespaceUser{},
|
||||||
|
&ListTaskAssginee{},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue