This commit is contained in:
hiifong 2025-03-12 15:40:54 +00:00 committed by GitHub
commit 216b0b84fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
104 changed files with 766 additions and 215 deletions

View File

@ -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`.

View File

@ -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)
}

View File

@ -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)

View File

@ -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

View File

@ -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),

View File

@ -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)
}

View File

@ -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

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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.

View File

@ -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
}

View File

@ -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
}

View File

@ -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"

View File

@ -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{

View File

@ -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"),
},
}
}

View File

@ -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`},

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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")
}

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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},

View File

@ -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),

View File

@ -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++
}

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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{

View File

@ -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") {

View File

@ -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)
}

View File

@ -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),

View File

@ -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
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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,

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -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")
}

View File

@ -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)

View File

@ -1,4 +1,120 @@
{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin config")}}
<h4 class="ui top attached header">
UI
</h4>
<div class="ui attached table segment">
<form class="ui form" method="post" action="{{AppSubUrl}}/-/admin/config/ui">
{{.CsrfTokenHtml}}
<dl class="admin-dl-horizontal">
<dt>{{ctx.Locale.Tr "admin.config.ui.explore_paging_num"}}</dt>
<dd>
<div class="inline field">
<input name="explore_paging_num" type="number" min="0" value="{{.UI.ExplorePagingNum}}">
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.issue_paging_num"}}</dt>
<dd>
<div class="inline field">
<input name="issue_paging_num" type="number" min="0" value="{{.UI.IssuePagingNum}}">
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.feed_max_commit_numb"}}</dt>
<dd>
<div class="inline field">
<input name="feed_max_commit_num" type="number" min="0" value="{{.UI.FeedMaxCommitNum}}">
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.feed_paging_num"}}</dt>
<dd>
<div class="inline field">
<input name="feed_paging_num" type="number" min="0" value="{{.UI.FeedPagingNum}}">
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.packages_paging_num"}}</dt>
<dd>
<div class="inline field">
<input name="packages_paging_num" type="number" min="0" value="{{.UI.PackagesPagingNum}}">
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.repo_search_paging_num"}}</dt>
<dd>
<div class="inline field">
<input name="repo_search_paging_num" type="number" min="0" value="{{.UI.RepoSearchPagingNum}}">
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.members_paging_num"}}</dt>
<dd>
<div class="inline field">
<input name="members_paging_num" type="number" min="0" value="{{.UI.MembersPagingNum}}">
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.sitemap_paging_num"}}</dt>
<dd>
<div class="inline field">
<input name="sitemap_paging_num" type="number" min="0" value="{{.UI.SitemapPagingNum}}">
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.graph_max_commit_num"}}</dt>
<dd>
<div class="inline field">
<input name="graph_max_commit_num" type="number" min="0" value="{{.UI.GraphMaxCommitNum}}">
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.code_comment_lines"}}</dt>
<dd>
<div class="inline field">
<input name="code_comment_lines" type="number" min="0" value="{{.UI.CodeCommentLines}}">
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.reaction_max_user_num"}}</dt>
<dd>
<div class="inline field">
<input name="reaction_max_user_num" type="number" min="0" value="{{.UI.ReactionMaxUserNum}}">
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.explore_paging_default_sort"}}</dt>
<dd>
<div class="inline field">
<select name="explore_paging_default_sort" class="ui dropdown">
{{$explorePagingDefaultSort := .UI.ExplorePagingDefaultSort}}
{{range $value := .UI.ExplorePagingSortOption}}
<option value="{{$value}}" {{Iif (eq $explorePagingDefaultSort $value) "selected"}}>{{$value}}</option>
{{end}}
</select>
</div>
</dd>
<div class="divider"></div>
<dt>{{ctx.Locale.Tr "admin.config.ui.show_user_email"}}</dt>
<dd>
<div class="ui toggle checkbox">
<input name="show_user_email" type="checkbox" {{if .UI.ShowUserEmail}}checked{{end}}><label></label>
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.default_show_full_name"}}</dt>
<dd>
<div class="ui toggle checkbox">
<input name="default_show_full_name" type="checkbox" {{if .UI.DefaultShowFullName}}checked{{end}}><label></label>
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.search_repo_description"}}</dt>
<dd>
<div class="ui toggle checkbox">
<input name="search_repo_description" type="checkbox" {{if .UI.SearchRepoDescription}}checked{{end}}><label></label>
</div>
</dd>
<dt>{{ctx.Locale.Tr "admin.config.ui.only_show_relevant_repos"}}</dt>
<dd>
<div class="ui toggle checkbox">
<input name="only_show_relevant_repos" type="checkbox" {{if .UI.OnlyShowRelevantRepos}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
<dt><button class="ui primary button">{{ctx.Locale.Tr "save"}}</button></dt>
<dd></dd>
</dl>
</form>
</div>
<h4 class="ui top attached header">
{{ctx.Locale.Tr "admin.config.picture_config"}}
</h4>

View File

@ -52,7 +52,7 @@
<tr>
<td>{{.ID}}</td>
<td>
<a href="{{.HomeLink}}">{{if and DefaultShowFullName .FullName}}{{.FullName}} ({{.Name}}){{else}}{{.Name}}{{end}}</a>
<a href="{{.HomeLink}}">{{if and (DefaultShowFullName ctx) .FullName}}{{.FullName}} ({{.Name}}){{else}}{{.Name}}{{end}}</a>
{{if .Visibility.IsPrivate}}
<span class="text gold">{{svg "octicon-lock"}}</span>
{{end}}

View File

@ -30,9 +30,9 @@
{{$hasRepositoryAccess = index $.RepositoryAccessMap .Repository.ID}}
{{end}}
{{if $hasRepositoryAccess}}
{{ctx.Locale.Tr "packages.published_by_in" $timeStr .Creator.HomeLink .Creator.GetDisplayName .Repository.Link .Repository.FullName}}
{{ctx.Locale.Tr "packages.published_by_in" $timeStr .Creator.HomeLink (.Creator.GetDisplayName ctx) .Repository.Link .Repository.FullName}}
{{else}}
{{ctx.Locale.Tr "packages.published_by" $timeStr .Creator.HomeLink .Creator.GetDisplayName}}
{{ctx.Locale.Tr "packages.published_by" $timeStr .Creator.HomeLink .Creator.GetDisplayName ctx}}
{{end}}
</div>
</div>

View File

@ -25,7 +25,7 @@
<div class="flex-item-main">
<a class="flex-item-title" href="{{.VersionWebLink}}">{{.Version.LowerVersion}}</a>
<div class="flex-item-body">
{{ctx.Locale.Tr "packages.published_by" (DateUtils.TimeSince .Version.CreatedUnix) .Creator.HomeLink .Creator.GetDisplayName}}
{{ctx.Locale.Tr "packages.published_by" (DateUtils.TimeSince .Version.CreatedUnix) .Creator.HomeLink .Creator.GetDisplayName ctx}}
</div>
</div>
</div>

View File

@ -8,9 +8,9 @@
<div>
{{$timeStr := DateUtils.TimeSince .PackageDescriptor.Version.CreatedUnix}}
{{if .HasRepositoryAccess}}
{{ctx.Locale.Tr "packages.published_by_in" $timeStr .PackageDescriptor.Creator.HomeLink .PackageDescriptor.Creator.GetDisplayName .PackageDescriptor.Repository.Link .PackageDescriptor.Repository.FullName}}
{{ctx.Locale.Tr "packages.published_by_in" $timeStr .PackageDescriptor.Creator.HomeLink (.PackageDescriptor.Creator.GetDisplayName ctx) .PackageDescriptor.Repository.Link .PackageDescriptor.Repository.FullName}}
{{else}}
{{ctx.Locale.Tr "packages.published_by" $timeStr .PackageDescriptor.Creator.HomeLink .PackageDescriptor.Creator.GetDisplayName}}
{{ctx.Locale.Tr "packages.published_by" $timeStr .PackageDescriptor.Creator.HomeLink (.PackageDescriptor.Creator.GetDisplayName ctx)}}
{{end}}
</div>
</div>

View File

@ -40,7 +40,7 @@
</a>
{{range .Actors}}
<a class="item{{if eq .ID $.CurActor}} active{{end}}" href="?workflow={{$.CurWorkflow}}&actor={{.ID}}&status={{$.CurStatus}}">
{{ctx.AvatarUtils.Avatar . 20}} {{.GetDisplayName}}
{{ctx.AvatarUtils.Avatar . 20}} {{.GetDisplayName ctx}}
</a>
{{end}}
</div>

View File

@ -22,7 +22,7 @@
{{ctx.Locale.Tr "actions.runs.commit"}}
<a href="{{$.RepoLink}}/commit/{{.CommitSHA}}">{{ShortSha .CommitSHA}}</a>
{{ctx.Locale.Tr "actions.runs.pushed_by"}}
<a href="{{.TriggerUser.HomeLink}}">{{.TriggerUser.GetDisplayName}}</a>
<a href="{{.TriggerUser.HomeLink}}">{{.TriggerUser.GetDisplayName ctx}}</a>
{{- end -}}
</div>
</div>

View File

@ -17,7 +17,7 @@
<div class="tw-flex">
{{$userName := .Author.Name}}
{{if .User}}
{{if and .User.FullName DefaultShowFullName}}
{{if and .User.FullName (DefaultShowFullName ctx)}}
{{$userName = .User.FullName}}
{{end}}
{{ctx.AvatarUtils.Avatar .User 28 "tw-mr-2"}}<a class="muted author-wrapper" href="{{.User.HomeLink}}">{{$userName}}</a>

View File

@ -21,18 +21,18 @@
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.ContextUser.ShortName ctx 40}}</span>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
{{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.SignedUser.ShortName ctx 40}}</span>
</div>
{{range .Orgs}}
<div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar . 28 "mini"}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
<span class="truncated-item-name">{{.ShortName ctx 40}}</span>
</div>
{{end}}
</div>

