diff --git a/models/issues/milestone_test.go b/models/issues/milestone_test.go index 28cd0c028b8af..bd3dee3f7a9c3 100644 --- a/models/issues/milestone_test.go +++ b/models/issues/milestone_test.go @@ -98,7 +98,7 @@ func TestGetMilestones(t *testing.T) { milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ ListOptions: db.ListOptions{ Page: page, - PageSize: setting.UI.IssuePagingNum, + PageSize: setting.Config().UI.IssuePagingNum.Value(t.Context()), }, RepoID: repo.ID, IsClosed: optional.Some(false), @@ -115,7 +115,7 @@ func TestGetMilestones(t *testing.T) { milestones, err = db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ ListOptions: db.ListOptions{ Page: page, - PageSize: setting.UI.IssuePagingNum, + PageSize: setting.Config().UI.IssuePagingNum.Value(t.Context()), }, RepoID: repo.ID, IsClosed: optional.Some(true), @@ -231,7 +231,7 @@ func TestGetMilestonesByRepoIDs(t *testing.T) { openMilestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ ListOptions: db.ListOptions{ Page: page, - PageSize: setting.UI.IssuePagingNum, + PageSize: setting.Config().UI.IssuePagingNum.Value(t.Context()), }, RepoIDs: []int64{repo1.ID, repo2.ID}, IsClosed: optional.Some(false), @@ -249,7 +249,7 @@ func TestGetMilestonesByRepoIDs(t *testing.T) { issues_model.FindMilestoneOptions{ ListOptions: db.ListOptions{ Page: page, - PageSize: setting.UI.IssuePagingNum, + PageSize: setting.Config().UI.IssuePagingNum.Value(t.Context()), }, RepoIDs: []int64{repo1.ID, repo2.ID}, IsClosed: optional.Some(true), diff --git a/models/migrations/fixtures/Test_MigrateIniToDatabase/system_setting.yml b/models/migrations/fixtures/Test_MigrateIniToDatabase/system_setting.yml new file mode 100644 index 0000000000000..8c2a74c26cae9 --- /dev/null +++ b/models/migrations/fixtures/Test_MigrateIniToDatabase/system_setting.yml @@ -0,0 +1,16 @@ +- + id: 1 + setting_key: revision + version: 1 + +- + id: 2 + setting_key: picture.enable_federated_avatar + setting_value: false + version: 1 + +- + id: 3 + setting_key: picture.disable_gravatar + setting_value: true + version: 1 diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 297c50a267f2d..b7842498c3e38 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -377,6 +377,7 @@ func prepareMigrationTasks() []*migration { newMigration(314, "Update OwnerID as zero for repository level action tables", v1_24.UpdateOwnerIDOfRepoLevelActionsTables), newMigration(315, "Add Ephemeral to ActionRunner", v1_24.AddEphemeralToActionRunner), newMigration(316, "Add description for secrets and variables", v1_24.AddDescriptionForSecretsAndVariables), + newMigration(317, "Migrate the configuration of the ui section of the ini configuration file to the system setting table.", v1_24.MigrateIniToDatabase), } return preparedMigrations } diff --git a/models/migrations/v1_24/main_test.go b/models/migrations/v1_24/main_test.go new file mode 100644 index 0000000000000..53050554d47ee --- /dev/null +++ b/models/migrations/v1_24/main_test.go @@ -0,0 +1,14 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_24 //nolint + +import ( + "testing" + + "code.gitea.io/gitea/models/migrations/base" +) + +func TestMain(m *testing.M) { + base.MainTest(m) +} diff --git a/models/migrations/v1_24/v317.go b/models/migrations/v1_24/v317.go new file mode 100644 index 0000000000000..de9cd46a60af1 --- /dev/null +++ b/models/migrations/v1_24/v317.go @@ -0,0 +1,103 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_24 //nolint + +import ( + "fmt" + "math" + "strconv" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" + + "xorm.io/xorm" +) + +const keyRevision = "revision" + +type Setting struct { + ID int64 `xorm:"pk autoincr"` + SettingKey string `xorm:"varchar(255) unique"` // key should be lowercase + SettingValue string `xorm:"text"` + Version int `xorm:"version"` + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` +} + +// TableName sets the table name for the settings struct +func (s *Setting) TableName() string { + return "system_setting" +} + +func MigrateIniToDatabase(x *xorm.Engine) error { + uiMap := make(map[string]string) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("EXPLORE_PAGING_NUM"))] = strconv.Itoa(setting.ExplorePagingNum) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("SITEMAP_PAGING_NUM"))] = strconv.Itoa(setting.SitemapPagingNum) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("ISSUE_PAGING_NUM"))] = strconv.Itoa(setting.IssuePagingNum) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("REPO_SEARCH_PAGING_NUM"))] = strconv.Itoa(setting.RepoSearchPagingNum) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("MEMBERS_PAGING_NUM"))] = strconv.Itoa(setting.MembersPagingNum) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("FEED_MAX_COMMIT_NUM"))] = strconv.Itoa(setting.FeedMaxCommitNum) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("FEED_PAGING_NUM"))] = strconv.Itoa(setting.FeedPagingNum) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("PACKAGES_PAGING_NUM"))] = strconv.Itoa(setting.PackagesPagingNum) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("CODE_COMMENT_LINES"))] = strconv.Itoa(setting.CodeCommentLines) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("SHOW_USER_EMAIL"))] = strconv.FormatBool(setting.ShowUserEmail) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("SEARCH_REPO_DESCRIPTION"))] = strconv.FormatBool(setting.SearchRepoDescription) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("ONLY_SHOW_RELEVANT_REPOS"))] = strconv.FormatBool(setting.OnlyShowRelevantRepos) + uiMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("EXPLORE_PAGING_DEFAULT_SORT"))] = fmt.Sprintf("\"%s\"", setting.ExploreDefaultSort) + + sess := x.NewSession() + defer sess.Close() + + if err := sess.Begin(); err != nil { + return err + } + + if err := sess.Sync(new(Setting)); err != nil { + return err + } + + _ = getRevision(sess) // prepare the "revision" key ahead + + if _, err := sess.Exec("UPDATE system_setting SET version=version+1 WHERE setting_key=?", keyRevision); err != nil { + return err + } + for k, v := range uiMap { + res, err := sess.Exec("UPDATE system_setting SET version=version+1, setting_value=? WHERE setting_key=?", v, k) + if err != nil { + return err + } + rows, _ := res.RowsAffected() + if rows == 0 { // if no existing row, insert a new row + if _, err = sess.Insert(&Setting{SettingKey: k, SettingValue: v}); err != nil { + return err + } + } + } + + return sess.Commit() +} + +func getRevision(sess *xorm.Session) int { + revision := &Setting{} + exist, err := sess.Where("setting_key = ?", keyRevision).Get(revision) + if err != nil { + return 0 + } else if !exist { + _, err = sess.Insert(&Setting{SettingKey: keyRevision, Version: 1}) + if err != nil { + return 0 + } + return 1 + } + + if revision.Version <= 0 || revision.Version >= math.MaxInt-1 { + _, err = sess.Exec("UPDATE system_setting SET version=1 WHERE setting_key=?", keyRevision) + if err != nil { + return 0 + } + return 1 + } + return revision.Version +} diff --git a/models/migrations/v1_24/v317_test.go b/models/migrations/v1_24/v317_test.go new file mode 100644 index 0000000000000..0b05b71acdb87 --- /dev/null +++ b/models/migrations/v1_24/v317_test.go @@ -0,0 +1,27 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_24 //nolint + +import ( + "testing" + + "code.gitea.io/gitea/models/migrations/base" + + "github.com/stretchr/testify/assert" +) + +func Test_MigrateIniToDatabase(t *testing.T) { + // Prepare and load the testing database + x, deferable := base.PrepareTestEnv(t, 0, new(Setting)) + defer deferable() + if x == nil || t.Failed() { + return + } + + assert.NoError(t, MigrateIniToDatabase(x)) + + cnt, err := x.Table("system_setting").Where("setting_key LIKE 'ui.%'").Count() + assert.NoError(t, err) + assert.EqualValues(t, 13, cnt) +} diff --git a/modules/indexer/code/gitgrep/gitgrep.go b/modules/indexer/code/gitgrep/gitgrep.go index 093c189ba39e3..f9a9f663b5e5c 100644 --- a/modules/indexer/code/gitgrep/gitgrep.go +++ b/modules/indexer/code/gitgrep/gitgrep.go @@ -47,8 +47,8 @@ func PerformSearch(ctx context.Context, page int, repoID int64, gitRepo *git.Rep } total = len(res) - pageStart := min((page-1)*setting.UI.RepoSearchPagingNum, len(res)) - pageEnd := min(page*setting.UI.RepoSearchPagingNum, len(res)) + pageStart := min((page-1)*setting.Config().UI.RepoSearchPagingNum.Value(ctx), len(res)) + pageEnd := min(page*setting.Config().UI.RepoSearchPagingNum.Value(ctx), len(res)) res = res[pageStart:pageEnd] for _, r := range res { searchResults = append(searchResults, &code_indexer.Result{ diff --git a/modules/setting/config.go b/modules/setting/config.go index 03558574c2110..f766847d5e890 100644 --- a/modules/setting/config.go +++ b/modules/setting/config.go @@ -4,6 +4,7 @@ package setting import ( + "context" "sync" "code.gitea.io/gitea/modules/log" @@ -51,9 +52,62 @@ type RepositoryStruct struct { OpenWithEditorApps *config.Value[OpenWithEditorAppsType] } +type UIStruct struct { + ExplorePagingNum *config.Value[int] + SitemapPagingNum *config.Value[int] + IssuePagingNum *config.Value[int] + RepoSearchPagingNum *config.Value[int] + MembersPagingNum *config.Value[int] + FeedMaxCommitNum *config.Value[int] + FeedPagingNum *config.Value[int] + PackagesPagingNum *config.Value[int] + CodeCommentLines *config.Value[int] + ShowUserEmail *config.Value[bool] + SearchRepoDescription *config.Value[bool] + OnlyShowRelevantRepos *config.Value[bool] + ExploreDefaultSort *config.Value[string] +} + +func (u *UIStruct) ToStruct(ctx context.Context) UIForm { + return UIForm{ + ExplorePagingNum: u.ExplorePagingNum.Value(ctx), + SitemapPagingNum: u.SitemapPagingNum.Value(ctx), + IssuePagingNum: u.IssuePagingNum.Value(ctx), + RepoSearchPagingNum: u.RepoSearchPagingNum.Value(ctx), + MembersPagingNum: u.MembersPagingNum.Value(ctx), + FeedMaxCommitNum: u.FeedMaxCommitNum.Value(ctx), + FeedPagingNum: u.FeedPagingNum.Value(ctx), + PackagesPagingNum: u.PackagesPagingNum.Value(ctx), + CodeCommentLines: u.CodeCommentLines.Value(ctx), + ShowUserEmail: u.ShowUserEmail.Value(ctx), + SearchRepoDescription: u.SearchRepoDescription.Value(ctx), + OnlyShowRelevantRepos: u.OnlyShowRelevantRepos.Value(ctx), + ExplorePagingDefaultSort: u.ExploreDefaultSort.Value(ctx), + ExplorePagingSortOption: []string{"recentupdate", "alphabetically", "reverselastlogin", "newest", "oldest"}, + } +} + +type UIForm struct { + ExplorePagingNum int + SitemapPagingNum int + IssuePagingNum int + RepoSearchPagingNum int + MembersPagingNum int + FeedMaxCommitNum int + FeedPagingNum int + PackagesPagingNum int + CodeCommentLines int + ShowUserEmail bool + SearchRepoDescription bool + OnlyShowRelevantRepos bool + ExplorePagingDefaultSort string + ExplorePagingSortOption []string +} + type ConfigStruct struct { Picture *PictureStruct Repository *RepositoryStruct + UI *UIStruct } var ( @@ -71,6 +125,21 @@ func initDefaultConfig() { Repository: &RepositoryStruct{ OpenWithEditorApps: config.ValueJSON[OpenWithEditorAppsType]("repository.open-with.editor-apps"), }, + UI: &UIStruct{ + ExplorePagingNum: config.ValueJSON[int]("ui.explore_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "EXPLORE_PAGING_NUM"}).WithDefault(20), + SitemapPagingNum: config.ValueJSON[int]("ui.sitemap_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "SITEMAP_PAGING_NUM"}).WithDefault(20), + IssuePagingNum: config.ValueJSON[int]("ui.issue_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "ISSUE_PAGING_NUM"}).WithDefault(20), + RepoSearchPagingNum: config.ValueJSON[int]("ui.repo_search_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "REPO_SEARCH_PAGING_NUM"}).WithDefault(20), + MembersPagingNum: config.ValueJSON[int]("ui.members_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "MEMBERS_PAGING_NUM"}).WithDefault(20), + FeedMaxCommitNum: config.ValueJSON[int]("ui.feed_max_commit_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "FEED_MAX_COMMIT_NUM"}).WithDefault(20), + FeedPagingNum: config.ValueJSON[int]("ui.feed_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "FEED_PAGE_NUM"}).WithDefault(20), + PackagesPagingNum: config.ValueJSON[int]("ui.packages_paging_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "PACKAGES_PAGING_NUM"}).WithDefault(20), + CodeCommentLines: config.ValueJSON[int]("ui.code_comment_lines").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "CODE_COMMENT_LINES"}).WithDefault(4), + ShowUserEmail: config.ValueJSON[bool]("ui.show_user_email").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "SHOW_USER_EMAIL"}).WithDefault(true), + SearchRepoDescription: config.ValueJSON[bool]("ui.search_repo_description").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "SEARCH_REPO_DESCRIPTION"}).WithDefault(false), + OnlyShowRelevantRepos: config.ValueJSON[bool]("ui.only_show_relevant_repos").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "ONLY_SHOW_RELEVANT_REPOS"}).WithDefault(false), + ExploreDefaultSort: config.ValueJSON[string]("ui.explore_paging_default_sort").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "EXPLORE_PAGING_DEFAULT_SORT"}).WithDefault("recentupdate"), + }, } } diff --git a/modules/setting/ui.go b/modules/setting/ui.go index 3d9c916bf7f72..f4289647a3d25 100644 --- a/modules/setting/ui.go +++ b/modules/setting/ui.go @@ -11,156 +11,143 @@ import ( ) // UI settings -var UI = struct { - ExplorePagingNum int - SitemapPagingNum int - IssuePagingNum int - RepoSearchPagingNum int - MembersPagingNum int - FeedMaxCommitNum int - FeedPagingNum int - PackagesPagingNum int - GraphMaxCommitNum int - CodeCommentLines int - ReactionMaxUserNum int - MaxDisplayFileSize int64 - ShowUserEmail bool - DefaultShowFullName bool - DefaultTheme string - Themes []string - FileIconTheme string - Reactions []string - ReactionsLookup container.Set[string] `ini:"-"` - CustomEmojis []string - CustomEmojisMap map[string]string `ini:"-"` - SearchRepoDescription bool - OnlyShowRelevantRepos bool - ExploreDefaultSort string `ini:"EXPLORE_PAGING_DEFAULT_SORT"` - PreferredTimestampTense string - - AmbiguousUnicodeDetection bool - - Notification struct { - MinTimeout time.Duration - TimeoutStep time.Duration - MaxTimeout time.Duration - EventSourceUpdateTime time.Duration - } `ini:"ui.notification"` - - SVG struct { - Enabled bool `ini:"ENABLE_RENDER"` - } `ini:"ui.svg"` - - CSV struct { - MaxFileSize int64 - MaxRows int - } `ini:"ui.csv"` - - Admin struct { - UserPagingNum int - RepoPagingNum int - NoticePagingNum int - OrgPagingNum int - } `ini:"ui.admin"` - User struct { - RepoPagingNum int - OrgPagingNum int - } `ini:"ui.user"` - Meta struct { - Author string - Description string - Keywords string - } `ini:"ui.meta"` -}{ - ExplorePagingNum: 20, - SitemapPagingNum: 20, - IssuePagingNum: 20, - RepoSearchPagingNum: 20, - MembersPagingNum: 20, - FeedMaxCommitNum: 5, - FeedPagingNum: 20, - PackagesPagingNum: 20, - GraphMaxCommitNum: 100, - CodeCommentLines: 4, - ReactionMaxUserNum: 10, - MaxDisplayFileSize: 8388608, - DefaultTheme: `gitea-auto`, - FileIconTheme: `material`, - Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, - CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`}, - CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"}, - ExploreDefaultSort: "recentupdate", - PreferredTimestampTense: "mixed", - - AmbiguousUnicodeDetection: true, - - Notification: struct { - MinTimeout time.Duration - TimeoutStep time.Duration - MaxTimeout time.Duration - EventSourceUpdateTime time.Duration - }{ - MinTimeout: 10 * time.Second, - TimeoutStep: 10 * time.Second, - MaxTimeout: 60 * time.Second, - EventSourceUpdateTime: 10 * time.Second, - }, - SVG: struct { - Enabled bool `ini:"ENABLE_RENDER"` - }{ - Enabled: true, - }, - CSV: struct { - MaxFileSize int64 - MaxRows int - }{ - MaxFileSize: 524288, - MaxRows: 2500, - }, - Admin: struct { - UserPagingNum int - RepoPagingNum int - NoticePagingNum int - OrgPagingNum int - }{ - UserPagingNum: 50, - RepoPagingNum: 50, - NoticePagingNum: 25, - OrgPagingNum: 50, - }, - User: struct { - RepoPagingNum int - OrgPagingNum int - }{ - RepoPagingNum: 15, - OrgPagingNum: 15, - }, - Meta: struct { - Author string - Description string - Keywords string +var ( + UI = struct { + GraphMaxCommitNum int + ReactionMaxUserNum int + MaxDisplayFileSize int64 + DefaultShowFullName bool + DefaultTheme string + Themes []string + FileIconTheme string + Reactions []string + ReactionsLookup container.Set[string] `ini:"-"` + CustomEmojis []string + CustomEmojisMap map[string]string `ini:"-"` + PreferredTimestampTense string + + AmbiguousUnicodeDetection bool + + Notification struct { + MinTimeout time.Duration + TimeoutStep time.Duration + MaxTimeout time.Duration + EventSourceUpdateTime time.Duration + } `ini:"ui.notification"` + + SVG struct { + Enabled bool `ini:"ENABLE_RENDER"` + } `ini:"ui.svg"` + + CSV struct { + MaxFileSize int64 + MaxRows int + } `ini:"ui.csv"` + + Admin struct { + UserPagingNum int + RepoPagingNum int + NoticePagingNum int + OrgPagingNum int + } `ini:"ui.admin"` + User struct { + RepoPagingNum int + OrgPagingNum int + } `ini:"ui.user"` + Meta struct { + Author string + Description string + Keywords string + } `ini:"ui.meta"` }{ - Author: "Gitea - Git with a cup of tea", - Description: "Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go", - Keywords: "go,git,self-hosted,gitea", - }, -} + GraphMaxCommitNum: 100, + ReactionMaxUserNum: 10, + MaxDisplayFileSize: 8388608, + DefaultTheme: `gitea-auto`, + FileIconTheme: `material`, + Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, + CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`}, + CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"}, + PreferredTimestampTense: "mixed", + + AmbiguousUnicodeDetection: true, + + Notification: struct { + MinTimeout time.Duration + TimeoutStep time.Duration + MaxTimeout time.Duration + EventSourceUpdateTime time.Duration + }{ + MinTimeout: 10 * time.Second, + TimeoutStep: 10 * time.Second, + MaxTimeout: 60 * time.Second, + EventSourceUpdateTime: 10 * time.Second, + }, + SVG: struct { + Enabled bool `ini:"ENABLE_RENDER"` + }{ + Enabled: true, + }, + CSV: struct { + MaxFileSize int64 + MaxRows int + }{ + MaxFileSize: 524288, + MaxRows: 2500, + }, + Admin: struct { + UserPagingNum int + RepoPagingNum int + NoticePagingNum int + OrgPagingNum int + }{ + UserPagingNum: 50, + RepoPagingNum: 50, + NoticePagingNum: 25, + OrgPagingNum: 50, + }, + User: struct { + RepoPagingNum int + OrgPagingNum int + }{ + RepoPagingNum: 15, + OrgPagingNum: 15, + }, + Meta: struct { + Author string + Description string + Keywords string + }{ + Author: "Gitea - Git with a cup of tea", + Description: "Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go", + Keywords: "go,git,self-hosted,gitea", + }, + } + + ExplorePagingNum int // Depreciated: migrated to database + SitemapPagingNum int // Depreciated: migrated to database + IssuePagingNum int // Depreciated: migrated to database + RepoSearchPagingNum int // Depreciated: migrated to database + MembersPagingNum int // Depreciated: migrated to database + FeedMaxCommitNum int // Depreciated: migrated to database + FeedPagingNum int // Depreciated: migrated to database + PackagesPagingNum int // Depreciated: migrated to database + CodeCommentLines int // Depreciated: migrated to database + ShowUserEmail bool // Depreciated: migrated to database + SearchRepoDescription bool // Depreciated: migrated to database + OnlyShowRelevantRepos bool // Depreciated: migrated to database + ExploreDefaultSort string // Depreciated: migrated to database +) func loadUIFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "ui", &UI) sec := rootCfg.Section("ui") - UI.ShowUserEmail = sec.Key("SHOW_USER_EMAIL").MustBool(true) UI.DefaultShowFullName = sec.Key("DEFAULT_SHOW_FULL_NAME").MustBool(false) - UI.SearchRepoDescription = sec.Key("SEARCH_REPO_DESCRIPTION").MustBool(true) if UI.PreferredTimestampTense != "mixed" && UI.PreferredTimestampTense != "absolute" { log.Fatal("ui.PREFERRED_TIMESTAMP_TENSE must be either 'mixed' or 'absolute'") } - // OnlyShowRelevantRepos=false is important for many private/enterprise instances, - // because many private repositories do not have "description/topic", users just want to search by their names. - UI.OnlyShowRelevantRepos = sec.Key("ONLY_SHOW_RELEVANT_REPOS").MustBool(false) - UI.ReactionsLookup = make(container.Set[string]) for _, reaction := range UI.Reactions { UI.ReactionsLookup.Add(reaction) @@ -169,4 +156,33 @@ func loadUIFrom(rootCfg ConfigProvider) { for _, emoji := range UI.CustomEmojis { UI.CustomEmojisMap[emoji] = ":" + emoji + ":" } + + ExplorePagingNum = sec.Key("EXPLORE_PAGING_NUM").MustInt(20) + deprecatedSettingDB(rootCfg, "ui", "EXPLORE_PAGING_NUM") + SitemapPagingNum = sec.Key("SITEMAP_PAGING_NUM").MustInt(20) + deprecatedSettingDB(rootCfg, "ui", "SITEMAP_PAGING_NUM") + IssuePagingNum = sec.Key("ISSUE_PAGING_NUM").MustInt(20) + deprecatedSettingDB(rootCfg, "ui", "ISSUE_PAGING_NUM") + RepoSearchPagingNum = sec.Key("REPO_SEARCH_PAGING_NUM").MustInt(20) + deprecatedSettingDB(rootCfg, "ui", "REPO_SEARCH_PAGING_NUM") + MembersPagingNum = sec.Key("MEMBERS_PAGING_NUM").MustInt(20) + deprecatedSettingDB(rootCfg, "ui", "MEMBERS_PAGING_NUM") + FeedMaxCommitNum = sec.Key("FEED_MAX_COMMIT_NUM").MustInt(5) + deprecatedSettingDB(rootCfg, "ui", "FEED_MAX_COMMIT_NUM") + FeedPagingNum = sec.Key("FEED_PAGING_NUM").MustInt(20) + deprecatedSettingDB(rootCfg, "ui", "FEED_PAGING_NUM") + PackagesPagingNum = sec.Key("PACKAGES_PAGING_NUM").MustInt(20) + deprecatedSettingDB(rootCfg, "ui", "PACKAGES_PAGING_NUM") + CodeCommentLines = sec.Key("CODE_COMMENT_LINES").MustInt(4) + deprecatedSettingDB(rootCfg, "ui", "CODE_COMMENT_LINES") + ShowUserEmail = sec.Key("SHOW_USER_EMAIL").MustBool(true) + deprecatedSettingDB(rootCfg, "ui", "SHOW_USER_EMAIL") + SearchRepoDescription = sec.Key("SEARCH_REPO_DESCRIPTION").MustBool(true) + deprecatedSettingDB(rootCfg, "ui", "SEARCH_REPO_DESCRIPTION") + // OnlyShowRelevantRepos=false is important for many private/enterprise instances, + // because many private repositories do not have "description/topic", users just want to search by their names. + OnlyShowRelevantRepos = sec.Key("ONLY_SHOW_RELEVANT_REPOS").MustBool(false) + deprecatedSettingDB(rootCfg, "ui", "ONLY_SHOW_RELEVANT_REPOS") + ExploreDefaultSort = sec.Key("EXPLORE_PAGING_DEFAULT_SORT").MustString("recentupdate") + deprecatedSettingDB(rootCfg, "ui", "EXPLORE_PAGING_DEFAULT_SORT") } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 876e135b22f57..973991feef406 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3016,6 +3016,7 @@ dashboard.sync_branch.started = Branches Sync started dashboard.sync_tag.started = Tags Sync started dashboard.rebuild_issue_indexer = Rebuild issue indexer dashboard.sync_repo_licenses = Sync repo licenses +dashboard.update_settings_success = Update settings success users.user_manage_panel = User Account Management users.new_account = Create User Account @@ -3364,6 +3365,21 @@ config.session_life_time = Session Life Time config.https_only = HTTPS Only config.cookie_life_time = Cookie Life Time +config.ui_config = UI Configuration +config.ui.explore_paging_num = Explore Paging Number +config.ui.issue_paging_num = Issue Paging Number +config.ui.feed_max_commit_numb = Feed Max Commit Number +config.ui.feed_paging_num = Feed Paging Number +config.ui.packages_paging_num = Packages Paging Number +config.ui.repo_search_paging_num = Repo Search Paging Number +config.ui.members_paging_num = Member Paging Number +config.ui.sitemap_paging_num = Sitemap Paging Number +config.ui.code_comment_lines = Code Comment Lines +config.ui.explore_paging_default_sort = Explore Paging Default Sort +config.ui.show_user_email = Show User Email +config.ui.search_repo_description = Search Repo Description +config.ui.only_show_relevant_repos = Only Show Relevant Repos + config.picture_config = Picture and Avatar Configuration config.picture_service = Picture Service config.disable_gravatar = Disable Gravatar diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index c9575ff98a001..fdcee0d0e7d20 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -260,7 +260,7 @@ func SearchIssues(ctx *context.APIContext) { // so the default limit is set to fit UI needs limit := ctx.FormInt("limit") if limit == 0 { - limit = setting.UI.IssuePagingNum + limit = setting.Config().UI.IssuePagingNum.Value(ctx) } else if limit > setting.API.MaxResponseItems { limit = setting.API.MaxResponseItems } diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go index 520f14e89fa2e..f0ce8a5e030c8 100644 --- a/routers/web/admin/config.go +++ b/routers/web/admin/config.go @@ -5,6 +5,7 @@ package admin import ( + "fmt" "net/http" "net/url" "strconv" @@ -19,7 +20,9 @@ import ( "code.gitea.io/gitea/modules/setting/config" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/mailer" "gitea.com/go-chi/session" @@ -191,6 +194,7 @@ func ConfigSettings(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("admin.config_settings") ctx.Data["PageIsAdminConfig"] = true ctx.Data["PageIsAdminConfigSettings"] = true + ctx.Data["UI"] = setting.Config().UI.ToStruct(ctx) ctx.Data["DefaultOpenWithEditorAppsString"] = setting.DefaultOpenWithEditorApps().ToTextareaString() ctx.HTML(http.StatusOK, tplConfigSettings) } @@ -253,3 +257,35 @@ func ChangeConfig(ctx *context.Context) { config.GetDynGetter().InvalidateCache() ctx.JSONOK() } + +func ChangeUIConfig(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.UIForm) + log.Debug("ChangeUIConfig form: %+v", form) + formMap := make(map[string]string) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("EXPLORE_PAGING_NUM"))] = strconv.Itoa(form.ExplorePagingNum) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("SITEMAP_PAGING_NUM"))] = strconv.Itoa(form.SitemapPagingNum) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("ISSUE_PAGING_NUM"))] = strconv.Itoa(form.IssuePagingNum) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("REPO_SEARCH_PAGING_NUM"))] = strconv.Itoa(form.RepoSearchPagingNum) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("MEMBERS_PAGING_NUM"))] = strconv.Itoa(form.MembersPagingNum) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("FEED_MAX_COMMIT_NUM"))] = strconv.Itoa(form.FeedMaxCommitNum) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("FEED_PAGING_NUM"))] = strconv.Itoa(form.FeedPagingNum) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("PACKAGES_PAGING_NUM"))] = strconv.Itoa(form.PackagesPagingNum) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("CODE_COMMENT_LINES"))] = strconv.Itoa(form.CodeCommentLines) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("SHOW_USER_EMAIL"))] = strconv.FormatBool(form.ShowUserEmail) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("SEARCH_REPO_DESCRIPTION"))] = strconv.FormatBool(form.SearchRepoDescription) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("ONLY_SHOW_RELEVANT_REPOS"))] = strconv.FormatBool(form.OnlyShowRelevantRepos) + formMap[fmt.Sprintf("ui.%s", util.ToSnakeCase("EXPLORE_PAGING_DEFAULT_SORT"))] = fmt.Sprintf("\"%s\"", form.ExplorePagingDefaultSort) + + log.Debug("ChangeUIConfig form: %+v", formMap) + + if err := system_model.SetSettings(ctx, formMap); err != nil { + log.Error("set ui configuration failed: %v", err) + ctx.ServerError("SetSettings", err) + return + } + + config.GetDynGetter().InvalidateCache() + + ctx.Flash.Success(ctx.Tr("admin.dashboard.update_settings_success")) + ctx.Redirect(setting.AppSubURL + "/-/admin/config/settings") +} diff --git a/routers/web/admin/packages.go b/routers/web/admin/packages.go index 5122342259584..ef83649be8868 100644 --- a/routers/web/admin/packages.go +++ b/routers/web/admin/packages.go @@ -38,7 +38,7 @@ func Packages(ctx *context.Context) { Sort: sort, IsInternal: optional.Some(false), Paginator: &db.ListOptions{ - PageSize: setting.UI.PackagesPagingNum, + PageSize: setting.Config().UI.PackagesPagingNum.Value(ctx), Page: page, }, }) @@ -76,7 +76,7 @@ func Packages(ctx *context.Context) { ctx.Data["TotalBlobSize"] = totalBlobSize - totalUnreferencedBlobSize ctx.Data["TotalUnreferencedBlobSize"] = totalUnreferencedBlobSize - pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) + pager := context.NewPagination(int(total), setting.Config().UI.PackagesPagingNum.Value(ctx), page, 5) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go index 8f6518a4fc37c..8e10fbea775ac 100644 --- a/routers/web/explore/code.go +++ b/routers/web/explore/code.go @@ -78,7 +78,7 @@ func Code(ctx *context.Context) { Language: prepareSearch.Language, Paginator: &db.ListOptions{ Page: page, - PageSize: setting.UI.RepoSearchPagingNum, + PageSize: setting.Config().UI.RepoSearchPagingNum.Value(ctx), }, }) if err != nil { @@ -129,7 +129,7 @@ func Code(ctx *context.Context) { ctx.Data["SearchResults"] = searchResults ctx.Data["SearchResultLanguages"] = searchResultLanguages - pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) + pager := context.NewPagination(total, setting.Config().UI.RepoSearchPagingNum.Value(ctx), page, 5) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager diff --git a/routers/web/explore/org.go b/routers/web/explore/org.go index 7bb71acfd78e0..5fc2f9e400470 100644 --- a/routers/web/explore/org.go +++ b/routers/web/explore/org.go @@ -40,14 +40,14 @@ func Organizations(ctx *context.Context) { ) sortOrder := ctx.FormString("sort") if sortOrder == "" { - sortOrder = util.Iif(supportedSortOrders.Contains(setting.UI.ExploreDefaultSort), setting.UI.ExploreDefaultSort, "newest") + sortOrder = util.Iif(supportedSortOrders.Contains(setting.Config().UI.ExploreDefaultSort.Value(ctx)), setting.Config().UI.ExploreDefaultSort.Value(ctx), "newest") ctx.SetFormString("sort", sortOrder) } RenderUserSearch(ctx, &user_model.SearchUserOptions{ Actor: ctx.Doer, Type: user_model.UserTypeOrganization, - ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum}, + ListOptions: db.ListOptions{PageSize: setting.Config().UI.ExplorePagingNum.Value(ctx)}, Visible: visibleTypes, SupportedSortOrders: supportedSortOrders, diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go index cf3128314bdee..9b85abf19006d 100644 --- a/routers/web/explore/repo.go +++ b/routers/web/explore/repo.go @@ -46,7 +46,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { } if isSitemap { - opts.PageSize = setting.UI.SitemapPagingNum + opts.PageSize = setting.Config().UI.SitemapPagingNum.Value(ctx) } var ( @@ -58,7 +58,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { sortOrder := ctx.FormString("sort") if sortOrder == "" { - sortOrder = setting.UI.ExploreDefaultSort + sortOrder = setting.Config().UI.ExploreDefaultSort.Value(ctx) } if order, ok := repo_model.OrderByFlatMap[sortOrder]; ok { @@ -108,7 +108,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { AllLimited: true, TopicOnly: topicOnly, Language: language, - IncludeDescription: setting.UI.SearchRepoDescription, + IncludeDescription: setting.Config().UI.SearchRepoDescription.Value(ctx), OnlyShowRelevant: opts.OnlyShowRelevant, Archived: archived, Fork: fork, @@ -159,7 +159,7 @@ func Repos(ctx *context.Context) { ownerID = ctx.Doer.ID } - onlyShowRelevant := setting.UI.OnlyShowRelevantRepos + onlyShowRelevant := setting.Config().UI.OnlyShowRelevantRepos.Value(ctx) _ = ctx.Req.ParseForm() // parse the form first, to prepare the ctx.Req.Form field if len(ctx.Req.Form[relevantReposOnlyParam]) != 0 { @@ -167,7 +167,7 @@ func Repos(ctx *context.Context) { } RenderRepoSearch(ctx, &RepoSearchOptions{ - PageSize: setting.UI.ExplorePagingNum, + PageSize: setting.Config().UI.ExplorePagingNum.Value(ctx), OwnerID: ownerID, Private: ctx.Doer != nil, TplName: tplExploreRepos, diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go index e1e1ec1cfdbc4..8222521c5dd70 100644 --- a/routers/web/explore/user.go +++ b/routers/web/explore/user.go @@ -44,7 +44,7 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, } if isSitemap { - opts.PageSize = setting.UI.SitemapPagingNum + opts.PageSize = setting.Config().UI.SitemapPagingNum.Value(ctx) } var ( @@ -58,7 +58,7 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, sortOrder := ctx.FormString("sort") if sortOrder == "" { - sortOrder = setting.UI.ExploreDefaultSort + sortOrder = setting.Config().UI.ExploreDefaultSort.Value(ctx) } ctx.Data["SortType"] = sortOrder @@ -116,7 +116,7 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, ctx.Data["Total"] = count ctx.Data["Users"] = users ctx.Data["UsersTwoFaStatus"] = user_model.UserList(users).GetTwoFaStatus(ctx) - ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail + ctx.Data["ShowUserEmail"] = setting.Config().UI.ShowUserEmail.Value(ctx) ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) @@ -147,14 +147,14 @@ func Users(ctx *context.Context) { ) sortOrder := ctx.FormString("sort") if sortOrder == "" { - sortOrder = util.Iif(supportedSortOrders.Contains(setting.UI.ExploreDefaultSort), setting.UI.ExploreDefaultSort, "newest") + sortOrder = util.Iif(supportedSortOrders.Contains(setting.Config().UI.ExploreDefaultSort.Value(ctx)), setting.Config().UI.ExploreDefaultSort.Value(ctx), "newest") ctx.SetFormString("sort", sortOrder) } RenderUserSearch(ctx, &user_model.SearchUserOptions{ Actor: ctx.Doer, Type: user_model.UserTypeIndividual, - ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum}, + ListOptions: db.ListOptions{PageSize: setting.Config().UI.ExplorePagingNum.Value(ctx)}, IsActive: optional.Some(true), Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, diff --git a/routers/web/home.go b/routers/web/home.go index 208cc36dfb833..3f6a1584da06a 100644 --- a/routers/web/home.go +++ b/routers/web/home.go @@ -80,7 +80,7 @@ func HomeSitemap(ctx *context.Context) { } count := int(cnt) idx := 1 - for i := 0; i < count; i += setting.UI.SitemapPagingNum { + for i := 0; i < count; i += setting.Config().UI.SitemapPagingNum.Value(ctx) { m.Add(sitemap.URL{URL: setting.AppURL + "explore/users/sitemap-" + strconv.Itoa(idx) + ".xml"}) idx++ } @@ -99,7 +99,7 @@ func HomeSitemap(ctx *context.Context) { } count := int(cnt) idx := 1 - for i := 0; i < count; i += setting.UI.SitemapPagingNum { + for i := 0; i < count; i += setting.Config().UI.SitemapPagingNum.Value(ctx) { m.Add(sitemap.URL{URL: setting.AppURL + "explore/repos/sitemap-" + strconv.Itoa(idx) + ".xml"}) idx++ } diff --git a/routers/web/org/home.go b/routers/web/org/home.go index e3c2dcf0bdd24..7d44b7a48dad8 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -55,7 +55,7 @@ func home(ctx *context.Context, viewRepositories bool) { var orderBy db.SearchOrderBy sortOrder := ctx.FormString("sort") if _, ok := repo_model.OrderByFlatMap[sortOrder]; !ok { - sortOrder = setting.UI.ExploreDefaultSort // TODO: add new default sort order for org home? + sortOrder = setting.Config().UI.ExploreDefaultSort.Value(ctx) // TODO: add new default sort order for org home? } ctx.Data["SortType"] = sortOrder orderBy = repo_model.OrderByFlatMap[sortOrder] @@ -132,7 +132,7 @@ func home(ctx *context.Context, viewRepositories bool) { Private: ctx.IsSigned, Actor: ctx.Doer, Language: language, - IncludeDescription: setting.UI.SearchRepoDescription, + IncludeDescription: setting.Config().UI.SearchRepoDescription.Value(ctx), Archived: archived, Fork: fork, Mirror: mirror, diff --git a/routers/web/org/members.go b/routers/web/org/members.go index 7d88d6b1adee8..83ed22fa97261 100644 --- a/routers/web/org/members.go +++ b/routers/web/org/members.go @@ -60,9 +60,9 @@ func Members(ctx *context.Context) { return } - pager := context.NewPagination(int(total), setting.UI.MembersPagingNum, page, 5) + pager := context.NewPagination(int(total), setting.Config().UI.MembersPagingNum.Value(ctx), page, 5) opts.ListOptions.Page = page - opts.ListOptions.PageSize = setting.UI.MembersPagingNum + opts.ListOptions.PageSize = setting.Config().UI.MembersPagingNum.Value(ctx) members, membersIsPublic, err := organization.FindOrgMembers(ctx, opts) if err != nil { ctx.ServerError("GetMembers", err) diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 985fd2ca45d4d..2906ed6c1c8cd 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -64,7 +64,7 @@ func Projects(ctx *context.Context) { projects, total, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ ListOptions: db.ListOptions{ Page: page, - PageSize: setting.UI.IssuePagingNum, + PageSize: setting.Config().UI.IssuePagingNum.Value(ctx), }, OwnerID: ctx.ContextUser.ID, IsClosed: optional.Some(isShowClosed), @@ -121,10 +121,10 @@ func Projects(ctx *context.Context) { numPages := 0 if total > 0 { - numPages = (int(total) - 1/setting.UI.IssuePagingNum) + numPages = (int(total) - 1/setting.Config().UI.IssuePagingNum.Value(ctx)) } - pager := context.NewPagination(int(total), setting.UI.IssuePagingNum, page, numPages) + pager := context.NewPagination(int(total), setting.Config().UI.IssuePagingNum.Value(ctx), page, numPages) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager diff --git a/routers/web/repo/issue_list.go b/routers/web/repo/issue_list.go index a65ae77795584..28a3a42fef211 100644 --- a/routers/web/repo/issue_list.go +++ b/routers/web/repo/issue_list.go @@ -177,7 +177,7 @@ func SearchIssues(ctx *context.Context) { // so the default limit is set to fit UI needs limit := ctx.FormInt("limit") if limit == 0 { - limit = setting.UI.IssuePagingNum + limit = setting.Config().UI.IssuePagingNum.Value(ctx) } else if limit > setting.API.MaxResponseItems { limit = setting.API.MaxResponseItems } @@ -603,14 +603,14 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt default: total = int(issueStats.OpenCount) } - pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, 5) + pager := context.NewPagination(total, setting.Config().UI.IssuePagingNum.Value(ctx), page, 5) var issues issues_model.IssueList { ids, err := issueIDsFromSearch(ctx, keyword, &issues_model.IssuesOptions{ Paginator: &db.ListOptions{ Page: pager.Paginater.Current(), - PageSize: setting.UI.IssuePagingNum, + PageSize: setting.Config().UI.IssuePagingNum.Value(ctx), }, RepoIDs: []int64{repo.ID}, AssigneeID: optional.Some(assigneeID), diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go index f1d0a857eaa70..4f3a5f925549b 100644 --- a/routers/web/repo/milestone.go +++ b/routers/web/repo/milestone.go @@ -46,7 +46,7 @@ func Milestones(ctx *context.Context) { miles, total, err := db.FindAndCount[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{ ListOptions: db.ListOptions{ Page: page, - PageSize: setting.UI.IssuePagingNum, + PageSize: setting.Config().UI.IssuePagingNum.Value(ctx), }, RepoID: ctx.Repo.Repository.ID, IsClosed: optional.Some(isShowClosed), @@ -91,7 +91,7 @@ func Milestones(ctx *context.Context) { ctx.Data["Keyword"] = keyword ctx.Data["IsShowClosed"] = isShowClosed - pager := context.NewPagination(int(total), setting.UI.IssuePagingNum, page, 5) + pager := context.NewPagination(int(total), setting.Config().UI.IssuePagingNum.Value(ctx), page, 5) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager diff --git a/routers/web/repo/packages.go b/routers/web/repo/packages.go index 65a340a799700..208e8c7285cd2 100644 --- a/routers/web/repo/packages.go +++ b/routers/web/repo/packages.go @@ -30,7 +30,7 @@ func Packages(ctx *context.Context) { pvs, total, err := packages.SearchLatestVersions(ctx, &packages.PackageSearchOptions{ Paginator: &db.ListOptions{ - PageSize: setting.UI.PackagesPagingNum, + PageSize: setting.Config().UI.PackagesPagingNum.Value(ctx), Page: page, }, OwnerID: ctx.ContextUser.ID, @@ -67,7 +67,7 @@ func Packages(ctx *context.Context) { ctx.Data["Total"] = total ctx.Data["RepositoryAccessMap"] = map[int64]bool{ctx.Repo.Repository.ID: true} // There is only the current repository - pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) + pager := context.NewPagination(int(total), setting.Config().UI.PackagesPagingNum.Value(ctx), page, 5) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 5b81a5e4d1b6e..ed4fcda924577 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -78,7 +78,7 @@ func Projects(ctx *context.Context) { projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ ListOptions: db.ListOptions{ - PageSize: setting.UI.IssuePagingNum, + PageSize: setting.Config().UI.IssuePagingNum.Value(ctx), Page: page, }, RepoID: repo.ID, @@ -116,10 +116,10 @@ func Projects(ctx *context.Context) { numPages := 0 if count > 0 { - numPages = (int(count) - 1/setting.UI.IssuePagingNum) + numPages = (int(count) - 1/setting.Config().UI.IssuePagingNum.Value(ctx)) } - pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, numPages) + pager := context.NewPagination(total, setting.Config().UI.IssuePagingNum.Value(ctx), page, numPages) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go index 12216fc620109..c706915f9eca1 100644 --- a/routers/web/repo/search.go +++ b/routers/web/repo/search.go @@ -44,7 +44,7 @@ func Search(ctx *context.Context) { Language: prepareSearch.Language, Paginator: &db.ListOptions{ Page: page, - PageSize: setting.UI.RepoSearchPagingNum, + PageSize: setting.Config().UI.RepoSearchPagingNum.Value(ctx), }, }) if err != nil { @@ -71,7 +71,7 @@ func Search(ctx *context.Context) { ctx.Data["SearchResults"] = searchResults ctx.Data["SearchResultLanguages"] = searchResultLanguages - pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) + pager := context.NewPagination(total, setting.Config().UI.RepoSearchPagingNum.Value(ctx), page, 5) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go index 655291d25c3a4..0c4582eb15e81 100644 --- a/routers/web/repo/setting/lfs.go +++ b/routers/web/repo/setting/lfs.go @@ -55,10 +55,10 @@ func LFSFiles(ctx *context.Context) { } ctx.Data["Total"] = total - pager := context.NewPagination(int(total), setting.UI.ExplorePagingNum, page, 5) + pager := context.NewPagination(int(total), setting.Config().UI.ExplorePagingNum.Value(ctx), page, 5) ctx.Data["Title"] = ctx.Tr("repo.settings.lfs") ctx.Data["PageIsSettingsLFS"] = true - lfsMetaObjects, err := git_model.GetLFSMetaObjects(ctx, ctx.Repo.Repository.ID, pager.Paginater.Current(), setting.UI.ExplorePagingNum) + lfsMetaObjects, err := git_model.GetLFSMetaObjects(ctx, ctx.Repo.Repository.ID, pager.Paginater.Current(), setting.Config().UI.ExplorePagingNum.Value(ctx)) if err != nil { ctx.ServerError("LFSFiles", err) return @@ -87,10 +87,10 @@ func LFSLocks(ctx *context.Context) { } ctx.Data["Total"] = total - pager := context.NewPagination(int(total), setting.UI.ExplorePagingNum, page, 5) + pager := context.NewPagination(int(total), setting.Config().UI.ExplorePagingNum.Value(ctx), page, 5) ctx.Data["Title"] = ctx.Tr("repo.settings.lfs_locks") ctx.Data["PageIsSettingsLFS"] = true - lfsLocks, err := git_model.GetLFSLockByRepoID(ctx, ctx.Repo.Repository.ID, pager.Paginater.Current(), setting.UI.ExplorePagingNum) + lfsLocks, err := git_model.GetLFSLockByRepoID(ctx, ctx.Repo.Repository.ID, pager.Paginater.Current(), setting.Config().UI.ExplorePagingNum.Value(ctx)) if err != nil { ctx.ServerError("LFSLocks", err) return diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 62b146c7f3200..8363dd49f7c72 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -38,7 +38,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) { prepareContextForCommonProfile(ctx) ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) - ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate + ctx.Data["ShowUserEmail"] = setting.Config().UI.ShowUserEmail.Value(ctx) && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate if setting.Service.UserLocationMapURL != "" { ctx.Data["ContextUserLocationMapURL"] = setting.Service.UserLocationMapURL + url.QueryEscape(ctx.ContextUser.Location) } @@ -153,7 +153,7 @@ func LoadHeaderCount(ctx *context.Context) error { OwnerID: ctx.ContextUser.ID, Private: ctx.IsSigned, Collaborate: optional.Some(false), - IncludeDescription: setting.UI.SearchRepoDescription, + IncludeDescription: setting.Config().UI.SearchRepoDescription.Value(ctx), }) if err != nil { return err diff --git a/routers/web/user/code.go b/routers/web/user/code.go index f9aa58b877600..8957404db2239 100644 --- a/routers/web/user/code.go +++ b/routers/web/user/code.go @@ -74,7 +74,7 @@ func CodeSearch(ctx *context.Context) { Language: prepareSearch.Language, Paginator: &db.ListOptions{ Page: page, - PageSize: setting.UI.RepoSearchPagingNum, + PageSize: setting.Config().UI.RepoSearchPagingNum.Value(ctx), }, }) if err != nil { @@ -112,7 +112,7 @@ func CodeSearch(ctx *context.Context) { ctx.Data["SearchResults"] = searchResults ctx.Data["SearchResultLanguages"] = searchResultLanguages - pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) + pager := context.NewPagination(total, setting.Config().UI.RepoSearchPagingNum.Value(ctx), page, 5) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 8e030a62a2f23..16171081483d5 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -129,7 +129,7 @@ func Dashboard(ctx *context.Context) { Date: ctx.FormString("date"), ListOptions: db.ListOptions{ Page: page, - PageSize: setting.UI.FeedPagingNum, + PageSize: setting.Config().UI.FeedPagingNum.Value(ctx), }, }) if err != nil { @@ -139,7 +139,7 @@ func Dashboard(ctx *context.Context) { ctx.Data["Feeds"] = feeds - pager := context.NewPagination(int(count), setting.UI.FeedPagingNum, page, 5) + pager := context.NewPagination(int(count), setting.Config().UI.FeedPagingNum.Value(ctx), page, 5) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager @@ -231,7 +231,7 @@ func Milestones(ctx *context.Context) { milestones, err := db.Find[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{ ListOptions: db.ListOptions{ Page: page, - PageSize: setting.UI.IssuePagingNum, + PageSize: setting.Config().UI.IssuePagingNum.Value(ctx), }, RepoCond: repoCond, IsClosed: optional.Some(isShowClosed), @@ -330,7 +330,7 @@ func Milestones(ctx *context.Context) { ctx.Data["RepoIDs"] = repoIDs ctx.Data["IsShowClosed"] = isShowClosed - pager := context.NewPagination(pagerCount, setting.UI.IssuePagingNum, page, 5) + pager := context.NewPagination(pagerCount, setting.Config().UI.IssuePagingNum.Value(ctx), page, 5) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager @@ -527,7 +527,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { } opts.Paginator = &db.ListOptions{ Page: page, - PageSize: setting.UI.IssuePagingNum, + PageSize: setting.Config().UI.IssuePagingNum.Value(ctx), } // Get IDs for labels (a filter option for issues/pulls). @@ -645,7 +645,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { ctx.Data["State"] = "open" } - pager := context.NewPagination(shownIssues, setting.UI.IssuePagingNum, page, 5) + pager := context.NewPagination(shownIssues, setting.Config().UI.IssuePagingNum.Value(ctx), page, 5) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager diff --git a/routers/web/user/home_test.go b/routers/web/user/home_test.go index b2c8ad98ba2e4..c050e2f63e685 100644 --- a/routers/web/user/home_test.go +++ b/routers/web/user/home_test.go @@ -11,6 +11,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/setting/config" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/contexttest" @@ -20,7 +21,8 @@ import ( func TestArchivedIssues(t *testing.T) { // Arrange - setting.UI.IssuePagingNum = 1 + issuePagingNumValue := config.Value[int]{} + setting.Config().UI.IssuePagingNum = issuePagingNumValue.WithDefault(1) assert.NoError(t, unittest.LoadFixtures()) ctx, _ := contexttest.MockContext(t, "issues") @@ -51,7 +53,8 @@ func TestArchivedIssues(t *testing.T) { } func TestIssues(t *testing.T) { - setting.UI.IssuePagingNum = 1 + issuePagingNumValue := config.Value[int]{} + setting.Config().UI.IssuePagingNum = issuePagingNumValue.WithDefault(1) assert.NoError(t, unittest.LoadFixtures()) ctx, _ := contexttest.MockContext(t, "issues") @@ -65,7 +68,8 @@ func TestIssues(t *testing.T) { } func TestPulls(t *testing.T) { - setting.UI.IssuePagingNum = 20 + issuePagingNumValue := config.Value[int]{} + setting.Config().UI.IssuePagingNum = issuePagingNumValue.WithDefault(20) assert.NoError(t, unittest.LoadFixtures()) ctx, _ := contexttest.MockContext(t, "pulls") @@ -78,7 +82,8 @@ func TestPulls(t *testing.T) { } func TestMilestones(t *testing.T) { - setting.UI.IssuePagingNum = 1 + issuePagingNumValue := config.Value[int]{} + setting.Config().UI.IssuePagingNum = issuePagingNumValue.WithDefault(1) assert.NoError(t, unittest.LoadFixtures()) ctx, _ := contexttest.MockContext(t, "milestones") @@ -97,7 +102,8 @@ func TestMilestones(t *testing.T) { } func TestMilestonesForSpecificRepo(t *testing.T) { - setting.UI.IssuePagingNum = 1 + issuePagingNumValue := config.Value[int]{} + setting.Config().UI.IssuePagingNum = issuePagingNumValue.WithDefault(1) assert.NoError(t, unittest.LoadFixtures()) ctx, _ := contexttest.MockContext(t, "milestones") diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index 1c91ff6364324..1af5cd24bb7f8 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -287,7 +287,7 @@ func NotificationSubscriptions(ctx *context.Context) { } issues, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{ Paginator: &db.ListOptions{ - PageSize: setting.UI.IssuePagingNum, + PageSize: setting.Config().UI.IssuePagingNum.Value(ctx), Page: page, }, SubscriberID: ctx.Doer.ID, @@ -352,7 +352,7 @@ func NotificationSubscriptions(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("notification.subscriptions") // redirect to last page if request page is more than total pages - pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5) + pager := context.NewPagination(int(count), setting.Config().UI.IssuePagingNum.Value(ctx), page, 5) if pager.Paginater.Current() < page { ctx.Redirect(fmt.Sprintf("/notifications/subscriptions?page=%d", pager.Paginater.Current())) return @@ -428,7 +428,7 @@ func NotificationWatching(ctx *context.Context) { WatchedByID: ctx.Doer.ID, Collaborate: optional.Some(false), TopicOnly: ctx.FormBool("topic"), - IncludeDescription: setting.UI.SearchRepoDescription, + IncludeDescription: setting.Config().UI.SearchRepoDescription.Value(ctx), Archived: archived, Fork: fork, Mirror: mirror, diff --git a/routers/web/user/package.go b/routers/web/user/package.go index c01bc96e2bfe6..fc2bdd37c3e08 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -52,7 +52,7 @@ func ListPackages(ctx *context.Context) { pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{ Paginator: &db.ListOptions{ - PageSize: setting.UI.PackagesPagingNum, + PageSize: setting.Config().UI.PackagesPagingNum.Value(ctx), Page: page, }, OwnerID: ctx.ContextUser.ID, @@ -127,7 +127,7 @@ func ListPackages(ctx *context.Context) { } } - pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) + pager := context.NewPagination(int(total), setting.Config().UI.PackagesPagingNum.Value(ctx), page, 5) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager @@ -329,7 +329,7 @@ func ListPackageVersions(ctx *context.Context) { page = 1 } pagination := &db.ListOptions{ - PageSize: setting.UI.PackagesPagingNum, + PageSize: setting.Config().UI.PackagesPagingNum.Value(ctx), Page: page, } @@ -399,7 +399,7 @@ func ListPackageVersions(ctx *context.Context) { return } - pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) + pager := context.NewPagination(int(total), setting.Config().UI.PackagesPagingNum.Value(ctx), page, 5) pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 39f066a53c904..2516ecdab2302 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -113,7 +113,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb sortOrder := ctx.FormString("sort") if _, ok := repo_model.OrderByFlatMap[sortOrder]; !ok { - sortOrder = setting.UI.ExploreDefaultSort // TODO: add new default sort order for user home? + sortOrder = setting.Config().UI.ExploreDefaultSort.Value(ctx) // TODO: add new default sort order for user home? } ctx.Data["SortType"] = sortOrder orderBy = repo_model.OrderByFlatMap[sortOrder] @@ -167,7 +167,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb total = int(numFollowing) case "activity": date := ctx.FormString("date") - pagingNum = setting.UI.FeedPagingNum + pagingNum = setting.Config().UI.FeedPagingNum.Value(ctx) items, count, err := feed_service.GetFeeds(ctx, activities_model.GetFeedsOptions{ RequestedUser: ctx.ContextUser, Actor: ctx.Doer, @@ -203,7 +203,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb Collaborate: optional.Some(false), TopicOnly: topicOnly, Language: language, - IncludeDescription: setting.UI.SearchRepoDescription, + IncludeDescription: setting.Config().UI.SearchRepoDescription.Value(ctx), Archived: archived, Fork: fork, Mirror: mirror, @@ -230,7 +230,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb Collaborate: optional.Some(false), TopicOnly: topicOnly, Language: language, - IncludeDescription: setting.UI.SearchRepoDescription, + IncludeDescription: setting.Config().UI.SearchRepoDescription.Value(ctx), Archived: archived, Fork: fork, Mirror: mirror, @@ -285,7 +285,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb Collaborate: optional.Some(false), TopicOnly: topicOnly, Language: language, - IncludeDescription: setting.UI.SearchRepoDescription, + IncludeDescription: setting.Config().UI.SearchRepoDescription.Value(ctx), Archived: archived, Fork: fork, Mirror: mirror, diff --git a/routers/web/user/search.go b/routers/web/user/search.go index be5eee90a971a..7768be6ee6acf 100644 --- a/routers/web/user/search.go +++ b/routers/web/user/search.go @@ -21,7 +21,7 @@ func SearchCandidates(ctx *context.Context) { Keyword: ctx.FormTrim("q"), Type: user_model.UserTypeIndividual, IsActive: optional.Some(true), - ListOptions: db.ListOptions{PageSize: setting.UI.MembersPagingNum}, + ListOptions: db.ListOptions{PageSize: setting.Config().UI.MembersPagingNum.Value(ctx)}, }) if err != nil { ctx.ServerError("Unable to search users", err) diff --git a/routers/web/web.go b/routers/web/web.go index f4bd3ef4bce99..94105e201d1aa 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -721,6 +721,7 @@ func registerRoutes(m *web.Router) { m.Group("/config", func() { m.Get("", admin.Config) m.Post("", admin.ChangeConfig) + m.Post("/ui", web.Bind(forms.UIForm{}), admin.ChangeUIConfig) m.Post("/test_mail", admin.SendTestMail) m.Post("/test_cache", admin.TestCache) m.Get("/settings", admin.ConfigSettings) diff --git a/services/forms/admin_config_form.go b/services/forms/admin_config_form.go new file mode 100644 index 0000000000000..d3797d05c21cf --- /dev/null +++ b/services/forms/admin_config_form.go @@ -0,0 +1,35 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package forms + +import ( + "net/http" + + "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" + + "gitea.com/go-chi/binding" +) + +type UIForm struct { + ExplorePagingNum int + SitemapPagingNum int + IssuePagingNum int + RepoSearchPagingNum int + MembersPagingNum int + FeedMaxCommitNum int + FeedPagingNum int + PackagesPagingNum int + CodeCommentLines int + ShowUserEmail bool + SearchRepoDescription bool + OnlyShowRelevantRepos bool + ExplorePagingDefaultSort string `binding:"In(recentupdate,alphabetically,reverselastlogin,newest,oldest)"` +} + +// Validate validates fields +func (f *UIForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetValidateContext(req) + return middleware.Validate(errs, ctx.Data, f, ctx.Locale) +} diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index b17cc3ce4160e..890748cf0eaec 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -911,7 +911,7 @@ func (g *GiteaLocalUploader) CreateReviews(ctx context.Context, reviews ...*base _ = writer.Close() }(comment) - patch, _ = git.CutDiffAroundLine(reader, int64((&issues_model.Comment{Line: int64(line + comment.Position - 1)}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines) + patch, _ = git.CutDiffAroundLine(reader, int64((&issues_model.Comment{Line: int64(line + comment.Position - 1)}).UnsignedLine()), line < 0, setting.Config().UI.CodeCommentLines.Value(ctx)) if comment.CreatedAt.IsZero() { comment.CreatedAt = review.CreatedAt diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 658747e7c83f1..c8f3035fc6851 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -537,8 +537,8 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { } theCommits := repo_module.GitToPushCommits(commits) - if len(theCommits.Commits) > setting.UI.FeedMaxCommitNum { - theCommits.Commits = theCommits.Commits[:setting.UI.FeedMaxCommitNum] + if len(theCommits.Commits) > setting.Config().UI.FeedMaxCommitNum.Value(ctx) { + theCommits.Commits = theCommits.Commits[:setting.Config().UI.FeedMaxCommitNum.Value(ctx)] } newCommit, err := gitRepo.GetCommit(newCommitID) diff --git a/services/pull/review.go b/services/pull/review.go index 78723a58ae305..0281ae3709130 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -264,7 +264,7 @@ func createCodeComment(ctx context.Context, doer *user_model.User, repo *repo_mo _ = writer.Close() }() - patch, err = git.CutDiffAroundLine(reader, int64((&issues_model.Comment{Line: line}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines) + patch, err = git.CutDiffAroundLine(reader, int64((&issues_model.Comment{Line: line}).UnsignedLine()), line < 0, setting.Config().UI.CodeCommentLines.Value(ctx)) if err != nil { log.Error("Error whilst generating patch: %v", err) return nil, err diff --git a/services/repository/push.go b/services/repository/push.go index c40333f0a8b76..bd7163dda60eb 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -277,8 +277,8 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { commits.CompareURL = "" } - if len(commits.Commits) > setting.UI.FeedMaxCommitNum { - commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum] + if len(commits.Commits) > setting.Config().UI.FeedMaxCommitNum.Value(ctx) { + commits.Commits = commits.Commits[:setting.Config().UI.FeedMaxCommitNum.Value(ctx)] } notify_service.PushCommits(ctx, pusher, repo, opts, commits) diff --git a/templates/admin/config_settings.tmpl b/templates/admin/config_settings.tmpl index 6b9bb8275cca5..a15ac36804e96 100644 --- a/templates/admin/config_settings.tmpl +++ b/templates/admin/config_settings.tmpl @@ -1,4 +1,102 @@ {{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin config")}} +