From 43729085acd711558f6f6bb0f3a86972686bedfe Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 29 Mar 2024 01:24:50 -0600 Subject: [PATCH 01/15] Rename project board -> column to make the UI less confusion --- models/activities/statistic.go | 4 +- models/issues/issue_project.go | 28 +- models/issues/issue_search.go | 14 +- models/migrations/v1_22/v293_test.go | 24 +- models/project/board.go | 320 ------------------ models/project/board_test.go | 44 --- models/project/column.go | 287 ++++++++++++++++ models/project/column_test.go | 44 +++ models/project/issue.go | 12 +- models/project/project.go | 89 ++--- models/project/project_test.go | 14 +- models/project/view_board.go | 45 +++ modules/indexer/issues/bleve/bleve.go | 4 +- modules/indexer/issues/db/options.go | 2 +- modules/indexer/issues/dboptions.go | 2 +- .../issues/elasticsearch/elasticsearch.go | 4 +- modules/indexer/issues/indexer_test.go | 4 +- modules/indexer/issues/internal/model.go | 4 +- .../indexer/issues/internal/tests/tests.go | 4 +- .../indexer/issues/meilisearch/meilisearch.go | 4 +- modules/indexer/issues/util.go | 2 +- modules/metrics/collector.go | 2 +- routers/web/org/projects.go | 54 +-- routers/web/repo/issue.go | 12 +- routers/web/repo/projects.go | 58 ++-- routers/web/web.go | 12 +- services/forms/repo_form.go | 36 +- 27 files changed, 545 insertions(+), 584 deletions(-) delete mode 100644 models/project/board.go delete mode 100644 models/project/board_test.go create mode 100644 models/project/column.go create mode 100644 models/project/column_test.go create mode 100644 models/project/view_board.go diff --git a/models/activities/statistic.go b/models/activities/statistic.go index d1a459d1b2c8a..ff81ad78a1db4 100644 --- a/models/activities/statistic.go +++ b/models/activities/statistic.go @@ -30,7 +30,7 @@ type Statistic struct { Mirror, Release, AuthSource, Webhook, Milestone, Label, HookTask, Team, UpdateTask, Project, - ProjectBoard, Attachment, + ProjectColumn, Attachment, Branches, Tags, CommitStatus int64 IssueByLabel []IssueByLabelCount IssueByRepository []IssueByRepositoryCount @@ -115,6 +115,6 @@ func GetStatistic(ctx context.Context) (stats Statistic) { stats.Counter.Team, _ = e.Count(new(organization.Team)) stats.Counter.Attachment, _ = e.Count(new(repo_model.Attachment)) stats.Counter.Project, _ = e.Count(new(project_model.Project)) - stats.Counter.ProjectBoard, _ = e.Count(new(project_model.Board)) + stats.Counter.ProjectColumn, _ = e.Count(new(project_model.Column)) return stats } diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 907a5a17b9f20..330175afe373e 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -37,22 +37,22 @@ func (issue *Issue) projectID(ctx context.Context) int64 { return ip.ProjectID } -// ProjectBoardID return project board id if issue was assigned to one -func (issue *Issue) ProjectBoardID(ctx context.Context) int64 { +// ProjectColumnID return project column id if issue was assigned to one +func (issue *Issue) ProjectColumnID(ctx context.Context) int64 { var ip project_model.ProjectIssue has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip) if err != nil || !has { return 0 } - return ip.ProjectBoardID + return ip.ProjectColumnID } -// LoadIssuesFromBoard load issues assigned to this board -func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList, error) { +// LoadIssuesFromColumn load issues assigned to this board +func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column) (IssueList, error) { issueList, err := Issues(ctx, &IssuesOptions{ - ProjectBoardID: b.ID, - ProjectID: b.ProjectID, - SortType: "project-column-sorting", + ProjectColumnID: b.ID, + ProjectID: b.ProjectID, + SortType: "project-column-sorting", }) if err != nil { return nil, err @@ -60,9 +60,9 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList if b.Default { issues, err := Issues(ctx, &IssuesOptions{ - ProjectBoardID: db.NoConditionID, - ProjectID: b.ProjectID, - SortType: "project-column-sorting", + ProjectColumnID: db.NoConditionID, + ProjectID: b.ProjectID, + SortType: "project-column-sorting", }) if err != nil { return nil, err @@ -77,11 +77,11 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList return issueList, nil } -// LoadIssuesFromBoardList load issues assigned to the boards -func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList) (map[int64]IssueList, error) { +// LoadIssuesFromColumnList load issues assigned to the boards +func LoadIssuesFromColumnList(ctx context.Context, bs project_model.ColumnList) (map[int64]IssueList, error) { issuesMap := make(map[int64]IssueList, len(bs)) for i := range bs { - il, err := LoadIssuesFromBoard(ctx, bs[i]) + il, err := LoadIssuesFromColumn(ctx, bs[i]) if err != nil { return nil, err } diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 921dd9973ec9f..491def1229153 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -33,7 +33,7 @@ type IssuesOptions struct { //nolint SubscriberID int64 MilestoneIDs []int64 ProjectID int64 - ProjectBoardID int64 + ProjectColumnID int64 IsClosed optional.Option[bool] IsPull optional.Option[bool] LabelIDs []int64 @@ -169,12 +169,12 @@ func applyProjectCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Sessio return sess } -func applyProjectBoardCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { - // opts.ProjectBoardID == 0 means all project boards, +func applyProjectColumnCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { + // opts.ProjectColumnID == 0 means all project columns, // do not need to apply any condition - if opts.ProjectBoardID > 0 { - sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": opts.ProjectBoardID})) - } else if opts.ProjectBoardID == db.NoConditionID { + if opts.ProjectColumnID > 0 { + sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": opts.ProjectColumnID})) + } else if opts.ProjectColumnID == db.NoConditionID { sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": 0})) } return sess @@ -246,7 +246,7 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { applyProjectCondition(sess, opts) - applyProjectBoardCondition(sess, opts) + applyProjectColumnCondition(sess, opts) if opts.IsPull.Has() { sess.And("issue.is_pull=?", opts.IsPull.Value()) diff --git a/models/migrations/v1_22/v293_test.go b/models/migrations/v1_22/v293_test.go index ccc92f39a60d2..cfe4345143e0a 100644 --- a/models/migrations/v1_22/v293_test.go +++ b/models/migrations/v1_22/v293_test.go @@ -15,7 +15,7 @@ import ( func Test_CheckProjectColumnsConsistency(t *testing.T) { // Prepare and load the testing database - x, deferable := base.PrepareTestEnv(t, 0, new(project.Project), new(project.Board)) + x, deferable := base.PrepareTestEnv(t, 0, new(project.Project), new(project.Column)) defer deferable() if x == nil || t.Failed() { return @@ -23,22 +23,22 @@ func Test_CheckProjectColumnsConsistency(t *testing.T) { assert.NoError(t, CheckProjectColumnsConsistency(x)) - // check if default board was added - var defaultBoard project.Board - has, err := x.Where("project_id=? AND `default` = ?", 1, true).Get(&defaultBoard) + // check if default column was added + var defaultColumn project.Column + has, err := x.Where("project_id=? AND `default` = ?", 1, true).Get(&defaultColumn) assert.NoError(t, err) assert.True(t, has) - assert.Equal(t, int64(1), defaultBoard.ProjectID) - assert.True(t, defaultBoard.Default) + assert.Equal(t, int64(1), defaultColumn.ProjectID) + assert.True(t, defaultColumn.Default) // check if multiple defaults, previous were removed and last will be kept - expectDefaultBoard, err := project.GetBoard(db.DefaultContext, 2) + expectDefaultColumn, err := project.GetColumn(db.DefaultContext, 2) assert.NoError(t, err) - assert.Equal(t, int64(2), expectDefaultBoard.ProjectID) - assert.False(t, expectDefaultBoard.Default) + assert.Equal(t, int64(2), expectDefaultColumn.ProjectID) + assert.False(t, expectDefaultColumn.Default) - expectNonDefaultBoard, err := project.GetBoard(db.DefaultContext, 3) + expectNonDefaultColumn, err := project.GetColumn(db.DefaultContext, 3) assert.NoError(t, err) - assert.Equal(t, int64(2), expectNonDefaultBoard.ProjectID) - assert.True(t, expectNonDefaultBoard.Default) + assert.Equal(t, int64(2), expectNonDefaultColumn.ProjectID) + assert.True(t, expectNonDefaultColumn.Default) } diff --git a/models/project/board.go b/models/project/board.go deleted file mode 100644 index 5f142a356c68d..0000000000000 --- a/models/project/board.go +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -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 - - // CardType is used to represent a project board card type - CardType 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 -) - -const ( - // CardTypeTextOnly is a project board card type that is text only - CardTypeTextOnly CardType = iota - - // CardTypeImagesAndText is a project board card type that has images and text - CardTypeImagesAndText -) - -// 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(ctx context.Context) int { - c, err := db.GetEngine(ctx).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 - } -} - -// IsCardTypeValid checks if the project board card type is valid -func IsCardTypeValid(p CardType) bool { - switch p { - case CardTypeTextOnly, CardTypeImagesAndText: - 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 - } - - board := Board{ - CreatedUnix: timeutil.TimeStampNow(), - CreatorID: project.CreatorID, - Title: "Backlog", - ProjectID: project.ID, - Default: true, - } - if err := db.Insert(ctx, board); err != nil { - return err - } - - 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(ctx context.Context, board *Board) error { - if len(board.Color) != 0 && !BoardColorPattern.MatchString(board.Color) { - return fmt.Errorf("bad color code: %s", board.Color) - } - - _, err := db.GetEngine(ctx).Insert(board) - return err -} - -// DeleteBoardByID removes all issues references to the project board. -func DeleteBoardByID(ctx context.Context, boardID int64) error { - ctx, committer, err := db.TxContext(ctx) - 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 { - board, err := GetBoard(ctx, boardID) - if err != nil { - if IsErrProjectBoardNotExist(err) { - return nil - } - - return err - } - - if board.Default { - return fmt.Errorf("deleteBoardByID: cannot delete default board") - } - - if err = board.removeIssues(ctx); err != nil { - return err - } - - if _, err := db.GetEngine(ctx).ID(board.ID).NoAutoCondition().Delete(board); err != nil { - return err - } - return nil -} - -func deleteBoardByProjectID(ctx context.Context, projectID int64) error { - _, err := db.GetEngine(ctx).Where("project_id=?", projectID).Delete(&Board{}) - return err -} - -// GetBoard fetches the current board of a project -func GetBoard(ctx context.Context, boardID int64) (*Board, error) { - board := new(Board) - has, err := db.GetEngine(ctx).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(ctx context.Context, 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 := db.GetEngine(ctx).ID(board.ID).Cols(fieldToUpdate...).Update(board) - - return err -} - -// GetBoards fetches all boards related to a project -func (p *Project) GetBoards(ctx context.Context) (BoardList, error) { - boards := make([]*Board, 0, 5) - - if err := db.GetEngine(ctx).Where("project_id=? AND `default`=?", p.ID, false).OrderBy("sorting").Find(&boards); err != nil { - return nil, err - } - - defaultB, err := p.getDefaultBoard(ctx) - if err != nil { - return nil, err - } - - return append([]*Board{defaultB}, boards...), nil -} - -// getDefaultBoard return default board and ensure only one exists -func (p *Project) getDefaultBoard(ctx context.Context) (*Board, error) { - var board Board - has, err := db.GetEngine(ctx). - Where("project_id=? AND `default` = ?", p.ID, true). - Desc("id").Get(&board) - if err != nil { - return nil, err - } - - if has { - return &board, nil - } - - // create a default board if none is found - board = Board{ - ProjectID: p.ID, - Default: true, - Title: "Uncategorized", - CreatorID: p.CreatorID, - } - if _, err := db.GetEngine(ctx).Insert(&board); err != nil { - return nil, err - } - return &board, nil -} - -// SetDefaultBoard represents a board for issues not assigned to one -func SetDefaultBoard(ctx context.Context, projectID, boardID int64) error { - return db.WithTx(ctx, func(ctx context.Context) error { - if _, err := GetBoard(ctx, boardID); err != nil { - return err - } - - if _, err := db.GetEngine(ctx).Where(builder.Eq{ - "project_id": projectID, - "`default`": true, - }).Cols("`default`").Update(&Board{Default: false}); err != nil { - return err - } - - _, err := db.GetEngine(ctx).ID(boardID). - Where(builder.Eq{"project_id": projectID}). - Cols("`default`").Update(&Board{Default: true}) - return err - }) -} - -// UpdateBoardSorting update project board sorting -func UpdateBoardSorting(ctx context.Context, bs BoardList) error { - return db.WithTx(ctx, func(ctx context.Context) error { - for i := range bs { - if _, err := db.GetEngine(ctx).ID(bs[i].ID).Cols( - "sorting", - ).Update(bs[i]); err != nil { - return err - } - } - return nil - }) -} diff --git a/models/project/board_test.go b/models/project/board_test.go deleted file mode 100644 index 71ba29a5896dc..0000000000000 --- a/models/project/board_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package project - -import ( - "testing" - - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" - - "github.com/stretchr/testify/assert" -) - -func TestGetDefaultBoard(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - projectWithoutDefault, err := GetProjectByID(db.DefaultContext, 5) - assert.NoError(t, err) - - // check if default board was added - board, err := projectWithoutDefault.getDefaultBoard(db.DefaultContext) - assert.NoError(t, err) - assert.Equal(t, int64(5), board.ProjectID) - assert.Equal(t, "Uncategorized", board.Title) - - projectWithMultipleDefaults, err := GetProjectByID(db.DefaultContext, 6) - assert.NoError(t, err) - - // check if multiple defaults were removed - board, err = projectWithMultipleDefaults.getDefaultBoard(db.DefaultContext) - assert.NoError(t, err) - assert.Equal(t, int64(6), board.ProjectID) - assert.Equal(t, int64(9), board.ID) - - // set 8 as default board - assert.NoError(t, SetDefaultBoard(db.DefaultContext, board.ProjectID, 8)) - - // then 9 will become a non-default board - board, err = GetBoard(db.DefaultContext, 9) - assert.NoError(t, err) - assert.Equal(t, int64(6), board.ProjectID) - assert.False(t, board.Default) -} diff --git a/models/project/column.go b/models/project/column.go new file mode 100644 index 0000000000000..b218180f58cb5 --- /dev/null +++ b/models/project/column.go @@ -0,0 +1,287 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +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 ( + + // CardType is used to represent a project column card type + CardType uint8 + + // ColumnList is a list of all project columns in a repository + ColumnList []*Column +) + +const ( + // CardTypeTextOnly is a project column card type that is text only + CardTypeTextOnly CardType = iota + + // CardTypeImagesAndText is a project column card type that has images and text + CardTypeImagesAndText +) + +// ColumnColorPattern is a regexp witch can validate ColumnColor +var ColumnColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$") + +// Column is used to represent column on a project +type Column struct { + ID int64 `xorm:"pk autoincr"` + Title string + Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific column will be assigned to this column + 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 (Column) TableName() string { + return "project_board" // FIXME: the legacy table name should be project_column +} + +// NumIssues return counter of all issues assigned to the column +func (b *Column) NumIssues(ctx context.Context) int { + c, err := db.GetEngine(ctx).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(Column)) +} + +// IsCardTypeValid checks if the project column card type is valid +func IsCardTypeValid(p CardType) bool { + switch p { + case CardTypeTextOnly, CardTypeImagesAndText: + return true + default: + return false + } +} + +func createColumnsForProjectsBoradViewType(ctx context.Context, project *Project) error { + var items []string + + switch project.BoardViewType { + case BoardViewTypeBugTriage: + items = setting.Project.ProjectBoardBugTriageType + case BoardViewTypeBasicKanban: + items = setting.Project.ProjectBoardBasicKanbanType + case BoardViewTypeNone: + fallthrough + default: + return nil + } + + return db.WithTx(ctx, func(ctx context.Context) error { + column := Column{ + CreatedUnix: timeutil.TimeStampNow(), + CreatorID: project.CreatorID, + Title: "Backlog", + ProjectID: project.ID, + Default: true, + } + if err := db.Insert(ctx, column); err != nil { + return err + } + + if len(items) == 0 { + return nil + } + + boards := make([]Column, 0, len(items)) + for _, v := range items { + boards = append(boards, Column{ + CreatedUnix: timeutil.TimeStampNow(), + CreatorID: project.CreatorID, + Title: v, + ProjectID: project.ID, + }) + } + + return db.Insert(ctx, boards) + }) +} + +// NewColumn adds a new project column to a given project +func NewColumn(ctx context.Context, column *Column) error { + if len(column.Color) != 0 && !ColumnColorPattern.MatchString(column.Color) { + return fmt.Errorf("bad color code: %s", column.Color) + } + + _, err := db.GetEngine(ctx).Insert(column) + return err +} + +// DeleteColumnByID removes all issues references to the project board. +func DeleteColumnByID(ctx context.Context, columnID int64) error { + return db.WithTx(ctx, func(ctx context.Context) error { + return deleteColumnByID(ctx, columnID) + }) +} + +func deleteColumnByID(ctx context.Context, columnID int64) error { + column, err := GetColumn(ctx, columnID) + if err != nil { + if IsErrProjectColumnNotExist(err) { + return nil + } + + return err + } + + if column.Default { + return fmt.Errorf("deleteBoardByID: cannot delete default board") + } + + if err = column.removeIssues(ctx); err != nil { + return err + } + + if _, err := db.GetEngine(ctx).ID(column.ID).NoAutoCondition().Delete(column); err != nil { + return err + } + return nil +} + +func deleteColumnByProjectID(ctx context.Context, projectID int64) error { + _, err := db.GetEngine(ctx).Where("project_id=?", projectID).Delete(&Column{}) + return err +} + +// GetColumn fetches the current board of a project +func GetColumn(ctx context.Context, columnID int64) (*Column, error) { + board := new(Column) + has, err := db.GetEngine(ctx).ID(columnID).Get(board) + if err != nil { + return nil, err + } else if !has { + return nil, ErrProjectColumnNotExist{ColumnID: columnID} + } + + return board, nil +} + +// UpdateColumn updates a project column +func UpdateColumn(ctx context.Context, column *Column) error { + var fieldToUpdate []string + + if column.Sorting != 0 { + fieldToUpdate = append(fieldToUpdate, "sorting") + } + + if column.Title != "" { + fieldToUpdate = append(fieldToUpdate, "title") + } + + if len(column.Color) != 0 && !ColumnColorPattern.MatchString(column.Color) { + return fmt.Errorf("bad color code: %s", column.Color) + } + fieldToUpdate = append(fieldToUpdate, "color") + + _, err := db.GetEngine(ctx).ID(column.ID).Cols(fieldToUpdate...).Update(column) + + return err +} + +// GetColumns fetches all boards related to a project +func (p *Project) GetColumns(ctx context.Context) (ColumnList, error) { + boards := make([]*Column, 0, 5) + + if err := db.GetEngine(ctx).Where("project_id=? AND `default`=?", p.ID, false).OrderBy("sorting").Find(&boards); err != nil { + return nil, err + } + + defaultB, err := p.getDefaultColumn(ctx) + if err != nil { + return nil, err + } + + return append([]*Column{defaultB}, boards...), nil +} + +// getDefaultColumn return default column and ensure only one exists +func (p *Project) getDefaultColumn(ctx context.Context) (*Column, error) { + var column Column + has, err := db.GetEngine(ctx). + Where("project_id=? AND `default` = ?", p.ID, true). + Desc("id").Get(&column) + if err != nil { + return nil, err + } + + if has { + return &column, nil + } + + // create a default column if none is found + column = Column{ + ProjectID: p.ID, + Default: true, + Title: "Uncategorized", + CreatorID: p.CreatorID, + } + if _, err := db.GetEngine(ctx).Insert(&column); err != nil { + return nil, err + } + return &column, nil +} + +// SetDefaultColumn represents a column for issues not assigned to one +func SetDefaultColumn(ctx context.Context, projectID, columnID int64) error { + return db.WithTx(ctx, func(ctx context.Context) error { + if _, err := GetColumn(ctx, columnID); err != nil { + return err + } + + if _, err := db.GetEngine(ctx).Where(builder.Eq{ + "project_id": projectID, + "`default`": true, + }).Cols("`default`").Update(&Column{Default: false}); err != nil { + return err + } + + _, err := db.GetEngine(ctx).ID(columnID). + Where(builder.Eq{"project_id": projectID}). + Cols("`default`").Update(&Column{Default: true}) + return err + }) +} + +// UpdateColumnSorting update project column sorting +func UpdateColumnSorting(ctx context.Context, cl ColumnList) error { + return db.WithTx(ctx, func(ctx context.Context) error { + for i := range cl { + if _, err := db.GetEngine(ctx).ID(cl[i].ID).Cols( + "sorting", + ).Update(cl[i]); err != nil { + return err + } + } + return nil + }) +} diff --git a/models/project/column_test.go b/models/project/column_test.go new file mode 100644 index 0000000000000..1babaea4280d2 --- /dev/null +++ b/models/project/column_test.go @@ -0,0 +1,44 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package project + +import ( + "testing" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" +) + +func TestGetDefaultcolumn(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + projectWithoutDefault, err := GetProjectByID(db.DefaultContext, 5) + assert.NoError(t, err) + + // check if default column was added + column, err := projectWithoutDefault.getDefaultColumn(db.DefaultContext) + assert.NoError(t, err) + assert.Equal(t, int64(5), column.ProjectID) + assert.Equal(t, "Uncategorized", column.Title) + + projectWithMultipleDefaults, err := GetProjectByID(db.DefaultContext, 6) + assert.NoError(t, err) + + // check if multiple defaults were removed + column, err = projectWithMultipleDefaults.getDefaultColumn(db.DefaultContext) + assert.NoError(t, err) + assert.Equal(t, int64(6), column.ProjectID) + assert.Equal(t, int64(9), column.ID) + + // set 8 as default column + assert.NoError(t, SetDefaultColumn(db.DefaultContext, column.ProjectID, 8)) + + // then 9 will become a non-default column + column, err = GetColumn(db.DefaultContext, 9) + assert.NoError(t, err) + assert.Equal(t, int64(6), column.ProjectID) + assert.False(t, column.Default) +} diff --git a/models/project/issue.go b/models/project/issue.go index ebc9719de55d0..ba192485db6e4 100644 --- a/models/project/issue.go +++ b/models/project/issue.go @@ -17,10 +17,10 @@ type ProjectIssue struct { //revive:disable-line:exported 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"` + // If 0, then it has not been added to a specific column in the project + ProjectColumnID int64 `xorm:"'project_board_id' INDEX"` - // the sorting order on the board + // the sorting order on the column Sorting int64 `xorm:"NOT NULL DEFAULT 0"` } @@ -75,8 +75,8 @@ func (p *Project) NumOpenIssues(ctx context.Context) int { return int(c) } -// MoveIssuesOnProjectBoard moves or keeps issues in a column and sorts them inside that column -func MoveIssuesOnProjectBoard(ctx context.Context, board *Board, sortedIssueIDs map[int64]int64) error { +// MoveIssuesOnProjectColumn moves or keeps issues in a column and sorts them inside that column +func MoveIssuesOnProjectColumn(ctx context.Context, board *Column, sortedIssueIDs map[int64]int64) error { return db.WithTx(ctx, func(ctx context.Context) error { sess := db.GetEngine(ctx) @@ -102,7 +102,7 @@ func MoveIssuesOnProjectBoard(ctx context.Context, board *Board, sortedIssueIDs }) } -func (b *Board) removeIssues(ctx context.Context) error { +func (b *Column) removeIssues(ctx context.Context) error { _, err := db.GetEngine(ctx).Exec("UPDATE `project_issue` SET project_board_id = 0 WHERE project_board_id = ? ", b.ID) return err } diff --git a/models/project/project.go b/models/project/project.go index 8f9ee2a99e9c7..a6c24672f26ae 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -21,12 +21,6 @@ import ( ) type ( - // BoardConfig is used to identify the type of board that is being created - BoardConfig struct { - BoardType BoardType - Translation string - } - // CardConfig is used to identify the type of board card that is being used CardConfig struct { CardType CardType @@ -68,39 +62,39 @@ func (err ErrProjectNotExist) Unwrap() error { return util.ErrNotExist } -// ErrProjectBoardNotExist represents a "ProjectBoardNotExist" kind of error. -type ErrProjectBoardNotExist struct { - BoardID int64 +// ErrProjectColumnNotExist represents a "ProjectBoardNotExist" kind of error. +type ErrProjectColumnNotExist struct { + ColumnID int64 } -// IsErrProjectBoardNotExist checks if an error is a ErrProjectBoardNotExist -func IsErrProjectBoardNotExist(err error) bool { - _, ok := err.(ErrProjectBoardNotExist) +// IsErrProjectColumnNotExist checks if an error is a ErrProjectBoardNotExist +func IsErrProjectColumnNotExist(err error) bool { + _, ok := err.(ErrProjectColumnNotExist) return ok } -func (err ErrProjectBoardNotExist) Error() string { - return fmt.Sprintf("project board does not exist [id: %d]", err.BoardID) +func (err ErrProjectColumnNotExist) Error() string { + return fmt.Sprintf("project column does not exist [id: %d]", err.ColumnID) } -func (err ErrProjectBoardNotExist) Unwrap() error { +func (err ErrProjectColumnNotExist) Unwrap() error { return util.ErrNotExist } // Project represents a project board type Project struct { - ID int64 `xorm:"pk autoincr"` - Title string `xorm:"INDEX NOT NULL"` - Description string `xorm:"TEXT"` - OwnerID int64 `xorm:"INDEX"` - Owner *user_model.User `xorm:"-"` - RepoID int64 `xorm:"INDEX"` - Repo *repo_model.Repository `xorm:"-"` - CreatorID int64 `xorm:"NOT NULL"` - IsClosed bool `xorm:"INDEX"` - BoardType BoardType - CardType CardType - Type Type + ID int64 `xorm:"pk autoincr"` + Title string `xorm:"INDEX NOT NULL"` + Description string `xorm:"TEXT"` + OwnerID int64 `xorm:"INDEX"` + Owner *user_model.User `xorm:"-"` + RepoID int64 `xorm:"INDEX"` + Repo *repo_model.Repository `xorm:"-"` + CreatorID int64 `xorm:"NOT NULL"` + IsClosed bool `xorm:"INDEX"` + BoardViewType BoardViewType `xorm:"'board_type'"` + CardType CardType + Type Type RenderedContent template.HTML `xorm:"-"` @@ -165,15 +159,6 @@ func init() { db.RegisterModel(new(Project)) } -// GetBoardConfig retrieves the types of configurations project boards could have -func GetBoardConfig() []BoardConfig { - return []BoardConfig{ - {BoardTypeNone, "repo.projects.type.none"}, - {BoardTypeBasicKanban, "repo.projects.type.basic_kanban"}, - {BoardTypeBugTriage, "repo.projects.type.bug_triage"}, - } -} - // GetCardConfig retrieves the types of configurations project board cards could have func GetCardConfig() []CardConfig { return []CardConfig{ @@ -244,8 +229,8 @@ func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy { // NewProject creates a new Project func NewProject(ctx context.Context, p *Project) error { - if !IsBoardTypeValid(p.BoardType) { - p.BoardType = BoardTypeNone + if !IsBoardViewTypeValid(p.BoardViewType) { + p.BoardViewType = BoardViewTypeNone } if !IsCardTypeValid(p.CardType) { @@ -256,27 +241,19 @@ func NewProject(ctx context.Context, p *Project) error { return util.NewInvalidArgumentErrorf("project type is not valid") } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if err := db.Insert(ctx, p); err != nil { - return err - } - - if p.RepoID > 0 { - if _, err := db.Exec(ctx, "UPDATE `repository` SET num_projects = num_projects + 1 WHERE id = ?", p.RepoID); err != nil { + return db.WithTx(ctx, func(tx context.Context) error { + if err := db.Insert(ctx, p); err != nil { return err } - } - if err := createBoardsForProjectsType(ctx, p); err != nil { - return err - } + if p.RepoID > 0 { + if _, err := db.Exec(ctx, "UPDATE `repository` SET num_projects = num_projects + 1 WHERE id = ?", p.RepoID); err != nil { + return err + } + } - return committer.Commit() + return createColumnsForProjectsBoradViewType(ctx, p) + }) } // GetProjectByID returns the projects in a repository @@ -410,7 +387,7 @@ func DeleteProjectByID(ctx context.Context, id int64) error { return err } - if err := deleteBoardByProjectID(ctx, id); err != nil { + if err := deleteColumnByProjectID(ctx, id); err != nil { return err } diff --git a/models/project/project_test.go b/models/project/project_test.go index 8fbbdedecf012..49188777e3ef9 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -51,13 +51,13 @@ func TestProject(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) project := &Project{ - Type: TypeRepository, - BoardType: BoardTypeBasicKanban, - CardType: CardTypeTextOnly, - Title: "New Project", - RepoID: 1, - CreatedUnix: timeutil.TimeStampNow(), - CreatorID: 2, + Type: TypeRepository, + BoardViewType: BoardViewTypeBasicKanban, + CardType: CardTypeTextOnly, + Title: "New Project", + RepoID: 1, + CreatedUnix: timeutil.TimeStampNow(), + CreatorID: 2, } assert.NoError(t, NewProject(db.DefaultContext, project)) diff --git a/models/project/view_board.go b/models/project/view_board.go new file mode 100644 index 0000000000000..973b700c2ec18 --- /dev/null +++ b/models/project/view_board.go @@ -0,0 +1,45 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package project + +type ( + // BoardViewType is used to represent a project column type + BoardViewType uint8 + + // BoardConfig is used to identify the type of board that is being created + BoardConfig struct { + BoardType BoardViewType + Translation string + } +) + +const ( + // BoardViewTypeNone is a project board type that has no predefined columns + BoardViewTypeNone BoardViewType = iota + + // BoardViewTypeBasicKanban is a project board type that has basic predefined columns + BoardViewTypeBasicKanban + + // BoardViewTypeBugTriage is a project board type that has predefined columns suited to hunting down bugs + BoardViewTypeBugTriage +) + +// GetBoardViewConfig retrieves the types of configurations project boards could have +func GetBoardViewConfig() []BoardConfig { + return []BoardConfig{ + {BoardViewTypeNone, "repo.projects.type.none"}, + {BoardViewTypeBasicKanban, "repo.projects.type.basic_kanban"}, + {BoardViewTypeBugTriage, "repo.projects.type.bug_triage"}, + } +} + +// IsBoardViewTypeValid checks if the project board type is valid +func IsBoardViewTypeValid(p BoardViewType) bool { + switch p { + case BoardViewTypeNone, BoardViewTypeBasicKanban, BoardViewTypeBugTriage: + return true + default: + return false + } +} diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go index 1f54be721b37c..a6d366ec1c22f 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -228,8 +228,8 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( if options.ProjectID.Has() { queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectID.Value(), "project_id")) } - if options.ProjectBoardID.Has() { - queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectBoardID.Value(), "project_board_id")) + if options.ProjectColumnID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectColumnID.Value(), "project_board_id")) } if options.PosterID.Has() { diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go index eeaf1696adaf7..875a4ca279dd6 100644 --- a/modules/indexer/issues/db/options.go +++ b/modules/indexer/issues/db/options.go @@ -61,7 +61,7 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m ReviewedID: convertID(options.ReviewedID), SubscriberID: convertID(options.SubscriberID), ProjectID: convertID(options.ProjectID), - ProjectBoardID: convertID(options.ProjectBoardID), + ProjectColumnID: convertID(options.ProjectColumnID), IsClosed: options.IsClosed, IsPull: options.IsPull, IncludedLabelNames: nil, diff --git a/modules/indexer/issues/dboptions.go b/modules/indexer/issues/dboptions.go index 4a98b4588a3da..3bda12efb9ecf 100644 --- a/modules/indexer/issues/dboptions.go +++ b/modules/indexer/issues/dboptions.go @@ -50,7 +50,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp } searchOpt.ProjectID = convertID(opts.ProjectID) - searchOpt.ProjectBoardID = convertID(opts.ProjectBoardID) + searchOpt.ProjectColumnID = convertID(opts.ProjectColumnID) searchOpt.PosterID = convertID(opts.PosterID) searchOpt.AssigneeID = convertID(opts.AssigneeID) searchOpt.MentionID = convertID(opts.MentionedID) diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go index 53b383c8d5d78..979bed7f739b8 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -198,8 +198,8 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( if options.ProjectID.Has() { query.Must(elastic.NewTermQuery("project_id", options.ProjectID.Value())) } - if options.ProjectBoardID.Has() { - query.Must(elastic.NewTermQuery("project_board_id", options.ProjectBoardID.Value())) + if options.ProjectColumnID.Has() { + query.Must(elastic.NewTermQuery("project_board_id", options.ProjectColumnID.Value())) } if options.PosterID.Has() { diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go index 0d0cfc851697d..35d43742be9d8 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -369,13 +369,13 @@ func searchIssueInProject(t *testing.T) { }, { SearchOptions{ - ProjectBoardID: optional.Some(int64(1)), + ProjectColumnID: optional.Some(int64(1)), }, []int64{1}, }, { SearchOptions{ - ProjectBoardID: optional.Some(int64(0)), // issue with in default board + ProjectColumnID: optional.Some(int64(0)), // issue with in default board }, []int64{2}, }, diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go index e9c4eca559290..5b34b50ae76ed 100644 --- a/modules/indexer/issues/internal/model.go +++ b/modules/indexer/issues/internal/model.go @@ -89,8 +89,8 @@ type SearchOptions struct { MilestoneIDs []int64 // milestones the issues have - ProjectID optional.Option[int64] // project the issues belong to - ProjectBoardID optional.Option[int64] // project board the issues belong to + ProjectID optional.Option[int64] // project the issues belong to + ProjectColumnID optional.Option[int64] // project column the issues belong to PosterID optional.Option[int64] // poster of the issues diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index 7f32876d80574..eea985d872eee 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -343,7 +343,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ProjectBoardID: optional.Some(int64(1)), + ProjectColumnID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -361,7 +361,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ProjectBoardID: optional.Some(int64(0)), + ProjectColumnID: optional.Some(int64(0)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go index 8a7cec6cba4dd..9332319339215 100644 --- a/modules/indexer/issues/meilisearch/meilisearch.go +++ b/modules/indexer/issues/meilisearch/meilisearch.go @@ -174,8 +174,8 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( if options.ProjectID.Has() { query.And(inner_meilisearch.NewFilterEq("project_id", options.ProjectID.Value())) } - if options.ProjectBoardID.Has() { - query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectBoardID.Value())) + if options.ProjectColumnID.Has() { + query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectColumnID.Value())) } if options.PosterID.Has() { diff --git a/modules/indexer/issues/util.go b/modules/indexer/issues/util.go index 9861c808dcf6d..055bbbf43b213 100644 --- a/modules/indexer/issues/util.go +++ b/modules/indexer/issues/util.go @@ -105,7 +105,7 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD NoLabel: len(labels) == 0, MilestoneID: issue.MilestoneID, ProjectID: projectID, - ProjectBoardID: issue.ProjectBoardID(ctx), + ProjectBoardID: issue.ProjectColumnID(ctx), PosterID: issue.PosterID, AssigneeID: issue.AssigneeID, MentionIDs: mentionIDs, diff --git a/modules/metrics/collector.go b/modules/metrics/collector.go index 1bf8f58b93a66..e645adeaf1e94 100755 --- a/modules/metrics/collector.go +++ b/modules/metrics/collector.go @@ -338,7 +338,7 @@ func (c Collector) Collect(ch chan<- prometheus.Metric) { ch <- prometheus.MustNewConstMetric( c.ProjectBoards, prometheus.GaugeValue, - float64(stats.Counter.ProjectBoard), + float64(stats.Counter.ProjectColumn), ) ch <- prometheus.MustNewConstMetric( c.PublicKeys, diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 596a370d2e551..36f4f3ef50702 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -141,7 +141,7 @@ func canWriteProjects(ctx *context.Context) bool { // RenderNewProject render creating a project page func RenderNewProject(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.new") - ctx.Data["BoardTypes"] = project_model.GetBoardConfig() + ctx.Data["BoardTypes"] = project_model.GetBoardViewConfig() ctx.Data["CardTypes"] = project_model.GetCardConfig() ctx.Data["CanWriteProjects"] = canWriteProjects(ctx) ctx.Data["PageIsViewProjects"] = true @@ -170,12 +170,12 @@ func NewProjectPost(ctx *context.Context) { } newProject := project_model.Project{ - OwnerID: ctx.ContextUser.ID, - Title: form.Title, - Description: form.Content, - CreatorID: ctx.Doer.ID, - BoardType: form.BoardType, - CardType: form.CardType, + OwnerID: ctx.ContextUser.ID, + Title: form.Title, + Description: form.Content, + CreatorID: ctx.Doer.ID, + BoardViewType: form.BoardType, + CardType: form.CardType, } if ctx.ContextUser.IsOrganization() { @@ -327,13 +327,13 @@ func ViewProject(ctx *context.Context) { return } - boards, err := project.GetBoards(ctx) + boards, err := project.GetColumns(ctx) if err != nil { ctx.ServerError("GetProjectBoards", err) return } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards) + issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, boards) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return @@ -458,8 +458,8 @@ func UpdateIssueProject(ctx *context.Context) { ctx.JSONOK() } -// DeleteProjectBoard allows for the deletion of a project board -func DeleteProjectBoard(ctx *context.Context) { +// DeleteProjectColumn allows for the deletion of a project board +func DeleteProjectColumn(ctx *context.Context) { if ctx.Doer == nil { ctx.JSON(http.StatusForbidden, map[string]string{ "message": "Only signed in users are allowed to perform this action.", @@ -473,7 +473,7 @@ func DeleteProjectBoard(ctx *context.Context) { return } - pb, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) + pb, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) if err != nil { ctx.ServerError("GetProjectBoard", err) return @@ -492,7 +492,7 @@ func DeleteProjectBoard(ctx *context.Context) { return } - if err := project_model.DeleteBoardByID(ctx, ctx.ParamsInt64(":boardID")); err != nil { + if err := project_model.DeleteColumnByID(ctx, ctx.ParamsInt64(":boardID")); err != nil { ctx.ServerError("DeleteProjectBoardByID", err) return } @@ -500,9 +500,9 @@ func DeleteProjectBoard(ctx *context.Context) { ctx.JSONOK() } -// AddBoardToProjectPost allows a new board to be added to a project. -func AddBoardToProjectPost(ctx *context.Context) { - form := web.GetForm(ctx).(*forms.EditProjectBoardForm) +// AddColumnToProjectPost allows a new board to be added to a project. +func AddColumnToProjectPost(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.EditProjectColumnForm) project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) if err != nil { @@ -510,7 +510,7 @@ func AddBoardToProjectPost(ctx *context.Context) { return } - if err := project_model.NewBoard(ctx, &project_model.Board{ + if err := project_model.NewColumn(ctx, &project_model.Column{ ProjectID: project.ID, Title: form.Title, Color: form.Color, @@ -524,7 +524,7 @@ func AddBoardToProjectPost(ctx *context.Context) { } // CheckProjectBoardChangePermissions check permission -func CheckProjectBoardChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Board) { +func CheckProjectBoardChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Column) { if ctx.Doer == nil { ctx.JSON(http.StatusForbidden, map[string]string{ "message": "Only signed in users are allowed to perform this action.", @@ -538,7 +538,7 @@ func CheckProjectBoardChangePermissions(ctx *context.Context) (*project_model.Pr return nil, nil } - board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) + board, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) if err != nil { ctx.ServerError("GetProjectBoard", err) return nil, nil @@ -559,9 +559,9 @@ func CheckProjectBoardChangePermissions(ctx *context.Context) (*project_model.Pr return project, board } -// EditProjectBoard allows a project board's to be updated -func EditProjectBoard(ctx *context.Context) { - form := web.GetForm(ctx).(*forms.EditProjectBoardForm) +// EditProjectColumn allows a project board's to be updated +func EditProjectColumn(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.EditProjectColumnForm) _, board := CheckProjectBoardChangePermissions(ctx) if ctx.Written() { return @@ -577,7 +577,7 @@ func EditProjectBoard(ctx *context.Context) { board.Sorting = form.Sorting } - if err := project_model.UpdateBoard(ctx, board); err != nil { + if err := project_model.UpdateColumn(ctx, board); err != nil { ctx.ServerError("UpdateProjectBoard", err) return } @@ -592,7 +592,7 @@ func SetDefaultProjectBoard(ctx *context.Context) { return } - if err := project_model.SetDefaultBoard(ctx, project.ID, board.ID); err != nil { + if err := project_model.SetDefaultColumn(ctx, project.ID, board.ID); err != nil { ctx.ServerError("SetDefaultBoard", err) return } @@ -619,9 +619,9 @@ func MoveIssues(ctx *context.Context) { return } - board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) + board, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) if err != nil { - ctx.NotFoundOrServerError("GetProjectBoard", project_model.IsErrProjectBoardNotExist, err) + ctx.NotFoundOrServerError("GetProjectBoard", project_model.IsErrProjectColumnNotExist, err) return } @@ -671,7 +671,7 @@ func MoveIssues(ctx *context.Context) { } } - if err = project_model.MoveIssuesOnProjectBoard(ctx, board, sortedIssueIDs); err != nil { + if err = project_model.MoveIssuesOnProjectColumn(ctx, board, sortedIssueIDs); err != nil { ctx.ServerError("MoveIssuesOnProjectBoard", err) return } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 6c2d4a73902a7..af030213cd913 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2826,12 +2826,12 @@ func ListIssues(ctx *context.Context) { Page: ctx.FormInt("page"), PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), }, - Keyword: keyword, - RepoIDs: []int64{ctx.Repo.Repository.ID}, - IsPull: isPull, - IsClosed: isClosed, - ProjectBoardID: projectID, - SortBy: issue_indexer.SortByCreatedDesc, + Keyword: keyword, + RepoIDs: []int64{ctx.Repo.Repository.ID}, + IsPull: isPull, + IsClosed: isClosed, + ProjectID: projectID, + SortBy: issue_indexer.SortByCreatedDesc, } if since != 0 { searchOpt.UpdatedAfterUnix = optional.Some(since) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index a2db1fc770a5b..4225b89aa7aa6 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -132,7 +132,7 @@ func Projects(ctx *context.Context) { // RenderNewProject render creating a project page func RenderNewProject(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.new") - ctx.Data["BoardTypes"] = project_model.GetBoardConfig() + ctx.Data["BoardTypes"] = project_model.GetBoardViewConfig() ctx.Data["CardTypes"] = project_model.GetCardConfig() ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.Data["CancelLink"] = ctx.Repo.Repository.Link() + "/projects" @@ -150,13 +150,13 @@ func NewProjectPost(ctx *context.Context) { } if err := project_model.NewProject(ctx, &project_model.Project{ - RepoID: ctx.Repo.Repository.ID, - Title: form.Title, - Description: form.Content, - CreatorID: ctx.Doer.ID, - BoardType: form.BoardType, - CardType: form.CardType, - Type: project_model.TypeRepository, + RepoID: ctx.Repo.Repository.ID, + Title: form.Title, + Description: form.Content, + CreatorID: ctx.Doer.ID, + BoardViewType: form.BoardType, + CardType: form.CardType, + Type: project_model.TypeRepository, }); err != nil { ctx.ServerError("NewProject", err) return @@ -309,13 +309,13 @@ func ViewProject(ctx *context.Context) { return } - boards, err := project.GetBoards(ctx) + boards, err := project.GetColumns(ctx) if err != nil { ctx.ServerError("GetProjectBoards", err) return } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards) + issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, boards) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return @@ -432,7 +432,7 @@ func DeleteProjectBoard(ctx *context.Context) { return } - pb, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) + pb, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) if err != nil { ctx.ServerError("GetProjectBoard", err) return @@ -451,7 +451,7 @@ func DeleteProjectBoard(ctx *context.Context) { return } - if err := project_model.DeleteBoardByID(ctx, ctx.ParamsInt64(":boardID")); err != nil { + if err := project_model.DeleteColumnByID(ctx, ctx.ParamsInt64(":boardID")); err != nil { ctx.ServerError("DeleteProjectBoardByID", err) return } @@ -461,7 +461,7 @@ func DeleteProjectBoard(ctx *context.Context) { // AddBoardToProjectPost allows a new board to be added to a project. func AddBoardToProjectPost(ctx *context.Context) { - form := web.GetForm(ctx).(*forms.EditProjectBoardForm) + form := web.GetForm(ctx).(*forms.EditProjectColumnForm) if !ctx.Repo.IsOwner() && !ctx.Repo.IsAdmin() && !ctx.Repo.CanAccess(perm.AccessModeWrite, unit.TypeProjects) { ctx.JSON(http.StatusForbidden, map[string]string{ "message": "Only authorized users are allowed to perform this action.", @@ -479,7 +479,7 @@ func AddBoardToProjectPost(ctx *context.Context) { return } - if err := project_model.NewBoard(ctx, &project_model.Board{ + if err := project_model.NewColumn(ctx, &project_model.Column{ ProjectID: project.ID, Title: form.Title, Color: form.Color, @@ -492,7 +492,7 @@ func AddBoardToProjectPost(ctx *context.Context) { ctx.JSONOK() } -func checkProjectBoardChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Board) { +func checkProjectBoardChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Column) { if ctx.Doer == nil { ctx.JSON(http.StatusForbidden, map[string]string{ "message": "Only signed in users are allowed to perform this action.", @@ -517,7 +517,7 @@ func checkProjectBoardChangePermissions(ctx *context.Context) (*project_model.Pr return nil, nil } - board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) + board, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) if err != nil { ctx.ServerError("GetProjectBoard", err) return nil, nil @@ -538,9 +538,9 @@ func checkProjectBoardChangePermissions(ctx *context.Context) (*project_model.Pr return project, board } -// EditProjectBoard allows a project board's to be updated -func EditProjectBoard(ctx *context.Context) { - form := web.GetForm(ctx).(*forms.EditProjectBoardForm) +// EditProjectColumn allows a project board's to be updated +func EditProjectColumn(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.EditProjectColumnForm) _, board := checkProjectBoardChangePermissions(ctx) if ctx.Written() { return @@ -556,7 +556,7 @@ func EditProjectBoard(ctx *context.Context) { board.Sorting = form.Sorting } - if err := project_model.UpdateBoard(ctx, board); err != nil { + if err := project_model.UpdateColumn(ctx, board); err != nil { ctx.ServerError("UpdateProjectBoard", err) return } @@ -564,14 +564,14 @@ func EditProjectBoard(ctx *context.Context) { ctx.JSONOK() } -// SetDefaultProjectBoard set default board for uncategorized issues/pulls -func SetDefaultProjectBoard(ctx *context.Context) { +// SetDefaultProjectColumn set default board for uncategorized issues/pulls +func SetDefaultProjectColumn(ctx *context.Context) { project, board := checkProjectBoardChangePermissions(ctx) if ctx.Written() { return } - if err := project_model.SetDefaultBoard(ctx, project.ID, board.ID); err != nil { + if err := project_model.SetDefaultColumn(ctx, project.ID, board.ID); err != nil { ctx.ServerError("SetDefaultBoard", err) return } @@ -609,12 +609,12 @@ func MoveIssues(ctx *context.Context) { return } - board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) + board, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) if err != nil { - if project_model.IsErrProjectBoardNotExist(err) { - ctx.NotFound("ProjectBoardNotExist", nil) + if project_model.IsErrProjectColumnNotExist(err) { + ctx.NotFound("ProjectColumnNotExist", nil) } else { - ctx.ServerError("GetProjectBoard", err) + ctx.ServerError("GetProjectColumn", err) } return } @@ -664,8 +664,8 @@ func MoveIssues(ctx *context.Context) { } } - if err = project_model.MoveIssuesOnProjectBoard(ctx, board, sortedIssueIDs); err != nil { - ctx.ServerError("MoveIssuesOnProjectBoard", err) + if err = project_model.MoveIssuesOnProjectColumn(ctx, board, sortedIssueIDs); err != nil { + ctx.ServerError("MoveIssuesOnProjectColumn", err) return } diff --git a/routers/web/web.go b/routers/web/web.go index 4fff994e42474..04255f26a7d02 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -997,7 +997,7 @@ func registerRoutes(m *web.Route) { m.Get("/new", org.RenderNewProject) m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost) m.Group("/{id}", func() { - m.Post("", web.Bind(forms.EditProjectBoardForm{}), org.AddBoardToProjectPost) + m.Post("", web.Bind(forms.EditProjectColumnForm{}), org.AddColumnToProjectPost) m.Post("/delete", org.DeleteProject) m.Get("/edit", org.RenderEditProject) @@ -1005,8 +1005,8 @@ func registerRoutes(m *web.Route) { m.Post("/{action:open|close}", org.ChangeProjectStatus) m.Group("/{boardID}", func() { - m.Put("", web.Bind(forms.EditProjectBoardForm{}), org.EditProjectBoard) - m.Delete("", org.DeleteProjectBoard) + m.Put("", web.Bind(forms.EditProjectColumnForm{}), org.EditProjectColumn) + m.Delete("", org.DeleteProjectColumn) m.Post("/default", org.SetDefaultProjectBoard) m.Post("/move", org.MoveIssues) @@ -1336,7 +1336,7 @@ func registerRoutes(m *web.Route) { m.Get("/new", repo.RenderNewProject) m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost) m.Group("/{id}", func() { - m.Post("", web.Bind(forms.EditProjectBoardForm{}), repo.AddBoardToProjectPost) + m.Post("", web.Bind(forms.EditProjectColumnForm{}), repo.AddBoardToProjectPost) m.Post("/delete", repo.DeleteProject) m.Get("/edit", repo.RenderEditProject) @@ -1344,9 +1344,9 @@ func registerRoutes(m *web.Route) { m.Post("/{action:open|close}", repo.ChangeProjectStatus) m.Group("/{boardID}", func() { - m.Put("", web.Bind(forms.EditProjectBoardForm{}), repo.EditProjectBoard) + m.Put("", web.Bind(forms.EditProjectColumnForm{}), repo.EditProjectColumn) m.Delete("", repo.DeleteProjectBoard) - m.Post("/default", repo.SetDefaultProjectBoard) + m.Post("/default", repo.SetDefaultProjectColumn) m.Post("/move", repo.MoveIssues) }) diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index e45a2a1695522..7f3ee2ec5208b 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -504,18 +504,11 @@ func (i IssueLockForm) HasValidReason() bool { return false } -// __________ __ __ -// \______ \_______ ____ |__| ____ _____/ |_ ______ -// | ___/\_ __ \/ _ \ | |/ __ \_/ ___\ __\/ ___/ -// | | | | \( <_> ) | \ ___/\ \___| | \___ \ -// |____| |__| \____/\__| |\___ >\___ >__| /____ > -// \______| \/ \/ \/ - // CreateProjectForm form for creating a project type CreateProjectForm struct { Title string `binding:"Required;MaxSize(100)"` Content string - BoardType project_model.BoardType + BoardType project_model.BoardViewType CardType project_model.CardType } @@ -524,25 +517,18 @@ type CreateProjectForm struct { type UserCreateProjectForm struct { Title string `binding:"Required;MaxSize(100)"` Content string - BoardType project_model.BoardType + BoardType project_model.BoardViewType CardType project_model.CardType UID int64 `binding:"Required"` } -// EditProjectBoardForm is a form for editing a project board -type EditProjectBoardForm struct { +// EditProjectColumnForm is a form for editing a project board +type EditProjectColumnForm struct { Title string `binding:"Required;MaxSize(100)"` Sorting int8 Color string `binding:"MaxSize(7)"` } -// _____ .__.__ __ -// / \ |__| | ____ _______/ |_ ____ ____ ____ -// / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \ -// / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/ -// \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ > -// \/ \/ \/ \/ \/ - // CreateMilestoneForm form for creating milestone type CreateMilestoneForm struct { Title string `binding:"Required;MaxSize(50)"` @@ -556,13 +542,6 @@ func (f *CreateMilestoneForm) Validate(req *http.Request, errs binding.Errors) b return middleware.Validate(errs, ctx.Data, f, ctx.Locale) } -// .____ ___. .__ -// | | _____ \_ |__ ____ | | -// | | \__ \ | __ \_/ __ \| | -// | |___ / __ \| \_\ \ ___/| |__ -// |_______ (____ /___ /\___ >____/ -// \/ \/ \/ \/ - // CreateLabelForm form for creating label type CreateLabelForm struct { ID int64 @@ -590,13 +569,6 @@ func (f *InitializeLabelsForm) Validate(req *http.Request, errs binding.Errors) return middleware.Validate(errs, ctx.Data, f, ctx.Locale) } -// __________ .__ .__ __________ __ -// \______ \__ __| | | | \______ \ ____ ________ __ ____ _______/ |_ -// | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\ -// | | | | / |_| |__ | | \ ___< <_| | | /\ ___/ \___ \ | | -// |____| |____/|____/____/ |____|_ /\___ >__ |____/ \___ >____ > |__| -// \/ \/ |__| \/ \/ - // MergePullRequestForm form for merging Pull Request // swagger:model MergePullRequestOption type MergePullRequestForm struct { From 49fce5de0bd182cdcf8ec2c978f53663e4f49f07 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 30 Mar 2024 10:46:18 +0800 Subject: [PATCH 02/15] rename more board -> column --- models/issues/comment.go | 4 +- models/issues/issue_project.go | 4 +- .../project/{view_board.go => board_view.go} | 6 +- models/project/column.go | 12 ++-- models/project/issue.go | 6 +- models/project/project.go | 12 ++-- models/unit/unit.go | 2 +- modules/indexer/issues/indexer_test.go | 2 +- modules/indexer/issues/internal/model.go | 2 +- .../indexer/issues/internal/tests/tests.go | 14 ++-- modules/indexer/issues/util.go | 2 +- modules/metrics/collector.go | 8 +-- routers/web/org/projects.go | 70 +++++++++---------- routers/web/org/projects_test.go | 6 +- routers/web/repo/projects.go | 64 ++++++++--------- routers/web/repo/projects_test.go | 6 +- routers/web/web.go | 6 +- services/forms/repo_form.go | 2 +- services/forms/user_form_hidden_comments.go | 2 +- 19 files changed, 113 insertions(+), 117 deletions(-) rename models/project/{view_board.go => board_view.go} (78%) diff --git a/models/issues/comment.go b/models/issues/comment.go index 6f65a5dbbc616..bbf782b785e6f 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -100,8 +100,8 @@ const ( CommentTypeMergePull // 28 merge pull request CommentTypePullRequestPush // 29 push to PR head branch - CommentTypeProject // 30 Project changed - CommentTypeProjectBoard // 31 Project board changed + CommentTypeProject // 30 Project changed + CommentTypeProjectColumn // 31 Project column changed CommentTypeDismissReview // 32 Dismiss Review diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 330175afe373e..c95a095d9f69c 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -47,7 +47,7 @@ func (issue *Issue) ProjectColumnID(ctx context.Context) int64 { return ip.ProjectColumnID } -// LoadIssuesFromColumn load issues assigned to this board +// LoadIssuesFromColumn load issues assigned to this column func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column) (IssueList, error) { issueList, err := Issues(ctx, &IssuesOptions{ ProjectColumnID: b.ID, @@ -77,7 +77,7 @@ func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column) (IssueLi return issueList, nil } -// LoadIssuesFromColumnList load issues assigned to the boards +// LoadIssuesFromColumnList load issues assigned to the columns func LoadIssuesFromColumnList(ctx context.Context, bs project_model.ColumnList) (map[int64]IssueList, error) { issuesMap := make(map[int64]IssueList, len(bs)) for i := range bs { diff --git a/models/project/view_board.go b/models/project/board_view.go similarity index 78% rename from models/project/view_board.go rename to models/project/board_view.go index 973b700c2ec18..7c4e9ffc745cd 100644 --- a/models/project/view_board.go +++ b/models/project/board_view.go @@ -15,13 +15,13 @@ type ( ) const ( - // BoardViewTypeNone is a project board type that has no predefined columns + // BoardViewTypeNone is a project board view type that has no predefined columns BoardViewTypeNone BoardViewType = iota - // BoardViewTypeBasicKanban is a project board type that has basic predefined columns + // BoardViewTypeBasicKanban is a project board view type that has basic predefined columns BoardViewTypeBasicKanban - // BoardViewTypeBugTriage is a project board type that has predefined columns suited to hunting down bugs + // BoardViewTypeBugTriage is a project board view type that has predefined columns suited to hunting down bugs BoardViewTypeBugTriage ) diff --git a/models/project/column.go b/models/project/column.go index b218180f58cb5..8a5eb78dc86cd 100644 --- a/models/project/column.go +++ b/models/project/column.go @@ -137,7 +137,7 @@ func NewColumn(ctx context.Context, column *Column) error { return err } -// DeleteColumnByID removes all issues references to the project board. +// DeleteColumnByID removes all issues references to the project column. func DeleteColumnByID(ctx context.Context, columnID int64) error { return db.WithTx(ctx, func(ctx context.Context) error { return deleteColumnByID(ctx, columnID) @@ -155,7 +155,7 @@ func deleteColumnByID(ctx context.Context, columnID int64) error { } if column.Default { - return fmt.Errorf("deleteBoardByID: cannot delete default board") + return fmt.Errorf("deleteBoardByID: cannot delete default column") } if err = column.removeIssues(ctx); err != nil { @@ -173,17 +173,17 @@ func deleteColumnByProjectID(ctx context.Context, projectID int64) error { return err } -// GetColumn fetches the current board of a project +// GetColumn fetches the current column of a project func GetColumn(ctx context.Context, columnID int64) (*Column, error) { - board := new(Column) - has, err := db.GetEngine(ctx).ID(columnID).Get(board) + column := new(Column) + has, err := db.GetEngine(ctx).ID(columnID).Get(column) if err != nil { return nil, err } else if !has { return nil, ErrProjectColumnNotExist{ColumnID: columnID} } - return board, nil + return column, nil } // UpdateColumn updates a project column diff --git a/models/project/issue.go b/models/project/issue.go index ba192485db6e4..63352ed96c8fb 100644 --- a/models/project/issue.go +++ b/models/project/issue.go @@ -76,7 +76,7 @@ func (p *Project) NumOpenIssues(ctx context.Context) int { } // MoveIssuesOnProjectColumn moves or keeps issues in a column and sorts them inside that column -func MoveIssuesOnProjectColumn(ctx context.Context, board *Column, sortedIssueIDs map[int64]int64) error { +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) @@ -84,7 +84,7 @@ func MoveIssuesOnProjectColumn(ctx context.Context, board *Column, sortedIssueID for _, issueID := range sortedIssueIDs { issueIDs = append(issueIDs, issueID) } - count, err := sess.Table(new(ProjectIssue)).Where("project_id=?", board.ProjectID).In("issue_id", issueIDs).Count() + count, err := sess.Table(new(ProjectIssue)).Where("project_id=?", column.ProjectID).In("issue_id", issueIDs).Count() if err != nil { return err } @@ -93,7 +93,7 @@ func MoveIssuesOnProjectColumn(ctx context.Context, board *Column, sortedIssueID } for sorting, issueID := range sortedIssueIDs { - _, err = sess.Exec("UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", board.ID, sorting, issueID) + _, err = sess.Exec("UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", column.ID, sorting, issueID) if err != nil { return err } diff --git a/models/project/project.go b/models/project/project.go index a6c24672f26ae..a5bb4d3305613 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -21,7 +21,7 @@ import ( ) type ( - // CardConfig is used to identify the type of board card that is being used + // CardConfig is used to identify the type of column card that is being used CardConfig struct { CardType CardType Translation string @@ -32,7 +32,7 @@ type ( ) const ( - // TypeIndividual is a type of project board that is owned by an individual + // TypeIndividual is a type of project column that is owned by an individual TypeIndividual Type = iota + 1 // TypeRepository is a project that is tied to a repository @@ -62,12 +62,12 @@ func (err ErrProjectNotExist) Unwrap() error { return util.ErrNotExist } -// ErrProjectColumnNotExist represents a "ProjectBoardNotExist" kind of error. +// ErrProjectColumnNotExist represents a "ErrProjectColumnNotExist" kind of error. type ErrProjectColumnNotExist struct { ColumnID int64 } -// IsErrProjectColumnNotExist checks if an error is a ErrProjectBoardNotExist +// IsErrProjectColumnNotExist checks if an error is a ErrProjectColumnNotExist func IsErrProjectColumnNotExist(err error) bool { _, ok := err.(ErrProjectColumnNotExist) return ok @@ -81,7 +81,7 @@ func (err ErrProjectColumnNotExist) Unwrap() error { return util.ErrNotExist } -// Project represents a project board +// Project represents a project type Project struct { ID int64 `xorm:"pk autoincr"` Title string `xorm:"INDEX NOT NULL"` @@ -159,7 +159,7 @@ func init() { db.RegisterModel(new(Project)) } -// GetCardConfig retrieves the types of configurations project board cards could have +// GetCardConfig retrieves the types of configurations project column cards could have func GetCardConfig() []CardConfig { return []CardConfig{ {CardTypeTextOnly, "repo.projects.card_type.text_only"}, diff --git a/models/unit/unit.go b/models/unit/unit.go index b216712d37c7c..2c6cf1178b7ca 100644 --- a/models/unit/unit.go +++ b/models/unit/unit.go @@ -27,7 +27,7 @@ const ( TypeWiki // 5 Wiki TypeExternalWiki // 6 ExternalWiki TypeExternalTracker // 7 ExternalTracker - TypeProjects // 8 Kanban board + TypeProjects // 8 Projects TypePackages // 9 Packages TypeActions // 10 Actions ) diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go index 35d43742be9d8..e426229f78e2c 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -375,7 +375,7 @@ func searchIssueInProject(t *testing.T) { }, { SearchOptions{ - ProjectColumnID: optional.Some(int64(0)), // issue with in default board + ProjectColumnID: optional.Some(int64(0)), // issue with in default column }, []int64{2}, }, diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go index 5b34b50ae76ed..2dfee8b72e197 100644 --- a/modules/indexer/issues/internal/model.go +++ b/modules/indexer/issues/internal/model.go @@ -27,7 +27,7 @@ type IndexerData struct { NoLabel bool `json:"no_label"` // True if LabelIDs is empty MilestoneID int64 `json:"milestone_id"` ProjectID int64 `json:"project_id"` - ProjectBoardID int64 `json:"project_board_id"` + ProjectColumnID int64 `json:"project_board_id"` // the key should be kept as project_board_id to keep compatible PosterID int64 `json:"poster_id"` AssigneeID int64 `json:"assignee_id"` MentionIDs []int64 `json:"mention_ids"` diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index eea985d872eee..16f0a78ec0411 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -338,7 +338,7 @@ var cases = []*testIndexerCase{ }, }, { - Name: "ProjectBoardID", + Name: "ProjectColumnID", SearchOptions: &internal.SearchOptions{ Paginator: &db.ListOptions{ PageSize: 5, @@ -348,15 +348,15 @@ var cases = []*testIndexerCase{ Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) for _, v := range result.Hits { - assert.Equal(t, int64(1), data[v.ID].ProjectBoardID) + assert.Equal(t, int64(1), data[v.ID].ProjectColumnID) } assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { - return v.ProjectBoardID == 1 + return v.ProjectColumnID == 1 }), result.Total) }, }, { - Name: "no ProjectBoardID", + Name: "no ProjectColumnID", SearchOptions: &internal.SearchOptions{ Paginator: &db.ListOptions{ PageSize: 5, @@ -366,10 +366,10 @@ var cases = []*testIndexerCase{ Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) for _, v := range result.Hits { - assert.Equal(t, int64(0), data[v.ID].ProjectBoardID) + assert.Equal(t, int64(0), data[v.ID].ProjectColumnID) } assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { - return v.ProjectBoardID == 0 + return v.ProjectColumnID == 0 }), result.Total) }, }, @@ -706,7 +706,7 @@ func generateDefaultIndexerData() []*internal.IndexerData { NoLabel: len(labelIDs) == 0, MilestoneID: issueIndex % 4, ProjectID: issueIndex % 5, - ProjectBoardID: issueIndex % 6, + ProjectColumnID: issueIndex % 6, PosterID: id%10 + 1, // PosterID should not be 0 AssigneeID: issueIndex % 10, MentionIDs: mentionIDs, diff --git a/modules/indexer/issues/util.go b/modules/indexer/issues/util.go index 055bbbf43b213..e752ae6f2436c 100644 --- a/modules/indexer/issues/util.go +++ b/modules/indexer/issues/util.go @@ -105,7 +105,7 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD NoLabel: len(labels) == 0, MilestoneID: issue.MilestoneID, ProjectID: projectID, - ProjectBoardID: issue.ProjectColumnID(ctx), + ProjectColumnID: issue.ProjectColumnID(ctx), PosterID: issue.PosterID, AssigneeID: issue.AssigneeID, MentionIDs: mentionIDs, diff --git a/modules/metrics/collector.go b/modules/metrics/collector.go index e645adeaf1e94..be3d5649e1814 100755 --- a/modules/metrics/collector.go +++ b/modules/metrics/collector.go @@ -36,7 +36,7 @@ type Collector struct { Oauths *prometheus.Desc Organizations *prometheus.Desc Projects *prometheus.Desc - ProjectBoards *prometheus.Desc + ProjectColumns *prometheus.Desc PublicKeys *prometheus.Desc Releases *prometheus.Desc Repositories *prometheus.Desc @@ -146,7 +146,7 @@ func NewCollector() Collector { "Number of projects", nil, nil, ), - ProjectBoards: prometheus.NewDesc( + ProjectColumns: prometheus.NewDesc( namespace+"projects_boards", "Number of project boards", nil, nil, @@ -219,7 +219,7 @@ func (c Collector) Describe(ch chan<- *prometheus.Desc) { ch <- c.Oauths ch <- c.Organizations ch <- c.Projects - ch <- c.ProjectBoards + ch <- c.ProjectColumns ch <- c.PublicKeys ch <- c.Releases ch <- c.Repositories @@ -336,7 +336,7 @@ func (c Collector) Collect(ch chan<- prometheus.Metric) { float64(stats.Counter.Project), ) ch <- prometheus.MustNewConstMetric( - c.ProjectBoards, + c.ProjectColumns, prometheus.GaugeValue, float64(stats.Counter.ProjectColumn), ) diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 36f4f3ef50702..4da5375e9ac4e 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -315,7 +315,7 @@ func EditProjectPost(ctx *context.Context) { } } -// ViewProject renders the project board for a project +// ViewProject renders the project with board view for a project func ViewProject(ctx *context.Context) { project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) if err != nil { @@ -329,7 +329,7 @@ func ViewProject(ctx *context.Context) { boards, err := project.GetColumns(ctx) if err != nil { - ctx.ServerError("GetProjectBoards", err) + ctx.ServerError("GetProjectColumns", err) return } @@ -458,7 +458,7 @@ func UpdateIssueProject(ctx *context.Context) { ctx.JSONOK() } -// DeleteProjectColumn allows for the deletion of a project board +// DeleteProjectColumn allows for the deletion of a project column func DeleteProjectColumn(ctx *context.Context) { if ctx.Doer == nil { ctx.JSON(http.StatusForbidden, map[string]string{ @@ -475,32 +475,32 @@ func DeleteProjectColumn(ctx *context.Context) { pb, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) if err != nil { - ctx.ServerError("GetProjectBoard", err) + ctx.ServerError("GetProjectColumn", err) return } if pb.ProjectID != ctx.ParamsInt64(":id") { ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ - "message": fmt.Sprintf("ProjectBoard[%d] is not in Project[%d] as expected", pb.ID, project.ID), + "message": fmt.Sprintf("ProjectColumn[%d] is not in Project[%d] as expected", pb.ID, project.ID), }) return } if project.OwnerID != ctx.ContextUser.ID { ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ - "message": fmt.Sprintf("ProjectBoard[%d] is not in Owner[%d] as expected", pb.ID, ctx.ContextUser.ID), + "message": fmt.Sprintf("ProjectColumn[%d] is not in Owner[%d] as expected", pb.ID, ctx.ContextUser.ID), }) return } if err := project_model.DeleteColumnByID(ctx, ctx.ParamsInt64(":boardID")); err != nil { - ctx.ServerError("DeleteProjectBoardByID", err) + ctx.ServerError("DeleteProjectColumnByID", err) return } ctx.JSONOK() } -// AddColumnToProjectPost allows a new board to be added to a project. +// AddColumnToProjectPost allows a new column to be added to a project. func AddColumnToProjectPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.EditProjectColumnForm) @@ -516,15 +516,15 @@ func AddColumnToProjectPost(ctx *context.Context) { Color: form.Color, CreatorID: ctx.Doer.ID, }); err != nil { - ctx.ServerError("NewProjectBoard", err) + ctx.ServerError("NewProjectColumn", err) return } ctx.JSONOK() } -// CheckProjectBoardChangePermissions check permission -func CheckProjectBoardChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Column) { +// CheckProjectColumnChangePermissions check permission +func CheckProjectColumnChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Column) { if ctx.Doer == nil { ctx.JSON(http.StatusForbidden, map[string]string{ "message": "Only signed in users are allowed to perform this action.", @@ -538,62 +538,60 @@ func CheckProjectBoardChangePermissions(ctx *context.Context) (*project_model.Pr return nil, nil } - board, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) + column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) if err != nil { - ctx.ServerError("GetProjectBoard", err) + ctx.ServerError("GetProjectColumn", err) return nil, nil } - if board.ProjectID != ctx.ParamsInt64(":id") { + if column.ProjectID != ctx.ParamsInt64(":id") { ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ - "message": fmt.Sprintf("ProjectBoard[%d] is not in Project[%d] as expected", board.ID, project.ID), + "message": fmt.Sprintf("ProjectColumn[%d] is not in Project[%d] as expected", column.ID, project.ID), }) return nil, nil } if project.OwnerID != ctx.ContextUser.ID { ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ - "message": fmt.Sprintf("ProjectBoard[%d] is not in Repository[%d] as expected", board.ID, project.ID), + "message": fmt.Sprintf("ProjectColumn[%d] is not in Repository[%d] as expected", column.ID, project.ID), }) return nil, nil } - return project, board + return project, column } -// EditProjectColumn allows a project board's to be updated +// EditProjectColumn allows a project column's to be updated func EditProjectColumn(ctx *context.Context) { form := web.GetForm(ctx).(*forms.EditProjectColumnForm) - _, board := CheckProjectBoardChangePermissions(ctx) + _, column := CheckProjectColumnChangePermissions(ctx) if ctx.Written() { return } if form.Title != "" { - board.Title = form.Title + column.Title = form.Title } - - board.Color = form.Color - + column.Color = form.Color if form.Sorting != 0 { - board.Sorting = form.Sorting + column.Sorting = form.Sorting } - if err := project_model.UpdateColumn(ctx, board); err != nil { - ctx.ServerError("UpdateProjectBoard", err) + if err := project_model.UpdateColumn(ctx, column); err != nil { + ctx.ServerError("UpdateProjectColumn", err) return } ctx.JSONOK() } -// SetDefaultProjectBoard set default board for uncategorized issues/pulls -func SetDefaultProjectBoard(ctx *context.Context) { - project, board := CheckProjectBoardChangePermissions(ctx) +// SetDefaultProjectColumn set default column for uncategorized issues/pulls +func SetDefaultProjectColumn(ctx *context.Context) { + project, column := CheckProjectColumnChangePermissions(ctx) if ctx.Written() { return } - if err := project_model.SetDefaultColumn(ctx, project.ID, board.ID); err != nil { - ctx.ServerError("SetDefaultBoard", err) + if err := project_model.SetDefaultColumn(ctx, project.ID, column.ID); err != nil { + ctx.ServerError("SetDefaultColumn", err) return } @@ -619,13 +617,13 @@ func MoveIssues(ctx *context.Context) { return } - board, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) + column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) if err != nil { - ctx.NotFoundOrServerError("GetProjectBoard", project_model.IsErrProjectColumnNotExist, err) + ctx.NotFoundOrServerError("GetProjectColumn", project_model.IsErrProjectColumnNotExist, err) return } - if board.ProjectID != project.ID { + if column.ProjectID != project.ID { ctx.NotFound("BoardNotInProject", nil) return } @@ -671,8 +669,8 @@ func MoveIssues(ctx *context.Context) { } } - if err = project_model.MoveIssuesOnProjectColumn(ctx, board, sortedIssueIDs); err != nil { - ctx.ServerError("MoveIssuesOnProjectBoard", err) + if err = project_model.MoveIssuesOnProjectColumn(ctx, column, sortedIssueIDs); err != nil { + ctx.ServerError("MoveIssuesOnProjectColumn", err) return } diff --git a/routers/web/org/projects_test.go b/routers/web/org/projects_test.go index f4ccfe1c066ca..cd28c6025bb3f 100644 --- a/routers/web/org/projects_test.go +++ b/routers/web/org/projects_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestCheckProjectBoardChangePermissions(t *testing.T) { +func TestCheckProjectColumnChangePermissions(t *testing.T) { unittest.PrepareTestEnv(t) ctx, _ := contexttest.MockContext(t, "user2/-/projects/4/4") contexttest.LoadUser(t, ctx, 2) @@ -21,8 +21,8 @@ func TestCheckProjectBoardChangePermissions(t *testing.T) { ctx.SetParams(":id", "4") ctx.SetParams(":boardID", "4") - project, board := org.CheckProjectBoardChangePermissions(ctx) + project, column := org.CheckProjectColumnChangePermissions(ctx) assert.NotNil(t, project) - assert.NotNil(t, board) + assert.NotNil(t, column) assert.False(t, ctx.Written()) } diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 4225b89aa7aa6..a697de2d5dc69 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -293,7 +293,7 @@ func EditProjectPost(ctx *context.Context) { } } -// ViewProject renders the project board for a project +// ViewProject renders the project with board view func ViewProject(ctx *context.Context) { project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) if err != nil { @@ -311,7 +311,7 @@ func ViewProject(ctx *context.Context) { boards, err := project.GetColumns(ctx) if err != nil { - ctx.ServerError("GetProjectBoards", err) + ctx.ServerError("GetProjectColumns", err) return } @@ -406,8 +406,8 @@ func UpdateIssueProject(ctx *context.Context) { ctx.JSONOK() } -// DeleteProjectBoard allows for the deletion of a project board -func DeleteProjectBoard(ctx *context.Context) { +// DeleteProjectColumn allows for the deletion of a project column +func DeleteProjectColumn(ctx *context.Context) { if ctx.Doer == nil { ctx.JSON(http.StatusForbidden, map[string]string{ "message": "Only signed in users are allowed to perform this action.", @@ -434,33 +434,33 @@ func DeleteProjectBoard(ctx *context.Context) { pb, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) if err != nil { - ctx.ServerError("GetProjectBoard", err) + ctx.ServerError("GetProjectColumn", err) return } if pb.ProjectID != ctx.ParamsInt64(":id") { ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ - "message": fmt.Sprintf("ProjectBoard[%d] is not in Project[%d] as expected", pb.ID, project.ID), + "message": fmt.Sprintf("ProjectColumn[%d] is not in Project[%d] as expected", pb.ID, project.ID), }) return } if project.RepoID != ctx.Repo.Repository.ID { ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ - "message": fmt.Sprintf("ProjectBoard[%d] is not in Repository[%d] as expected", pb.ID, ctx.Repo.Repository.ID), + "message": fmt.Sprintf("ProjectColumn[%d] is not in Repository[%d] as expected", pb.ID, ctx.Repo.Repository.ID), }) return } if err := project_model.DeleteColumnByID(ctx, ctx.ParamsInt64(":boardID")); err != nil { - ctx.ServerError("DeleteProjectBoardByID", err) + ctx.ServerError("DeleteProjectColumnByID", err) return } ctx.JSONOK() } -// AddBoardToProjectPost allows a new board to be added to a project. -func AddBoardToProjectPost(ctx *context.Context) { +// AddColumnToProjectPost allows a new column to be added to a project. +func AddColumnToProjectPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.EditProjectColumnForm) if !ctx.Repo.IsOwner() && !ctx.Repo.IsAdmin() && !ctx.Repo.CanAccess(perm.AccessModeWrite, unit.TypeProjects) { ctx.JSON(http.StatusForbidden, map[string]string{ @@ -485,14 +485,14 @@ func AddBoardToProjectPost(ctx *context.Context) { Color: form.Color, CreatorID: ctx.Doer.ID, }); err != nil { - ctx.ServerError("NewProjectBoard", err) + ctx.ServerError("NewProjectColumn", err) return } ctx.JSONOK() } -func checkProjectBoardChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Column) { +func checkProjectColumnChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Column) { if ctx.Doer == nil { ctx.JSON(http.StatusForbidden, map[string]string{ "message": "Only signed in users are allowed to perform this action.", @@ -517,61 +517,59 @@ func checkProjectBoardChangePermissions(ctx *context.Context) (*project_model.Pr return nil, nil } - board, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) + column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) if err != nil { - ctx.ServerError("GetProjectBoard", err) + ctx.ServerError("GetProjectColumn", err) return nil, nil } - if board.ProjectID != ctx.ParamsInt64(":id") { + if column.ProjectID != ctx.ParamsInt64(":id") { ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ - "message": fmt.Sprintf("ProjectBoard[%d] is not in Project[%d] as expected", board.ID, project.ID), + "message": fmt.Sprintf("ProjectColumn[%d] is not in Project[%d] as expected", column.ID, project.ID), }) return nil, nil } if project.RepoID != ctx.Repo.Repository.ID { ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ - "message": fmt.Sprintf("ProjectBoard[%d] is not in Repository[%d] as expected", board.ID, ctx.Repo.Repository.ID), + "message": fmt.Sprintf("ProjectColumn[%d] is not in Repository[%d] as expected", column.ID, ctx.Repo.Repository.ID), }) return nil, nil } - return project, board + return project, column } -// EditProjectColumn allows a project board's to be updated +// EditProjectColumn allows a project column's to be updated func EditProjectColumn(ctx *context.Context) { form := web.GetForm(ctx).(*forms.EditProjectColumnForm) - _, board := checkProjectBoardChangePermissions(ctx) + _, column := checkProjectColumnChangePermissions(ctx) if ctx.Written() { return } if form.Title != "" { - board.Title = form.Title + column.Title = form.Title } - - board.Color = form.Color - + column.Color = form.Color if form.Sorting != 0 { - board.Sorting = form.Sorting + column.Sorting = form.Sorting } - if err := project_model.UpdateColumn(ctx, board); err != nil { - ctx.ServerError("UpdateProjectBoard", err) + if err := project_model.UpdateColumn(ctx, column); err != nil { + ctx.ServerError("UpdateProjectColumn", err) return } ctx.JSONOK() } -// SetDefaultProjectColumn set default board for uncategorized issues/pulls +// SetDefaultProjectColumn set default column for uncategorized issues/pulls func SetDefaultProjectColumn(ctx *context.Context) { - project, board := checkProjectBoardChangePermissions(ctx) + project, column := checkProjectColumnChangePermissions(ctx) if ctx.Written() { return } - if err := project_model.SetDefaultColumn(ctx, project.ID, board.ID); err != nil { + if err := project_model.SetDefaultColumn(ctx, project.ID, column.ID); err != nil { ctx.ServerError("SetDefaultBoard", err) return } @@ -609,7 +607,7 @@ func MoveIssues(ctx *context.Context) { return } - board, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) + column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) if err != nil { if project_model.IsErrProjectColumnNotExist(err) { ctx.NotFound("ProjectColumnNotExist", nil) @@ -619,7 +617,7 @@ func MoveIssues(ctx *context.Context) { return } - if board.ProjectID != project.ID { + if column.ProjectID != project.ID { ctx.NotFound("BoardNotInProject", nil) return } @@ -664,7 +662,7 @@ func MoveIssues(ctx *context.Context) { } } - if err = project_model.MoveIssuesOnProjectColumn(ctx, board, sortedIssueIDs); err != nil { + if err = project_model.MoveIssuesOnProjectColumn(ctx, column, sortedIssueIDs); err != nil { ctx.ServerError("MoveIssuesOnProjectColumn", err) return } diff --git a/routers/web/repo/projects_test.go b/routers/web/repo/projects_test.go index 479f8c55a22a6..11a17a2ccefc3 100644 --- a/routers/web/repo/projects_test.go +++ b/routers/web/repo/projects_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestCheckProjectBoardChangePermissions(t *testing.T) { +func TestCheckProjectColumnChangePermissions(t *testing.T) { unittest.PrepareTestEnv(t) ctx, _ := contexttest.MockContext(t, "user2/repo1/projects/1/2") contexttest.LoadUser(t, ctx, 2) @@ -20,8 +20,8 @@ func TestCheckProjectBoardChangePermissions(t *testing.T) { ctx.SetParams(":id", "1") ctx.SetParams(":boardID", "2") - project, board := checkProjectBoardChangePermissions(ctx) + project, column := checkProjectColumnChangePermissions(ctx) assert.NotNil(t, project) - assert.NotNil(t, board) + assert.NotNil(t, column) assert.False(t, ctx.Written()) } diff --git a/routers/web/web.go b/routers/web/web.go index 04255f26a7d02..12a86fd8a5e08 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1007,7 +1007,7 @@ func registerRoutes(m *web.Route) { m.Group("/{boardID}", func() { m.Put("", web.Bind(forms.EditProjectColumnForm{}), org.EditProjectColumn) m.Delete("", org.DeleteProjectColumn) - m.Post("/default", org.SetDefaultProjectBoard) + m.Post("/default", org.SetDefaultProjectColumn) m.Post("/move", org.MoveIssues) }) @@ -1336,7 +1336,7 @@ func registerRoutes(m *web.Route) { m.Get("/new", repo.RenderNewProject) m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost) m.Group("/{id}", func() { - m.Post("", web.Bind(forms.EditProjectColumnForm{}), repo.AddBoardToProjectPost) + m.Post("", web.Bind(forms.EditProjectColumnForm{}), repo.AddColumnToProjectPost) m.Post("/delete", repo.DeleteProject) m.Get("/edit", repo.RenderEditProject) @@ -1345,7 +1345,7 @@ func registerRoutes(m *web.Route) { m.Group("/{boardID}", func() { m.Put("", web.Bind(forms.EditProjectColumnForm{}), repo.EditProjectColumn) - m.Delete("", repo.DeleteProjectBoard) + m.Delete("", repo.DeleteProjectColumn) m.Post("/default", repo.SetDefaultProjectColumn) m.Post("/move", repo.MoveIssues) diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 7f3ee2ec5208b..9e653d79e2dba 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -522,7 +522,7 @@ type UserCreateProjectForm struct { UID int64 `binding:"Required"` } -// EditProjectColumnForm is a form for editing a project board +// EditProjectColumnForm is a form for editing a project column type EditProjectColumnForm struct { Title string `binding:"Required;MaxSize(100)"` Sorting int8 diff --git a/services/forms/user_form_hidden_comments.go b/services/forms/user_form_hidden_comments.go index c21fddf47864f..b9677c1800631 100644 --- a/services/forms/user_form_hidden_comments.go +++ b/services/forms/user_form_hidden_comments.go @@ -65,7 +65,7 @@ var hiddenCommentTypeGroups = hiddenCommentTypeGroupsType{ }, "project": { /*30*/ issues_model.CommentTypeProject, - /*31*/ issues_model.CommentTypeProjectBoard, + /*31*/ issues_model.CommentTypeProjectColumn, }, "issue_ref": { /*33*/ issues_model.CommentTypeChangeIssueRef, From ff6a72d1d6dc49b17db6a967edcc07cc774d81d9 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 30 Mar 2024 20:04:19 +0800 Subject: [PATCH 03/15] Fix bug --- models/project/project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/project/project.go b/models/project/project.go index a5bb4d3305613..69b5e7de162b5 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -241,7 +241,7 @@ func NewProject(ctx context.Context, p *Project) error { return util.NewInvalidArgumentErrorf("project type is not valid") } - return db.WithTx(ctx, func(tx context.Context) error { + return db.WithTx(ctx, func(ctx context.Context) error { if err := db.Insert(ctx, p); err != nil { return err } From 311a82ff720491671eb771b9de2a66962b91d604 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 30 Mar 2024 21:01:51 +0800 Subject: [PATCH 04/15] Rename boardview type -> template type --- models/project/board_view.go | 45 ---------------------------------- models/project/column.go | 10 ++++---- models/project/project.go | 30 +++++++++++------------ models/project/project_test.go | 14 +++++------ models/project/template.go | 45 ++++++++++++++++++++++++++++++++++ routers/web/org/projects.go | 14 +++++------ routers/web/repo/projects.go | 16 ++++++------ services/forms/repo_form.go | 4 +-- templates/projects/new.tmpl | 2 +- 9 files changed, 90 insertions(+), 90 deletions(-) delete mode 100644 models/project/board_view.go create mode 100644 models/project/template.go diff --git a/models/project/board_view.go b/models/project/board_view.go deleted file mode 100644 index 7c4e9ffc745cd..0000000000000 --- a/models/project/board_view.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package project - -type ( - // BoardViewType is used to represent a project column type - BoardViewType uint8 - - // BoardConfig is used to identify the type of board that is being created - BoardConfig struct { - BoardType BoardViewType - Translation string - } -) - -const ( - // BoardViewTypeNone is a project board view type that has no predefined columns - BoardViewTypeNone BoardViewType = iota - - // BoardViewTypeBasicKanban is a project board view type that has basic predefined columns - BoardViewTypeBasicKanban - - // BoardViewTypeBugTriage is a project board view type that has predefined columns suited to hunting down bugs - BoardViewTypeBugTriage -) - -// GetBoardViewConfig retrieves the types of configurations project boards could have -func GetBoardViewConfig() []BoardConfig { - return []BoardConfig{ - {BoardViewTypeNone, "repo.projects.type.none"}, - {BoardViewTypeBasicKanban, "repo.projects.type.basic_kanban"}, - {BoardViewTypeBugTriage, "repo.projects.type.bug_triage"}, - } -} - -// IsBoardViewTypeValid checks if the project board type is valid -func IsBoardViewTypeValid(p BoardViewType) bool { - switch p { - case BoardViewTypeNone, BoardViewTypeBasicKanban, BoardViewTypeBugTriage: - return true - default: - return false - } -} diff --git a/models/project/column.go b/models/project/column.go index 8a5eb78dc86cd..6636cb34d2db1 100644 --- a/models/project/column.go +++ b/models/project/column.go @@ -83,15 +83,15 @@ func IsCardTypeValid(p CardType) bool { } } -func createColumnsForProjectsBoradViewType(ctx context.Context, project *Project) error { +func createDefaultColumnsForProject(ctx context.Context, project *Project) error { var items []string - switch project.BoardViewType { - case BoardViewTypeBugTriage: + switch project.TemplateType { + case TemplateTypeBugTriage: items = setting.Project.ProjectBoardBugTriageType - case BoardViewTypeBasicKanban: + case TemplateTypeBasicKanban: items = setting.Project.ProjectBoardBasicKanbanType - case BoardViewTypeNone: + case TemplateTypeNone: fallthrough default: return nil diff --git a/models/project/project.go b/models/project/project.go index 69b5e7de162b5..3cf6abc661663 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -83,18 +83,18 @@ func (err ErrProjectColumnNotExist) Unwrap() error { // Project represents a project type Project struct { - ID int64 `xorm:"pk autoincr"` - Title string `xorm:"INDEX NOT NULL"` - Description string `xorm:"TEXT"` - OwnerID int64 `xorm:"INDEX"` - Owner *user_model.User `xorm:"-"` - RepoID int64 `xorm:"INDEX"` - Repo *repo_model.Repository `xorm:"-"` - CreatorID int64 `xorm:"NOT NULL"` - IsClosed bool `xorm:"INDEX"` - BoardViewType BoardViewType `xorm:"'board_type'"` - CardType CardType - Type Type + ID int64 `xorm:"pk autoincr"` + Title string `xorm:"INDEX NOT NULL"` + Description string `xorm:"TEXT"` + OwnerID int64 `xorm:"INDEX"` + Owner *user_model.User `xorm:"-"` + RepoID int64 `xorm:"INDEX"` + Repo *repo_model.Repository `xorm:"-"` + CreatorID int64 `xorm:"NOT NULL"` + IsClosed bool `xorm:"INDEX"` + TemplateType TemplateType `xorm:"'board_type'"` + CardType CardType + Type Type RenderedContent template.HTML `xorm:"-"` @@ -229,8 +229,8 @@ func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy { // NewProject creates a new Project func NewProject(ctx context.Context, p *Project) error { - if !IsBoardViewTypeValid(p.BoardViewType) { - p.BoardViewType = BoardViewTypeNone + if !IsTemplateTypeValid(p.TemplateType) { + p.TemplateType = TemplateTypeNone } if !IsCardTypeValid(p.CardType) { @@ -252,7 +252,7 @@ func NewProject(ctx context.Context, p *Project) error { } } - return createColumnsForProjectsBoradViewType(ctx, p) + return createDefaultColumnsForProject(ctx, p) }) } diff --git a/models/project/project_test.go b/models/project/project_test.go index 49188777e3ef9..dd421b4659999 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -51,13 +51,13 @@ func TestProject(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) project := &Project{ - Type: TypeRepository, - BoardViewType: BoardViewTypeBasicKanban, - CardType: CardTypeTextOnly, - Title: "New Project", - RepoID: 1, - CreatedUnix: timeutil.TimeStampNow(), - CreatorID: 2, + Type: TypeRepository, + TemplateType: TemplateTypeBasicKanban, + CardType: CardTypeTextOnly, + Title: "New Project", + RepoID: 1, + CreatedUnix: timeutil.TimeStampNow(), + CreatorID: 2, } assert.NoError(t, NewProject(db.DefaultContext, project)) diff --git a/models/project/template.go b/models/project/template.go new file mode 100644 index 0000000000000..9dcf32616d348 --- /dev/null +++ b/models/project/template.go @@ -0,0 +1,45 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package project + +type ( + // TemplateType is used to represent a project template type + TemplateType uint8 + + // TemplateConfig is used to identify the template type of project that is being created + TemplateConfig struct { + TemplateType TemplateType + Translation string + } +) + +const ( + // TemplateTypeNone is a project template type that has no predefined columns + TemplateTypeNone TemplateType = iota + + // TemplateTypeBasicKanban is a project template type that has basic predefined columns + TemplateTypeBasicKanban + + // TemplateTypeBugTriage is a project template type that has predefined columns suited to hunting down bugs + TemplateTypeBugTriage +) + +// GetTemplateConfigs retrieves the template configs of configurations project boards could have +func GetTemplateConfigs() []TemplateConfig { + return []TemplateConfig{ + {TemplateTypeNone, "repo.projects.type.none"}, + {TemplateTypeBasicKanban, "repo.projects.type.basic_kanban"}, + {TemplateTypeBugTriage, "repo.projects.type.bug_triage"}, + } +} + +// IsTemplateTypeValid checks if the project board type is valid +func IsTemplateTypeValid(p TemplateType) bool { + switch p { + case TemplateTypeNone, TemplateTypeBasicKanban, TemplateTypeBugTriage: + return true + default: + return false + } +} diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 4da5375e9ac4e..dbd225d1d986d 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -141,7 +141,7 @@ func canWriteProjects(ctx *context.Context) bool { // RenderNewProject render creating a project page func RenderNewProject(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.new") - ctx.Data["BoardTypes"] = project_model.GetBoardViewConfig() + ctx.Data["TemplateConfigs"] = project_model.GetTemplateConfigs() ctx.Data["CardTypes"] = project_model.GetCardConfig() ctx.Data["CanWriteProjects"] = canWriteProjects(ctx) ctx.Data["PageIsViewProjects"] = true @@ -170,12 +170,12 @@ func NewProjectPost(ctx *context.Context) { } newProject := project_model.Project{ - OwnerID: ctx.ContextUser.ID, - Title: form.Title, - Description: form.Content, - CreatorID: ctx.Doer.ID, - BoardViewType: form.BoardType, - CardType: form.CardType, + OwnerID: ctx.ContextUser.ID, + Title: form.Title, + Description: form.Content, + CreatorID: ctx.Doer.ID, + TemplateType: form.BoardType, + CardType: form.CardType, } if ctx.ContextUser.IsOrganization() { diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index a697de2d5dc69..9da35fb50c687 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -132,7 +132,7 @@ func Projects(ctx *context.Context) { // RenderNewProject render creating a project page func RenderNewProject(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.new") - ctx.Data["BoardTypes"] = project_model.GetBoardViewConfig() + ctx.Data["TemplateConfigs"] = project_model.GetTemplateConfigs() ctx.Data["CardTypes"] = project_model.GetCardConfig() ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.Data["CancelLink"] = ctx.Repo.Repository.Link() + "/projects" @@ -150,13 +150,13 @@ func NewProjectPost(ctx *context.Context) { } if err := project_model.NewProject(ctx, &project_model.Project{ - RepoID: ctx.Repo.Repository.ID, - Title: form.Title, - Description: form.Content, - CreatorID: ctx.Doer.ID, - BoardViewType: form.BoardType, - CardType: form.CardType, - Type: project_model.TypeRepository, + RepoID: ctx.Repo.Repository.ID, + Title: form.Title, + Description: form.Content, + CreatorID: ctx.Doer.ID, + TemplateType: form.BoardType, + CardType: form.CardType, + Type: project_model.TypeRepository, }); err != nil { ctx.ServerError("NewProject", err) return diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 9e653d79e2dba..2c1e708cba8ee 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -508,7 +508,7 @@ func (i IssueLockForm) HasValidReason() bool { type CreateProjectForm struct { Title string `binding:"Required;MaxSize(100)"` Content string - BoardType project_model.BoardViewType + BoardType project_model.TemplateType // NOTE: don't change the name except you know what you are doing CardType project_model.CardType } @@ -517,7 +517,7 @@ type CreateProjectForm struct { type UserCreateProjectForm struct { Title string `binding:"Required;MaxSize(100)"` Content string - BoardType project_model.BoardViewType + BoardType project_model.TemplateType CardType project_model.CardType UID int64 `binding:"Required"` } diff --git a/templates/projects/new.tmpl b/templates/projects/new.tmpl index 92ee36c1c44fd..dded2790177c1 100644 --- a/templates/projects/new.tmpl +++ b/templates/projects/new.tmpl @@ -28,7 +28,7 @@
{{ctx.Locale.Tr "repo.projects.template.desc_helper"}}
From f3554db10a4706752bc9b2d7467f65c81ce33be0 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 31 Mar 2024 21:58:38 +0800 Subject: [PATCH 05/15] Rename boardtype -> templatetype on template and form --- routers/web/org/projects.go | 2 +- routers/web/repo/projects.go | 2 +- services/forms/repo_form.go | 18 ++++-------------- templates/projects/new.tmpl | 4 ++-- 4 files changed, 8 insertions(+), 18 deletions(-) diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index dbd225d1d986d..b5c401046f611 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -174,7 +174,7 @@ func NewProjectPost(ctx *context.Context) { Title: form.Title, Description: form.Content, CreatorID: ctx.Doer.ID, - TemplateType: form.BoardType, + TemplateType: form.TemplateType, CardType: form.CardType, } diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 9da35fb50c687..fb83a094a01dd 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -154,7 +154,7 @@ func NewProjectPost(ctx *context.Context) { Title: form.Title, Description: form.Content, CreatorID: ctx.Doer.ID, - TemplateType: form.BoardType, + TemplateType: form.TemplateType, CardType: form.CardType, Type: project_model.TypeRepository, }); err != nil { diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 2c1e708cba8ee..c7f8845467e9e 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -506,20 +506,10 @@ func (i IssueLockForm) HasValidReason() bool { // CreateProjectForm form for creating a project type CreateProjectForm struct { - Title string `binding:"Required;MaxSize(100)"` - Content string - BoardType project_model.TemplateType // NOTE: don't change the name except you know what you are doing - CardType project_model.CardType -} - -// UserCreateProjectForm is a from for creating an individual or organization -// form. -type UserCreateProjectForm struct { - Title string `binding:"Required;MaxSize(100)"` - Content string - BoardType project_model.TemplateType - CardType project_model.CardType - UID int64 `binding:"Required"` + Title string `binding:"Required;MaxSize(100)"` + Content string + TemplateType project_model.TemplateType // NOTE: don't change the name except you know what you are doing + CardType project_model.CardType } // EditProjectColumnForm is a form for editing a project column diff --git a/templates/projects/new.tmpl b/templates/projects/new.tmpl index dded2790177c1..bd173b54bcec9 100644 --- a/templates/projects/new.tmpl +++ b/templates/projects/new.tmpl @@ -25,11 +25,11 @@
From 37eb84a0e0059c69be81643a7ccd20c6a0d728fb Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 31 Mar 2024 22:00:05 +0800 Subject: [PATCH 06/15] remove unnecessary comment --- services/forms/repo_form.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index c7f8845467e9e..67b5c687efa45 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -508,7 +508,7 @@ func (i IssueLockForm) HasValidReason() bool { type CreateProjectForm struct { Title string `binding:"Required;MaxSize(100)"` Content string - TemplateType project_model.TemplateType // NOTE: don't change the name except you know what you are doing + TemplateType project_model.TemplateType CardType project_model.CardType } From d876464286d50067f3de5349d89b52cf6db0f74a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 16 Apr 2024 23:51:56 +0800 Subject: [PATCH 07/15] more renames --- models/project/column.go | 24 ++++++++++++------------ models/project/column_test.go | 2 +- models/project/issue.go | 4 ++-- models/project/project.go | 2 +- routers/web/org/projects.go | 8 ++++---- routers/web/org/projects_test.go | 2 +- routers/web/repo/projects.go | 22 +++++++++++----------- routers/web/repo/projects_test.go | 2 +- routers/web/web.go | 4 ++-- 9 files changed, 35 insertions(+), 35 deletions(-) diff --git a/models/project/column.go b/models/project/column.go index 6636cb34d2db1..6e0ecdc4a9b82 100644 --- a/models/project/column.go +++ b/models/project/column.go @@ -56,17 +56,17 @@ func (Column) TableName() string { } // NumIssues return counter of all issues assigned to the column -func (b *Column) NumIssues(ctx context.Context) int { - c, err := db.GetEngine(ctx).Table("project_issue"). - Where("project_id=?", b.ProjectID). - And("project_board_id=?", b.ID). +func (c *Column) NumIssues(ctx context.Context) int { + total, err := db.GetEngine(ctx).Table("project_issue"). + Where("project_id=?", c.ProjectID). + And("project_board_id=?", c.ID). GroupBy("issue_id"). Cols("issue_id"). Count() if err != nil { return 0 } - return int(c) + return int(total) } func init() { @@ -113,9 +113,9 @@ func createDefaultColumnsForProject(ctx context.Context, project *Project) error return nil } - boards := make([]Column, 0, len(items)) + columns := make([]Column, 0, len(items)) for _, v := range items { - boards = append(boards, Column{ + columns = append(columns, Column{ CreatedUnix: timeutil.TimeStampNow(), CreatorID: project.CreatorID, Title: v, @@ -123,7 +123,7 @@ func createDefaultColumnsForProject(ctx context.Context, project *Project) error }) } - return db.Insert(ctx, boards) + return db.Insert(ctx, columns) }) } @@ -210,18 +210,18 @@ func UpdateColumn(ctx context.Context, column *Column) error { // GetColumns fetches all boards related to a project func (p *Project) GetColumns(ctx context.Context) (ColumnList, error) { - boards := make([]*Column, 0, 5) + columns := make([]*Column, 0, 5) - if err := db.GetEngine(ctx).Where("project_id=? AND `default`=?", p.ID, false).OrderBy("sorting").Find(&boards); err != nil { + if err := db.GetEngine(ctx).Where("project_id=? AND `default`=?", p.ID, false).OrderBy("sorting").Find(&columns); err != nil { return nil, err } - defaultB, err := p.getDefaultColumn(ctx) + defaultCol, err := p.getDefaultColumn(ctx) if err != nil { return nil, err } - return append([]*Column{defaultB}, boards...), nil + return append([]*Column{defaultCol}, columns...), nil } // getDefaultColumn return default column and ensure only one exists diff --git a/models/project/column_test.go b/models/project/column_test.go index 1babaea4280d2..5001513984ad6 100644 --- a/models/project/column_test.go +++ b/models/project/column_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestGetDefaultcolumn(t *testing.T) { +func TestGetDefaultColumn(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) projectWithoutDefault, err := GetProjectByID(db.DefaultContext, 5) diff --git a/models/project/issue.go b/models/project/issue.go index 63352ed96c8fb..b3bea4330f972 100644 --- a/models/project/issue.go +++ b/models/project/issue.go @@ -102,7 +102,7 @@ func MoveIssuesOnProjectColumn(ctx context.Context, column *Column, sortedIssueI }) } -func (b *Column) removeIssues(ctx context.Context) error { - _, err := db.GetEngine(ctx).Exec("UPDATE `project_issue` SET project_board_id = 0 WHERE project_board_id = ? ", b.ID) +func (c *Column) removeIssues(ctx context.Context) error { + _, err := db.GetEngine(ctx).Exec("UPDATE `project_issue` SET project_board_id = 0 WHERE project_board_id = ? ", c.ID) return err } diff --git a/models/project/project.go b/models/project/project.go index 3cf6abc661663..675cd1e861eb9 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -92,7 +92,7 @@ type Project struct { Repo *repo_model.Repository `xorm:"-"` CreatorID int64 `xorm:"NOT NULL"` IsClosed bool `xorm:"INDEX"` - TemplateType TemplateType `xorm:"'board_type'"` + TemplateType TemplateType `xorm:"'board_type'"` // TODO: rename the column to template_type CardType CardType Type Type diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index b5c401046f611..1d97a9b64e024 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -473,7 +473,7 @@ func DeleteProjectColumn(ctx *context.Context) { return } - pb, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) + pb, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":columnID")) if err != nil { ctx.ServerError("GetProjectColumn", err) return @@ -492,7 +492,7 @@ func DeleteProjectColumn(ctx *context.Context) { return } - if err := project_model.DeleteColumnByID(ctx, ctx.ParamsInt64(":boardID")); err != nil { + if err := project_model.DeleteColumnByID(ctx, ctx.ParamsInt64(":columnID")); err != nil { ctx.ServerError("DeleteProjectColumnByID", err) return } @@ -538,7 +538,7 @@ func CheckProjectColumnChangePermissions(ctx *context.Context) (*project_model.P return nil, nil } - column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) + column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":columnID")) if err != nil { ctx.ServerError("GetProjectColumn", err) return nil, nil @@ -617,7 +617,7 @@ func MoveIssues(ctx *context.Context) { return } - column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) + column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":columnID")) if err != nil { ctx.NotFoundOrServerError("GetProjectColumn", project_model.IsErrProjectColumnNotExist, err) return diff --git a/routers/web/org/projects_test.go b/routers/web/org/projects_test.go index cd28c6025bb3f..ab419cc8789bc 100644 --- a/routers/web/org/projects_test.go +++ b/routers/web/org/projects_test.go @@ -19,7 +19,7 @@ func TestCheckProjectColumnChangePermissions(t *testing.T) { contexttest.LoadUser(t, ctx, 2) ctx.ContextUser = ctx.Doer // user2 ctx.SetParams(":id", "4") - ctx.SetParams(":boardID", "4") + ctx.SetParams(":columnID", "4") project, column := org.CheckProjectColumnChangePermissions(ctx) assert.NotNil(t, project) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index fb83a094a01dd..7ef43c3350b55 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -36,7 +36,7 @@ const ( // MustEnableRepoProjects check if repo projects are enabled in settings func MustEnableRepoProjects(ctx *context.Context) { if unit.TypeProjects.UnitGlobalDisabled() { - ctx.NotFound("EnableKanbanBoard", nil) + ctx.NotFound("EnableRepoProjects", nil) return } @@ -309,15 +309,15 @@ func ViewProject(ctx *context.Context) { return } - boards, err := project.GetColumns(ctx) + columns, err := project.GetColumns(ctx) if err != nil { ctx.ServerError("GetProjectColumns", err) return } - issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, boards) + issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, columns) if err != nil { - ctx.ServerError("LoadIssuesOfBoards", err) + ctx.ServerError("LoadIssuesOfColumns", err) return } @@ -372,7 +372,7 @@ func ViewProject(ctx *context.Context) { ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.Data["Project"] = project ctx.Data["IssuesMap"] = issuesMap - ctx.Data["Columns"] = boards // TODO: rename boards to columns in backend + ctx.Data["Columns"] = columns ctx.HTML(http.StatusOK, tplProjectsView) } @@ -432,7 +432,7 @@ func DeleteProjectColumn(ctx *context.Context) { return } - pb, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) + pb, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":columnID")) if err != nil { ctx.ServerError("GetProjectColumn", err) return @@ -451,7 +451,7 @@ func DeleteProjectColumn(ctx *context.Context) { return } - if err := project_model.DeleteColumnByID(ctx, ctx.ParamsInt64(":boardID")); err != nil { + if err := project_model.DeleteColumnByID(ctx, ctx.ParamsInt64(":columnID")); err != nil { ctx.ServerError("DeleteProjectColumnByID", err) return } @@ -517,7 +517,7 @@ func checkProjectColumnChangePermissions(ctx *context.Context) (*project_model.P return nil, nil } - column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) + column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":columnID")) if err != nil { ctx.ServerError("GetProjectColumn", err) return nil, nil @@ -570,7 +570,7 @@ func SetDefaultProjectColumn(ctx *context.Context) { } if err := project_model.SetDefaultColumn(ctx, project.ID, column.ID); err != nil { - ctx.ServerError("SetDefaultBoard", err) + ctx.ServerError("SetDefaultColumn", err) return } @@ -607,7 +607,7 @@ func MoveIssues(ctx *context.Context) { return } - column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":boardID")) + column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":columnID")) if err != nil { if project_model.IsErrProjectColumnNotExist(err) { ctx.NotFound("ProjectColumnNotExist", nil) @@ -618,7 +618,7 @@ func MoveIssues(ctx *context.Context) { } if column.ProjectID != project.ID { - ctx.NotFound("BoardNotInProject", nil) + ctx.NotFound("ColumnNotInProject", nil) return } diff --git a/routers/web/repo/projects_test.go b/routers/web/repo/projects_test.go index 11a17a2ccefc3..d61230a57e6be 100644 --- a/routers/web/repo/projects_test.go +++ b/routers/web/repo/projects_test.go @@ -18,7 +18,7 @@ func TestCheckProjectColumnChangePermissions(t *testing.T) { contexttest.LoadUser(t, ctx, 2) contexttest.LoadRepo(t, ctx, 1) ctx.SetParams(":id", "1") - ctx.SetParams(":boardID", "2") + ctx.SetParams(":columnID", "2") project, column := checkProjectColumnChangePermissions(ctx) assert.NotNil(t, project) diff --git a/routers/web/web.go b/routers/web/web.go index 12a86fd8a5e08..243efc3888ac7 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1004,7 +1004,7 @@ func registerRoutes(m *web.Route) { m.Post("/edit", web.Bind(forms.CreateProjectForm{}), org.EditProjectPost) m.Post("/{action:open|close}", org.ChangeProjectStatus) - m.Group("/{boardID}", func() { + m.Group("/{columnID}", func() { m.Put("", web.Bind(forms.EditProjectColumnForm{}), org.EditProjectColumn) m.Delete("", org.DeleteProjectColumn) m.Post("/default", org.SetDefaultProjectColumn) @@ -1343,7 +1343,7 @@ func registerRoutes(m *web.Route) { m.Post("/edit", web.Bind(forms.CreateProjectForm{}), repo.EditProjectPost) m.Post("/{action:open|close}", repo.ChangeProjectStatus) - m.Group("/{boardID}", func() { + m.Group("/{columnID}", func() { m.Put("", web.Bind(forms.EditProjectColumnForm{}), repo.EditProjectColumn) m.Delete("", repo.DeleteProjectColumn) m.Post("/default", repo.SetDefaultProjectColumn) From e5cab92f60bad686fd155d14f13d995a94d0c150 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 17 Apr 2024 14:05:58 +0800 Subject: [PATCH 08/15] more renames --- models/project/column.go | 4 ++-- models/project/template.go | 2 +- routers/web/org/projects.go | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/models/project/column.go b/models/project/column.go index 6e0ecdc4a9b82..eb9a4b186c929 100644 --- a/models/project/column.go +++ b/models/project/column.go @@ -52,7 +52,7 @@ type Column struct { // TableName return the real table name func (Column) TableName() string { - return "project_board" // FIXME: the legacy table name should be project_column + return "project_board" // TODO: the legacy table name should be project_column } // NumIssues return counter of all issues assigned to the column @@ -155,7 +155,7 @@ func deleteColumnByID(ctx context.Context, columnID int64) error { } if column.Default { - return fmt.Errorf("deleteBoardByID: cannot delete default column") + return fmt.Errorf("deleteColumnByID: cannot delete default column") } if err = column.removeIssues(ctx); err != nil { diff --git a/models/project/template.go b/models/project/template.go index 9dcf32616d348..f3fb4f4a77a4c 100644 --- a/models/project/template.go +++ b/models/project/template.go @@ -34,7 +34,7 @@ func GetTemplateConfigs() []TemplateConfig { } } -// IsTemplateTypeValid checks if the project board type is valid +// IsTemplateTypeValid checks if the project template type is valid func IsTemplateTypeValid(p TemplateType) bool { switch p { case TemplateTypeNone, TemplateTypeBasicKanban, TemplateTypeBugTriage: diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index e2064c179a09c..9c8e826440a3d 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -327,15 +327,15 @@ func ViewProject(ctx *context.Context) { return } - boards, err := project.GetColumns(ctx) + columns, err := project.GetColumns(ctx) if err != nil { ctx.ServerError("GetProjectColumns", err) return } - issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, boards) + issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, columns) if err != nil { - ctx.ServerError("LoadIssuesOfBoards", err) + ctx.ServerError("LoadIssuesOfColumns", err) return } @@ -378,7 +378,7 @@ func ViewProject(ctx *context.Context) { ctx.Data["CanWriteProjects"] = canWriteProjects(ctx) ctx.Data["Project"] = project ctx.Data["IssuesMap"] = issuesMap - ctx.Data["Columns"] = boards // TODO: rename boards to columns in backend + ctx.Data["Columns"] = columns shared_user.RenderUserHeader(ctx) err = shared_user.LoadHeaderCount(ctx) @@ -624,7 +624,7 @@ func MoveIssues(ctx *context.Context) { } if column.ProjectID != project.ID { - ctx.NotFound("BoardNotInProject", nil) + ctx.NotFound("ColumnNotInProject", nil) return } From e6a7fd3b6ab81d10f5cddc983d9ecfaaa1c67db3 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 18 Apr 2024 15:07:06 +0800 Subject: [PATCH 09/15] Fix comment of boards --- models/project/template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/project/template.go b/models/project/template.go index f3fb4f4a77a4c..06d5d2af1482b 100644 --- a/models/project/template.go +++ b/models/project/template.go @@ -25,7 +25,7 @@ const ( TemplateTypeBugTriage ) -// GetTemplateConfigs retrieves the template configs of configurations project boards could have +// GetTemplateConfigs retrieves the template configs of configurations project columns could have func GetTemplateConfigs() []TemplateConfig { return []TemplateConfig{ {TemplateTypeNone, "repo.projects.type.none"}, From 925dcf8f4732e397d1b8b0bfd9aa6cb2a0f834f6 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 18 Apr 2024 16:38:57 +0800 Subject: [PATCH 10/15] Update models/project/column.go Co-authored-by: yp05327 <576951401@qq.com> --- models/project/column.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/project/column.go b/models/project/column.go index eb9a4b186c929..6e579706db9e2 100644 --- a/models/project/column.go +++ b/models/project/column.go @@ -208,7 +208,7 @@ func UpdateColumn(ctx context.Context, column *Column) error { return err } -// GetColumns fetches all boards related to a project +// GetColumns fetches all columns related to a project func (p *Project) GetColumns(ctx context.Context) (ColumnList, error) { columns := make([]*Column, 0, 5) From 78f7639c5d0a726704045c525ea1da2181ab3a1d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 18 Apr 2024 16:45:09 +0800 Subject: [PATCH 11/15] Update documentation about boards --- docs/content/administration/config-cheat-sheet.en-us.md | 2 +- docs/content/index.en-us.md | 2 +- docs/content/installation/comparison.en-us.md | 2 +- docs/content/usage/permissions.en-us.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md index ff8bcb066c41d..8c720032a53fc 100644 --- a/docs/content/administration/config-cheat-sheet.en-us.md +++ b/docs/content/administration/config-cheat-sheet.en-us.md @@ -829,7 +829,7 @@ and ## Project (`project`) -Default templates for project boards: +Default templates for project board view: - `PROJECT_BOARD_BASIC_KANBAN_TYPE`: **To Do, In Progress, Done** - `PROJECT_BOARD_BUG_TRIAGE_TYPE`: **Needs Triage, High Priority, Low Priority, Closed** diff --git a/docs/content/index.en-us.md b/docs/content/index.en-us.md index 170bf26f71618..f9e6df8c1e75a 100644 --- a/docs/content/index.en-us.md +++ b/docs/content/index.en-us.md @@ -37,7 +37,7 @@ You can try it out using [the online demo](https://try.gitea.io/). - CI/CD: Gitea Actions supports CI/CD functionality, compatible with GitHub Actions. Users can write workflows in familiar YAML format and reuse a variety of existing Actions plugins. Actions plugins support downloading from any Git website. -- Project Management: Gitea tracks project requirements, features, and bugs through boards and issues. Issues support features like branches, tags, milestones, assignments, time tracking, due dates, dependencies, and more. +- Project Management: Gitea tracks project requirements, features, and bugs through columns and issues. Issues support features like branches, tags, milestones, assignments, time tracking, due dates, dependencies, and more. - Artifact Repository: Gitea supports over 20 different types of public or private software package management, including Cargo, Chef, Composer, Conan, Conda, Container, Helm, Maven, npm, NuGet, Pub, PyPI, RubyGems, Vagrant, and more. diff --git a/docs/content/installation/comparison.en-us.md b/docs/content/installation/comparison.en-us.md index 3fb6561f3156f..fdb8c3bcde13b 100644 --- a/docs/content/installation/comparison.en-us.md +++ b/docs/content/installation/comparison.en-us.md @@ -104,7 +104,7 @@ _Symbols used in table:_ | Comment reactions | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | ✘ | | Lock Discussion | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | ✘ | | Batch issue handling | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | ✘ | -| Issue Boards (Kanban) | [/](https://github.com/go-gitea/gitea/issues/14710) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ | ✘ | +| Projects | [/](https://github.com/go-gitea/gitea/issues/14710) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ | ✘ | | Create branch from issue | [✘](https://github.com/go-gitea/gitea/issues/20226) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ | ✘ | | Convert comment to new issue | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | ✘ | | Issue search | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ | diff --git a/docs/content/usage/permissions.en-us.md b/docs/content/usage/permissions.en-us.md index 1e0c6c0bb17cf..e4bef138ab0e4 100644 --- a/docs/content/usage/permissions.en-us.md +++ b/docs/content/usage/permissions.en-us.md @@ -48,7 +48,7 @@ With different permissions, people could do different things with these units. | Wiki | View wiki pages. Clone the wiki repository. | Create/Edit wiki pages, push | - | | ExternalWiki | Link to an external wiki | - | - | | ExternalTracker | Link to an external issue tracker | - | - | -| Projects | View the boards | Change issues across boards | - | +| Projects | View the columns of projects | Change issues across columns | - | | Packages | View the packages | Upload/Delete packages | - | | Actions | View the Actions logs | Approve / Cancel / Restart | - | | Settings | - | - | Manage the repository | From b0d4084ab1d8d45e52b1273d508f127f666c3823 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 19 Apr 2024 19:41:42 +0800 Subject: [PATCH 12/15] More renames --- modules/metrics/collector.go | 4 ++-- options/locale/locale_en-US.ini | 4 ++-- routers/web/org/projects.go | 2 +- templates/repo/header.tmpl | 2 +- templates/repo/issue/filter_actions.tmpl | 2 +- templates/repo/settings/options.tmpl | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/metrics/collector.go b/modules/metrics/collector.go index be3d5649e1814..230260ff94896 100755 --- a/modules/metrics/collector.go +++ b/modules/metrics/collector.go @@ -147,8 +147,8 @@ func NewCollector() Collector { nil, nil, ), ProjectColumns: prometheus.NewDesc( - namespace+"projects_boards", - "Number of project boards", + namespace+"projects_boards", // TODO: change the key name will affect the consume's result history + "Number of project columns", nil, nil, ), PublicKeys: prometheus.NewDesc( diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index c602aba53dc5f..587853beb0847 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1213,7 +1213,7 @@ branches = Branches tags = Tags issues = Issues pulls = Pull Requests -project_board = Projects +projects = Projects packages = Packages actions = Actions labels = Labels @@ -1377,7 +1377,7 @@ ext_issues = Access to External Issues ext_issues.desc = Link to an external issue tracker. projects = Projects -projects.desc = Manage issues and pulls in project boards. +projects.desc = Manage issues and pulls in projects. projects.description = Description (optional) projects.description_placeholder = Description projects.create = Create Project diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index ae8adc98f7461..c033812bc5bc5 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -35,7 +35,7 @@ const ( // MustEnableProjects check if projects are enabled in settings func MustEnableProjects(ctx *context.Context) { if unit.TypeProjects.UnitGlobalDisabled() { - ctx.NotFound("EnableKanbanBoard", nil) + ctx.NotFound("EnableProjects", nil) return } } diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 775aa300639f4..7773a6dc6d3e7 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -179,7 +179,7 @@ {{$projectsUnit := .Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeProjects}} {{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead ctx.Consts.RepoUnitTypeProjects) ($projectsUnit.ProjectsConfig.IsProjectsAllowed "repo")}} - {{svg "octicon-project"}} {{ctx.Locale.Tr "repo.project_board"}} + {{svg "octicon-project"}} {{ctx.Locale.Tr "repo.projects"}} {{if .Repository.NumOpenProjects}} {{CountFmt .Repository.NumOpenProjects}} {{end}} diff --git a/templates/repo/issue/filter_actions.tmpl b/templates/repo/issue/filter_actions.tmpl index f23ca36d78c8b..18986db773b50 100644 --- a/templates/repo/issue/filter_actions.tmpl +++ b/templates/repo/issue/filter_actions.tmpl @@ -71,7 +71,7 @@