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.

issue_project.go 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // Copyright 2021 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. project_model "code.gitea.io/gitea/models/project"
  9. user_model "code.gitea.io/gitea/models/user"
  10. )
  11. // LoadProject load the project the issue was assigned to
  12. func (issue *Issue) LoadProject(ctx context.Context) (err error) {
  13. if issue.Project == nil {
  14. var p project_model.Project
  15. has, err := db.GetEngine(ctx).Table("project").
  16. Join("INNER", "project_issue", "project.id=project_issue.project_id").
  17. Where("project_issue.issue_id = ?", issue.ID).Get(&p)
  18. if err != nil {
  19. return err
  20. } else if has {
  21. issue.Project = &p
  22. }
  23. }
  24. return err
  25. }
  26. func (issue *Issue) projectID(ctx context.Context) int64 {
  27. var ip project_model.ProjectIssue
  28. has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)
  29. if err != nil || !has {
  30. return 0
  31. }
  32. return ip.ProjectID
  33. }
  34. // ProjectBoardID return project board id if issue was assigned to one
  35. func (issue *Issue) ProjectBoardID() int64 {
  36. return issue.projectBoardID(db.DefaultContext)
  37. }
  38. func (issue *Issue) projectBoardID(ctx context.Context) int64 {
  39. var ip project_model.ProjectIssue
  40. has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)
  41. if err != nil || !has {
  42. return 0
  43. }
  44. return ip.ProjectBoardID
  45. }
  46. // LoadIssuesFromBoard load issues assigned to this board
  47. func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList, error) {
  48. issueList := make(IssueList, 0, 10)
  49. if b.ID != 0 {
  50. issues, err := Issues(ctx, &IssuesOptions{
  51. ProjectBoardID: b.ID,
  52. ProjectID: b.ProjectID,
  53. SortType: "project-column-sorting",
  54. })
  55. if err != nil {
  56. return nil, err
  57. }
  58. issueList = issues
  59. }
  60. if b.Default {
  61. issues, err := Issues(ctx, &IssuesOptions{
  62. ProjectBoardID: -1, // Issues without ProjectBoardID
  63. ProjectID: b.ProjectID,
  64. SortType: "project-column-sorting",
  65. })
  66. if err != nil {
  67. return nil, err
  68. }
  69. issueList = append(issueList, issues...)
  70. }
  71. if err := issueList.LoadComments(ctx); err != nil {
  72. return nil, err
  73. }
  74. return issueList, nil
  75. }
  76. // LoadIssuesFromBoardList load issues assigned to the boards
  77. func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList) (map[int64]IssueList, error) {
  78. issuesMap := make(map[int64]IssueList, len(bs))
  79. for i := range bs {
  80. il, err := LoadIssuesFromBoard(ctx, bs[i])
  81. if err != nil {
  82. return nil, err
  83. }
  84. issuesMap[bs[i].ID] = il
  85. }
  86. return issuesMap, nil
  87. }
  88. // ChangeProjectAssign changes the project associated with an issue
  89. func ChangeProjectAssign(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error {
  90. ctx, committer, err := db.TxContext(ctx)
  91. if err != nil {
  92. return err
  93. }
  94. defer committer.Close()
  95. if err := addUpdateIssueProject(ctx, issue, doer, newProjectID); err != nil {
  96. return err
  97. }
  98. return committer.Commit()
  99. }
  100. func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error {
  101. oldProjectID := issue.projectID(ctx)
  102. if err := issue.LoadRepo(ctx); err != nil {
  103. return err
  104. }
  105. // Only check if we add a new project and not remove it.
  106. if newProjectID > 0 {
  107. newProject, err := project_model.GetProjectByID(ctx, newProjectID)
  108. if err != nil {
  109. return err
  110. }
  111. if newProject.RepoID != issue.RepoID && newProject.OwnerID != issue.Repo.OwnerID {
  112. return fmt.Errorf("issue's repository is not the same as project's repository")
  113. }
  114. }
  115. if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil {
  116. return err
  117. }
  118. if oldProjectID > 0 || newProjectID > 0 {
  119. if _, err := CreateComment(ctx, &CreateCommentOptions{
  120. Type: CommentTypeProject,
  121. Doer: doer,
  122. Repo: issue.Repo,
  123. Issue: issue,
  124. OldProjectID: oldProjectID,
  125. ProjectID: newProjectID,
  126. }); err != nil {
  127. return err
  128. }
  129. }
  130. return db.Insert(ctx, &project_model.ProjectIssue{
  131. IssueID: issue.ID,
  132. ProjectID: newProjectID,
  133. })
  134. }
  135. // MoveIssueAcrossProjectBoards move a card from one board to another
  136. func MoveIssueAcrossProjectBoards(issue *Issue, board *project_model.Board) error {
  137. ctx, committer, err := db.TxContext(db.DefaultContext)
  138. if err != nil {
  139. return err
  140. }
  141. defer committer.Close()
  142. sess := db.GetEngine(ctx)
  143. var pis project_model.ProjectIssue
  144. has, err := sess.Where("issue_id=?", issue.ID).Get(&pis)
  145. if err != nil {
  146. return err
  147. }
  148. if !has {
  149. return fmt.Errorf("issue has to be added to a project first")
  150. }
  151. pis.ProjectBoardID = board.ID
  152. if _, err := sess.ID(pis.ID).Cols("project_board_id").Update(&pis); err != nil {
  153. return err
  154. }
  155. return committer.Commit()
  156. }