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