Changes
18 changed files (+810/-713)
-
-
@@ -1063,44 +1063,6 @@ func (err ErrLabelNotExist) Error() string {return fmt.Sprintf("label does not exist [label_id: %d]", err.LabelID) } // __________ __ __ // \______ \_______ ____ |__| ____ _____/ |_ ______ // | ___/\_ __ \/ _ \ | |/ __ \_/ ___\ __\/ ___/ // | | | | \( <_> ) | \ ___/\ \___| | \___ \ // |____| |__| \____/\__| |\___ >\___ >__| /____ > // \______| \/ \/ \/ // ErrProjectNotExist represents a "ProjectNotExist" kind of error. type ErrProjectNotExist struct { ID int64 RepoID int64 } // IsErrProjectNotExist checks if an error is a ErrProjectNotExist func IsErrProjectNotExist(err error) bool { _, ok := err.(ErrProjectNotExist) return ok } func (err ErrProjectNotExist) Error() string { return fmt.Sprintf("projects does not exist [id: %d]", err.ID) } // ErrProjectBoardNotExist represents a "ProjectBoardNotExist" kind of error. type ErrProjectBoardNotExist struct { BoardID int64 } // IsErrProjectBoardNotExist checks if an error is a ErrProjectBoardNotExist func IsErrProjectBoardNotExist(err error) bool { _, ok := err.(ErrProjectBoardNotExist) return ok } func (err ErrProjectBoardNotExist) Error() string { return fmt.Sprintf("project board does not exist [id: %d]", err.BoardID) } // _____ .__.__ __ // / \ |__| | ____ _______/ |_ ____ ____ ____ // / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \
-
-
-
@@ -19,6 +19,7 @@ import ("code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user"
-
@@ -45,14 +46,14 @@ type Issue struct {PosterID int64 `xorm:"INDEX"` Poster *user_model.User `xorm:"-"` OriginalAuthor string OriginalAuthorID int64 `xorm:"index"` Title string `xorm:"name"` Content string `xorm:"LONGTEXT"` RenderedContent string `xorm:"-"` Labels []*Label `xorm:"-"` MilestoneID int64 `xorm:"INDEX"` Milestone *Milestone `xorm:"-"` Project *Project `xorm:"-"` OriginalAuthorID int64 `xorm:"index"` Title string `xorm:"name"` Content string `xorm:"LONGTEXT"` RenderedContent string `xorm:"-"` Labels []*Label `xorm:"-"` MilestoneID int64 `xorm:"INDEX"` Milestone *Milestone `xorm:"-"` Project *project_model.Project `xorm:"-"` Priority int AssigneeID int64 `xorm:"-"` Assignee *user_model.User `xorm:"-"`
-
@@ -2135,7 +2136,7 @@ func deleteIssue(ctx context.Context, issue *Issue) error {&IssueWatch{}, &Stopwatch{}, &TrackedTime{}, &ProjectIssue{}, &project_model.ProjectIssue{}, &repo_model.Attachment{}, &PullRequest{}, ); err != nil {
-
@@ -2469,7 +2470,7 @@ func deleteIssuesByRepoID(sess db.Engine, repoID int64) (attachmentPaths []strin} if _, err = sess.In("issue_id", deleteCond). Delete(&ProjectIssue{}); err != nil { Delete(&project_model.ProjectIssue{}); err != nil { return }
-
-
-
@@ -17,6 +17,7 @@ import ("code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git"
-
@@ -204,8 +205,8 @@ type Comment struct {RemovedLabels []*Label `xorm:"-"` OldProjectID int64 ProjectID int64 OldProject *Project `xorm:"-"` Project *Project `xorm:"-"` OldProject *project_model.Project `xorm:"-"` Project *project_model.Project `xorm:"-"` OldMilestoneID int64 MilestoneID int64 OldMilestone *Milestone `xorm:"-"`
-
@@ -469,7 +470,7 @@ func (c *Comment) LoadLabel() error {// LoadProject if comment.Type is CommentTypeProject, then load project. func (c *Comment) LoadProject() error { if c.OldProjectID > 0 { var oldProject Project var oldProject project_model.Project has, err := db.GetEngine(db.DefaultContext).ID(c.OldProjectID).Get(&oldProject) if err != nil { return err
-
@@ -479,7 +480,7 @@ func (c *Comment) LoadProject() error {} if c.ProjectID > 0 { var project Project var project project_model.Project has, err := db.GetEngine(db.DefaultContext).ID(c.ProjectID).Get(&project) if err != nil { return err
-
-
models/issue_project.go (new)
-
@@ -0,0 +1,181 @@// Copyright 2021 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 ( "context" "fmt" "code.gitea.io/gitea/models/db" project_model "code.gitea.io/gitea/models/project" user_model "code.gitea.io/gitea/models/user" ) // LoadProject load the project the issue was assigned to func (i *Issue) LoadProject() (err error) { return i.loadProject(db.GetEngine(db.DefaultContext)) } func (i *Issue) loadProject(e db.Engine) (err error) { if i.Project == nil { var p project_model.Project if _, err = e.Table("project"). Join("INNER", "project_issue", "project.id=project_issue.project_id"). Where("project_issue.issue_id = ?", i.ID). Get(&p); err != nil { return err } i.Project = &p } return } // ProjectID return project id if issue was assigned to one func (i *Issue) ProjectID() int64 { return i.projectID(db.GetEngine(db.DefaultContext)) } func (i *Issue) projectID(e db.Engine) int64 { var ip project_model.ProjectIssue has, err := e.Where("issue_id=?", i.ID).Get(&ip) if err != nil || !has { return 0 } return ip.ProjectID } // ProjectBoardID return project board id if issue was assigned to one func (i *Issue) ProjectBoardID() int64 { return i.projectBoardID(db.GetEngine(db.DefaultContext)) } func (i *Issue) projectBoardID(e db.Engine) int64 { var ip project_model.ProjectIssue has, err := e.Where("issue_id=?", i.ID).Get(&ip) if err != nil || !has { return 0 } return ip.ProjectBoardID } // LoadIssuesFromBoard load issues assigned to this board func LoadIssuesFromBoard(b *project_model.Board) (IssueList, error) { 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...) } if err := IssueList(issueList).LoadComments(); err != nil { return nil, err } return issueList, nil } // LoadIssuesFromBoardList load issues assigned to the boards func LoadIssuesFromBoardList(bs project_model.BoardList) (map[int64]IssueList, error) { issuesMap := make(map[int64]IssueList, len(bs)) for i := range bs { il, err := LoadIssuesFromBoard(bs[i]) if err != nil { return nil, err } issuesMap[bs[i].ID] = il } return issuesMap, nil } // ChangeProjectAssign changes the project associated with an issue func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64) error { ctx, committer, err := db.TxContext() if err != nil { return err } defer committer.Close() if err := addUpdateIssueProject(ctx, issue, doer, newProjectID); err != nil { return err } return committer.Commit() } func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error { e := db.GetEngine(ctx) oldProjectID := issue.projectID(e) if _, err := e.Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil { return err } if err := issue.loadRepo(ctx); err != nil { return err } if oldProjectID > 0 || newProjectID > 0 { if _, err := createComment(ctx, &CreateCommentOptions{ Type: CommentTypeProject, Doer: doer, Repo: issue.Repo, Issue: issue, OldProjectID: oldProjectID, ProjectID: newProjectID, }); err != nil { return err } } _, err := e.Insert(&project_model.ProjectIssue{ IssueID: issue.ID, ProjectID: newProjectID, }) return err } // MoveIssueAcrossProjectBoards move a card from one board to another func MoveIssueAcrossProjectBoards(issue *Issue, board *project_model.Board) error { ctx, committer, err := db.TxContext() if err != nil { return err } defer committer.Close() sess := db.GetEngine(ctx) var pis project_model.ProjectIssue has, err := sess.Where("issue_id=?", issue.ID).Get(&pis) if err != nil { return err } if !has { return fmt.Errorf("issue has to be added to a project first") } pis.ProjectBoardID = board.ID if _, err := sess.ID(pis.ID).Cols("project_board_id").Update(&pis); err != nil { return err } return committer.Commit() }
-
-
models/project/board.go (new)
-
@@ -0,0 +1,289 @@// 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 project import ( "context" "fmt" "regexp" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "xorm.io/builder" ) type ( // BoardType is used to represent a project board type BoardType uint8 // BoardList is a list of all project boards in a repository BoardList []*Board ) const ( // BoardTypeNone is a project board type that has no predefined columns BoardTypeNone BoardType = iota // BoardTypeBasicKanban is a project board type that has basic predefined columns BoardTypeBasicKanban // BoardTypeBugTriage is a project board type that has predefined columns suited to hunting down bugs BoardTypeBugTriage ) // BoardColorPattern is a regexp witch can validate BoardColor var BoardColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$") // Board is used to represent boards on a project type Board 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 Sorting int8 `xorm:"NOT NULL DEFAULT 0"` Color string `xorm:"VARCHAR(7)"` ProjectID int64 `xorm:"INDEX NOT NULL"` CreatorID int64 `xorm:"NOT NULL"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` } // TableName return the real table name func (Board) TableName() string { return "project_board" } // NumIssues return counter of all issues assigned to the board func (b *Board) NumIssues() int { c, err := db.GetEngine(db.DefaultContext).Table("project_issue"). Where("project_id=?", b.ProjectID). And("project_board_id=?", b.ID). GroupBy("issue_id"). Cols("issue_id"). Count() if err != nil { return 0 } return int(c) } func init() { db.RegisterModel(new(Board)) } // IsBoardTypeValid checks if the project board type is valid func IsBoardTypeValid(p BoardType) bool { switch p { case BoardTypeNone, BoardTypeBasicKanban, BoardTypeBugTriage: return true default: return false } } func createBoardsForProjectsType(ctx context.Context, project *Project) error { var items []string switch project.BoardType { case BoardTypeBugTriage: items = setting.Project.ProjectBoardBugTriageType case BoardTypeBasicKanban: items = setting.Project.ProjectBoardBasicKanbanType case BoardTypeNone: fallthrough default: return nil } if len(items) == 0 { return nil } boards := make([]Board, 0, len(items)) for _, v := range items { boards = append(boards, Board{ CreatedUnix: timeutil.TimeStampNow(), CreatorID: project.CreatorID, Title: v, ProjectID: project.ID, }) } return db.Insert(ctx, boards) } // NewBoard adds a new project board to a given project func NewBoard(board *Board) error { if len(board.Color) != 0 && !BoardColorPattern.MatchString(board.Color) { return fmt.Errorf("bad color code: %s", board.Color) } _, err := db.GetEngine(db.DefaultContext).Insert(board) return err } // DeleteBoardByID removes all issues references to the project board. func DeleteBoardByID(boardID int64) error { ctx, committer, err := db.TxContext() if err != nil { return err } defer committer.Close() if err := deleteBoardByID(ctx, boardID); err != nil { return err } return committer.Commit() } func deleteBoardByID(ctx context.Context, boardID int64) error { e := db.GetEngine(ctx) board, err := getBoard(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 deleteBoardByProjectID(e db.Engine, projectID int64) error { _, err := e.Where("project_id=?", projectID).Delete(&Board{}) return err } // GetBoard fetches the current board of a project func GetBoard(boardID int64) (*Board, error) { return getBoard(db.GetEngine(db.DefaultContext), boardID) } func getBoard(e db.Engine, boardID int64) (*Board, error) { board := new(Board) has, err := e.ID(boardID).Get(board) if err != nil { return nil, err } else if !has { return nil, ErrProjectBoardNotExist{BoardID: boardID} } return board, nil } // UpdateBoard updates a project board func UpdateBoard(board *Board) error { return updateBoard(db.GetEngine(db.DefaultContext), board) } func updateBoard(e db.Engine, board *Board) error { var fieldToUpdate []string if board.Sorting != 0 { fieldToUpdate = append(fieldToUpdate, "sorting") } if board.Title != "" { fieldToUpdate = append(fieldToUpdate, "title") } if len(board.Color) != 0 && !BoardColorPattern.MatchString(board.Color) { return fmt.Errorf("bad color code: %s", board.Color) } fieldToUpdate = append(fieldToUpdate, "color") _, err := e.ID(board.ID).Cols(fieldToUpdate...).Update(board) return err } // GetBoards fetches all boards related to a project // if no default board set, first board is a temporary "Uncategorized" board func GetBoards(projectID int64) (BoardList, error) { return getBoards(db.GetEngine(db.DefaultContext), projectID) } func getBoards(e db.Engine, projectID int64) ([]*Board, error) { boards := make([]*Board, 0, 5) if err := e.Where("project_id=? AND `default`=?", projectID, false).OrderBy("Sorting").Find(&boards); err != nil { return nil, err } defaultB, err := getDefaultBoard(e, projectID) if err != nil { return nil, err } return append([]*Board{defaultB}, boards...), nil } // getDefaultBoard return default board and create a dummy if none exist func getDefaultBoard(e db.Engine, projectID int64) (*Board, error) { var board Board 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 return &Board{ ProjectID: projectID, Title: "Uncategorized", Default: true, }, nil } // SetDefaultBoard represents a board for issues not assigned to one // if boardID is 0 unset default func SetDefaultBoard(projectID, boardID int64) error { _, err := db.GetEngine(db.DefaultContext).Where(builder.Eq{ "project_id": projectID, "`default`": true, }).Cols("`default`").Update(&Board{Default: false}) if err != nil { return err } if boardID > 0 { _, err = db.GetEngine(db.DefaultContext).ID(boardID).Where(builder.Eq{"project_id": projectID}). Cols("`default`").Update(&Board{Default: true}) } return err } // UpdateBoardSorting update project board sorting func UpdateBoardSorting(bs BoardList) error { for i := range bs { _, err := db.GetEngine(db.DefaultContext).ID(bs[i].ID).Cols( "sorting", ).Update(bs[i]) if err != nil { return err } } return nil }
-
-
models/project/issue.go (new)
-
@@ -0,0 +1,100 @@// 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 project import ( "context" "fmt" "code.gitea.io/gitea/models/db" ) // 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"` // If 0, then it has not been added to a specific board in the project ProjectBoardID int64 `xorm:"INDEX"` } func init() { db.RegisterModel(new(ProjectIssue)) } func deleteProjectIssuesByProjectID(e db.Engine, projectID int64) error { _, err := e.Where("project_id=?", projectID).Delete(&ProjectIssue{}) return err } // NumIssues return counter of all issues assigned to a project func (p *Project) NumIssues() int { c, err := db.GetEngine(db.DefaultContext).Table("project_issue"). Where("project_id=?", p.ID). GroupBy("issue_id"). Cols("issue_id"). Count() if err != nil { return 0 } return int(c) } // NumClosedIssues return counter of closed issues assigned to a project func (p *Project) NumClosedIssues() int { c, err := db.GetEngine(db.DefaultContext).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 { return 0 } return int(c) } // NumOpenIssues return counter of open issues assigned to a project func (p *Project) NumOpenIssues() int { c, err := db.GetEngine(db.DefaultContext).Table("project_issue"). Join("INNER", "issue", "project_issue.issue_id=issue.id"). Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, false).Count("issue.id") if err != nil { return 0 } return int(c) } // MoveIssuesOnProjectBoard moves or keeps issues in a column and sorts them inside that column func MoveIssuesOnProjectBoard(board *Board, sortedIssueIDs map[int64]int64) error { return db.WithTx(func(ctx context.Context) error { sess := db.GetEngine(ctx) issueIDs := make([]int64, 0, len(sortedIssueIDs)) for _, issueID := range sortedIssueIDs { issueIDs = append(issueIDs, issueID) } count, err := sess.Table(new(ProjectIssue)).Where("project_id=?", board.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=?", board.ID, sorting, issueID) if err != nil { return err } } return nil }) } func (pb *Board) removeIssues(e db.Engine) error { _, err := e.Exec("UPDATE `project_issue` SET project_board_id = 0 WHERE project_board_id = ? ", pb.ID) return err }
-
-
-
@@ -0,0 +1,23 @@// 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 project import ( "path/filepath" "testing" "code.gitea.io/gitea/models/unittest" _ "code.gitea.io/gitea/models/repo" ) func TestMain(m *testing.M) { unittest.MainTest(m, filepath.Join("..", ".."), "project.yml", "project_board.yml", "project_issue.yml", "repository.yml", ) }
-
-
-
@@ -2,9 +2,10 @@// Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package models package project import ( "context" "errors" "fmt"
-
@@ -19,25 +20,56 @@ import (type ( // ProjectsConfig is used to identify the type of board that is being created ProjectsConfig struct { BoardType ProjectBoardType BoardType BoardType Translation string } // ProjectType is used to identify the type of project in question and ownership ProjectType uint8 // Type is used to identify the type of project in question and ownership Type uint8 ) const ( // ProjectTypeIndividual is a type of project board that is owned by an individual ProjectTypeIndividual ProjectType = iota + 1 // TypeIndividual is a type of project board that is owned by an individual TypeIndividual Type = iota + 1 // ProjectTypeRepository is a project that is tied to a repository ProjectTypeRepository // TypeRepository is a project that is tied to a repository TypeRepository // ProjectTypeOrganization is a project that is tied to an organisation ProjectTypeOrganization // TypeOrganization is a project that is tied to an organisation TypeOrganization ) // ErrProjectNotExist represents a "ProjectNotExist" kind of error. type ErrProjectNotExist struct { ID int64 RepoID int64 } // IsErrProjectNotExist checks if an error is a ErrProjectNotExist func IsErrProjectNotExist(err error) bool { _, ok := err.(ErrProjectNotExist) return ok } func (err ErrProjectNotExist) Error() string { return fmt.Sprintf("projects does not exist [id: %d]", err.ID) } // ErrProjectBoardNotExist represents a "ProjectBoardNotExist" kind of error. type ErrProjectBoardNotExist struct { BoardID int64 } // IsErrProjectBoardNotExist checks if an error is a ErrProjectBoardNotExist func IsErrProjectBoardNotExist(err error) bool { _, ok := err.(ErrProjectBoardNotExist) return ok } func (err ErrProjectBoardNotExist) Error() string { return fmt.Sprintf("project board does not exist [id: %d]", err.BoardID) } // Project represents a project board type Project struct { ID int64 `xorm:"pk autoincr"`
-
@@ -46,8 +78,8 @@ type Project struct {RepoID int64 `xorm:"INDEX"` CreatorID int64 `xorm:"NOT NULL"` IsClosed bool `xorm:"INDEX"` BoardType ProjectBoardType Type ProjectType BoardType BoardType Type Type RenderedContent string `xorm:"-"`
-
@@ -63,37 +95,39 @@ func init() {// GetProjectsConfig retrieves the types of configurations projects could have func GetProjectsConfig() []ProjectsConfig { return []ProjectsConfig{ {ProjectBoardTypeNone, "repo.projects.type.none"}, {ProjectBoardTypeBasicKanban, "repo.projects.type.basic_kanban"}, {ProjectBoardTypeBugTriage, "repo.projects.type.bug_triage"}, {BoardTypeNone, "repo.projects.type.none"}, {BoardTypeBasicKanban, "repo.projects.type.basic_kanban"}, {BoardTypeBugTriage, "repo.projects.type.bug_triage"}, } } // IsProjectTypeValid checks if a project type is valid func IsProjectTypeValid(p ProjectType) bool { // IsTypeValid checks if a project type is valid func IsTypeValid(p Type) bool { switch p { case ProjectTypeRepository: case TypeRepository: return true default: return false } } // ProjectSearchOptions are options for GetProjects type ProjectSearchOptions struct { // SearchOptions are options for GetProjects type SearchOptions struct { RepoID int64 Page int IsClosed util.OptionalBool SortType string Type ProjectType Type Type } // GetProjects returns a list of all projects that have been created in the repository func GetProjects(opts ProjectSearchOptions) ([]*Project, int64, error) { return getProjects(db.GetEngine(db.DefaultContext), opts) func GetProjects(opts SearchOptions) ([]*Project, int64, error) { return GetProjectsCtx(db.DefaultContext, opts) } func getProjects(e db.Engine, opts ProjectSearchOptions) ([]*Project, int64, error) { // GetProjectsCtx returns a list of all projects that have been created in the repository func GetProjectsCtx(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) { e := db.GetEngine(ctx) projects := make([]*Project, 0, setting.UI.IssuePagingNum) var cond builder.Cond = builder.Eq{"repo_id": opts.RepoID}
-
@@ -135,11 +169,11 @@ func getProjects(e db.Engine, opts ProjectSearchOptions) ([]*Project, int64, err// NewProject creates a new Project func NewProject(p *Project) error { if !IsProjectBoardTypeValid(p.BoardType) { p.BoardType = ProjectBoardTypeNone if !IsBoardTypeValid(p.BoardType) { p.BoardType = BoardTypeNone } if !IsProjectTypeValid(p.Type) { if !IsTypeValid(p.Type) { return errors.New("project type is not valid") }
-
@@ -157,7 +191,7 @@ func NewProject(p *Project) error {return err } if err := createBoardsForProjectsType(db.GetEngine(ctx), p); err != nil { if err := createBoardsForProjectsType(ctx, p); err != nil { return err }
-
@@ -200,7 +234,7 @@ func updateRepositoryProjectCount(e db.Engine, repoID int64) error {builder.Eq{ "`num_projects`": builder.Select("count(*)").From("`project`"). Where(builder.Eq{"`project`.`repo_id`": repoID}. And(builder.Eq{"`project`.`type`": ProjectTypeRepository})), And(builder.Eq{"`project`.`type`": TypeRepository})), }).From("`repository`").Where(builder.Eq{"id": repoID})); err != nil { return err }
-
@@ -209,7 +243,7 @@ func updateRepositoryProjectCount(e db.Engine, repoID int64) error {builder.Eq{ "`num_closed_projects`": builder.Select("count(*)").From("`project`"). Where(builder.Eq{"`project`.`repo_id`": repoID}. And(builder.Eq{"`project`.`type`": ProjectTypeRepository}). And(builder.Eq{"`project`.`type`": TypeRepository}). And(builder.Eq{"`project`.`is_closed`": true})), }).From("`repository`").Where(builder.Eq{"id": repoID})); err != nil { return err
-
@@ -224,18 +258,17 @@ func ChangeProjectStatusByRepoIDAndID(repoID, projectID int64, isClosed bool) erreturn err } defer committer.Close() sess := db.GetEngine(ctx) p := new(Project) has, err := sess.ID(projectID).Where("repo_id = ?", repoID).Get(p) has, err := db.GetEngine(ctx).ID(projectID).Where("repo_id = ?", repoID).Get(p) if err != nil { return err } else if !has { return ErrProjectNotExist{ID: projectID, RepoID: repoID} } if err := changeProjectStatus(sess, p, isClosed); err != nil { if err := changeProjectStatus(ctx, p, isClosed); err != nil { return err }
-
@@ -250,16 +283,17 @@ func ChangeProjectStatus(p *Project, isClosed bool) error {} defer committer.Close() if err := changeProjectStatus(db.GetEngine(ctx), p, isClosed); err != nil { if err := changeProjectStatus(ctx, p, isClosed); err != nil { return err } return committer.Commit() } func changeProjectStatus(e db.Engine, p *Project, isClosed bool) error { func changeProjectStatus(ctx context.Context, p *Project, isClosed bool) error { p.IsClosed = isClosed p.ClosedDateUnix = timeutil.TimeStampNow() e := db.GetEngine(ctx) count, err := e.ID(p.ID).Where("repo_id = ? AND is_closed = ?", p.RepoID, !isClosed).Cols("is_closed", "closed_date_unix").Update(p) if err != nil { return err
-
@@ -279,14 +313,16 @@ func DeleteProjectByID(id int64) error {} defer committer.Close() if err := deleteProjectByID(db.GetEngine(ctx), id); err != nil { if err := DeleteProjectByIDCtx(ctx, id); err != nil { return err } return committer.Commit() } func deleteProjectByID(e db.Engine, id int64) error { // DeleteProjectByIDCtx deletes a project from a repository. func DeleteProjectByIDCtx(ctx context.Context, id int64) error { e := db.GetEngine(ctx) p, err := getProjectByID(e, id) if err != nil { if IsErrProjectNotExist(err) {
-
@@ -299,7 +335,7 @@ func deleteProjectByID(e db.Engine, id int64) error {return err } if err := deleteProjectBoardByProjectID(e, id); err != nil { if err := deleteBoardByProjectID(e, id); err != nil { return err }
-
-
-
@@ -2,7 +2,7 @@// Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package models package project import ( "testing"
-
@@ -14,33 +14,33 @@ import () func TestIsProjectTypeValid(t *testing.T) { const UnknownType ProjectType = 15 const UnknownType Type = 15 cases := []struct { typ ProjectType typ Type valid bool }{ {ProjectTypeIndividual, false}, {ProjectTypeRepository, true}, {ProjectTypeOrganization, false}, {TypeIndividual, false}, {TypeRepository, true}, {TypeOrganization, false}, {UnknownType, false}, } for _, v := range cases { assert.Equal(t, v.valid, IsProjectTypeValid(v.typ)) assert.Equal(t, v.valid, IsTypeValid(v.typ)) } } func TestGetProjects(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) projects, _, err := GetProjects(ProjectSearchOptions{RepoID: 1}) projects, _, err := GetProjects(SearchOptions{RepoID: 1}) assert.NoError(t, err) // 1 value for this repo exists in the fixtures assert.Len(t, projects, 1) projects, _, err = GetProjects(ProjectSearchOptions{RepoID: 3}) projects, _, err = GetProjects(SearchOptions{RepoID: 3}) assert.NoError(t, err) // 1 value for this repo exists in the fixtures
-
@@ -51,8 +51,8 @@ func TestProject(t *testing.T) {assert.NoError(t, unittest.PrepareTestDatabase()) project := &Project{ Type: ProjectTypeRepository, BoardType: ProjectBoardTypeBasicKanban, Type: TypeRepository, BoardType: BoardTypeBasicKanban, Title: "New Project", RepoID: 1, CreatedUnix: timeutil.TimeStampNow(),
-
-
models/project_board.go (deleted)
-
@@ -1,321 +0,0 @@// 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 ( "fmt" "regexp" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "xorm.io/builder" ) 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 ) // BoardColorPattern is a regexp witch can validate BoardColor var BoardColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$") // 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 Sorting int8 `xorm:"NOT NULL DEFAULT 0"` Color string `xorm:"VARCHAR(7)"` 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:"-"` } func init() { db.RegisterModel(new(ProjectBoard)) } // 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 db.Engine, 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 } 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 { if len(board.Color) != 0 && !BoardColorPattern.MatchString(board.Color) { return fmt.Errorf("bad color code: %s", board.Color) } _, err := db.GetEngine(db.DefaultContext).Insert(board) return err } // DeleteProjectBoardByID removes all issues references to the project board. func DeleteProjectBoardByID(boardID int64) error { ctx, committer, err := db.TxContext() if err != nil { return err } defer committer.Close() if err := deleteProjectBoardByID(db.GetEngine(ctx), boardID); err != nil { return err } return committer.Commit() } func deleteProjectBoardByID(e db.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 db.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(db.GetEngine(db.DefaultContext), boardID) } func getProjectBoard(e db.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 a project board func UpdateProjectBoard(board *ProjectBoard) error { return updateProjectBoard(db.GetEngine(db.DefaultContext), board) } func updateProjectBoard(e db.Engine, board *ProjectBoard) error { var fieldToUpdate []string if board.Sorting != 0 { fieldToUpdate = append(fieldToUpdate, "sorting") } if board.Title != "" { fieldToUpdate = append(fieldToUpdate, "title") } if len(board.Color) != 0 && !BoardColorPattern.MatchString(board.Color) { return fmt.Errorf("bad color code: %s", board.Color) } fieldToUpdate = append(fieldToUpdate, "color") _, err := e.ID(board.ID).Cols(fieldToUpdate...).Update(board) return err } // GetProjectBoards fetches all boards related to a project // if no default board set, first board is a temporary "Uncategorized" board func GetProjectBoards(projectID int64) (ProjectBoardList, error) { return getProjectBoards(db.GetEngine(db.DefaultContext), projectID) } func getProjectBoards(e db.Engine, projectID int64) ([]*ProjectBoard, error) { boards := make([]*ProjectBoard, 0, 5) if err := e.Where("project_id=? AND `default`=?", projectID, false).OrderBy("Sorting").Find(&boards); err != nil { return nil, err } defaultB, err := getDefaultBoard(e, projectID) if err != nil { return nil, err } return append([]*ProjectBoard{defaultB}, boards...), nil } // getDefaultBoard return default board and create a dummy if none exist func getDefaultBoard(e db.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 return &ProjectBoard{ ProjectID: projectID, Title: "Uncategorized", Default: true, }, nil } // SetDefaultBoard represents a board for issues not assigned to one // if boardID is 0 unset default func SetDefaultBoard(projectID, boardID int64) error { _, err := db.GetEngine(db.DefaultContext).Where(builder.Eq{ "project_id": projectID, "`default`": true, }).Cols("`default`").Update(&ProjectBoard{Default: false}) if err != nil { return err } if boardID > 0 { _, err = db.GetEngine(db.DefaultContext).ID(boardID).Where(builder.Eq{"project_id": projectID}). Cols("`default`").Update(&ProjectBoard{Default: true}) } return err } // LoadIssues load issues assigned to this board func (b *ProjectBoard) LoadIssues() (IssueList, error) { issueList := make([]*Issue, 0, 10) if b.ID != 0 { issues, err := Issues(&IssuesOptions{ ProjectBoardID: b.ID, ProjectID: b.ProjectID, SortType: "project-column-sorting", }) if err != nil { return nil, err } issueList = issues } if b.Default { issues, err := Issues(&IssuesOptions{ ProjectBoardID: -1, // Issues without ProjectBoardID ProjectID: b.ProjectID, SortType: "project-column-sorting", }) if err != nil { return nil, err } issueList = append(issueList, issues...) } if err := IssueList(issueList).LoadComments(); err != nil { return nil, err } b.Issues = issueList return issueList, nil } // 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 } // UpdateProjectBoardSorting update project board sorting func UpdateProjectBoardSorting(bs ProjectBoardList) error { for i := range bs { _, err := db.GetEngine(db.DefaultContext).ID(bs[i].ID).Cols( "sorting", ).Update(bs[i]) if err != nil { return err } } return nil }
-
-
models/project_issue.go (deleted)
-
@@ -1,218 +0,0 @@// 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 ( "context" "fmt" "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" ) // ProjectIssue saves relation from issue to a project type ProjectIssue struct { ID int64 `xorm:"pk autoincr"` IssueID int64 `xorm:"INDEX"` ProjectID int64 `xorm:"INDEX"` // If 0, then it has not been added to a specific board in the project ProjectBoardID int64 `xorm:"INDEX"` Sorting int64 `xorm:"NOT NULL DEFAULT 0"` } func init() { db.RegisterModel(new(ProjectIssue)) } func deleteProjectIssuesByProjectID(e db.Engine, projectID int64) error { _, err := e.Where("project_id=?", projectID).Delete(&ProjectIssue{}) return err } // ___ // |_ _|___ ___ _ _ ___ // | |/ __/ __| | | |/ _ \ // | |\__ \__ \ |_| | __/ // |___|___/___/\__,_|\___| // LoadProject load the project the issue was assigned to func (i *Issue) LoadProject() (err error) { return i.loadProject(db.GetEngine(db.DefaultContext)) } func (i *Issue) loadProject(e db.Engine) (err error) { if i.Project == nil { var p Project if _, err = e.Table("project"). Join("INNER", "project_issue", "project.id=project_issue.project_id"). Where("project_issue.issue_id = ?", i.ID). Get(&p); err != nil { return err } i.Project = &p } return } // ProjectID return project id if issue was assigned to one func (i *Issue) ProjectID() int64 { return i.projectID(db.GetEngine(db.DefaultContext)) } func (i *Issue) projectID(e db.Engine) int64 { var ip ProjectIssue has, err := e.Where("issue_id=?", i.ID).Get(&ip) if err != nil || !has { return 0 } return ip.ProjectID } // ProjectBoardID return project board id if issue was assigned to one func (i *Issue) ProjectBoardID() int64 { return i.projectBoardID(db.GetEngine(db.DefaultContext)) } func (i *Issue) projectBoardID(e db.Engine) int64 { var ip ProjectIssue has, err := e.Where("issue_id=?", i.ID).Get(&ip) if err != nil || !has { return 0 } return ip.ProjectBoardID } // ____ _ _ // | _ \ _ __ ___ (_) ___ ___| |_ // | |_) | '__/ _ \| |/ _ \/ __| __| // | __/| | | (_) | | __/ (__| |_ // |_| |_| \___// |\___|\___|\__| // |__/ // NumIssues return counter of all issues assigned to a project func (p *Project) NumIssues() int { c, err := db.GetEngine(db.DefaultContext).Table("project_issue"). Where("project_id=?", p.ID). GroupBy("issue_id"). Cols("issue_id"). Count() if err != nil { return 0 } return int(c) } // NumClosedIssues return counter of closed issues assigned to a project func (p *Project) NumClosedIssues() int { c, err := db.GetEngine(db.DefaultContext).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 { return 0 } return int(c) } // NumOpenIssues return counter of open issues assigned to a project func (p *Project) NumOpenIssues() int { c, err := db.GetEngine(db.DefaultContext).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 { return 0 } return int(c) } // ChangeProjectAssign changes the project associated with an issue func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64) error { ctx, committer, err := db.TxContext() if err != nil { return err } defer committer.Close() if err := addUpdateIssueProject(ctx, issue, doer, newProjectID); err != nil { return err } return committer.Commit() } func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error { e := db.GetEngine(ctx) oldProjectID := issue.projectID(e) if _, err := e.Where("project_issue.issue_id=?", issue.ID).Delete(&ProjectIssue{}); err != nil { return err } if err := issue.loadRepo(ctx); err != nil { return err } if oldProjectID > 0 || newProjectID > 0 { if _, err := createComment(ctx, &CreateCommentOptions{ Type: CommentTypeProject, Doer: doer, Repo: issue.Repo, Issue: issue, OldProjectID: oldProjectID, ProjectID: newProjectID, }); err != nil { return err } } _, err := e.Insert(&ProjectIssue{ IssueID: issue.ID, ProjectID: newProjectID, }) return err } // ____ _ _ ____ _ // | _ \ _ __ ___ (_) ___ ___| |_| __ ) ___ __ _ _ __ __| | // | |_) | '__/ _ \| |/ _ \/ __| __| _ \ / _ \ / _` | '__/ _` | // | __/| | | (_) | | __/ (__| |_| |_) | (_) | (_| | | | (_| | // |_| |_| \___// |\___|\___|\__|____/ \___/ \__,_|_| \__,_| // |__/ // MoveIssuesOnProjectBoard moves or keeps issues in a column and sorts them inside that column func MoveIssuesOnProjectBoard(board *ProjectBoard, sortedIssueIDs map[int64]int64) error { return db.WithTx(func(ctx context.Context) error { sess := db.GetEngine(ctx) issueIDs := make([]int64, 0, len(sortedIssueIDs)) for _, issueID := range sortedIssueIDs { issueIDs = append(issueIDs, issueID) } count, err := sess.Table(new(ProjectIssue)).Where("project_id=?", board.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=?", board.ID, sorting, issueID) if err != nil { return err } } return nil }) } func (pb *ProjectBoard) removeIssues(e db.Engine) error { _, err := e.Exec("UPDATE `project_issue` SET project_board_id = 0, sorting = 0 WHERE project_board_id = ? ", pb.ID) return err }
-
-
-
@@ -21,6 +21,7 @@ import ("code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user"
-
@@ -748,14 +749,14 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {} } projects, _, err := getProjects(sess, ProjectSearchOptions{ projects, _, err := project_model.GetProjectsCtx(ctx, project_model.SearchOptions{ RepoID: repoID, }) if err != nil { return fmt.Errorf("get projects: %v", err) } for i := range projects { if err := deleteProjectByID(sess, projects[i].ID); err != nil { if err := project_model.DeleteProjectByIDCtx(ctx, projects[i].ID); err != nil { return fmt.Errorf("delete project [%d]: %v", projects[i].ID, err) } }
-
-
-
@@ -9,6 +9,7 @@ import ("code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook"
-
@@ -106,7 +107,7 @@ func GetStatistic() (stats Statistic) {stats.Counter.HookTask, _ = e.Count(new(webhook.HookTask)) stats.Counter.Team, _ = e.Count(new(organization.Team)) stats.Counter.Attachment, _ = e.Count(new(repo_model.Attachment)) stats.Counter.Project, _ = e.Count(new(Project)) stats.Counter.ProjectBoard, _ = e.Count(new(ProjectBoard)) stats.Counter.Project, _ = e.Count(new(project_model.Project)) stats.Counter.ProjectBoard, _ = e.Count(new(project_model.Board)) return }
-
-
-
@@ -20,6 +20,7 @@ import ("code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user"
-
@@ -336,9 +337,9 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti} if ctx.Repo.CanWriteIssuesOrPulls(ctx.Params(":type") == "pulls") { projects, _, err := models.GetProjects(models.ProjectSearchOptions{ projects, _, err := project_model.GetProjects(project_model.SearchOptions{ RepoID: repo.ID, Type: models.ProjectTypeRepository, Type: project_model.TypeRepository, IsClosed: util.OptionalBoolOf(isShowClosed), }) if err != nil {
-
@@ -446,22 +447,22 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.Rfunc retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { var err error ctx.Data["OpenProjects"], _, err = models.GetProjects(models.ProjectSearchOptions{ ctx.Data["OpenProjects"], _, err = project_model.GetProjects(project_model.SearchOptions{ RepoID: repo.ID, Page: -1, IsClosed: util.OptionalBoolFalse, Type: models.ProjectTypeRepository, Type: project_model.TypeRepository, }) if err != nil { ctx.ServerError("GetProjects", err) return } ctx.Data["ClosedProjects"], _, err = models.GetProjects(models.ProjectSearchOptions{ ctx.Data["ClosedProjects"], _, err = project_model.GetProjects(project_model.SearchOptions{ RepoID: repo.ID, Page: -1, IsClosed: util.OptionalBoolTrue, Type: models.ProjectTypeRepository, Type: project_model.TypeRepository, }) if err != nil { ctx.ServerError("GetProjects", err)
-
@@ -814,7 +815,7 @@ func NewIssue(ctx *context.Context) {projectID := ctx.FormInt64("project") if projectID > 0 { project, err := models.GetProjectByID(projectID) project, err := project_model.GetProjectByID(projectID) if err != nil { log.Error("GetProjectByID: %d: %v", projectID, err) } else if project.RepoID != ctx.Repo.Repository.ID {
-
@@ -926,7 +927,7 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull} if form.ProjectID > 0 { p, err := models.GetProjectByID(form.ProjectID) p, err := project_model.GetProjectByID(form.ProjectID) if err != nil { ctx.ServerError("GetProjectByID", err) return nil, nil, 0, 0
-
@@ -1413,7 +1414,7 @@ func ViewIssue(ctx *context.Context) {return } ghostProject := &models.Project{ ghostProject := &project_model.Project{ ID: -1, Title: ctx.Tr("repo.issues.deleted_project"), }
-
-
-
@@ -12,6 +12,7 @@ import ("code.gitea.io/gitea/models" "code.gitea.io/gitea/models/perm" project_model "code.gitea.io/gitea/models/project" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context"
-
@@ -69,12 +70,12 @@ func Projects(ctx *context.Context) {total = repo.NumClosedProjects } projects, count, err := models.GetProjects(models.ProjectSearchOptions{ projects, count, err := project_model.GetProjects(project_model.SearchOptions{ RepoID: repo.ID, Page: page, IsClosed: util.OptionalBoolOf(isShowClosed), SortType: sortType, Type: models.ProjectTypeRepository, Type: project_model.TypeRepository, }) if err != nil { ctx.ServerError("GetProjects", err)
-
@@ -122,7 +123,7 @@ func Projects(ctx *context.Context) {// NewProject render creating a project page func NewProject(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.new") ctx.Data["ProjectTypes"] = models.GetProjectsConfig() ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig() ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.HTML(http.StatusOK, tplProjectsNew) }
-
@@ -134,18 +135,18 @@ func NewProjectPost(ctx *context.Context) {if ctx.HasError() { ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.Data["ProjectTypes"] = models.GetProjectsConfig() ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig() ctx.HTML(http.StatusOK, tplProjectsNew) return } if err := models.NewProject(&models.Project{ if err := project_model.NewProject(&project_model.Project{ RepoID: ctx.Repo.Repository.ID, Title: form.Title, Description: form.Content, CreatorID: ctx.Doer.ID, BoardType: form.BoardType, Type: models.ProjectTypeRepository, Type: project_model.TypeRepository, }); err != nil { ctx.ServerError("NewProject", err) return
-
@@ -168,8 +169,8 @@ func ChangeProjectStatus(ctx *context.Context) {} id := ctx.ParamsInt64(":id") if err := models.ChangeProjectStatusByRepoIDAndID(ctx.Repo.Repository.ID, id, toClose); err != nil { if models.IsErrProjectNotExist(err) { if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx.Repo.Repository.ID, id, toClose); err != nil { if project_model.IsErrProjectNotExist(err) { ctx.NotFound("", err) } else { ctx.ServerError("ChangeProjectStatusByIDAndRepoID", err)
-
@@ -181,9 +182,9 @@ func ChangeProjectStatus(ctx *context.Context) {// DeleteProject delete a project func DeleteProject(ctx *context.Context) { p, err := models.GetProjectByID(ctx.ParamsInt64(":id")) p, err := project_model.GetProjectByID(ctx.ParamsInt64(":id")) if err != nil { if models.IsErrProjectNotExist(err) { if project_model.IsErrProjectNotExist(err) { ctx.NotFound("", nil) } else { ctx.ServerError("GetProjectByID", err)
-
@@ -195,7 +196,7 @@ func DeleteProject(ctx *context.Context) {return } if err := models.DeleteProjectByID(p.ID); err != nil { if err := project_model.DeleteProjectByID(p.ID); err != nil { ctx.Flash.Error("DeleteProjectByID: " + err.Error()) } else { ctx.Flash.Success(ctx.Tr("repo.projects.deletion_success"))
-
@@ -212,9 +213,9 @@ func EditProject(ctx *context.Context) {ctx.Data["PageIsEditProjects"] = true ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) p, err := models.GetProjectByID(ctx.ParamsInt64(":id")) p, err := project_model.GetProjectByID(ctx.ParamsInt64(":id")) if err != nil { if models.IsErrProjectNotExist(err) { if project_model.IsErrProjectNotExist(err) { ctx.NotFound("", nil) } else { ctx.ServerError("GetProjectByID", err)
-
@@ -244,9 +245,9 @@ func EditProjectPost(ctx *context.Context) {return } p, err := models.GetProjectByID(ctx.ParamsInt64(":id")) p, err := project_model.GetProjectByID(ctx.ParamsInt64(":id")) if err != nil { if models.IsErrProjectNotExist(err) { if project_model.IsErrProjectNotExist(err) { ctx.NotFound("", nil) } else { ctx.ServerError("GetProjectByID", err)
-
@@ -260,7 +261,7 @@ func EditProjectPost(ctx *context.Context) {p.Title = form.Title p.Description = form.Content if err = models.UpdateProject(p); err != nil { if err = project_model.UpdateProject(p); err != nil { ctx.ServerError("UpdateProjects", err) return }
-
@@ -271,9 +272,9 @@ func EditProjectPost(ctx *context.Context) {// ViewProject renders the project board for a project func ViewProject(ctx *context.Context) { project, err := models.GetProjectByID(ctx.ParamsInt64(":id")) project, err := project_model.GetProjectByID(ctx.ParamsInt64(":id")) if err != nil { if models.IsErrProjectNotExist(err) { if project_model.IsErrProjectNotExist(err) { ctx.NotFound("", nil) } else { ctx.ServerError("GetProjectByID", err)
-
@@ -285,7 +286,7 @@ func ViewProject(ctx *context.Context) {return } boards, err := models.GetProjectBoards(project.ID) boards, err := project_model.GetBoards(project.ID) if err != nil { ctx.ServerError("GetProjectBoards", err) return
-
@@ -295,27 +296,29 @@ func ViewProject(ctx *context.Context) {boards[0].Title = ctx.Tr("repo.projects.type.uncategorized") } issueList, err := boards.LoadIssues() issuesMap, err := models.LoadIssuesFromBoardList(boards) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return } linkedPrsMap := make(map[int64][]*models.Issue) for _, issue := range issueList { var referencedIds []int64 for _, comment := range issue.Comments { if comment.RefIssueID != 0 && comment.RefIsPull { referencedIds = append(referencedIds, comment.RefIssueID) for _, issuesList := range issuesMap { for _, issue := range issuesList { var referencedIds []int64 for _, comment := range issue.Comments { if comment.RefIssueID != 0 && comment.RefIsPull { referencedIds = append(referencedIds, comment.RefIssueID) } } } if len(referencedIds) > 0 { if linkedPrs, err := models.Issues(&models.IssuesOptions{ IssueIDs: referencedIds, IsPull: util.OptionalBoolTrue, }); err == nil { linkedPrsMap[issue.ID] = linkedPrs if len(referencedIds) > 0 { if linkedPrs, err := models.Issues(&models.IssuesOptions{ IssueIDs: referencedIds, IsPull: util.OptionalBoolTrue, }); err == nil { linkedPrsMap[issue.ID] = linkedPrs } } } }
-
@@ -335,6 +338,7 @@ func ViewProject(ctx *context.Context) {ctx.Data["IsProjectsPage"] = true ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.Data["Project"] = project ctx.Data["IssuesMap"] = issuesMap ctx.Data["Boards"] = boards ctx.HTML(http.StatusOK, tplProjectsView)
-
@@ -381,9 +385,9 @@ func DeleteProjectBoard(ctx *context.Context) {return } project, err := models.GetProjectByID(ctx.ParamsInt64(":id")) project, err := project_model.GetProjectByID(ctx.ParamsInt64(":id")) if err != nil { if models.IsErrProjectNotExist(err) { if project_model.IsErrProjectNotExist(err) { ctx.NotFound("", nil) } else { ctx.ServerError("GetProjectByID", err)
-
@@ -391,7 +395,7 @@ func DeleteProjectBoard(ctx *context.Context) {return } pb, err := models.GetProjectBoard(ctx.ParamsInt64(":boardID")) pb, err := project_model.GetBoard(ctx.ParamsInt64(":boardID")) if err != nil { ctx.ServerError("GetProjectBoard", err) return
-
@@ -410,7 +414,7 @@ func DeleteProjectBoard(ctx *context.Context) {return } if err := models.DeleteProjectBoardByID(ctx.ParamsInt64(":boardID")); err != nil { if err := project_model.DeleteBoardByID(ctx.ParamsInt64(":boardID")); err != nil { ctx.ServerError("DeleteProjectBoardByID", err) return }
-
@@ -430,9 +434,9 @@ func AddBoardToProjectPost(ctx *context.Context) {return } project, err := models.GetProjectByID(ctx.ParamsInt64(":id")) project, err := project_model.GetProjectByID(ctx.ParamsInt64(":id")) if err != nil { if models.IsErrProjectNotExist(err) { if project_model.IsErrProjectNotExist(err) { ctx.NotFound("", nil) } else { ctx.ServerError("GetProjectByID", err)
-
@@ -440,7 +444,7 @@ func AddBoardToProjectPost(ctx *context.Context) {return } if err := models.NewProjectBoard(&models.ProjectBoard{ if err := project_model.NewBoard(&project_model.Board{ ProjectID: project.ID, Title: form.Title, Color: form.Color,
-
@@ -455,7 +459,7 @@ func AddBoardToProjectPost(ctx *context.Context) {}) } func checkProjectBoardChangePermissions(ctx *context.Context) (*models.Project, *models.ProjectBoard) { func checkProjectBoardChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Board) { if ctx.Doer == nil { ctx.JSON(http.StatusForbidden, map[string]string{ "message": "Only signed in users are allowed to perform this action.",
-
@@ -470,9 +474,9 @@ func checkProjectBoardChangePermissions(ctx *context.Context) (*models.Project,return nil, nil } project, err := models.GetProjectByID(ctx.ParamsInt64(":id")) project, err := project_model.GetProjectByID(ctx.ParamsInt64(":id")) if err != nil { if models.IsErrProjectNotExist(err) { if project_model.IsErrProjectNotExist(err) { ctx.NotFound("", nil) } else { ctx.ServerError("GetProjectByID", err)
-
@@ -480,7 +484,7 @@ func checkProjectBoardChangePermissions(ctx *context.Context) (*models.Project,return nil, nil } board, err := models.GetProjectBoard(ctx.ParamsInt64(":boardID")) board, err := project_model.GetBoard(ctx.ParamsInt64(":boardID")) if err != nil { ctx.ServerError("GetProjectBoard", err) return nil, nil
-
@@ -519,7 +523,7 @@ func EditProjectBoard(ctx *context.Context) {board.Sorting = form.Sorting } if err := models.UpdateProjectBoard(board); err != nil { if err := project_model.UpdateBoard(board); err != nil { ctx.ServerError("UpdateProjectBoard", err) return }
-
@@ -536,7 +540,7 @@ func SetDefaultProjectBoard(ctx *context.Context) {return } if err := models.SetDefaultBoard(project.ID, board.ID); err != nil { if err := project_model.SetDefaultBoard(project.ID, board.ID); err != nil { ctx.ServerError("SetDefaultBoard", err) return }
-
@@ -562,9 +566,9 @@ func MoveIssues(ctx *context.Context) {return } project, err := models.GetProjectByID(ctx.ParamsInt64(":id")) project, err := project_model.GetProjectByID(ctx.ParamsInt64(":id")) if err != nil { if models.IsErrProjectNotExist(err) { if project_model.IsErrProjectNotExist(err) { ctx.NotFound("ProjectNotExist", nil) } else { ctx.ServerError("GetProjectByID", err)
-
@@ -576,19 +580,18 @@ func MoveIssues(ctx *context.Context) {return } var board *models.ProjectBoard var board *project_model.Board if ctx.ParamsInt64(":boardID") == 0 { board = &models.ProjectBoard{ board = &project_model.Board{ ID: 0, ProjectID: project.ID, Title: ctx.Tr("repo.projects.type.uncategorized"), } } else { // column board, err = models.GetProjectBoard(ctx.ParamsInt64(":boardID")) board, err = project_model.GetBoard(ctx.ParamsInt64(":boardID")) if err != nil { if models.IsErrProjectBoardNotExist(err) { if project_model.IsErrProjectBoardNotExist(err) { ctx.NotFound("ProjectBoardNotExist", nil) } else { ctx.ServerError("GetProjectBoard", err)
-
@@ -634,7 +637,7 @@ func MoveIssues(ctx *context.Context) {return } if err = models.MoveIssuesOnProjectBoard(board, sortedIssueIDs); err != nil { if err = project_model.MoveIssuesOnProjectBoard(board, sortedIssueIDs); err != nil { ctx.ServerError("MoveIssuesOnProjectBoard", err) return }
-
@@ -647,8 +650,43 @@ func MoveIssues(ctx *context.Context) {// CreateProject renders the generic project creation page func CreateProject(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.new") ctx.Data["ProjectTypes"] = models.GetProjectsConfig() ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig() ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.HTML(http.StatusOK, tplGenericProjectsNew) } // CreateProjectPost creates an individual and/or organization project func CreateProjectPost(ctx *context.Context, form forms.UserCreateProjectForm) { user := checkContextUser(ctx, form.UID) if ctx.Written() { return } ctx.Data["ContextUser"] = user if ctx.HasError() { ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.HTML(http.StatusOK, tplGenericProjectsNew) return } projectType := project_model.TypeIndividual if user.IsOrganization() { projectType = project_model.TypeOrganization } if err := project_model.NewProject(&project_model.Project{ Title: form.Title, Description: form.Content, CreatorID: user.ID, BoardType: form.BoardType, Type: projectType, }); err != nil { ctx.ServerError("NewProject", err) return } ctx.Flash.Success(ctx.Tr("repo.projects.create_success", form.Title)) ctx.Redirect(setting.AppSubURL + "/") }
-
-
-
@@ -13,6 +13,7 @@ import ("code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context"
-
@@ -216,10 +217,10 @@ func Profile(ctx *context.Context) {total = int(count) case "projects": ctx.Data["OpenProjects"], _, err = models.GetProjects(models.ProjectSearchOptions{ ctx.Data["OpenProjects"], _, err = project_model.GetProjects(project_model.SearchOptions{ Page: -1, IsClosed: util.OptionalBoolFalse, Type: models.ProjectTypeIndividual, Type: project_model.TypeIndividual, }) if err != nil { ctx.ServerError("GetProjects", err)
-
-
-
@@ -11,6 +11,7 @@ import ("strings" "code.gitea.io/gitea/models" project_model "code.gitea.io/gitea/models/project" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs"
-
@@ -499,7 +500,7 @@ func (i IssueLockForm) HasValidReason() bool {type CreateProjectForm struct { Title string `binding:"Required;MaxSize(100)"` Content string BoardType models.ProjectBoardType BoardType project_model.BoardType } // UserCreateProjectForm is a from for creating an individual or organization
-
@@ -507,7 +508,7 @@ type CreateProjectForm struct {type UserCreateProjectForm struct { Title string `binding:"Required;MaxSize(100)"` Content string BoardType models.ProjectBoardType BoardType project_model.BoardType UID int64 `binding:"Required"` }
-
-
-
@@ -84,7 +84,7 @@<div class="board-column-header df ac sb"> <div class="ui large label board-label py-2"> <div class="ui small circular grey label board-card-cnt"> {{len .Issues}} {{.NumIssues}} </div> {{.Title}} </div>
-
@@ -175,7 +175,7 @@<div class="ui cards board" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}" data-project="{{$.Project.ID}}" data-board="{{.ID}}" id="board_{{.ID}}"> {{ range .Issues }} {{ range (index $.IssuesMap .ID) }} <!-- start issue card --> <div class="card board-card" data-issue="{{.ID}}">
-