2020-08-17 06:07:38 +03:00
|
|
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a MIT-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package models
|
|
|
|
|
|
|
|
import (
|
|
|
|
"code.gitea.io/gitea/modules/setting"
|
|
|
|
"code.gitea.io/gitea/modules/timeutil"
|
|
|
|
|
2021-01-15 23:29:32 +03:00
|
|
|
"xorm.io/builder"
|
2020-08-17 06:07:38 +03:00
|
|
|
"xorm.io/xorm"
|
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
|
|
|
// ProjectBoardType is used to represent a project board type
|
|
|
|
ProjectBoardType uint8
|
|
|
|
|
|
|
|
// ProjectBoardList is a list of all project boards in a repository
|
|
|
|
ProjectBoardList []*ProjectBoard
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// ProjectBoardTypeNone is a project board type that has no predefined columns
|
|
|
|
ProjectBoardTypeNone ProjectBoardType = iota
|
|
|
|
|
|
|
|
// ProjectBoardTypeBasicKanban is a project board type that has basic predefined columns
|
|
|
|
ProjectBoardTypeBasicKanban
|
|
|
|
|
|
|
|
// ProjectBoardTypeBugTriage is a project board type that has predefined columns suited to hunting down bugs
|
|
|
|
ProjectBoardTypeBugTriage
|
|
|
|
)
|
|
|
|
|
|
|
|
// ProjectBoard is used to represent boards on a project
|
|
|
|
type ProjectBoard struct {
|
|
|
|
ID int64 `xorm:"pk autoincr"`
|
|
|
|
Title string
|
|
|
|
Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board
|
|
|
|
|
|
|
|
ProjectID int64 `xorm:"INDEX NOT NULL"`
|
|
|
|
CreatorID int64 `xorm:"NOT NULL"`
|
|
|
|
|
|
|
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
|
|
|
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
|
|
|
|
|
|
|
Issues []*Issue `xorm:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsProjectBoardTypeValid checks if the project board type is valid
|
|
|
|
func IsProjectBoardTypeValid(p ProjectBoardType) bool {
|
|
|
|
switch p {
|
|
|
|
case ProjectBoardTypeNone, ProjectBoardTypeBasicKanban, ProjectBoardTypeBugTriage:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func createBoardsForProjectsType(sess *xorm.Session, project *Project) error {
|
|
|
|
|
|
|
|
var items []string
|
|
|
|
|
|
|
|
switch project.BoardType {
|
|
|
|
|
|
|
|
case ProjectBoardTypeBugTriage:
|
|
|
|
items = setting.Project.ProjectBoardBugTriageType
|
|
|
|
|
|
|
|
case ProjectBoardTypeBasicKanban:
|
|
|
|
items = setting.Project.ProjectBoardBasicKanbanType
|
|
|
|
|
|
|
|
case ProjectBoardTypeNone:
|
|
|
|
fallthrough
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(items) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var boards = make([]ProjectBoard, 0, len(items))
|
|
|
|
|
|
|
|
for _, v := range items {
|
|
|
|
boards = append(boards, ProjectBoard{
|
|
|
|
CreatedUnix: timeutil.TimeStampNow(),
|
|
|
|
CreatorID: project.CreatorID,
|
|
|
|
Title: v,
|
|
|
|
ProjectID: project.ID,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := sess.Insert(boards)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewProjectBoard adds a new project board to a given project
|
|
|
|
func NewProjectBoard(board *ProjectBoard) error {
|
|
|
|
_, err := x.Insert(board)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteProjectBoardByID removes all issues references to the project board.
|
|
|
|
func DeleteProjectBoardByID(boardID int64) error {
|
|
|
|
sess := x.NewSession()
|
|
|
|
defer sess.Close()
|
|
|
|
if err := sess.Begin(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := deleteProjectBoardByID(sess, boardID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return sess.Commit()
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteProjectBoardByID(e Engine, boardID int64) error {
|
|
|
|
board, err := getProjectBoard(e, boardID)
|
|
|
|
if err != nil {
|
|
|
|
if IsErrProjectBoardNotExist(err) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = board.removeIssues(e); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := e.ID(board.ID).Delete(board); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteProjectBoardByProjectID(e Engine, projectID int64) error {
|
|
|
|
_, err := e.Where("project_id=?", projectID).Delete(&ProjectBoard{})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetProjectBoard fetches the current board of a project
|
|
|
|
func GetProjectBoard(boardID int64) (*ProjectBoard, error) {
|
|
|
|
return getProjectBoard(x, boardID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getProjectBoard(e Engine, boardID int64) (*ProjectBoard, error) {
|
|
|
|
board := new(ProjectBoard)
|
|
|
|
|
|
|
|
has, err := e.ID(boardID).Get(board)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else if !has {
|
|
|
|
return nil, ErrProjectBoardNotExist{BoardID: boardID}
|
|
|
|
}
|
|
|
|
|
|
|
|
return board, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateProjectBoard updates the title of a project board
|
|
|
|
func UpdateProjectBoard(board *ProjectBoard) error {
|
|
|
|
return updateProjectBoard(x, board)
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateProjectBoard(e Engine, board *ProjectBoard) error {
|
|
|
|
_, err := e.ID(board.ID).Cols(
|
|
|
|
"title",
|
|
|
|
).Update(board)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetProjectBoards fetches all boards related to a project
|
2021-01-15 23:29:32 +03:00
|
|
|
// if no default board set, first board is a temporary "Uncategorized" board
|
|
|
|
func GetProjectBoards(projectID int64) (ProjectBoardList, error) {
|
|
|
|
return getProjectBoards(x, projectID)
|
|
|
|
}
|
2020-08-17 06:07:38 +03:00
|
|
|
|
2021-01-15 23:29:32 +03:00
|
|
|
func getProjectBoards(e Engine, projectID int64) ([]*ProjectBoard, error) {
|
2020-08-17 06:07:38 +03:00
|
|
|
var boards = make([]*ProjectBoard, 0, 5)
|
|
|
|
|
2021-01-15 23:29:32 +03:00
|
|
|
if err := e.Where("project_id=? AND `default`=?", projectID, false).Find(&boards); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultB, err := getDefaultBoard(e, projectID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return append([]*ProjectBoard{defaultB}, boards...), nil
|
2020-08-17 06:07:38 +03:00
|
|
|
}
|
|
|
|
|
2021-01-15 23:29:32 +03:00
|
|
|
// getDefaultBoard return default board and create a dummy if none exist
|
|
|
|
func getDefaultBoard(e Engine, projectID int64) (*ProjectBoard, error) {
|
|
|
|
var board ProjectBoard
|
|
|
|
exist, err := e.Where("project_id=? AND `default`=?", projectID, true).Get(&board)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if exist {
|
|
|
|
return &board, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// represents a board for issues not assigned to one
|
2020-08-17 06:07:38 +03:00
|
|
|
return &ProjectBoard{
|
|
|
|
ProjectID: projectID,
|
|
|
|
Title: "Uncategorized",
|
|
|
|
Default: true,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-01-15 23:29:32 +03:00
|
|
|
// SetDefaultBoard represents a board for issues not assigned to one
|
|
|
|
// if boardID is 0 unset default
|
|
|
|
func SetDefaultBoard(projectID, boardID int64) error {
|
|
|
|
sess := x
|
|
|
|
|
|
|
|
_, err := sess.Where(builder.Eq{
|
|
|
|
"project_id": projectID,
|
|
|
|
"`default`": true,
|
|
|
|
}).Cols("`default`").Update(&ProjectBoard{Default: false})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if boardID > 0 {
|
|
|
|
_, err = sess.ID(boardID).Where(builder.Eq{"project_id": projectID}).
|
|
|
|
Cols("`default`").Update(&ProjectBoard{Default: true})
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-08-17 06:07:38 +03:00
|
|
|
// LoadIssues load issues assigned to this board
|
|
|
|
func (b *ProjectBoard) LoadIssues() (IssueList, error) {
|
2021-01-15 23:29:32 +03:00
|
|
|
issueList := make([]*Issue, 0, 10)
|
|
|
|
|
|
|
|
if b.ID != 0 {
|
|
|
|
issues, err := Issues(&IssuesOptions{
|
|
|
|
ProjectBoardID: b.ID,
|
|
|
|
ProjectID: b.ProjectID,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
issueList = issues
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.Default {
|
|
|
|
issues, err := Issues(&IssuesOptions{
|
|
|
|
ProjectBoardID: -1, // Issues without ProjectBoardID
|
|
|
|
ProjectID: b.ProjectID,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
issueList = append(issueList, issues...)
|
|
|
|
}
|
|
|
|
|
2021-01-20 22:53:48 +03:00
|
|
|
if err := IssueList(issueList).LoadComments(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-15 23:29:32 +03:00
|
|
|
b.Issues = issueList
|
|
|
|
return issueList, nil
|
2020-08-17 06:07:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// LoadIssues load issues assigned to the boards
|
|
|
|
func (bs ProjectBoardList) LoadIssues() (IssueList, error) {
|
|
|
|
issues := make(IssueList, 0, len(bs)*10)
|
|
|
|
for i := range bs {
|
|
|
|
il, err := bs[i].LoadIssues()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
bs[i].Issues = il
|
|
|
|
issues = append(issues, il...)
|
|
|
|
}
|
|
|
|
return issues, nil
|
|
|
|
}
|