Skip to content

Commit 8756c20

Browse files
committed
Update
1 parent 1ea5216 commit 8756c20

File tree

47 files changed

+603
-102
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+603
-102
lines changed

models/issues/milestone_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func TestGetMilestones(t *testing.T) {
9898
milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{
9999
ListOptions: db.ListOptions{
100100
Page: page,
101-
PageSize: setting.UI.IssuePagingNum,
101+
PageSize: setting.Config().UI.IssuePagingNum.Value(t.Context()),
102102
},
103103
RepoID: repo.ID,
104104
IsClosed: optional.Some(false),
@@ -115,7 +115,7 @@ func TestGetMilestones(t *testing.T) {
115115
milestones, err = db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{
116116
ListOptions: db.ListOptions{
117117
Page: page,
118-
PageSize: setting.UI.IssuePagingNum,
118+
PageSize: setting.Config().UI.IssuePagingNum.Value(t.Context()),
119119
},
120120
RepoID: repo.ID,
121121
IsClosed: optional.Some(true),
@@ -231,7 +231,7 @@ func TestGetMilestonesByRepoIDs(t *testing.T) {
231231
openMilestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{
232232
ListOptions: db.ListOptions{
233233
Page: page,
234-
PageSize: setting.UI.IssuePagingNum,
234+
PageSize: setting.Config().UI.IssuePagingNum.Value(t.Context()),
235235
},
236236
RepoIDs: []int64{repo1.ID, repo2.ID},
237237
IsClosed: optional.Some(false),
@@ -249,7 +249,7 @@ func TestGetMilestonesByRepoIDs(t *testing.T) {
249249
issues_model.FindMilestoneOptions{
250250
ListOptions: db.ListOptions{
251251
Page: page,
252-
PageSize: setting.UI.IssuePagingNum,
252+
PageSize: setting.Config().UI.IssuePagingNum.Value(t.Context()),
253253
},
254254
RepoIDs: []int64{repo1.ID, repo2.ID},
255255
IsClosed: optional.Some(true),
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# type Setting struct {
2+
# ID int64 `xorm:"pk autoincr"`
3+
# SettingKey string `xorm:"varchar(255) unique"` // key should be lowercase
4+
# SettingValue string `xorm:"text"`
5+
# Version int `xorm:"version"`
6+
# Created timeutil.TimeStamp `xorm:"created"`
7+
# Updated timeutil.TimeStamp `xorm:"updated"`
8+
# }
9+
-
10+
id: 1
11+
setting_key: revision
12+
version: 1
13+
14+
-
15+
id: 2
16+
setting_key: picture.enable_federated_avatar
17+
setting_value: false
18+
version: 1
19+
20+
-
21+
id: 3
22+
setting_key: picture.disable_gravatar
23+
setting_value: true
24+
version: 1

models/migrations/migrations.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ func prepareMigrationTasks() []*migration {
376376
newMigration(313, "Move PinOrder from issue table to a new table issue_pin", v1_24.MovePinOrderToTableIssuePin),
377377
newMigration(314, "Update OwnerID as zero for repository level action tables", v1_24.UpdateOwnerIDOfRepoLevelActionsTables),
378378
newMigration(315, "Add Ephemeral to ActionRunner", v1_24.AddEphemeralToActionRunner),
379+
newMigration(316, "Migrate the configuration of the ui section of the ini configuration file to the system setting table.", v1_24.MigrateIniToDatabase),
379380
}
380381
return preparedMigrations
381382
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_24 //nolint
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/models/migrations/base"
10+
)
11+
12+
func TestMain(m *testing.M) {
13+
base.MainTest(m)
14+
}

models/migrations/v1_24/v316.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_24 //nolint
5+
6+
import (
7+
"math"
8+
9+
"code.gitea.io/gitea/modules/setting"
10+
"code.gitea.io/gitea/modules/timeutil"
11+
"code.gitea.io/gitea/modules/util"
12+
13+
"xorm.io/xorm"
14+
)
15+
16+
const keyRevision = "revision"
17+
18+
type Setting struct {
19+
ID int64 `xorm:"pk autoincr"`
20+
SettingKey string `xorm:"varchar(255) unique"` // key should be lowercase
21+
SettingValue string `xorm:"text"`
22+
Version int `xorm:"version"`
23+
Created timeutil.TimeStamp `xorm:"created"`
24+
Updated timeutil.TimeStamp `xorm:"updated"`
25+
}
26+
27+
// TableName sets the table name for the settings struct
28+
func (s *Setting) TableName() string {
29+
return "system_setting"
30+
}
31+
32+
func MigrateIniToDatabase(x *xorm.Engine) error {
33+
uiMap, err := util.ConfigSectionToMap(
34+
setting.UI, "ui",
35+
[]string{
36+
"GraphMaxCommitNum", "ReactionMaxUserNum", "MaxDisplayFileSize", "DefaultShowFullName", "DefaultTheme", "Themes",
37+
"FileIconTheme", "Reactions", "CustomEmojis", "PreferredTimestampTense", "AmbiguousUnicodeDetection",
38+
}...,
39+
)
40+
if err != nil {
41+
return err
42+
}
43+
44+
sess := x.NewSession()
45+
defer sess.Close()
46+
47+
if err = sess.Begin(); err != nil {
48+
return err
49+
}
50+
51+
if err = sess.Sync(new(Setting)); err != nil {
52+
return err
53+
}
54+
55+
_ = getRevision(sess) // prepare the "revision" key ahead
56+
57+
_, err = sess.Exec("UPDATE system_setting SET version=version+1 WHERE setting_key=?", keyRevision)
58+
if err != nil {
59+
return err
60+
}
61+
for k, v := range uiMap {
62+
res, err := sess.Exec("UPDATE system_setting SET version=version+1, setting_value=? WHERE setting_key=?", v, k)
63+
if err != nil {
64+
return err
65+
}
66+
rows, _ := res.RowsAffected()
67+
if rows == 0 { // if no existing row, insert a new row
68+
if _, err = sess.Insert(&Setting{SettingKey: k, SettingValue: v}); err != nil {
69+
return err
70+
}
71+
}
72+
}
73+
74+
return sess.Commit()
75+
}
76+
77+
func getRevision(sess *xorm.Session) int {
78+
revision := &Setting{}
79+
exist, err := sess.Where("setting_key = ?", keyRevision).Get(revision)
80+
if err != nil {
81+
return 0
82+
} else if !exist {
83+
_, err = sess.Insert(&Setting{SettingKey: keyRevision, Version: 1})
84+
if err != nil {
85+
return 0
86+
}
87+
return 1
88+
}
89+
90+
if revision.Version <= 0 || revision.Version >= math.MaxInt-1 {
91+
_, err = sess.Exec("UPDATE system_setting SET version=1 WHERE setting_key=?", keyRevision)
92+
if err != nil {
93+
return 0
94+
}
95+
return 1
96+
}
97+
return revision.Version
98+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_24 //nolint
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/models/migrations/base"
10+
11+
"github.com/stretchr/testify/assert"
12+
)
13+
14+
func Test_MigrateIniToDatabase(t *testing.T) {
15+
// Prepare and load the testing database
16+
x, deferable := base.PrepareTestEnv(t, 0, new(Setting))
17+
defer deferable()
18+
if x == nil || t.Failed() {
19+
return
20+
}
21+
22+
assert.NoError(t, MigrateIniToDatabase(x))
23+
24+
cnt, err := x.Table("system_setting").Where("setting_key LIKE 'ui.%'").Count()
25+
assert.NoError(t, err)
26+
assert.EqualValues(t, 13, cnt)
27+
}

modules/indexer/code/gitgrep/gitgrep.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ func PerformSearch(ctx context.Context, page int, repoID int64, gitRepo *git.Rep
4747
}
4848

4949
total = len(res)
50-
pageStart := min((page-1)*setting.UI.RepoSearchPagingNum, len(res))
51-
pageEnd := min(page*setting.UI.RepoSearchPagingNum, len(res))
50+
pageStart := min((page-1)*setting.Config().UI.RepoSearchPagingNum.Value(ctx), len(res))
51+
pageEnd := min(page*setting.Config().UI.RepoSearchPagingNum.Value(ctx), len(res))
5252
res = res[pageStart:pageEnd]
5353
for _, r := range res {
5454
searchResults = append(searchResults, &code_indexer.Result{

modules/setting/config.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package setting
55

66
import (
7+
"context"
78
"sync"
89

910
"code.gitea.io/gitea/modules/log"
@@ -51,9 +52,66 @@ type RepositoryStruct struct {
5152
OpenWithEditorApps *config.Value[OpenWithEditorAppsType]
5253
}
5354

55+
type UIStruct struct {
56+
ExplorePagingNum *config.Value[int]
57+
SitemapPagingNum *config.Value[int]
58+
IssuePagingNum *config.Value[int]
59+
RepoSearchPagingNum *config.Value[int]
60+
MembersPagingNum *config.Value[int]
61+
FeedMaxCommitNum *config.Value[int]
62+
FeedPagingNum *config.Value[int]
63+
PackagesPagingNum *config.Value[int]
64+
GraphMaxCommitNum *config.Value[int]
65+
CodeCommentLines *config.Value[int]
66+
ShowUserEmail *config.Value[bool]
67+
SearchRepoDescription *config.Value[bool]
68+
OnlyShowRelevantRepos *config.Value[bool]
69+
ExploreDefaultSort *config.Value[string]
70+
}
71+
72+
func (u *UIStruct) ToStruct(ctx context.Context) UIForm {
73+
return UIForm{
74+
ExplorePagingNum: u.ExplorePagingNum.Value(ctx),
75+
SitemapPagingNum: u.SitemapPagingNum.Value(ctx),
76+
IssuePagingNum: u.IssuePagingNum.Value(ctx),
77+
RepoSearchPagingNum: u.RepoSearchPagingNum.Value(ctx),
78+
MembersPagingNum: u.MembersPagingNum.Value(ctx),
79+
FeedMaxCommitNum: u.FeedMaxCommitNum.Value(ctx),
80+
FeedPagingNum: u.FeedPagingNum.Value(ctx),
81+
PackagesPagingNum: u.PackagesPagingNum.Value(ctx),
82+
GraphMaxCommitNum: u.GraphMaxCommitNum.Value(ctx),
83+
CodeCommentLines: u.CodeCommentLines.Value(ctx),
84+
ShowUserEmail: u.ShowUserEmail.Value(ctx),
85+
SearchRepoDescription: u.SearchRepoDescription.Value(ctx),
86+
OnlyShowRelevantRepos: u.OnlyShowRelevantRepos.Value(ctx),
87+
ExplorePagingDefaultSort: u.ExploreDefaultSort.Value(ctx),
88+
ExplorePagingSortOption: []string{"recentupdate", "alphabetically", "reverselastlogin", "newest", "oldest"},
89+
}
90+
}
91+
92+
type UIForm struct {
93+
ExplorePagingNum int
94+
SitemapPagingNum int
95+
IssuePagingNum int
96+
RepoSearchPagingNum int
97+
MembersPagingNum int
98+
FeedMaxCommitNum int
99+
FeedPagingNum int
100+
PackagesPagingNum int
101+
GraphMaxCommitNum int
102+
CodeCommentLines int
103+
ShowUserEmail bool
104+
DefaultShowFullName bool
105+
SearchRepoDescription bool
106+
OnlyShowRelevantRepos bool
107+
ExplorePagingDefaultSort string
108+
ExplorePagingSortOption []string
109+
}
110+
54111
type ConfigStruct struct {
55112
Picture *PictureStruct
56113
Repository *RepositoryStruct
114+
UI *UIStruct
57115
}
58116

59117
var (
@@ -71,6 +129,22 @@ func initDefaultConfig() {
71129
Repository: &RepositoryStruct{
72130
OpenWithEditorApps: config.ValueJSON[OpenWithEditorAppsType]("repository.open-with.editor-apps"),
73131
},
132+
UI: &UIStruct{
133+
ExplorePagingNum: config.ValueJSON[int]("ui.explore_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "EXPLORE_PAGING_NUM"}).WithDefault(20),
134+
SitemapPagingNum: config.ValueJSON[int]("ui.sitemap_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "SITEMAP_PAGING_NUM"}).WithDefault(20),
135+
IssuePagingNum: config.ValueJSON[int]("ui.issue_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "ISSUE_PAGING_NUM"}).WithDefault(20),
136+
RepoSearchPagingNum: config.ValueJSON[int]("ui.repo_search_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "REPO_SEARCH_PAGING_NUM"}).WithDefault(20),
137+
MembersPagingNum: config.ValueJSON[int]("ui.members_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "MEMBERS_PAGING_NUM"}).WithDefault(20),
138+
FeedMaxCommitNum: config.ValueJSON[int]("ui.feed_max_commit_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "FEED_MAX_COMMIT_NUM"}).WithDefault(20),
139+
FeedPagingNum: config.ValueJSON[int]("ui.feed_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "FEED_PAGE_NUM"}).WithDefault(20),
140+
PackagesPagingNum: config.ValueJSON[int]("ui.packages_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "PACKAGES_PAGING_NUM"}).WithDefault(20),
141+
GraphMaxCommitNum: config.ValueJSON[int]("ui.graph_max_commit_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "GRAPH_MAX_COMMIT_NUM"}).WithDefault(100),
142+
CodeCommentLines: config.ValueJSON[int]("ui.code_comment_lines").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "CODE_COMMENT_LINES"}).WithDefault(4),
143+
ShowUserEmail: config.ValueJSON[bool]("ui.show_user_email").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "SHOW_USER_EMAIL"}).WithDefault(true),
144+
SearchRepoDescription: config.ValueJSON[bool]("ui.search_repo_description").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "SEARCH_REPO_DESCRIPTION"}).WithDefault(false),
145+
OnlyShowRelevantRepos: config.ValueJSON[bool]("ui.only_show_relevant_repos").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "ONLY_SHOW_RELEVANT_REPOS"}).WithDefault(false),
146+
ExploreDefaultSort: config.ValueJSON[string]("ui.explore_paging_default_sort").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "EXPLORE_PAGING_DEFAULT_SORT"}).WithDefault("recentupdate"),
147+
},
74148
}
75149
}
76150

modules/util/util.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ import (
88
"crypto/rand"
99
"fmt"
1010
"math/big"
11+
"reflect"
12+
"slices"
1113
"strconv"
1214
"strings"
1315

16+
"code.gitea.io/gitea/modules/json"
1417
"code.gitea.io/gitea/modules/optional"
1518

1619
"golang.org/x/text/cases"
@@ -257,3 +260,56 @@ func ReserveLineBreakForTextarea(input string) string {
257260
// Other than this, we should respect the original content, even leading or trailing spaces.
258261
return strings.ReplaceAll(input, "\r\n", "\n")
259262
}
263+
264+
func ConfigSectionToMap(in any, section string, skipFields ...string) (map[string]string, error) {
265+
if section == "" {
266+
return nil, fmt.Errorf("section is empty")
267+
}
268+
out := map[string]string{}
269+
270+
v := reflect.ValueOf(in)
271+
if v.Kind() == reflect.Ptr {
272+
v = v.Elem()
273+
}
274+
if v.Kind() != reflect.Struct {
275+
return nil, fmt.Errorf("in is not a struct")
276+
}
277+
278+
t := v.Type()
279+
for i := 0; i < v.NumField(); i++ {
280+
fi := t.Field(i)
281+
fieldName := fi.Name
282+
if slices.Contains(skipFields, fieldName) {
283+
continue
284+
}
285+
if tagValue := fi.Tag.Get("ini"); tagValue == "-" {
286+
continue
287+
} else if tagValue != "" {
288+
fieldName = tagValue
289+
}
290+
switch v.FieldByName(fi.Name).Kind() {
291+
case reflect.Bool,
292+
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
293+
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
294+
out[fmt.Sprintf("%s.%s", section, ToSnakeCase(fieldName))] = fmt.Sprintf("%v", v.FieldByName(fi.Name).Interface())
295+
case reflect.String:
296+
marshal, err := json.Marshal(v.FieldByName(fi.Name).Interface())
297+
if err != nil {
298+
return nil, err
299+
}
300+
out[fmt.Sprintf("%s.%s", section, ToSnakeCase(fieldName))] = fmt.Sprintf("%v", string(marshal))
301+
case reflect.Slice, reflect.Array:
302+
if v.FieldByName(fi.Name).Len() == 0 {
303+
continue
304+
}
305+
marshal, err := json.Marshal(v.FieldByName(fi.Name).Interface())
306+
if err != nil {
307+
return nil, err
308+
}
309+
out[fmt.Sprintf("%s.%s", section, ToSnakeCase(fieldName))] = fmt.Sprintf("%v", string(marshal))
310+
default:
311+
}
312+
}
313+
314+
return out, nil
315+
}

0 commit comments

Comments
 (0)