Skip to content

Commit e516018

Browse files
denyskondelvh
andauthored
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 <[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)