mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-04 04:14:01 +01:00 
			
		
		
		
	Merge 43320701efb57c692d2f64761065adccdf06f540 into fe2599715730c385da38650903f3bc8400a4c919
This commit is contained in:
		
						commit
						320f4e8277
					
				@ -216,8 +216,8 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final
 | 
			
		||||
		return urlStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	disableGravatar := setting.Config().Picture.DisableGravatar.Value(ctx)
 | 
			
		||||
	if !disableGravatar {
 | 
			
		||||
	enableGravatar := setting.Config().Picture.EnableGravatar.Value(ctx)
 | 
			
		||||
	if enableGravatar {
 | 
			
		||||
		// copy GravatarSourceURL, because we will modify its Path.
 | 
			
		||||
		avatarURLCopy := *avatarSetting.gravatarSourceURL
 | 
			
		||||
		avatarURLCopy.Path = path.Join(avatarURLCopy.Path, HashEmail(email))
 | 
			
		||||
 | 
			
		||||
@ -19,12 +19,14 @@ const gravatarSource = "https://secure.gravatar.com/avatar/"
 | 
			
		||||
func disableGravatar(t *testing.T) {
 | 
			
		||||
	err := system_model.SetSettings(t.Context(), map[string]string{setting.Config().Picture.EnableFederatedAvatar.DynKey(): "false"})
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	err = system_model.SetSettings(t.Context(), map[string]string{setting.Config().Picture.DisableGravatar.DynKey(): "true"})
 | 
			
		||||
	// EnableGravatar.SelectFrom == picture.disable_gravatar for backwards compatibility; .Value will flip correctly but the true value here is counterintuitive
 | 
			
		||||
	err = system_model.SetSettings(t.Context(), map[string]string{setting.Config().Picture.EnableGravatar.SelectFromKey(): "true"})
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func enableGravatar(t *testing.T) {
 | 
			
		||||
	err := system_model.SetSettings(t.Context(), map[string]string{setting.Config().Picture.DisableGravatar.DynKey(): "false"})
 | 
			
		||||
	// EnableGravatar.SelectFrom == picture.disable_gravatar for backwards compatibility; .Value will flip correctly but the false value here is counterintuitive
 | 
			
		||||
	err := system_model.SetSettings(t.Context(), map[string]string{setting.Config().Picture.EnableGravatar.SelectFromKey(): "false"})
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	setting.GravatarSource = gravatarSource
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -150,3 +150,34 @@ func (d *dbConfigCachedGetter) InvalidateCache() {
 | 
			
		||||
func NewDatabaseDynKeyGetter() config.DynKeyGetter {
 | 
			
		||||
	return &dbConfigCachedGetter{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type dbConfigSetter struct {
 | 
			
		||||
	mu sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ config.DynKeySetter = (*dbConfigSetter)(nil)
 | 
			
		||||
 | 
			
		||||
func (d *dbConfigSetter) SetValue(ctx context.Context, dynKey, value string) error {
 | 
			
		||||
	d.mu.RLock()
 | 
			
		||||
	defer d.mu.RUnlock()
 | 
			
		||||
	_ = GetRevision(ctx) // prepare the "revision" key ahead
 | 
			
		||||
	return db.WithTx(ctx, func(ctx context.Context) error {
 | 
			
		||||
		e := db.GetEngine(ctx)
 | 
			
		||||
		res, err := e.Exec("UPDATE system_setting SET version=version+1, setting_value=? WHERE setting_key=?", value, dynKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		rows, _ := res.RowsAffected()
 | 
			
		||||
		if rows == 0 { // if no existing row, insert a new row
 | 
			
		||||
			if _, err = e.Insert(&Setting{SettingKey: dynKey, SettingValue: value}); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewDatabaseDynKeySetter() config.DynKeySetter {
 | 
			
		||||
	return &dbConfigSetter{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -69,12 +69,12 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
 | 
			
		||||
	useLocalAvatar := false
 | 
			
		||||
	autoGenerateAvatar := false
 | 
			
		||||
 | 
			
		||||
	disableGravatar := setting.Config().Picture.DisableGravatar.Value(ctx)
 | 
			
		||||
	enableGravatar := setting.Config().Picture.EnableGravatar.Value(ctx)
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case u.UseCustomAvatar:
 | 
			
		||||
	case u.UseCustomAvatar, enableGravatar:
 | 
			
		||||
		useLocalAvatar = true
 | 
			
		||||
	case disableGravatar, setting.OfflineMode:
 | 
			
		||||
	case setting.OfflineMode:
 | 
			
		||||
		useLocalAvatar = true
 | 
			
		||||
		autoGenerateAvatar = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type PictureStruct struct {
 | 
			
		||||
	DisableGravatar       *config.Value[bool]
 | 
			
		||||
	EnableGravatar        *config.Value[bool]
 | 
			
		||||
	EnableFederatedAvatar *config.Value[bool]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -66,7 +66,7 @@ func initDefaultConfig() {
 | 
			
		||||
	config.SetCfgSecKeyGetter(&cfgSecKeyGetter{})
 | 
			
		||||
	defaultConfig = &ConfigStruct{
 | 
			
		||||
		Picture: &PictureStruct{
 | 
			
		||||
			DisableGravatar:       config.ValueJSON[bool]("picture.disable_gravatar").WithFileConfig(config.CfgSecKey{Sec: "picture", Key: "DISABLE_GRAVATAR"}),
 | 
			
		||||
			EnableGravatar:        config.ValueJSON[bool]("picture.enable_gravatar").SelectFrom("picture.disable_gravatar").WithFileConfig(config.CfgSecKey{Sec: "picture", Key: "DISABLE_GRAVATAR"}).Invert(),
 | 
			
		||||
			EnableFederatedAvatar: config.ValueJSON[bool]("picture.enable_federated_avatar").WithFileConfig(config.CfgSecKey{Sec: "picture", Key: "ENABLE_FEDERATED_AVATAR"}),
 | 
			
		||||
		},
 | 
			
		||||
		Repository: &RepositoryStruct{
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										29
									
								
								modules/setting/config/setter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								modules/setting/config/setter.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
// Copyright 2023 The Gitea Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var setterMu sync.RWMutex
 | 
			
		||||
 | 
			
		||||
type DynKeySetter interface {
 | 
			
		||||
	SetValue(ctx context.Context, dynKey, value string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var dynKeySetterInternal DynKeySetter
 | 
			
		||||
 | 
			
		||||
func SetDynSetter(p DynKeySetter) {
 | 
			
		||||
	setterMu.Lock()
 | 
			
		||||
	dynKeySetterInternal = p
 | 
			
		||||
	setterMu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetDynSetter() DynKeySetter {
 | 
			
		||||
	getterMu.RLock()
 | 
			
		||||
	defer getterMu.RUnlock()
 | 
			
		||||
	return dynKeySetterInternal
 | 
			
		||||
}
 | 
			
		||||
@ -19,11 +19,12 @@ type CfgSecKey struct {
 | 
			
		||||
type Value[T any] struct {
 | 
			
		||||
	mu sync.RWMutex
 | 
			
		||||
 | 
			
		||||
	cfgSecKey CfgSecKey
 | 
			
		||||
	dynKey    string
 | 
			
		||||
	cfgSecKey             CfgSecKey
 | 
			
		||||
	dynKey, selectFromKey string
 | 
			
		||||
 | 
			
		||||
	def, value T
 | 
			
		||||
	revision   int
 | 
			
		||||
	def, value  T
 | 
			
		||||
	revision    int
 | 
			
		||||
	flipBoolean bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (value *Value[T]) parse(key, valStr string) (v T) {
 | 
			
		||||
@ -33,9 +34,41 @@ func (value *Value[T]) parse(key, valStr string) (v T) {
 | 
			
		||||
			log.Error("Unable to unmarshal json config for key %q, err: %v", key, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return value.invert(v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (value *Value[T]) invertBoolStr(val string) (inverted string) {
 | 
			
		||||
	if val == "true" {
 | 
			
		||||
		return "false"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "true"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (value *Value[T]) invert(val T) (v T) {
 | 
			
		||||
	v = val
 | 
			
		||||
	if value.flipBoolean {
 | 
			
		||||
		// if value is of type bool
 | 
			
		||||
		if _, ok := any(val).(bool); ok {
 | 
			
		||||
			// invert the boolean value upon retrieval
 | 
			
		||||
			v = any(!any(val).(bool)).(T)
 | 
			
		||||
		} else {
 | 
			
		||||
			log.Warn("Ignoring attempt to invert key '%q' for non boolean type", value.selectFromKey)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return v
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (value *Value[T]) getKey() string {
 | 
			
		||||
	if value.selectFromKey != "" {
 | 
			
		||||
		return value.selectFromKey
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return value.dynKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (value *Value[T]) Value(ctx context.Context) (v T) {
 | 
			
		||||
	dg := GetDynGetter()
 | 
			
		||||
	if dg == nil {
 | 
			
		||||
@ -57,7 +90,7 @@ func (value *Value[T]) Value(ctx context.Context) (v T) {
 | 
			
		||||
 | 
			
		||||
	// try to parse the config and cache it
 | 
			
		||||
	var valStr *string
 | 
			
		||||
	if dynVal, has := dg.GetValue(ctx, value.dynKey); has {
 | 
			
		||||
	if dynVal, has := dg.GetValue(ctx, value.getKey()); has {
 | 
			
		||||
		valStr = &dynVal
 | 
			
		||||
	} else if cfgVal, has := GetCfgSecKeyGetter().GetValue(value.cfgSecKey.Sec, value.cfgSecKey.Key); has {
 | 
			
		||||
		valStr = &cfgVal
 | 
			
		||||
@ -79,6 +112,10 @@ func (value *Value[T]) DynKey() string {
 | 
			
		||||
	return value.dynKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (value *Value[T]) SelectFromKey() string {
 | 
			
		||||
	return value.selectFromKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (value *Value[T]) WithDefault(def T) *Value[T] {
 | 
			
		||||
	value.def = def
 | 
			
		||||
	return value
 | 
			
		||||
@ -93,6 +130,32 @@ func (value *Value[T]) WithFileConfig(cfgSecKey CfgSecKey) *Value[T] {
 | 
			
		||||
	return value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (value *Value[bool]) Invert() *Value[bool] {
 | 
			
		||||
	value.flipBoolean = true
 | 
			
		||||
	return value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (value *Value[any]) SelectFrom(sectionName string) *Value[any] {
 | 
			
		||||
	value.selectFromKey = sectionName
 | 
			
		||||
	return value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (value *Value[any]) SetValue(val string) error {
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	ds := GetDynSetter()
 | 
			
		||||
	if ds == nil {
 | 
			
		||||
		// this is an edge case: the database is not initialized but the system setting is going to be used
 | 
			
		||||
		// it should panic to avoid inconsistent config values (from config / system setting) and fix the code
 | 
			
		||||
		panic("no config dyn value getter")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if value.flipBoolean {
 | 
			
		||||
		return ds.SetValue(ctx, value.getKey(), value.invertBoolStr(val))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ds.SetValue(ctx, value.getKey(), val)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ValueJSON[T any](dynKey string) *Value[T] {
 | 
			
		||||
	return &Value[T]{dynKey: dynKey}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										152
									
								
								modules/setting/config/value_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								modules/setting/config/value_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,152 @@
 | 
			
		||||
// Copyright 2023 The Gitea Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestValue_parse(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string // description of this test case
 | 
			
		||||
		// Named input parameters for target function.
 | 
			
		||||
		key    string
 | 
			
		||||
		valStr string
 | 
			
		||||
		want   bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:   "Parse Invert Retrieval",
 | 
			
		||||
			key:    "picture.disable_gravatar",
 | 
			
		||||
			valStr: "false",
 | 
			
		||||
			want:   true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			value := ValueJSON[bool]("picture.disable_gravatar").Invert()
 | 
			
		||||
			got := value.parse(tt.key, tt.valStr)
 | 
			
		||||
 | 
			
		||||
			if got != tt.want {
 | 
			
		||||
				t.Errorf("parse() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValue_getKey(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name       string // description of this test case
 | 
			
		||||
		valueClass *Value[bool]
 | 
			
		||||
		want       string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:       "Custom dynKey name",
 | 
			
		||||
			valueClass: ValueJSON[bool]("picture.enable_gravatar").SelectFrom("picture.disable_gravatar"),
 | 
			
		||||
			want:       "picture.disable_gravatar",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "Normal dynKey name",
 | 
			
		||||
			valueClass: ValueJSON[bool]("picture.disable_gravatar"),
 | 
			
		||||
			want:       "picture.disable_gravatar",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			got := tt.valueClass.getKey()
 | 
			
		||||
 | 
			
		||||
			if got != tt.want {
 | 
			
		||||
				t.Errorf("getKey() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValue_invert(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string // description of this test case
 | 
			
		||||
		// Named input parameters for target function.
 | 
			
		||||
		valueClass *Value[bool]
 | 
			
		||||
		want       bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:       "Invert typed true",
 | 
			
		||||
			valueClass: ValueJSON[bool]("picture.enable_gravatar").WithDefault(true).Invert(),
 | 
			
		||||
			want:       false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "Invert typed false",
 | 
			
		||||
			valueClass: ValueJSON[bool]("picture.enable_gravatar").WithDefault(false).Invert(),
 | 
			
		||||
			want:       true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "Invert typed Does not invert",
 | 
			
		||||
			valueClass: ValueJSON[bool]("picture.enable_gravatar").WithDefault(false),
 | 
			
		||||
			want:       false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			got := tt.valueClass.invert(tt.valueClass.def)
 | 
			
		||||
 | 
			
		||||
			if got != tt.want {
 | 
			
		||||
				t.Errorf("invert() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValue_invertBoolStr(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string // description of this test case
 | 
			
		||||
		// Named input parameters for target function.
 | 
			
		||||
		valueClass *Value[bool]
 | 
			
		||||
		val        string
 | 
			
		||||
		want       string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:       "Invert boolean string true",
 | 
			
		||||
			valueClass: ValueJSON[bool]("picture.enable_gravatar"),
 | 
			
		||||
			val:        "true",
 | 
			
		||||
			want:       "false",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "Invert boolean string false",
 | 
			
		||||
			valueClass: ValueJSON[bool]("picture.enable_gravatar"),
 | 
			
		||||
			val:        "false",
 | 
			
		||||
			want:       "true",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			got := tt.valueClass.invertBoolStr(tt.val)
 | 
			
		||||
			if got != tt.want {
 | 
			
		||||
				t.Errorf("invertBoolStr() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValue_SelectFromKey(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string // description of this test case
 | 
			
		||||
		// Named input parameters for target function.
 | 
			
		||||
		valueClass *Value[bool]
 | 
			
		||||
		want       string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:       "SelectFrom set and get",
 | 
			
		||||
			valueClass: ValueJSON[bool]("picture.enable_gravatar").SelectFrom("picture.disable_gravatar"),
 | 
			
		||||
			want:       "picture.disable_gravatar",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			got := tt.valueClass.SelectFromKey()
 | 
			
		||||
 | 
			
		||||
			if got != tt.want {
 | 
			
		||||
				t.Errorf("SelectFromKey() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -23,7 +23,7 @@ var (
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	GravatarSource        string
 | 
			
		||||
	DisableGravatar       bool // Depreciated: migrated to database
 | 
			
		||||
	EnableGravatar        bool // Depreciated: migrated to database
 | 
			
		||||
	EnableFederatedAvatar bool // Depreciated: migrated to database
 | 
			
		||||
 | 
			
		||||
	RepoAvatar = struct {
 | 
			
		||||
@ -65,9 +65,9 @@ func loadAvatarsFrom(rootCfg ConfigProvider) error {
 | 
			
		||||
		GravatarSource = source
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar())
 | 
			
		||||
	EnableGravatar = !sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar())
 | 
			
		||||
	deprecatedSettingDB(rootCfg, "", "DISABLE_GRAVATAR")
 | 
			
		||||
	EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar))
 | 
			
		||||
	EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(EnableGravatar))
 | 
			
		||||
	deprecatedSettingDB(rootCfg, "", "ENABLE_FEDERATED_AVATAR")
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
@ -77,14 +77,12 @@ func GetDefaultDisableGravatar() bool {
 | 
			
		||||
	return OfflineMode
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetDefaultEnableFederatedAvatar(disableGravatar bool) bool {
 | 
			
		||||
func GetDefaultEnableFederatedAvatar(enableGravatar bool) bool {
 | 
			
		||||
	v := !InstallLock
 | 
			
		||||
	if OfflineMode {
 | 
			
		||||
		v = false
 | 
			
		||||
	}
 | 
			
		||||
	if disableGravatar {
 | 
			
		||||
	if OfflineMode || !enableGravatar {
 | 
			
		||||
		v = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return v
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -316,8 +316,8 @@ mail_notify = Enable Email Notifications
 | 
			
		||||
server_service_title = Server and Third-Party Service Settings
 | 
			
		||||
offline_mode = Enable Local Mode
 | 
			
		||||
offline_mode_popup = Disable third-party content delivery networks and serve all resources locally.
 | 
			
		||||
disable_gravatar = Disable Gravatar
 | 
			
		||||
disable_gravatar_popup = Disable Gravatar and third-party avatar sources. A default avatar will be used unless a user locally uploads an avatar.
 | 
			
		||||
enable_gravatar = Enable Gravatar
 | 
			
		||||
enable_gravatar_popup = Enable Gravatar and third-party avatar sources. A default avatar will be used unless a user locally uploads an avatar.
 | 
			
		||||
federated_avatar_lookup = Enable Federated Avatars
 | 
			
		||||
federated_avatar_lookup_popup = Enable federated avatar lookup using Libravatar.
 | 
			
		||||
disable_registration = Disable Self-Registration
 | 
			
		||||
@ -3436,7 +3436,7 @@ config.cookie_life_time = Cookie Life Time
 | 
			
		||||
 | 
			
		||||
config.picture_config = Picture and Avatar Configuration
 | 
			
		||||
config.picture_service = Picture Service
 | 
			
		||||
config.disable_gravatar = Disable Gravatar
 | 
			
		||||
config.enable_gravatar = Enable 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.git_guide_remote_name = Repository remote name for git commands in the guide
 | 
			
		||||
 | 
			
		||||
@ -38,6 +38,7 @@ func InitDBEngine(ctx context.Context) (err error) {
 | 
			
		||||
		log.Info("Backing off for %d seconds", int64(setting.Database.DBConnectBackoff/time.Second))
 | 
			
		||||
		time.Sleep(setting.Database.DBConnectBackoff)
 | 
			
		||||
	}
 | 
			
		||||
	config.SetDynSetter(system_model.NewDatabaseDynKeySetter())
 | 
			
		||||
	config.SetDynGetter(system_model.NewDatabaseDynKeyGetter())
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -136,7 +136,7 @@ func Install(ctx *context.Context) {
 | 
			
		||||
 | 
			
		||||
	// Server and other services settings
 | 
			
		||||
	form.OfflineMode = setting.OfflineMode
 | 
			
		||||
	form.DisableGravatar = setting.DisableGravatar             // when installing, there is no database connection so that given a default value
 | 
			
		||||
	form.EnableGravatar = setting.EnableGravatar               // when installing, there is no database connection so that given a default value
 | 
			
		||||
	form.EnableFederatedAvatar = setting.EnableFederatedAvatar // when installing, there is no database connection so that given a default value
 | 
			
		||||
 | 
			
		||||
	form.EnableOpenIDSignIn = setting.Service.EnableOpenIDSignIn
 | 
			
		||||
@ -427,7 +427,8 @@ func SubmitInstall(ctx *context.Context) {
 | 
			
		||||
 | 
			
		||||
	cfg.Section("server").Key("OFFLINE_MODE").SetValue(strconv.FormatBool(form.OfflineMode))
 | 
			
		||||
	if err := system_model.SetSettings(ctx, map[string]string{
 | 
			
		||||
		setting.Config().Picture.DisableGravatar.DynKey():       strconv.FormatBool(form.DisableGravatar),
 | 
			
		||||
		// Form is submitted on install and should use the SelectFrom key for backwards compatibility; getting the value will properly invert the boolean
 | 
			
		||||
		setting.Config().Picture.EnableGravatar.SelectFromKey(): strconv.FormatBool(!form.EnableGravatar),
 | 
			
		||||
		setting.Config().Picture.EnableFederatedAvatar.DynKey(): strconv.FormatBool(form.EnableFederatedAvatar),
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form)
 | 
			
		||||
 | 
			
		||||
@ -198,6 +198,10 @@ func ConfigSettings(ctx *context.Context) {
 | 
			
		||||
func ChangeConfig(ctx *context.Context) {
 | 
			
		||||
	cfg := setting.Config()
 | 
			
		||||
 | 
			
		||||
	subValueSet := map[string]func(string) error{
 | 
			
		||||
		cfg.Picture.EnableGravatar.DynKey(): cfg.Picture.EnableGravatar.SetValue,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	marshalBool := func(v string) ([]byte, error) {
 | 
			
		||||
		b, _ := strconv.ParseBool(v)
 | 
			
		||||
		return json.Marshal(b)
 | 
			
		||||
@ -230,8 +234,9 @@ func ChangeConfig(ctx *context.Context) {
 | 
			
		||||
		}
 | 
			
		||||
		return json.Marshal(openWithEditorApps)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	marshallers := map[string]func(string) ([]byte, error){
 | 
			
		||||
		cfg.Picture.DisableGravatar.DynKey():       marshalBool,
 | 
			
		||||
		cfg.Picture.EnableGravatar.DynKey():        marshalBool,
 | 
			
		||||
		cfg.Picture.EnableFederatedAvatar.DynKey(): marshalBool,
 | 
			
		||||
		cfg.Repository.OpenWithEditorApps.DynKey(): marshalOpenWithApps,
 | 
			
		||||
		cfg.Repository.GitGuideRemoteName.DynKey(): marshalString(cfg.Repository.GitGuideRemoteName.DefaultValue()),
 | 
			
		||||
@ -260,7 +265,15 @@ loop:
 | 
			
		||||
			ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
 | 
			
		||||
			break loop
 | 
			
		||||
		}
 | 
			
		||||
		configSettings[key] = string(marshaledValue)
 | 
			
		||||
 | 
			
		||||
		if setter, ok := subValueSet[key]; ok {
 | 
			
		||||
			if err := setter(string(marshaledValue)); err != nil {
 | 
			
		||||
				ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
 | 
			
		||||
				break loop
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			configSettings[key] = string(marshaledValue)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
@ -316,7 +316,7 @@ func editUserCommon(ctx *context.Context) {
 | 
			
		||||
	ctx.Data["DisableGitHooks"] = setting.DisableGitHooks
 | 
			
		||||
	ctx.Data["DisableImportLocal"] = !setting.ImportLocalPaths
 | 
			
		||||
	ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
 | 
			
		||||
	ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx)
 | 
			
		||||
	ctx.Data["EnableGravatar"] = setting.Config().Picture.EnableGravatar.Value(ctx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EditUser show editing user page
 | 
			
		||||
 | 
			
		||||
@ -47,7 +47,7 @@ func Profile(ctx *context.Context) {
 | 
			
		||||
	ctx.Data["Title"] = ctx.Tr("settings.profile")
 | 
			
		||||
	ctx.Data["PageIsSettingsProfile"] = true
 | 
			
		||||
	ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
 | 
			
		||||
	ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx)
 | 
			
		||||
	ctx.Data["EnableGravatar"] = setting.Config().Picture.EnableGravatar.Value(ctx)
 | 
			
		||||
 | 
			
		||||
	ctx.Data["UserDisabledFeatures"] = user_model.DisabledFeaturesWithLoginType(ctx.Doer)
 | 
			
		||||
 | 
			
		||||
@ -59,7 +59,7 @@ func ProfilePost(ctx *context.Context) {
 | 
			
		||||
	ctx.Data["Title"] = ctx.Tr("settings")
 | 
			
		||||
	ctx.Data["PageIsSettingsProfile"] = true
 | 
			
		||||
	ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
 | 
			
		||||
	ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx)
 | 
			
		||||
	ctx.Data["EnableGravatar"] = setting.Config().Picture.EnableGravatar.Value(ctx)
 | 
			
		||||
	ctx.Data["UserDisabledFeatures"] = user_model.DisabledFeaturesWithLoginType(ctx.Doer)
 | 
			
		||||
 | 
			
		||||
	if ctx.HasError() {
 | 
			
		||||
 | 
			
		||||
@ -46,7 +46,7 @@ type InstallForm struct {
 | 
			
		||||
	MailNotify      bool
 | 
			
		||||
 | 
			
		||||
	OfflineMode                    bool
 | 
			
		||||
	DisableGravatar                bool
 | 
			
		||||
	EnableGravatar                 bool
 | 
			
		||||
	EnableFederatedAvatar          bool
 | 
			
		||||
	EnableOpenIDSignIn             bool
 | 
			
		||||
	EnableOpenIDSignUp             bool
 | 
			
		||||
 | 
			
		||||
@ -3,10 +3,10 @@
 | 
			
		||||
</h4>
 | 
			
		||||
<div class="ui attached table segment">
 | 
			
		||||
	<dl class="admin-dl-horizontal">
 | 
			
		||||
		<dt>{{ctx.Locale.Tr "admin.config.disable_gravatar"}}</dt>
 | 
			
		||||
		<dt>{{ctx.Locale.Tr "admin.config.enable_gravatar"}}</dt>
 | 
			
		||||
		<dd>
 | 
			
		||||
			<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.disable_gravatar"}}">
 | 
			
		||||
				<input type="checkbox" data-config-dyn-key="picture.disable_gravatar" {{if .SystemConfig.Picture.DisableGravatar.Value ctx}}checked{{end}}><label></label>
 | 
			
		||||
			<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.enable_gravatar"}}">
 | 
			
		||||
				<input type="checkbox" data-config-dyn-key="picture.enable_gravatar" {{if .SystemConfig.Picture.EnableGravatar.Value ctx}}checked{{end}}><label></label>
 | 
			
		||||
			</div>
 | 
			
		||||
		</dd>
 | 
			
		||||
		<div class="divider"></div>
 | 
			
		||||
 | 
			
		||||
@ -174,7 +174,7 @@
 | 
			
		||||
		<div class="ui attached segment">
 | 
			
		||||
			<form class="ui form" action="./avatar" method="post" enctype="multipart/form-data">
 | 
			
		||||
				{{.CsrfTokenHtml}}
 | 
			
		||||
				{{if not .DisableGravatar}}
 | 
			
		||||
				{{if .EnableGravatar}}
 | 
			
		||||
				<div class="inline field">
 | 
			
		||||
					<div class="ui radio checkbox">
 | 
			
		||||
						<input name="source" value="lookup" type="radio" {{if not .User.UseCustomAvatar}}checked{{end}}>
 | 
			
		||||
 | 
			
		||||
@ -210,9 +210,9 @@
 | 
			
		||||
								</div>
 | 
			
		||||
							</div>
 | 
			
		||||
							<div class="inline field">
 | 
			
		||||
								<div class="ui checkbox" id="disable-gravatar">
 | 
			
		||||
									<label data-tooltip-content="{{ctx.Locale.Tr "install.disable_gravatar_popup"}}">{{ctx.Locale.Tr "install.disable_gravatar"}}</label>
 | 
			
		||||
									<input name="disable_gravatar" type="checkbox" {{if .disable_gravatar}}checked{{end}}>
 | 
			
		||||
								<div class="ui checkbox" id="enable-gravatar">
 | 
			
		||||
									<label data-tooltip-content="{{ctx.Locale.Tr "install.enable_gravatar_popup"}}">{{ctx.Locale.Tr "install.enable_gravatar"}}</label>
 | 
			
		||||
									<input name="enable_gravatar" type="checkbox" {{if .enable_gravatar}}checked{{end}}>
 | 
			
		||||
								</div>
 | 
			
		||||
							</div>
 | 
			
		||||
							<div class="inline field">
 | 
			
		||||
 | 
			
		||||
@ -103,7 +103,7 @@
 | 
			
		||||
		<div class="ui attached segment">
 | 
			
		||||
			<form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data">
 | 
			
		||||
				{{.CsrfTokenHtml}}
 | 
			
		||||
				{{if not .DisableGravatar}}
 | 
			
		||||
				{{if .EnableGravatar}}
 | 
			
		||||
				<div class="inline field">
 | 
			
		||||
					<div class="ui radio checkbox">
 | 
			
		||||
						<input name="source" value="lookup" type="radio" {{if not .SignedUser.UseCustomAvatar}}checked{{end}}>
 | 
			
		||||
 | 
			
		||||
@ -62,20 +62,20 @@ function initPreInstall() {
 | 
			
		||||
  // TODO: better handling of exclusive relations.
 | 
			
		||||
  document.querySelector<HTMLInputElement>('#offline-mode input').addEventListener('change', function () {
 | 
			
		||||
    if (this.checked) {
 | 
			
		||||
      document.querySelector<HTMLInputElement>('#disable-gravatar input').checked = true;
 | 
			
		||||
      document.querySelector<HTMLInputElement>('#enable-gravatar input').checked = false;
 | 
			
		||||
      document.querySelector<HTMLInputElement>('#federated-avatar-lookup input').checked = false;
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  document.querySelector<HTMLInputElement>('#disable-gravatar input').addEventListener('change', function () {
 | 
			
		||||
  document.querySelector<HTMLInputElement>('#enable-gravatar input').addEventListener('change', function () {
 | 
			
		||||
    if (this.checked) {
 | 
			
		||||
      document.querySelector<HTMLInputElement>('#federated-avatar-lookup input').checked = false;
 | 
			
		||||
      document.querySelector<HTMLInputElement>('#federated-avatar-lookup input').checked = true;
 | 
			
		||||
    } else {
 | 
			
		||||
      document.querySelector<HTMLInputElement>('#offline-mode input').checked = false;
 | 
			
		||||
      document.querySelector<HTMLInputElement>('#offline-mode input').checked = true;
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  document.querySelector<HTMLInputElement>('#federated-avatar-lookup input').addEventListener('change', function () {
 | 
			
		||||
    if (this.checked) {
 | 
			
		||||
      document.querySelector<HTMLInputElement>('#disable-gravatar input').checked = false;
 | 
			
		||||
      document.querySelector<HTMLInputElement>('#enable-gravatar input').checked = false;
 | 
			
		||||
      document.querySelector<HTMLInputElement>('#offline-mode input').checked = false;
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user