2018-05-09 19:29:04 +03:00
// Copyright 2018 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2018-05-09 19:29:04 +03:00
2022-06-13 12:37:59 +03:00
package issues
2018-05-09 19:29:04 +03:00
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"
2023-12-07 10:27:36 +03:00
"xorm.io/builder"
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.
2022-05-20 17:08:52 +03:00
func ( issue * Issue ) LoadAssignees ( ctx context . Context ) ( 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 { }
2022-05-20 17:08:52 +03:00
issue . Assignee = nil
2018-05-09 19:29:04 +03:00
2022-05-20 17:08:52 +03:00
err = db . GetEngine ( ctx ) . Table ( "`user`" ) .
2018-05-09 19:29:04 +03:00
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 ]
}
2022-06-20 13:02:49 +03:00
return err
2018-05-09 19:29:04 +03:00
}
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.
2022-11-19 11:12:33 +03:00
func GetAssigneeIDsByIssue ( ctx context . Context , issueID int64 ) ( [ ] int64 , error ) {
2019-11-18 11:08:20 +03:00
userIDs := make ( [ ] int64 , 0 , 5 )
2022-11-19 11:12:33 +03:00
return userIDs , db . GetEngine ( ctx ) .
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
// IsUserAssignedToIssue returns true when the user is assigned to the issue
2022-05-20 17:08:52 +03:00
func IsUserAssignedToIssue ( ctx context . Context , issue * Issue , user * user_model . User ) ( isAssigned bool , err error ) {
2023-12-07 10:27:36 +03:00
return db . Exist [ IssueAssignees ] ( ctx , builder . Eq { "assignee_id" : user . ID , "issue_id" : issue . ID } )
2018-05-09 19:29:04 +03:00
}
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.
2023-04-14 21:18:28 +03:00
func ToggleIssueAssignee ( ctx context . Context , issue * Issue , doer * user_model . User , assigneeID int64 ) ( removed bool , comment * Comment , err error ) {
ctx , committer , err := db . TxContext ( ctx )
2021-11-19 16:39:57 +03:00
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 ) {
2022-05-20 17:08:52 +03:00
removed , err = toggleUserAssignee ( ctx , issue , assigneeID )
2018-05-09 19:29:04 +03:00
if err != nil {
2022-10-24 22:29:17 +03:00
return false , nil , fmt . Errorf ( "UpdateIssueUserByAssignee: %w" , 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 {
2022-10-24 22:29:17 +03:00
return false , nil , fmt . Errorf ( "loadRepo: %w" , 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-12-10 05:46:31 +03:00
comment , err = CreateComment ( ctx , opts )
2019-10-25 17:46:37 +03:00
if err != nil {
2022-10-24 22:29:17 +03:00
return false , nil , fmt . Errorf ( "createComment: %w" , 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
2022-05-20 17:08:52 +03:00
func toggleUserAssignee ( ctx context . Context , issue * Issue , assigneeID int64 ) ( removed bool , err error ) {
2019-10-25 17:46:37 +03:00
// Check if the user exists
2022-12-03 05:48:26 +03:00
assignee , err := user_model . GetUserByID ( ctx , 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
2022-05-20 17:08:52 +03:00
found := false
i := 0
for ; i < len ( issue . Assignees ) ; i ++ {
2019-10-25 17:46:37 +03:00
if issue . Assignees [ i ] . ID == assigneeID {
2022-05-20 17:08:52 +03:00
found = true
2019-10-25 17:46:37 +03:00
break
2018-05-09 19:29:04 +03:00
}
}
2019-10-25 17:46:37 +03:00
assigneeIn := IssueAssignees { AssigneeID : assigneeID , IssueID : issue . ID }
2022-05-20 17:08:52 +03:00
if found {
issue . Assignees = append ( issue . Assignees [ : i ] , issue . Assignees [ i + 1 : ] ... )
_ , err = db . DeleteByBean ( ctx , & assigneeIn )
2018-05-09 19:29:04 +03:00
if err != nil {
2022-05-20 17:08:52 +03:00
return found , err
2019-10-25 17:46:37 +03:00
}
} else {
issue . Assignees = append ( issue . Assignees , assignee )
2022-05-20 17:08:52 +03:00
if err = db . Insert ( ctx , & assigneeIn ) ; err != nil {
return found , err
2018-05-09 19:29:04 +03:00
}
}
2022-05-20 17:08:52 +03:00
return found , nil
2018-05-09 19:29:04 +03:00
}
// MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs
2022-11-19 11:12:33 +03:00
func MakeIDsFromAPIAssigneesToAdd ( ctx context . Context , 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
Improve utils of slices (#22379)
- Move the file `compare.go` and `slice.go` to `slice.go`.
- Fix `ExistsInSlice`, it's buggy
- It uses `sort.Search`, so it assumes that the input slice is sorted.
- It passes `func(i int) bool { return slice[i] == target })` to
`sort.Search`, that's incorrect, check the doc of `sort.Search`.
- Conbine `IsInt64InSlice(int64, []int64)` and `ExistsInSlice(string,
[]string)` to `SliceContains[T]([]T, T)`.
- Conbine `IsSliceInt64Eq([]int64, []int64)` and `IsEqualSlice([]string,
[]string)` to `SliceSortedEqual[T]([]T, T)`.
- Add `SliceEqual[T]([]T, T)` as a distinction from
`SliceSortedEqual[T]([]T, T)`.
- Redesign `RemoveIDFromList([]int64, int64) ([]int64, bool)` to
`SliceRemoveAll[T]([]T, T) []T`.
- Add `SliceContainsFunc[T]([]T, func(T) bool)` and
`SliceRemoveAllFunc[T]([]T, func(T) bool)` for general use.
- Add comments to explain why not `golang.org/x/exp/slices`.
- Add unit tests.
2023-01-11 08:31:16 +03:00
if oneAssignee != "" && ! util . SliceContainsString ( multipleAssignees , oneAssignee ) {
2019-11-10 11:07:21 +03:00
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
2022-11-19 11:12:33 +03:00
assigneeIDs , err = user_model . GetUserIDsByNames ( ctx , requestAssignees , false )
2018-05-09 19:29:04 +03:00
2022-06-20 13:02:49 +03:00
return assigneeIDs , err
2018-05-09 19:29:04 +03:00
}