Skip to content

Commit e516018

Browse files
denyskondelvh
andauthored
Add default board to new projects, remove uncategorized pseudo-board (go-gitea#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 go-gitea#29873 Fixes go-gitea#14679 along the way Fixes go-gitea#29853 Co-authored-by: delvh <[email protected]>
1 parent 4eb86d6 commit e516018

File tree

17 files changed

+399
-195
lines changed

17 files changed

+399
-195
lines changed

models/fixtures/project.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,27 @@
4545
type: 2
4646
created_unix: 1688973000
4747
updated_unix: 1688973000
48+
49+
-
50+
id: 5
51+
title: project without default column
52+
owner_id: 2
53+
repo_id: 0
54+
is_closed: false
55+
creator_id: 2
56+
board_type: 1
57+
type: 2
58+
created_unix: 1688973000
59+
updated_unix: 1688973000
60+
61+
-
62+
id: 6
63+
title: project with multiple default columns
64+
owner_id: 2
65+
repo_id: 0
66+
is_closed: false
67+
creator_id: 2
68+
board_type: 1
69+
type: 2
70+
created_unix: 1688973000
71+
updated_unix: 1688973000

models/fixtures/project_board.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
project_id: 1
44
title: To Do
55
creator_id: 2
6+
default: true
67
created_unix: 1588117528
78
updated_unix: 1588117528
89

@@ -29,3 +30,48 @@
2930
creator_id: 2
3031
created_unix: 1588117528
3132
updated_unix: 1588117528
33+
34+
-
35+
id: 5
36+
project_id: 2
37+
title: Backlog
38+
creator_id: 2
39+
default: true
40+
created_unix: 1588117528
41+
updated_unix: 1588117528
42+
43+
-
44+
id: 6
45+
project_id: 4
46+
title: Backlog
47+
creator_id: 2
48+
default: true
49+
created_unix: 1588117528
50+
updated_unix: 1588117528
51+
52+
-
53+
id: 7
54+
project_id: 5
55+
title: Done
56+
creator_id: 2
57+
default: false
58+
created_unix: 1588117528
59+
updated_unix: 1588117528
60+
61+
-
62+
id: 8
63+
project_id: 6
64+
title: Backlog
65+
creator_id: 2
66+
default: true
67+
created_unix: 1588117528
68+
updated_unix: 1588117528
69+
70+
-
71+
id: 9
72+
project_id: 6
73+
title: Uncategorized
74+
creator_id: 2
75+
default: true
76+
created_unix: 1588117528
77+
updated_unix: 1588117528

models/issues/issue_project.go

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,13 @@ func (issue *Issue) ProjectBoardID(ctx context.Context) int64 {
4949

5050
// LoadIssuesFromBoard load issues assigned to this board
5151
func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList, error) {
52-
issueList := make(IssueList, 0, 10)
53-
54-
if b.ID > 0 {
55-
issues, err := Issues(ctx, &IssuesOptions{
56-
ProjectBoardID: b.ID,
57-
ProjectID: b.ProjectID,
58-
SortType: "project-column-sorting",
59-
})
60-
if err != nil {
61-
return nil, err
62-
}
63-
issueList = issues
52+
issueList, err := Issues(ctx, &IssuesOptions{
53+
ProjectBoardID: b.ID,
54+
ProjectID: b.ProjectID,
55+
SortType: "project-column-sorting",
56+
})
57+
if err != nil {
58+
return nil, err
6459
}
6560

6661
if b.Default {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
-
2+
id: 1
3+
title: project without default column
4+
owner_id: 2
5+
repo_id: 0
6+
is_closed: false
7+
creator_id: 2
8+
board_type: 1
9+
type: 2
10+
created_unix: 1688973000
11+
updated_unix: 1688973000
12+
13+
-
14+
id: 2
15+
title: project with multiple default columns
16+
owner_id: 2
17+
repo_id: 0
18+
is_closed: false
19+
creator_id: 2
20+
board_type: 1
21+
type: 2
22+
created_unix: 1688973000
23+
updated_unix: 1688973000
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
-
2+
id: 1
3+
project_id: 1
4+
title: Done
5+
creator_id: 2
6+
default: false
7+
created_unix: 1588117528
8+
updated_unix: 1588117528
9+
10+
-
11+
id: 2
12+
project_id: 2
13+
title: Backlog
14+
creator_id: 2
15+
default: true
16+
created_unix: 1588117528
17+
updated_unix: 1588117528
18+
19+
-
20+
id: 3
21+
project_id: 2
22+
title: Uncategorized
23+
creator_id: 2
24+
default: true
25+
created_unix: 1588117528
26+
updated_unix: 1588117528

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,8 @@ var migrations = []Migration{
568568
NewMigration("Add PayloadVersion to HookTask", v1_22.AddPayloadVersionToHookTaskTable),
569569
// v291 -> v292
570570
NewMigration("Add Index to attachment.comment_id", v1_22.AddCommentIDIndexofAttachment),
571+
// v292 -> v293
572+
NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency),
571573
}
572574

573575
// GetCurrentDBVersion returns the current db version

models/migrations/v1_22/v292.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_22 //nolint
5+
6+
import (
7+
"code.gitea.io/gitea/models/project"
8+
"code.gitea.io/gitea/modules/setting"
9+
10+
"xorm.io/builder"
11+
"xorm.io/xorm"
12+
)
13+
14+
// CheckProjectColumnsConsistency ensures there is exactly one default board per project present
15+
func CheckProjectColumnsConsistency(x *xorm.Engine) error {
16+
sess := x.NewSession()
17+
defer sess.Close()
18+
19+
if err := sess.Begin(); err != nil {
20+
return err
21+
}
22+
23+
limit := setting.Database.IterateBufferSize
24+
if limit <= 0 {
25+
limit = 50
26+
}
27+
28+
start := 0
29+
30+
for {
31+
var projects []project.Project
32+
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).
33+
Limit(limit, start).
34+
Find(&projects); err != nil {
35+
return err
36+
}
37+
38+
if len(projects) == 0 {
39+
break
40+
}
41+
start += len(projects)
42+
43+
for _, p := range projects {
44+
var boards []project.Board
45+
if err := sess.Where("project_id=? AND `default` = ?", p.ID, true).OrderBy("sorting").Find(&boards); err != nil {
46+
return err
47+
}
48+
49+
if len(boards) == 0 {
50+
if _, err := sess.Insert(project.Board{
51+
ProjectID: p.ID,
52+
Default: true,
53+
Title: "Uncategorized",
54+
CreatorID: p.CreatorID,
55+
}); err != nil {
56+
return err
57+
}
58+
continue
59+
}
60+
61+
var boardsToUpdate []int64
62+
for id, b := range boards {
63+
if id > 0 {
64+
boardsToUpdate = append(boardsToUpdate, b.ID)
65+
}
66+
}
67+
68+
if _, err := sess.Where(builder.Eq{"project_id": p.ID}.And(builder.In("id", boardsToUpdate))).
69+
Cols("`default`").Update(&project.Board{Default: false}); err != nil {
70+
return err
71+
}
72+
}
73+
74+
if start%1000 == 0 {
75+
if err := sess.Commit(); err != nil {
76+
return err
77+
}
78+
if err := sess.Begin(); err != nil {
79+
return err
80+
}
81+
}
82+
}
83+
84+
return sess.Commit()
85+
}

models/migrations/v1_22/v292_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_22 //nolint
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/models/db"
10+
"code.gitea.io/gitea/models/migrations/base"
11+
"code.gitea.io/gitea/models/project"
12+
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func Test_CheckProjectColumnsConsistency(t *testing.T) {
17+
// Prepare and load the testing database
18+
x, deferable := base.PrepareTestEnv(t, 0, new(project.Project), new(project.Board))
19+
defer deferable()
20+
if x == nil || t.Failed() {
21+
return
22+
}
23+
24+
assert.NoError(t, CheckProjectColumnsConsistency(x))
25+
26+
// check if default board was added
27+
var defaultBoard project.Board
28+
has, err := x.Where("project_id=? AND `default` = ?", 1, true).Get(&defaultBoard)
29+
assert.NoError(t, err)
30+
assert.True(t, has)
31+
assert.Equal(t, int64(1), defaultBoard.ProjectID)
32+
assert.True(t, defaultBoard.Default)
33+
34+
// check if multiple defaults were removed
35+
expectDefaultBoard, err := project.GetBoard(db.DefaultContext, 2)
36+
assert.NoError(t, err)
37+
assert.Equal(t, int64(2), expectDefaultBoard.ProjectID)
38+
assert.True(t, expectDefaultBoard.Default)
39+
40+
expectNonDefaultBoard, err := project.GetBoard(db.DefaultContext, 3)
41+
assert.NoError(t, err)
42+
assert.Equal(t, int64(2), expectNonDefaultBoard.ProjectID)
43+
assert.False(t, expectNonDefaultBoard.Default)
44+
}

0 commit comments

Comments
 (0)