View File

@ -40,7 +40,7 @@
<span class="author flex-text-inline">
{{$userName := $commit.Commit.Author.Name}}
{{if $commit.User}}
{{if and $commit.User.FullName DefaultShowFullName}}
{{if and $commit.User.FullName (DefaultShowFullName ctx)}}
{{$userName = $commit.User.FullName}}
{{end}}
{{ctx.AvatarUtils.Avatar $commit.User 18}}

View File

@ -28,9 +28,9 @@
{{if .OriginalAuthor}}
{{ctx.Locale.Tr .GetLastEventLabelFake $timeStr .OriginalAuthor}}
{{else if gt .Poster.ID 0}}
{{ctx.Locale.Tr .GetLastEventLabel $timeStr .Poster.HomeLink .Poster.GetDisplayName}}
{{ctx.Locale.Tr .GetLastEventLabel $timeStr .Poster.HomeLink (.Poster.GetDisplayName ctx)}}
{{else}}
{{ctx.Locale.Tr .GetLastEventLabelFake $timeStr .Poster.GetDisplayName}}
{{ctx.Locale.Tr .GetLastEventLabelFake $timeStr .Poster.GetDisplayName ctx}}
{{end}}
</span>
</div>

View File

@ -118,7 +118,7 @@
</div>
{{range .Assignees}}
<div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/assignee">
{{ctx.AvatarUtils.Avatar . 20}} {{.GetDisplayName}}
{{ctx.AvatarUtils.Avatar . 20}} {{.GetDisplayName ctx}}
</div>
{{end}}
</div>

