diff options
Diffstat (limited to 'models/project/issue.go')
-rw-r--r-- | models/project/issue.go | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/models/project/issue.go b/models/project/issue.go new file mode 100644 index 0000000..3361b53 --- /dev/null +++ b/models/project/issue.go @@ -0,0 +1,143 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package project + +import ( + "context" + "fmt" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" +) + +// ProjectIssue saves relation from issue to a project +type ProjectIssue struct { //revive:disable-line:exported + ID int64 `xorm:"pk autoincr"` + IssueID int64 `xorm:"INDEX"` + ProjectID int64 `xorm:"INDEX"` + + // ProjectColumnID should not be zero since 1.22. If it's zero, the issue will not be displayed on UI and it might result in errors. + ProjectColumnID int64 `xorm:"'project_board_id' INDEX"` + + // the sorting order on the column + Sorting int64 `xorm:"NOT NULL DEFAULT 0"` +} + +func init() { + db.RegisterModel(new(ProjectIssue)) +} + +func deleteProjectIssuesByProjectID(ctx context.Context, projectID int64) error { + _, err := db.GetEngine(ctx).Where("project_id=?", projectID).Delete(&ProjectIssue{}) + return err +} + +// NumIssues return counter of all issues assigned to a project +func (p *Project) NumIssues(ctx context.Context) int { + c, err := db.GetEngine(ctx).Table("project_issue"). + Where("project_id=?", p.ID). + GroupBy("issue_id"). + Cols("issue_id"). + Count() + if err != nil { + log.Error("NumIssues: %v", err) + return 0 + } + return int(c) +} + +// NumClosedIssues return counter of closed issues assigned to a project +func (p *Project) NumClosedIssues(ctx context.Context) int { + c, err := db.GetEngine(ctx).Table("project_issue"). + Join("INNER", "issue", "project_issue.issue_id=issue.id"). + Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, true). + Cols("issue_id"). + Count() + if err != nil { + log.Error("NumClosedIssues: %v", err) + return 0 + } + return int(c) +} + +// NumOpenIssues return counter of open issues assigned to a project +func (p *Project) NumOpenIssues(ctx context.Context) int { + c, err := db.GetEngine(ctx).Table("project_issue"). + Join("INNER", "issue", "project_issue.issue_id=issue.id"). + Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, false). + Cols("issue_id"). + Count() + if err != nil { + log.Error("NumOpenIssues: %v", err) + return 0 + } + return int(c) +} + +// MoveIssuesOnProjectColumn moves or keeps issues in a column and sorts them inside that column +func MoveIssuesOnProjectColumn(ctx context.Context, column *Column, sortedIssueIDs map[int64]int64) error { + return db.WithTx(ctx, func(ctx context.Context) error { + sess := db.GetEngine(ctx) + issueIDs := util.ValuesOfMap(sortedIssueIDs) + + count, err := sess.Table(new(ProjectIssue)).Where("project_id=?", column.ProjectID).In("issue_id", issueIDs).Count() + if err != nil { + return err + } + if int(count) != len(sortedIssueIDs) { + return fmt.Errorf("all issues have to be added to a project first") + } + + for sorting, issueID := range sortedIssueIDs { + _, err = sess.Exec("UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", column.ID, sorting, issueID) + if err != nil { + return err + } + } + return nil + }) +} + +func (c *Column) moveIssuesToAnotherColumn(ctx context.Context, newColumn *Column) error { + if c.ProjectID != newColumn.ProjectID { + return fmt.Errorf("columns have to be in the same project") + } + + if c.ID == newColumn.ID { + return nil + } + + res := struct { + MaxSorting int64 + IssueCount int64 + }{} + if _, err := db.GetEngine(ctx).Select("max(sorting) as max_sorting, count(*) as issue_count"). + Table("project_issue"). + Where("project_id=?", newColumn.ProjectID). + And("project_board_id=?", newColumn.ID). + Get(&res); err != nil { + return err + } + + issues, err := c.GetIssues(ctx) + if err != nil { + return err + } + if len(issues) == 0 { + return nil + } + + nextSorting := util.Iif(res.IssueCount > 0, res.MaxSorting+1, 0) + return db.WithTx(ctx, func(ctx context.Context) error { + for i, issue := range issues { + issue.ProjectColumnID = newColumn.ID + issue.Sorting = nextSorting + int64(i) + if _, err := db.GetEngine(ctx).ID(issue.ID).Cols("project_board_id", "sorting").Update(issue); err != nil { + return err + } + } + return nil + }) +} |