summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--models/fixtures/project.yml24
-rw-r--r--models/fixtures/project_board.yml46
-rw-r--r--models/issues/issue_project.go19
-rw-r--r--models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project.yml23
-rw-r--r--models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml26
-rw-r--r--models/migrations/migrations.go2
-rw-r--r--models/migrations/v1_22/v292.go85
-rw-r--r--models/migrations/v1_22/v292_test.go44
-rw-r--r--models/project/board.go74
-rw-r--r--models/project/board_test.go40
-rw-r--r--models/project/project_test.go12
-rw-r--r--options/locale/locale_en-US.ini5
-rw-r--r--routers/web/org/projects.go108
-rw-r--r--routers/web/repo/projects.go52
-rw-r--r--routers/web/web.go2
-rw-r--r--templates/projects/view.tmpl31
-rw-r--r--web_src/js/features/repo-projects.js1
17 files changed, 399 insertions, 195 deletions
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 80488bc895..89ad456a35 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -570,6 +570,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,40 +258,60 @@ 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{
+ 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})
- if err != nil {
+ }).Cols("`default`").Update(&Board{Default: false}); 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})
- }
+ _, 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 c83f386f2a..c28ed6c376 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1423,7 +1423,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
@@ -1431,10 +1430,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 ad8bb90d9e..db1dac0120 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 4c171defbd..aa42585590 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -314,10 +314,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)
@@ -582,21 +578,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 {
@@ -627,28 +608,19 @@ func MoveIssues(ctx *context.Context) {
return
}
- var board *project_model.Board
-
- 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
+ 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
}
type movedIssuesForm struct {
diff --git a/routers/web/web.go b/routers/web/web.go
index 7329acd155..40f4ffc018 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -986,7 +986,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)
})
@@ -1360,7 +1359,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 @@
</div>
{{.Title}}
</div>
- {{if and $canWriteProject (ne .ID 0)}}
+ {{if $canWriteProject}}
<div class="ui dropdown jump item">
<div class="tw-px-2">
{{svg "octicon-kebab-horizontal"}}
@@ -86,29 +86,20 @@
</a>
{{if not .Default}}
<a class="item show-modal button default-project-column-show"
- data-modal="#default-project-column-modal-{{.ID}}"
- data-modal-default-project-column-header="{{ctx.Locale.Tr "repo.projects.column.set_default"}}"
- data-modal-default-project-column-content="{{ctx.Locale.Tr "repo.projects.column.set_default_desc"}}"
- data-url="{{$.Link}}/{{.ID}}/default">
+ data-modal="#default-project-column-modal-{{.ID}}"
+ data-modal-default-project-column-header="{{ctx.Locale.Tr "repo.projects.column.set_default"}}"
+ data-modal-default-project-column-content="{{ctx.Locale.Tr "repo.projects.column.set_default_desc"}}"
+ data-url="{{$.Link}}/{{.ID}}/default">
{{svg "octicon-pin"}}
{{ctx.Locale.Tr "repo.projects.column.set_default"}}
</a>
- {{else}}
- <a class="item show-modal button default-project-column-show"
- data-modal="#default-project-column-modal-{{.ID}}"
- data-modal-default-project-column-header="{{ctx.Locale.Tr "repo.projects.column.unset_default"}}"
- data-modal-default-project-column-content="{{ctx.Locale.Tr "repo.projects.column.unset_default_desc"}}"
- data-url="{{$.Link}}/{{.ID}}/unsetdefault">
- {{svg "octicon-pin-slash"}}
- {{ctx.Locale.Tr "repo.projects.column.unset_default"}}
+ <a class="item show-modal button show-delete-project-column-modal"
+ data-modal="#delete-project-column-modal-{{.ID}}"
+ data-url="{{$.Link}}/{{.ID}}">
+ {{svg "octicon-trash"}}
+ {{ctx.Locale.Tr "repo.projects.column.delete"}}
</a>
{{end}}
- <a class="item show-modal button show-delete-project-column-modal"
- data-modal="#delete-project-column-modal-{{.ID}}"
- data-url="{{$.Link}}/{{.ID}}">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "repo.projects.column.delete"}}
- </a>
<div class="ui small modal edit-project-column-modal" id="edit-project-column-modal-{{.ID}}">
<div class="header">
@@ -165,7 +156,7 @@
<div class="divider"></div>
- <div class="ui cards {{if and $canWriteProject (ne .ID 0)}}{{/* ID 0 is default column which cannot be moved */}}tw-cursor-grab{{end}}" data-url="{{$.Link}}/{{.ID}}" data-project="{{$.Project.ID}}" data-board="{{.ID}}" id="board_{{.ID}}">
+ <div class="ui cards{{if $canWriteProject}} tw-cursor-grab{{end}}" data-url="{{$.Link}}/{{.ID}}" data-project="{{$.Project.ID}}" data-board="{{.ID}}" id="board_{{.ID}}">
{{range (index $.IssuesMap .ID)}}
<div class="issue-card gt-word-break {{if $canWriteProject}}tw-cursor-grab{{end}}" data-issue="{{.ID}}">
{{template "repo/issue/card" (dict "Issue" . "Page" $)}}
diff --git a/web_src/js/features/repo-projects.js b/web_src/js/features/repo-projects.js
index fc688bb695..1747cb2b3a 100644
--- a/web_src/js/features/repo-projects.js
+++ b/web_src/js/features/repo-projects.js
@@ -58,7 +58,6 @@ async function initRepoProjectSortable() {
createSortable(mainBoard, {
group: 'project-column',
draggable: '.project-column',
- filter: '[data-id="0"]',
animation: 150,
ghostClass: 'card-ghost',
delayOnTouchOnly: true,