View File

@ -31,7 +31,7 @@
<span class="item empty-list {{if $issueAssignees}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_assignees"}}</span>
{{range $issueAssignees}}
<a class="item" href="{{$pageMeta.RepoLink}}/{{if $pageMeta.IsPullRequest}}pulls{{else}}issues{{end}}?assignee={{.ID}}">
{{ctx.AvatarUtils.Avatar . 20}} {{.GetDisplayName}}
{{ctx.AvatarUtils.Avatar . 20}} {{.GetDisplayName ctx}}
</a>
{{end}}
</div>

View File

@ -3,7 +3,7 @@
<span class="text"><strong>{{ctx.Locale.Tr "repo.issues.num_participants" .NumParticipants}}</strong></span>
<div class="ui list tw-flex tw-flex-wrap">
{{range .Participants}}
<a {{if gt .ID 0}}href="{{.HomeLink}}"{{end}} data-tooltip-content="{{.GetDisplayName}}">
<a {{if gt .ID 0}}href="{{.HomeLink}}"{{end}} data-tooltip-content="{{.GetDisplayName ctx}}">
{{ctx.AvatarUtils.Avatar . 20 "tw-my-0.5 tw-mr-1"}}
</a>
{{end}}

View File

@ -51,7 +51,7 @@
<div class="item">
<div class="flex-text-inline tw-flex-1">
{{if .User}}
<a class="muted flex-text-inline tw-gap-2" href="{{.User.HomeLink}}">{{ctx.AvatarUtils.Avatar .User 20}} {{.User.GetDisplayName}}</a>
<a class="muted flex-text-inline tw-gap-2" href="{{.User.HomeLink}}">{{ctx.AvatarUtils.Avatar .User 20}} {{.User.GetDisplayName ctx}}</a>
{{else if .Team}}
<span class="flex-text-inline tw-gap-2">{{svg "octicon-people" 20}} {{$repoOwnerName}}/{{.Team.Name}}</span>
{{end}}

