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