Settings Management

Settings Management

The Settings Management system provides a central way to configure application-wide settings. It controls global or user-specific settings for other systems such as general user registration rules for User Management or app icons & private policy pages for Theme Management.

Functional areas

General user settings

  • Enable/disable free user registration
  • Enable/disable “forgot password” functionality
  • Define a domain whitelist (each entry represents an allowed domain suffix, e.g. @nago-dev.com).
    If the list is empty, any domain is allowed.
  • Define default roles and groups for new users
  • Define default roles and groups for anonymous users (not inherited to valid, invalid or logged-in users)

Free registration form rules

  • Configure which contact information is required during free registration
  • Use regular expressions for validation:
    • ^.*$ → optional
    • ^.+$ → required
    • ^(OptionA|OptionB)$ → restrict values to a fixed set
  • Define specific user consent options for the registration process
⚠️
If you restrict values to a fixed set, ensure that you add supporting text for the field, otherwise users will not know which values are valid!
ℹ️
Use the reflect package to add supporting text e.g. for the salutation field in user.Settings.
xreflect.SetFieldTagFor[user.Settings]("Salutation", "supportingText", "'Mrs' or 'Mr'")

Example: User Management – GDPR consents

import (
    "go.wdy.de/nago/application"
    "go.wdy.de/nago/application/settings"
    "go.wdy.de/nago/application/user"
	
    "github.com/worldiety/option"
)


func configureGDPRConsents(cfg *application.Configurator) {
    usrSettings := settings.ReadGlobal[user.Settings](std.Must(cfg.SettingsManagement()).UseCases.LoadGlobal)
    
    // do not append, just clear it
    usrSettings.Consents = []user.ConsentOption{
        {
            ID: consent.DataProtectionProvision,
            Register: user.ConsentText{Label: "Yes, I have read and accepted the [Privacy Policy](https://www.nago-dv.com/private_policy"},
            Required: true,
        },
        {
            ID: consent.SMS,
            Profile: user.ConsentText{Label: "Transfer of my data to the project sponsor"},
            Required: false,
        },
        {
            ID: consent.Newsletter,
            Register: user.ConsentText{
                Label:          "Yes, I would like to receive news from the Nago community via email.",
                SupportingText: "You can unsubscribe at any time in your account settings or via the unsubscribe link in the emails.",
        },
            Profile: user.ConsentText{
                Label:          "Newsletter",
                SupportingText: "Receive regular email updates",
			},
            Required: false,
        },
	}
    
    // apply settings
    option.MustZero(option.Must(cfg.SettingsManagement()).UseCases.StoreGlobal(user.SU(), usrSettings))
}

Global settings

  • Each system can register its own global settings
  • These settings are automatically exposed via the Admin Center UI
  • Examples:
    • Schedule Management: periodic jobs with custom parameters
    • Theme Management: system-wide design configuration

Custom settings must be defined in a struct and registered as a variant of settings.GlobalSettings. They are then automatically available in the Admin Center UI.

Example: Scheduler with custom settings

type Settings struct {
    _        any           `title:"Events" description:"Configure events."`
    Lifetime time.Duration `json:"lifetime" label:"Lifetime of events" supportingText:"Events will be deleted after the defined lifetime."`
}

func (t Settings) GlobalSettings() bool {
    return true
}

var _ = enum.Variant[settings.GlobalSettings, Settings]()

Usage example in a scheduler configuration:

import (
	"context"
	"time"
	
    "go.wdy.de/nago/application/scheduler"
    cfgscheduler "go.wdy.de/nago/application/scheduler/cfg"
    "go.wdy.de/nago/application/settings"
    "go.wdy.de/nago/application/user"
	
    "github.com/worldiety/option"
)

type DeleteOldest func(subject auth.Subject, settings Settings) error

schedulers := std.Must(cfgscheduler.Enable(cfg))

option.MustZero(schedulers.UseCases.Configure(user.SU(), scheduler.Options{
    ID:          "nago.dev.events.remove",
    Name:        "Remove events",
    Description: "Events that are not used must be removed.",
    Kind:        scheduler.Schedule,
    Defaults: scheduler.Settings{
        StartDelay: time.Second * 10,
        PauseTime:  time.Hour * 24,
    },
    Runner: func(ctx context.Context) error {
        myEventSettings := settings.ReadGlobal[Settings](settingsManagement.UseCases.LoadGlobal)
        return DeleteOldest(user.SU(), myEventSettings) // security note: cron job
    },
}))

For more information have a look at Scheduler Management.

Example: custom theme settings via code configuration

import (
    "context"
    _ "embed"
    "time"
    
    "go.wdy.de/nago/application"
    "go.wdy.de/nago/application/image"
    "go.wdy.de/nago/application/settings"
    "go.wdy.de/nago/application/user"
    "go.wdy.de/nago/presentation/core"
    
    "github.com/worldiety/option"
)

//go:embed nago_icon.png
var nagoIcon application.StaticBytes

func LoadMySettings(settingsManagement application.SettingsManagement, imageManagement application.ImageManagement) {
	userSettings := settings.ReadGlobal[user.Settings](settingsManagement.UseCases.LoadGlobal)
	storeSettings := settingsManagement.UseCases.StoreGlobal

	userSettings.SelfRegistration = true
	userSettings.SelfPasswordReset = true
	userSettings.AllowedDomains = []string{
		"@nago-dev.com",
		"@worldiety.de",
	}
	userSettings.AnonRoles = []role.ID{
		"nago.dev",
	}

	option.MustZero(storeSettings(user.SU(), userSettings))

	themeSettings := settings.ReadGlobal[theme.Settings](settingsManagement.UseCases.LoadGlobal)
	srcSet := option.Must(imageManagement.UseCases.CreateSrcSet(user.SU(), image.Options{}, core.MemFile{
		Filename:     "nago_icon.png",
		MimeTypeHint: "png",
		Bytes:        nagoIcon,
	}))

	themeSettings.PageLogoLight = srcSet.ID
	themeSettings.PageLogoDark = srcSet.ID
	themeSettings.Impress = "page/privacy"

	option.MustZero(storeSettings(user.SU(), themeSettings))
}

Configuration via the admin center:

Dependencies

Requires:

  • No other systems

Is required by:

Activation

This system is activated via the configurator:

std.Must(cfg.SettingsManagement())
settingsManagement := std.Must(cfg.SettingsManagement())