View File

@ -202,7 +202,7 @@
{{if eq .Poster.ID .Assignee.ID}}
{{ctx.Locale.Tr "repo.issues.remove_self_assignment" $createdStr}}
{{else}}
{{ctx.Locale.Tr "repo.issues.remove_assignee_at" .Poster.GetDisplayName $createdStr}}
{{ctx.Locale.Tr "repo.issues.remove_assignee_at" (.Poster.GetDisplayName ctx) $createdStr}}
{{end}}
</span>
{{else}}
@ -212,7 +212,7 @@
{{if eq .Poster.ID .AssigneeID}}
{{ctx.Locale.Tr "repo.issues.self_assign_at" $createdStr}}
{{else}}
{{ctx.Locale.Tr "repo.issues.add_assignee_at" .Poster.GetDisplayName $createdStr}}
{{ctx.Locale.Tr "repo.issues.add_assignee_at" (.Poster.GetDisplayName ctx) $createdStr}}
{{end}}
</span>
{{end}}
@ -521,10 +521,10 @@
{{if eq .PosterID .AssigneeID}}
{{ctx.Locale.Tr "repo.issues.review.remove_review_request_self" $createdStr}}
{{else}}
{{ctx.Locale.Tr "repo.issues.review.remove_review_request" .Assignee.GetDisplayName $createdStr}}
{{ctx.Locale.Tr "repo.issues.review.remove_review_request" (.Assignee.GetDisplayName ctx) $createdStr}}
{{end}}
{{else}}
{{ctx.Locale.Tr "repo.issues.review.add_review_request" .Assignee.GetDisplayName $createdStr}}
{{ctx.Locale.Tr "repo.issues.review.add_review_request" (.Assignee.GetDisplayName ctx) $createdStr}}
{{end}}
{{else}}
<!-- If the assigned team is deleted, just displaying "Ghost Team" in the comment -->

View File

