Assignees optimizations (#47)

This commit is contained in:
konrad 2019-01-08 19:13:07 +00:00 committed by Gitea
parent 31c39388f1
commit 364a172876
11 changed files with 921 additions and 108 deletions

View file

@ -115,10 +115,10 @@ Sorry for some of them being in German, I'll tranlate them at some point.
* [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 * [x] Assignees
* [ ] Check if something changed at all before running everything * [x] Check if something changed at all before running everything
* [ ] Don't use `list.ReadOne()`, gets too much unnessecary shit * [x] Don't use `list.ReadOne()`, gets too much unnessecary shit
* [ ] Wegen Performance auf eigene endpoints umziehen, wie labels * [x] Wegen Performance auf eigene endpoints umziehen, wie labels
* [ ] "One endpoint to rule them all" -> Array-addable * [x] "One endpoint to rule them all" -> Array-addable
* [x] Labels * [x] Labels
* [ ] Check if something changed at all before running everything * [ ] Check if something changed at all before running everything
* [ ] Editable via task edit, like assignees * [ ] Editable via task edit, like assignees

View file

@ -130,13 +130,17 @@ GET http://localhost:8080/api/v1/tasks/caldav
### ###
# Update a task # Update a task
POST http://localhost:8080/api/v1/tasks/1 POST http://localhost:8080/api/v1/tasks/3565
Authorization: Bearer {{auth_token}} Authorization: Bearer {{auth_token}}
Content-Type: application/json Content-Type: application/json
{"assignees":[ {
{"id": 1} "assignees": [
]} {
"id": 1
}
]
}
### ###
@ -151,3 +155,21 @@ Content-Type: application/json
} }
### ###
# Get all assignees
GET http://localhost:8080/api/v1/tasks/3565/assignees
Authorization: Bearer {{auth_token}}
###
# Add a bunch of assignees
PUT http://localhost:8080/api/v1/tasks/3565/assignees/bulk
Authorization: Bearer {{auth_token}}
Content-Type: application/json
{
"assignees": [
{"id": 17}
]
}
###

View file

@ -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
// 2019-01-03 22:32:38.523459581 +0100 CET m=+0.109277724 // 2019-01-07 23:16:50.581590248 +0100 CET m=+0.121922055
package docs package docs
@ -2649,6 +2649,189 @@ var doc = `{
} }
} }
}, },
"/tasks/{taskID}/assignees": {
"put": {
"security": [
{
"JWTKeyAuth": []
}
],
"description": "Adds a new assignee to a task. The assignee needs to have access to the list, the doer must be able to edit this task.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"assignees"
],
"summary": "Add a new assignee to a task",
"parameters": [
{
"description": "The assingee object",
"name": "assignee",
"in": "body",
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/models.ListTaskAssginee"
}
},
{
"type": "integer",
"description": "Task ID",
"name": "taskID",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "The created assingee object.",
"schema": {
"type": "object",
"$ref": "#/definitions/models.ListTaskAssginee"
}
},
"400": {
"description": "Invalid assignee object provided.",
"schema": {
"type": "object",
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
}
},
"500": {
"description": "Internal error",
"schema": {
"type": "object",
"$ref": "#/definitions/models.Message"
}
}
}
}
},
"/tasks/{taskID}/assignees/bulk": {
"put": {
"security": [
{
"JWTKeyAuth": []
}
],
"description": "Adds new assignees to a task. The assignee needs to have access to the list, the doer must be able to edit this task. Every user not in the list will be unassigned from the task, pass an empty array to unassign everyone.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"assignees"
],
"summary": "Add new assignees to a task",
"parameters": [
{
"description": "The array of assignees",
"name": "assignee",
"in": "body",
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/models.BulkAssignees"
}
},
{
"type": "integer",
"description": "Task ID",
"name": "taskID",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "The created assingees object.",
"schema": {
"type": "object",
"$ref": "#/definitions/models.ListTaskAssginee"
}
},
"400": {
"description": "Invalid assignee object provided.",
"schema": {
"type": "object",
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
}
},
"500": {
"description": "Internal error",
"schema": {
"type": "object",
"$ref": "#/definitions/models.Message"
}
}
}
}
},
"/tasks/{taskID}/assignees/{userID}": {
"delete": {
"security": [
{
"JWTKeyAuth": []
}
],
"description": "Un-assign a user from a task.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"assignees"
],
"summary": "Delete an assignee",
"parameters": [
{
"type": "integer",
"description": "Task ID",
"name": "taskID",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "Assignee user ID",
"name": "userID",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "The assignee was successfully deleted.",
"schema": {
"type": "object",
"$ref": "#/definitions/models.Message"
}
},
"403": {
"description": "Not allowed to delete the assignee.",
"schema": {
"type": "object",
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
}
},
"500": {
"description": "Internal error",
"schema": {
"type": "object",
"$ref": "#/definitions/models.Message"
}
}
}
}
},
"/tasks/{task}/labels": { "/tasks/{task}/labels": {
"get": { "get": {
"security": [ "security": [
@ -3517,6 +3700,18 @@ var doc = `{
} }
} }
}, },
"models.BulkAssignees": {
"type": "object",
"properties": {
"assignees": {
"description": "A list with all assignees",
"type": "array",
"items": {
"$ref": "#/definitions/models.User"
}
}
}
},
"models.BulkTask": { "models.BulkTask": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -3795,6 +3990,17 @@ var doc = `{
} }
} }
}, },
"models.ListTaskAssginee": {
"type": "object",
"properties": {
"created": {
"type": "integer"
},
"user_id": {
"type": "integer"
}
}
},
"models.ListUser": { "models.ListUser": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -4081,6 +4287,10 @@ var doc = `{
"team_id": { "team_id": {
"description": "The team id.", "description": "The team id.",
"type": "integer" "type": "integer"
},
"updated": {
"description": "A unix timestamp when this relation was last updated. You cannot change this value.",
"type": "integer"
} }
} }
}, },

