Assignees optimizations #47
|
@ -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)
|
||||
|
|
|
@ -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 <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
|
||||
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
|
||||
}
|
|
@ -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 <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 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)
|
||||
}
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
|
|
Loading…
Reference in New Issue