@ -16,7 +16,7 @@
{{$needDivider = true}}
<div class="item context js-aria-clickable quote-reply {{if .diff}}quote-reply-diff{{end}}" data-target="{{.item.HashTag}}-raw">{{ctx.Locale.Tr "repo.issues.context.quote_reply"}}</div>
{{if not ctx.Consts.RepoUnitTypeIssues.UnitGlobalDisabled}}
<div class="item context js-aria-clickable reference-issue" data-target="{{.item.HashTag}}-raw" data-modal="#reference-issue-modal" data-poster="{{.item.Poster.GetDisplayName}}" data-poster-username="{{.item.Poster.Name}}" data-reference="{{$referenceUrl}}">{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}</div>
<div class="item context js-aria-clickable reference-issue" data-target="{{.item.HashTag}}-raw" data-modal="#reference-issue-modal" data-poster="{{.item.Poster.GetDisplayName ctx}}" data-poster-username="{{.item.Poster.Name}}" data-reference="{{$referenceUrl}}">{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}</div>
{{end}}
{{if or ctx.RootData.Permission.IsAdmin .IsCommentPoster ctx.RootData.HasIssuesOrPullsWritePermission}}
<div class="divider"></div>
@ -33,10 +33,10 @@
<div class="divider"></div>
{{end}}
{{if $canUserBlock}}
<div class="item context js-aria-clickable show-modal" data-modal="#block-user-modal" data-modal-modal-blockee="{{.item.Poster.Name}}" data-modal-modal-blockee-name="{{.item.Poster.GetDisplayName}}" data-modal-modal-form.action="{{AppSubUrl}}/user/settings/blocked_users">{{ctx.Locale.Tr "user.block.block.user"}}</div>
<div class="item context js-aria-clickable show-modal" data-modal="#block-user-modal" data-modal-modal-blockee="{{.item.Poster.Name}}" data-modal-modal-blockee-name="{{.item.Poster.GetDisplayName ctx}}" data-modal-modal-form.action="{{AppSubUrl}}/user/settings/blocked_users">{{ctx.Locale.Tr "user.block.block.user"}}</div>
{{end}}
{{if $canOrgBlock}}
<div class="item context js-aria-clickable show-modal" data-modal="#block-user-modal" data-modal-modal-blockee="{{.item.Poster.Name}}" data-modal-modal-blockee-name="{{.item.Poster.GetDisplayName}}" data-modal-modal-form.action="{{ctx.RootData.Repository.Owner.OrganisationLink}}/settings/blocked_users">{{ctx.Locale.Tr "user.block.block.org"}}</div>
<div class="item context js-aria-clickable show-modal" data-modal="#block-user-modal" data-modal-modal-blockee="{{.item.Poster.Name}}" data-modal-modal-blockee-name="{{.item.Poster.GetDisplayName ctx}}" data-modal-modal-form.action="{{ctx.RootData.Repository.Owner.OrganisationLink}}/settings/blocked_users">{{ctx.Locale.Tr "user.block.block.org"}}</div>
{{end}}
{{end}}
{{end}}

View File

@ -3,8 +3,8 @@
{{$hasReacted := $value.HasUser ctx.RootData.SignedUserID}}
<a role="button" class="ui label basic{{if $hasReacted}} primary{{end}}{{if not ctx.RootData.IsSigned}} disabled{{end}}"
data-global-click="onCommentReactionButtonClick"
data-tooltip-content title="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ctx.Locale.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}"
aria-label="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ctx.Locale.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}"
data-tooltip-content title="{{$value.GetFirstUsers ctx}}{{if gt ($value.GetMoreUserCount ctx) 0}} {{ctx.Locale.Tr "repo.reactions_more" ($value.GetMoreUserCount ctx)}}{{end}}"
aria-label="{{$value.GetFirstUsers ctx}}{{if gt ($value.GetMoreUserCount ctx) 0}} {{ctx.Locale.Tr "repo.reactions_more" ($value.GetMoreUserCount ctx)}}{{end}}"
data-tooltip-placement="bottom-start"
data-reaction-content="{{$key}}" data-has-reacted="{{$hasReacted}}">
<span class="reaction">{{ReactionToEmoji $key}}</span>

View File

