diff --git a/docs/api/swagger.yaml b/docs/api/swagger.yaml index 88ad8eb55..0eca007ea 100644 --- a/docs/api/swagger.yaml +++ b/docs/api/swagger.yaml @@ -1581,9 +1581,7 @@ definitions: type: integer x-go-name: MaxFeaturedTags max_profile_fields: - description: |- - The maximum number of profile fields allowed for each account. - Currently not configurable, so this is hardcoded to 6. (https://codeberg.org/superseriousbusiness/gotosocial/issues/1876) + description: The maximum number of profile fields allowed for each account. format: int64 type: integer x-go-name: MaxProfileFields diff --git a/docs/configuration/accounts.md b/docs/configuration/accounts.md index 788cf79ac..b9f72252c 100644 --- a/docs/configuration/accounts.md +++ b/docs/configuration/accounts.md @@ -64,4 +64,12 @@ accounts-allow-custom-css: false # Examples: [500, 5000, 9999] # Default: 10000 accounts-custom-css-length: 10000 + +# Int. The maximum number of profile fields allowed for each account. +# +# Note that going way higher than the default might break federation. +# +# Examples: [4, 6, 12] +# Default: 6 +accounts-max-profile-fields: 6 ``` diff --git a/example/config.yaml b/example/config.yaml index 209e2dc9d..ce81da1fe 100644 --- a/example/config.yaml +++ b/example/config.yaml @@ -533,6 +533,14 @@ accounts-allow-custom-css: false # Default: 10000 accounts-custom-css-length: 10000 +# Int. The maximum number of profile fields allowed for each account. +# +# Note that going way higher than the default might break federation. +# +# Examples: [4, 6, 12] +# Default: 6 +accounts-max-profile-fields: 6 + ######################## ##### MEDIA CONFIG ##### ######################## diff --git a/internal/api/client/instance/instancepatch_test.go b/internal/api/client/instance/instancepatch_test.go index 5b2b71d37..1139ac6d2 100644 --- a/internal/api/client/instance/instancepatch_test.go +++ b/internal/api/client/instance/instancepatch_test.go @@ -147,7 +147,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() { "accounts": { "allow_custom_css": true, "max_featured_tags": 10, - "max_profile_fields": 6 + "max_profile_fields": 8 }, "emojis": { "emoji_size_limit": 51200 @@ -290,7 +290,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() { "accounts": { "allow_custom_css": true, "max_featured_tags": 10, - "max_profile_fields": 6 + "max_profile_fields": 8 }, "emojis": { "emoji_size_limit": 51200 @@ -433,7 +433,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() { "accounts": { "allow_custom_css": true, "max_featured_tags": 10, - "max_profile_fields": 6 + "max_profile_fields": 8 }, "emojis": { "emoji_size_limit": 51200 @@ -627,7 +627,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() { "accounts": { "allow_custom_css": true, "max_featured_tags": 10, - "max_profile_fields": 6 + "max_profile_fields": 8 }, "emojis": { "emoji_size_limit": 51200 @@ -792,7 +792,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() { "accounts": { "allow_custom_css": true, "max_featured_tags": 10, - "max_profile_fields": 6 + "max_profile_fields": 8 }, "emojis": { "emoji_size_limit": 51200 @@ -976,7 +976,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch9() { "accounts": { "allow_custom_css": true, "max_featured_tags": 10, - "max_profile_fields": 6 + "max_profile_fields": 8 }, "emojis": { "emoji_size_limit": 51200 diff --git a/internal/api/model/instance.go b/internal/api/model/instance.go index 73aa8ef82..ab8c83d1b 100644 --- a/internal/api/model/instance.go +++ b/internal/api/model/instance.go @@ -60,7 +60,6 @@ type InstanceConfigurationAccounts struct { // Currently not implemented, so this is hardcoded to 10. MaxFeaturedTags int `json:"max_featured_tags"` // The maximum number of profile fields allowed for each account. - // Currently not configurable, so this is hardcoded to 6. (https://codeberg.org/superseriousbusiness/gotosocial/issues/1876) MaxProfileFields int `json:"max_profile_fields"` } diff --git a/internal/config/config.go b/internal/config/config.go index 0aa90ce44..dd03b1104 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -105,6 +105,7 @@ type Configuration struct { AccountsRegistrationBacklogLimit int `name:"accounts-registration-backlog-limit" usage:"Limit how big the 'accounts pending approval' queue can grow before registration is closed. 0 or less = no limit."` AccountsAllowCustomCSS bool `name:"accounts-allow-custom-css" usage:"Allow accounts to enable custom CSS for their profile pages and statuses."` AccountsCustomCSSLength int `name:"accounts-custom-css-length" usage:"Maximum permitted length (characters) of custom CSS for accounts."` + AccountsMaxProfileFields int `name:"accounts-max-profile-fields" usage:"Maximum number of profile fields allowed for each account."` MediaDescriptionMinChars int `name:"media-description-min-chars" usage:"Min required chars for an image description"` MediaDescriptionMaxChars int `name:"media-description-max-chars" usage:"Max permitted chars for an image description"` diff --git a/internal/config/defaults.go b/internal/config/defaults.go index 100738253..81242996d 100644 --- a/internal/config/defaults.go +++ b/internal/config/defaults.go @@ -75,6 +75,7 @@ var Defaults = Configuration{ AccountsRegistrationBacklogLimit: 20, AccountsAllowCustomCSS: false, AccountsCustomCSSLength: 10000, + AccountsMaxProfileFields: 6, MediaDescriptionMinChars: 0, MediaDescriptionMaxChars: 1500, diff --git a/internal/config/helpers.gen.go b/internal/config/helpers.gen.go index d38458304..a41c95861 100644 --- a/internal/config/helpers.gen.go +++ b/internal/config/helpers.gen.go @@ -79,6 +79,7 @@ func (cfg *Configuration) RegisterFlags(flags *pflag.FlagSet) { flags.Int("accounts-registration-backlog-limit", cfg.AccountsRegistrationBacklogLimit, "Limit how big the 'accounts pending approval' queue can grow before registration is closed. 0 or less = no limit.") flags.Bool("accounts-allow-custom-css", cfg.AccountsAllowCustomCSS, "Allow accounts to enable custom CSS for their profile pages and statuses.") flags.Int("accounts-custom-css-length", cfg.AccountsCustomCSSLength, "Maximum permitted length (characters) of custom CSS for accounts.") + flags.Int("accounts-max-profile-fields", cfg.AccountsMaxProfileFields, "Maximum amount of profile fields an account can have.") flags.Int("media-description-min-chars", cfg.MediaDescriptionMinChars, "Min required chars for an image description") flags.Int("media-description-max-chars", cfg.MediaDescriptionMaxChars, "Max permitted chars for an image description") flags.Int("media-remote-cache-days", cfg.MediaRemoteCacheDays, "Number of days to locally cache media from remote instances. If set to 0, remote media will be kept indefinitely.") @@ -206,7 +207,7 @@ func (cfg *Configuration) RegisterFlags(flags *pflag.FlagSet) { } func (cfg *Configuration) MarshalMap() map[string]any { - cfgmap := make(map[string]any, 181) + cfgmap := make(map[string]any, 182) cfgmap["log-level"] = cfg.LogLevel cfgmap["log-timestamp-format"] = cfg.LogTimestampFormat cfgmap["log-db-queries"] = cfg.LogDbQueries @@ -257,6 +258,7 @@ func (cfg *Configuration) MarshalMap() map[string]any { cfgmap["accounts-registration-backlog-limit"] = cfg.AccountsRegistrationBacklogLimit cfgmap["accounts-allow-custom-css"] = cfg.AccountsAllowCustomCSS cfgmap["accounts-custom-css-length"] = cfg.AccountsCustomCSSLength + cfgmap["accounts-max-profile-fields"] = cfg.AccountsMaxProfileFields cfgmap["media-description-min-chars"] = cfg.MediaDescriptionMinChars cfgmap["media-description-max-chars"] = cfg.MediaDescriptionMaxChars cfgmap["media-remote-cache-days"] = cfg.MediaRemoteCacheDays @@ -805,6 +807,14 @@ func (cfg *Configuration) UnmarshalMap(cfgmap map[string]any) error { } } + if ival, ok := cfgmap["accounts-max-profile-fields"]; ok { + var err error + cfg.AccountsMaxProfileFields, err = cast.ToIntE(ival) + if err != nil { + return fmt.Errorf("error casting %#v -> int for 'accounts-max-profile-fields': %w", ival, err) + } + } + if ival, ok := cfgmap["media-description-min-chars"]; ok { var err error cfg.MediaDescriptionMinChars, err = cast.ToIntE(ival) @@ -3138,6 +3148,31 @@ func GetAccountsCustomCSSLength() int { return global.GetAccountsCustomCSSLength // SetAccountsCustomCSSLength safely sets the value for global configuration 'AccountsCustomCSSLength' field func SetAccountsCustomCSSLength(v int) { global.SetAccountsCustomCSSLength(v) } +// AccountsMaxProfileFieldsFlag returns the flag name for the 'AccountsMaxProfileFields' field +func AccountsMaxProfileFieldsFlag() string { return "accounts-max-profile-fields" } + +// GetAccountsMaxProfileFields safely fetches the Configuration value for state's 'AccountsMaxProfileFields' field +func (st *ConfigState) GetAccountsMaxProfileFields() (v int) { + st.mutex.RLock() + v = st.config.AccountsMaxProfileFields + st.mutex.RUnlock() + return +} + +// SetAccountsMaxProfileFields safely sets the Configuration value for state's 'AccountsMaxProfileFields' field +func (st *ConfigState) SetAccountsMaxProfileFields(v int) { + st.mutex.Lock() + defer st.mutex.Unlock() + st.config.AccountsMaxProfileFields = v + st.reloadToViper() +} + +// GetAccountsMaxProfileFields safely fetches the value for global configuration 'AccountsMaxProfileFields' field +func GetAccountsMaxProfileFields() int { return global.GetAccountsMaxProfileFields() } + +// SetAccountsMaxProfileFields safely sets the value for global configuration 'AccountsMaxProfileFields' field +func SetAccountsMaxProfileFields(v int) { global.SetAccountsMaxProfileFields(v) } + // MediaDescriptionMinCharsFlag returns the flag name for the 'MediaDescriptionMinChars' field func MediaDescriptionMinCharsFlag() string { return "media-description-min-chars" } diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index 6da78ed83..bef44e32f 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -50,7 +50,6 @@ const ( instancePollsMinExpiration = 300 // seconds instancePollsMaxExpiration = 2629746 // seconds instanceAccountsMaxFeaturedTags = 10 - instanceAccountsMaxProfileFields = 6 // FIXME: https://codeberg.org/superseriousbusiness/gotosocial/issues/1876 instanceSourceURL = "https://codeberg.org/superseriousbusiness/gotosocial" instanceMastodonVersion = "3.5.3" ) @@ -1814,7 +1813,7 @@ func (c *Converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Ins instance.Configuration.Polls.MaxExpiration = instancePollsMaxExpiration instance.Configuration.Accounts.AllowCustomCSS = config.GetAccountsAllowCustomCSS() instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags - instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields + instance.Configuration.Accounts.MaxProfileFields = config.GetAccountsMaxProfileFields() instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize()) // #nosec G115 -- Already validated. instance.Configuration.OIDCEnabled = config.GetOIDCEnabled() @@ -1992,7 +1991,7 @@ func (c *Converter) InstanceToAPIV2Instance(ctx context.Context, i *gtsmodel.Ins instance.Configuration.Polls.MaxExpiration = instancePollsMaxExpiration instance.Configuration.Accounts.AllowCustomCSS = config.GetAccountsAllowCustomCSS() instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags - instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields + instance.Configuration.Accounts.MaxProfileFields = config.GetAccountsMaxProfileFields() instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize()) // #nosec G115 -- Already validated. instance.Configuration.OIDCEnabled = config.GetOIDCEnabled() diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go index 6a5503e83..74250769b 100644 --- a/internal/typeutils/internaltofrontend_test.go +++ b/internal/typeutils/internaltofrontend_test.go @@ -2517,7 +2517,7 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() { "accounts": { "allow_custom_css": true, "max_featured_tags": 10, - "max_profile_fields": 6 + "max_profile_fields": 8 }, "emojis": { "emoji_size_limit": 51200 @@ -2621,7 +2621,7 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV2ToFrontend() { "accounts": { "allow_custom_css": true, "max_featured_tags": 10, - "max_profile_fields": 6 + "max_profile_fields": 8 }, "statuses": { "max_characters": 5000, diff --git a/internal/validate/formvalidation.go b/internal/validate/formvalidation.go index 08fa23dcb..42c231491 100644 --- a/internal/validate/formvalidation.go +++ b/internal/validate/formvalidation.go @@ -42,7 +42,6 @@ const ( maximumUsernameLength = 64 maximumEmojiCategoryLength = 64 maximumProfileFieldLength = 255 - maximumProfileFields = 6 maximumListTitleLength = 200 maximumFilterKeywordLength = 40 maximumFilterTitleLength = 200 @@ -269,6 +268,7 @@ func ULID(i string, name string) error { // and also iterates through the fields and trims each name + value // to maximumProfileFieldLength, if they were above. func ProfileFields(fields []*gtsmodel.Field) error { + maximumProfileFields := config.GetAccountsMaxProfileFields() if len(fields) > maximumProfileFields { return fmt.Errorf("cannot have more than %d profile fields", maximumProfileFields) } diff --git a/test/envparsing.sh b/test/envparsing.sh index ccbc65cac..21abe5f51 100755 --- a/test/envparsing.sh +++ b/test/envparsing.sh @@ -7,6 +7,7 @@ EXPECT=$(cat << "EOF" "account-domain": "peepee", "accounts-allow-custom-css": true, "accounts-custom-css-length": 5000, + "accounts-max-profile-fields": 8, "accounts-reason-required": false, "accounts-registration-backlog-limit": 100, "accounts-registration-daily-limit": 50, @@ -247,6 +248,7 @@ GTS_INSTANCE_LANGUAGES="nl,en-gb" \ GTS_INSTANCE_STATS_MODE="baffle" \ GTS_ACCOUNTS_ALLOW_CUSTOM_CSS=true \ GTS_ACCOUNTS_CUSTOM_CSS_LENGTH=5000 \ +GTS_ACCOUNTS_MAX_PROFILE_FIELDS=8 \ GTS_ACCOUNTS_REGISTRATION_BACKLOG_LIMIT=100 \ GTS_ACCOUNTS_REGISTRATION_DAILY_LIMIT=50 \ GTS_ACCOUNTS_REGISTRATION_OPEN=true \ diff --git a/testrig/config.go b/testrig/config.go index 3e949ed89..3f8f22430 100644 --- a/testrig/config.go +++ b/testrig/config.go @@ -109,6 +109,7 @@ func testDefaults() config.Configuration { AccountsRegistrationBacklogLimit: 20, AccountsAllowCustomCSS: true, AccountsCustomCSSLength: 10000, + AccountsMaxProfileFields: 8, MediaDescriptionMinChars: 0, MediaDescriptionMaxChars: 500, diff --git a/web/template/about.tmpl b/web/template/about.tmpl index 37d115256..c263d73a7 100644 --- a/web/template/about.tmpl +++ b/web/template/about.tmpl @@ -81,6 +81,11 @@ Polls can have up to {{- .instance.Configuration.Polls.MaxCharactersPerOption }} characters per option. {{- end -}} +{{- define "profileLimits" -}} +Profiles can have up to +{{- .instance.Configuration.Accounts.MaxProfileFields }} profile fields. +{{- end -}} + {{- with . }}