You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

assignees.go 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package issues
  4. import (
  5. "context"
  6. "fmt"
  7. "code.gitea.io/gitea/models/db"
  8. user_model "code.gitea.io/gitea/models/user"
  9. "code.gitea.io/gitea/modules/util"
  10. )
  11. // IssueAssignees saves all issue assignees
  12. type IssueAssignees struct {
  13. ID int64 `xorm:"pk autoincr"`
  14. AssigneeID int64 `xorm:"INDEX"`
  15. IssueID int64 `xorm:"INDEX"`
  16. }
  17. func init() {
  18. db.RegisterModel(new(IssueAssignees))
  19. }
  20. // LoadAssignees load assignees of this issue.
  21. func (issue *Issue) LoadAssignees(ctx context.Context) (err error) {
  22. // Reset maybe preexisting assignees
  23. issue.Assignees = []*user_model.User{}
  24. issue.Assignee = nil
  25. err = db.GetEngine(ctx).Table("`user`").
  26. Join("INNER", "issue_assignees", "assignee_id = `user`.id").
  27. Where("issue_assignees.issue_id = ?", issue.ID).
  28. Find(&issue.Assignees)
  29. if err != nil {
  30. return err
  31. }
  32. // Check if we have at least one assignee and if yes put it in as `Assignee`
  33. if len(issue.Assignees) > 0 {
  34. issue.Assignee = issue.Assignees[0]
  35. }
  36. return err
  37. }
  38. // GetAssigneeIDsByIssue returns the IDs of users assigned to an issue
  39. // but skips joining with `user` for performance reasons.
  40. // User permissions must be verified elsewhere if required.
  41. func GetAssigneeIDsByIssue(ctx context.Context, issueID int64) ([]int64, error) {
  42. userIDs := make([]int64, 0, 5)
  43. return userIDs, db.GetEngine(ctx).
  44. Table("issue_assignees").
  45. Cols("assignee_id").
  46. Where("issue_id = ?", issueID).
  47. Distinct("assignee_id").
  48. Find(&userIDs)
  49. }
  50. // IsUserAssignedToIssue returns true when the user is assigned to the issue
  51. func IsUserAssignedToIssue(ctx context.Context, issue *Issue, user *user_model.User) (isAssigned bool, err error) {
  52. return db.GetByBean(ctx, &IssueAssignees{IssueID: issue.ID, AssigneeID: user.ID})
  53. }
  54. // ToggleIssueAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it.
  55. func ToggleIssueAssignee(issue *Issue, doer *user_model.User, assigneeID int64) (removed bool, comment *Comment, err error) {
  56. ctx, committer, err := db.TxContext(db.DefaultContext)
  57. if err != nil {
  58. return false, nil, err
  59. }
  60. defer committer.Close()
  61. removed, comment, err = toggleIssueAssignee(ctx, issue, doer, assigneeID, false)
  62. if err != nil {
  63. return false, nil, err
  64. }
  65. if err := committer.Commit(); err != nil {
  66. return false, nil, err
  67. }
  68. return removed, comment, nil
  69. }
  70. func toggleIssueAssignee(ctx context.Context, issue *Issue, doer *user_model.User, assigneeID int64, isCreate bool) (removed bool, comment *Comment, err error) {
  71. removed, err = toggleUserAssignee(ctx, issue, assigneeID)
  72. if err != nil {
  73. return false, nil, fmt.Errorf("UpdateIssueUserByAssignee: %w", err)
  74. }
  75. // Repo infos
  76. if err = issue.LoadRepo(ctx); err != nil {
  77. return false, nil, fmt.Errorf("loadRepo: %w", err)
  78. }
  79. opts := &CreateCommentOptions{
  80. Type: CommentTypeAssignees,
  81. Doer: doer,
  82. Repo: issue.Repo,
  83. Issue: issue,
  84. RemovedAssignee: removed,
  85. AssigneeID: assigneeID,
  86. }
  87. // Comment
  88. comment, err = CreateComment(ctx, opts)
  89. if err != nil {
  90. return false, nil, fmt.Errorf("createComment: %w", err)
  91. }
  92. // if pull request is in the middle of creation - don't call webhook
  93. if isCreate {
  94. return removed, comment, err
  95. }
  96. return removed, comment, nil
  97. }
  98. // toggles user assignee state in database
  99. func toggleUserAssignee(ctx context.Context, issue *Issue, assigneeID int64) (removed bool, err error) {
  100. // Check if the user exists
  101. assignee, err := user_model.GetUserByID(ctx, assigneeID)
  102. if err != nil {
  103. return false, err
  104. }
  105. // Check if the submitted user is already assigned, if yes delete him otherwise add him
  106. found := false
  107. i := 0
  108. for ; i < len(issue.Assignees); i++ {
  109. if issue.Assignees[i].ID == assigneeID {
  110. found = true
  111. break
  112. }
  113. }
  114. assigneeIn := IssueAssignees{AssigneeID: assigneeID, IssueID: issue.ID}
  115. if found {
  116. issue.Assignees = append(issue.Assignees[:i], issue.Assignees[i+1:]...)
  117. _, err = db.DeleteByBean(ctx, &assigneeIn)
  118. if err != nil {
  119. return found, err
  120. }
  121. } else {
  122. issue.Assignees = append(issue.Assignees, assignee)
  123. if err = db.Insert(ctx, &assigneeIn); err != nil {
  124. return found, err
  125. }
  126. }
  127. return found, nil
  128. }
  129. // MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs
  130. func MakeIDsFromAPIAssigneesToAdd(ctx context.Context, oneAssignee string, multipleAssignees []string) (assigneeIDs []int64, err error) {
  131. var requestAssignees []string
  132. // Keeping the old assigning method for compatibility reasons
  133. if oneAssignee != "" && !util.SliceContainsString(multipleAssignees, oneAssignee) {
  134. requestAssignees = append(requestAssignees, oneAssignee)
  135. }
  136. // Prevent empty assignees
  137. if len(multipleAssignees) > 0 && multipleAssignees[0] != "" {
  138. requestAssignees = append(requestAssignees, multipleAssignees...)
  139. }
  140. // Get the IDs of all assignees
  141. assigneeIDs, err = user_model.GetUserIDsByNames(ctx, requestAssignees, false)
  142. return assigneeIDs, err
  143. }