@ -78,7 +78,7 @@
{{.Issue.OriginalAuthor}}
<span class="pull-desc">{{ctx.Locale.Tr "repo.pulls.merged_title_desc" .NumCommits $headHref $baseHref $mergedStr}}</span>
{{else}}
<a {{if gt .Issue.PullRequest.Merger.ID 0}}href="{{.Issue.PullRequest.Merger.HomeLink}}"{{end}}>{{.Issue.PullRequest.Merger.GetDisplayName}}</a>
<a {{if gt .Issue.PullRequest.Merger.ID 0}}href="{{.Issue.PullRequest.Merger.HomeLink}}"{{end}}>{{.Issue.PullRequest.Merger.GetDisplayName ctx}}</a>
<span class="pull-desc">{{ctx.Locale.Tr "repo.pulls.merged_title_desc" .NumCommits $headHref $baseHref $mergedStr}}</span>
{{end}}
{{else}}
@ -86,7 +86,7 @@
<span id="pull-desc-display" class="pull-desc">{{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}</span>
{{else}}
<span id="pull-desc-display" class="pull-desc">
<a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.GetDisplayName}}</a>
<a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.GetDisplayName ctx}}</a>
{{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}
</span>
{{end}}
@ -126,9 +126,9 @@
{{if .Issue.OriginalAuthor}}
{{ctx.Locale.Tr "repo.issues.opened_by_fake" $createdStr .Issue.OriginalAuthor}}
{{else if gt .Issue.Poster.ID 0}}
{{ctx.Locale.Tr "repo.issues.opened_by" $createdStr .Issue.Poster.HomeLink .Issue.Poster.GetDisplayName}}
{{ctx.Locale.Tr "repo.issues.opened_by" $createdStr .Issue.Poster.HomeLink (.Issue.Poster.GetDisplayName ctx)}}
{{else}}
{{ctx.Locale.Tr "repo.issues.opened_by_fake" $createdStr .Issue.Poster.GetDisplayName}}
{{ctx.Locale.Tr "repo.issues.opened_by_fake" $createdStr .Issue.Poster.GetDisplayName ctx}}
{{end}}
·
{{ctx.Locale.TrN .Issue.NumComments "repo.issues.num_comments_1" "repo.issues.num_comments" .Issue.NumComments}}

View File

@ -4,7 +4,7 @@
{{else}}
{{if .LatestCommitUser}}
{{ctx.AvatarUtils.Avatar .LatestCommitUser 24}}
{{if and .LatestCommitUser.FullName DefaultShowFullName}}
{{if and .LatestCommitUser.FullName (DefaultShowFullName ctx)}}
<a class="muted author-wrapper" title="{{.LatestCommitUser.FullName}}" href="{{.LatestCommitUser.HomeLink}}"><strong>{{.LatestCommitUser.FullName}}</strong></a>
{{else}}
<a class="muted author-wrapper" title="{{if .LatestCommit.Author}}{{.LatestCommit.Author.Name}}{{else}}{{.LatestCommitUser.Name}}{{end}}" href="{{.LatestCommitUser.HomeLink}}"><strong>{{if .LatestCommit.Author}}{{.LatestCommit.Author.Name}}{{else}}{{.LatestCommitUser.Name}}{{end}}</strong></a>

View File

@ -64,18 +64,18 @@
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.ContextUser.ShortName ctx 40}}</span>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
{{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.SignedUser.ShortName ctx 40}}</span>
</div>
{{range .Orgs}}
<div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar . 28 "mini"}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
<span class="truncated-item-name">{{.ShortName ctx 40}}</span>
</div>
{{end}}
</div>

View File

@ -65,18 +65,18 @@
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.ContextUser.ShortName ctx 40}}</span>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
{{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.SignedUser.ShortName ctx 40}}</span>
</div>
{{range .Orgs}}
<div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar . 28 "mini"}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
<span class="truncated-item-name">{{.ShortName ctx 40}}</span>
</div>
{{end}}
</div>

View File

@ -38,18 +38,18 @@
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{ctx.AvatarUtils.Avatar .ContextUser}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.ContextUser.ShortName ctx 40}}</span>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
{{ctx.AvatarUtils.Avatar .SignedUser}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.SignedUser.ShortName ctx 40}}</span>
</div>
{{range .Orgs}}
<div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar .}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
<span class="truncated-item-name">{{.ShortName ctx 40}}</span>
</div>
{{end}}
</div>

View File

@ -80,18 +80,18 @@
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.ContextUser.ShortName ctx 40}}</span>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
{{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.SignedUser.ShortName ctx 40}}</span>
</div>
{{range .Orgs}}
<div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar . 28 "mini"}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
<span class="truncated-item-name">{{.ShortName ctx 40}}</span>
</div>
{{end}}
</div>

View File

@ -76,18 +76,18 @@
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{ctx.AvatarUtils.Avatar .ContextUser}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.ContextUser.ShortName ctx 40}}</span>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
{{ctx.AvatarUtils.Avatar .SignedUser}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.SignedUser.ShortName ctx 40}}</span>
</div>
{{range .Orgs}}
<div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar .}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
<span class="truncated-item-name">{{.ShortName ctx 40}}</span>
</div>
{{end}}
</div>

View File

@ -78,18 +78,18 @@
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.ContextUser.ShortName ctx 40}}</span>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
{{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.SignedUser.ShortName ctx 40}}</span>
</div>
{{range .Orgs}}
<div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar . 28 "mini"}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
<span class="truncated-item-name">{{.ShortName ctx 40}}</span>
</div>
{{end}}
</div>

