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 (
2021-11-19 16:39:57 +03:00
"context"
2018-05-09 19:29:04 +03:00
"fmt"
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2019-11-10 11:07:21 +03:00
"code.gitea.io/gitea/modules/util"
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" `
}
2021-09-19 14:49:59 +03:00
func init ( ) {
db . RegisterModel ( new ( IssueAssignees ) )
}
2020-02-29 05:49:50 +03:00
// LoadAssignees load assignees of this issue.
func ( issue * Issue ) LoadAssignees ( ) error {
2021-09-23 18:45:36 +03:00
return issue . loadAssignees ( db . GetEngine ( db . DefaultContext ) )
2020-02-29 05:49:50 +03:00
}
2018-05-09 19:29:04 +03:00
// This loads all assignees of an issue
2021-09-19 14:49:59 +03:00
func ( issue * Issue ) loadAssignees ( e db . Engine ) ( err error ) {
2018-05-09 19:29:04 +03:00
// Reset maybe preexisting assignees
2021-11-24 12:49:20 +03:00
issue . Assignees = [ ] * user_model . User { }
2018-05-09 19:29:04 +03:00
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 )
2021-09-23 18:45:36 +03:00
return userIDs , db . GetEngine ( db . DefaultContext ) . Table ( "issue_assignees" ) .
2019-11-18 11:08:20 +03:00
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
2021-11-24 12:49:20 +03:00
func GetAssigneesByIssue ( issue * Issue ) ( assignees [ ] * user_model . User , err error ) {
2021-09-23 18:45:36 +03:00
return getAssigneesByIssue ( db . GetEngine ( db . DefaultContext ) , issue )
2019-01-05 00:51:27 +03:00
}
2021-11-24 12:49:20 +03:00
func getAssigneesByIssue ( e db . Engine , issue * Issue ) ( assignees [ ] * user_model . User , err error ) {
2019-01-05 00:51:27 +03:00
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
2021-11-24 12:49:20 +03:00
func IsUserAssignedToIssue ( issue * Issue , user * user_model . User ) ( isAssigned bool , err error ) {
2021-09-23 18:45:36 +03:00
return isUserAssignedToIssue ( db . GetEngine ( db . DefaultContext ) , issue , user )
2019-10-25 17:46:37 +03:00
}
2021-11-24 12:49:20 +03:00
func isUserAssignedToIssue ( e db . Engine , issue * Issue , user * user_model . User ) ( isAssigned bool , err error ) {
2019-10-25 17:46:37 +03:00
return e . Get ( & IssueAssignees { IssueID : issue . ID , AssigneeID : user . ID } )
2018-05-09 19:29:04 +03:00
}
// ClearAssigneeByUserID deletes all assignments of an user
2021-09-19 14:49:59 +03:00
func clearAssigneeByUserID ( sess db . Engine , userID int64 ) ( err error ) {
2018-05-09 19:29:04 +03:00
_ , err = sess . Delete ( & IssueAssignees { AssigneeID : userID } )
return
}
2022-03-29 17:57:33 +03:00
// ToggleIssueAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it.
func ToggleIssueAssignee ( issue * Issue , doer * user_model . User , assigneeID int64 ) ( removed bool , comment * Comment , err error ) {
2021-11-19 16:39:57 +03:00
ctx , committer , err := db . TxContext ( )
if err != nil {
2019-10-25 17:46:37 +03:00
return false , nil , err
2018-05-09 19:29:04 +03:00
}
2021-11-19 16:39:57 +03:00
defer committer . Close ( )
2018-05-09 19:29:04 +03:00
2022-03-29 17:57:33 +03:00
removed , comment , err = toggleIssueAssignee ( ctx , issue , doer , assigneeID , false )
2019-10-25 17:46:37 +03:00
if err != nil {
return false , nil , err
2018-05-09 19:29:04 +03:00
}
2021-11-19 16:39:57 +03:00
if err := committer . 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
}
2022-03-29 17:57:33 +03:00
func toggleIssueAssignee ( ctx context . Context , issue * Issue , doer * user_model . User , assigneeID int64 , isCreate bool ) ( removed bool , comment * Comment , err error ) {
2021-11-19 16:39:57 +03:00
sess := db . GetEngine ( ctx )
2019-10-25 17:46:37 +03:00
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
2022-04-08 12:11:15 +03:00
if err = issue . LoadRepo ( ctx ) ; 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
}
2021-03-14 21:52:12 +03:00
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
2022-04-08 12:11:15 +03:00
comment , err = CreateCommentCtx ( ctx , 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
2021-11-19 16:39:57 +03:00
func toggleUserAssignee ( e db . Engine , issue * Issue , assigneeID int64 ) ( removed bool , err error ) {
2019-10-25 17:46:37 +03:00
// Check if the user exists
2021-11-24 12:49:20 +03:00
assignee , err := user_model . GetUserByIDEngine ( e , assigneeID )
2019-10-25 17:46:37 +03:00
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
2021-03-14 21:52:12 +03:00
// Prevent empty assignees
2019-11-10 11:07:21 +03:00
if len ( multipleAssignees ) > 0 && multipleAssignees [ 0 ] != "" {
requestAssignees = append ( requestAssignees , multipleAssignees ... )
2018-05-09 19:29:04 +03:00
}
// Get the IDs of all assignees
2021-11-24 12:49:20 +03:00
assigneeIDs , err = user_model . GetUserIDsByNames ( requestAssignees , false )
2018-05-09 19:29:04 +03:00
return
}