Move some service configuration from app.ini to database

pull/35607/head
Lunny Xiao 2025-10-08 14:20:54 +07:00
parent 03fce8f3d0
commit 82067ba731
No known key found for this signature in database
GPG Key ID: C3B7C91B632F738A
35 changed files with 311 additions and 125 deletions

@ -844,13 +844,16 @@ LEVEL = Info
;;
;; Default value for KeepEmailPrivate
;; Each new user will get the value of this setting copied into their profile
;; Deprecated: Moved to database based configuration which can be set from UI
;DEFAULT_KEEP_EMAIL_PRIVATE = false
;;
;; Default value for AllowCreateOrganization
;; Every new user will have rights set to create organizations depending on this setting
;; Deprecated: Moved to database based configuration which can be set from UI
;DEFAULT_ALLOW_CREATE_ORGANIZATION = true
;; Default value for IsRestricted
;; Every new user will have restricted permissions depending on this setting
;; Deprecated: Moved to database based configuration which can be set from UI
;DEFAULT_USER_IS_RESTRICTED = false
;;
;; Either "public", "limited" or "private", default is "public"
@ -870,13 +873,16 @@ LEVEL = Info
;;
;; Default value for DefaultOrgMemberVisible
;; True will make the membership of the users visible when added to the organisation
;; Deprecated: Moved to database based configuration which can be set from UI
;DEFAULT_ORG_MEMBER_VISIBLE = false
;;
;; Default value for EnableDependencies
;; Repositories will use dependencies by default depending on this setting
;; Deprecated: Moved to database based configuration which can be set from UI
;DEFAULT_ENABLE_DEPENDENCIES = true
;;
;; Dependencies can be added from any repository where the user is granted access or only from the current repository depending on this setting.
;; Deprecated: Moved to database based configuration which can be set from UI
;ALLOW_CROSS_REPOSITORY_DEPENDENCIES = true
;;
;; Default map service. No external API support has been included. A service has to allow
@ -889,17 +895,21 @@ LEVEL = Info
; USER_LOCATION_MAP_URL =
;;
;; Enable heatmap on users profiles.
;; Deprecated: Moved to database based configuration which can be set from UI
;ENABLE_USER_HEATMAP = true
;;
;; Enable Timetracking
;; Deprecated: Moved to database based configuration which can be set from UI
;ENABLE_TIMETRACKING = true
;;
;; Default value for EnableTimetracking
;; Repositories will use timetracking by default depending on this setting
;; Deprecated: Moved to database based configuration which can be set from UI
;DEFAULT_ENABLE_TIMETRACKING = true
;;
;; Default value for AllowOnlyContributorsToTrackTime
;; Only users with write permissions can track time if this is true
;; Deprecated: Moved to database based configuration which can be set from UI
;DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME = true
;;
;; Value for the domain part of the user's email address in the git log if user
@ -918,10 +928,12 @@ LEVEL = Info
;; Default value for AutoWatchNewRepos
;; When adding a repo to a team or creating a new repo all team members will watch the
;; repo automatically if enabled
;; Deprecated: Moved to database based configuration which can be set from UI
;AUTO_WATCH_NEW_REPOS = true
;;
;; Default value for AutoWatchOnChanges
;; Make the user watch a repository When they commit for the first time
;; Deprecated: Moved to database based configuration which can be set from UI
;AUTO_WATCH_ON_CHANGES = false
;;
;; Minimum amount of time a user must exist before comments are kept when the user is deleted.

@ -7,8 +7,10 @@ import (
"testing"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/setting/config"
"github.com/stretchr/testify/assert"
)
@ -32,7 +34,11 @@ func TestIssueList_LoadRepositories(t *testing.T) {
func TestIssueList_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
setting.Service.EnableTimetracking = true
system.SetSettings(t.Context(), map[string]string{
setting.Config().Service.EnableTimeTracking.DynKey(): "true",
})
config.GetDynGetter().InvalidateCache()
issueList := issues_model.IssueList{
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}),
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}),