View File

@ -75,18 +75,18 @@
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.ContextUser.ShortName ctx 40}}</span>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
{{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.SignedUser.ShortName ctx 40}}</span>
</div>
{{range .Orgs}}
<div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar . 28 "mini"}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
<span class="truncated-item-name">{{.ShortName ctx 40}}</span>
</div>
{{end}}
</div>

View File

@ -78,18 +78,18 @@
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{ctx.AvatarUtils.Avatar .ContextUser}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.ContextUser.ShortName ctx 40}}</span>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
{{ctx.AvatarUtils.Avatar .SignedUser}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.SignedUser.ShortName ctx 40}}</span>
</div>
{{range .Orgs}}
<div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar .}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
<span class="truncated-item-name">{{.ShortName ctx 40}}</span>
</div>
{{end}}
</div>

View File

@ -65,18 +65,18 @@
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.ContextUser.ShortName ctx 40}}</span>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
{{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.SignedUser.ShortName ctx 40}}</span>
</div>
{{range .Orgs}}
<div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar . 28 "mini"}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
<span class="truncated-item-name">{{.ShortName ctx 40}}</span>
</div>
{{end}}
</div>

View File

@ -14,20 +14,20 @@
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.ContextUser.ShortName ctx 40}}</span>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
{{if .CanForkToUser}}
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
{{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.SignedUser.ShortName ctx 40}}</span>
</div>
{{end}}
{{range .Orgs}}
<div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar . 28 "mini"}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
<span class="truncated-item-name">{{.ShortName ctx 40}}</span>
</div>
{{end}}
</div>

View File

@ -50,7 +50,7 @@
{{svg (MigrationIcon $release.Repo.GetOriginalURLHostname) 20 "tw-mr-1"}}{{$release.OriginalAuthor}}
{{else if $release.Publisher}}
{{ctx.AvatarUtils.Avatar $release.Publisher 20 "tw-mr-1"}}
<a href="{{$release.Publisher.HomeLink}}">{{$release.Publisher.GetDisplayName}}</a>
<a href="{{$release.Publisher.HomeLink}}">{{$release.Publisher.GetDisplayName ctx}}</a>
{{else}}
Ghost
{{end}}

View File

@ -1 +1 @@
<span class="gt-ellipsis">{{.Name}}{{if DefaultShowFullName}}<span class="search-fullname"> {{.FullName}}</span>{{end}}</span>
<span class="gt-ellipsis">{{.Name}}{{if DefaultShowFullName ctx}}<span class="search-fullname"> {{.FullName}}</span>{{end}}</span>

View File

@ -89,7 +89,7 @@
{{$userIDs := .AllowlistUserIDs}}
{{range $.Users}}
{{if SliceUtils.Contains $userIDs .ID}}
<a class="ui basic label" href="{{.HomeLink}}">{{ctx.AvatarUtils.Avatar . 26}} {{.GetDisplayName}}</a>
<a class="ui basic label" href="{{.HomeLink}}">{{ctx.AvatarUtils.Avatar . 26}} {{.GetDisplayName ctx}}</a>
{{end}}
{{end}}
{{if $.Owner.IsOrganization}}

View File

@ -44,9 +44,9 @@
{{if .OriginalAuthor}}
{{ctx.Locale.Tr .GetLastEventLabelFake $timeStr .OriginalAuthor}}
{{else if gt .Poster.ID 0}}
{{ctx.Locale.Tr .GetLastEventLabel $timeStr .Poster.HomeLink .Poster.GetDisplayName}}
{{ctx.Locale.Tr .GetLastEventLabel $timeStr .Poster.HomeLink (.Poster.GetDisplayName ctx)}}
{{else}}
{{ctx.Locale.Tr .GetLastEventLabelFake $timeStr .Poster.GetDisplayName}}
{{ctx.Locale.Tr .GetLastEventLabelFake $timeStr .Poster.GetDisplayName ctx}}
{{end}}
{{if .IsPull}}
<div class="branches flex-text-inline">
@ -137,7 +137,7 @@
{{if .Assignees}}
<div class="text grey">
{{range .Assignees}}
<a class="ui assignee tw-no-underline" href="{{.HomeLink}}" data-tooltip-content="{{.GetDisplayName}}">
<a class="ui assignee tw-no-underline" href="{{.HomeLink}}" data-tooltip-content="{{.GetDisplayName ctx}}">
{{ctx.AvatarUtils.Avatar . 20}}
</a>
{{end}}

