diff --git a/changelog/unreleased/default-language.md b/changelog/unreleased/default-language.md new file mode 100644 index 00000000000..ddfdf62cb4f --- /dev/null +++ b/changelog/unreleased/default-language.md @@ -0,0 +1,6 @@ +Enhancement: The default language added + +The ability of configuration the default language has been added to the setting service. + +https://github.com/owncloud/ocis/pull/7417 +https://github.com/owncloud/enterprise/issues/5915 diff --git a/services/settings/README.md b/services/settings/README.md index 256cf4b290a..db1cd3ed425 100644 --- a/services/settings/README.md +++ b/services/settings/README.md @@ -71,3 +71,9 @@ Services can set or query ocis *setting values* of a user from settings bundles. ## Service Accounts The settings service needs to know the ID's of service accounts but it doesn't need their secrets. Currently only one service account can be configured which has the admin role. This can be set with the `SETTINGS_SERVICE_ACCOUNT_ID_ADMIN` envvar, but it will also pick up the global `OCIS_SERVICE_ACCOUNT_ID` envvar. Also see the 'auth-service' service description for additional details. + +## Default Language + +The default language can be defined via SETTINGS_DEFAULT_LANGUAGE environment variable. If this variable is not defined, English will be used as default. The value has the ISO 639-1 format ("de", "en", etc.) and is limited by the list supported languages. This setting can be used to set the default language for invitation emails. + +Important developer note: the list of supported languages is at the moment not easy defineable, as it is the minimum intersection of languages shown in the WebUI and languages defined in the ocis code for the use of notifications. Even more, not all languages where there are translations available on transifex, are available in the WebUI respectively for ocis notifications, and the translation rate for existing languages is partially not that high. You will see therefore quite often English default strings though a supported language may exist and was selected. diff --git a/services/settings/pkg/command/server.go b/services/settings/pkg/command/server.go index 7975ea41cee..ca87133fb08 100644 --- a/services/settings/pkg/command/server.go +++ b/services/settings/pkg/command/server.go @@ -55,7 +55,7 @@ func Server(cfg *config.Config) *cli.Command { mtrcs := metrics.New() mtrcs.BuildInfo.WithLabelValues(version.GetString()).Set(1) - handle := svc.NewService(cfg, logger) + handle := svc.NewDefaultLanguageService(cfg, svc.NewService(cfg, logger)) // prepare an HTTP server and add it to the group run. httpServer, err := http.Server( diff --git a/services/settings/pkg/config/config.go b/services/settings/pkg/config/config.go index 2ca13dff155..7b75412021e 100644 --- a/services/settings/pkg/config/config.go +++ b/services/settings/pkg/config/config.go @@ -39,6 +39,8 @@ type Config struct { ServiceAccountIDAdmin string `yaml:"service_account_id_admin" env:"OCIS_SERVICE_ACCOUNT_ID;SETTINGS_SERVICE_ACCOUNT_ID_ADMIN" desc:"The ID of the service account having the admin role. See the 'auth-service' service description for more details."` + DefaultLanguage string `yaml:"default_language" env:"SETTINGS_DEFAULT_LANGUAGE" desc:"The default language. If not defined, English will be used as default. See the documentation for more details."` + Context context.Context `yaml:"-"` } diff --git a/services/settings/pkg/server/grpc/option.go b/services/settings/pkg/server/grpc/option.go index 5359d883e84..2d4992de831 100644 --- a/services/settings/pkg/server/grpc/option.go +++ b/services/settings/pkg/server/grpc/option.go @@ -6,7 +6,7 @@ import ( "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/settings/pkg/config" "github.com/owncloud/ocis/v2/services/settings/pkg/metrics" - svc "github.com/owncloud/ocis/v2/services/settings/pkg/service/v0" + "github.com/owncloud/ocis/v2/services/settings/pkg/settings" "github.com/urfave/cli/v2" "go.opentelemetry.io/otel/trace" ) @@ -21,7 +21,7 @@ type Options struct { Context context.Context Config *config.Config Metrics *metrics.Metrics - ServiceHandler svc.Service + ServiceHandler settings.ServiceHandler Flags []cli.Flag TraceProvider trace.TracerProvider } @@ -80,7 +80,7 @@ func Flags(val []cli.Flag) Option { } // ServiceHandler provides a function to set the ServiceHandler option -func ServiceHandler(val svc.Service) Option { +func ServiceHandler(val settings.ServiceHandler) Option { return func(o *Options) { o.ServiceHandler = val } diff --git a/services/settings/pkg/server/http/option.go b/services/settings/pkg/server/http/option.go index 793ab7f2c0d..3af982d9786 100644 --- a/services/settings/pkg/server/http/option.go +++ b/services/settings/pkg/server/http/option.go @@ -6,7 +6,7 @@ import ( "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/settings/pkg/config" "github.com/owncloud/ocis/v2/services/settings/pkg/metrics" - svc "github.com/owncloud/ocis/v2/services/settings/pkg/service/v0" + "github.com/owncloud/ocis/v2/services/settings/pkg/settings" "github.com/urfave/cli/v2" "go.opentelemetry.io/otel/trace" ) @@ -21,7 +21,7 @@ type Options struct { Context context.Context Config *config.Config Metrics *metrics.Metrics - ServiceHandler svc.Service + ServiceHandler settings.ServiceHandler Flags []cli.Flag TraceProvider trace.TracerProvider } @@ -80,7 +80,7 @@ func Flags(val []cli.Flag) Option { } // ServiceHandler provides a function to set the ServiceHandler option -func ServiceHandler(val svc.Service) Option { +func ServiceHandler(val settings.ServiceHandler) Option { return func(o *Options) { o.ServiceHandler = val } diff --git a/services/settings/pkg/service/v0/service.go b/services/settings/pkg/service/v0/service.go index 58ed710d9a7..48897b1a09e 100644 --- a/services/settings/pkg/service/v0/service.go +++ b/services/settings/pkg/service/v0/service.go @@ -33,7 +33,7 @@ type Service struct { } // NewService returns a service implementation for Service. -func NewService(cfg *config.Config, logger log.Logger) Service { +func NewService(cfg *config.Config, logger log.Logger) settings.ServiceHandler { service := Service{ id: "ocis-settings", config: cfg, diff --git a/services/settings/pkg/service/v0/servicedecorator.go b/services/settings/pkg/service/v0/servicedecorator.go new file mode 100644 index 00000000000..c8a5f9e0123 --- /dev/null +++ b/services/settings/pkg/service/v0/servicedecorator.go @@ -0,0 +1,84 @@ +package svc + +import ( + "context" + "strings" + + settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0" + settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" + "github.com/owncloud/ocis/v2/services/settings/pkg/config" + "github.com/owncloud/ocis/v2/services/settings/pkg/settings" + "github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults" +) + +var _defaultLanguage = "en" + +// NewDefaultLanguageService returns a default language decorator for ServiceHandler. +func NewDefaultLanguageService(cfg *config.Config, serviceHandler settings.ServiceHandler) settings.ServiceHandler { + defaultLanguage := cfg.DefaultLanguage + if defaultLanguage == "" { + defaultLanguage = _defaultLanguage + } + return &defaultLanguageDecorator{defaultLanguage: defaultLanguage, ServiceHandler: serviceHandler} +} + +type defaultLanguageDecorator struct { + defaultLanguage string + settings.ServiceHandler +} + +// GetValueByUniqueIdentifiers implements the ValueService interface +func (s *defaultLanguageDecorator) GetValueByUniqueIdentifiers(ctx context.Context, req *settingssvc.GetValueByUniqueIdentifiersRequest, res *settingssvc.GetValueResponse) error { + err := s.ServiceHandler.GetValueByUniqueIdentifiers(ctx, req, res) + if err != nil { + if strings.Contains(strings.ToLower(err.Error()), "not found") && req.GetSettingId() == defaults.SettingUUIDProfileLanguage && res.GetValue() == nil { + res.Value = s.withDefaultLanguageSetting(req.AccountUuid) + return nil + } + return err + } + return nil +} + +// ListValues implements the ValueServiceHandler interface +func (s *defaultLanguageDecorator) ListValues(ctx context.Context, req *settingssvc.ListValuesRequest, res *settingssvc.ListValuesResponse) error { + err := s.ServiceHandler.ListValues(ctx, req, res) + if err != nil { + return err + } + for _, v := range res.Values { + if v.GetValue().GetSettingId() == defaults.SettingUUIDProfileLanguage { + return nil + } + } + + res.Values = append(res.Values, s.withDefaultLanguageSetting(req.AccountUuid)) + return nil +} + +func (s *defaultLanguageDecorator) withDefaultLanguageSetting(accountUUID string) *settingsmsg.ValueWithIdentifier { + return &settingsmsg.ValueWithIdentifier{ + Identifier: &settingsmsg.Identifier{ + Extension: "ocis-accounts", + Bundle: "profile", + Setting: "language", + }, + Value: &settingsmsg.Value{ + BundleId: defaults.BundleUUIDProfile, + SettingId: defaults.SettingUUIDProfileLanguage, + AccountUuid: accountUUID, + Resource: &settingsmsg.Resource{ + Type: settingsmsg.Resource_TYPE_USER, + }, + Value: &settingsmsg.Value_ListValue{ + ListValue: &settingsmsg.ListValue{Values: []*settingsmsg.ListOptionValue{ + { + Option: &settingsmsg.ListOptionValue_StringValue{ + StringValue: s.defaultLanguage, + }, + }, + }}, + }, + }, + } +} diff --git a/services/settings/pkg/settings/settings.go b/services/settings/pkg/settings/settings.go index 6fefd82718e..21a17bb008b 100644 --- a/services/settings/pkg/settings/settings.go +++ b/services/settings/pkg/settings/settings.go @@ -3,7 +3,9 @@ package settings import ( "errors" + cs3permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1" settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0" + settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" "github.com/owncloud/ocis/v2/services/settings/pkg/config" ) @@ -25,6 +27,15 @@ type RegisterFunc func(*config.Config) Manager //go:generate mockery --name=Manager +// ServiceHandler combines handlers interfaces +type ServiceHandler interface { + settingssvc.BundleServiceHandler + settingssvc.ValueServiceHandler + settingssvc.RoleServiceHandler + settingssvc.PermissionServiceHandler + cs3permissions.PermissionsAPIServer +} + // Manager combines service interfaces for abstraction of storage implementations type Manager interface { BundleManager diff --git a/services/settings/pkg/store/defaults/defaults.go b/services/settings/pkg/store/defaults/defaults.go index e8f38c24f8c..46cbf2d8603 100644 --- a/services/settings/pkg/store/defaults/defaults.go +++ b/services/settings/pkg/store/defaults/defaults.go @@ -18,6 +18,9 @@ const ( // BundleUUIDRoleUserLight represents the user light role. BundleUUIDRoleUserLight = "38071a68-456a-4553-846a-fa67bf5596cc" + // BundleUUIDProfile represents the user profile + BundleUUIDProfile = "2a506de7-99bd-4f0d-994e-c38e72c28fd9" + // RoleManagementPermissionID is the hardcoded setting UUID for the role management permission RoleManagementPermissionID string = "a53e601e-571f-4f86-8fec-d4576ef49c62" // RoleManagementPermissionName is the hardcoded setting name for the role management permission @@ -767,7 +770,7 @@ func generateBundleUserLightRole() *settingsmsg.Bundle { func generateBundleProfileRequest() *settingsmsg.Bundle { return &settingsmsg.Bundle{ - Id: "2a506de7-99bd-4f0d-994e-c38e72c28fd9", + Id: BundleUUIDProfile, Name: "profile", Extension: "ocis-accounts", Type: settingsmsg.Bundle_TYPE_DEFAULT, @@ -837,7 +840,6 @@ var languageSetting = settingsmsg.Setting_SingleChoiceValue{ }, }, DisplayValue: "English", - Default: true, }, { Value: &settingsmsg.ListOptionValue{ @@ -885,10 +887,12 @@ func DefaultRoleAssignments(cfg *config.Config) []*settingsmsg.UserRoleAssignmen { AccountUuid: "4c510ada-c86b-4815-8820-42cdf82c3d51", RoleId: BundleUUIDRoleUser, - }, { + }, + { AccountUuid: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", RoleId: BundleUUIDRoleUser, - }, { + }, + { AccountUuid: "932b4540-8d16-481e-8ef4-588e4b6b151c", RoleId: BundleUUIDRoleUser, }, @@ -896,7 +900,8 @@ func DefaultRoleAssignments(cfg *config.Config) []*settingsmsg.UserRoleAssignmen // additional admin user AccountUuid: "058bff95-6708-4fe5-91e4-9ea3d377588b", // demo user "moss" RoleId: BundleUUIDRoleAdmin, - }, { + }, + { // default users with role "spaceadmin" AccountUuid: "534bb038-6f9d-4093-946f-133be61fa4e7", RoleId: BundleUUIDRoleSpaceAdmin,