@ -14,9 +14,11 @@ import (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/system"
"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"
"xorm.io/builder"
@ -384,7 +386,10 @@ func TestCountIssues(t *testing.T) {
func TestIssueLoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
setting.Service.EnableTimetracking = true
system.SetSettings(t.Context(), map[string]string{
setting.Config().Service.EnableTimeTracking.DynKey(): "true",
})
config.GetDynGetter().InvalidateCache()
issueList := issues_model.IssueList{
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}),

@ -4,6 +4,7 @@
package v1_6
import (
"context"
"fmt"
"time"
@ -99,7 +100,7 @@ func AddIssueDependencies(x *xorm.Engine) (err error) {
unit.Config = make(map[string]any)
}
if _, ok := unit.Config["EnableDependencies"]; !ok {
unit.Config["EnableDependencies"] = setting.Service.DefaultEnableDependencies
unit.Config["EnableDependencies"] = setting.Config().Service.DefaultEnableDependencies.Value(context.Background())
}
if _, err := x.ID(unit.ID).Cols("config").Update(unit); err != nil {
return err

@ -326,7 +326,7 @@ func CreateOrganization(ctx context.Context, org *Organization, owner *user_mode
if err = db.Insert(ctx, &OrgUser{
UID: owner.ID,
OrgID: org.ID,
IsPublic: setting.Service.DefaultOrgMemberVisible,
IsPublic: setting.Config().Service.DefaultOrgMemberVisible.Value(ctx),
}); err != nil {
return fmt.Errorf("insert org-user relation: %w", err)
}
@ -504,7 +504,7 @@ func AddOrgUser(ctx context.Context, orgID, uid int64) error {
ou := &OrgUser{
UID: uid,
OrgID: orgID,
IsPublic: setting.Service.DefaultOrgMemberVisible,
IsPublic: setting.Config().Service.DefaultOrgMemberVisible.Value(ctx),
}
if err := db.Insert(ctx, ou); err != nil {

@ -8,9 +8,11 @@ import (
"testing"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/system"
"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"
)
@ -141,12 +143,19 @@ func TestAddOrgUser(t *testing.T) {
assert.Equal(t, expectedNumMembers, org.NumMembers)
}
setting.Service.DefaultOrgMemberVisible = false
system.SetSettings(t.Context(), map[string]string{
setting.Config().Service.DefaultOrgMemberVisible.DynKey(): "false",
})
config.GetDynGetter().InvalidateCache()
testSuccess(3, 5, false)
testSuccess(3, 5, false)
testSuccess(6, 2, false)
setting.Service.DefaultOrgMemberVisible = true
system.SetSettings(t.Context(), map[string]string{
setting.Config().Service.DefaultOrgMemberVisible.DynKey(): "true",
})
config.GetDynGetter().InvalidateCache()
testSuccess(6, 3, true)
unittest.CheckConsistencyFor(t, &user_model.User{}, &organization.Team{})

@ -11,29 +11,22 @@ import (
"code.gitea.io/gitea/modules/setting"
)
// ___________.__ ___________ __
// \__ ___/|__| _____ ___\__ ___/___________ ____ | | __ ___________
// | | | |/ \_/ __ \| | \_ __ \__ \ _/ ___\| |/ // __ \_ __ \
// | | | | Y Y \ ___/| | | | \// __ \\ \___| <\ ___/| | \/
// |____| |__|__|_| /\___ >____| |__| (____ /\___ >__|_ \\___ >__|
// \/ \/ \/ \/ \/ \/
// CanEnableTimetracker returns true when the server admin enabled time tracking
// This overrules IsTimetrackerEnabled
func (repo *Repository) CanEnableTimetracker() bool {
return setting.Service.EnableTimetracking
func (repo *Repository) CanEnableTimetracker(ctx context.Context) bool {
return setting.Config().Service.EnableTimeTracking.Value(ctx)
}
// IsTimetrackerEnabled returns whether or not the timetracker is enabled. It returns the default value from config if an error occurs.
func (repo *Repository) IsTimetrackerEnabled(ctx context.Context) bool {
if !setting.Service.EnableTimetracking {
if !setting.Config().Service.EnableTimeTracking.Value(ctx) {
return false
}
var u *RepoUnit
var err error
if u, err = repo.GetUnit(ctx, unit.TypeIssues); err != nil {
return setting.Service.DefaultEnableTimetracking
return setting.Config().Service.DefaultEnableTimeTracking.Value(ctx)
}
return u.IssuesConfig().EnableTimetracker
}
@ -43,7 +36,7 @@ func (repo *Repository) AllowOnlyContributorsToTrackTime(ctx context.Context) bo
var u *RepoUnit
var err error
if u, err = repo.GetUnit(ctx, unit.TypeIssues); err != nil {
return setting.Service.DefaultAllowOnlyContributorsToTrackTime
return setting.Config().Service.DefaultAllowOnlyContributorsToTrackTime.Value(ctx)
}
return u.IssuesConfig().AllowOnlyContributorsToTrackTime
}
@ -54,7 +47,7 @@ func (repo *Repository) IsDependenciesEnabled(ctx context.Context) bool {
var err error
if u, err = repo.GetUnit(ctx, unit.TypeIssues); err != nil {
log.Trace("IsDependenciesEnabled: %v", err)
return setting.Service.DefaultEnableDependencies
return setting.Config().Service.DefaultEnableDependencies.Value(ctx)
}
return u.IssuesConfig().EnableDependencies
}

@ -164,7 +164,7 @@ func GetRepoWatchers(ctx context.Context, repoID int64, opts db.ListOptions) ([]
// WatchIfAuto subscribes to repo if AutoWatchOnChanges is set
func WatchIfAuto(ctx context.Context, userID, repoID int64, isWrite bool) error {
if !isWrite || !setting.Service.AutoWatchOnChanges {
if !isWrite || !setting.Config().Service.AutoWatchOnChanges.Value(ctx) {
return nil
}
watch, err := GetWatch(ctx, userID, repoID)

@ -8,9 +8,11 @@ import (
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/system"
"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"
)
@ -71,7 +73,10 @@ func TestWatchIfAuto(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, watchers, repo.NumWatches)
setting.Service.AutoWatchOnChanges = false
assert.NoError(t, system.SetSettings(t.Context(), map[string]string{
setting.Config().Service.AutoWatchOnChanges.DynKey(): "false",
}))
config.GetDynGetter().InvalidateCache()
prevCount := repo.NumWatches
@ -87,7 +92,10 @@ func TestWatchIfAuto(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, watchers, prevCount)
setting.Service.AutoWatchOnChanges = true
assert.NoError(t, system.SetSettings(t.Context(), map[string]string{
setting.Config().Service.AutoWatchOnChanges.DynKey(): "true",
}))
config.GetDynGetter().InvalidateCache()
// Must not add watch
assert.NoError(t, repo_model.WatchIfAuto(t.Context(), 8, 1, true))

@ -658,13 +658,13 @@ func createUser(ctx context.Context, u *User, meta *Meta, createdByAdmin bool, o
}
// set system defaults
u.KeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate
u.KeepEmailPrivate = setting.Config().Service.DefaultKeepEmailPrivate.Value(ctx)
u.Visibility = setting.Service.DefaultUserVisibilityMode
u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation
u.AllowCreateOrganization = setting.Config().Service.DefaultAllowCreateOrganization.Value(ctx) && !setting.Admin.DisableRegularOrgCreation
u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification
u.MaxRepoCreation = -1
u.Theme = setting.UI.DefaultTheme
u.IsRestricted = setting.Service.DefaultUserIsRestricted
u.IsRestricted = setting.Config().Service.DefaultUserIsRestricted.Value(ctx)
u.IsActive = !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm)
// Ensure consistency of the dates.

@ -83,7 +83,7 @@ loop:
}
then = now
if setting.Service.EnableTimetracking {
if setting.Config().Service.EnableTimeTracking.Value(ctx) {
usersStopwatches, err := issues_model.GetUIDsAndStopwatch(ctx)
if err != nil {
log.Error("Unable to get GetUIDsAndStopwatch: %v", err)

@ -52,9 +52,25 @@ type RepositoryStruct struct {
GitGuideRemoteName *config.Value[string]
}
type ServiceStruct struct {
DefaultKeepEmailPrivate *config.Value[bool]
DefaultAllowCreateOrganization *config.Value[bool]
DefaultUserIsRestricted *config.Value[bool]
EnableTimeTracking *config.Value[bool]
DefaultEnableTimeTracking *config.Value[bool]
DefaultEnableDependencies *config.Value[bool]
AllowCrossRepositoryDependencies *config.Value[bool]
DefaultAllowOnlyContributorsToTrackTime *config.Value[bool]
EnableUserHeatmap *config.Value[bool]
AutoWatchNewRepos *config.Value[bool]
AutoWatchOnChanges *config.Value[bool]
DefaultOrgMemberVisible *config.Value[bool]
}
type ConfigStruct struct {
Picture *PictureStruct
Repository *RepositoryStruct
Service *ServiceStruct
}
var (
@ -73,6 +89,20 @@ func initDefaultConfig() {
OpenWithEditorApps: config.ValueJSON[OpenWithEditorAppsType]("repository.open-with.editor-apps"),
GitGuideRemoteName: config.ValueJSON[string]("repository.git-guide-remote-name").WithDefault("origin"),
},
Service: &ServiceStruct{
DefaultKeepEmailPrivate: config.ValueJSON[bool]("service.default_keep_email_private").WithFileConfig(config.CfgSecKey{Sec: "service", Key: "DEFAULT_KEEP_EMAIL_PRIVATE"}).WithDefault(false),
DefaultAllowCreateOrganization: config.ValueJSON[bool]("service.default_allow_create_organization").WithFileConfig(config.CfgSecKey{Sec: "service", Key: "DEFAULT_ALLOW_CREATE_ORGANIZATION"}).WithDefault(true),
DefaultUserIsRestricted: config.ValueJSON[bool]("service.default_user_is_restricted").WithFileConfig(config.CfgSecKey{Sec: "service", Key: "DEFAULT_USER_IS_RESTRICTED"}).WithDefault(false),
EnableTimeTracking: config.ValueJSON[bool]("service.enable_time_tracking").WithFileConfig(config.CfgSecKey{Sec: "service", Key: "ENABLE_TIMETRACKING"}).WithDefault(true),
DefaultEnableTimeTracking: config.ValueJSON[bool]("service.default_enable_time_tracking").WithFileConfig(config.CfgSecKey{Sec: "service", Key: "DEFAULT_ENABLE_TIMETRACKING"}).WithDefault(true),
DefaultEnableDependencies: config.ValueJSON[bool]("service.default_enable_dependencies").WithFileConfig(config.CfgSecKey{Sec: "service", Key: "DEFAULT_ENABLE_DEPENDENCIES"}).WithDefault(true),
AllowCrossRepositoryDependencies: config.ValueJSON[bool]("service.allow_cross_repository_dependencies").WithFileConfig(config.CfgSecKey{Sec: "service", Key: "ALLOW_CROSS_REPOSITORY_DEPENDENCIES"}).WithDefault(true),
DefaultAllowOnlyContributorsToTrackTime: config.ValueJSON[bool]("service.default_allow_only_contributors_to_track_time").WithFileConfig(config.CfgSecKey{Sec: "service", Key: "DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME"}).WithDefault(true),
EnableUserHeatmap: config.ValueJSON[bool]("service.enable_user_heatmap").WithFileConfig(config.CfgSecKey{Sec: "service", Key: "ENABLE_USER_HEATMAP"}).WithDefault(true),
AutoWatchNewRepos: config.ValueJSON[bool]("service.auto_watch_new_repos").WithFileConfig(config.CfgSecKey{Sec: "service", Key: "AUTO_WATCH_NEW_REPOS"}).WithDefault(true),
AutoWatchOnChanges: config.ValueJSON[bool]("service.auto_watch_on_changes").WithFileConfig(config.CfgSecKey{Sec: "service", Key: "AUTO_WATCH_ON_CHANGES"}).WithDefault(false),
DefaultOrgMemberVisible: config.ValueJSON[bool]("service.default_org_member_visible").WithFileConfig(config.CfgSecKey{Sec: "service", Key: "DEFAULT_ORG_MEMBER_VISIBLE"}).WithDefault(false),
},
}
}

@ -68,20 +68,8 @@ var Service = struct {
McaptchaSecret string
McaptchaSitekey string
McaptchaURL string
DefaultKeepEmailPrivate bool
DefaultAllowCreateOrganization bool
DefaultUserIsRestricted bool
EnableTimetracking bool
DefaultEnableTimetracking bool
DefaultEnableDependencies bool
AllowCrossRepositoryDependencies bool
DefaultAllowOnlyContributorsToTrackTime bool
NoReplyAddress string
UserLocationMapURL string
EnableUserHeatmap bool
AutoWatchNewRepos bool
AutoWatchOnChanges bool
DefaultOrgMemberVisible bool
UserDeleteWithCommentsMaxTime time.Duration
ValidSiteURLSchemes []string
@ -202,21 +190,9 @@ func loadServiceFrom(rootCfg ConfigProvider) {
Service.McaptchaURL = sec.Key("MCAPTCHA_URL").MustString("https://demo.mcaptcha.org/")
Service.McaptchaSecret = sec.Key("MCAPTCHA_SECRET").MustString("")
Service.McaptchaSitekey = sec.Key("MCAPTCHA_SITEKEY").MustString("")
Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool()
Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true)
Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false)
Service.EnableTimetracking = sec.Key("ENABLE_TIMETRACKING").MustBool(true)
if Service.EnableTimetracking {
Service.DefaultEnableTimetracking = sec.Key("DEFAULT_ENABLE_TIMETRACKING").MustBool(true)
}
Service.DefaultEnableDependencies = sec.Key("DEFAULT_ENABLE_DEPENDENCIES").MustBool(true)
Service.AllowCrossRepositoryDependencies = sec.Key("ALLOW_CROSS_REPOSITORY_DEPENDENCIES").MustBool(true)
Service.DefaultAllowOnlyContributorsToTrackTime = sec.Key("DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME").MustBool(true)
Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply." + Domain)
Service.UserLocationMapURL = sec.Key("USER_LOCATION_MAP_URL").String()
Service.EnableUserHeatmap = sec.Key("ENABLE_USER_HEATMAP").MustBool(true)
Service.AutoWatchNewRepos = sec.Key("AUTO_WATCH_NEW_REPOS").MustBool(true)
Service.AutoWatchOnChanges = sec.Key("AUTO_WATCH_ON_CHANGES").MustBool(false)
modes := sec.Key("ALLOWED_USER_VISIBILITY_MODES").Strings(",")
if len(modes) != 0 {
Service.AllowedUserVisibilityModes = []string{}
@ -246,7 +222,6 @@ func loadServiceFrom(rootCfg ConfigProvider) {
Service.DefaultUserVisibilityMode = structs.VisibilityModes[Service.DefaultUserVisibility]
Service.DefaultOrgVisibility = sec.Key("DEFAULT_ORG_VISIBILITY").In("public", structs.ExtractKeysFromMapString(structs.VisibilityModes))
Service.DefaultOrgVisibilityMode = structs.VisibilityModes[Service.DefaultOrgVisibility]
Service.DefaultOrgMemberVisible = sec.Key("DEFAULT_ORG_MEMBER_VISIBLE").MustBool()
Service.UserDeleteWithCommentsMaxTime = sec.Key("USER_DELETE_WITH_COMMENTS_MAX_TIME").MustDuration(0)
sec.Key("VALID_SITE_URL_SCHEMES").MustString("http,https")
Service.ValidSiteURLSchemes = sec.Key("VALID_SITE_URL_SCHEMES").Strings(",")

@ -5,6 +5,7 @@
package templates
import (
"context"
"fmt"
"html/template"
"net/url"
@ -124,8 +125,8 @@ func NewFuncMap() template.FuncMap {
"MetaKeywords": func() string {
return setting.UI.Meta.Keywords
},
"EnableTimetracking": func() bool {
return setting.Service.EnableTimetracking
"EnableTimeTracking": func(ctx context.Context) bool {
return setting.Config().Service.EnableTimeTracking.Value(ctx)
},
"DisableWebhooks": func() bool {
return setting.DisableWebhooks

@ -3383,7 +3383,29 @@ config.default_enable_timetracking = Enable Time Tracking by Default
config.default_allow_only_contributors_to_track_time = Let Only Contributors Track Time
config.no_reply_address = Hidden Email Domain
config.default_visibility_organization = Default visibility for new Organizations
config.default_enable_dependencies = Enable Issue Dependencies by Default
config.service_default_keep_email_private = Default value for KeepEmailPrivate
config.service_default_keep_email_private_desc = Each new user will get the value of this setting copied into their profile
config.service_default_allow_create_organization = Default value for AllowCreateOrganization
config.service_default_allow_create_organization_desc = Every new user will have rights set to create organizations depending on this setting
config.service_default_user_is_restricted = Default value for IsRestricted
config.service_default_user_is_restricted_desc = Every new user will have restricted permissions depending on this setting
config.service_enable_time_tracking = Enable Time Tracking
config.service_default_enable_time_tracking = Default value for EnableTimetracking
config.service_default_enable_time_tracking_desc = Repositories will use timetracking by default depending on this setting
config.service_default_enable_dependencies = Enable Issue Dependencies by Default
config.service_default_enable_dependencies_desc = Repositories will use dependencies by default depending on this setting
config.service_allow_cross_repository_dependencies = Allow Cross Repository Issue Dependencies
config.service_allow_cross_repository_dependencies_desc = Dependencies can be added from any repository where the user is granted access or only from the current repository depending on this setting.
config.service_default_allow_only_contributors_to_track_time = Default value for AllowOnlyContributorsToTrackTime
config.service_default_allow_only_contributors_to_track_time_desc = Only users with write permissions can track time if this is true
config.service_enable_user_heatmap = Enable User Heatmap
config.service_enable_user_heatmap_desc = Show a heatmap of user activity on user profiles
config.service_auto_watch_new_repos = Enable Auto Watch for New Repositories
config.service_auto_watch_new_repos_desc = When adding a repo to a team or creating a new repo all team members will watch the repo automatically if enabled
config.service_auto_watch_on_changes = Enable Auto Watch for Repositories Push
config.service_auto_watch_on_changes_desc = Make the user watch a repository When they commit for the first time
config.service_default_org_member_visible = Default value for DefaultOrgMemberVisible
config.service_default_org_member_visible_desc = True will make the membership of the users visible when added to the organisation
config.webhook_config = Webhook Configuration
config.queue_length = Queue Length

@ -1010,7 +1010,7 @@ func Routes() *web.Router {
m.Group("/{username}", func() {
m.Get("", reqExploreSignIn(), user.GetInfo)
if setting.Service.EnableUserHeatmap {
if setting.Config().Service.EnableUserHeatmap.Value(gocontext.Background()) {
m.Get("/heatmap", user.GetUserHeatmapData)
}

@ -509,7 +509,7 @@ func getParamsIssue(ctx *context.APIContext) *issues_model.Issue {
func getFormIssue(ctx *context.APIContext, form *api.IssueMeta) *issues_model.Issue {
var repo *repo_model.Repository
if form.Owner != ctx.Repo.Repository.OwnerName || form.Name != ctx.Repo.Repository.Name {
if !setting.Service.AllowCrossRepositoryDependencies {
if !setting.Config().Service.AllowCrossRepositoryDependencies.Value(ctx) {
ctx.JSON(http.StatusBadRequest, "CrossRepositoryDependencies not enabled")
return nil
}

@ -62,7 +62,7 @@ func GetGeneralRepoSettings(ctx *context.APIContext) {
HTTPGitDisabled: setting.Repository.DisableHTTPGit,
MigrationsDisabled: setting.Repository.DisableMigrations,
StarsDisabled: setting.Repository.DisableStars,
TimeTrackingDisabled: !setting.Service.EnableTimetracking,
TimeTrackingDisabled: !setting.Config().Service.EnableTimeTracking.Value(ctx),
LFSDisabled: !setting.LFS.StartServer,
})
}

@ -145,9 +145,9 @@ func Install(ctx *context.Context) {
form.AllowOnlyExternalRegistration = setting.Service.AllowOnlyExternalRegistration
form.EnableCaptcha = setting.Service.EnableCaptcha
form.RequireSignInView = setting.Service.RequireSignInViewStrict
form.DefaultKeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate
form.DefaultAllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization
form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking
form.DefaultKeepEmailPrivate = setting.Config().Service.DefaultKeepEmailPrivate.Value(ctx)
form.DefaultAllowCreateOrganization = setting.Config().Service.DefaultAllowCreateOrganization.Value(ctx)
form.DefaultEnableTimetracking = setting.Config().Service.DefaultEnableTimeTracking.Value(ctx)
form.NoReplyAddress = setting.Service.NoReplyAddress
form.PasswordAlgorithm = hash.ConfigHashAlgorithm(setting.PasswordHashAlgo)

@ -233,6 +233,20 @@ func ChangeConfig(ctx *context.Context) {
marshallers := map[string]func(string) ([]byte, error){
cfg.Picture.DisableGravatar.DynKey(): marshalBool,
cfg.Picture.EnableFederatedAvatar.DynKey(): marshalBool,
cfg.Service.DefaultKeepEmailPrivate.DynKey(): marshalBool,
cfg.Service.DefaultAllowCreateOrganization.DynKey(): marshalBool,
cfg.Service.DefaultUserIsRestricted.DynKey(): marshalBool,
cfg.Service.EnableTimeTracking.DynKey(): marshalBool,
cfg.Service.DefaultEnableTimeTracking.DynKey(): marshalBool,
cfg.Service.DefaultEnableDependencies.DynKey(): marshalBool,
cfg.Service.AllowCrossRepositoryDependencies.DynKey(): marshalBool,
cfg.Service.DefaultAllowOnlyContributorsToTrackTime.DynKey(): marshalBool,
cfg.Service.EnableUserHeatmap.DynKey(): marshalBool,
cfg.Service.AutoWatchNewRepos.DynKey(): marshalBool,
cfg.Service.AutoWatchOnChanges.DynKey(): marshalBool,
cfg.Service.DefaultOrgMemberVisible.DynKey(): marshalBool,
cfg.Repository.OpenWithEditorApps.DynKey(): marshalOpenWithApps,
cfg.Repository.GitGuideRemoteName.DynKey(): marshalString(cfg.Repository.GitGuideRemoteName.DefaultValue()),
}

@ -191,7 +191,7 @@ func SignInOAuthCallback(ctx *context.Context) {
isAdmin, isRestricted := getUserAdminAndRestrictedFromGroupClaims(source, &gothUser)
u.IsAdmin = isAdmin.ValueOrDefault(user_service.UpdateOptionField[bool]{FieldValue: false}).FieldValue
u.IsRestricted = isRestricted.ValueOrDefault(setting.Service.DefaultUserIsRestricted)
u.IsRestricted = isRestricted.ValueOrDefault(setting.Config().Service.DefaultUserIsRestricted.Value(ctx))
linkAccountData := &LinkAccountData{authSource.ID, gothUser}
if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingDisabled {

@ -46,7 +46,7 @@ func AddDependency(ctx *context.Context) {
// Check if both issues are in the same repo if cross repository dependencies is not enabled
if issue.RepoID != dep.RepoID {
if !setting.Service.AllowCrossRepositoryDependencies {
if !setting.Config().Service.AllowCrossRepositoryDependencies.Value(ctx) {
ctx.Flash.Error(ctx.Tr("repo.issues.dependency.add_error_dep_not_same_repo"))
return
}

@ -464,7 +464,7 @@ func prepareIssueViewSidebarDependency(ctx *context.Context, issue *issues_model
ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx, ctx.Doer, issue.IsPull)
// check if dependencies can be created across repositories
ctx.Data["AllowCrossRepositoryDependencies"] = setting.Service.AllowCrossRepositoryDependencies
ctx.Data["AllowCrossRepositoryDependencies"] = setting.Config().Service.AllowCrossRepositoryDependencies.Value(ctx)
// Get Dependencies
blockedBy, err := issue.BlockedByDependencies(ctx, db.ListOptions{})

@ -109,7 +109,7 @@ func Dashboard(ctx *context.Context) {
"uid": uid,
}
if setting.Service.EnableUserHeatmap {
if setting.Config().Service.EnableUserHeatmap.Value(ctx) {
data, err := activities_model.GetUserHeatmapDataByUserTeam(ctx, ctxUser, ctx.Org.Team, ctx.Doer)
if err != nil {
ctx.ServerError("GetUserHeatmapDataByUserTeam", err)

@ -162,7 +162,7 @@ func prepareUserProfileTabData(ctx *context.Context, profileDbRepo *repo_model.R
total = int(numFollowing)
case "activity":
// prepare heatmap data
if setting.Service.EnableUserHeatmap {
if setting.Config().Service.EnableUserHeatmap.Value(ctx) {
data, err := activities_model.GetUserHeatmapDataByUser(ctx, ctx.ContextUser, ctx.Doer)
if err != nil {
ctx.ServerError("GetUserHeatmapDataByUser", err)

@ -251,7 +251,7 @@ func AddTeamMember(ctx context.Context, team *organization.Team, user *user_mode
// this behaviour may spend much time so run it in a goroutine
// FIXME: Update watch repos batchly
if setting.Service.AutoWatchNewRepos {
if setting.Config().Service.AutoWatchNewRepos.Value(ctx) {
// Get team and its repositories.
repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
TeamID: team.ID,

@ -376,9 +376,9 @@ func createRepositoryInDB(ctx context.Context, doer, u *user_model.User, repo *r
RepoID: repo.ID,
Type: tp,
Config: &repo_model.IssuesConfig{
EnableTimetracker: setting.Service.DefaultEnableTimetracking,
AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime,
EnableDependencies: setting.Service.DefaultEnableDependencies,
EnableTimetracker: setting.Config().Service.DefaultEnableTimeTracking.Value(ctx),
AllowOnlyContributorsToTrackTime: setting.Config().Service.DefaultAllowOnlyContributorsToTrackTime.Value(ctx),
EnableDependencies: setting.Config().Service.DefaultEnableDependencies.Value(ctx),
},
})
case unit.TypePullRequests:
@ -447,7 +447,7 @@ func createRepositoryInDB(ctx context.Context, doer, u *user_model.User, repo *r
return fmt.Errorf("RecalculateAccesses: %w", err)
}
if setting.Service.AutoWatchNewRepos {
if setting.Config().Service.AutoWatchNewRepos.Value(ctx) {
if err = repo_model.WatchRepo(ctx, doer, repo, true); err != nil {
return fmt.Errorf("WatchRepo: %w", err)
}

@ -45,7 +45,7 @@ func addRepositoryToTeam(ctx context.Context, t *organization.Team, repo *repo_m
}
// Make all team members watch this repo if enabled in global settings
if setting.Service.AutoWatchNewRepos {
if setting.Config().Service.AutoWatchNewRepos.Value(ctx) {
if err = t.LoadMembers(ctx); err != nil {
return fmt.Errorf("getMembers: %w", err)
}

@ -13,9 +13,11 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/system"
"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"
"code.gitea.io/gitea/modules/timeutil"
org_service "code.gitea.io/gitea/services/org"
@ -170,7 +172,10 @@ func TestCreateUser_Issue5882(t *testing.T) {
{&user_model.User{Name: "GiteaBot2", Email: "GiteaBot2@gitea.io", Passwd: passwd, MustChangePassword: false}, true},
}
setting.Service.DefaultAllowCreateOrganization = true
system.SetSettings(t.Context(), map[string]string{
setting.Config().Service.DefaultAllowCreateOrganization.DynKey(): "true",
})
config.GetDynGetter().InvalidateCache()
for _, v := range tt {
setting.Admin.DisableRegularOrgCreation = v.disableOrgCreation

@ -4,4 +4,6 @@
{{template "admin/config_settings/repository" .}}
{{template "admin/config_settings/service" .}}
{{template "admin/layout_footer" .}}

@ -0,0 +1,90 @@
<h4 class="ui top attached header">
{{ctx.Locale.Tr "admin.config.service_config"}}
</h4>
<div class="ui attached table segment">
<dl class="admin-dl-horizontal">
<dt>{{ctx.Locale.Tr "admin.config.service_default_keep_email_private"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.service_default_keep_email_private_desc"}}">
<input type="checkbox" data-config-dyn-key="service.default_keep_email_private" {{if .SystemConfig.Service.DefaultKeepEmailPrivate.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
<dt>{{ctx.Locale.Tr "admin.config.service_default_allow_create_organization"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.service_default_allow_create_organization_desc"}}">
<input type="checkbox" data-config-dyn-key="service.default_allow_create_organization" {{if .SystemConfig.Service.DefaultAllowCreateOrganization.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
<dt>{{ctx.Locale.Tr "admin.config.service_default_user_is_restricted"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.service_default_user_is_restricted_desc"}}">
<input type="checkbox" data-config-dyn-key="service.default_user_is_restricted" {{if .SystemConfig.Service.DefaultUserIsRestricted.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
<dt>{{ctx.Locale.Tr "admin.config.service_enable_time_tracking"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.service_enable_time_tracking_desc"}}">
<input type="checkbox" data-config-dyn-key="service.enable_time_tracking" {{if .SystemConfig.Service.EnableTimeTracking.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
<dt>{{ctx.Locale.Tr "admin.config.service_default_enable_time_tracking"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.service_default_enable_time_tracking_desc"}}">
<input type="checkbox" data-config-dyn-key="service.default_enable_time_tracking" {{if .SystemConfig.Service.DefaultEnableTimeTracking.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
<dt>{{ctx.Locale.Tr "admin.config.service_default_enable_dependencies"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.service_default_enable_dependencies_desc"}}">
<input type="checkbox" data-config-dyn-key="service.default_enable_dependencies" {{if .SystemConfig.Service.DefaultEnableDependencies.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
<dt>{{ctx.Locale.Tr "admin.config.service_allow_cross_repository_dependencies"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.service_allow_cross_repository_dependencies_desc"}}">
<input type="checkbox" data-config-dyn-key="service.allow_cross_repository_dependencies" {{if .SystemConfig.Service.AllowCrossRepositoryDependencies.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
<dt>{{ctx.Locale.Tr "admin.config.service_default_allow_only_contributors_to_track_time"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.service_default_allow_only_contributors_to_track_time_desc"}}">
<input type="checkbox" data-config-dyn-key="service.default_allow_only_contributors_to_track_time" {{if .SystemConfig.Service.DefaultAllowOnlyContributorsToTrackTime.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
<dt>{{ctx.Locale.Tr "admin.config.service_enable_user_heatmap"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.service_enable_user_heatmap_desc"}}">
<input type="checkbox" data-config-dyn-key="service.enable_user_heatmap" {{if .SystemConfig.Service.EnableUserHeatmap.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
<dt>{{ctx.Locale.Tr "admin.config.service_auto_watch_new_repos"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.service_auto_watch_new_repos_desc"}}">
<input type="checkbox" data-config-dyn-key="service.auto_watch_new_repos" {{if .SystemConfig.Service.AutoWatchNewRepos.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
<dt>{{ctx.Locale.Tr "admin.config.service_auto_watch_on_changes"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.service_auto_watch_on_changes_desc"}}">
<input type="checkbox" data-config-dyn-key="service.auto_watch_on_changes" {{if .SystemConfig.Service.AutoWatchOnChanges.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
<dt>{{ctx.Locale.Tr "admin.config.service_default_org_member_visible"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.service_default_org_member_visible_desc"}}">
<input type="checkbox" data-config-dyn-key="service.default_org_member_visible" {{if .SystemConfig.Service.DefaultOrgMemberVisible.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
</dl>
</div>

@ -16,7 +16,7 @@ If you introduce mistakes in it, Gitea JavaScript code wouldn't run correctly.
csrfToken: '{{.CsrfToken}}',
pageData: {{.PageData}},
notificationSettings: {{NotificationSettings}}, {{/*a map provided by NewFuncMap in helper.go*/}}
enableTimeTracking: {{EnableTimetracking}},
enableTimeTracking: {{EnableTimeTracking ctx}},
{{if or .Participants .Assignees .MentionableTeams}}
mentionValues: Array.from(new Map([
{{- range .Participants -}}

@ -44,7 +44,7 @@
{{end}}
</a>
{{end}}
{{if and EnableTimetracking .IsOrganizationOwner}}
{{if and (EnableTimeTracking ctx) .IsOrganizationOwner}}
<a class="{{if $.PageIsOrgTimes}}active{{end}} item" href="{{$.OrgLink}}/worktime">
{{svg "octicon-clock"}} {{ctx.Locale.Tr "org.worktime"}}
</a>

@ -8,14 +8,22 @@ import (
"testing"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/setting/config"
"github.com/stretchr/testify/assert"
)
func TestRepoWatch(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// Test round-trip auto-watch
setting.Service.AutoWatchOnChanges = true
assert.NoError(t, system.SetSettings(t.Context(), map[string]string{
setting.Config().Service.AutoWatchOnChanges.DynKey(): "true",
}))
config.GetDynGetter().InvalidateCache()
session := loginUser(t, "user2")
unittest.AssertNotExistsBean(t, &repo_model.Watch{UserID: 2, RepoID: 3})
testEditFile(t, session, "org3", "repo3", "master", "README.md", "Hello, World (Edited for watch)\n")

@ -10,9 +10,11 @@ import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/system"
"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"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/tests"
@ -40,7 +42,10 @@ func TestSignup(t *testing.T) {
func TestSignupAsRestricted(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer test.MockVariableValue(&setting.Service.EnableCaptcha, false)()
defer test.MockVariableValue(&setting.Service.DefaultUserIsRestricted, true)()
system.SetSettings(t.Context(), map[string]string{
setting.Config().Service.DefaultUserIsRestricted.DynKey(): "true",
})
config.GetDynGetter().InvalidateCache()
req := NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
"user_name": "restrictedUser",