View file

@ -2636,6 +2636,189 @@
} }
} }
}, },
"/tasks/{taskID}/assignees": {
"put": {
"security": [
{
"JWTKeyAuth": []
}
],
"description": "Adds a new assignee to a task. The assignee needs to have access to the list, the doer must be able to edit this task.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"assignees"
],
"summary": "Add a new assignee to a task",
"parameters": [
{
"description": "The assingee object",
"name": "assignee",
"in": "body",
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/models.ListTaskAssginee"
}
},
{
"type": "integer",
"description": "Task ID",
"name": "taskID",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "The created assingee object.",
"schema": {
"type": "object",
"$ref": "#/definitions/models.ListTaskAssginee"
}
},
"400": {
"description": "Invalid assignee object provided.",
"schema": {
"type": "object",
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
}
},
"500": {
"description": "Internal error",
"schema": {
"type": "object",
"$ref": "#/definitions/models.Message"
}
}
}
}
},
"/tasks/{taskID}/assignees/bulk": {
"put": {
"security": [
{
"JWTKeyAuth": []
}
],
"description": "Adds new assignees to a task. The assignee needs to have access to the list, the doer must be able to edit this task. Every user not in the list will be unassigned from the task, pass an empty array to unassign everyone.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"assignees"
],
"summary": "Add new assignees to a task",
"parameters": [
{
"description": "The array of assignees",
"name": "assignee",
"in": "body",
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/models.BulkAssignees"
}
},
{
"type": "integer",
"description": "Task ID",
"name": "taskID",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "The created assingees object.",
"schema": {
"type": "object",
"$ref": "#/definitions/models.ListTaskAssginee"
}
},
"400": {
"description": "Invalid assignee object provided.",
"schema": {
"type": "object",
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
}
},
"500": {
"description": "Internal error",
"schema": {
"type": "object",
"$ref": "#/definitions/models.Message"
}
}
}
}
},
"/tasks/{taskID}/assignees/{userID}": {
"delete": {
"security": [
{
"JWTKeyAuth": []
}
],
"description": "Un-assign a user from a task.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"assignees"
],
"summary": "Delete an assignee",
"parameters": [
{
"type": "integer",
"description": "Task ID",
"name": "taskID",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "Assignee user ID",
"name": "userID",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "The assignee was successfully deleted.",
"schema": {
"type": "object",
"$ref": "#/definitions/models.Message"
}
},
"403": {
"description": "Not allowed to delete the assignee.",
"schema": {
"type": "object",
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
}
},
"500": {
"description": "Internal error",
"schema": {
"type": "object",
"$ref": "#/definitions/models.Message"
}
}
}
}
},
"/tasks/{task}/labels": { "/tasks/{task}/labels": {
"get": { "get": {
"security": [ "security": [
@ -3503,6 +3686,18 @@
} }
} }
}, },
"models.BulkAssignees": {
"type": "object",
"properties": {
"assignees": {
"description": "A list with all assignees",
"type": "array",
"items": {
"$ref": "#/definitions/models.User"
}
}
}
},
"models.BulkTask": { "models.BulkTask": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -3781,6 +3976,17 @@
} }
} }
}, },
"models.ListTaskAssginee": {
"type": "object",
"properties": {
"created": {
"type": "integer"
},
"user_id": {
"type": "integer"
}
}
},
"models.ListUser": { "models.ListUser": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -4067,6 +4273,10 @@
"team_id": { "team_id": {
"description": "The team id.", "description": "The team id.",
"type": "integer" "type": "integer"
},
"updated": {
"description": "A unix timestamp when this relation was last updated. You cannot change this value.",
"type": "integer"
} }
} }
}, },

