2018-05-09 19:29:04 +03:00
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"fmt"
2019-11-10 11:07:21 +03:00
"code.gitea.io/gitea/modules/util"
2019-10-17 12:26:49 +03:00
"xorm.io/xorm"
2018-05-09 19:29:04 +03:00
)
// IssueAssignees saves all issue assignees
type IssueAssignees struct {
ID int64 ` xorm:"pk autoincr" `
AssigneeID int64 ` xorm:"INDEX" `
IssueID int64 ` xorm:"INDEX" `
}
// This loads all assignees of an issue
func ( issue * Issue ) loadAssignees ( e Engine ) ( err error ) {
// Reset maybe preexisting assignees
issue . Assignees = [ ] * User { }
err = e . Table ( "`user`" ) .
Join ( "INNER" , "issue_assignees" , "assignee_id = `user`.id" ) .
Where ( "issue_assignees.issue_id = ?" , issue . ID ) .
Find ( & issue . Assignees )
if err != nil {
return err
}
// Check if we have at least one assignee and if yes put it in as `Assignee`
if len ( issue . Assignees ) > 0 {
issue . Assignee = issue . Assignees [ 0 ]
}
return
}
2019-11-18 11:08:20 +03:00
// GetAssigneeIDsByIssue returns the IDs of users assigned to an issue
// but skips joining with `user` for performance reasons.
// User permissions must be verified elsewhere if required.
func GetAssigneeIDsByIssue ( issueID int64 ) ( [ ] int64 , error ) {
userIDs := make ( [ ] int64 , 0 , 5 )
return userIDs , x . Table ( "issue_assignees" ) .
Cols ( "assignee_id" ) .
Where ( "issue_id = ?" , issueID ) .
Distinct ( "assignee_id" ) .
Find ( & userIDs )
}
2018-05-09 19:29:04 +03:00
// GetAssigneesByIssue returns everyone assigned to that issue
func GetAssigneesByIssue ( issue * Issue ) ( assignees [ ] * User , err error ) {
2019-01-05 00:51:27 +03:00
return getAssigneesByIssue ( x , issue )
}
func getAssigneesByIssue ( e Engine , issue * Issue ) ( assignees [ ] * User , err error ) {
err = issue . loadAssignees ( e )
2018-05-09 19:29:04 +03:00
if err != nil {
return assignees , err
}
return issue . Assignees , nil
}
// IsUserAssignedToIssue returns true when the user is assigned to the issue
func IsUserAssignedToIssue ( issue * Issue , user * User ) ( isAssigned bool , err error ) {
2019-10-25 17:46:37 +03:00
return isUserAssignedToIssue ( x , issue , user )
}
func isUserAssignedToIssue ( e Engine , issue * Issue , user * User ) ( isAssigned bool , err error ) {
return e . Get ( & IssueAssignees { IssueID : issue . ID , AssigneeID : user . ID } )
2018-05-09 19:29:04 +03:00
}
// MakeAssigneeList concats a string with all names of the assignees. Useful for logs.
func MakeAssigneeList ( issue * Issue ) ( assigneeList string , err error ) {
err = issue . loadAssignees ( x )
if err != nil {
return "" , err
}
for in , assignee := range issue . Assignees {
assigneeList += assignee . Name
if len ( issue . Assignees ) > ( in + 1 ) {
assigneeList += ", "
}
}
return
}
// ClearAssigneeByUserID deletes all assignments of an user
func clearAssigneeByUserID ( sess * xorm . Session , userID int64 ) ( err error ) {
_ , err = sess . Delete ( & IssueAssignees { AssigneeID : userID } )
return
}
2019-10-25 17:46:37 +03:00
// ToggleAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it.
func ( issue * Issue ) ToggleAssignee ( doer * User , assigneeID int64 ) ( removed bool , comment * Comment , err error ) {
2018-05-09 19:29:04 +03:00
sess := x . NewSession ( )
defer sess . Close ( )
if err := sess . Begin ( ) ; err != nil {
2019-10-25 17:46:37 +03:00
return false , nil , err
2018-05-09 19:29:04 +03:00
}
2019-10-25 17:46:37 +03:00
removed , comment , err = issue . toggleAssignee ( sess , doer , assigneeID , false )
if err != nil {
return false , nil , err
2018-05-09 19:29:04 +03:00
}
2019-07-31 12:24:38 +03:00
if err := sess . Commit ( ) ; err != nil {
2019-10-25 17:46:37 +03:00
return false , nil , err
2019-07-31 12:24:38 +03:00
}
2019-10-25 17:46:37 +03:00
return removed , comment , nil
2018-05-09 19:29:04 +03:00
}
2019-10-25 17:46:37 +03:00
func ( issue * Issue ) toggleAssignee ( sess * xorm . Session , doer * User , assigneeID int64 , isCreate bool ) ( removed bool , comment * Comment , err error ) {
removed , err = toggleUserAssignee ( sess , issue , assigneeID )
2018-05-09 19:29:04 +03:00
if err != nil {
2019-10-25 17:46:37 +03:00
return false , nil , fmt . Errorf ( "UpdateIssueUserByAssignee: %v" , err )
2018-05-09 19:29:04 +03:00
}
// Repo infos
if err = issue . loadRepo ( sess ) ; err != nil {
2019-10-25 17:46:37 +03:00
return false , nil , fmt . Errorf ( "loadRepo: %v" , err )
2018-05-09 19:29:04 +03:00
}
2019-12-01 05:44:39 +03:00
var opts = & CreateCommentOptions {
2019-11-15 21:18:09 +03:00
Type : CommentTypeAssignees ,
Doer : doer ,
Repo : issue . Repo ,
Issue : issue ,
RemovedAssignee : removed ,
AssigneeID : assigneeID ,
2019-12-01 05:44:39 +03:00
}
// Comment
2019-12-16 06:54:24 +03:00
comment , err = createComment ( sess , opts )
2019-10-25 17:46:37 +03:00
if err != nil {
2019-11-15 21:18:09 +03:00
return false , nil , fmt . Errorf ( "createComment: %v" , err )
2018-05-09 19:29:04 +03:00
}
2018-11-28 14:26:14 +03:00
// if pull request is in the middle of creation - don't call webhook
2018-11-20 20:10:18 +03:00
if isCreate {
2019-10-25 17:46:37 +03:00
return removed , comment , err
2018-11-20 20:10:18 +03:00
}
2019-10-25 17:46:37 +03:00
return removed , comment , nil
2018-05-09 19:29:04 +03:00
}
2019-10-25 17:46:37 +03:00
// toggles user assignee state in database
func toggleUserAssignee ( e * xorm . Session , issue * Issue , assigneeID int64 ) ( removed bool , err error ) {
2018-05-09 19:29:04 +03:00
2019-10-25 17:46:37 +03:00
// Check if the user exists
assignee , err := getUserByID ( e , assigneeID )
if err != nil {
return false , err
2018-05-09 19:29:04 +03:00
}
2019-10-25 17:46:37 +03:00
// Check if the submitted user is already assigned, if yes delete him otherwise add him
var i int
for i = 0 ; i < len ( issue . Assignees ) ; i ++ {
if issue . Assignees [ i ] . ID == assigneeID {
break
2018-05-09 19:29:04 +03:00
}
}
2019-10-25 17:46:37 +03:00
assigneeIn := IssueAssignees { AssigneeID : assigneeID , IssueID : issue . ID }
2018-05-09 19:29:04 +03:00
2019-10-25 17:46:37 +03:00
toBeDeleted := i < len ( issue . Assignees )
if toBeDeleted {
issue . Assignees = append ( issue . Assignees [ : i ] , issue . Assignees [ i : ] ... )
_ , err = e . Delete ( assigneeIn )
2018-05-09 19:29:04 +03:00
if err != nil {
2019-10-25 17:46:37 +03:00
return toBeDeleted , err
}
} else {
issue . Assignees = append ( issue . Assignees , assignee )
_ , err = e . Insert ( assigneeIn )
if err != nil {
return toBeDeleted , err
2018-05-09 19:29:04 +03:00
}
}
2019-10-25 17:46:37 +03:00
return toBeDeleted , nil
2018-05-09 19:29:04 +03:00
}
// MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs
func MakeIDsFromAPIAssigneesToAdd ( oneAssignee string , multipleAssignees [ ] string ) ( assigneeIDs [ ] int64 , err error ) {
2019-11-10 11:07:21 +03:00
var requestAssignees [ ] string
2018-05-09 19:29:04 +03:00
// Keeping the old assigning method for compatibility reasons
2019-11-10 11:07:21 +03:00
if oneAssignee != "" && ! util . IsStringInSlice ( oneAssignee , multipleAssignees ) {
requestAssignees = append ( requestAssignees , oneAssignee )
}
2018-05-09 19:29:04 +03:00
2019-11-10 11:07:21 +03:00
//Prevent empty assignees
if len ( multipleAssignees ) > 0 && multipleAssignees [ 0 ] != "" {
requestAssignees = append ( requestAssignees , multipleAssignees ... )
2018-05-09 19:29:04 +03:00
}
// Get the IDs of all assignees
2019-11-10 11:07:21 +03:00
assigneeIDs , err = GetUserIDsByNames ( requestAssignees , false )
2018-05-09 19:29:04 +03:00
return
}