From 62af6f443cde849ff7800cfc2b4e49aced878e06 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Mon, 6 Mar 2023 13:09:26 +0100 Subject: [PATCH 01/14] use feature reva Signed-off-by: jkoberg --- go.mod | 2 ++ go.sum | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 5128ac0a5c1..df2cd1d98de 100644 --- a/go.mod +++ b/go.mod @@ -293,3 +293,5 @@ require ( ) replace github.com/cs3org/go-cs3apis => github.com/c0rby/go-cs3apis v0.0.0-20230110100311-5b424f1baa35 + +replace github.com/cs3org/reva/v2 => github.com/kobergj/reva/v2 v2.0.0-20230306133911-885670aab8df diff --git a/go.sum b/go.sum index 853be9e1c3e..f2823cbffd9 100644 --- a/go.sum +++ b/go.sum @@ -342,8 +342,6 @@ github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4= github.com/crewjam/saml v0.4.10 h1:Rjs6x4s/aQFXiaPjw3uhB4VdxRqoxHXOJrrj4BsMn9o= github.com/crewjam/saml v0.4.10/go.mod h1:9Zh6dWPtB3MSzTRt8fIFH60Z351QQ+s7hCU3J/tTlA4= -github.com/cs3org/reva/v2 v2.12.1-0.20230308104654-4ebb727fa197 h1:CGUH/W7ZNN3pm7ElRq3vYfVIkoHj3975EY7veaR/nyY= -github.com/cs3org/reva/v2 v2.12.1-0.20230308104654-4ebb727fa197/go.mod h1:EGPAVHVqMHZ6GBo4T6QMw13D0djGU8ZCi/o5siJ9IUs= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= @@ -864,6 +862,8 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0= github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/kobergj/reva/v2 v2.0.0-20230306133911-885670aab8df h1:W0PRhL0uX1Bd+1GxizjEXJ6zekAO46V4XZs/9b2g9TM= +github.com/kobergj/reva/v2 v2.0.0-20230306133911-885670aab8df/go.mod h1:dbaNP2U3nGQA5BHLc5w/hqviq7b0F4eygNwC38jeaiU= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= From 70b3db5ff45b9efc07f890c39d69190ba853118a Mon Sep 17 00:00:00 2001 From: jkoberg Date: Tue, 7 Mar 2023 13:23:58 +0100 Subject: [PATCH 02/14] translate templates Signed-off-by: jkoberg --- go.mod | 3 +- go.sum | 2 + services/userlog/pkg/service/conversion.go | 67 +++++++++++------- services/userlog/pkg/service/http.go | 4 +- services/userlog/pkg/service/templates.go | 81 +++++++++------------- 5 files changed, 82 insertions(+), 75 deletions(-) diff --git a/go.mod b/go.mod index df2cd1d98de..e14d5bd0005 100644 --- a/go.mod +++ b/go.mod @@ -214,6 +214,7 @@ require ( github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.15.11 // indirect github.com/klauspost/cpuid/v2 v2.1.0 // indirect + github.com/leonelquinteros/gotext v1.5.2 // indirect github.com/libregraph/oidc-go v1.0.0 // indirect github.com/longsleep/go-metrics v1.0.0 // indirect github.com/longsleep/rndm v1.2.0 // indirect @@ -293,5 +294,3 @@ require ( ) replace github.com/cs3org/go-cs3apis => github.com/c0rby/go-cs3apis v0.0.0-20230110100311-5b424f1baa35 - -replace github.com/cs3org/reva/v2 => github.com/kobergj/reva/v2 v2.0.0-20230306133911-885670aab8df diff --git a/go.sum b/go.sum index f2823cbffd9..f3ec4dab073 100644 --- a/go.sum +++ b/go.sum @@ -886,6 +886,8 @@ github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leonelquinteros/gotext v1.5.2 h1:T2y6ebHli+rMBCjcJlHTXyUrgXqsKBhl/ormgvt7lPo= +github.com/leonelquinteros/gotext v1.5.2/go.mod h1:AT4NpQrOmyj1L/+hLja6aR0lk81yYYL4ePnj2kp7d6M= github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5 h1:brLMXSjWoWhGXs8LpK+Lx+FQCtGLUa51Mq/ggHv9AV0= github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5/go.mod h1:Tnm4pyVJTEbHm3GUNmceWT1DHzdrYqrJmZyt/xh7L+A= github.com/libregraph/lico v0.59.4 h1:aQNwWAtC3hyQeZpP+5U1M7PIrPtvHanRWRfMp33wbUQ= diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go index a3463508944..5f821068ef7 100644 --- a/services/userlog/pkg/service/conversion.go +++ b/services/userlog/pkg/service/conversion.go @@ -12,12 +12,16 @@ import ( "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" + "github.com/leonelquinteros/gotext" ehmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/eventhistory/v0" ) var ( _resourceTypeSpace = "storagespace" _resourceTypeShare = "share" + + // TODO: from config + _pathToLocales = "/home/jkoberg/ocis/services/userlog/pkg/service/locales" ) // OC10Notification is the oc10 style representation of an event @@ -37,7 +41,7 @@ type OC10Notification struct { } // ConvertEvent converts an eventhistory event to an OC10Notification -func (ul *UserlogService) ConvertEvent(event *ehmsg.Event) (OC10Notification, error) { +func (ul *UserlogService) ConvertEvent(event *ehmsg.Event, locale string) (OC10Notification, error) { etype, ok := ul.registeredEvents[event.Type] if !ok { // this should not happen @@ -55,33 +59,33 @@ func (ul *UserlogService) ConvertEvent(event *ehmsg.Event) (OC10Notification, er return OC10Notification{}, errors.New("unknown event type") // space related case events.SpaceDisabled: - return ul.spaceMessage(event.Id, SpaceDisabled, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp) + return ul.spaceMessage(event.Id, SpaceDisabled, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp, locale) case events.SpaceDeleted: - return ul.spaceDeletedMessage(event.Id, ev.Executant, ev.ID.GetOpaqueId(), ev.SpaceName, ev.Timestamp) + return ul.spaceDeletedMessage(event.Id, ev.Executant, ev.ID.GetOpaqueId(), ev.SpaceName, ev.Timestamp, locale) case events.SpaceShared: - return ul.spaceMessage(event.Id, SpaceShared, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp) + return ul.spaceMessage(event.Id, SpaceShared, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp, locale) case events.SpaceUnshared: - return ul.spaceMessage(event.Id, SpaceUnshared, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp) + return ul.spaceMessage(event.Id, SpaceUnshared, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp, locale) case events.SpaceMembershipExpired: - return ul.spaceMessage(event.Id, SpaceMembershipExpired, ev.SpaceOwner, ev.SpaceID.GetOpaqueId(), ev.ExpiredAt) + return ul.spaceMessage(event.Id, SpaceMembershipExpired, ev.SpaceOwner, ev.SpaceID.GetOpaqueId(), ev.ExpiredAt, locale) // share related case events.ShareCreated: - return ul.shareMessage(event.Id, ShareCreated, ev.Executant, ev.ItemID, ev.ShareID, utils.TSToTime(ev.CTime)) + return ul.shareMessage(event.Id, ShareCreated, ev.Executant, ev.ItemID, ev.ShareID, utils.TSToTime(ev.CTime), locale) case events.ShareExpired: - return ul.shareMessage(event.Id, ShareExpired, ev.ShareOwner, ev.ItemID, ev.ShareID, ev.ExpiredAt) + return ul.shareMessage(event.Id, ShareExpired, ev.ShareOwner, ev.ItemID, ev.ShareID, ev.ExpiredAt, locale) case events.ShareRemoved: - return ul.shareMessage(event.Id, ShareRemoved, ev.Executant, ev.ItemID, ev.ShareID, ev.Timestamp) + return ul.shareMessage(event.Id, ShareRemoved, ev.Executant, ev.ItemID, ev.ShareID, ev.Timestamp, locale) } } -func (ul *UserlogService) spaceDeletedMessage(eventid string, executant *user.UserId, spaceid string, spacename string, ts time.Time) (OC10Notification, error) { +func (ul *UserlogService) spaceDeletedMessage(eventid string, executant *user.UserId, spaceid string, spacename string, ts time.Time, locale string) (OC10Notification, error) { _, user, err := utils.Impersonate(executant, ul.gwClient, ul.cfg.MachineAuthAPIKey) if err != nil { return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := ul.composeMessage(SpaceDeleted, map[string]string{ + subj, subjraw, msg, msgraw, err := ul.composeMessage(SpaceDeleted, locale, map[string]interface{}{ "username": user.GetDisplayName(), "spacename": spacename, }) @@ -110,7 +114,7 @@ func (ul *UserlogService) spaceDeletedMessage(eventid string, executant *user.Us }, nil } -func (ul *UserlogService) spaceMessage(eventid string, eventname string, executant *user.UserId, spaceid string, ts time.Time) (OC10Notification, error) { +func (ul *UserlogService) spaceMessage(eventid string, nt NotificationTemplate, executant *user.UserId, spaceid string, ts time.Time, locale string) (OC10Notification, error) { ctx, user, err := utils.Impersonate(executant, ul.gwClient, ul.cfg.MachineAuthAPIKey) if err != nil { return OC10Notification{}, err @@ -121,7 +125,7 @@ func (ul *UserlogService) spaceMessage(eventid string, eventname string, executa return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := ul.composeMessage(eventname, map[string]string{ + subj, subjraw, msg, msgraw, err := ul.composeMessage(nt, locale, map[string]interface{}{ "username": user.GetDisplayName(), "spacename": space.GetName(), }) @@ -144,7 +148,7 @@ func (ul *UserlogService) spaceMessage(eventid string, eventname string, executa }, nil } -func (ul *UserlogService) shareMessage(eventid string, eventname string, executant *user.UserId, resourceid *storageprovider.ResourceId, shareid *collaboration.ShareId, ts time.Time) (OC10Notification, error) { +func (ul *UserlogService) shareMessage(eventid string, nt NotificationTemplate, executant *user.UserId, resourceid *storageprovider.ResourceId, shareid *collaboration.ShareId, ts time.Time, locale string) (OC10Notification, error) { ctx, user, err := utils.Impersonate(executant, ul.gwClient, ul.cfg.MachineAuthAPIKey) if err != nil { return OC10Notification{}, err @@ -155,7 +159,7 @@ func (ul *UserlogService) shareMessage(eventid string, eventname string, executa return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := ul.composeMessage(eventname, map[string]string{ + subj, subjraw, msg, msgraw, err := ul.composeMessage(nt, locale, map[string]interface{}{ "username": user.GetDisplayName(), "resourcename": info.GetName(), }) @@ -178,23 +182,23 @@ func (ul *UserlogService) shareMessage(eventid string, eventname string, executa }, nil } -func (ul *UserlogService) composeMessage(eventname string, vars map[string]string) (string, string, string, string, error) { - tpl, ok := _templates[eventname] - if !ok { - return "", "", "", "", errors.New("unknown template name") +func (ul *UserlogService) composeMessage(nt NotificationTemplate, locale string, vars map[string]interface{}) (string, string, string, string, error) { + subj, msg, err := ul.parseTemplate(nt, locale) + if err != nil { + return "", "", "", "", err } - subject := ul.executeTemplate(tpl.Subject, vars) + subject := ul.executeTemplate(subj, vars) - subjectraw := ul.executeTemplate(tpl.Subject, map[string]string{ + subjectraw := ul.executeTemplate(subj, map[string]interface{}{ "username": "{user}", "spacename": "{space}", "resourcename": "{resource}", }) - message := ul.executeTemplate(tpl.Message, vars) + message := ul.executeTemplate(msg, vars) - messageraw := ul.executeTemplate(tpl.Message, map[string]string{ + messageraw := ul.executeTemplate(msg, map[string]interface{}{ "username": "{user}", "spacename": "{space}", "resourcename": "{resource}", @@ -238,7 +242,22 @@ func (ul *UserlogService) getDetails(user *user.User, space *storageprovider.Sto return details } -func (ul *UserlogService) executeTemplate(tpl *template.Template, vars map[string]string) string { +func (ul *UserlogService) parseTemplate(nt NotificationTemplate, locale string) (*template.Template, *template.Template, error) { + // Create Locale with library path and language code and load domain '.../default.po' + l := gotext.NewLocale(_pathToLocales, locale) + l.AddDomain("default") + + subject, err := template.New("").Parse(l.Get(nt.Subject)) + if err != nil { + return nil, nil, err + } + + message, err := template.New("").Parse(l.Get(nt.Message)) + return subject, message, err + +} + +func (ul *UserlogService) executeTemplate(tpl *template.Template, vars map[string]interface{}) string { var writer bytes.Buffer if err := tpl.Execute(&writer, vars); err != nil { ul.log.Error().Err(err).Str("templateName", tpl.Name()).Msg("cannot execute template") diff --git a/services/userlog/pkg/service/http.go b/services/userlog/pkg/service/http.go index 725275e04d4..96c2fbe4b74 100644 --- a/services/userlog/pkg/service/http.go +++ b/services/userlog/pkg/service/http.go @@ -28,9 +28,11 @@ func (ul *UserlogService) HandleGetEvents(w http.ResponseWriter, r *http.Request return } + locale := r.Header.Get("Prefered-Language") + resp := GetEventResponseOC10{} for _, e := range evs { - noti, err := ul.ConvertEvent(e) + noti, err := ul.ConvertEvent(e, locale) if err != nil { ul.log.Error().Err(err).Str("eventid", e.Id).Str("eventtype", e.Type).Msg("failed to convert event") continue diff --git a/services/userlog/pkg/service/templates.go b/services/userlog/pkg/service/templates.go index bc9bf33d2b7..45258fb0f30 100644 --- a/services/userlog/pkg/service/templates.go +++ b/services/userlog/pkg/service/templates.go @@ -1,65 +1,50 @@ package service -import "text/template" - // the available templates var ( - SpaceShared = "space-shared" - SpaceSharedSubject = "Space shared" - SpaceSharedMessage = "{{ .username }} added you to Space {{ .spacename }}" - - SpaceUnshared = "space-unshared" - SpaceUnsharedSubject = "Removed from Space" - SpaceUnsharedMessage = "{{ .username }} removed you from Space {{ .spacename }}" + SpaceShared = NotificationTemplate{ + Subject: "Space shared", + Message: "{{ .username }} added you to Space {{ .spacename }}", + } - SpaceDisabled = "space-disabled" - SpaceDisabledSubject = "Space disabled" - SpaceDisabledMessage = "{{ .username }} disabled Space {{ .spacename }}" + SpaceUnshared = NotificationTemplate{ + Subject: "Removed from Space", + Message: "{{ .username }} removed you from Space {{ .spacename }}", + } - SpaceDeleted = "space-deleted" - SpaceDeletedSubject = "Space deleted" - SpaceDeletedMessage = "{{ .username }} deleted Space {{ .spacename }}" + SpaceDisabled = NotificationTemplate{ + Subject: "Space disabled", + Message: "{{ .username }} disabled Space {{ .spacename }}", + } - SpaceMembershipExpired = "space-membership-expired" - SpaceMembershipExpiredSubject = "Membership expired" - SpaceMembershipExpiredMessage = "Access to Space {{ .spacename }} lost" + SpaceDeleted = NotificationTemplate{ + Subject: "Space deleted", + Message: "{{ .username }} deleted Space {{ .spacename }}", + } - ShareCreated = "item-shared" - ShareCreatedSubject = "Resource shared" - ShareCreatedMessage = "{{ .username }} shared {{ .resourcename }} with you" + SpaceMembershipExpired = NotificationTemplate{ + Subject: "Membership expired", + Message: "Access to Space {{ .spacename }} lost", + } - ShareRemoved = "item-unshared" - ShareRemovedSubject = "Resource unshared" - ShareRemovedMessage = "{{ .username }} unshared {{ .resourcename }} with you" + ShareCreated = NotificationTemplate{ + Subject: "Resource shared", + Message: "{{ .username }} shared {{ .resourcename }} with you", + } - ShareExpired = "share-expired" - ShareExpiredSubject = "Share expired" - ShareExpiredMessage = "Access to {{ .resourcename }} expired" -) + ShareRemoved = NotificationTemplate{ + Subject: "Resource unshared", + Message: "{{ .username }} unshared {{ .resourcename }} with you", + } -// rendered templates -var ( - _templates = map[string]NotificationTemplate{ - SpaceShared: notiTmpl(SpaceSharedSubject, SpaceSharedMessage), - SpaceUnshared: notiTmpl(SpaceUnsharedSubject, SpaceUnsharedMessage), - SpaceDisabled: notiTmpl(SpaceDisabledSubject, SpaceDisabledMessage), - SpaceDeleted: notiTmpl(SpaceDeletedSubject, SpaceDeletedMessage), - SpaceMembershipExpired: notiTmpl(SpaceMembershipExpiredSubject, SpaceMembershipExpiredMessage), - ShareCreated: notiTmpl(ShareCreatedSubject, ShareCreatedMessage), - ShareRemoved: notiTmpl(ShareRemovedSubject, ShareRemovedMessage), - ShareExpired: notiTmpl(ShareExpiredSubject, ShareExpiredMessage), + ShareExpired = NotificationTemplate{ + Subject: "Share expired", + Message: "Access to {{ .resourcename }} expired", } ) // NotificationTemplate is the data structure for the notifications type NotificationTemplate struct { - Subject *template.Template - Message *template.Template -} - -func notiTmpl(subjectname string, messagename string) NotificationTemplate { - return NotificationTemplate{ - Subject: template.Must(template.New("").Parse(subjectname)), - Message: template.Must(template.New("").Parse(messagename)), - } + Subject string + Message string } From b5ac1cd941969efdf0dc5bfca952c43d8f1a148c Mon Sep 17 00:00:00 2001 From: jkoberg Date: Tue, 7 Mar 2023 14:47:04 +0100 Subject: [PATCH 03/14] minimize request while rendering events Signed-off-by: jkoberg --- services/userlog/pkg/service/conversion.go | 239 ++++++++++++++------- services/userlog/pkg/service/http.go | 7 +- services/userlog/pkg/service/service.go | 57 +++-- services/userlog/pkg/service/templates.go | 7 + 4 files changed, 213 insertions(+), 97 deletions(-) diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go index 5f821068ef7..3b79e1c0a8d 100644 --- a/services/userlog/pkg/service/conversion.go +++ b/services/userlog/pkg/service/conversion.go @@ -2,10 +2,12 @@ package service import ( "bytes" + "context" "errors" "text/template" "time" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" @@ -40,9 +42,39 @@ type OC10Notification struct { MessageDetails map[string]interface{} `json:"messageRichParameters"` } +// Converter is responsible for converting eventhistory events to OC10Notifications +type Converter struct { + locale string + gwClient gateway.GatewayAPIClient + machineAuthAPIKey string + serviceName string + registeredEvents map[string]events.Unmarshaller + + // cached within one request not query other service too much + spaces map[string]*storageprovider.StorageSpace + users map[string]*user.User + resources map[string]*storageprovider.ResourceInfo + contexts map[string]context.Context +} + +// NewConverter returns a new Converter +func NewConverter(loc string, gwc gateway.GatewayAPIClient, machineAuthAPIKey string, name string, registeredEvents map[string]events.Unmarshaller) *Converter { + return &Converter{ + locale: loc, + gwClient: gwc, + machineAuthAPIKey: machineAuthAPIKey, + serviceName: name, + registeredEvents: registeredEvents, + spaces: make(map[string]*storageprovider.StorageSpace), + users: make(map[string]*user.User), + resources: make(map[string]*storageprovider.ResourceInfo), + contexts: make(map[string]context.Context), + } +} + // ConvertEvent converts an eventhistory event to an OC10Notification -func (ul *UserlogService) ConvertEvent(event *ehmsg.Event, locale string) (OC10Notification, error) { - etype, ok := ul.registeredEvents[event.Type] +func (c *Converter) ConvertEvent(event *ehmsg.Event) (OC10Notification, error) { + etype, ok := c.registeredEvents[event.Type] if !ok { // this should not happen return OC10Notification{}, errors.New("eventtype not registered") @@ -59,50 +91,46 @@ func (ul *UserlogService) ConvertEvent(event *ehmsg.Event, locale string) (OC10N return OC10Notification{}, errors.New("unknown event type") // space related case events.SpaceDisabled: - return ul.spaceMessage(event.Id, SpaceDisabled, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp, locale) + return c.spaceMessage(event.Id, SpaceDisabled, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp) case events.SpaceDeleted: - return ul.spaceDeletedMessage(event.Id, ev.Executant, ev.ID.GetOpaqueId(), ev.SpaceName, ev.Timestamp, locale) + return c.spaceDeletedMessage(event.Id, ev.Executant, ev.ID.GetOpaqueId(), ev.SpaceName, ev.Timestamp) case events.SpaceShared: - return ul.spaceMessage(event.Id, SpaceShared, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp, locale) + return c.spaceMessage(event.Id, SpaceShared, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp) case events.SpaceUnshared: - return ul.spaceMessage(event.Id, SpaceUnshared, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp, locale) + return c.spaceMessage(event.Id, SpaceUnshared, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp) case events.SpaceMembershipExpired: - return ul.spaceMessage(event.Id, SpaceMembershipExpired, ev.SpaceOwner, ev.SpaceID.GetOpaqueId(), ev.ExpiredAt, locale) + return c.spaceMessage(event.Id, SpaceMembershipExpired, ev.SpaceOwner, ev.SpaceID.GetOpaqueId(), ev.ExpiredAt) // share related case events.ShareCreated: - return ul.shareMessage(event.Id, ShareCreated, ev.Executant, ev.ItemID, ev.ShareID, utils.TSToTime(ev.CTime), locale) + return c.shareMessage(event.Id, ShareCreated, ev.Executant, ev.ItemID, ev.ShareID, utils.TSToTime(ev.CTime)) case events.ShareExpired: - return ul.shareMessage(event.Id, ShareExpired, ev.ShareOwner, ev.ItemID, ev.ShareID, ev.ExpiredAt, locale) + return c.shareMessage(event.Id, ShareExpired, ev.ShareOwner, ev.ItemID, ev.ShareID, ev.ExpiredAt) case events.ShareRemoved: - return ul.shareMessage(event.Id, ShareRemoved, ev.Executant, ev.ItemID, ev.ShareID, ev.Timestamp, locale) + return c.shareMessage(event.Id, ShareRemoved, ev.Executant, ev.ItemID, ev.ShareID, ev.Timestamp) } } -func (ul *UserlogService) spaceDeletedMessage(eventid string, executant *user.UserId, spaceid string, spacename string, ts time.Time, locale string) (OC10Notification, error) { - _, user, err := utils.Impersonate(executant, ul.gwClient, ul.cfg.MachineAuthAPIKey) +func (c *Converter) spaceDeletedMessage(eventid string, executant *user.UserId, spaceid string, spacename string, ts time.Time) (OC10Notification, error) { + usr, err := c.getUser(context.Background(), executant) if err != nil { return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := ul.composeMessage(SpaceDeleted, locale, map[string]interface{}{ - "username": user.GetDisplayName(), + subj, subjraw, msg, msgraw, err := composeMessage(SpaceDeleted, c.locale, map[string]interface{}{ + "username": usr.GetDisplayName(), "spacename": spacename, }) if err != nil { return OC10Notification{}, err } - details := ul.getDetails(user, nil, nil, nil) - details["space"] = map[string]string{ - "id": spaceid, - "name": spacename, - } + space := &storageprovider.StorageSpace{Id: &storageprovider.StorageSpaceId{OpaqueId: spaceid}, Name: spacename} return OC10Notification{ EventID: eventid, - Service: ul.cfg.Service.Name, - UserName: user.GetUsername(), + Service: c.serviceName, + UserName: usr.GetUsername(), Timestamp: ts.Format(time.RFC3339Nano), ResourceID: spaceid, ResourceType: _resourceTypeSpace, @@ -110,23 +138,28 @@ func (ul *UserlogService) spaceDeletedMessage(eventid string, executant *user.Us SubjectRaw: subjraw, Message: msg, MessageRaw: msgraw, - MessageDetails: details, + MessageDetails: generateDetails(usr, space, nil, nil), }, nil } -func (ul *UserlogService) spaceMessage(eventid string, nt NotificationTemplate, executant *user.UserId, spaceid string, ts time.Time, locale string) (OC10Notification, error) { - ctx, user, err := utils.Impersonate(executant, ul.gwClient, ul.cfg.MachineAuthAPIKey) +func (c *Converter) spaceMessage(eventid string, nt NotificationTemplate, executant *user.UserId, spaceid string, ts time.Time) (OC10Notification, error) { + usr, err := c.getUser(context.Background(), executant) + if err != nil { + return OC10Notification{}, err + } + + ctx, err := c.authenticate(usr) if err != nil { return OC10Notification{}, err } - space, err := ul.getSpace(ctx, spaceid) + space, err := c.getSpace(ctx, spaceid) if err != nil { return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := ul.composeMessage(nt, locale, map[string]interface{}{ - "username": user.GetDisplayName(), + subj, subjraw, msg, msgraw, err := composeMessage(nt, c.locale, map[string]interface{}{ + "username": usr.GetDisplayName(), "spacename": space.GetName(), }) if err != nil { @@ -135,8 +168,8 @@ func (ul *UserlogService) spaceMessage(eventid string, nt NotificationTemplate, return OC10Notification{ EventID: eventid, - Service: ul.cfg.Service.Name, - UserName: user.GetUsername(), + Service: c.serviceName, + UserName: usr.GetUsername(), Timestamp: ts.Format(time.RFC3339Nano), ResourceID: spaceid, ResourceType: _resourceTypeSpace, @@ -144,23 +177,28 @@ func (ul *UserlogService) spaceMessage(eventid string, nt NotificationTemplate, SubjectRaw: subjraw, Message: msg, MessageRaw: msgraw, - MessageDetails: ul.getDetails(user, space, nil, nil), + MessageDetails: generateDetails(usr, space, nil, nil), }, nil } -func (ul *UserlogService) shareMessage(eventid string, nt NotificationTemplate, executant *user.UserId, resourceid *storageprovider.ResourceId, shareid *collaboration.ShareId, ts time.Time, locale string) (OC10Notification, error) { - ctx, user, err := utils.Impersonate(executant, ul.gwClient, ul.cfg.MachineAuthAPIKey) +func (c *Converter) shareMessage(eventid string, nt NotificationTemplate, executant *user.UserId, resourceid *storageprovider.ResourceId, shareid *collaboration.ShareId, ts time.Time) (OC10Notification, error) { + usr, err := c.getUser(context.Background(), executant) if err != nil { return OC10Notification{}, err } - info, err := ul.getResource(ctx, resourceid) + ctx, err := c.authenticate(usr) if err != nil { return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := ul.composeMessage(nt, locale, map[string]interface{}{ - "username": user.GetDisplayName(), + info, err := c.getResource(ctx, resourceid) + if err != nil { + return OC10Notification{}, err + } + + subj, subjraw, msg, msgraw, err := composeMessage(nt, c.locale, map[string]interface{}{ + "username": usr.GetDisplayName(), "resourcename": info.GetName(), }) if err != nil { @@ -169,8 +207,8 @@ func (ul *UserlogService) shareMessage(eventid string, nt NotificationTemplate, return OC10Notification{ EventID: eventid, - Service: ul.cfg.Service.Name, - UserName: user.GetUsername(), + Service: c.serviceName, + UserName: usr.GetUsername(), Timestamp: ts.Format(time.RFC3339Nano), ResourceID: storagespace.FormatResourceID(*info.GetId()), ResourceType: _resourceTypeShare, @@ -178,37 +216,105 @@ func (ul *UserlogService) shareMessage(eventid string, nt NotificationTemplate, SubjectRaw: subjraw, Message: msg, MessageRaw: msgraw, - MessageDetails: ul.getDetails(user, nil, info, shareid), + MessageDetails: generateDetails(usr, nil, info, shareid), }, nil } -func (ul *UserlogService) composeMessage(nt NotificationTemplate, locale string, vars map[string]interface{}) (string, string, string, string, error) { - subj, msg, err := ul.parseTemplate(nt, locale) +func (c *Converter) authenticate(usr *user.User) (context.Context, error) { + if ctx, ok := c.contexts[usr.GetId().GetOpaqueId()]; ok { + return ctx, nil + } + ctx, err := authenticate(usr, c.gwClient, c.machineAuthAPIKey) + if err == nil { + c.contexts[usr.GetId().GetOpaqueId()] = ctx + } + return ctx, err +} + +func (c *Converter) getSpace(ctx context.Context, spaceID string) (*storageprovider.StorageSpace, error) { + if space, ok := c.spaces[spaceID]; ok { + return space, nil + } + space, err := getSpace(ctx, spaceID, c.gwClient) + if err == nil { + c.spaces[spaceID] = space + } + return space, err +} + +func (c *Converter) getResource(ctx context.Context, resourceID *storageprovider.ResourceId) (*storageprovider.ResourceInfo, error) { + if r, ok := c.resources[resourceID.GetOpaqueId()]; ok { + return r, nil + } + resource, err := getResource(ctx, resourceID, c.gwClient) + if err == nil { + c.resources[resourceID.GetOpaqueId()] = resource + } + return resource, err +} + +func (c *Converter) getUser(ctx context.Context, userID *user.UserId) (*user.User, error) { + if u, ok := c.users[userID.GetOpaqueId()]; ok { + return u, nil + } + usr, err := getUser(ctx, userID, c.gwClient) + if err == nil { + c.users[userID.GetOpaqueId()] = usr + } + return usr, err +} + +func composeMessage(nt NotificationTemplate, locale string, vars map[string]interface{}) (string, string, string, string, error) { + subj, msg, err := parseTemplate(nt, locale) if err != nil { return "", "", "", "", err } - subject := ul.executeTemplate(subj, vars) + subject, err := executeTemplate(subj, vars) + if err != nil { + return "", "", "", "", err + } - subjectraw := ul.executeTemplate(subj, map[string]interface{}{ - "username": "{user}", - "spacename": "{space}", - "resourcename": "{resource}", - }) + subjectraw, err := executeTemplate(subj, _placeholders) + if err != nil { + return "", "", "", "", err + } + + message, err := executeTemplate(msg, vars) + if err != nil { + return "", "", "", "", err + } - message := ul.executeTemplate(msg, vars) + messageraw, err := executeTemplate(msg, _placeholders) + return subject, subjectraw, message, messageraw, err - messageraw := ul.executeTemplate(msg, map[string]interface{}{ - "username": "{user}", - "spacename": "{space}", - "resourcename": "{resource}", - }) +} + +func parseTemplate(nt NotificationTemplate, locale string) (*template.Template, *template.Template, error) { + // Create Locale with library path and language code and load domain '.../default.po' + l := gotext.NewLocale(_pathToLocales, locale) + l.AddDomain("default") + + subject, err := template.New("").Parse(l.Get(nt.Subject)) + if err != nil { + return nil, nil, err + } + + message, err := template.New("").Parse(l.Get(nt.Message)) + return subject, message, err - return subject, subjectraw, message, messageraw, nil +} + +func executeTemplate(tpl *template.Template, vars map[string]interface{}) (string, error) { + var writer bytes.Buffer + if err := tpl.Execute(&writer, vars); err != nil { + return "", err + } + return writer.String(), nil } -func (ul *UserlogService) getDetails(user *user.User, space *storageprovider.StorageSpace, item *storageprovider.ResourceInfo, shareid *collaboration.ShareId) map[string]interface{} { +func generateDetails(user *user.User, space *storageprovider.StorageSpace, item *storageprovider.ResourceInfo, shareid *collaboration.ShareId) map[string]interface{} { details := make(map[string]interface{}) if user != nil { @@ -241,28 +347,3 @@ func (ul *UserlogService) getDetails(user *user.User, space *storageprovider.Sto return details } - -func (ul *UserlogService) parseTemplate(nt NotificationTemplate, locale string) (*template.Template, *template.Template, error) { - // Create Locale with library path and language code and load domain '.../default.po' - l := gotext.NewLocale(_pathToLocales, locale) - l.AddDomain("default") - - subject, err := template.New("").Parse(l.Get(nt.Subject)) - if err != nil { - return nil, nil, err - } - - message, err := template.New("").Parse(l.Get(nt.Message)) - return subject, message, err - -} - -func (ul *UserlogService) executeTemplate(tpl *template.Template, vars map[string]interface{}) string { - var writer bytes.Buffer - if err := tpl.Execute(&writer, vars); err != nil { - ul.log.Error().Err(err).Str("templateName", tpl.Name()).Msg("cannot execute template") - return "" - } - - return writer.String() -} diff --git a/services/userlog/pkg/service/http.go b/services/userlog/pkg/service/http.go index 96c2fbe4b74..ffcbdfaef3e 100644 --- a/services/userlog/pkg/service/http.go +++ b/services/userlog/pkg/service/http.go @@ -7,6 +7,9 @@ import ( revactx "github.com/cs3org/reva/v2/pkg/ctx" ) +// HeaderPreferedLanguage is the header where the client can set the locale +var HeaderPreferedLanguage = "Prefered-Language" + // ServeHTTP fulfills Handler interface func (ul *UserlogService) ServeHTTP(w http.ResponseWriter, r *http.Request) { ul.m.ServeHTTP(w, r) @@ -28,11 +31,11 @@ func (ul *UserlogService) HandleGetEvents(w http.ResponseWriter, r *http.Request return } - locale := r.Header.Get("Prefered-Language") + conv := NewConverter(r.Header.Get(HeaderPreferedLanguage), ul.gwClient, ul.cfg.MachineAuthAPIKey, ul.cfg.Service.Name, ul.registeredEvents) resp := GetEventResponseOC10{} for _, e := range evs { - noti, err := ul.ConvertEvent(e, locale) + noti, err := conv.ConvertEvent(e) if err != nil { ul.log.Error().Err(err).Str("eventid", e.Id).Str("eventtype", e.Type).Msg("failed to convert event") continue diff --git a/services/userlog/pkg/service/service.go b/services/userlog/pkg/service/service.go index 3a8e8559974..f6132e5e383 100644 --- a/services/userlog/pkg/service/service.go +++ b/services/userlog/pkg/service/service.go @@ -12,6 +12,7 @@ import ( user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/utils" "github.com/go-chi/chi/v5" @@ -20,6 +21,7 @@ import ( ehsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/eventhistory/v0" "github.com/owncloud/ocis/v2/services/userlog/pkg/config" "go-micro.dev/v4/store" + "google.golang.org/grpc/metadata" ) // UserlogService is the service responsible for user activities @@ -91,7 +93,7 @@ func (ul *UserlogService) MemorizeEvents(ch <-chan events.Event) { case events.SpaceDisabled: users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ID.GetOpaqueId(), viewer) case events.SpaceDeleted: - for u, _ := range e.FinalMembers { + for u := range e.FinalMembers { users = append(users, u) } case events.SpaceShared: @@ -251,7 +253,7 @@ func (ul *UserlogService) findSpaceMembers(ctx context.Context, spaceID string, return nil, errors.New("need authenticated context to find space members") } - space, err := ul.getSpace(ctx, spaceID) + space, err := getSpace(ctx, spaceID, ul.gwClient) if err != nil { return nil, err } @@ -325,7 +327,7 @@ func (ul *UserlogService) resolveID(ctx context.Context, userid *user.UserId, gr // resolves the users of a group func (ul *UserlogService) resolveGroup(ctx context.Context, groupID string) ([]string, error) { - grp, err := ul.getGroup(ctx, groupID) + grp, err := getGroup(ctx, groupID, ul.gwClient) if err != nil { return nil, err } @@ -338,22 +340,45 @@ func (ul *UserlogService) resolveGroup(ctx context.Context, groupID string) ([]s return userIDs, nil } -func (ul *UserlogService) impersonate(u *user.UserId) context.Context { - if u == nil { - ul.log.Debug().Msg("cannot impersonate nil user") +func (ul *UserlogService) impersonate(uid *user.UserId) context.Context { + if uid == nil { + ul.log.Error().Msg("cannot impersonate nil user") return nil } - ctx, _, err := utils.Impersonate(u, ul.gwClient, ul.cfg.MachineAuthAPIKey) + u, err := getUser(context.Background(), uid, ul.gwClient) if err != nil { - ul.log.Error().Err(err).Str("userid", u.GetOpaqueId()).Msg("failed to impersonate user") + ul.log.Error().Err(err).Msg("cannot get user") + return nil + } + + ctx, err := authenticate(u, ul.gwClient, ul.cfg.MachineAuthAPIKey) + if err != nil { + ul.log.Error().Err(err).Str("userid", u.GetId().GetOpaqueId()).Msg("failed to impersonate user") return nil } return ctx } -func (ul *UserlogService) getSpace(ctx context.Context, spaceID string) (*storageprovider.StorageSpace, error) { - res, err := ul.gwClient.ListStorageSpaces(ctx, listStorageSpaceRequest(spaceID)) +func authenticate(usr *user.User, gwc gateway.GatewayAPIClient, machineAuthAPIKey string) (context.Context, error) { + ctx := revactx.ContextSetUser(context.Background(), usr) + authRes, err := gwc.Authenticate(ctx, &gateway.AuthenticateRequest{ + Type: "machine", + ClientId: "userid:" + usr.GetId().GetOpaqueId(), + ClientSecret: machineAuthAPIKey, + }) + if err != nil { + return nil, err + } + if authRes.GetStatus().GetCode() != rpc.Code_CODE_OK { + return nil, fmt.Errorf("error impersonating user: %s", authRes.Status.Message) + } + + return metadata.AppendToOutgoingContext(ctx, revactx.TokenHeader, authRes.Token), nil +} + +func getSpace(ctx context.Context, spaceID string, gwc gateway.GatewayAPIClient) (*storageprovider.StorageSpace, error) { + res, err := gwc.ListStorageSpaces(ctx, listStorageSpaceRequest(spaceID)) if err != nil { return nil, err } @@ -369,8 +394,8 @@ func (ul *UserlogService) getSpace(ctx context.Context, spaceID string) (*storag return res.StorageSpaces[0], nil } -func (ul *UserlogService) getUser(ctx context.Context, userid *user.UserId) (*user.User, error) { - getUserResponse, err := ul.gwClient.GetUser(context.Background(), &user.GetUserRequest{ +func getUser(ctx context.Context, userid *user.UserId, gwc gateway.GatewayAPIClient) (*user.User, error) { + getUserResponse, err := gwc.GetUser(context.Background(), &user.GetUserRequest{ UserId: userid, }) if err != nil { @@ -384,8 +409,8 @@ func (ul *UserlogService) getUser(ctx context.Context, userid *user.UserId) (*us return getUserResponse.GetUser(), nil } -func (ul *UserlogService) getGroup(ctx context.Context, groupid string) (*group.Group, error) { - r, err := ul.gwClient.GetGroup(ctx, &group.GetGroupRequest{GroupId: &group.GroupId{OpaqueId: groupid}}) +func getGroup(ctx context.Context, groupid string, gwc gateway.GatewayAPIClient) (*group.Group, error) { + r, err := gwc.GetGroup(ctx, &group.GetGroupRequest{GroupId: &group.GroupId{OpaqueId: groupid}}) if err != nil { return nil, err } @@ -397,8 +422,8 @@ func (ul *UserlogService) getGroup(ctx context.Context, groupid string) (*group. return r.GetGroup(), nil } -func (ul *UserlogService) getResource(ctx context.Context, resourceid *storageprovider.ResourceId) (*storageprovider.ResourceInfo, error) { - res, err := ul.gwClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: resourceid}}) +func getResource(ctx context.Context, resourceid *storageprovider.ResourceId, gwc gateway.GatewayAPIClient) (*storageprovider.ResourceInfo, error) { + res, err := gwc.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: resourceid}}) if err != nil { return nil, err } diff --git a/services/userlog/pkg/service/templates.go b/services/userlog/pkg/service/templates.go index 45258fb0f30..47bb74de615 100644 --- a/services/userlog/pkg/service/templates.go +++ b/services/userlog/pkg/service/templates.go @@ -43,6 +43,13 @@ var ( } ) +// holds the information to link the raw template to the details +var _placeholders = map[string]interface{}{ + "username": "{user}", + "spacename": "{space}", + "resourcename": "{resource}", +} + // NotificationTemplate is the data structure for the notifications type NotificationTemplate struct { Subject string From 4a21a7be5f63dc7bdc76eff4666e2fdda99866b3 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Wed, 8 Mar 2023 12:03:01 +0100 Subject: [PATCH 04/14] mark templates as translatable Signed-off-by: jkoberg --- go.mod | 2 ++ go.sum | 3 ++ services/userlog/Makefile | 3 ++ services/userlog/pkg/service/templates.go | 35 ++++++++++++----------- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index e14d5bd0005..3640683660e 100644 --- a/go.mod +++ b/go.mod @@ -191,6 +191,7 @@ require ( github.com/gookit/goutil v0.6.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/schema v1.2.0 // indirect + github.com/gosexy/gettext v0.0.0-20160830220431-74466a0a0c4a // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/hashicorp/consul/api v1.15.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -207,6 +208,7 @@ require ( github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jessevdk/go-flags v1.5.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/json-iterator/go v1.1.12 // indirect diff --git a/go.sum b/go.sum index f3ec4dab073..84e91e9e0c9 100644 --- a/go.sum +++ b/go.sum @@ -717,6 +717,8 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7 github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosexy/gettext v0.0.0-20160830220431-74466a0a0c4a h1:N2b2mb4Gki1SlF3WuhR9P1YHOpl7oy/b+xxX4A3iM2E= +github.com/gosexy/gettext v0.0.0-20160830220431-74466a0a0c4a/go.mod h1:IEJaV4/6J0VpoQ33kFCUUP6umRjrcBVEbOva6XCub/Q= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= @@ -815,6 +817,7 @@ github.com/jellydator/ttlcache/v2 v2.11.1/go.mod h1:RtE5Snf0/57e+2cLWFYWCCsLas2H github.com/jellydator/ttlcache/v3 v3.0.1 h1:cHgCSMS7TdQcoprXnWUptJZzyFsqs18Lt8VVhRuZYVU= github.com/jellydator/ttlcache/v3 v3.0.1/go.mod h1:WwTaEmcXQ3MTjOm4bsZoDFiCu/hMvNWLO1w67RXz6h4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= diff --git a/services/userlog/Makefile b/services/userlog/Makefile index 12a1d0a7d60..1051c48792f 100644 --- a/services/userlog/Makefile +++ b/services/userlog/Makefile @@ -30,6 +30,9 @@ ci-go-generate: $(MOCKERY) # CI runs ci-node-generate automatically before this .PHONY: ci-node-generate ci-node-generate: +############ translations ######## +include ../../.make/l10n.mk + ############ licenses ############ .PHONY: ci-node-check-licenses ci-node-check-licenses: diff --git a/services/userlog/pkg/service/templates.go b/services/userlog/pkg/service/templates.go index 47bb74de615..5d3983e9d51 100644 --- a/services/userlog/pkg/service/templates.go +++ b/services/userlog/pkg/service/templates.go @@ -1,45 +1,48 @@ package service +// Template marks the string as a translatable template +func Template(s string) string { return s } + // the available templates var ( SpaceShared = NotificationTemplate{ - Subject: "Space shared", - Message: "{{ .username }} added you to Space {{ .spacename }}", + Subject: Template("Space shared"), + Message: Template("{{ .username }} added you to Space {{ .spacename }}"), } SpaceUnshared = NotificationTemplate{ - Subject: "Removed from Space", - Message: "{{ .username }} removed you from Space {{ .spacename }}", + Subject: Template("Removed from Space"), + Message: Template("{{ .username }} removed you from Space {{ .spacename }}"), } SpaceDisabled = NotificationTemplate{ - Subject: "Space disabled", - Message: "{{ .username }} disabled Space {{ .spacename }}", + Subject: Template("Space disabled"), + Message: Template("{{ .username }} disabled Space {{ .spacename }}"), } SpaceDeleted = NotificationTemplate{ - Subject: "Space deleted", - Message: "{{ .username }} deleted Space {{ .spacename }}", + Subject: Template("Space deleted"), + Message: Template("{{ .username }} deleted Space {{ .spacename }}"), } SpaceMembershipExpired = NotificationTemplate{ - Subject: "Membership expired", - Message: "Access to Space {{ .spacename }} lost", + Subject: Template("Membership expired"), + Message: Template("Access to Space {{ .spacename }} lost"), } ShareCreated = NotificationTemplate{ - Subject: "Resource shared", - Message: "{{ .username }} shared {{ .resourcename }} with you", + Subject: Template("Resource shared"), + Message: Template("{{ .username }} shared {{ .resourcename }} with you"), } ShareRemoved = NotificationTemplate{ - Subject: "Resource unshared", - Message: "{{ .username }} unshared {{ .resourcename }} with you", + Subject: Template("Resource unshared"), + Message: Template("{{ .username }} unshared {{ .resourcename }} with you"), } ShareExpired = NotificationTemplate{ - Subject: "Share expired", - Message: "Access to {{ .resourcename }} expired", + Subject: Template("Share expired"), + Message: Template("Access to {{ .resourcename }} expired"), } ) From b3bd2dc7c4ee175484264c3b2353310b40bfc68c Mon Sep 17 00:00:00 2001 From: jkoberg Date: Wed, 8 Mar 2023 12:39:18 +0100 Subject: [PATCH 05/14] reverse template parsing logic Signed-off-by: jkoberg --- services/userlog/pkg/service/conversion.go | 44 ++++++++-------------- services/userlog/pkg/service/templates.go | 26 ++++++------- 2 files changed, 29 insertions(+), 41 deletions(-) diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go index 3b79e1c0a8d..ec182462b01 100644 --- a/services/userlog/pkg/service/conversion.go +++ b/services/userlog/pkg/service/conversion.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "errors" + "strings" "text/template" "time" @@ -24,6 +25,7 @@ var ( // TODO: from config _pathToLocales = "/home/jkoberg/ocis/services/userlog/pkg/service/locales" + _domain = "default" ) // OC10Notification is the oc10 style representation of an event @@ -265,47 +267,33 @@ func (c *Converter) getUser(ctx context.Context, userID *user.UserId) (*user.Use } func composeMessage(nt NotificationTemplate, locale string, vars map[string]interface{}) (string, string, string, string, error) { - subj, msg, err := parseTemplate(nt, locale) - if err != nil { - return "", "", "", "", err - } - - subject, err := executeTemplate(subj, vars) - if err != nil { - return "", "", "", "", err - } - - subjectraw, err := executeTemplate(subj, _placeholders) - if err != nil { - return "", "", "", "", err - } + subjectraw, messageraw := loadTemplates(nt, locale) - message, err := executeTemplate(msg, vars) + subject, err := executeTemplate(subjectraw, vars) if err != nil { return "", "", "", "", err } - messageraw, err := executeTemplate(msg, _placeholders) + message, err := executeTemplate(messageraw, vars) return subject, subjectraw, message, messageraw, err } -func parseTemplate(nt NotificationTemplate, locale string) (*template.Template, *template.Template, error) { - // Create Locale with library path and language code and load domain '.../default.po' +func loadTemplates(nt NotificationTemplate, locale string) (string, string) { + // Create Locale with library path and language code and load default domain l := gotext.NewLocale(_pathToLocales, locale) - l.AddDomain("default") + l.AddDomain(_domain) + return l.Get(nt.Subject), l.Get(nt.Message) +} - subject, err := template.New("").Parse(l.Get(nt.Subject)) +func executeTemplate(raw string, vars map[string]interface{}) (string, error) { + for o, n := range _placeholders { + raw = strings.ReplaceAll(raw, o, n) + } + tpl, err := template.New("").Parse(raw) if err != nil { - return nil, nil, err + return "", err } - - message, err := template.New("").Parse(l.Get(nt.Message)) - return subject, message, err - -} - -func executeTemplate(tpl *template.Template, vars map[string]interface{}) (string, error) { var writer bytes.Buffer if err := tpl.Execute(&writer, vars); err != nil { return "", err diff --git a/services/userlog/pkg/service/templates.go b/services/userlog/pkg/service/templates.go index 5d3983e9d51..412b6ee4efb 100644 --- a/services/userlog/pkg/service/templates.go +++ b/services/userlog/pkg/service/templates.go @@ -7,50 +7,50 @@ func Template(s string) string { return s } var ( SpaceShared = NotificationTemplate{ Subject: Template("Space shared"), - Message: Template("{{ .username }} added you to Space {{ .spacename }}"), + Message: Template("{user} added you to Space {space}"), } SpaceUnshared = NotificationTemplate{ Subject: Template("Removed from Space"), - Message: Template("{{ .username }} removed you from Space {{ .spacename }}"), + Message: Template("{user} removed you from Space {space}"), } SpaceDisabled = NotificationTemplate{ Subject: Template("Space disabled"), - Message: Template("{{ .username }} disabled Space {{ .spacename }}"), + Message: Template("{user} disabled Space {space}"), } SpaceDeleted = NotificationTemplate{ Subject: Template("Space deleted"), - Message: Template("{{ .username }} deleted Space {{ .spacename }}"), + Message: Template("{user} deleted Space {space}"), } SpaceMembershipExpired = NotificationTemplate{ Subject: Template("Membership expired"), - Message: Template("Access to Space {{ .spacename }} lost"), + Message: Template("Access to Space {space} lost"), } ShareCreated = NotificationTemplate{ Subject: Template("Resource shared"), - Message: Template("{{ .username }} shared {{ .resourcename }} with you"), + Message: Template("{user} shared {resource} with you"), } ShareRemoved = NotificationTemplate{ Subject: Template("Resource unshared"), - Message: Template("{{ .username }} unshared {{ .resourcename }} with you"), + Message: Template("{user} unshared {resource} with you"), } ShareExpired = NotificationTemplate{ Subject: Template("Share expired"), - Message: Template("Access to {{ .resourcename }} expired"), + Message: Template("Access to {resource} expired"), } ) -// holds the information to link the raw template to the details -var _placeholders = map[string]interface{}{ - "username": "{user}", - "spacename": "{space}", - "resourcename": "{resource}", +// holds the information to turn the raw template into a parseable go template +var _placeholders = map[string]string{ + "{user}": "{{ .username }}", + "{space}": "{{ .spacename }}", + "{resource}": "{{ .resourcename }}", } // NotificationTemplate is the data structure for the notifications From cfca9ec4b1d0dd08e3ce8ec797d81dd4c4cf227b Mon Sep 17 00:00:00 2001 From: jkoberg Date: Wed, 8 Mar 2023 16:27:24 +0100 Subject: [PATCH 06/14] adjust makefile Signed-off-by: jkoberg --- services/userlog/Makefile | 12 +++++++++++- services/userlog/pkg/service/conversion.go | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/services/userlog/Makefile b/services/userlog/Makefile index 1051c48792f..ee92367c812 100644 --- a/services/userlog/Makefile +++ b/services/userlog/Makefile @@ -31,7 +31,17 @@ ci-go-generate: $(MOCKERY) # CI runs ci-node-generate automatically before this ci-node-generate: ############ translations ######## -include ../../.make/l10n.mk +.PHONY: l10n-pull +l10n-pull: + cd $(OUTPUT_DIR) && tx pull -a --skip --minimum-perc=75 + +.PHONY: l10n-push +l10n-push: + cd $(OUTPUT_DIR) && tx push -s --skip + +.PHONY: l10n-read +l10n-read: + go-xgettext -o $(OUTPUT_DIR)/locale/en/LC_MESSAGES/default.pot --keyword=Template -s pkg/service/templates.go ############ licenses ############ .PHONY: ci-node-check-licenses diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go index ec182462b01..5d5b0b6bcee 100644 --- a/services/userlog/pkg/service/conversion.go +++ b/services/userlog/pkg/service/conversion.go @@ -52,7 +52,7 @@ type Converter struct { serviceName string registeredEvents map[string]events.Unmarshaller - // cached within one request not query other service too much + // cached within one request not to query other service too much spaces map[string]*storageprovider.StorageSpace users map[string]*user.User resources map[string]*storageprovider.ResourceInfo From 1fc2bbcb08af52fab0bf84a021e378d9c665458a Mon Sep 17 00:00:00 2001 From: jkoberg Date: Thu, 9 Mar 2023 15:16:59 +0100 Subject: [PATCH 07/14] embed translation on startup Signed-off-by: jkoberg --- services/userlog/pkg/service/conversion.go | 8 ++++++-- services/userlog/pkg/service/service.go | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go index 5d5b0b6bcee..f45f116dac3 100644 --- a/services/userlog/pkg/service/conversion.go +++ b/services/userlog/pkg/service/conversion.go @@ -3,6 +3,7 @@ package service import ( "bytes" "context" + "embed" "errors" "strings" "text/template" @@ -19,12 +20,15 @@ import ( ehmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/eventhistory/v0" ) +//go:embed l10n/locale +var _translationFS embed.FS + var ( _resourceTypeSpace = "storagespace" _resourceTypeShare = "share" // TODO: from config - _pathToLocales = "/home/jkoberg/ocis/services/userlog/pkg/service/locales" + _pathToLocales = "/home/jkoberg/ocis/services/userlog/l10n/locale" _domain = "default" ) @@ -281,7 +285,7 @@ func composeMessage(nt NotificationTemplate, locale string, vars map[string]inte func loadTemplates(nt NotificationTemplate, locale string) (string, string) { // Create Locale with library path and language code and load default domain - l := gotext.NewLocale(_pathToLocales, locale) + l := gotext.NewLocaleFS("l10n/locale", locale, _translationFS) l.AddDomain(_domain) return l.Get(nt.Subject), l.Get(nt.Message) } diff --git a/services/userlog/pkg/service/service.go b/services/userlog/pkg/service/service.go index f6132e5e383..5fa9c3eefa3 100644 --- a/services/userlog/pkg/service/service.go +++ b/services/userlog/pkg/service/service.go @@ -384,7 +384,7 @@ func getSpace(ctx context.Context, spaceID string, gwc gateway.GatewayAPIClient) } if res.GetStatus().GetCode() != rpc.Code_CODE_OK { - return nil, fmt.Errorf("Unexpected status code while getting space: %v", res.GetStatus().GetCode()) + return nil, fmt.Errorf("Error while getting space: (%v) %s", res.GetStatus().GetCode(), res.GetStatus().GetMessage()) } if len(res.StorageSpaces) == 0 { From cb31f9923fcfd1dcb44dc2dfe4b2b4a897535e89 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Thu, 9 Mar 2023 15:33:59 +0100 Subject: [PATCH 08/14] remove l10n from gitignore Signed-off-by: jkoberg --- .gitignore | 1 - services/userlog/Makefile | 8 +- .../pkg/service/l10n/locale/default.pot | 82 +++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 services/userlog/pkg/service/l10n/locale/default.pot diff --git a/.gitignore b/.gitignore index 68c73746e07..6a402cb68ff 100644 --- a/.gitignore +++ b/.gitignore @@ -41,7 +41,6 @@ vendor-php # drone CI is in .drone.star, do not let someone accidentally commit a local .drone.yml .drone.yml -**/l10n/locale **/l10n/template.pot # protogen autogenerated diff --git a/services/userlog/Makefile b/services/userlog/Makefile index ee92367c812..ea50ca0ee5e 100644 --- a/services/userlog/Makefile +++ b/services/userlog/Makefile @@ -1,6 +1,9 @@ SHELL := bash NAME := userlog +# Where to write the files generated by this makefile. +OUTPUT_DIR = ./pkg/service/l10n + include ../../.make/recursion.mk ############ tooling ############ @@ -41,7 +44,10 @@ l10n-push: .PHONY: l10n-read l10n-read: - go-xgettext -o $(OUTPUT_DIR)/locale/en/LC_MESSAGES/default.pot --keyword=Template -s pkg/service/templates.go + go-xgettext -o $(OUTPUT_DIR)/locale/default.pot --keyword=Template -s pkg/service/templates.go + +.PHONY: l10n-write +l10n-write: ############ licenses ############ .PHONY: ci-node-check-licenses diff --git a/services/userlog/pkg/service/l10n/locale/default.pot b/services/userlog/pkg/service/l10n/locale/default.pot new file mode 100644 index 00000000000..196807f5afb --- /dev/null +++ b/services/userlog/pkg/service/l10n/locale/default.pot @@ -0,0 +1,82 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "Project-Id-Version: \n" + "Report-Msgid-Bugs-To: EMAIL\n" + "POT-Creation-Date: 2023-03-09 15:58+0100\n" + "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" + "Last-Translator: FULL NAME \n" + "Language-Team: LANGUAGE \n" + "Language: \n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=CHARSET\n" + "Content-Transfer-Encoding: 8bit\n" + +#: pkg/service/templates.go:30 +msgid "Access to Space {space} lost" +msgstr "" + +#: pkg/service/templates.go:45 +msgid "Access to {resource} expired" +msgstr "" + +#: pkg/service/templates.go:29 +msgid "Membership expired" +msgstr "" + +#: pkg/service/templates.go:14 +msgid "Removed from Space" +msgstr "" + +#: pkg/service/templates.go:34 +msgid "Resource shared" +msgstr "" + +#: pkg/service/templates.go:39 +msgid "Resource unshared" +msgstr "" + +#: pkg/service/templates.go:44 +msgid "Share expired" +msgstr "" + +#: pkg/service/templates.go:24 +msgid "Space deleted" +msgstr "" + +#: pkg/service/templates.go:19 +msgid "Space disabled" +msgstr "" + +#: pkg/service/templates.go:9 +msgid "Space shared" +msgstr "" + +#: pkg/service/templates.go:10 +msgid "{user} added you to Space {space}" +msgstr "" + +#: pkg/service/templates.go:25 +msgid "{user} deleted Space {space}" +msgstr "" + +#: pkg/service/templates.go:20 +msgid "{user} disabled Space {space}" +msgstr "" + +#: pkg/service/templates.go:15 +msgid "{user} removed you from Space {space}" +msgstr "" + +#: pkg/service/templates.go:35 +msgid "{user} shared {resource} with you" +msgstr "" + +#: pkg/service/templates.go:40 +msgid "{user} unshared {resource} with you" +msgstr "" + From a2737cdc989502e6e36a158cfc79e56b596ba0a0 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Thu, 9 Mar 2023 15:20:52 +0100 Subject: [PATCH 09/14] use forked gotext pkg Signed-off-by: jkoberg --- go.mod | 6 +++--- go.sum | 11 ++++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 3640683660e..8fff99795cc 100644 --- a/go.mod +++ b/go.mod @@ -47,6 +47,7 @@ require ( github.com/jellydator/ttlcache/v2 v2.11.1 github.com/jellydator/ttlcache/v3 v3.0.1 github.com/justinas/alice v1.2.0 + github.com/leonelquinteros/gotext v1.5.2 github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5 github.com/libregraph/lico v0.59.4 github.com/mitchellh/mapstructure v1.5.0 @@ -191,7 +192,6 @@ require ( github.com/gookit/goutil v0.6.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/schema v1.2.0 // indirect - github.com/gosexy/gettext v0.0.0-20160830220431-74466a0a0c4a // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/hashicorp/consul/api v1.15.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -208,7 +208,6 @@ require ( github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jessevdk/go-flags v1.5.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -216,7 +215,6 @@ require ( github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.15.11 // indirect github.com/klauspost/cpuid/v2 v2.1.0 // indirect - github.com/leonelquinteros/gotext v1.5.2 // indirect github.com/libregraph/oidc-go v1.0.0 // indirect github.com/longsleep/go-metrics v1.0.0 // indirect github.com/longsleep/rndm v1.2.0 // indirect @@ -296,3 +294,5 @@ require ( ) replace github.com/cs3org/go-cs3apis => github.com/c0rby/go-cs3apis v0.0.0-20230110100311-5b424f1baa35 + +replace github.com/leonelquinteros/gotext => github.com/kobergj/gotext v0.0.0-20230309141732-b909eb0b8956 diff --git a/go.sum b/go.sum index 84e91e9e0c9..17b4c91cc37 100644 --- a/go.sum +++ b/go.sum @@ -342,6 +342,8 @@ github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4= github.com/crewjam/saml v0.4.10 h1:Rjs6x4s/aQFXiaPjw3uhB4VdxRqoxHXOJrrj4BsMn9o= github.com/crewjam/saml v0.4.10/go.mod h1:9Zh6dWPtB3MSzTRt8fIFH60Z351QQ+s7hCU3J/tTlA4= +github.com/cs3org/reva/v2 v2.12.1-0.20230308104654-4ebb727fa197 h1:CGUH/W7ZNN3pm7ElRq3vYfVIkoHj3975EY7veaR/nyY= +github.com/cs3org/reva/v2 v2.12.1-0.20230308104654-4ebb727fa197/go.mod h1:EGPAVHVqMHZ6GBo4T6QMw13D0djGU8ZCi/o5siJ9IUs= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= @@ -717,8 +719,6 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7 github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gosexy/gettext v0.0.0-20160830220431-74466a0a0c4a h1:N2b2mb4Gki1SlF3WuhR9P1YHOpl7oy/b+xxX4A3iM2E= -github.com/gosexy/gettext v0.0.0-20160830220431-74466a0a0c4a/go.mod h1:IEJaV4/6J0VpoQ33kFCUUP6umRjrcBVEbOva6XCub/Q= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= @@ -817,7 +817,6 @@ github.com/jellydator/ttlcache/v2 v2.11.1/go.mod h1:RtE5Snf0/57e+2cLWFYWCCsLas2H github.com/jellydator/ttlcache/v3 v3.0.1 h1:cHgCSMS7TdQcoprXnWUptJZzyFsqs18Lt8VVhRuZYVU= github.com/jellydator/ttlcache/v3 v3.0.1/go.mod h1:WwTaEmcXQ3MTjOm4bsZoDFiCu/hMvNWLO1w67RXz6h4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -865,8 +864,8 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0= github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/kobergj/reva/v2 v2.0.0-20230306133911-885670aab8df h1:W0PRhL0uX1Bd+1GxizjEXJ6zekAO46V4XZs/9b2g9TM= -github.com/kobergj/reva/v2 v2.0.0-20230306133911-885670aab8df/go.mod h1:dbaNP2U3nGQA5BHLc5w/hqviq7b0F4eygNwC38jeaiU= +github.com/kobergj/gotext v0.0.0-20230309141732-b909eb0b8956 h1:b3MBTVgsfKXD/CdnAAqD7czu9Npx9QG+rHZycBIXJGE= +github.com/kobergj/gotext v0.0.0-20230309141732-b909eb0b8956/go.mod h1:AT4NpQrOmyj1L/+hLja6aR0lk81yYYL4ePnj2kp7d6M= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -889,8 +888,6 @@ github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/leonelquinteros/gotext v1.5.2 h1:T2y6ebHli+rMBCjcJlHTXyUrgXqsKBhl/ormgvt7lPo= -github.com/leonelquinteros/gotext v1.5.2/go.mod h1:AT4NpQrOmyj1L/+hLja6aR0lk81yYYL4ePnj2kp7d6M= github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5 h1:brLMXSjWoWhGXs8LpK+Lx+FQCtGLUa51Mq/ggHv9AV0= github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5/go.mod h1:Tnm4pyVJTEbHm3GUNmceWT1DHzdrYqrJmZyt/xh7L+A= github.com/libregraph/lico v0.59.4 h1:aQNwWAtC3hyQeZpP+5U1M7PIrPtvHanRWRfMp33wbUQ= From 495d507ce1739e8e5ad313f151a0aed828cea5a7 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Fri, 10 Mar 2023 11:31:46 +0100 Subject: [PATCH 10/14] add .tx folder Signed-off-by: jkoberg --- services/userlog/pkg/service/l10n/.tx/config | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100755 services/userlog/pkg/service/l10n/.tx/config diff --git a/services/userlog/pkg/service/l10n/.tx/config b/services/userlog/pkg/service/l10n/.tx/config new file mode 100755 index 00000000000..c8380df8df3 --- /dev/null +++ b/services/userlog/pkg/service/l10n/.tx/config @@ -0,0 +1,10 @@ +[main] +host = https://www.transifex.com + +[o:owncloud-org:p:owncloud:r:ocis-userlog] +file_filter = locale//LC_MESSAGES/userlog.po +minimum_perc = 0 +source_file = userlog.pot +source_lang = en +type = PO + From 3e090c557f108fc246f221761c5ec8323e8d6b38 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Mon, 13 Mar 2023 10:19:44 +0100 Subject: [PATCH 11/14] make translationpath configurable Signed-off-by: jkoberg --- services/userlog/Makefile | 2 +- services/userlog/README.md | 14 +++++++++++ services/userlog/pkg/config/config.go | 1 + services/userlog/pkg/service/conversion.go | 29 +++++++++++++--------- services/userlog/pkg/service/http.go | 2 +- services/userlog/pkg/service/service.go | 1 + 6 files changed, 35 insertions(+), 14 deletions(-) diff --git a/services/userlog/Makefile b/services/userlog/Makefile index ea50ca0ee5e..79c4acbd3d1 100644 --- a/services/userlog/Makefile +++ b/services/userlog/Makefile @@ -44,7 +44,7 @@ l10n-push: .PHONY: l10n-read l10n-read: - go-xgettext -o $(OUTPUT_DIR)/locale/default.pot --keyword=Template -s pkg/service/templates.go + go-xgettext -o $(OUTPUT_DIR)/locale/userlog.pot --keyword=Template -s pkg/service/templates.go .PHONY: l10n-write l10n-write: diff --git a/services/userlog/README.md b/services/userlog/README.md index 352e76fa9c5..2fd9bb6181d 100644 --- a/services/userlog/README.md +++ b/services/userlog/README.md @@ -31,3 +31,17 @@ The `userlog` service provides an API to retrieve configured events. For now, th ## Deleting To delete events for an user, use a `DELETE` request to `ocs/v2.php/apps/notifications/api/v1/notifications` containing the IDs to delete. + +## Translations + +The `userlog` service uses embedded translations to provide full functionality even in the single binary case. The service supports using custom translations instead. Set `USERLOG_TRANSLATION_PATH` to a folder that contains the translation files. In this folder translation files need to be named `userlog.po` (or `userlog.mo`) and need to be stored in a folder defining their language code. In general the pattern for a translation file needs to be: +``` +{USERLOG_TRANSLATION_PATH}/{language-code}/LC_MESSAGES/userlog.po +``` +So for example for language `en_US` one needs to place the corresponding translation files to `{USERLOG_TRANSLATION_PATH}/en_US/LC_MESSAGES/userlog.po`. + +If a requested translation is not available the service falls back to the language default (so for example if `en_US` is not available, the service would fall back to translations in `en` folder). + +If the default language is also not available (for example the language code is `de_DE` and neither `de_DE` nor `de` folder is available, the service falls back to system default (dev `en`) + +It is currently not possible to mix custom and default translations. diff --git a/services/userlog/pkg/config/config.go b/services/userlog/pkg/config/config.go index 04e74cf3bae..085a2504231 100644 --- a/services/userlog/pkg/config/config.go +++ b/services/userlog/pkg/config/config.go @@ -22,6 +22,7 @@ type Config struct { MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;USERLOG_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."` RevaGateway string `yaml:"reva_gateway" env:"REVA_GATEWAY" desc:"CS3 gateway used to look up user metadata"` + TranslationPath string `yaml:"translation_path" env:"USERLOG_TRANSLATION_PATH" desc:"(optional) Set this to a path with custom translations to overwrite buildin translations. See readme for details"` Events Events `yaml:"events"` Store Store `yaml:"store"` diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go index f45f116dac3..15b6600ef07 100644 --- a/services/userlog/pkg/service/conversion.go +++ b/services/userlog/pkg/service/conversion.go @@ -27,9 +27,7 @@ var ( _resourceTypeSpace = "storagespace" _resourceTypeShare = "share" - // TODO: from config - _pathToLocales = "/home/jkoberg/ocis/services/userlog/l10n/locale" - _domain = "default" + _domain = "userlog" ) // OC10Notification is the oc10 style representation of an event @@ -55,6 +53,7 @@ type Converter struct { machineAuthAPIKey string serviceName string registeredEvents map[string]events.Unmarshaller + translationPath string // cached within one request not to query other service too much spaces map[string]*storageprovider.StorageSpace @@ -64,13 +63,14 @@ type Converter struct { } // NewConverter returns a new Converter -func NewConverter(loc string, gwc gateway.GatewayAPIClient, machineAuthAPIKey string, name string, registeredEvents map[string]events.Unmarshaller) *Converter { +func NewConverter(loc string, gwc gateway.GatewayAPIClient, machineAuthAPIKey string, name string, translationPath string, registeredEvents map[string]events.Unmarshaller) *Converter { return &Converter{ locale: loc, gwClient: gwc, machineAuthAPIKey: machineAuthAPIKey, serviceName: name, registeredEvents: registeredEvents, + translationPath: translationPath, spaces: make(map[string]*storageprovider.StorageSpace), users: make(map[string]*user.User), resources: make(map[string]*storageprovider.ResourceInfo), @@ -123,7 +123,7 @@ func (c *Converter) spaceDeletedMessage(eventid string, executant *user.UserId, return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := composeMessage(SpaceDeleted, c.locale, map[string]interface{}{ + subj, subjraw, msg, msgraw, err := composeMessage(SpaceDeleted, c.locale, c.translationPath, map[string]interface{}{ "username": usr.GetDisplayName(), "spacename": spacename, }) @@ -164,7 +164,7 @@ func (c *Converter) spaceMessage(eventid string, nt NotificationTemplate, execut return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := composeMessage(nt, c.locale, map[string]interface{}{ + subj, subjraw, msg, msgraw, err := composeMessage(nt, c.locale, c.translationPath, map[string]interface{}{ "username": usr.GetDisplayName(), "spacename": space.GetName(), }) @@ -203,7 +203,7 @@ func (c *Converter) shareMessage(eventid string, nt NotificationTemplate, execut return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := composeMessage(nt, c.locale, map[string]interface{}{ + subj, subjraw, msg, msgraw, err := composeMessage(nt, c.locale, c.translationPath, map[string]interface{}{ "username": usr.GetDisplayName(), "resourcename": info.GetName(), }) @@ -270,8 +270,8 @@ func (c *Converter) getUser(ctx context.Context, userID *user.UserId) (*user.Use return usr, err } -func composeMessage(nt NotificationTemplate, locale string, vars map[string]interface{}) (string, string, string, string, error) { - subjectraw, messageraw := loadTemplates(nt, locale) +func composeMessage(nt NotificationTemplate, locale string, path string, vars map[string]interface{}) (string, string, string, string, error) { + subjectraw, messageraw := loadTemplates(nt, locale, path) subject, err := executeTemplate(subjectraw, vars) if err != nil { @@ -283,10 +283,15 @@ func composeMessage(nt NotificationTemplate, locale string, vars map[string]inte } -func loadTemplates(nt NotificationTemplate, locale string) (string, string) { +func loadTemplates(nt NotificationTemplate, locale string, path string) (string, string) { // Create Locale with library path and language code and load default domain - l := gotext.NewLocaleFS("l10n/locale", locale, _translationFS) - l.AddDomain(_domain) + var l *gotext.Locale + if path == "" { + l = gotext.NewLocaleFS("l10n/locale", locale, _translationFS) + } else { // use custom path instead + l = gotext.NewLocale(path, locale) + } + l.AddDomain(_domain) // make domain configurable only if needed return l.Get(nt.Subject), l.Get(nt.Message) } diff --git a/services/userlog/pkg/service/http.go b/services/userlog/pkg/service/http.go index ffcbdfaef3e..7f316cc7847 100644 --- a/services/userlog/pkg/service/http.go +++ b/services/userlog/pkg/service/http.go @@ -31,7 +31,7 @@ func (ul *UserlogService) HandleGetEvents(w http.ResponseWriter, r *http.Request return } - conv := NewConverter(r.Header.Get(HeaderPreferedLanguage), ul.gwClient, ul.cfg.MachineAuthAPIKey, ul.cfg.Service.Name, ul.registeredEvents) + conv := NewConverter(r.Header.Get(HeaderPreferedLanguage), ul.gwClient, ul.cfg.MachineAuthAPIKey, ul.cfg.Service.Name, ul.cfg.TranslationPath, ul.registeredEvents) resp := GetEventResponseOC10{} for _, e := range evs { diff --git a/services/userlog/pkg/service/service.go b/services/userlog/pkg/service/service.go index 5fa9c3eefa3..2c6ee2837da 100644 --- a/services/userlog/pkg/service/service.go +++ b/services/userlog/pkg/service/service.go @@ -33,6 +33,7 @@ type UserlogService struct { historyClient ehsvc.EventHistoryService gwClient gateway.GatewayAPIClient registeredEvents map[string]events.Unmarshaller + translationPath string } // NewUserlogService returns an EventHistory service From a0df1157d6e2b67617f365bcee341a1dcd1167ca Mon Sep 17 00:00:00 2001 From: mmattel Date: Mon, 13 Mar 2023 11:50:33 +0100 Subject: [PATCH 12/14] update the tranlations part in readme --- services/userlog/README.md | 15 +++++++++------ services/userlog/pkg/config/config.go | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/services/userlog/README.md b/services/userlog/README.md index 2fd9bb6181d..a9c3476f3f1 100644 --- a/services/userlog/README.md +++ b/services/userlog/README.md @@ -34,14 +34,17 @@ To delete events for an user, use a `DELETE` request to `ocs/v2.php/apps/notific ## Translations -The `userlog` service uses embedded translations to provide full functionality even in the single binary case. The service supports using custom translations instead. Set `USERLOG_TRANSLATION_PATH` to a folder that contains the translation files. In this folder translation files need to be named `userlog.po` (or `userlog.mo`) and need to be stored in a folder defining their language code. In general the pattern for a translation file needs to be: -``` +The `userlog` service has embedded translations sourced via transifex to provide a basic set of translated languages. These embedded translations are available for all deployment scenarios. In addition, the service supports custom translations, though it is currently not possible to just add custom translations to embedded ones. If custom translations are configured, the embedded ones are not used. To configure custom translations, the `USERLOG_TRANSLATION_PATH` environment variable needs to point to a base folder that will further contain the translation files. This path must be available from all instances of the userlog service, a shared storage is recommended. Translation files must be of type [.po](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html#PO-Files) or [.mo](https://www.gnu.org/software/gettext/manual/html_node/Binaries.html). For each language, the filename needs to be `userlog.po` (or `userlog.mo`) and stored in a folder structure defining the language code. In general the path/name pattern for a translation file needs to be: + +```text {USERLOG_TRANSLATION_PATH}/{language-code}/LC_MESSAGES/userlog.po ``` -So for example for language `en_US` one needs to place the corresponding translation files to `{USERLOG_TRANSLATION_PATH}/en_US/LC_MESSAGES/userlog.po`. -If a requested translation is not available the service falls back to the language default (so for example if `en_US` is not available, the service would fall back to translations in `en` folder). +The language-code pattern is composed as `language[_territory]` where `language` is the base language and `_territory` is optional defining a country. + +As example, for the language `de_DE`, one needs to place the corresponding translation files to `{USERLOG_TRANSLATION_PATH}/de_DE/LC_MESSAGES/userlog.po`. -If the default language is also not available (for example the language code is `de_DE` and neither `de_DE` nor `de` folder is available, the service falls back to system default (dev `en`) +### Translation Rules -It is currently not possible to mix custom and default translations. +* If a requested language-code is not available, the service tries to fallback to the base language if available. As example, if `de_DE` is not available, the service tries to fall back to translations in `de` folder. +* If the base language is also not available like when the language code is `de_DE` and neither `de_DE` nor the `de` folder is available, the service falls back to the systems default `en`, which is the source of the texts provided by the code. diff --git a/services/userlog/pkg/config/config.go b/services/userlog/pkg/config/config.go index 085a2504231..c7d18c0056c 100644 --- a/services/userlog/pkg/config/config.go +++ b/services/userlog/pkg/config/config.go @@ -22,7 +22,7 @@ type Config struct { MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;USERLOG_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."` RevaGateway string `yaml:"reva_gateway" env:"REVA_GATEWAY" desc:"CS3 gateway used to look up user metadata"` - TranslationPath string `yaml:"translation_path" env:"USERLOG_TRANSLATION_PATH" desc:"(optional) Set this to a path with custom translations to overwrite buildin translations. See readme for details"` + TranslationPath string `yaml:"translation_path" env:"USERLOG_TRANSLATION_PATH" desc:"(optional) Set this to a path with custom translations to overwrite the builtin translations. See the documentation for more details."` Events Events `yaml:"events"` Store Store `yaml:"store"` From c236e0386102119fabf0bca235ed84f9567cd014 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Mon, 13 Mar 2023 16:14:14 +0100 Subject: [PATCH 13/14] remove .pot file Signed-off-by: jkoberg --- .../pkg/service/l10n/locale/default.pot | 82 ------------------- 1 file changed, 82 deletions(-) delete mode 100644 services/userlog/pkg/service/l10n/locale/default.pot diff --git a/services/userlog/pkg/service/l10n/locale/default.pot b/services/userlog/pkg/service/l10n/locale/default.pot deleted file mode 100644 index 196807f5afb..00000000000 --- a/services/userlog/pkg/service/l10n/locale/default.pot +++ /dev/null @@ -1,82 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "Project-Id-Version: \n" - "Report-Msgid-Bugs-To: EMAIL\n" - "POT-Creation-Date: 2023-03-09 15:58+0100\n" - "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" - "Last-Translator: FULL NAME \n" - "Language-Team: LANGUAGE \n" - "Language: \n" - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=CHARSET\n" - "Content-Transfer-Encoding: 8bit\n" - -#: pkg/service/templates.go:30 -msgid "Access to Space {space} lost" -msgstr "" - -#: pkg/service/templates.go:45 -msgid "Access to {resource} expired" -msgstr "" - -#: pkg/service/templates.go:29 -msgid "Membership expired" -msgstr "" - -#: pkg/service/templates.go:14 -msgid "Removed from Space" -msgstr "" - -#: pkg/service/templates.go:34 -msgid "Resource shared" -msgstr "" - -#: pkg/service/templates.go:39 -msgid "Resource unshared" -msgstr "" - -#: pkg/service/templates.go:44 -msgid "Share expired" -msgstr "" - -#: pkg/service/templates.go:24 -msgid "Space deleted" -msgstr "" - -#: pkg/service/templates.go:19 -msgid "Space disabled" -msgstr "" - -#: pkg/service/templates.go:9 -msgid "Space shared" -msgstr "" - -#: pkg/service/templates.go:10 -msgid "{user} added you to Space {space}" -msgstr "" - -#: pkg/service/templates.go:25 -msgid "{user} deleted Space {space}" -msgstr "" - -#: pkg/service/templates.go:20 -msgid "{user} disabled Space {space}" -msgstr "" - -#: pkg/service/templates.go:15 -msgid "{user} removed you from Space {space}" -msgstr "" - -#: pkg/service/templates.go:35 -msgid "{user} shared {resource} with you" -msgstr "" - -#: pkg/service/templates.go:40 -msgid "{user} unshared {resource} with you" -msgstr "" - From 230977e406d02deeb9fc89945d214b020f2069d4 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Mon, 13 Mar 2023 16:43:02 +0100 Subject: [PATCH 14/14] add empty locale folder, add reader pkg to bingo add empty locale for de_DE add reader pkg to bingo, add empty german translation --- .bingo/Variables.mk | 40 +++++---- .bingo/variables.env | 4 +- Makefile | 4 +- services/userlog/Makefile | 7 +- .../l10n/locale/de_DE/LC_MESSAGES/userlog.po | 83 +++++++++++++++++++ 5 files changed, 118 insertions(+), 20 deletions(-) create mode 100644 services/userlog/pkg/service/l10n/locale/de_DE/LC_MESSAGES/userlog.po diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index 2dfa10bf8d6..559efc0369f 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -1,4 +1,4 @@ -# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.7. DO NOT EDIT. +# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.6. DO NOT EDIT. # All tools are designed to be build inside $GOBIN. BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST))) GOPATH ?= $(shell go env GOPATH) @@ -21,95 +21,101 @@ BINGO := $(GOBIN)/bingo-v0.7.0 $(BINGO): $(BINGO_DIR)/bingo.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/bingo-v0.7.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=bingo.mod -o=$(GOBIN)/bingo-v0.7.0 "github.com/bwplotka/bingo" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=bingo.mod -o=$(GOBIN)/bingo-v0.7.0 "github.com/bwplotka/bingo" BUF := $(GOBIN)/buf-v1.3.1 $(BUF): $(BINGO_DIR)/buf.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/buf-v1.3.1" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buf.mod -o=$(GOBIN)/buf-v1.3.1 "github.com/bufbuild/buf/cmd/buf" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=buf.mod -o=$(GOBIN)/buf-v1.3.1 "github.com/bufbuild/buf/cmd/buf" BUILDIFIER := $(GOBIN)/buildifier-v0.0.0-20220323134444-a9f46b2bb3de $(BUILDIFIER): $(BINGO_DIR)/buildifier.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/buildifier-v0.0.0-20220323134444-a9f46b2bb3de" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildifier.mod -o=$(GOBIN)/buildifier-v0.0.0-20220323134444-a9f46b2bb3de "github.com/bazelbuild/buildtools/buildifier" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=buildifier.mod -o=$(GOBIN)/buildifier-v0.0.0-20220323134444-a9f46b2bb3de "github.com/bazelbuild/buildtools/buildifier" CALENS := $(GOBIN)/calens-v0.2.0 $(CALENS): $(BINGO_DIR)/calens.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/calens-v0.2.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=calens.mod -o=$(GOBIN)/calens-v0.2.0 "github.com/restic/calens" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=calens.mod -o=$(GOBIN)/calens-v0.2.0 "github.com/restic/calens" GO_LICENSES := $(GOBIN)/go-licenses-v1.5.0 $(GO_LICENSES): $(BINGO_DIR)/go-licenses.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/go-licenses-v1.5.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=go-licenses.mod -o=$(GOBIN)/go-licenses-v1.5.0 "github.com/google/go-licenses" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=go-licenses.mod -o=$(GOBIN)/go-licenses-v1.5.0 "github.com/google/go-licenses" + +GO_XGETTEXT := $(GOBIN)/go-xgettext-v0.0.0-20160830220431-74466a0a0c4a +$(GO_XGETTEXT): $(BINGO_DIR)/go-xgettext.mod + @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. + @echo "(re)installing $(GOBIN)/go-xgettext-v0.0.0-20160830220431-74466a0a0c4a" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=go-xgettext.mod -o=$(GOBIN)/go-xgettext-v0.0.0-20160830220431-74466a0a0c4a "github.com/gosexy/gettext/go-xgettext" GOLANGCI_LINT := $(GOBIN)/golangci-lint-v1.47.3 $(GOLANGCI_LINT): $(BINGO_DIR)/golangci-lint.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/golangci-lint-v1.47.3" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=golangci-lint.mod -o=$(GOBIN)/golangci-lint-v1.47.3 "github.com/golangci/golangci-lint/cmd/golangci-lint" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=golangci-lint.mod -o=$(GOBIN)/golangci-lint-v1.47.3 "github.com/golangci/golangci-lint/cmd/golangci-lint" HUGO := $(GOBIN)/hugo-v0.94.0 $(HUGO): $(BINGO_DIR)/hugo.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/hugo-v0.94.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=hugo.mod -o=$(GOBIN)/hugo-v0.94.0 "github.com/gohugoio/hugo" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=hugo.mod -o=$(GOBIN)/hugo-v0.94.0 "github.com/gohugoio/hugo" MOCKERY := $(GOBIN)/mockery-v2.14.1 $(MOCKERY): $(BINGO_DIR)/mockery.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/mockery-v2.14.1" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=mockery.mod -o=$(GOBIN)/mockery-v2.14.1 "github.com/vektra/mockery/v2" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=mockery.mod -o=$(GOBIN)/mockery-v2.14.1 "github.com/vektra/mockery/v2" MUTAGEN := $(GOBIN)/mutagen-v0.13.1 $(MUTAGEN): $(BINGO_DIR)/mutagen.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/mutagen-v0.13.1" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=mutagen.mod -o=$(GOBIN)/mutagen-v0.13.1 "github.com/mutagen-io/mutagen/cmd/mutagen" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=mutagen.mod -o=$(GOBIN)/mutagen-v0.13.1 "github.com/mutagen-io/mutagen/cmd/mutagen" PROTOC_GEN_DOC := $(GOBIN)/protoc-gen-doc-v1.5.1 $(PROTOC_GEN_DOC): $(BINGO_DIR)/protoc-gen-doc.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/protoc-gen-doc-v1.5.1" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=protoc-gen-doc.mod -o=$(GOBIN)/protoc-gen-doc-v1.5.1 "github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=protoc-gen-doc.mod -o=$(GOBIN)/protoc-gen-doc-v1.5.1 "github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc" PROTOC_GEN_GO := $(GOBIN)/protoc-gen-go-v1.28.1 $(PROTOC_GEN_GO): $(BINGO_DIR)/protoc-gen-go.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/protoc-gen-go-v1.28.1" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=protoc-gen-go.mod -o=$(GOBIN)/protoc-gen-go-v1.28.1 "google.golang.org/protobuf/cmd/protoc-gen-go" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=protoc-gen-go.mod -o=$(GOBIN)/protoc-gen-go-v1.28.1 "google.golang.org/protobuf/cmd/protoc-gen-go" PROTOC_GEN_MICRO := $(GOBIN)/protoc-gen-micro-v1.0.0 $(PROTOC_GEN_MICRO): $(BINGO_DIR)/protoc-gen-micro.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/protoc-gen-micro-v1.0.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=protoc-gen-micro.mod -o=$(GOBIN)/protoc-gen-micro-v1.0.0 "github.com/go-micro/generator/cmd/protoc-gen-micro" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=protoc-gen-micro.mod -o=$(GOBIN)/protoc-gen-micro-v1.0.0 "github.com/go-micro/generator/cmd/protoc-gen-micro" PROTOC_GEN_MICROWEB := $(GOBIN)/protoc-gen-microweb-v0.0.0-20220808092353-b5d6c3960e19 $(PROTOC_GEN_MICROWEB): $(BINGO_DIR)/protoc-gen-microweb.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/protoc-gen-microweb-v0.0.0-20220808092353-b5d6c3960e19" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=protoc-gen-microweb.mod -o=$(GOBIN)/protoc-gen-microweb-v0.0.0-20220808092353-b5d6c3960e19 "github.com/owncloud/protoc-gen-microweb" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=protoc-gen-microweb.mod -o=$(GOBIN)/protoc-gen-microweb-v0.0.0-20220808092353-b5d6c3960e19 "github.com/owncloud/protoc-gen-microweb" PROTOC_GEN_OPENAPIV2 := $(GOBIN)/protoc-gen-openapiv2-v2.13.0 $(PROTOC_GEN_OPENAPIV2): $(BINGO_DIR)/protoc-gen-openapiv2.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/protoc-gen-openapiv2-v2.13.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=protoc-gen-openapiv2.mod -o=$(GOBIN)/protoc-gen-openapiv2-v2.13.0 "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=protoc-gen-openapiv2.mod -o=$(GOBIN)/protoc-gen-openapiv2-v2.13.0 "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2" PROTOC_GO_INJECT_TAG := $(GOBIN)/protoc-go-inject-tag-v1.4.0 $(PROTOC_GO_INJECT_TAG): $(BINGO_DIR)/protoc-go-inject-tag.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/protoc-go-inject-tag-v1.4.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=protoc-go-inject-tag.mod -o=$(GOBIN)/protoc-go-inject-tag-v1.4.0 "github.com/favadi/protoc-go-inject-tag" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=protoc-go-inject-tag.mod -o=$(GOBIN)/protoc-go-inject-tag-v1.4.0 "github.com/favadi/protoc-go-inject-tag" REFLEX := $(GOBIN)/reflex-v0.3.1 $(REFLEX): $(BINGO_DIR)/reflex.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/reflex-v0.3.1" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=reflex.mod -o=$(GOBIN)/reflex-v0.3.1 "github.com/cespare/reflex" + @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=reflex.mod -o=$(GOBIN)/reflex-v0.3.1 "github.com/cespare/reflex" diff --git a/.bingo/variables.env b/.bingo/variables.env index 2044ef41123..6ccf239b2d9 100644 --- a/.bingo/variables.env +++ b/.bingo/variables.env @@ -1,4 +1,4 @@ -# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.7. DO NOT EDIT. +# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.6. DO NOT EDIT. # All tools are designed to be build inside $GOBIN. # Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk. GOBIN=${GOBIN:=$(go env GOBIN)} @@ -18,6 +18,8 @@ CALENS="${GOBIN}/calens-v0.2.0" GO_LICENSES="${GOBIN}/go-licenses-v1.5.0" +GO_XGETTEXT="${GOBIN}/go-xgettext-v0.0.0-20160830220431-74466a0a0c4a" + GOLANGCI_LINT="${GOBIN}/golangci-lint-v1.47.3" HUGO="${GOBIN}/hugo-v0.94.0" diff --git a/Makefile b/Makefile index 1e30910b706..a09fc7d2cfb 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,9 @@ WHITE := $(shell tput -Txterm setaf 7) RESET := $(shell tput -Txterm sgr0) -L10N_MODULES := $(shell find . -path '*.tx*' -name 'config' | sed 's|/[^/]*$$||' | sed 's|/[^/]*$$||' | sed 's|/[^/]*$$||') +# add a service here when it uses transifex +L10N_MODULES := \ + services/userlog # if you add a module here please also add it to the .drone.star file OCIS_MODULES = \ diff --git a/services/userlog/Makefile b/services/userlog/Makefile index 79c4acbd3d1..f5a0bb752da 100644 --- a/services/userlog/Makefile +++ b/services/userlog/Makefile @@ -3,6 +3,7 @@ NAME := userlog # Where to write the files generated by this makefile. OUTPUT_DIR = ./pkg/service/l10n +TEMPLATE_FILE = ./pkg/service/l10n/locale/userlog.pot include ../../.make/recursion.mk @@ -43,12 +44,16 @@ l10n-push: cd $(OUTPUT_DIR) && tx push -s --skip .PHONY: l10n-read -l10n-read: +l10n-read: $(GO_XGETTEXT) go-xgettext -o $(OUTPUT_DIR)/locale/userlog.pot --keyword=Template -s pkg/service/templates.go .PHONY: l10n-write l10n-write: +.PHONY: l10n-clean +l10n-clean: + rm -f $(TEMPLATE_FILE); + ############ licenses ############ .PHONY: ci-node-check-licenses ci-node-check-licenses: diff --git a/services/userlog/pkg/service/l10n/locale/de_DE/LC_MESSAGES/userlog.po b/services/userlog/pkg/service/l10n/locale/de_DE/LC_MESSAGES/userlog.po new file mode 100644 index 00000000000..d38f57feac1 --- /dev/null +++ b/services/userlog/pkg/service/l10n/locale/de_DE/LC_MESSAGES/userlog.po @@ -0,0 +1,83 @@ +# German translations for package +# German translation for . +# Copyright (C) 2023 THE 'S COPYRIGHT HOLDER +# This file is distributed under the same license as the package. +# Michael Barz , 2023. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: EMAIL\n" +"POT-Creation-Date: 2023-03-13 23:36+0100\n" +"PO-Revision-Date: 2023-03-13 23:36+0100\n" +"Last-Translator: Michael Barz \n" +"Language-Team: German \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: pkg/service/templates.go:30 +msgid "Access to Space {space} lost" +msgstr "" + +#: pkg/service/templates.go:45 +msgid "Access to {resource} expired" +msgstr "" + +#: pkg/service/templates.go:29 +msgid "Membership expired" +msgstr "" + +#: pkg/service/templates.go:14 +msgid "Removed from Space" +msgstr "" + +#: pkg/service/templates.go:34 +msgid "Resource shared" +msgstr "" + +#: pkg/service/templates.go:39 +msgid "Resource unshared" +msgstr "" + +#: pkg/service/templates.go:44 +msgid "Share expired" +msgstr "" + +#: pkg/service/templates.go:24 +msgid "Space deleted" +msgstr "" + +#: pkg/service/templates.go:19 +msgid "Space disabled" +msgstr "" + +#: pkg/service/templates.go:9 +msgid "Space shared" +msgstr "" + +#: pkg/service/templates.go:10 +msgid "{user} added you to Space {space}" +msgstr "" + +#: pkg/service/templates.go:25 +msgid "{user} deleted Space {space}" +msgstr "" + +#: pkg/service/templates.go:20 +msgid "{user} disabled Space {space}" +msgstr "" + +#: pkg/service/templates.go:15 +msgid "{user} removed you from Space {space}" +msgstr "" + +#: pkg/service/templates.go:35 +msgid "{user} shared {resource} with you" +msgstr "" + +#: pkg/service/templates.go:40 +msgid "{user} unshared {resource} with you" +msgstr ""