From e5160185ed65fd1c2bcb2fc7dc7e0b5514ddb299 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Wed, 27 Mar 2024 21:54:32 +0100 Subject: [PATCH] Add default board to new projects, remove uncategorized pseudo-board (#29874) On creation of an empty project (no template) a default board will be created instead of falling back to the uneditable pseudo-board. Every project now has to have exactly one default boards. As a consequence, you cannot unset a board as default, instead you have to set another board as default. Existing projects will be modified using a cron job, additionally this check will run every midnight by default. Deleting the default board is not allowed, you have to set another board as default to do it. Fixes #29873 Fixes #14679 along the way Fixes #29853 Co-authored-by: delvh --- models/fixtures/project.yml | 24 ++++ models/fixtures/project_board.yml | 46 ++++++++ models/issues/issue_project.go | 19 ++- .../project.yml | 23 ++++ .../project_board.yml | 26 +++++ models/migrations/migrations.go | 2 + models/migrations/v1_22/v292.go | 85 ++++++++++++++ models/migrations/v1_22/v292_test.go | 44 +++++++ models/project/board.go | 76 ++++++++---- models/project/board_test.go | 40 +++++++ models/project/project_test.go | 12 +- options/locale/locale_en-US.ini | 5 +- routers/web/org/projects.go | 108 +++--------------- routers/web/repo/projects.go | 52 ++------- routers/web/web.go | 2 - templates/projects/view.tmpl | 31 ++--- web_src/js/features/repo-projects.js | 1 - 17 files changed, 400 insertions(+), 196 deletions(-) create mode 100644 models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project.yml create mode 100644 models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml create mode 100644 models/migrations/v1_22/v292.go create mode 100644 models/migrations/v1_22/v292_test.go create mode 100644 models/project/board_test.go diff --git a/models/fixtures/project.yml b/models/fixtures/project.yml index 1bf8030f6a..44d87bce04 100644 --- a/models/fixtures/project.yml +++ b/models/fixtures/project.yml @@ -45,3 +45,27 @@ type: 2 created_unix: 1688973000 updated_unix: 1688973000 + +- + id: 5 + title: project without default column + owner_id: 2 + repo_id: 0 + is_closed: false + creator_id: 2 + board_type: 1 + type: 2 + created_unix: 1688973000 + updated_unix: 1688973000 + +- + id: 6 + title: project with multiple default columns + owner_id: 2 + repo_id: 0 + is_closed: false + creator_id: 2 + board_type: 1 + type: 2 + created_unix: 1688973000 + updated_unix: 1688973000 diff --git a/models/fixtures/project_board.yml b/models/fixtures/project_board.yml index dc4f9cf565..3293dea6ed 100644 --- a/models/fixtures/project_board.yml +++ b/models/fixtures/project_board.yml @@ -3,6 +3,7 @@ project_id: 1 title: To Do creator_id: 2 + default: true created_unix: 1588117528 updated_unix: 1588117528 @@ -29,3 +30,48 @@ creator_id: 2 created_unix: 1588117528 updated_unix: 1588117528 + +- + id: 5 + project_id: 2 + title: Backlog + creator_id: 2 + default: true + created_unix: 1588117528 + updated_unix: 1588117528 + +- + id: 6 + project_id: 4 + title: Backlog + creator_id: 2 + default: true + created_unix: 1588117528 + updated_unix: 1588117528 + +- + id: 7 + project_id: 5 + title: Done + creator_id: 2 + default: false + created_unix: 1588117528 + updated_unix: 1588117528 + +- + id: 8 + project_id: 6 + title: Backlog + creator_id: 2 + default: true + created_unix: 1588117528 + updated_unix: 1588117528 + +- + id: 9 + project_id: 6 + title: Uncategorized + creator_id: 2 + default: true + created_unix: 1588117528 + updated_unix: 1588117528 diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index cc7ffb356a..907a5a17b9 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -49,18 +49,13 @@ func (issue *Issue) ProjectBoardID(ctx context.Context) int64 { // LoadIssuesFromBoard load issues assigned to this board func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList, error) { - issueList := make(IssueList, 0, 10) - - if b.ID > 0 { - issues, err := Issues(ctx, &IssuesOptions{ - ProjectBoardID: b.ID, - ProjectID: b.ProjectID, - SortType: "project-column-sorting", - }) - if err != nil { - return nil, err - } - issueList = issues + issueList, err := Issues(ctx, &IssuesOptions{ + ProjectBoardID: b.ID, + ProjectID: b.ProjectID, + SortType: "project-column-sorting", + }) + if err != nil { + return nil, err } if b.Default { diff --git a/models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project.yml b/models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project.yml new file mode 100644 index 0000000000..2450d20beb --- /dev/null +++ b/models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project.yml @@ -0,0 +1,23 @@ +- + id: 1 + title: project without default column + owner_id: 2 + repo_id: 0 + is_closed: false + creator_id: 2 + board_type: 1 + type: 2 + created_unix: 1688973000 + updated_unix: 1688973000 + +- + id: 2 + title: project with multiple default columns + owner_id: 2 + repo_id: 0 + is_closed: false + creator_id: 2 + board_type: 1 + type: 2 + created_unix: 1688973000 + updated_unix: 1688973000 diff --git a/models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml b/models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml new file mode 100644 index 0000000000..2e1b1c7eee --- /dev/null +++ b/models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml @@ -0,0 +1,26 @@ +- + id: 1 + project_id: 1 + title: Done + creator_id: 2 + default: false + created_unix: 1588117528 + updated_unix: 1588117528 + +- + id: 2 + project_id: 2 + title: Backlog + creator_id: 2 + default: true + created_unix: 1588117528 + updated_unix: 1588117528 + +- + id: 3 + project_id: 2 + title: Uncategorized + creator_id: 2 + default: true + created_unix: 1588117528 + updated_unix: 1588117528 diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 87fddefb88..77895fba61 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -568,6 +568,8 @@ var migrations = []Migration{ NewMigration("Add PayloadVersion to HookTask", v1_22.AddPayloadVersionToHookTaskTable), // v291 -> v292 NewMigration("Add Index to attachment.comment_id", v1_22.AddCommentIDIndexofAttachment), + // v292 -> v293 + NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_22/v292.go b/models/migrations/v1_22/v292.go new file mode 100644 index 0000000000..7c051a2b75 --- /dev/null +++ b/models/migrations/v1_22/v292.go @@ -0,0 +1,85 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "code.gitea.io/gitea/models/project" + "code.gitea.io/gitea/modules/setting" + + "xorm.io/builder" + "xorm.io/xorm" +) + +// CheckProjectColumnsConsistency ensures there is exactly one default board per project present +func CheckProjectColumnsConsistency(x *xorm.Engine) error { + sess := x.NewSession() + defer sess.Close() + + if err := sess.Begin(); err != nil { + return err + } + + limit := setting.Database.IterateBufferSize + if limit <= 0 { + limit = 50 + } + + start := 0 + + for { + var projects []project.Project + if err := sess.SQL("SELECT DISTINCT `p`.`id`, `p`.`creator_id` FROM `project` `p` WHERE (SELECT COUNT(*) FROM `project_board` `pb` WHERE `pb`.`project_id` = `p`.`id` AND `pb`.`default` = ?) != 1", true). + Limit(limit, start). + Find(&projects); err != nil { + return err + } + + if len(projects) == 0 { + break + } + start += len(projects) + + for _, p := range projects { + var boards []project.Board + if err := sess.Where("project_id=? AND `default` = ?", p.ID, true).OrderBy("sorting").Find(&boards); err != nil { + return err + } + + if len(boards) == 0 { + if _, err := sess.Insert(project.Board{ + ProjectID: p.ID, + Default: true, + Title: "Uncategorized", + CreatorID: p.CreatorID, + }); err != nil { + return err + } + continue + } + + var boardsToUpdate []int64 + for id, b := range boards { + if id > 0 { + boardsToUpdate = append(boardsToUpdate, b.ID) + } + } + + if _, err := sess.Where(builder.Eq{"project_id": p.ID}.And(builder.In("id", boardsToUpdate))). + Cols("`default`").Update(&project.Board{Default: false}); err != nil { + return err + } + } + + if start%1000 == 0 { + if err := sess.Commit(); err != nil { + return err + } + if err := sess.Begin(); err != nil { + return err + } + } + } + + return sess.Commit() +} diff --git a/models/migrations/v1_22/v292_test.go b/models/migrations/v1_22/v292_test.go new file mode 100644 index 0000000000..5e32e0220f --- /dev/null +++ b/models/migrations/v1_22/v292_test.go @@ -0,0 +1,44 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "testing" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/migrations/base" + "code.gitea.io/gitea/models/project" + + "github.com/stretchr/testify/assert" +) + +func Test_CheckProjectColumnsConsistency(t *testing.T) { + // Prepare and load the testing database + x, deferable := base.PrepareTestEnv(t, 0, new(project.Project), new(project.Board)) + defer deferable() + if x == nil || t.Failed() { + return + } + + 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) + assert.NoError(t, err) + assert.True(t, has) + assert.Equal(t, int64(1), defaultBoard.ProjectID) + assert.True(t, defaultBoard.Default) + + // check if multiple defaults were removed + expectDefaultBoard, err := project.GetBoard(db.DefaultContext, 2) + assert.NoError(t, err) + assert.Equal(t, int64(2), expectDefaultBoard.ProjectID) + assert.True(t, expectDefaultBoard.Default) + + expectNonDefaultBoard, err := project.GetBoard(db.DefaultContext, 3) + assert.NoError(t, err) + assert.Equal(t, int64(2), expectNonDefaultBoard.ProjectID) + assert.False(t, expectNonDefaultBoard.Default) +} diff --git a/models/project/board.go b/models/project/board.go index c0e6529880..5605f259b5 100644 --- a/models/project/board.go +++ b/models/project/board.go @@ -123,6 +123,17 @@ func createBoardsForProjectsType(ctx context.Context, project *Project) error { 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 } @@ -176,6 +187,10 @@ func deleteBoardByID(ctx context.Context, boardID int64) error { return err } + if board.Default { + return fmt.Errorf("deleteBoardByID: cannot delete default board") + } + if err = board.removeIssues(ctx); err != nil { return err } @@ -228,7 +243,6 @@ func UpdateBoard(ctx context.Context, board *Board) error { } // GetBoards fetches all boards related to a project -// if no default board set, first board is a temporary "Uncategorized" board func (p *Project) GetBoards(ctx context.Context) (BoardList, error) { boards := make([]*Board, 0, 5) @@ -244,41 +258,61 @@ func (p *Project) GetBoards(ctx context.Context) (BoardList, error) { return append([]*Board{defaultB}, boards...), nil } -// getDefaultBoard return default board and create a dummy if none exist +// getDefaultBoard return default board and ensure only one exists func (p *Project) getDefaultBoard(ctx context.Context) (*Board, error) { - var board Board - exist, err := db.GetEngine(ctx).Where("project_id=? AND `default`=?", p.ID, true).Get(&board) - if err != nil { + var boards []Board + if err := db.GetEngine(ctx).Where("project_id=? AND `default` = ?", p.ID, true).OrderBy("sorting").Find(&boards); err != nil { return nil, err } - if exist { + + // create a default board if none is found + if len(boards) == 0 { + board := Board{ + ProjectID: p.ID, + Default: true, + Title: "Uncategorized", + CreatorID: p.CreatorID, + } + if _, err := db.GetEngine(ctx).Insert(); err != nil { + return nil, err + } return &board, nil } - // represents a board for issues not assigned to one - return &Board{ - ProjectID: p.ID, - Title: "Uncategorized", - Default: true, - }, nil + // unset default boards where too many default boards exist + if len(boards) > 1 { + var boardsToUpdate []int64 + for id, b := range boards { + if id > 0 { + boardsToUpdate = append(boardsToUpdate, b.ID) + } + } + + if _, err := db.GetEngine(ctx).Where(builder.Eq{"project_id": p.ID}.And(builder.In("id", boardsToUpdate))). + Cols("`default`").Update(&Board{Default: false}); err != nil { + return nil, err + } + } + + return &boards[0], nil } // SetDefaultBoard represents a board for issues not assigned to one -// if boardID is 0 unset default func SetDefaultBoard(ctx context.Context, projectID, boardID int64) error { - _, err := db.GetEngine(ctx).Where(builder.Eq{ - "project_id": projectID, - "`default`": true, - }).Cols("`default`").Update(&Board{Default: false}) - if err != nil { + if _, err := GetBoard(ctx, boardID); err != nil { return err } - if boardID > 0 { - _, err = db.GetEngine(ctx).ID(boardID).Where(builder.Eq{"project_id": projectID}). - Cols("`default`").Update(&Board{Default: true}) + 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 } diff --git a/models/project/board_test.go b/models/project/board_test.go new file mode 100644 index 0000000000..c1c6f0180b --- /dev/null +++ b/models/project/board_test.go @@ -0,0 +1,40 @@ +// 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(8), board.ID) + + 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/project_test.go b/models/project/project_test.go index 7a37c1faf2..8fbbdedecf 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -92,19 +92,19 @@ func TestProjectsSort(t *testing.T) { }{ { sortType: "default", - wants: []int64{1, 3, 2, 4}, + wants: []int64{1, 3, 2, 6, 5, 4}, }, { sortType: "oldest", - wants: []int64{4, 2, 3, 1}, + wants: []int64{4, 5, 6, 2, 3, 1}, }, { sortType: "recentupdate", - wants: []int64{1, 3, 2, 4}, + wants: []int64{1, 3, 2, 6, 5, 4}, }, { sortType: "leastupdate", - wants: []int64{4, 2, 3, 1}, + wants: []int64{4, 5, 6, 2, 3, 1}, }, } @@ -113,8 +113,8 @@ func TestProjectsSort(t *testing.T) { OrderBy: GetSearchOrderByBySortType(tt.sortType), }) assert.NoError(t, err) - assert.EqualValues(t, int64(4), count) - if assert.Len(t, projects, 4) { + assert.EqualValues(t, int64(6), count) + if assert.Len(t, projects, 6) { for i := range projects { assert.EqualValues(t, tt.wants[i], projects[i].ID) } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 07082f99ae..b7bcf20d30 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1392,7 +1392,6 @@ projects.type.basic_kanban = "Basic Kanban" projects.type.bug_triage = "Bug Triage" projects.template.desc = "Template" projects.template.desc_helper = "Select a project template to get started" -projects.type.uncategorized = Uncategorized projects.column.edit = "Edit Column" projects.column.edit_title = "Name" projects.column.new_title = "Name" @@ -1400,10 +1399,8 @@ projects.column.new_submit = "Create Column" projects.column.new = "New Column" projects.column.set_default = "Set Default" projects.column.set_default_desc = "Set this column as default for uncategorized issues and pulls" -projects.column.unset_default = "Unset Default" -projects.column.unset_default_desc = "Unset this column as default" projects.column.delete = "Delete Column" -projects.column.deletion_desc = "Deleting a project column moves all related issues to 'Uncategorized'. Continue?" +projects.column.deletion_desc = "Deleting a project column moves all related issues to the default column. Continue?" projects.column.color = "Color" projects.open = Open projects.close = Close diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 928676a52b..596a370d2e 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -207,11 +207,7 @@ func ChangeProjectStatus(ctx *context.Context) { id := ctx.ParamsInt64(":id") if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx, 0, id, toClose); err != nil { - if project_model.IsErrProjectNotExist(err) { - ctx.NotFound("", err) - } else { - ctx.ServerError("ChangeProjectStatusByRepoIDAndID", err) - } + ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err) return } ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects?state=" + url.QueryEscape(ctx.Params(":action"))) @@ -221,11 +217,7 @@ func ChangeProjectStatus(ctx *context.Context) { func DeleteProject(ctx *context.Context) { p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) if err != nil { - if project_model.IsErrProjectNotExist(err) { - ctx.NotFound("", nil) - } else { - ctx.ServerError("GetProjectByID", err) - } + ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err) return } if p.OwnerID != ctx.ContextUser.ID { @@ -254,11 +246,7 @@ func RenderEditProject(ctx *context.Context) { p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) if err != nil { - if project_model.IsErrProjectNotExist(err) { - ctx.NotFound("", nil) - } else { - ctx.ServerError("GetProjectByID", err) - } + ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err) return } if p.OwnerID != ctx.ContextUser.ID { @@ -303,11 +291,7 @@ func EditProjectPost(ctx *context.Context) { p, err := project_model.GetProjectByID(ctx, projectID) if err != nil { - if project_model.IsErrProjectNotExist(err) { - ctx.NotFound("", nil) - } else { - ctx.ServerError("GetProjectByID", err) - } + ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err) return } if p.OwnerID != ctx.ContextUser.ID { @@ -335,11 +319,7 @@ func EditProjectPost(ctx *context.Context) { func ViewProject(ctx *context.Context) { project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) if err != nil { - if project_model.IsErrProjectNotExist(err) { - ctx.NotFound("", nil) - } else { - ctx.ServerError("GetProjectByID", err) - } + ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err) return } if project.OwnerID != ctx.ContextUser.ID { @@ -353,10 +333,6 @@ func ViewProject(ctx *context.Context) { return } - if boards[0].ID == 0 { - boards[0].Title = ctx.Locale.TrString("repo.projects.type.uncategorized") - } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) @@ -493,11 +469,7 @@ func DeleteProjectBoard(ctx *context.Context) { project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) if err != nil { - if project_model.IsErrProjectNotExist(err) { - ctx.NotFound("", nil) - } else { - ctx.ServerError("GetProjectByID", err) - } + ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err) return } @@ -534,11 +506,7 @@ func AddBoardToProjectPost(ctx *context.Context) { project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) if err != nil { - if project_model.IsErrProjectNotExist(err) { - ctx.NotFound("", nil) - } else { - ctx.ServerError("GetProjectByID", err) - } + ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err) return } @@ -566,11 +534,7 @@ func CheckProjectBoardChangePermissions(ctx *context.Context) (*project_model.Pr project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) if err != nil { - if project_model.IsErrProjectNotExist(err) { - ctx.NotFound("", nil) - } else { - ctx.ServerError("GetProjectByID", err) - } + ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err) return nil, nil } @@ -636,21 +600,6 @@ func SetDefaultProjectBoard(ctx *context.Context) { ctx.JSONOK() } -// UnsetDefaultProjectBoard unset default board for uncategorized issues/pulls -func UnsetDefaultProjectBoard(ctx *context.Context) { - project, _ := CheckProjectBoardChangePermissions(ctx) - if ctx.Written() { - return - } - - if err := project_model.SetDefaultBoard(ctx, project.ID, 0); err != nil { - ctx.ServerError("SetDefaultBoard", err) - return - } - - ctx.JSONOK() -} - // MoveIssues moves or keeps issues in a column and sorts them inside that column func MoveIssues(ctx *context.Context) { if ctx.Doer == nil { @@ -662,11 +611,7 @@ func MoveIssues(ctx *context.Context) { project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) if err != nil { - if project_model.IsErrProjectNotExist(err) { - ctx.NotFound("ProjectNotExist", nil) - } else { - ctx.ServerError("GetProjectByID", err) - } + ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err) return } if project.OwnerID != ctx.ContextUser.ID { @@ -674,28 +619,15 @@ func MoveIssues(ctx *context.Context) { return } - var board *project_model.Board + board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) + if err != nil { + ctx.NotFoundOrServerError("GetProjectBoard", project_model.IsErrProjectBoardNotExist, err) + return + } - if ctx.ParamsInt64(":boardID") == 0 { - board = &project_model.Board{ - ID: 0, - ProjectID: project.ID, - Title: ctx.Locale.TrString("repo.projects.type.uncategorized"), - } - } else { - board, err = project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) - if err != nil { - if project_model.IsErrProjectBoardNotExist(err) { - ctx.NotFound("ProjectBoardNotExist", nil) - } else { - ctx.ServerError("GetProjectBoard", err) - } - return - } - if board.ProjectID != project.ID { - ctx.NotFound("BoardNotInProject", nil) - return - } + if board.ProjectID != project.ID { + ctx.NotFound("BoardNotInProject", nil) + return } type movedIssuesForm struct { @@ -718,11 +650,7 @@ func MoveIssues(ctx *context.Context) { } movedIssues, err := issues_model.GetIssuesByIDs(ctx, issueIDs) if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.NotFound("IssueNotExisting", nil) - } else { - ctx.ServerError("GetIssueByID", err) - } + ctx.NotFoundOrServerError("GetIssueByID", issues_model.IsErrIssueNotExist, err) return } diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 2cba5c0970..a2db1fc770 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -315,10 +315,6 @@ func ViewProject(ctx *context.Context) { return } - if boards[0].ID == 0 { - boards[0].Title = ctx.Locale.TrString("repo.projects.type.uncategorized") - } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) @@ -583,21 +579,6 @@ func SetDefaultProjectBoard(ctx *context.Context) { ctx.JSONOK() } -// UnSetDefaultProjectBoard unset default board for uncategorized issues/pulls -func UnSetDefaultProjectBoard(ctx *context.Context) { - project, _ := checkProjectBoardChangePermissions(ctx) - if ctx.Written() { - return - } - - if err := project_model.SetDefaultBoard(ctx, project.ID, 0); err != nil { - ctx.ServerError("SetDefaultBoard", err) - return - } - - ctx.JSONOK() -} - // MoveIssues moves or keeps issues in a column and sorts them inside that column func MoveIssues(ctx *context.Context) { if ctx.Doer == nil { @@ -628,28 +609,19 @@ func MoveIssues(ctx *context.Context) { return } - var board *project_model.Board + board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) + if err != nil { + if project_model.IsErrProjectBoardNotExist(err) { + ctx.NotFound("ProjectBoardNotExist", nil) + } else { + ctx.ServerError("GetProjectBoard", err) + } + return + } - if ctx.ParamsInt64(":boardID") == 0 { - board = &project_model.Board{ - ID: 0, - ProjectID: project.ID, - Title: ctx.Locale.TrString("repo.projects.type.uncategorized"), - } - } else { - board, err = project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) - if err != nil { - if project_model.IsErrProjectBoardNotExist(err) { - ctx.NotFound("ProjectBoardNotExist", nil) - } else { - ctx.ServerError("GetProjectBoard", err) - } - return - } - if board.ProjectID != project.ID { - ctx.NotFound("BoardNotInProject", nil) - return - } + if board.ProjectID != project.ID { + ctx.NotFound("BoardNotInProject", nil) + return } type movedIssuesForm struct { diff --git a/routers/web/web.go b/routers/web/web.go index 3d790d1621..4fff994e42 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1008,7 +1008,6 @@ func registerRoutes(m *web.Route) { m.Put("", web.Bind(forms.EditProjectBoardForm{}), org.EditProjectBoard) m.Delete("", org.DeleteProjectBoard) m.Post("/default", org.SetDefaultProjectBoard) - m.Post("/unsetdefault", org.UnsetDefaultProjectBoard) m.Post("/move", org.MoveIssues) }) @@ -1348,7 +1347,6 @@ func registerRoutes(m *web.Route) { m.Put("", web.Bind(forms.EditProjectBoardForm{}), repo.EditProjectBoard) m.Delete("", repo.DeleteProjectBoard) m.Post("/default", repo.SetDefaultProjectBoard) - m.Post("/unsetdefault", repo.UnSetDefaultProjectBoard) m.Post("/move", repo.MoveIssues) }) diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index ba5cbc3b45..e6c6c20497 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -74,7 +74,7 @@ {{.Title}} - {{if and $canWriteProject (ne .ID 0)}} + {{if $canWriteProject}}