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.

project_issue.go 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "context"
  7. "fmt"
  8. "code.gitea.io/gitea/models/db"
  9. user_model "code.gitea.io/gitea/models/user"
  10. )
  11. // ProjectIssue saves relation from issue to a project
  12. type ProjectIssue struct {
  13. ID int64 `xorm:"pk autoincr"`
  14. IssueID int64 `xorm:"INDEX"`
  15. ProjectID int64 `xorm:"INDEX"`
  16. // If 0, then it has not been added to a specific board in the project
  17. ProjectBoardID int64 `xorm:"INDEX"`
  18. }
  19. func init() {
  20. db.RegisterModel(new(ProjectIssue))
  21. }
  22. func deleteProjectIssuesByProjectID(e db.Engine, projectID int64) error {
  23. _, err := e.Where("project_id=?", projectID).Delete(&ProjectIssue{})
  24. return err
  25. }
  26. // ___
  27. // |_ _|___ ___ _ _ ___
  28. // | |/ __/ __| | | |/ _ \
  29. // | |\__ \__ \ |_| | __/
  30. // |___|___/___/\__,_|\___|
  31. // LoadProject load the project the issue was assigned to
  32. func (i *Issue) LoadProject() (err error) {
  33. return i.loadProject(db.GetEngine(db.DefaultContext))
  34. }
  35. func (i *Issue) loadProject(e db.Engine) (err error) {
  36. if i.Project == nil {
  37. var p Project
  38. if _, err = e.Table("project").
  39. Join("INNER", "project_issue", "project.id=project_issue.project_id").
  40. Where("project_issue.issue_id = ?", i.ID).
  41. Get(&p); err != nil {
  42. return err
  43. }
  44. i.Project = &p
  45. }
  46. return
  47. }
  48. // ProjectID return project id if issue was assigned to one
  49. func (i *Issue) ProjectID() int64 {
  50. return i.projectID(db.GetEngine(db.DefaultContext))
  51. }
  52. func (i *Issue) projectID(e db.Engine) int64 {
  53. var ip ProjectIssue
  54. has, err := e.Where("issue_id=?", i.ID).Get(&ip)
  55. if err != nil || !has {
  56. return 0
  57. }
  58. return ip.ProjectID
  59. }
  60. // ProjectBoardID return project board id if issue was assigned to one
  61. func (i *Issue) ProjectBoardID() int64 {
  62. return i.projectBoardID(db.GetEngine(db.DefaultContext))
  63. }
  64. func (i *Issue) projectBoardID(e db.Engine) int64 {
  65. var ip ProjectIssue
  66. has, err := e.Where("issue_id=?", i.ID).Get(&ip)
  67. if err != nil || !has {
  68. return 0
  69. }
  70. return ip.ProjectBoardID
  71. }
  72. // ____ _ _
  73. // | _ \ _ __ ___ (_) ___ ___| |_
  74. // | |_) | '__/ _ \| |/ _ \/ __| __|
  75. // | __/| | | (_) | | __/ (__| |_
  76. // |_| |_| \___// |\___|\___|\__|
  77. // |__/
  78. // NumIssues return counter of all issues assigned to a project
  79. func (p *Project) NumIssues() int {
  80. c, err := db.GetEngine(db.DefaultContext).Table("project_issue").
  81. Where("project_id=?", p.ID).
  82. GroupBy("issue_id").
  83. Cols("issue_id").
  84. Count()
  85. if err != nil {
  86. return 0
  87. }
  88. return int(c)
  89. }
  90. // NumClosedIssues return counter of closed issues assigned to a project
  91. func (p *Project) NumClosedIssues() int {
  92. c, err := db.GetEngine(db.DefaultContext).Table("project_issue").
  93. Join("INNER", "issue", "project_issue.issue_id=issue.id").
  94. Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, true).
  95. Cols("issue_id").
  96. Count()
  97. if err != nil {
  98. return 0
  99. }
  100. return int(c)
  101. }
  102. // NumOpenIssues return counter of open issues assigned to a project
  103. func (p *Project) NumOpenIssues() int {
  104. c, err := db.GetEngine(db.DefaultContext).Table("project_issue").
  105. Join("INNER", "issue", "project_issue.issue_id=issue.id").
  106. Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, false).
  107. Cols("issue_id").
  108. Count()
  109. if err != nil {
  110. return 0
  111. }
  112. return int(c)
  113. }
  114. // ChangeProjectAssign changes the project associated with an issue
  115. func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64) error {
  116. ctx, committer, err := db.TxContext()
  117. if err != nil {
  118. return err
  119. }
  120. defer committer.Close()
  121. if err := addUpdateIssueProject(ctx, issue, doer, newProjectID); err != nil {
  122. return err
  123. }
  124. return committer.Commit()
  125. }
  126. func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error {
  127. e := db.GetEngine(ctx)
  128. oldProjectID := issue.projectID(e)
  129. if _, err := e.Where("project_issue.issue_id=?", issue.ID).Delete(&ProjectIssue{}); err != nil {
  130. return err
  131. }
  132. if err := issue.loadRepo(e); err != nil {
  133. return err
  134. }
  135. if oldProjectID > 0 || newProjectID > 0 {
  136. if _, err := createComment(ctx, &CreateCommentOptions{
  137. Type: CommentTypeProject,
  138. Doer: doer,
  139. Repo: issue.Repo,
  140. Issue: issue,
  141. OldProjectID: oldProjectID,
  142. ProjectID: newProjectID,
  143. }); err != nil {
  144. return err
  145. }
  146. }
  147. _, err := e.Insert(&ProjectIssue{
  148. IssueID: issue.ID,
  149. ProjectID: newProjectID,
  150. })
  151. return err
  152. }
  153. // ____ _ _ ____ _
  154. // | _ \ _ __ ___ (_) ___ ___| |_| __ ) ___ __ _ _ __ __| |
  155. // | |_) | '__/ _ \| |/ _ \/ __| __| _ \ / _ \ / _` | '__/ _` |
  156. // | __/| | | (_) | | __/ (__| |_| |_) | (_) | (_| | | | (_| |
  157. // |_| |_| \___// |\___|\___|\__|____/ \___/ \__,_|_| \__,_|
  158. // |__/
  159. // MoveIssueAcrossProjectBoards move a card from one board to another
  160. func MoveIssueAcrossProjectBoards(issue *Issue, board *ProjectBoard) error {
  161. ctx, committer, err := db.TxContext()
  162. if err != nil {
  163. return err
  164. }
  165. defer committer.Close()
  166. sess := db.GetEngine(ctx)
  167. var pis ProjectIssue
  168. has, err := sess.Where("issue_id=?", issue.ID).Get(&pis)
  169. if err != nil {
  170. return err
  171. }
  172. if !has {
  173. return fmt.Errorf("issue has to be added to a project first")
  174. }
  175. pis.ProjectBoardID = board.ID
  176. if _, err := sess.ID(pis.ID).Cols("project_board_id").Update(&pis); err != nil {
  177. return err
  178. }
  179. return committer.Commit()
  180. }
  181. func (pb *ProjectBoard) removeIssues(e db.Engine) error {
  182. _, err := e.Exec("UPDATE `project_issue` SET project_board_id = 0 WHERE project_board_id = ? ", pb.ID)
  183. return err
  184. }