選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

issue_project.go 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package issues
  4. import (
  5. "context"
  6. "code.gitea.io/gitea/models/db"
  7. project_model "code.gitea.io/gitea/models/project"
  8. user_model "code.gitea.io/gitea/models/user"
  9. "code.gitea.io/gitea/modules/util"
  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(ctx context.Context) int64 {
  36. var ip project_model.ProjectIssue
  37. has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)
  38. if err != nil || !has {
  39. return 0
  40. }
  41. return ip.ProjectBoardID
  42. }
  43. // LoadIssuesFromBoard load issues assigned to this board
  44. func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList, error) {
  45. issueList, err := Issues(ctx, &IssuesOptions{
  46. ProjectBoardID: b.ID,
  47. ProjectID: b.ProjectID,
  48. SortType: "project-column-sorting",
  49. })
  50. if err != nil {
  51. return nil, err
  52. }
  53. if b.Default {
  54. issues, err := Issues(ctx, &IssuesOptions{
  55. ProjectBoardID: db.NoConditionID,
  56. ProjectID: b.ProjectID,
  57. SortType: "project-column-sorting",
  58. })
  59. if err != nil {
  60. return nil, err
  61. }
  62. issueList = append(issueList, issues...)
  63. }
  64. if err := issueList.LoadComments(ctx); err != nil {
  65. return nil, err
  66. }
  67. return issueList, nil
  68. }
  69. // LoadIssuesFromBoardList load issues assigned to the boards
  70. func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList) (map[int64]IssueList, error) {
  71. issuesMap := make(map[int64]IssueList, len(bs))
  72. for i := range bs {
  73. il, err := LoadIssuesFromBoard(ctx, bs[i])
  74. if err != nil {
  75. return nil, err
  76. }
  77. issuesMap[bs[i].ID] = il
  78. }
  79. return issuesMap, nil
  80. }
  81. // IssueAssignOrRemoveProject changes the project associated with an issue
  82. // If newProjectID is 0, the issue is removed from the project
  83. func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID, newColumnID int64) error {
  84. return db.WithTx(ctx, func(ctx context.Context) error {
  85. oldProjectID := issue.projectID(ctx)
  86. if err := issue.LoadRepo(ctx); err != nil {
  87. return err
  88. }
  89. // Only check if we add a new project and not remove it.
  90. if newProjectID > 0 {
  91. newProject, err := project_model.GetProjectByID(ctx, newProjectID)
  92. if err != nil {
  93. return err
  94. }
  95. if !newProject.CanBeAccessedByOwnerRepo(issue.Repo.OwnerID, issue.Repo) {
  96. return util.NewPermissionDeniedErrorf("issue %d can't be accessed by project %d", issue.ID, newProject.ID)
  97. }
  98. if newColumnID == 0 {
  99. newDefaultColumn, err := newProject.GetDefaultBoard(ctx)
  100. if err != nil {
  101. return err
  102. }
  103. newColumnID = newDefaultColumn.ID
  104. }
  105. }
  106. if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil {
  107. return err
  108. }
  109. if oldProjectID > 0 || newProjectID > 0 {
  110. if _, err := CreateComment(ctx, &CreateCommentOptions{
  111. Type: CommentTypeProject,
  112. Doer: doer,
  113. Repo: issue.Repo,
  114. Issue: issue,
  115. OldProjectID: oldProjectID,
  116. ProjectID: newProjectID,
  117. }); err != nil {
  118. return err
  119. }
  120. }
  121. if newProjectID == 0 {
  122. return nil
  123. }
  124. if newColumnID == 0 {
  125. panic("newColumnID must not be zero") // shouldn't happen
  126. }
  127. res := struct {
  128. MaxSorting int64
  129. IssueCount int64
  130. }{}
  131. if _, err := db.GetEngine(ctx).Select("max(sorting) as max_sorting, count(*) as issue_count").Table("project_issue").
  132. Where("project_id=?", newProjectID).
  133. And("project_board_id=?", newColumnID).
  134. Get(&res); err != nil {
  135. return err
  136. }
  137. newSorting := util.Iif(res.IssueCount > 0, res.MaxSorting+1, 0)
  138. return db.Insert(ctx, &project_model.ProjectIssue{
  139. IssueID: issue.ID,
  140. ProjectID: newProjectID,
  141. ProjectBoardID: newColumnID,
  142. Sorting: newSorting,
  143. })
  144. })
  145. }