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