mirror of https://github.com/go-gitea/gitea
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 <dev.lh@web.de>pull/30144/head^2
parent
4eb86d6823
commit
e5160185ed
@ -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 |
@ -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 |
@ -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() |
||||||
|
} |
@ -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) |
||||||
|
} |
@ -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) |
||||||
|
} |
Loading…
Reference in new issue