View file

@ -21,6 +21,14 @@ definitions:
minLength: 3 minLength: 3
type: string type: string
type: object type: object
models.BulkAssignees:
properties:
assignees:
description: A list with all assignees
items:
$ref: '#/definitions/models.User'
type: array
type: object
models.BulkTask: models.BulkTask:
properties: properties:
assignees: assignees:
@ -244,6 +252,13 @@ definitions:
change this value. change this value.
type: integer type: integer
type: object type: object
models.ListTaskAssginee:
properties:
created:
type: integer
user_id:
type: integer
type: object
models.ListUser: models.ListUser:
properties: properties:
created: created:
@ -475,6 +490,10 @@ definitions:
team_id: team_id:
description: The team id. description: The team id.
type: integer type: integer
updated:
description: A unix timestamp when this relation was last updated. You cannot
change this value.
type: integer
type: object type: object
models.TeamUser: models.TeamUser:
properties: properties:
@ -2341,6 +2360,130 @@ paths:
summary: Remove a label from a task summary: Remove a label from a task
tags: tags:
- labels - labels
/tasks/{taskID}/assignees:
put:
consumes:
- application/json
description: Adds a new assignee to a task. The assignee needs to have access
to the list, the doer must be able to edit this task.
parameters:
- description: The assingee object
in: body
name: assignee
required: true
schema:
$ref: '#/definitions/models.ListTaskAssginee'
type: object
- description: Task ID
in: path
name: taskID
required: true
type: integer
produces:
- application/json
responses:
"200":
description: The created assingee object.
schema:
$ref: '#/definitions/models.ListTaskAssginee'
type: object
"400":
description: Invalid assignee object provided.
schema:
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
type: object
"500":
description: Internal error
schema:
$ref: '#/definitions/models.Message'
type: object
security:
- JWTKeyAuth: []
summary: Add a new assignee to a task
tags:
- assignees
/tasks/{taskID}/assignees/{userID}:
delete:
consumes:
- application/json
description: Un-assign a user from a task.
parameters:
- description: Task ID
in: path
name: taskID
required: true
type: integer
- description: Assignee user ID
in: path
name: userID
required: true
type: integer
produces:
- application/json
responses:
"200":
description: The assignee was successfully deleted.
schema:
$ref: '#/definitions/models.Message'
type: object
"403":
description: Not allowed to delete the assignee.
schema:
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
type: object
"500":
description: Internal error
schema:
$ref: '#/definitions/models.Message'
type: object
security:
- JWTKeyAuth: []
summary: Delete an assignee
tags:
- assignees
/tasks/{taskID}/assignees/bulk:
put:
consumes:
- application/json
description: Adds new assignees to a task. The assignee needs to have access
to the list, the doer must be able to edit this task. Every user not in the
list will be unassigned from the task, pass an empty array to unassign everyone.
parameters:
- description: The array of assignees
in: body
name: assignee
required: true
schema:
$ref: '#/definitions/models.BulkAssignees'
type: object
- description: Task ID
in: path
name: taskID
required: true
type: integer
produces:
- application/json
responses:
"200":
description: The created assingees object.
schema:
$ref: '#/definitions/models.ListTaskAssginee'
type: object
"400":
description: Invalid assignee object provided.
schema:
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
type: object
"500":
description: Internal error
schema:
$ref: '#/definitions/models.Message'
type: object
security:
- JWTKeyAuth: []
summary: Add new assignees to a task
tags:
- assignees
/tasks/all: /tasks/all:
get: get:
consumes: consumes:

View file

