From 2c242743d0bf8cd21f599cca1cfefad7967f7245 Mon Sep 17 00:00:00 2001 From: Roman Perekhod Date: Wed, 19 Apr 2023 16:33:37 +0200 Subject: [PATCH] Determine the users preferred language to translate emails via Transifex. #6087 --- services/notifications/pkg/service/service.go | 71 +++++++++++++++++-- services/notifications/pkg/service/shares.go | 20 ++---- services/notifications/pkg/service/spaces.go | 29 +++----- .../settings/pkg/store/metadata/values.go | 36 +++++++++- 4 files changed, 112 insertions(+), 44 deletions(-) diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index 714696e720f..31345a43da5 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -17,8 +17,13 @@ import ( provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/events" "github.com/owncloud/ocis/v2/ocis-pkg/log" + "github.com/owncloud/ocis/v2/ocis-pkg/middleware" + "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" + "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" + settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" "github.com/owncloud/ocis/v2/services/notifications/pkg/channels" "github.com/owncloud/ocis/v2/services/notifications/pkg/email" + "go-micro.dev/v4/metadata" "google.golang.org/protobuf/types/known/fieldmaskpb" ) @@ -34,12 +39,14 @@ func NewEventsNotifier( logger log.Logger, gwClient gateway.GatewayAPIClient, machineAuthAPIKey, emailTemplatePath, ocisURL string) Service { + return eventsNotifier{ logger: logger, channel: channel, events: events, signals: make(chan os.Signal, 1), gwClient: gwClient, + valueService: settingssvc.NewValueService("com.owncloud.api.settings", grpc.DefaultClient()), machineAuthAPIKey: machineAuthAPIKey, emailTemplatePath: emailTemplatePath, ocisURL: ocisURL, @@ -52,6 +59,7 @@ type eventsNotifier struct { events <-chan events.Event signals chan os.Signal gwClient gateway.GatewayAPIClient + valueService settingssvc.ValueService machineAuthAPIKey string emailTemplatePath string translationPath string @@ -87,24 +95,75 @@ func (s eventsNotifier) Run() error { } } -func (s eventsNotifier) render(template email.MessageTemplate, values map[string]interface{}) (string, string, error) { +func (s eventsNotifier) render(template email.MessageTemplate, values map[string]interface{}) func(string) (string, string, error) { // The locate have to come from the user setting - return email.RenderEmailTemplate(template, "en", s.emailTemplatePath, s.translationPath, values) + return func(locale string) (string, string, error) { + return email.RenderEmailTemplate(template, locale, s.emailTemplatePath, s.translationPath, values) + } } -func (s eventsNotifier) send(ctx context.Context, u *user.UserId, g *group.GroupId, msg, subj, sender string) error { +func (s eventsNotifier) send(ctx context.Context, u *user.UserId, g *group.GroupId, renderFunc func(string) (string, string, error), sender string) error { if u != nil { + locale, err := s.getUserLang(ctx, u) + if err != nil { + return err + } + subj, msg, err := renderFunc(locale) + if err != nil { + return err + } return s.channel.SendMessage(ctx, []string{u.GetOpaqueId()}, msg, subj, sender) - } if g != nil { - return s.channel.SendMessageToGroup(ctx, g, msg, subj, sender) - } + res, err := s.gwClient.GetGroup(ctx, &group.GetGroupRequest{GroupId: g}) + if err != nil { + return err + } + if res.Status.Code != rpc.Code_CODE_OK { + return errors.New("could not get group") + } + for _, id := range res.Group.Members { + locale, err := s.getUserLang(ctx, id) + if err != nil { + return err + } + subj, msg, err := renderFunc(locale) + if err != nil { + return err + } + err = s.channel.SendMessage(ctx, []string{u.GetOpaqueId()}, msg, subj, sender) + if err != nil { + s.logger.Error().Err(err).Str("event", "SendEmailGroupMembers").Msg("failed to send a message") + } + } + if err != nil { + s.logger.Error().Err(err).Str("event", "SendEmailGroupMembers").Msg("failed to send a message") + } + } return nil } +func (s eventsNotifier) getUserLang(ctx context.Context, u *user.UserId) (string, error) { + defaultLocale := "en" + granteeCtx := metadata.Set(ctx, middleware.AccountID, u.OpaqueId) + resp, err := s.valueService.GetValueByUniqueIdentifiers(granteeCtx, + &v0.GetValueByUniqueIdentifiersRequest{ + AccountUuid: u.OpaqueId, + SettingId: "aa8cfbe5-95d4-4f7e-a032-c3c01f5f062f", + }) + + if err != nil || resp == nil { + return defaultLocale, nil + } + val := resp.Value.GetValue().GetListValue().GetValues() + if len(val) > 0 && val[0] != nil { + return val[0].GetStringValue(), nil + } + return defaultLocale, nil +} + func (s eventsNotifier) getGranteeName(ctx context.Context, u *user.UserId, g *group.GroupId) (string, error) { switch { case u != nil: diff --git a/services/notifications/pkg/service/shares.go b/services/notifications/pkg/service/shares.go index 6451783bc2f..1468973d9e9 100644 --- a/services/notifications/pkg/service/shares.go +++ b/services/notifications/pkg/service/shares.go @@ -42,18 +42,14 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { } sharerDisplayName := owner.GetDisplayName() - subj, msg, err := s.render(email.ShareCreated, map[string]interface{}{ + renderFunc := s.render(email.ShareCreated, map[string]interface{}{ "ShareGrantee": shareGrantee, "ShareSharer": sharerDisplayName, "ShareFolder": resourceInfo.Name, "ShareLink": shareLink, }) - if err != nil { - s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("Could not render E-Mail body template for shares") - } - - if err := s.send(ownerCtx, e.GranteeUserID, e.GranteeGroupID, msg, subj, sharerDisplayName); err != nil { + if err := s.send(ownerCtx, e.GranteeUserID, e.GranteeGroupID, renderFunc, sharerDisplayName); err != nil { s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("failed to send a message") } @@ -81,22 +77,18 @@ func (s eventsNotifier) handleShareExpired(e events.ShareExpired) { shareGrantee, err := s.getGranteeName(ctx, e.GranteeUserID, e.GranteeGroupID) if err != nil { - s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("Could not get grantee name") + s.logger.Error().Err(err).Str("event", "ShareExpired").Msg("Could not get grantee name") return } - subj, msg, err := s.render(email.ShareExpired, map[string]interface{}{ + renderFunc := s.render(email.ShareExpired, map[string]interface{}{ "ShareGrantee": shareGrantee, "ShareFolder": resourceInfo.GetName(), "ExpiredAt": e.ExpiredAt.Format("2006-01-02 15:04:05"), }) - if err != nil { - s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("Could not render E-Mail body template for shares") - } - - if err := s.send(ctx, e.GranteeUserID, e.GranteeGroupID, msg, subj, owner.GetDisplayName()); err != nil { - s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("failed to send a message") + if err := s.send(ctx, e.GranteeUserID, e.GranteeGroupID, renderFunc, owner.GetDisplayName()); err != nil { + s.logger.Error().Err(err).Str("event", "ShareExpired").Msg("failed to send a message") } } diff --git a/services/notifications/pkg/service/spaces.go b/services/notifications/pkg/service/spaces.go index bf7d7473428..cebc9479798 100644 --- a/services/notifications/pkg/service/spaces.go +++ b/services/notifications/pkg/service/spaces.go @@ -55,18 +55,14 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { } sharerDisplayName := owner.GetDisplayName() - subj, msg, err := s.render(email.SharedSpace, map[string]interface{}{ + renderFunc := s.render(email.SharedSpace, map[string]interface{}{ "SpaceGrantee": spaceGrantee, "SpaceSharer": sharerDisplayName, "SpaceName": resourceInfo.GetSpace().GetName(), "ShareLink": shareLink, }) - if err != nil { - logger.Error().Err(err).Msg("Could not render E-Mail template for spaces") - return - } - if err := s.send(ownerCtx, e.GranteeUserID, e.GranteeGroupID, msg, subj, sharerDisplayName); err != nil { + if err := s.send(ownerCtx, e.GranteeUserID, e.GranteeGroupID, renderFunc, sharerDisplayName); err != nil { logger.Error().Err(err).Msg("failed to send a message") } } @@ -117,20 +113,15 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) { } sharerDisplayName := owner.GetDisplayName() - subj, msg, err := s.render(email.UnsharedSpace, map[string]interface{}{ + renderFunc := s.render(email.UnsharedSpace, map[string]interface{}{ "SpaceGrantee": spaceGrantee, "SpaceSharer": sharerDisplayName, "SpaceName": resourceInfo.GetSpace().Name, "ShareLink": shareLink, }) - if err != nil { - logger.Error().Err(err).Msg("Could not render E-Mail template for spaces") - return - } - - if err := s.send(ownerCtx, e.GranteeUserID, e.GranteeGroupID, msg, subj, sharerDisplayName); err != nil { - logger.Error().Err(err).Msg("failed to send a message") + if err := s.send(ownerCtx, e.GranteeUserID, e.GranteeGroupID, renderFunc, sharerDisplayName); err != nil { + s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("failed to send a message") } } @@ -152,18 +143,14 @@ func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExp return } - subj, msg, err := s.render(email.MembershipExpired, map[string]interface{}{ + renderFunc := s.render(email.MembershipExpired, map[string]interface{}{ "SpaceGrantee": shareGrantee, "SpaceName": e.SpaceName, "ExpiredAt": e.ExpiredAt.Format("2006-01-02 15:04:05"), }) - if err != nil { - s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("Could not render E-Mail body template for shares") - } - - if err := s.send(ctx, e.GranteeUserID, e.GranteeGroupID, msg, subj, owner.GetDisplayName()); err != nil { - s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("failed to send a message") + if err := s.send(ctx, e.GranteeUserID, e.GranteeGroupID, renderFunc, owner.GetDisplayName()); err != nil { + s.logger.Error().Err(err).Str("event", "SpaceUnshared").Msg("failed to send a message") } } diff --git a/services/settings/pkg/store/metadata/values.go b/services/settings/pkg/store/metadata/values.go index a9df99dacea..c40f859fa73 100644 --- a/services/settings/pkg/store/metadata/values.go +++ b/services/settings/pkg/store/metadata/values.go @@ -4,7 +4,6 @@ package store import ( "context" "encoding/json" - "errors" "fmt" "github.com/cs3org/reva/v2/pkg/errtypes" @@ -87,8 +86,39 @@ func (s *Store) ReadValue(valueID string) (*settingsmsg.Value, error) { // ReadValueByUniqueIdentifiers tries to find a value given a set of unique identifiers func (s *Store) ReadValueByUniqueIdentifiers(accountUUID, settingID string) (*settingsmsg.Value, error) { - fmt.Println("ReadValueByUniqueIdentifiers not implemented") - return nil, errors.New("not implemented") + if settingID == "" { + return nil, fmt.Errorf("settingID can not be empty %w", settings.ErrNotFound) + } + s.Init() + ctx := context.TODO() + + vIDs, err := s.mdc.ReadDir(ctx, valuesFolderLocation) + if err != nil { + return nil, err + } + + for _, vid := range vIDs { + b, err := s.mdc.SimpleDownload(ctx, valuePath(vid)) + switch err.(type) { + case nil: + // continue + case errtypes.NotFound: + continue + default: + return nil, err + } + + v := &settingsmsg.Value{} + err = json.Unmarshal(b, v) + if err != nil { + return nil, err + } + + if v.AccountUuid == accountUUID && v.SettingId == settingID { + return v, nil + } + } + return nil, settings.ErrNotFound } // WriteValue writes the given value into a file within the dataPath