View File

@ -1 +1 @@
<a class="author text black tw-font-semibold muted"{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName}}</a>{{if .IsTypeBot}}<span class="ui basic label tw-p-1 tw-align-baseline">bot</span>{{end}}
<a class="author text black tw-font-semibold muted"{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName ctx}}</a>{{if .IsTypeBot}}<span class="ui basic label tw-p-1 tw-align-baseline">bot</span>{{end}}

View File

@ -39,7 +39,7 @@
</div>
<div class="flex-item-main">
<div class="flex-item-title">
<a class="item" href="{{.Blockee.HTMLURL}}">{{.Blockee.GetDisplayName}}</a>
<a class="item" href="{{.Blockee.HTMLURL}}">{{.Blockee.GetDisplayName ctx}}</a>
</div>
{{if .Note}}
<div class="flex-item-body">

View File

@ -1 +1 @@
<a{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName}}</a>
<a{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName ctx}}</a>

View File

@ -125,7 +125,7 @@
{{end}}
<li>
{{if not .UserBlocking}}
<a class="muted show-modal" href="#" data-modal="#block-user-modal" data-modal-modal-blockee="{{.ContextUser.Name}}" data-modal-modal-blockee-name="{{.ContextUser.GetDisplayName}}" data-modal-modal-form.action="{{AppSubUrl}}/user/settings/blocked_users">{{ctx.Locale.Tr "user.block.block.user"}}</a>
<a class="muted show-modal" href="#" data-modal="#block-user-modal" data-modal-modal-blockee="{{.ContextUser.Name}}" data-modal-modal-blockee-name="{{.ContextUser.GetDisplayName ctx}}" data-modal-modal-form.action="{{AppSubUrl}}/user/settings/blocked_users">{{ctx.Locale.Tr "user.block.block.user"}}</a>
{{else}}
<a class="muted" href="{{AppSubUrl}}/user/settings/blocked_users">{{ctx.Locale.Tr "user.block.unblock"}}</a>
{{end}}

View File

@ -4,7 +4,7 @@
<div class="ui floating dropdown jump">
<span class="text truncated-item-container">
{{ctx.AvatarUtils.Avatar .ContextUser 24 "tw-mr-1"}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.ContextUser.ShortName ctx 40}}</span>
<span class="org-visibility">
{{if .ContextUser.Visibility.IsLimited}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
{{if .ContextUser.Visibility.IsPrivate}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</div>{{end}}
@ -18,7 +18,7 @@
<div class="scrolling menu items">
<a class="{{if eq .ContextUser.ID .SignedUser.ID}}active selected{{end}} item truncated-item-container" href="{{AppSubUrl}}/{{if .PageIsIssues}}issues{{else if .PageIsPulls}}pulls{{else if .PageIsMilestonesDashboard}}milestones{{end}}">
{{ctx.AvatarUtils.Avatar .SignedUser}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
<span class="truncated-item-name">{{.SignedUser.ShortName ctx 40}}</span>
<span class="org-visibility">
{{if .SignedUser.Visibility.IsLimited}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
{{if .SignedUser.Visibility.IsPrivate}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</div>{{end}}
@ -27,7 +27,7 @@
{{range .Orgs}}
<a class="{{if eq $.ContextUser.ID .ID}}active selected{{end}} item truncated-item-container" title="{{.Name}}" href="{{.OrganisationLink}}/{{if $.PageIsIssues}}issues{{else if $.PageIsPulls}}pulls{{else if $.PageIsMilestonesDashboard}}milestones{{else}}dashboard{{end}}">
{{ctx.AvatarUtils.Avatar .}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
<span class="truncated-item-name">{{.ShortName ctx 40}}</span>
<span class="org-visibility">
{{if .Visibility.IsLimited}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
{{if .Visibility.IsPrivate}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</div>{{end}}

Some files were not shown because too many files have changed in this diff Show More