@ -151,6 +151,28 @@ func (l *List) GetSimpleByID() (err error) {
return 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.
var list List
exists, err := x.
Select("list.*").
Table(List{}).
Join("INNER", "tasks", "list.id = tasks.list_id").
Where("tasks.id = ?", taskID).
Get(&list)
if err != nil {
return
}
if !exists {
return &List{}, ErrListDoesNotExist{ID: l.ID}
}
return &list, nil
}
// Gets the lists only, without any tasks or so // Gets the lists only, without any tasks or so
func getRawListsForUser(search string, u *User, page int) (lists []*List, err error) { func getRawListsForUser(search string, u *User, page int) (lists []*List, err error) {
fullUser, err := GetUserByID(u.ID) fullUser, err := GetUserByID(u.ID)

View file

@ -0,0 +1,242 @@
// 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 <https://www.gnu.org/licenses/>.
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
// @Summary Delete an assignee
// @Description Un-assign a user from a task.
// @tags assignees
// @Accept json
// @Produce json
// @Security JWTKeyAuth
// @Param taskID path int true "Task ID"
// @Param userID path int true "Assignee user ID"
// @Success 200 {object} models.Message "The assignee was successfully deleted."
// @Failure 403 {object} code.vikunja.io/web.HTTPError "Not allowed to delete the assignee."
// @Failure 500 {object} models.Message "Internal error"
// @Router /tasks/{taskID}/assignees/{userID} [delete]
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
// @Summary Add a new assignee to a task
// @Description Adds a new assignee to a task. The assignee needs to have access to the list, the doer must be able to edit this task.
// @tags assignees
// @Accept json
// @Produce json
// @Security JWTKeyAuth
// @Param assignee body models.ListTaskAssginee true "The assingee object"
// @Param taskID path int true "Task ID"
// @Success 200 {object} models.ListTaskAssginee "The created assingee object."
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid assignee object provided."
// @Failure 500 {object} models.Message "Internal error"
// @Router /tasks/{taskID}/assignees [put]
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
}
// ReadAll gets all assignees for a task
// @Summary Get all assignees for a task
// @Description Returns an array with all assignees for this task.
// @tags assignees
// @Accept json
// @Produce json
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
// @Param s query string false "Search assignees by their username."
// @Security JWTKeyAuth
// @Success 200 {array} models.User "The assignees"
// @Failure 500 {object} models.Message "Internal error"
// @Router /labels [get]
func (la *ListTaskAssginee) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
var taskAssignees []*User
err := x.Table("task_assignees").
Select("users.*").
Join("INNER", "users", "task_assignees.user_id = users.id").
Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%").
Limit(getLimitFromPageIndex(page)).
Find(&taskAssignees)
return taskAssignees, err
}
// BulkAssignees is a helper struct used to update multiple assignees at once.
type BulkAssignees struct {
// A list with all assignees
Assignees []*User `json:"assignees"`
TaskID int64 `json:"-" param:"listtask"`
web.CRUDable `json:"-"`
web.Rights `json:"-"`
}
// Create adds new assignees to a task
// @Summary Add multiple new assignees to a task
// @Description Adds multiple new assignees to a task. The assignee needs to have access to the list, the doer must be able to edit this task. Every user not in the list will be unassigned from the task, pass an empty array to unassign everyone.
// @tags assignees
// @Accept json
// @Produce json
// @Security JWTKeyAuth
// @Param assignee body models.BulkAssignees true "The array of assignees"
// @Param taskID path int true "Task ID"
// @Success 200 {object} models.ListTaskAssginee "The created assingees object."
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid assignee object provided."
// @Failure 500 {object} models.Message "Internal error"
// @Router /tasks/{taskID}/assignees/bulk [put]
func (ba *BulkAssignees) Create(a web.Auth) (err error) {
task, err := GetListTaskByID(ba.TaskID) // We need to use the full method here because we need all current assignees.
if err != nil {
return
}
return task.updateTaskAssignees(ba.Assignees)
}

View file

@ -0,0 +1,47 @@
// 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 <https://www.gnu.org/licenses/>.
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 canDoListTaskAssingee(la.TaskID, a)
}
// CanCreate checks if a user can add a new assignee
func (ba *BulkAssignees) CanCreate(a web.Auth) bool {
return canDoListTaskAssingee(ba.TaskID, a)
}
// CanDelete checks if a user can delete an assignee
func (la *ListTaskAssginee) CanDelete(a web.Auth) bool {
return canDoListTaskAssingee(la.TaskID, a)
}
func canDoListTaskAssingee(taskID int64, a web.Auth) bool {
// Check if the current user can edit the list
list, err := GetListSimplByTaskID(taskID)
if err != nil {
log.Log.Errorf("Error during canDoListTaskAssingee for ListTaskAssginee: %v", err)
return false
}
return list.CanCreate(a)
}

View file

@ -76,25 +76,6 @@ 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) 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 // GetTasksByListID gets all todotasks for a list
func GetTasksByListID(listID int64) (tasks []*ListTask, err error) { func GetTasksByListID(listID int64) (tasks []*ListTask, err error) {
// make a map so we can put in a lot of other stuff more easily // 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 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) { func getTaskByIDSimple(taskID int64) (task ListTask, err error) {
if taskID < 1 { if taskID < 1 {
return ListTask{}, ErrListTaskDoesNotExist{taskID} return ListTask{}, ErrListTaskDoesNotExist{taskID}

View file

@ -143,73 +143,3 @@ 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
}

View file

@ -241,6 +241,22 @@ func RegisterRoutes(e *echo.Echo) {
} }
a.POST("/tasks/bulk", bulkTaskHandler.UpdateWeb) 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/assignees", assigneeTaskHandler.ReadAllWeb)
bulkAssigneeHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.BulkAssignees{}
},
}
a.PUT("/tasks/:listtask/assignees/bulk", bulkAssigneeHandler.CreateWeb)
labelTaskHandler := &handler.WebHandler{ labelTaskHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject { EmptyStruct: func() handler.CObject {
return &models.LabelTask{} return &models.LabelTask{}