diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 0fc49accef84e..443d179cc11b4 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1258,24 +1258,31 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Number of repositories that are displayed on one explore page +;; Deprecated in v1.24 ;EXPLORE_PAGING_NUM = 20 ;; ;; Number of issues that are displayed on one page +;; Deprecated in v1.24 ;ISSUE_PAGING_NUM = 20 ;; ;; Number of maximum commits displayed in one activity feed +;; Deprecated in v1.24 ;FEED_MAX_COMMIT_NUM = 5 ;; ;; Number of items that are displayed in home feed +;; Deprecated in v1.24 ;FEED_PAGING_NUM = 20 ;; ;; Number of items that are displayed in a single subsitemap +;; Deprecated in v1.24 ;SITEMAP_PAGING_NUM = 20 ;; ;; Number of maximum commits displayed in commit graph. +;; Deprecated in v1.24 ;GRAPH_MAX_COMMIT_NUM = 100 ;; ;; Number of line of codes shown for a code comment +;; Deprecated in v1.24 ;CODE_COMMENT_LINES = 4 ;; ;; Max size of files to be displayed (default is 8MiB) @@ -1285,6 +1292,7 @@ LEVEL = Info ;AMBIGUOUS_UNICODE_DETECTION = true ;; ;; Whether the email of the user should be shown in the Explore Users page +;; Deprecated in v1.24 ;SHOW_USER_EMAIL = true ;; ;; Set the default theme for the Gitea install @@ -1303,6 +1311,7 @@ LEVEL = Info ;REACTIONS = +1, -1, laugh, hooray, confused, heart, rocket, eyes ;; ;; Change the number of users that are displayed in reactions tooltip (triggered by mouse hover). +;; Deprecated in v1.24 ;REACTION_MAX_USER_NUM = 10 ;; ;; Additional Emojis not defined in the utf8 standard @@ -1311,17 +1320,21 @@ LEVEL = Info ;CUSTOM_EMOJIS = gitea, codeberg, gitlab, git, github, gogs ;; ;; Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used. +;; Deprecated in v1.24 ;DEFAULT_SHOW_FULL_NAME = false ;; ;; Whether to search within description at repository search on explore page. +;; Deprecated in v1.24 ;SEARCH_REPO_DESCRIPTION = true ;; ;; Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used. ;; A repo is considered irrelevant if it's a fork or if it has no metadata (no description, no icon, no topic). +;; Deprecated in v1.24 ;ONLY_SHOW_RELEVANT_REPOS = false ;; ;; Change the sort type of the explore pages. ;; Default is "recentupdate", but you also have "alphabetically", "reverselastlogin", "newest", "oldest". +;; Deprecated in v1.24 ;EXPLORE_PAGING_DEFAULT_SORT = recentupdate ;; ;; The tense all timestamps should be rendered in. Possible values are `absolute` time (i.e. 1970-01-01, 11:59) and `mixed`. diff --git a/models/actions/run_list.go b/models/actions/run_list.go index b9b9324e0754f..5cd6af8d12162 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -134,6 +134,6 @@ func GetActors(ctx context.Context, repoID int64) ([]*user_model.User, error) { GroupBy("`action_run`.trigger_user_id"). Where(builder.Eq{"`action_run`.repo_id": repoID}))). Cols("id", "name", "full_name", "avatar", "avatar_email", "use_custom_avatar"). - OrderBy(user_model.GetOrderByName()). + OrderBy(user_model.GetOrderByName(ctx)). Find(&actors) } diff --git a/models/activities/action.go b/models/activities/action.go index c16c49c0acbf2..96ea63f54c3a3 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -227,7 +227,7 @@ func (a *Action) ShortActUserName(ctx context.Context) string { // GetActDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME, or falls back to the username if it is blank. func (a *Action) GetActDisplayName(ctx context.Context) string { - if setting.UI.DefaultShowFullName { + if setting.Config().UI.DefaultShowFullName.Value(ctx) { trimmedFullName := strings.TrimSpace(a.GetActFullName(ctx)) if len(trimmedFullName) > 0 { return trimmedFullName @@ -238,7 +238,7 @@ func (a *Action) GetActDisplayName(ctx context.Context) string { // GetActDisplayNameTitle gets the action's display name used for the title (tooltip) based on DEFAULT_SHOW_FULL_NAME func (a *Action) GetActDisplayNameTitle(ctx context.Context) string { - if setting.UI.DefaultShowFullName { + if setting.Config().UI.DefaultShowFullName.Value(ctx) { return a.ShortActUserName(ctx) } return a.GetActFullName(ctx) diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index 6c74b533b3c54..4b1d495b10507 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -251,7 +251,7 @@ func (issues IssueList) LoadAssignees(ctx context.Context) error { } rows, err := db.GetEngine(ctx).Table("issue_assignees"). Join("INNER", "`user`", "`user`.id = `issue_assignees`.assignee_id"). - In("`issue_assignees`.issue_id", issueIDs[:limit]).OrderBy(user_model.GetOrderByName()). + In("`issue_assignees`.issue_id", issueIDs[:limit]).OrderBy(user_model.GetOrderByName(ctx)). Rows(new(AssigneeIssue)) if err != nil { return err 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/issues/reaction.go b/models/issues/reaction.go index f24001fd23156..f56ef758dd94f 100644 --- a/models/issues/reaction.go +++ b/models/issues/reaction.go @@ -355,9 +355,9 @@ func (list ReactionList) LoadUsers(ctx context.Context, repo *repo_model.Reposit } // GetFirstUsers returns first reacted user display names separated by comma -func (list ReactionList) GetFirstUsers() string { +func (list ReactionList) GetFirstUsers(ctx context.Context) string { var buffer bytes.Buffer - rem := setting.UI.ReactionMaxUserNum + rem := setting.Config().UI.ReactionMaxUserNum.Value(ctx) for _, reaction := range list { if buffer.Len() > 0 { buffer.WriteString(", ") @@ -371,9 +371,9 @@ func (list ReactionList) GetFirstUsers() string { } // GetMoreUserCount returns count of not shown users in reaction tooltip -func (list ReactionList) GetMoreUserCount() int { - if len(list) <= setting.UI.ReactionMaxUserNum { +func (list ReactionList) GetMoreUserCount(ctx context.Context) int { + if len(list) <= setting.Config().UI.ReactionMaxUserNum.Value(ctx) { return 0 } - return len(list) - setting.UI.ReactionMaxUserNum + return len(list) - setting.Config().UI.ReactionMaxUserNum.Value(ctx) } 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..f62e50bc69a2d --- /dev/null +++ b/models/migrations/fixtures/Test_MigrateIniToDatabase/system_setting.yml @@ -0,0 +1,24 @@ +# 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"` +# } +- + 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 5f79a873f14e7..a8550253dcba4 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -375,6 +375,7 @@ func prepareMigrationTasks() []*migration { newMigration(312, "Add DeleteBranchAfterMerge to AutoMerge", v1_24.AddDeleteBranchAfterMergeForAutoMerge), newMigration(313, "Move PinOrder from issue table to a new table issue_pin", v1_24.MovePinOrderToTableIssuePin), newMigration(314, "Update OwnerID as zero for repository level action tables", v1_24.UpdateOwnerIDOfRepoLevelActionsTables), + newMigration(315, "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/v315.go b/models/migrations/v1_24/v315.go new file mode 100644 index 0000000000000..8157f48f0ae84 --- /dev/null +++ b/models/migrations/v1_24/v315.go @@ -0,0 +1,98 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_24 //nolint + +import ( + "math" + + "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, err := util.ConfigSectionToMap( + setting.UI, "ui", + []string{ + "Reactions", "CustomEmojis", "MaxDisplayFileSize", "DefaultTheme", "Themes", + "FileIconTheme", "PreferredTimestampTense", "AmbiguousUnicodeDetection", + }..., + ) + if err != nil { + return err + } + + 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 + + _, err = sess.Exec("UPDATE system_setting SET version=version+1 WHERE setting_key=?", keyRevision) + if 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/v315_test.go b/models/migrations/v1_24/v315_test.go new file mode 100644 index 0000000000000..2a930bc353248 --- /dev/null +++ b/models/migrations/v1_24/v315_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, 16, cnt) +} diff --git a/models/organization/org.go b/models/organization/org.go index 3e55a36758963..fff8cc8b4ff6b 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -169,8 +169,8 @@ func (org *Organization) OrganisationLink() string { } // ShortName ellipses username to length -func (org *Organization) ShortName(length int) string { - return org.AsUser().ShortName(length) +func (org *Organization) ShortName(ctx context.Context, length int) string { + return org.AsUser().ShortName(ctx, length) } // HomeLink returns the user or organization home page link. diff --git a/models/organization/org_user.go b/models/organization/org_user.go index 08d936d922f05..d9b14b57f45f1 100644 --- a/models/organization/org_user.go +++ b/models/organization/org_user.go @@ -163,7 +163,7 @@ func GetOrgAssignees(ctx context.Context, orgID int64) (_ []*user_model.User, er if len(userIDs) > 0 { if err = e.In("id", uniqueUserIDs.Values()). Where(builder.Eq{"`user`.is_active": true}). - OrderBy(user_model.GetOrderByName()). + OrderBy(user_model.GetOrderByName(ctx)). Find(&users); err != nil { return nil, err } diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go index 232087d86594e..9630974688010 100644 --- a/models/repo/user_repo.go +++ b/models/repo/user_repo.go @@ -134,7 +134,7 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us if len(uniqueUserIDs) > 0 { if err = e.In("id", uniqueUserIDs.Values()). Where(builder.Eq{"`user`.is_active": true}). - OrderBy(user_model.GetOrderByName()). + OrderBy(user_model.GetOrderByName(ctx)). Find(&users); err != nil { return nil, err } diff --git a/models/user/user.go b/models/user/user.go index 3c72aa7cc4abb..e32632ef50ca2 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -440,8 +440,8 @@ func (u *User) EmailTo() string { // GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set, // returns username otherwise. -func (u *User) GetDisplayName() string { - if setting.UI.DefaultShowFullName { +func (u *User) GetDisplayName(ctx context.Context) string { + if setting.Config().UI.DefaultShowFullName.Value(ctx) { trimmed := strings.TrimSpace(u.FullName) if len(trimmed) > 0 { return trimmed @@ -482,8 +482,8 @@ func (u *User) GitName() string { } // ShortName ellipses username to length -func (u *User) ShortName(length int) string { - if setting.UI.DefaultShowFullName && len(u.FullName) > 0 { +func (u *User) ShortName(ctx context.Context, length int) string { + if setting.Config().UI.DefaultShowFullName.Value(ctx) && len(u.FullName) > 0 { return util.EllipsisDisplayString(u.FullName, length) } return util.EllipsisDisplayString(u.Name, length) @@ -1393,8 +1393,8 @@ func FixWrongUserType(ctx context.Context) (int64, error) { return db.GetEngine(ctx).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Cols("type").NoAutoTime().Update(&User{Type: 1}) } -func GetOrderByName() string { - if setting.UI.DefaultShowFullName { +func GetOrderByName(ctx context.Context) string { + if setting.Config().UI.DefaultShowFullName.Value(ctx) { return "full_name, name" } return "name" diff --git a/modules/indexer/code/gitgrep/gitgrep.go b/modules/indexer/code/gitgrep/gitgrep.go index a85c9d02a5e51..8cfebf2a38959 100644 --- a/modules/indexer/code/gitgrep/gitgrep.go +++ b/modules/indexer/code/gitgrep/gitgrep.go @@ -41,8 +41,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..766e99c23c113 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,71 @@ 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] + GraphMaxCommitNum *config.Value[int] + CodeCommentLines *config.Value[int] + ReactionMaxUserNum *config.Value[int] + ShowUserEmail *config.Value[bool] + DefaultShowFullName *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), + GraphMaxCommitNum: u.GraphMaxCommitNum.Value(ctx), + CodeCommentLines: u.CodeCommentLines.Value(ctx), + ReactionMaxUserNum: u.ReactionMaxUserNum.Value(ctx), + ShowUserEmail: u.ShowUserEmail.Value(ctx), + DefaultShowFullName: u.DefaultShowFullName.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 + GraphMaxCommitNum int + CodeCommentLines int + ReactionMaxUserNum int + ShowUserEmail bool + DefaultShowFullName bool + SearchRepoDescription bool + OnlyShowRelevantRepos bool + ExplorePagingDefaultSort string + ExplorePagingSortOption []string +} + type ConfigStruct struct { Picture *PictureStruct Repository *RepositoryStruct + UI *UIStruct } var ( @@ -71,6 +134,24 @@ 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), + GraphMaxCommitNum: config.ValueJSON[int]("ui.graph_max_commit_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "GRAPH_MAX_COMMIT_NUM"}).WithDefault(100), + CodeCommentLines: config.ValueJSON[int]("ui.code_comment_lines").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "CODE_COMMENT_LINES"}).WithDefault(4), + ReactionMaxUserNum: config.ValueJSON[int]("ui.reaction_max_user_num").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "REACTION_MAX_USER_NUM"}).WithDefault(10), + ShowUserEmail: config.ValueJSON[bool]("ui.show_user_email").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "SHOW_USER_EMAIL"}).WithDefault(true), + DefaultShowFullName: config.ValueJSON[bool]("ui.default_show_full_name").WithFileConfig(config.CfgSecKey{Sec: "ui", Key: "DEFAULT_SHOW_FULL_NAME"}).WithDefault(false), + 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..d9bfd0dcb85ab 100644 --- a/modules/setting/ui.go +++ b/modules/setting/ui.go @@ -85,6 +85,7 @@ var UI = struct { ReactionMaxUserNum: 10, MaxDisplayFileSize: 8388608, DefaultTheme: `gitea-auto`, + Themes: []string{`gitea-auto`, `gitea-dark`, `gitea-light`}, FileIconTheme: `material`, Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`}, diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 3237f8b2958a6..e9471813f682a 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -5,6 +5,7 @@ package templates import ( + "context" "fmt" "html" "html/template" @@ -103,8 +104,8 @@ func NewFuncMap() template.FuncMap { "AssetVersion": func() string { return setting.AssetVersion }, - "DefaultShowFullName": func() bool { - return setting.UI.DefaultShowFullName + "DefaultShowFullName": func(ctx context.Context) bool { + return setting.Config().UI.DefaultShowFullName.Value(ctx) }, "ShowFooterTemplateLoadTime": func() bool { return setting.Other.ShowFooterTemplateLoadTime diff --git a/modules/util/util.go b/modules/util/util.go index 1fb4cb21cb884..5e123055e986e 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -8,9 +8,12 @@ import ( "crypto/rand" "fmt" "math/big" + "reflect" + "slices" "strconv" "strings" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/optional" "golang.org/x/text/cases" @@ -257,3 +260,55 @@ func ReserveLineBreakForTextarea(input string) string { // Other than this, we should respect the original content, even leading or trailing spaces. return strings.ReplaceAll(input, "\r\n", "\n") } + +func ConfigSectionToMap(in any, keyPrefix string, skipFields ...string) (map[string]string, error) { + if keyPrefix == "" { + return nil, fmt.Errorf("keyPrefix is empty") + } + out := map[string]string{} + + v := reflect.ValueOf(in) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + if v.Kind() != reflect.Struct { + return nil, fmt.Errorf("in is not a struct") + } + + t := v.Type() + for i := 0; i < v.NumField(); i++ { + fi := t.Field(i) + fieldName := fi.Name + if slices.Contains(skipFields, fieldName) { + continue + } + if tagValue := fi.Tag.Get("ini"); tagValue == "-" { + continue + } else if tagValue != "" { + fieldName = tagValue + } + switch v.FieldByName(fi.Name).Kind() { + case reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + out[fmt.Sprintf("%s.%s", keyPrefix, ToSnakeCase(fieldName))] = fmt.Sprintf("%v", v.FieldByName(fi.Name).Interface()) + case reflect.String: + marshal, err := json.Marshal(v.FieldByName(fi.Name).Interface()) + if err != nil { + return nil, err + } + out[fmt.Sprintf("%s.%s", keyPrefix, ToSnakeCase(fieldName))] = fmt.Sprintf("%v", string(marshal)) + case reflect.Slice, reflect.Array: + if v.FieldByName(fi.Name).Len() == 0 { + continue + } + marshal, err := json.Marshal(v.FieldByName(fi.Name).Interface()) + if err != nil { + return nil, err + } + out[fmt.Sprintf("%s.%s", keyPrefix, ToSnakeCase(fieldName))] = fmt.Sprintf("%v", string(marshal)) + } + } + + return out, nil +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 2f13c1a19c645..0d8fbeb359739 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3010,6 +3010,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 @@ -3363,6 +3364,29 @@ config.picture_service = Picture Service config.disable_gravatar = Disable Gravatar config.enable_federated_avatar = Enable Federated Avatars config.open_with_editor_app_help = The "Open with" editors for the clone menu. If left empty, the default will be used. Expand to see the default. +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.graph_max_commit_num = Graph Max Commit Number +config.ui.code_comment_lines = Code Comment Lines +config.ui.max_display_file_size = Max Display File Size +config.ui.default_theme = Default Theme +config.ui.themes = Themes +config.ui.reactions = Reactions +config.ui.reaction_max_user_num = Reaction Max User Number +config.ui.custom_emojis = Custom Emojis +config.ui.explore_paging_default_sort = Explore Paging Default Sort +config.ui.ambiguous_unicode_detection = Ambiguous Unicode Detection +config.ui.show_user_email = Show User Email +config.ui.default_show_full_name = Default Show Full Name +config.ui.search_repo_description = Search Repo Description +config.ui.only_show_relevant_repos = Only Show Relevant Repos +config.ui.preferred_timestamp_tense = Preferred Timestamp Tense config.git_config = Git Configuration config.git_disable_diff_highlight = Disable Diff Syntax Highlight 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..d40bea8b26d2f 100644 --- a/routers/web/admin/config.go +++ b/routers/web/admin/config.go @@ -19,7 +19,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 +193,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 +256,31 @@ func ChangeConfig(ctx *context.Context) { config.GetDynGetter().InvalidateCache() ctx.JSONOK() } + +func ChangeUIConfig(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.UIForm) + if len(form.Themes) == 1 { + form.Themes = strings.Split(form.Themes[0], ",") + } else { + form.Themes = nil + } + log.Debug("ChangeUIConfig form: %+v", form) + formMap, err := util.ConfigSectionToMap(form, "ui") + if err != nil { + ctx.ServerError("unable convert struct to map[string]string", err) + return + } + + 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 ae5ff3db761bd..516a6d3740160 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/feed/convert.go b/routers/web/feed/convert.go index b04855fa6a6f8..3ae0276268dc2 100644 --- a/routers/web/feed/convert.go +++ b/routers/web/feed/convert.go @@ -77,7 +77,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio link := &feeds.Link{Href: act.GetCommentHTMLURL(ctx)} // title - title = act.ActUser.GetDisplayName() + " " + title = act.ActUser.GetDisplayName(ctx) + " " var titleExtra template.HTML switch act.OpType { case activities_model.ActionCreateRepo: @@ -246,7 +246,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio Description: desc, IsPermaLink: "false", Author: &feeds.Author{ - Name: act.ActUser.GetDisplayName(), + Name: act.ActUser.GetDisplayName(ctx), Email: act.ActUser.GetEmail(), }, Id: fmt.Sprintf("%v: %v", strconv.FormatInt(act.ID, 10), link.Href), @@ -302,7 +302,7 @@ func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release) ( Link: link, Created: rel.CreatedUnix.AsTime(), Author: &feeds.Author{ - Name: rel.Publisher.GetDisplayName(), + Name: rel.Publisher.GetDisplayName(ctx), Email: rel.Publisher.GetEmail(), }, Id: fmt.Sprintf("%v: %v", strconv.FormatInt(rel.ID, 10), link.Href), 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/actions/view.go b/routers/web/repo/actions/view.go index 41f0d2d0ec249..d0dee429df602 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -229,7 +229,7 @@ func ViewPost(ctx *context_module.Context) { } pusher := ViewUser{ - DisplayName: run.TriggerUser.GetDisplayName(), + DisplayName: run.TriggerUser.GetDisplayName(ctx), Link: run.TriggerUser.HomeLink(), } branch := ViewBranch{ diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index bbdcf9875eee8..303764da1236e 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -144,7 +144,7 @@ func Graph(ctx *context.Context) { page := ctx.FormInt("page") - graph, err := gitgraph.GetCommitGraph(ctx.Repo.GitRepo, page, 0, hidePRRefs, realBranches, files) + graph, err := gitgraph.GetCommitGraph(ctx, ctx.Repo.GitRepo, page, 0, hidePRRefs, realBranches, files) if err != nil { ctx.ServerError("GetCommitGraph", err) return @@ -168,7 +168,7 @@ func Graph(ctx *context.Context) { ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name - paginator := context.NewPagination(int(graphCommitsCount), setting.UI.GraphMaxCommitNum, page, 5) + paginator := context.NewPagination(int(graphCommitsCount), setting.Config().UI.GraphMaxCommitNum.Value(ctx), page, 5) paginator.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = paginator if ctx.FormBool("div-only") { diff --git a/routers/web/repo/issue_content_history.go b/routers/web/repo/issue_content_history.go index c2c208736c735..7543765613ee4 100644 --- a/routers/web/repo/issue_content_history.go +++ b/routers/web/repo/issue_content_history.go @@ -64,7 +64,7 @@ func GetContentHistoryList(ctx *context.Context) { } username := item.UserName - if setting.UI.DefaultShowFullName && strings.TrimSpace(item.UserFullName) != "" { + if setting.Config().UI.DefaultShowFullName.Value(ctx) && strings.TrimSpace(item.UserFullName) != "" { username = strings.TrimSpace(item.UserFullName) } 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/issue_poster.go b/routers/web/repo/issue_poster.go index 07059b9b7bfc3..0bbd5143a5a5e 100644 --- a/routers/web/repo/issue_poster.go +++ b/routers/web/repo/issue_poster.go @@ -34,7 +34,7 @@ func IssuePullPosters(ctx *context.Context) { func issuePosters(ctx *context.Context, isPullList bool) { repo := ctx.Repo.Repository search := strings.TrimSpace(ctx.FormString("q")) - posters, err := repo_model.GetIssuePostersWithSearch(ctx, repo, isPullList, search, setting.UI.DefaultShowFullName) + posters, err := repo_model.GetIssuePostersWithSearch(ctx, repo, isPullList, search, setting.Config().UI.DefaultShowFullName.Value(ctx)) if err != nil { ctx.JSON(http.StatusInternalServerError, err) return @@ -54,7 +54,7 @@ func issuePosters(ctx *context.Context, isPullList bool) { resp.Results = make([]*userSearchInfo, len(posters)) for i, user := range posters { resp.Results[i] = &userSearchInfo{UserID: user.ID, UserName: user.Name, AvatarLink: user.AvatarLink(ctx)} - if setting.UI.DefaultShowFullName { + if setting.Config().UI.DefaultShowFullName.Value(ctx) { resp.Results[i].FullName = user.FullName } } 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 ea40e64bbbb16..18c6f5a5b9bfa 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 665ce1a6a6fc1..a2c5721091cb7 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 dc78950cf2ecb..2ef46a4bd76f2 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -128,7 +128,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 { @@ -138,7 +138,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 @@ -230,7 +230,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), @@ -329,7 +329,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 @@ -526,7 +526,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). @@ -641,7 +641,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..6d6c260460167 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 + issuePagingNum := config.Value[int]{} + setting.Config().UI.IssuePagingNum = issuePagingNum.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 + issuePagingNum := config.Value[int]{} + setting.Config().UI.IssuePagingNum = issuePagingNum.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 + issuePagingNum := config.Value[int]{} + setting.Config().UI.IssuePagingNum = issuePagingNum.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 + issuePagingNum := config.Value[int]{} + setting.Config().UI.IssuePagingNum = issuePagingNum.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 + issuePagingNum := config.Value[int]{} + setting.Config().UI.IssuePagingNum = issuePagingNum.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 01dc8cf697c5b..0c125a08cd4c5 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -720,6 +720,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..3eb43a37cb58b --- /dev/null +++ b/services/forms/admin_config_form.go @@ -0,0 +1,44 @@ +// 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 + GraphMaxCommitNum int + CodeCommentLines int + ReactionMaxUserNum int + MaxDisplayFileSize int64 + ShowUserEmail bool + DefaultShowFullName bool + DefaultTheme string + Themes []string + SearchRepoDescription bool + OnlyShowRelevantRepos bool + ExplorePagingDefaultSort string `binding:"In(recentupdate,alphabetically,reverselastlogin,newest,oldest)"` + PreferredTimestampTense string `binding:"In(mixed,absolute)"` + + AmbiguousUnicodeDetection bool +} + +// 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/issue/reaction_test.go b/services/issue/reaction_test.go index 7734860fc0d71..cc1072341e845 100644 --- a/services/issue/reaction_test.go +++ b/services/issue/reaction_test.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/setting/config" "github.com/stretchr/testify/assert" ) @@ -72,7 +73,8 @@ func TestIssueDeleteReaction(t *testing.T) { func TestIssueReactionCount(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - setting.UI.ReactionMaxUserNum = 2 + reactionMaxUserNum := config.Value[int]{} + setting.Config().UI.ReactionMaxUserNum = reactionMaxUserNum.WithDefault(2) user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) @@ -101,13 +103,13 @@ func TestIssueReactionCount(t *testing.T) { reactions := reactionsList.GroupByType() assert.Len(t, reactions["heart"], 4) - assert.Equal(t, 2, reactions["heart"].GetMoreUserCount()) - assert.Equal(t, user1.Name+", "+user2.Name, reactions["heart"].GetFirstUsers()) + assert.Equal(t, 2, reactions["heart"].GetMoreUserCount(t.Context())) + assert.Equal(t, user1.Name+", "+user2.Name, reactions["heart"].GetFirstUsers(t.Context())) assert.True(t, reactions["heart"].HasUser(1)) assert.False(t, reactions["heart"].HasUser(5)) assert.False(t, reactions["heart"].HasUser(0)) assert.Len(t, reactions["+1"], 2) - assert.Equal(t, 0, reactions["+1"].GetMoreUserCount()) + assert.Equal(t, 0, reactions["+1"].GetMoreUserCount(t.Context())) assert.Len(t, reactions["-1"], 1) } 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 57dde25b3baae..503760a2c8a30 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -525,8 +525,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/pull/reviewer.go b/services/pull/reviewer.go index bf0d8cb298c8b..5d385be06edf4 100644 --- a/services/pull/reviewer.go +++ b/services/pull/reviewer.go @@ -62,7 +62,7 @@ func GetReviewers(ctx context.Context, repo *repo_model.Repository, doerID, post if len(uniqueUserIDs) > 0 { if err := e.In("id", uniqueUserIDs.Values()). Where(builder.Eq{"`user`.is_active": true}). - OrderBy(user_model.GetOrderByName()). + OrderBy(user_model.GetOrderByName(ctx)). Find(&users); err != nil { return nil, err } diff --git a/services/repository/gitgraph/graph.go b/services/repository/gitgraph/graph.go index d06d18c1b4d79..975dbf18a19a4 100644 --- a/services/repository/gitgraph/graph.go +++ b/services/repository/gitgraph/graph.go @@ -15,7 +15,7 @@ import ( ) // GetCommitGraph return a list of commit (GraphItems) from all branches -func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bool, branches, files []string) (*Graph, error) { +func GetCommitGraph(ctx context.Context, r *git.Repository, page, maxAllowedColors int, hidePRRefs bool, branches, files []string) (*Graph, error) { format := "DATA:%D|%H|%ad|%h|%s" if page == 0 { @@ -33,7 +33,7 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo } graphCmd.AddArguments("-C", "-M", "--date=iso-strict"). - AddOptionFormat("-n %d", setting.UI.GraphMaxCommitNum*page). + AddOptionFormat("-n %d", setting.Config().UI.GraphMaxCommitNum.Value(ctx)*page). AddOptionFormat("--pretty=format:%s", format) if len(branches) > 0 { @@ -49,7 +49,7 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo if err != nil { return nil, err } - commitsToSkip := setting.UI.GraphMaxCommitNum * (page - 1) + commitsToSkip := setting.Config().UI.GraphMaxCommitNum.Value(ctx) * (page - 1) scanner := bufio.NewScanner(stdoutReader) diff --git a/services/repository/gitgraph/graph_test.go b/services/repository/gitgraph/graph_test.go index 4c48b94aa20ae..ea35209c307da 100644 --- a/services/repository/gitgraph/graph_test.go +++ b/services/repository/gitgraph/graph_test.go @@ -22,7 +22,7 @@ func BenchmarkGetCommitGraph(b *testing.B) { defer currentRepo.Close() for b.Loop() { - graph, err := GetCommitGraph(currentRepo, 1, 0, false, nil, nil) + graph, err := GetCommitGraph(b.Context(), currentRepo, 1, 0, false, nil, nil) if err != nil { b.Error("Could get commit graph") } diff --git a/services/repository/push.go b/services/repository/push.go index 0ea51f9c072d2..a05371bb43345 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -263,8 +263,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..d0255dca1086e 100644 --- a/templates/admin/config_settings.tmpl +++ b/templates/admin/config_settings.tmpl @@ -1,4 +1,120 @@ {{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin config")}} +