From acdfbabd4789053c9e0282fd390708550d89aa0f Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Mon, 12 Sep 2022 16:59:49 +0200 Subject: [PATCH 01/13] add email templating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian Richter Co-authored-by: Jörn Dreyer --- services/notifications/pkg/command/server.go | 8 +- services/notifications/pkg/email/email.go | 25 +++ .../email/templates/shareCreated.email.tmpl | 1 + .../email/templates/sharedSpace.email.tmpl | 0 services/notifications/pkg/service/service.go | 148 +++++++++++++++--- 5 files changed, 159 insertions(+), 23 deletions(-) create mode 100644 services/notifications/pkg/email/email.go create mode 100644 services/notifications/pkg/email/templates/shareCreated.email.tmpl create mode 100644 services/notifications/pkg/email/templates/sharedSpace.email.tmpl diff --git a/services/notifications/pkg/command/server.go b/services/notifications/pkg/command/server.go index 8685b4e4291..8c8849611f6 100644 --- a/services/notifications/pkg/command/server.go +++ b/services/notifications/pkg/command/server.go @@ -5,6 +5,7 @@ import ( "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/events/server" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/go-micro/plugins/v4/events/natsjs" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" "github.com/owncloud/ocis/v2/services/notifications/pkg/channels" @@ -47,7 +48,12 @@ func Server(cfg *config.Config) *cli.Command { if err != nil { return err } - svc := service.NewEventsNotifier(evts, channel, logger) + gwclient, err := pool.GetGatewayServiceClient(cfg.Notifications.RevaGateway) + if err != nil { + logger.Fatal().Err(err).Str("addr", cfg.Notifications.RevaGateway).Msg("could not get reva client") + } + + svc := service.NewEventsNotifier(evts, channel, logger, gwclient, cfg.Commons.MachineAuthAPIKey) return svc.Run() }, } diff --git a/services/notifications/pkg/email/email.go b/services/notifications/pkg/email/email.go new file mode 100644 index 00000000000..bdc3ddf8087 --- /dev/null +++ b/services/notifications/pkg/email/email.go @@ -0,0 +1,25 @@ +package email + +import ( + "bytes" + "html/template" + "os" + "path/filepath" +) + +const templatePath string = "../../email/templates" + +// RenderEmailTemplate renders the email template for a new share +func RenderEmailTemplate(templateName string, templateVariables map[string]string) (string, error) { + content, err := os.ReadFile(filepath.Join(templatePath, templateName)) + if err != nil { + return "", err + } + tpl := template.Must(template.New("").Parse(string(content))) + writer := bytes.NewBufferString("") + err = tpl.Execute(writer, templateVariables) + if err != nil { + return "", err + } + return writer.String(), nil +} diff --git a/services/notifications/pkg/email/templates/shareCreated.email.tmpl b/services/notifications/pkg/email/templates/shareCreated.email.tmpl new file mode 100644 index 00000000000..408297ea3d7 --- /dev/null +++ b/services/notifications/pkg/email/templates/shareCreated.email.tmpl @@ -0,0 +1 @@ +{{ShareSharer}} has shared {{ShareFolder}} with you. \ No newline at end of file diff --git a/services/notifications/pkg/email/templates/sharedSpace.email.tmpl b/services/notifications/pkg/email/templates/sharedSpace.email.tmpl new file mode 100644 index 00000000000..e69de29bb2d diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index d6c8f970517..b386ae2de60 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -1,33 +1,47 @@ package service import ( + "context" "os" "os/signal" "syscall" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/notifications/pkg/channels" + "github.com/owncloud/ocis/v2/services/notifications/pkg/email" + "google.golang.org/grpc/metadata" + "google.golang.org/protobuf/types/known/fieldmaskpb" ) type Service interface { Run() error } -func NewEventsNotifier(events <-chan interface{}, channel channels.Channel, logger log.Logger) Service { +func NewEventsNotifier(events <-chan interface{}, channel channels.Channel, logger log.Logger, gwClient gateway.GatewayAPIClient, machineAuthAPIKey string) Service { return eventsNotifier{ - logger: logger, - channel: channel, - events: events, - signals: make(chan os.Signal, 1), + logger: logger, + channel: channel, + events: events, + signals: make(chan os.Signal, 1), + gwClient: gwClient, + machineAuthAPIKey: machineAuthAPIKey, } } type eventsNotifier struct { - logger log.Logger - channel channels.Channel - events <-chan interface{} - signals chan os.Signal + logger log.Logger + channel channels.Channel + events <-chan interface{} + signals chan os.Signal + gwClient gateway.GatewayAPIClient + machineAuthAPIKey string } func (s eventsNotifier) Run() error { @@ -39,20 +53,10 @@ func (s eventsNotifier) Run() error { case evt := <-s.events: go func() { switch e := evt.(type) { + case events.SpaceCreated: + s.handleSpaceCreated(e) case events.ShareCreated: - msg := "You got a share!" - var err error - if e.GranteeUserID != nil { - err = s.channel.SendMessage([]string{e.GranteeUserID.OpaqueId}, msg) - } else if e.GranteeGroupID != nil { - err = s.channel.SendMessageToGroup(e.GranteeGroupID, msg) - } - if err != nil { - s.logger.Error(). - Err(err). - Str("event", "ShareCreated"). - Msg("failed to send a message") - } + s.handleShareCreated(e) } }() case <-s.signals: @@ -62,3 +66,103 @@ func (s eventsNotifier) Run() error { } } } + +func (s eventsNotifier) handleSpaceCreated(e events.SpaceCreated) { + // TODO: implement me +} + +func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { + userResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ + UserId: e.Sharer, + }) + if err != nil || userResponse.Status.Code != rpcv1beta1.Code_CODE_OK { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Msg("Could not get user response from gatway client") + return + } + + // Get auth context + ownerCtx := ctxpkg.ContextSetUser(context.Background(), userResponse.User) + authRes, err := s.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{ + Type: "machine", + ClientId: "userid:" + e.Sharer.OpaqueId, + ClientSecret: s.machineAuthAPIKey, + }) + if err != nil || authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Msg("Could not impersonate sharer") + return + } + + if authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Msg("could not get authenticated context for user") + return + } + ownerCtx = metadata.AppendToOutgoingContext(ownerCtx, ctxpkg.TokenHeader, authRes.Token) + + resourceID, err := storagespace.ParseID(e.ItemID.OpaqueId) + if err != nil { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Str("itemid", e.ItemID.OpaqueId). + Msg("could not parse resourceid from ItemID ") + return + } + // TODO: maybe cache this stat to reduce storage iops + md, err := s.gwClient.Stat(ownerCtx, &providerv1beta1.StatRequest{ + Ref: &providerv1beta1.Reference{ + ResourceId: &resourceID, + }, + FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}}, + }) + + if err != nil || md.Status.Code != rpcv1beta1.Code_CODE_OK { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Str("itemid", e.ItemID.OpaqueId). + Msg("could not stat resource") + return + } + + if md.Status.Code != rpcv1beta1.Code_CODE_OK { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Str("itemid", e.ItemID.OpaqueId). + Str("rpc status", md.Status.Code.String()). + Msg("could not stat resource") + return + } + + msg, err := email.RenderEmailTemplate("shareCreated.email.tmpl", map[string]string{ + "ShareSharer": userResponse.User.DisplayName, + "ShareFolder": md.Info.Name, + }) + + if err != nil { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Msg("Could not render E-Mail template for shares") + } + if e.GranteeUserID != nil { + err = s.channel.SendMessage([]string{e.GranteeUserID.OpaqueId}, msg) + } else if e.GranteeGroupID != nil { + err = s.channel.SendMessageToGroup(e.GranteeGroupID, msg) + } + if err != nil { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Msg("failed to send a message") + } +} From 817ac00393d05f0fad652101f29afe81ab8a7876 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Wed, 14 Sep 2022 13:18:06 +0200 Subject: [PATCH 02/13] embedd templates Signed-off-by: Christian Richter --- services/notifications/pkg/email/email.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/services/notifications/pkg/email/email.go b/services/notifications/pkg/email/email.go index bdc3ddf8087..208d3f80dae 100644 --- a/services/notifications/pkg/email/email.go +++ b/services/notifications/pkg/email/email.go @@ -2,20 +2,19 @@ package email import ( "bytes" + "embed" "html/template" - "os" - "path/filepath" ) -const templatePath string = "../../email/templates" +// go:embed templates/*.tmpl // RenderEmailTemplate renders the email template for a new share func RenderEmailTemplate(templateName string, templateVariables map[string]string) (string, error) { - content, err := os.ReadFile(filepath.Join(templatePath, templateName)) + var fs embed.FS + tpl, err := template.ParseFS(fs, templateName) if err != nil { return "", err } - tpl := template.Must(template.New("").Parse(string(content))) writer := bytes.NewBufferString("") err = tpl.Execute(writer, templateVariables) if err != nil { From 22369b33f5882d42ba153e176af3f9580b28a8ec Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Wed, 14 Sep 2022 14:56:21 +0200 Subject: [PATCH 03/13] make email templates configurable Signed-off-by: Christian Richter --- services/notifications/pkg/command/server.go | 2 +- services/notifications/pkg/config/config.go | 1 + services/notifications/pkg/email/email.go | 22 +++++++++++++++---- services/notifications/pkg/service/service.go | 7 ++++-- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/services/notifications/pkg/command/server.go b/services/notifications/pkg/command/server.go index 8c8849611f6..03092895021 100644 --- a/services/notifications/pkg/command/server.go +++ b/services/notifications/pkg/command/server.go @@ -53,7 +53,7 @@ func Server(cfg *config.Config) *cli.Command { logger.Fatal().Err(err).Str("addr", cfg.Notifications.RevaGateway).Msg("could not get reva client") } - svc := service.NewEventsNotifier(evts, channel, logger, gwclient, cfg.Commons.MachineAuthAPIKey) + svc := service.NewEventsNotifier(evts, channel, logger, gwclient, cfg.Commons.MachineAuthAPIKey, cfg.Notifications.EmailTemplatePath) return svc.Run() }, } diff --git a/services/notifications/pkg/config/config.go b/services/notifications/pkg/config/config.go index cefd752b46e..e5a9f1344b5 100644 --- a/services/notifications/pkg/config/config.go +++ b/services/notifications/pkg/config/config.go @@ -26,6 +26,7 @@ type Notifications struct { Events Events `yaml:"events"` RevaGateway string `yaml:"reva_gateway" env:"REVA_GATEWAY;NOTIFICATIONS_REVA_GATEWAY" desc:"CS3 gateway used to look up user metadata"` MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;NOTIFICATIONS_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."` + EmailTemplatePath string `yaml:"email_template_path" env:"OCIS_EMAIL_TEMPLATE_PATH;NOTIFICATIONS_EMAIL_TEMPLATE_PATH" desc:"Path to the E-Mail templates for the notifications to override the embedded ones."` } // SMTP combines the smtp configuration options. diff --git a/services/notifications/pkg/email/email.go b/services/notifications/pkg/email/email.go index 208d3f80dae..fe56ca89068 100644 --- a/services/notifications/pkg/email/email.go +++ b/services/notifications/pkg/email/email.go @@ -4,16 +4,30 @@ import ( "bytes" "embed" "html/template" + "path/filepath" ) // go:embed templates/*.tmpl // RenderEmailTemplate renders the email template for a new share -func RenderEmailTemplate(templateName string, templateVariables map[string]string) (string, error) { +func RenderEmailTemplate(templateName string, templateVariables map[string]string, emailTemplatePath string) (string, error) { var fs embed.FS - tpl, err := template.ParseFS(fs, templateName) - if err != nil { - return "", err + var err error + var tpl *template.Template + templateHasBeenFound := false + if emailTemplatePath != "" { + // try to lookup the files in the filesystem + tpl, err = template.ParseFiles(filepath.Join(emailTemplatePath, templateName)) + if err == nil { + templateHasBeenFound = true + } + } + if !templateHasBeenFound { + // template has not been found in the fs, or path has not been specified => use embed templates + tpl, err = template.ParseFS(fs, templateName) + if err != nil { + return "", err + } } writer := bytes.NewBufferString("") err = tpl.Execute(writer, templateVariables) diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index b386ae2de60..dab60010001 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -24,7 +24,8 @@ type Service interface { Run() error } -func NewEventsNotifier(events <-chan interface{}, channel channels.Channel, logger log.Logger, gwClient gateway.GatewayAPIClient, machineAuthAPIKey string) Service { +// NewEventsNotifier provides a new eventsNotifier +func NewEventsNotifier(events <-chan interface{}, channel channels.Channel, logger log.Logger, gwClient gateway.GatewayAPIClient, machineAuthAPIKey, emailTemplatePath string) Service { return eventsNotifier{ logger: logger, channel: channel, @@ -32,6 +33,7 @@ func NewEventsNotifier(events <-chan interface{}, channel channels.Channel, logg signals: make(chan os.Signal, 1), gwClient: gwClient, machineAuthAPIKey: machineAuthAPIKey, + emailTemplatePath: emailTemplatePath, } } @@ -42,6 +44,7 @@ type eventsNotifier struct { signals chan os.Signal gwClient gateway.GatewayAPIClient machineAuthAPIKey string + emailTemplatePath string } func (s eventsNotifier) Run() error { @@ -146,7 +149,7 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { msg, err := email.RenderEmailTemplate("shareCreated.email.tmpl", map[string]string{ "ShareSharer": userResponse.User.DisplayName, "ShareFolder": md.Info.Name, - }) + }, s.emailTemplatePath) if err != nil { s.logger.Error(). From 70690a318d701ee003883955c9fdfac028a3055e Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Wed, 14 Sep 2022 16:24:21 +0200 Subject: [PATCH 04/13] add space notification Signed-off-by: Christian Richter --- .../email/templates/sharedSpace.email.tmpl | 1 + services/notifications/pkg/service/service.go | 92 ++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/services/notifications/pkg/email/templates/sharedSpace.email.tmpl b/services/notifications/pkg/email/templates/sharedSpace.email.tmpl index e69de29bb2d..0f67ce9656a 100644 --- a/services/notifications/pkg/email/templates/sharedSpace.email.tmpl +++ b/services/notifications/pkg/email/templates/sharedSpace.email.tmpl @@ -0,0 +1 @@ +{{SpaceSharer}} has invited you to join {{SpaceName}} \ No newline at end of file diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index dab60010001..8ae372788d8 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -71,7 +71,97 @@ func (s eventsNotifier) Run() error { } func (s eventsNotifier) handleSpaceCreated(e events.SpaceCreated) { - // TODO: implement me + userResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ + UserId: e.Executant, + }) + if err != nil || userResponse.Status.Code != rpcv1beta1.Code_CODE_OK { + s.logger.Error(). + Err(err). + Str("event", "SpaceCreated"). + Msg("Could not get user response from gatway client") + return + } + // Get auth context + ownerCtx := ctxpkg.ContextSetUser(context.Background(), userResponse.User) + authRes, err := s.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{ + Type: "machine", + ClientId: "userid:" + e.Executant.OpaqueId, + ClientSecret: s.machineAuthAPIKey, + }) + if err != nil || authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { + s.logger.Error(). + Err(err). + Str("event", "SpaceCreated"). + Msg("Could not impersonate sharer") + return + } + + if authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { + s.logger.Error(). + Err(err). + Str("event", "SpaceCreated"). + Msg("could not get authenticated context for user") + return + } + ownerCtx = metadata.AppendToOutgoingContext(ownerCtx, ctxpkg.TokenHeader, authRes.Token) + + resourceID, err := storagespace.ParseID(e.ID.OpaqueId) + if err != nil { + s.logger.Error(). + Err(err). + Str("event", "SpaceCreated"). + Str("itemid", e.ID.OpaqueId). + Msg("could not parse resourceid from ItemID ") + return + } + // TODO: maybe cache this stat to reduce storage iops + md, err := s.gwClient.Stat(ownerCtx, &providerv1beta1.StatRequest{ + Ref: &providerv1beta1.Reference{ + ResourceId: &resourceID, + }, + FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}}, + }) + + if err != nil || md.Status.Code != rpcv1beta1.Code_CODE_OK { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Str("itemid", e.ID.OpaqueId). + Msg("could not stat resource") + return + } + + if md.Status.Code != rpcv1beta1.Code_CODE_OK { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Str("itemid", e.ID.OpaqueId). + Str("rpc status", md.Status.Code.String()). + Msg("could not stat resource") + return + } + + // old code + msg, err := email.RenderEmailTemplate("sharedSpace.email.tmpl", map[string]string{ + "SpaceSharer": "spacesharer", + "SpaceName": "spacename", + }, s.emailTemplatePath) + + if err != nil { + s.logger.Error(). + Err(err). + Str("event", "SpaceCreated"). + Msg("Could not render E-Mail template for spaces") + } + if e.Executant != nil { + err = s.channel.SendMessage([]string{e.Executant.OpaqueId}, msg) + } + if err != nil { + s.logger.Error(). + Err(err). + Str("event", "SpaceCreated"). + Msg("failed to send a message") + } } func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { From daaa08aa0958a2a39211eef8ab9b3b6196e0a466 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Thu, 15 Sep 2022 16:02:19 +0200 Subject: [PATCH 05/13] bugfixes Signed-off-by: Christian Richter --- services/notifications/pkg/email/email.go | 8 +++++--- .../pkg/email/templates/shareCreated.email.tmpl | 2 +- .../pkg/email/templates/sharedSpace.email.tmpl | 2 +- services/notifications/pkg/service/service.go | 17 +++++------------ 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/services/notifications/pkg/email/email.go b/services/notifications/pkg/email/email.go index fe56ca89068..c0709b9ea17 100644 --- a/services/notifications/pkg/email/email.go +++ b/services/notifications/pkg/email/email.go @@ -7,11 +7,13 @@ import ( "path/filepath" ) -// go:embed templates/*.tmpl +var ( + //go:embed templates + templatesFS embed.FS +) // RenderEmailTemplate renders the email template for a new share func RenderEmailTemplate(templateName string, templateVariables map[string]string, emailTemplatePath string) (string, error) { - var fs embed.FS var err error var tpl *template.Template templateHasBeenFound := false @@ -24,7 +26,7 @@ func RenderEmailTemplate(templateName string, templateVariables map[string]strin } if !templateHasBeenFound { // template has not been found in the fs, or path has not been specified => use embed templates - tpl, err = template.ParseFS(fs, templateName) + tpl, err = template.ParseFS(templatesFS, filepath.Join("templates/", templateName)) if err != nil { return "", err } diff --git a/services/notifications/pkg/email/templates/shareCreated.email.tmpl b/services/notifications/pkg/email/templates/shareCreated.email.tmpl index 408297ea3d7..4f69160334c 100644 --- a/services/notifications/pkg/email/templates/shareCreated.email.tmpl +++ b/services/notifications/pkg/email/templates/shareCreated.email.tmpl @@ -1 +1 @@ -{{ShareSharer}} has shared {{ShareFolder}} with you. \ No newline at end of file +{{ .ShareSharer }} has shared {{ .ShareFolder }} with you. \ No newline at end of file diff --git a/services/notifications/pkg/email/templates/sharedSpace.email.tmpl b/services/notifications/pkg/email/templates/sharedSpace.email.tmpl index 0f67ce9656a..f70ec9a4cb6 100644 --- a/services/notifications/pkg/email/templates/sharedSpace.email.tmpl +++ b/services/notifications/pkg/email/templates/sharedSpace.email.tmpl @@ -1 +1 @@ -{{SpaceSharer}} has invited you to join {{SpaceName}} \ No newline at end of file +{{ .SpaceSharer }} has invited you to join {{ .SpaceName }} \ No newline at end of file diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index 8ae372788d8..7f01bacf669 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -72,7 +72,7 @@ func (s eventsNotifier) Run() error { func (s eventsNotifier) handleSpaceCreated(e events.SpaceCreated) { userResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ - UserId: e.Executant, + UserId: e.Owner, }) if err != nil || userResponse.Status.Code != rpcv1beta1.Code_CODE_OK { s.logger.Error(). @@ -143,8 +143,9 @@ func (s eventsNotifier) handleSpaceCreated(e events.SpaceCreated) { // old code msg, err := email.RenderEmailTemplate("sharedSpace.email.tmpl", map[string]string{ + // TODO: add additional fields here (like link etc.) "SpaceSharer": "spacesharer", - "SpaceName": "spacename", + "SpaceName": md.Info.Space.Name, }, s.emailTemplatePath) if err != nil { @@ -200,19 +201,10 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { } ownerCtx = metadata.AppendToOutgoingContext(ownerCtx, ctxpkg.TokenHeader, authRes.Token) - resourceID, err := storagespace.ParseID(e.ItemID.OpaqueId) - if err != nil { - s.logger.Error(). - Err(err). - Str("event", "ShareCreated"). - Str("itemid", e.ItemID.OpaqueId). - Msg("could not parse resourceid from ItemID ") - return - } // TODO: maybe cache this stat to reduce storage iops md, err := s.gwClient.Stat(ownerCtx, &providerv1beta1.StatRequest{ Ref: &providerv1beta1.Reference{ - ResourceId: &resourceID, + ResourceId: e.ItemID, }, FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}}, }) @@ -237,6 +229,7 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { } msg, err := email.RenderEmailTemplate("shareCreated.email.tmpl", map[string]string{ + // TODO: add additional fields here (like link etc.) "ShareSharer": userResponse.User.DisplayName, "ShareFolder": md.Info.Name, }, s.emailTemplatePath) From 6ea8623b55245af1546407eac4a926f21b16f004 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Fri, 16 Sep 2022 09:30:38 +0200 Subject: [PATCH 06/13] add email subject Signed-off-by: Christian Richter --- services/notifications/pkg/channels/channels.go | 11 ++++++----- services/notifications/pkg/service/service.go | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/services/notifications/pkg/channels/channels.go b/services/notifications/pkg/channels/channels.go index 06129399800..3929d30c068 100644 --- a/services/notifications/pkg/channels/channels.go +++ b/services/notifications/pkg/channels/channels.go @@ -19,9 +19,9 @@ import ( // Channel defines the methods of a communication channel. type Channel interface { // SendMessage sends a message to users. - SendMessage(userIDs []string, msg string) error + SendMessage(userIDs []string, msg, subject string) error // SendMessageToGroup sends a message to a group. - SendMessageToGroup(groupdID *groups.GroupId, msg string) error + SendMessageToGroup(groupdID *groups.GroupId, msg, subject string) error } // NewMailChannel instantiates a new mail communication channel. @@ -100,7 +100,7 @@ func (m Mail) getMailClient() (*mail.SMTPClient, error) { } // SendMessage sends a message to all given users. -func (m Mail) SendMessage(userIDs []string, msg string) error { +func (m Mail) SendMessage(userIDs []string, msg, subject string) error { if m.conf.Notifications.SMTP.Host == "" { return nil } @@ -118,12 +118,13 @@ func (m Mail) SendMessage(userIDs []string, msg string) error { email := mail.NewMSG() email.SetFrom(m.conf.Notifications.SMTP.Sender).AddTo(to...) email.SetBody(mail.TextPlain, msg) + email.SetSubject(subject) return email.Send(smtpClient) } // SendMessageToGroup sends a message to all members of the given group. -func (m Mail) SendMessageToGroup(groupID *groups.GroupId, msg string) error { +func (m Mail) SendMessageToGroup(groupID *groups.GroupId, msg, subject string) error { // TODO We need an authenticated context here... res, err := m.gatewayClient.GetGroup(context.Background(), &groups.GetGroupRequest{GroupId: groupID}) if err != nil { @@ -138,7 +139,7 @@ func (m Mail) SendMessageToGroup(groupID *groups.GroupId, msg string) error { members = append(members, id.OpaqueId) } - return m.SendMessage(members, msg) + return m.SendMessage(members, msg, subject) } func (m Mail) getReceiverAddresses(receivers []string) ([]string, error) { diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index 7f01bacf669..3746ab9f57a 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -155,7 +155,7 @@ func (s eventsNotifier) handleSpaceCreated(e events.SpaceCreated) { Msg("Could not render E-Mail template for spaces") } if e.Executant != nil { - err = s.channel.SendMessage([]string{e.Executant.OpaqueId}, msg) + err = s.channel.SendMessage([]string{e.Executant.OpaqueId}, msg, "You were invited to join a space") } if err != nil { s.logger.Error(). @@ -241,9 +241,9 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { Msg("Could not render E-Mail template for shares") } if e.GranteeUserID != nil { - err = s.channel.SendMessage([]string{e.GranteeUserID.OpaqueId}, msg) + err = s.channel.SendMessage([]string{e.GranteeUserID.OpaqueId}, msg, "You have received a share.") } else if e.GranteeGroupID != nil { - err = s.channel.SendMessageToGroup(e.GranteeGroupID, msg) + err = s.channel.SendMessageToGroup(e.GranteeGroupID, msg, "You have received a share.") } if err != nil { s.logger.Error(). From 8e0b07b200f71b67d2dd64ea33455c2f0451a525 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Fri, 16 Sep 2022 15:05:19 +0200 Subject: [PATCH 07/13] add share url to email text Signed-off-by: Christian Richter --- .../pkg/email/templates/shareCreated.email.tmpl | 4 +++- services/notifications/pkg/service/service.go | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/services/notifications/pkg/email/templates/shareCreated.email.tmpl b/services/notifications/pkg/email/templates/shareCreated.email.tmpl index 4f69160334c..376775dfad5 100644 --- a/services/notifications/pkg/email/templates/shareCreated.email.tmpl +++ b/services/notifications/pkg/email/templates/shareCreated.email.tmpl @@ -1 +1,3 @@ -{{ .ShareSharer }} has shared {{ .ShareFolder }} with you. \ No newline at end of file +{{ .ShareSharer }} has shared {{ .ShareFolder }} with you. + +Link: {{ .ShareLink }} \ No newline at end of file diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index 3746ab9f57a..85bbf55fc8d 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -2,6 +2,7 @@ package service import ( "context" + "net/url" "os" "os/signal" "syscall" @@ -228,10 +229,21 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { return } + shareLink, err := url.JoinPath(e.Executant.Idp, "files/shares/with-me") + + if err != nil { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Msg("could not create link to the share") + return + } + msg, err := email.RenderEmailTemplate("shareCreated.email.tmpl", map[string]string{ // TODO: add additional fields here (like link etc.) "ShareSharer": userResponse.User.DisplayName, "ShareFolder": md.Info.Name, + "ShareLink": shareLink, }, s.emailTemplatePath) if err != nil { From c2ef67f661b73f53511c0a5e26c6f330cb9ed92d Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Fri, 16 Sep 2022 15:22:45 +0200 Subject: [PATCH 08/13] fix SpacesShared Signed-off-by: Christian Richter --- services/notifications/pkg/service/service.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index 85bbf55fc8d..dc23e38867a 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -57,8 +57,8 @@ func (s eventsNotifier) Run() error { case evt := <-s.events: go func() { switch e := evt.(type) { - case events.SpaceCreated: - s.handleSpaceCreated(e) + case events.SpaceShared: + s.handleSpaceShared(e) case events.ShareCreated: s.handleShareCreated(e) } @@ -71,7 +71,7 @@ func (s eventsNotifier) Run() error { } } -func (s eventsNotifier) handleSpaceCreated(e events.SpaceCreated) { +func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { userResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ UserId: e.Owner, }) From 0422af2a11e00dd6b0808530c3ad1ba398920f19 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Mon, 19 Sep 2022 16:49:04 +0200 Subject: [PATCH 09/13] handle spaceshared event Signed-off-by: Christian Richter --- services/audit/pkg/types/events.go | 1 + services/notifications/pkg/command/server.go | 2 ++ .../email/templates/sharedSpace.email.tmpl | 4 ++- services/notifications/pkg/service/service.go | 31 +++++++++++++------ 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/services/audit/pkg/types/events.go b/services/audit/pkg/types/events.go index 2fe9d9aef6f..02c6dfa6694 100644 --- a/services/audit/pkg/types/events.go +++ b/services/audit/pkg/types/events.go @@ -29,6 +29,7 @@ func RegisteredEvents() []events.Unmarshaller { events.SpaceEnabled{}, events.SpaceDisabled{}, events.SpaceDeleted{}, + events.SpaceShared{}, events.UserCreated{}, events.UserDeleted{}, events.UserFeatureChanged{}, diff --git a/services/notifications/pkg/command/server.go b/services/notifications/pkg/command/server.go index 03092895021..5387cbbbe3d 100644 --- a/services/notifications/pkg/command/server.go +++ b/services/notifications/pkg/command/server.go @@ -28,8 +28,10 @@ func Server(cfg *config.Config) *cli.Command { Action: func(c *cli.Context) error { logger := logging.Configure(cfg.Service.Name, cfg.Log) + // evs defines a list of events to subscribe to evs := []events.Unmarshaller{ events.ShareCreated{}, + events.SpaceShared{}, } evtsCfg := cfg.Notifications.Events diff --git a/services/notifications/pkg/email/templates/sharedSpace.email.tmpl b/services/notifications/pkg/email/templates/sharedSpace.email.tmpl index f70ec9a4cb6..6ded0d6773b 100644 --- a/services/notifications/pkg/email/templates/sharedSpace.email.tmpl +++ b/services/notifications/pkg/email/templates/sharedSpace.email.tmpl @@ -1 +1,3 @@ -{{ .SpaceSharer }} has invited you to join {{ .SpaceName }} \ No newline at end of file +{{ .SpaceSharer }} has invited you to join {{ .SpaceName }} + +Link: {{ .ShareLink }} \ No newline at end of file diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index dc23e38867a..f52dd9c9861 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -73,7 +73,7 @@ func (s eventsNotifier) Run() error { func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { userResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ - UserId: e.Owner, + UserId: e.Creator, }) if err != nil || userResponse.Status.Code != rpcv1beta1.Code_CODE_OK { s.logger.Error(). @@ -120,7 +120,8 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { Ref: &providerv1beta1.Reference{ ResourceId: &resourceID, }, - FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}}, + // TODO: this filter needs to be implemented + //FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"space.name"}}, }) if err != nil || md.Status.Code != rpcv1beta1.Code_CODE_OK { @@ -142,11 +143,21 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { return } - // old code + shareLink, err := url.JoinPath(e.Executant.Idp, "files/spaces/projects", storagespace.FormatResourceID(*e.ID)) + + if err != nil { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Msg("could not create link to the share") + return + } + msg, err := email.RenderEmailTemplate("sharedSpace.email.tmpl", map[string]string{ // TODO: add additional fields here (like link etc.) - "SpaceSharer": "spacesharer", - "SpaceName": md.Info.Space.Name, + "SpaceSharer": userResponse.GetUser().DisplayName, + "SpaceName": md.GetInfo().GetSpace().Name, + "ShareLink": shareLink, }, s.emailTemplatePath) if err != nil { @@ -155,8 +166,10 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { Str("event", "SpaceCreated"). Msg("Could not render E-Mail template for spaces") } - if e.Executant != nil { - err = s.channel.SendMessage([]string{e.Executant.OpaqueId}, msg, "You were invited to join a space") + if e.GranteeUserID != nil { + err = s.channel.SendMessage([]string{e.GranteeUserID.OpaqueId}, msg, "You have received a share.") + } else if e.GranteeGroupID != nil { + err = s.channel.SendMessageToGroup(e.GranteeGroupID, msg, "You have received a share.") } if err != nil { s.logger.Error(). @@ -241,8 +254,8 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { msg, err := email.RenderEmailTemplate("shareCreated.email.tmpl", map[string]string{ // TODO: add additional fields here (like link etc.) - "ShareSharer": userResponse.User.DisplayName, - "ShareFolder": md.Info.Name, + "ShareSharer": userResponse.GetUser().DisplayName, + "ShareFolder": md.GetInfo().Name, "ShareLink": shareLink, }, s.emailTemplatePath) From 94024125fbe3abb38233ed1a457bf76262695046 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Tue, 20 Sep 2022 13:22:11 +0200 Subject: [PATCH 10/13] enhance templating Signed-off-by: Christian Richter --- .../notifications/pkg/channels/channels.go | 17 +++-- .../email/templates/shareCreated.email.tmpl | 17 ++++- .../email/templates/sharedSpace.email.tmpl | 19 +++++- services/notifications/pkg/service/service.go | 65 ++++++++++++++----- 4 files changed, 91 insertions(+), 27 deletions(-) diff --git a/services/notifications/pkg/channels/channels.go b/services/notifications/pkg/channels/channels.go index 3929d30c068..a86fff60d9b 100644 --- a/services/notifications/pkg/channels/channels.go +++ b/services/notifications/pkg/channels/channels.go @@ -4,6 +4,7 @@ package channels import ( "context" "crypto/tls" + "fmt" "strings" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" @@ -19,9 +20,9 @@ import ( // Channel defines the methods of a communication channel. type Channel interface { // SendMessage sends a message to users. - SendMessage(userIDs []string, msg, subject string) error + SendMessage(userIDs []string, msg, subject, senderDisplayName string) error // SendMessageToGroup sends a message to a group. - SendMessageToGroup(groupdID *groups.GroupId, msg, subject string) error + SendMessageToGroup(groupdID *groups.GroupId, msg, subject, senderDisplayName string) error } // NewMailChannel instantiates a new mail communication channel. @@ -100,7 +101,7 @@ func (m Mail) getMailClient() (*mail.SMTPClient, error) { } // SendMessage sends a message to all given users. -func (m Mail) SendMessage(userIDs []string, msg, subject string) error { +func (m Mail) SendMessage(userIDs []string, msg, subject, senderDisplayName string) error { if m.conf.Notifications.SMTP.Host == "" { return nil } @@ -116,7 +117,11 @@ func (m Mail) SendMessage(userIDs []string, msg, subject string) error { } email := mail.NewMSG() - email.SetFrom(m.conf.Notifications.SMTP.Sender).AddTo(to...) + if senderDisplayName != "" { + email.SetFrom(fmt.Sprintf("%s via owncloud <%s>", senderDisplayName, m.conf.Notifications.SMTP.Sender)).AddTo(to...) + } else { + email.SetFrom(m.conf.Notifications.SMTP.Sender).AddTo(to...) + } email.SetBody(mail.TextPlain, msg) email.SetSubject(subject) @@ -124,7 +129,7 @@ func (m Mail) SendMessage(userIDs []string, msg, subject string) error { } // SendMessageToGroup sends a message to all members of the given group. -func (m Mail) SendMessageToGroup(groupID *groups.GroupId, msg, subject string) error { +func (m Mail) SendMessageToGroup(groupID *groups.GroupId, msg, subject, senderDisplayName string) error { // TODO We need an authenticated context here... res, err := m.gatewayClient.GetGroup(context.Background(), &groups.GetGroupRequest{GroupId: groupID}) if err != nil { @@ -139,7 +144,7 @@ func (m Mail) SendMessageToGroup(groupID *groups.GroupId, msg, subject string) e members = append(members, id.OpaqueId) } - return m.SendMessage(members, msg, subject) + return m.SendMessage(members, msg, subject, senderDisplayName) } func (m Mail) getReceiverAddresses(receivers []string) ([]string, error) { diff --git a/services/notifications/pkg/email/templates/shareCreated.email.tmpl b/services/notifications/pkg/email/templates/shareCreated.email.tmpl index 376775dfad5..5c0b3fca0bf 100644 --- a/services/notifications/pkg/email/templates/shareCreated.email.tmpl +++ b/services/notifications/pkg/email/templates/shareCreated.email.tmpl @@ -1,3 +1,18 @@ +Hello {{ .ShareGrantee }}, + {{ .ShareSharer }} has shared {{ .ShareFolder }} with you. -Link: {{ .ShareLink }} \ No newline at end of file +Click here to view it: {{ .ShareLink }} + +---------------------------------------------------------- + +Hallo {{ .Grantee }}, + +{{ .ShareSharer }} hat dich zu {{ .ShareFolder }} eingeladen. + +Klicke hier zum Anzeigen: {{ .ShareLink }} + + +--- +ownCloud - Store. Share. Work. +https://owncloud.com \ No newline at end of file diff --git a/services/notifications/pkg/email/templates/sharedSpace.email.tmpl b/services/notifications/pkg/email/templates/sharedSpace.email.tmpl index 6ded0d6773b..16bef45f609 100644 --- a/services/notifications/pkg/email/templates/sharedSpace.email.tmpl +++ b/services/notifications/pkg/email/templates/sharedSpace.email.tmpl @@ -1,3 +1,18 @@ -{{ .SpaceSharer }} has invited you to join {{ .SpaceName }} +Hello {{ .SpaceGrantee }}, -Link: {{ .ShareLink }} \ No newline at end of file +{{ .SpaceSharer }} has invited you to join {{ .SpaceName }}. + +Click here to view it: {{ .ShareLink }} + +---------------------------------------------------------- + +Hallo {{ .SpaceGrantee }}, + +{{ .SpaceSharer }} hat dich in den Space {{ .SpaceName }} eingeladen. + +Klicke hier zum Anzeigen: {{ .ShareLink }} + + +--- +ownCloud - Store. Share. Work. +https://owncloud.com \ No newline at end of file diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index f52dd9c9861..1065c78f624 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -2,6 +2,7 @@ package service import ( "context" + "fmt" "net/url" "os" "os/signal" @@ -72,10 +73,21 @@ func (s eventsNotifier) Run() error { } func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { - userResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ + sharerUserResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ UserId: e.Creator, }) - if err != nil || userResponse.Status.Code != rpcv1beta1.Code_CODE_OK { + if err != nil || sharerUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK { + s.logger.Error(). + Err(err). + Str("event", "SpaceCreated"). + Msg("Could not get user response from gatway client") + return + } + + granteeUserResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ + UserId: e.Creator, + }) + if err != nil || sharerUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK { s.logger.Error(). Err(err). Str("event", "SpaceCreated"). @@ -83,7 +95,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { return } // Get auth context - ownerCtx := ctxpkg.ContextSetUser(context.Background(), userResponse.User) + ownerCtx := ctxpkg.ContextSetUser(context.Background(), sharerUserResponse.User) authRes, err := s.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{ Type: "machine", ClientId: "userid:" + e.Executant.OpaqueId, @@ -153,11 +165,12 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { return } + sharerDisplayName := sharerUserResponse.GetUser().DisplayName msg, err := email.RenderEmailTemplate("sharedSpace.email.tmpl", map[string]string{ - // TODO: add additional fields here (like link etc.) - "SpaceSharer": userResponse.GetUser().DisplayName, - "SpaceName": md.GetInfo().GetSpace().Name, - "ShareLink": shareLink, + "SpaceGrantee": granteeUserResponse.GetUser().DisplayName, + "SpaceSharer": sharerDisplayName, + "SpaceName": md.GetInfo().GetSpace().Name, + "ShareLink": shareLink, }, s.emailTemplatePath) if err != nil { @@ -166,10 +179,12 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { Str("event", "SpaceCreated"). Msg("Could not render E-Mail template for spaces") } + + emailSubject := fmt.Sprintf("%s invited you to join %s", sharerUserResponse.GetUser().DisplayName, md.GetInfo().GetSpace().Name) if e.GranteeUserID != nil { - err = s.channel.SendMessage([]string{e.GranteeUserID.OpaqueId}, msg, "You have received a share.") + err = s.channel.SendMessage([]string{e.GranteeUserID.OpaqueId}, msg, emailSubject, sharerDisplayName) } else if e.GranteeGroupID != nil { - err = s.channel.SendMessageToGroup(e.GranteeGroupID, msg, "You have received a share.") + err = s.channel.SendMessageToGroup(e.GranteeGroupID, msg, emailSubject, sharerDisplayName) } if err != nil { s.logger.Error(). @@ -180,10 +195,21 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { } func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { - userResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ + sharerUserResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ UserId: e.Sharer, }) - if err != nil || userResponse.Status.Code != rpcv1beta1.Code_CODE_OK { + if err != nil || sharerUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK { + s.logger.Error(). + Err(err). + Str("event", "ShareCreated"). + Msg("Could not get user response from gatway client") + return + } + + granteeUserResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ + UserId: e.GranteeUserID, + }) + if err != nil || sharerUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK { s.logger.Error(). Err(err). Str("event", "ShareCreated"). @@ -192,7 +218,7 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { } // Get auth context - ownerCtx := ctxpkg.ContextSetUser(context.Background(), userResponse.User) + ownerCtx := ctxpkg.ContextSetUser(context.Background(), sharerUserResponse.User) authRes, err := s.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{ Type: "machine", ClientId: "userid:" + e.Sharer.OpaqueId, @@ -252,11 +278,12 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { return } + sharerDisplayName := sharerUserResponse.GetUser().DisplayName msg, err := email.RenderEmailTemplate("shareCreated.email.tmpl", map[string]string{ - // TODO: add additional fields here (like link etc.) - "ShareSharer": userResponse.GetUser().DisplayName, - "ShareFolder": md.GetInfo().Name, - "ShareLink": shareLink, + "ShareGrantee": granteeUserResponse.GetUser().DisplayName, + "ShareSharer": sharerDisplayName, + "ShareFolder": md.GetInfo().Name, + "ShareLink": shareLink, }, s.emailTemplatePath) if err != nil { @@ -265,10 +292,12 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { Str("event", "ShareCreated"). Msg("Could not render E-Mail template for shares") } + + emailSubject := fmt.Sprintf("%s shared %s with you", sharerUserResponse.GetUser().DisplayName, md.GetInfo().Name) if e.GranteeUserID != nil { - err = s.channel.SendMessage([]string{e.GranteeUserID.OpaqueId}, msg, "You have received a share.") + err = s.channel.SendMessage([]string{e.GranteeUserID.OpaqueId}, msg, emailSubject, sharerDisplayName) } else if e.GranteeGroupID != nil { - err = s.channel.SendMessageToGroup(e.GranteeGroupID, msg, "You have received a share.") + err = s.channel.SendMessageToGroup(e.GranteeGroupID, msg, emailSubject, sharerDisplayName) } if err != nil { s.logger.Error(). From 6c6640959c6cabbcbca90e7291af14ecf765dd77 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Tue, 20 Sep 2022 16:06:14 +0200 Subject: [PATCH 11/13] add changelog Signed-off-by: Christian Richter --- changelog/unreleased/add-email-templating.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changelog/unreleased/add-email-templating.md diff --git a/changelog/unreleased/add-email-templating.md b/changelog/unreleased/add-email-templating.md new file mode 100644 index 00000000000..3b7e5930d6b --- /dev/null +++ b/changelog/unreleased/add-email-templating.md @@ -0,0 +1,7 @@ +Enhancement: Add Email templating + +We have added email templating to ocis. Which are send on the SpaceShared and ShareCreated event. + +https://github.com/owncloud/ocis/pull/4564 +https://github.com/owncloud/ocis/issues/4303 +https://github.com/cs3org/reva/pull/3252 \ No newline at end of file From 7a8e1b8106d6f59b1cf0b8177aaf4407e629c1ec Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Tue, 20 Sep 2022 16:56:38 +0200 Subject: [PATCH 12/13] backport url.JoinPath for go1.18 Signed-off-by: Christian Richter --- services/notifications/pkg/service/service.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index 1065c78f624..eb7b324d027 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -6,6 +6,7 @@ import ( "net/url" "os" "os/signal" + "path" "syscall" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" @@ -155,7 +156,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { return } - shareLink, err := url.JoinPath(e.Executant.Idp, "files/spaces/projects", storagespace.FormatResourceID(*e.ID)) + shareLink, err := urlJoinPath(e.Executant.Idp, "files/spaces/projects", storagespace.FormatResourceID(*e.ID)) if err != nil { s.logger.Error(). @@ -268,7 +269,7 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { return } - shareLink, err := url.JoinPath(e.Executant.Idp, "files/shares/with-me") + shareLink, err := urlJoinPath(e.Executant.Idp, "files/shares/with-me") if err != nil { s.logger.Error(). @@ -306,3 +307,13 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { Msg("failed to send a message") } } + +// TODO: this function is a backport for go1.19 url.JoinPath, upon go bump, replace this +func urlJoinPath(base string, elements ...string) (string, error) { + u, err := url.Parse(base) + if err != nil { + return "", err + } + u.Path = path.Join(append([]string{u.Path}, elements...)...) + return u.String(), nil +} From e96ab9f9c812b4d1d7f1991499e07af939a6e0e6 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Thu, 22 Sep 2022 09:37:54 +0200 Subject: [PATCH 13/13] incorporate requested changes Signed-off-by: Christian Richter --- services/notifications/pkg/email/email.go | 16 +++++----------- services/notifications/pkg/service/service.go | 10 +++++----- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/services/notifications/pkg/email/email.go b/services/notifications/pkg/email/email.go index c0709b9ea17..d3302ccdf74 100644 --- a/services/notifications/pkg/email/email.go +++ b/services/notifications/pkg/email/email.go @@ -16,23 +16,17 @@ var ( func RenderEmailTemplate(templateName string, templateVariables map[string]string, emailTemplatePath string) (string, error) { var err error var tpl *template.Template - templateHasBeenFound := false - if emailTemplatePath != "" { - // try to lookup the files in the filesystem - tpl, err = template.ParseFiles(filepath.Join(emailTemplatePath, templateName)) - if err == nil { - templateHasBeenFound = true - } - } - if !templateHasBeenFound { + // try to lookup the files in the filesystem + tpl, err = template.ParseFiles(filepath.Join(emailTemplatePath, templateName)) + if err != nil { // template has not been found in the fs, or path has not been specified => use embed templates tpl, err = template.ParseFS(templatesFS, filepath.Join("templates/", templateName)) if err != nil { return "", err } } - writer := bytes.NewBufferString("") - err = tpl.Execute(writer, templateVariables) + var writer bytes.Buffer + err = tpl.Execute(&writer, templateVariables) if err != nil { return "", err } diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index eb7b324d027..d2f35e11774 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -86,7 +86,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { } granteeUserResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ - UserId: e.Creator, + UserId: e.GranteeUserID, }) if err != nil || sharerUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK { s.logger.Error(). @@ -102,7 +102,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { ClientId: "userid:" + e.Executant.OpaqueId, ClientSecret: s.machineAuthAPIKey, }) - if err != nil || authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { + if err != nil { s.logger.Error(). Err(err). Str("event", "SpaceCreated"). @@ -137,7 +137,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { //FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"space.name"}}, }) - if err != nil || md.Status.Code != rpcv1beta1.Code_CODE_OK { + if err != nil { s.logger.Error(). Err(err). Str("event", "ShareCreated"). @@ -225,7 +225,7 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { ClientId: "userid:" + e.Sharer.OpaqueId, ClientSecret: s.machineAuthAPIKey, }) - if err != nil || authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { + if err != nil { s.logger.Error(). Err(err). Str("event", "ShareCreated"). @@ -250,7 +250,7 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}}, }) - if err != nil || md.Status.Code != rpcv1beta1.Code_CODE_OK { + if err != nil { s.logger.Error(). Err(err). Str("event", "ShareCreated").