diff --git a/services/notifications/pkg/channels/channels.go b/services/notifications/pkg/channels/channels.go
index eaf51e4e70b..a535882a751 100644
--- a/services/notifications/pkg/channels/channels.go
+++ b/services/notifications/pkg/channels/channels.go
@@ -8,9 +8,6 @@ import (
"strings"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
- groups "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
- rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
- "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/services/notifications/pkg/config"
"github.com/pkg/errors"
@@ -20,31 +17,24 @@ import (
// Channel defines the methods of a communication channel.
type Channel interface {
// SendMessage sends a message to users.
- SendMessage(ctx context.Context, userIDs []string, msg, subject, senderDisplayName string) error
- // SendMessageToGroup sends a message to a group.
- SendMessageToGroup(ctx context.Context, groupdID *groups.GroupId, msg, subject, senderDisplayName string) error
+ SendMessage(ctx context.Context, message *Message) error
+}
+
+// Message represent the already rendered message including the user id opaqueID
+type Message struct {
+ Sender string
+ Recipient []string
+ Subject string
+ TextBody string
+ HtmlBody string
+ AttachInline map[string][]byte
}
// NewMailChannel instantiates a new mail communication channel.
func NewMailChannel(cfg config.Config, logger log.Logger) (Channel, error) {
- tm, err := pool.StringToTLSMode(cfg.Notifications.GRPCClientTLS.Mode)
- if err != nil {
- logger.Error().Err(err).Msg("could not get gateway client tls mode")
- return nil, err
- }
- gc, err := pool.GetGatewayServiceClient(cfg.Notifications.RevaGateway,
- pool.WithTLSCACert(cfg.Notifications.GRPCClientTLS.CACert),
- pool.WithTLSMode(tm),
- )
- if err != nil {
- logger.Error().Err(err).Msg("could not get gateway client")
- return nil, err
- }
-
return Mail{
- gatewayClient: gc,
- conf: cfg,
- logger: logger,
+ conf: cfg,
+ logger: logger,
}, nil
}
@@ -111,73 +101,30 @@ func (m Mail) getMailClient() (*mail.SMTPClient, error) {
}
// SendMessage sends a message to all given users.
-func (m Mail) SendMessage(ctx context.Context, userIDs []string, msg, subject, senderDisplayName string) error {
+func (m Mail) SendMessage(ctx context.Context, message *Message) error {
if m.conf.Notifications.SMTP.Host == "" {
return nil
}
- to, err := m.getReceiverAddresses(ctx, userIDs)
- if err != nil {
- return err
- }
-
smtpClient, err := m.getMailClient()
if err != nil {
return err
}
email := mail.NewMSG()
- if senderDisplayName != "" {
- email.SetFrom(fmt.Sprintf("%s via %s", senderDisplayName, m.conf.Notifications.SMTP.Sender)).AddTo(to...)
+ if message.Sender != "" {
+ email.SetFrom(fmt.Sprintf("%s via %s", message.Sender, m.conf.Notifications.SMTP.Sender)).AddTo(message.Recipient...)
} else {
- 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(ctx context.Context, groupID *groups.GroupId, msg, subject, senderDisplayName string) error {
- res, err := m.gatewayClient.GetGroup(ctx, &groups.GetGroupRequest{GroupId: groupID})
- if err != nil {
- return err
- }
- if res.Status.Code != rpc.Code_CODE_OK {
- return errors.New("could not get group")
+ email.SetFrom(m.conf.Notifications.SMTP.Sender).AddTo(message.Recipient...)
}
-
- members := make([]string, 0, len(res.Group.Members))
- for _, id := range res.Group.Members {
- members = append(members, id.OpaqueId)
- }
-
- return m.SendMessage(ctx, members, msg, subject, senderDisplayName)
-}
-
-func (m Mail) getReceiverAddresses(ctx context.Context, receivers []string) ([]string, error) {
- addresses := make([]string, 0, len(receivers))
- for _, id := range receivers {
- // Authenticate is too costly but at the moment our only option to get the user.
- // We don't have an authenticated context so calling `GetUser` doesn't work.
- res, err := m.gatewayClient.Authenticate(ctx, &gateway.AuthenticateRequest{
- Type: "machine",
- ClientId: "userid:" + id,
- ClientSecret: m.conf.Notifications.MachineAuthAPIKey,
- })
- if err != nil {
- return nil, err
- }
- if res.Status.Code != rpc.Code_CODE_OK {
- m.logger.Error().
- Interface("status", res.Status).
- Str("receiver_id", id).
- Msg("could not get user")
- continue
+ email.SetSubject(message.Subject)
+ email.SetBody(mail.TextPlain, message.TextBody)
+ if message.HtmlBody != "" {
+ email.AddAlternative(mail.TextHTML, message.HtmlBody)
+ for filename, data := range message.AttachInline {
+ email.Attach(&mail.File{Data: data, Name: filename, Inline: true})
}
- addresses = append(addresses, res.User.Mail)
}
- return addresses, nil
+ return email.Send(smtpClient)
}
diff --git a/services/notifications/pkg/email/composer.go b/services/notifications/pkg/email/composer.go
index ece92e0a21c..8a33c20cf42 100644
--- a/services/notifications/pkg/email/composer.go
+++ b/services/notifications/pkg/email/composer.go
@@ -14,10 +14,48 @@ var (
_domain = "notifications"
)
+func NewTextTemplate(mt MessageTemplate, locale string, translationPath string, vars map[string]interface{}) (MessageTemplate, error) {
+ var err error
+ mt.Subject, err = ComposeMessage(mt.Subject, locale, translationPath, vars)
+ if err != nil {
+ return mt, err
+ }
+ mt.Greeting, err = ComposeMessage(mt.Greeting, locale, translationPath, vars)
+ if err != nil {
+ return mt, err
+ }
+ mt.MessageBody, err = ComposeMessage(mt.MessageBody, locale, translationPath, vars)
+ if err != nil {
+ return mt, err
+ }
+ mt.CallToAction, err = ComposeMessage(mt.CallToAction, locale, translationPath, vars)
+ if err != nil {
+ return mt, err
+ }
+ return mt, nil
+}
+
+func NewHtmlTemplate(mt MessageTemplate, locale string, translationPath string, vars map[string]interface{}) (MessageTemplate, error) {
+ var err error
+ mt.Greeting, err = ComposeMessage(newlineToBr(mt.Greeting), locale, translationPath, vars)
+ if err != nil {
+ return mt, err
+ }
+ mt.MessageBody, err = ComposeMessage(newlineToBr(mt.MessageBody), locale, translationPath, vars)
+ if err != nil {
+ return mt, err
+ }
+ mt.CallToAction, err = ComposeMessage(callToActionToHtml(mt.CallToAction), locale, translationPath, vars)
+ if err != nil {
+ return mt, err
+ }
+ return mt, nil
+}
+
// ComposeMessage renders the message based on template
-func ComposeMessage(template, locale string, path string) string {
+func ComposeMessage(template, locale string, path string, vars map[string]interface{}) (string, error) {
raw := loadTemplate(template, locale, path)
- return replacePlaceholders(raw)
+ return executeRaw(replacePlaceholders(raw), vars)
}
func loadTemplate(template, locale string, path string) string {
@@ -39,3 +77,15 @@ func replacePlaceholders(raw string) string {
}
return raw
}
+
+func newlineToBr(s string) string {
+ return strings.Replace(s, "\n", "
", -1)
+}
+
+func callToActionToHtml(s string) string {
+ if strings.TrimSpace(s) == "" {
+ return ""
+ }
+ s = strings.TrimSpace(strings.TrimRight(s, "{{ .ShareLink }}"))
+ return `` + s + ``
+}
diff --git a/services/notifications/pkg/email/email.go b/services/notifications/pkg/email/email.go
index 283559b9c5f..4a13e8d9bc5 100644
--- a/services/notifications/pkg/email/email.go
+++ b/services/notifications/pkg/email/email.go
@@ -8,7 +8,11 @@ import (
"embed"
"html"
"html/template"
+ "os"
"path/filepath"
+ "strings"
+
+ "github.com/owncloud/ocis/v2/services/notifications/pkg/channels"
)
var (
@@ -17,44 +21,46 @@ var (
)
// RenderEmailTemplate renders the email template for a new share
-func RenderEmailTemplate(mt MessageTemplate, locale string, emailTemplatePath string, translationPath string, vars map[string]interface{}) (string, string, error) {
- // translate a message
- mt.Subject = ComposeMessage(mt.Subject, locale, translationPath)
- mt.Greeting = ComposeMessage(mt.Greeting, locale, translationPath)
- mt.MessageBody = ComposeMessage(mt.MessageBody, locale, translationPath)
- mt.CallToAction = ComposeMessage(mt.CallToAction, locale, translationPath)
-
- // replace the body email placeholders with the values
- subject, err := executeRaw(mt.Subject, vars)
+func RenderEmailTemplate(mt MessageTemplate, locale string, emailTemplatePath string, translationPath string, vars map[string]interface{}) (*channels.Message, error) {
+ textMt, err := NewTextTemplate(mt, locale, translationPath, vars)
+ if err != nil {
+ return nil, err
+ }
+ textBody, err := emailTemplate(emailTemplatePath, textMt)
if err != nil {
- return "", "", err
+ return nil, err
}
- // replace the body email template placeholders with the translated template
- rawBody, err := executeEmailTemplate(emailTemplatePath, mt)
+ htmlMt, err := NewHtmlTemplate(mt, locale, translationPath, vars)
if err != nil {
- return "", "", err
+ return nil, err
}
- // replace the body email placeholders with the values
- body, err := executeRaw(rawBody, vars)
+ htmlBody, err := htmlEmailTemplate(emailTemplatePath, htmlMt)
if err != nil {
- return "", "", err
+ return nil, err
}
- return subject, body, nil
-}
-func executeEmailTemplate(emailTemplatePath string, mt MessageTemplate) (string, error) {
- var err error
- var tpl *template.Template
- // try to lookup the files in the filesystem
- tpl, err = template.ParseFiles(filepath.Join(emailTemplatePath, mt.bodyTemplate))
+ var data map[string][]byte
+ data, err = readImages(emailTemplatePath)
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/", mt.bodyTemplate))
+ data, err = readFs()
if err != nil {
- return "", err
+ return nil, err
}
}
+ return &channels.Message{
+ Subject: textMt.Subject,
+ TextBody: textBody,
+ HtmlBody: htmlBody,
+ AttachInline: data,
+ }, nil
+}
+
+func emailTemplate(emailTemplatePath string, mt MessageTemplate) (string, error) {
+ tpl, err := parseTemplate(emailTemplatePath, mt.textTemplate)
+ if err != nil {
+ return "", err
+ }
str, err := executeTemplate(tpl, mt)
if err != nil {
return "", err
@@ -62,6 +68,37 @@ func executeEmailTemplate(emailTemplatePath string, mt MessageTemplate) (string,
return html.UnescapeString(str), err
}
+func htmlEmailTemplate(emailTemplatePath string, mt MessageTemplate) (string, error) {
+ mailTpl, err := parseTemplate(emailTemplatePath, filepath.Join(emailTemplatePath, "html", "email.html.tmpl"))
+ if err != nil {
+ return "", err
+ }
+ str, err := executeTemplate(mailTpl, map[string]interface{}{
+ "Greeting": template.HTML(html.UnescapeString(strings.TrimSpace(mt.Greeting))),
+ "MessageBody": template.HTML(html.UnescapeString(strings.TrimSpace(mt.MessageBody))),
+ "CallToAction": template.HTML(html.UnescapeString(strings.TrimSpace(mt.CallToAction))),
+ })
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSpace(str), err
+}
+
+func parseTemplate(emailTemplatePath string, file string) (*template.Template, error) {
+ var err error
+ var tpl *template.Template
+ // try to lookup the files in the filesystem
+ tpl, err = template.ParseFiles(filepath.Join(emailTemplatePath, file))
+ 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", file))
+ if err != nil {
+ return nil, err
+ }
+ }
+ return tpl, err
+}
+
func executeRaw(raw string, vars map[string]interface{}) (string, error) {
tpl, err := template.New("").Parse(raw)
if err != nil {
@@ -77,3 +114,66 @@ func executeTemplate(tpl *template.Template, vars any) (string, error) {
}
return writer.String(), nil
}
+
+func readFs() (map[string][]byte, error) {
+ dir := filepath.Join("templates", "html", "img")
+ entries, err := templatesFS.ReadDir(dir)
+ if err != nil {
+ return nil, err
+ }
+
+ list := make(map[string][]byte)
+ for _, e := range entries {
+ if !e.IsDir() {
+ file, err := templatesFS.ReadFile(filepath.Join(dir, e.Name()))
+ if err != nil {
+ return nil, err
+ }
+ if !validateMime(file) {
+ continue
+ }
+ list[e.Name()] = file
+ }
+ }
+ return list, nil
+}
+
+func readImages(emailTemplatePath string) (map[string][]byte, error) {
+ dir := filepath.Join(emailTemplatePath, "html", "img")
+ entries, err := os.ReadDir(dir)
+ if err != nil {
+ return nil, err
+ }
+ list := make(map[string][]byte)
+ for _, e := range entries {
+ if !e.IsDir() {
+ file, err := os.ReadFile(filepath.Join(dir, e.Name()))
+ if err != nil {
+ return nil, err
+ }
+ if !validateMime(file) {
+ continue
+ }
+ list[e.Name()] = file
+ }
+ }
+ return list, nil
+}
+
+// signature image formats signature https://go.dev/src/net/http/sniff.go #L:118
+var signature = map[string]string{
+ "\xff\xd8\xff": "image/jpeg",
+ "\x89PNG\r\n\x1a\n": "image/png",
+ "GIF87a": "image/gif",
+ "GIF89a": "image/gif",
+}
+
+// validateMime validate the mime type of image file from its first few bytes
+func validateMime(incipit []byte) bool {
+ for s := range signature {
+ if strings.HasPrefix(string(incipit), s) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/services/notifications/pkg/email/templates.go b/services/notifications/pkg/email/templates.go
index f8161521764..bb0723d6933 100644
--- a/services/notifications/pkg/email/templates.go
+++ b/services/notifications/pkg/email/templates.go
@@ -7,7 +7,8 @@ func Template(s string) string { return s }
var (
// Shares
ShareCreated = MessageTemplate{
- bodyTemplate: "shares/shareCreated.email.body.tmpl",
+ textTemplate: "shares/shareCreated.email.body.tmpl",
+ htmlTemplate: "shares/shareCreated.email.body.html.tmpl",
// ShareCreated email template, Subject field (resolves directly)
Subject: Template(`{ShareSharer} shared '{ShareFolder}' with you`),
// ShareCreated email template, resolves via {{ .Greeting }}
@@ -19,7 +20,8 @@ var (
}
ShareExpired = MessageTemplate{
- bodyTemplate: "shares/shareExpired.email.body.tmpl",
+ textTemplate: "shares/shareExpired.email.body.tmpl",
+ htmlTemplate: "shares/shareExpired.email.body.html.tmpl",
// ShareExpired email template, Subject field (resolves directly)
Subject: Template(`Share to '{ShareFolder}' expired at {ExpiredAt}`),
// ShareExpired email template, resolves via {{ .Greeting }}
@@ -32,7 +34,8 @@ Even though this share has been revoked you still might have access through othe
// Spaces templates
SharedSpace = MessageTemplate{
- bodyTemplate: "spaces/sharedSpace.email.body.tmpl",
+ textTemplate: "spaces/sharedSpace.email.body.tmpl",
+ htmlTemplate: "spaces/sharedSpace.email.body.html.tmpl",
// SharedSpace email template, Subject field (resolves directly)
Subject: Template("{SpaceSharer} invited you to join {SpaceName}"),
// SharedSpace email template, resolves via {{ .Greeting }}
@@ -44,7 +47,8 @@ Even though this share has been revoked you still might have access through othe
}
UnsharedSpace = MessageTemplate{
- bodyTemplate: "spaces/unsharedSpace.email.body.tmpl",
+ textTemplate: "spaces/unsharedSpace.email.body.tmpl",
+ htmlTemplate: "spaces/unsharedSpace.email.body.html.tmpl",
// UnsharedSpace email template, Subject field (resolves directly)
Subject: Template(`{SpaceSharer} removed you from {SpaceName}`),
// UnsharedSpace email template, resolves via {{ .Greeting }}
@@ -58,7 +62,8 @@ You might still have access through your other groups or direct membership.`),
}
MembershipExpired = MessageTemplate{
- bodyTemplate: "spaces/membershipExpired.email.body.tmpl",
+ textTemplate: "spaces/membershipExpired.email.body.tmpl",
+ htmlTemplate: "spaces/membershipExpired.email.body.html.tmpl",
// MembershipExpired email template, Subject field (resolves directly)
Subject: Template(`Membership of '{SpaceName}' expired at {ExpiredAt}`),
// MembershipExpired email template, resolves via {{ .Greeting }}
@@ -84,8 +89,11 @@ var _placeholders = map[string]string{
// MessageTemplate is the data structure for the email
type MessageTemplate struct {
- // bodyTemplate represent the path to .tmpl file
- bodyTemplate string
+ // textTemplate represent the path to text plain .tmpl file
+ textTemplate string
+ // htmlTemplate represent the path to html .tmpl file
+ htmlTemplate string
+ // The fields below represent the placeholders for the translatable templates
Subject string
Greeting string
MessageBody string
diff --git a/services/notifications/pkg/email/templates/html/email.html.tmpl b/services/notifications/pkg/email/templates/html/email.html.tmpl
new file mode 100644
index 00000000000..5ba3adda1a2
--- /dev/null
+++ b/services/notifications/pkg/email/templates/html/email.html.tmpl
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+ {{ .Greeting }}
+
+ {{ .MessageBody }}
+ {{if ne .CallToAction "" }}
+
{{ .CallToAction }}
+ {{end}}
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+
+
diff --git a/services/notifications/pkg/email/templates/html/img/logo-mail.gif b/services/notifications/pkg/email/templates/html/img/logo-mail.gif
new file mode 100644
index 00000000000..c8bb0c7e51a
Binary files /dev/null and b/services/notifications/pkg/email/templates/html/img/logo-mail.gif differ
diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go
index e20b917c283..b518887cc4d 100644
--- a/services/notifications/pkg/service/service.go
+++ b/services/notifications/pkg/service/service.go
@@ -97,90 +97,91 @@ func (s eventsNotifier) Run() error {
}
}
-// recipient represent the already rendered message including the user id opaqueID
-type recipient struct {
- opaqueID string
- subject string
- msg string
-}
-
func (s eventsNotifier) render(ctx context.Context, template email.MessageTemplate,
- granteeFieldName string, fields map[string]interface{}, granteeList []*user.UserId) ([]recipient, error) {
+ granteeFieldName string, fields map[string]interface{}, granteeList []*user.User, sender string) ([]*channels.Message, error) {
// Render the Email Template for each user
- recipientList := make([]recipient, len(granteeList))
- for i, userID := range granteeList {
- locale, err := s.getUserLang(ctx, userID)
+ messageList := make([]*channels.Message, len(granteeList))
+ for i, usr := range granteeList {
+ locale, err := s.getUserLang(ctx, usr.GetId())
if err != nil {
return nil, err
}
- grantee, err := s.getUserName(ctx, userID)
- if err != nil {
- return nil, err
- }
- fields[granteeFieldName] = grantee
+ fields[granteeFieldName] = usr.GetDisplayName()
- subj, msg, err := email.RenderEmailTemplate(template, locale, s.emailTemplatePath, s.translationPath, fields)
+ rendered, err := email.RenderEmailTemplate(template, locale, s.emailTemplatePath, s.translationPath, fields)
if err != nil {
return nil, err
}
- recipientList[i] = recipient{opaqueID: userID.GetOpaqueId(), subject: subj, msg: msg}
+ rendered.Sender = sender
+ rendered.Recipient = []string{usr.GetMail()}
+ messageList[i] = rendered
}
- return recipientList, nil
+ return messageList, nil
}
-func (s eventsNotifier) send(ctx context.Context, recipientList []recipient, sender string) {
+func (s eventsNotifier) send(ctx context.Context, recipientList []*channels.Message) {
for _, r := range recipientList {
- err := s.channel.SendMessage(ctx, []string{r.opaqueID}, r.msg, r.subject, sender)
+ err := s.channel.SendMessage(ctx, r)
if err != nil {
s.logger.Error().Err(err).Str("event", "SendEmail").Msg("failed to send a message")
}
}
}
-func (s eventsNotifier) getGranteeList(ctx context.Context, owner, u *user.UserId, g *group.GroupId) ([]*user.UserId, error) {
+func (s eventsNotifier) getGranteeList(ctx context.Context, owner, u *user.UserId, g *group.GroupId) ([]*user.User, error) {
switch {
case u != nil:
- return []*user.UserId{u}, nil
+ usr, err := s.getUser(ctx, u)
+ if err != nil {
+ return nil, err
+ }
+ return []*user.User{usr}, nil
case g != nil:
res, err := s.gwClient.GetGroup(ctx, &group.GetGroupRequest{GroupId: g})
if err != nil {
return nil, err
}
- if res.Status.Code != rpc.Code_CODE_OK {
+ if res.GetStatus().GetCode() != rpc.Code_CODE_OK {
return nil, errors.New("could not get group")
}
- for i, userID := range res.GetGroup().GetMembers() {
+ userList := make([]*user.User, 0, len(res.GetGroup().GetMembers()))
+ for _, userID := range res.GetGroup().GetMembers() {
// remove an executant from a list
if userID.GetOpaqueId() == owner.GetOpaqueId() {
- res.Group.Members[i] = res.Group.Members[len(res.Group.Members)-1]
- return res.Group.Members[:len(res.Group.Members)-1], nil
+ continue
+ }
+ usr, err := s.getUser(ctx, userID)
+ if err != nil {
+ return nil, err
}
+ userList = append(userList, usr)
}
- return res.Group.Members, nil
+ return userList, nil
default:
return nil, errors.New("need at least one non-nil grantee")
}
}
-func (s eventsNotifier) getUserName(ctx context.Context, u *user.UserId) (string, error) {
+// func (s eventsNotifier) getUserName(ctx context.Context, u *user.UserId) (string, error) {
+func (s eventsNotifier) getUser(ctx context.Context, u *user.UserId) (*user.User, error) {
if u == nil {
- return "", errors.New("need at least one non-nil grantee")
+ return nil, errors.New("need at least one non-nil grantee")
}
r, err := s.gwClient.GetUser(ctx, &user.GetUserRequest{UserId: u})
if err != nil {
- return "", err
+ return nil, err
}
- if r.Status.Code != rpc.Code_CODE_OK {
- return "", fmt.Errorf("unexpected status code from gateway client: %d", r.GetStatus().GetCode())
+ if r.GetStatus().GetCode() != rpc.Code_CODE_OK {
+ return nil, fmt.Errorf("unexpected status code from gateway client: %d", r.GetStatus().GetCode())
}
- return r.GetUser().GetDisplayName(), nil
+ return r.GetUser(), nil
}
func (s eventsNotifier) getUserLang(ctx context.Context, u *user.UserId) (string, error) {
- granteeCtx := metadata.Set(ctx, middleware.AccountID, u.OpaqueId)
+ granteeCtx := metadata.Set(ctx, middleware.AccountID, u.GetOpaqueId())
if resp, err := s.valueService.GetValueByUniqueIdentifiers(granteeCtx,
&settingssvc.GetValueByUniqueIdentifiersRequest{
- AccountUuid: u.OpaqueId,
+ AccountUuid: u.GetOpaqueId(),
SettingId: defaults.SettingUUIDProfileLanguage,
}); err == nil {
if resp == nil {
@@ -206,8 +207,7 @@ func (s eventsNotifier) getResourceInfo(ctx context.Context, resourceID *provide
if err != nil {
return nil, err
}
-
- if md.Status.Code != rpc.Code_CODE_OK {
+ if md.GetStatus().GetCode() != rpc.Code_CODE_OK {
return nil, fmt.Errorf("could not resource info: %s", md.Status.Message)
}
return md.GetInfo(), nil
diff --git a/services/notifications/pkg/service/service_test.go b/services/notifications/pkg/service/service_test.go
index 4f6017eded5..fdc64410c9f 100644
--- a/services/notifications/pkg/service/service_test.go
+++ b/services/notifications/pkg/service/service_test.go
@@ -5,7 +5,6 @@ import (
"time"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
- group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
@@ -19,6 +18,7 @@ import (
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
"github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults"
+ "github.com/owncloud/ocis/v2/services/notifications/pkg/channels"
"github.com/owncloud/ocis/v2/services/notifications/pkg/service"
"github.com/test-go/testify/mock"
"go-micro.dev/v4/client"
@@ -32,12 +32,14 @@ var _ = Describe("Notifications", func() {
Id: &user.UserId{
OpaqueId: "sharer",
},
+ Mail: "sharer@owncloud.com",
DisplayName: "Dr. S. Harer",
}
sharee = &user.User{
Id: &user.UserId{
OpaqueId: "sharee",
},
+ Mail: "sharee@owncloud.com",
DisplayName: "Eric Expireling",
}
resourceid = &provider.ResourceId{
@@ -78,7 +80,7 @@ var _ = Describe("Notifications", func() {
},
Entry("Share Created", testChannel{
- expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true},
+ expectedReceipients: []string{sharee.GetMail()},
expectedSubject: "Dr. S. Harer shared 'secrets of the board' with you",
expectedMessage: `Hello Eric Expireling
@@ -103,7 +105,7 @@ https://owncloud.com
}),
Entry("Share Expired", testChannel{
- expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true},
+ expectedReceipients: []string{sharee.GetMail()},
expectedSubject: "Share to 'secrets of the board' expired at 2023-04-17 16:42:00",
expectedMessage: `Hello Eric Expireling,
@@ -128,7 +130,7 @@ https://owncloud.com
}),
Entry("Added to Space", testChannel{
- expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true},
+ expectedReceipients: []string{sharee.GetMail()},
expectedSubject: "Dr. S. Harer invited you to join secret space",
expectedMessage: `Hello Eric Expireling,
@@ -153,7 +155,7 @@ https://owncloud.com
}),
Entry("Removed from Space", testChannel{
- expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true},
+ expectedReceipients: []string{sharee.GetMail()},
expectedSubject: "Dr. S. Harer removed you from secret space",
expectedMessage: `Hello Eric Expireling,
@@ -179,7 +181,7 @@ https://owncloud.com
}),
Entry("Space Expired", testChannel{
- expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true},
+ expectedReceipients: []string{sharee.GetMail()},
expectedSubject: "Membership of 'secret space' expired at 2023-04-17 16:42:00",
expectedMessage: `Hello Eric Expireling,
@@ -208,27 +210,20 @@ https://owncloud.com
// NOTE: This is explictitly not testing the message itself. Should we?
type testChannel struct {
- expectedReceipients map[string]bool
+ expectedReceipients []string
expectedSubject string
expectedMessage string
expectedSender string
done chan struct{}
}
-func (tc testChannel) SendMessage(ctx context.Context, userIDs []string, msg, subject, senderDisplayName string) error {
+func (tc testChannel) SendMessage(ctx context.Context, m *channels.Message) error {
defer GinkgoRecover()
- for _, u := range userIDs {
- Expect(tc.expectedReceipients[u]).To(Equal(true))
- }
-
- Expect(msg).To(Equal(tc.expectedMessage))
- Expect(subject).To(Equal(tc.expectedSubject))
- Expect(senderDisplayName).To(Equal(tc.expectedSender))
+ Expect(m.Recipient).To(Equal(tc.expectedReceipients))
+ Expect(m.Subject).To(Equal(tc.expectedSubject))
+ Expect(m.TextBody).To(Equal(tc.expectedMessage))
+ Expect(m.Sender).To(Equal(tc.expectedSender))
tc.done <- struct{}{}
return nil
}
-
-func (tc testChannel) SendMessageToGroup(ctx context.Context, groupID *group.GroupId, msg, subject, senderDisplayName string) error {
- return tc.SendMessage(ctx, []string{groupID.GetOpaqueId()}, msg, subject, senderDisplayName)
-}
diff --git a/services/notifications/pkg/service/shares.go b/services/notifications/pkg/service/shares.go
index 4c8d8444cf5..1182ea38ee5 100644
--- a/services/notifications/pkg/service/shares.go
+++ b/services/notifications/pkg/service/shares.go
@@ -48,12 +48,12 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) {
"ShareSharer": sharerDisplayName,
"ShareFolder": resourceInfo.Name,
"ShareLink": shareLink,
- }, granteeList)
+ }, granteeList, sharerDisplayName)
if err != nil {
s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("could not get render the email")
return
}
- s.send(ownerCtx, recipientList, sharerDisplayName)
+ s.send(ownerCtx, recipientList)
}
func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
@@ -87,10 +87,10 @@ func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
map[string]interface{}{
"ShareFolder": resourceInfo.GetName(),
"ExpiredAt": e.ExpiredAt.Format("2006-01-02 15:04:05"),
- }, granteeList)
+ }, granteeList, owner.GetDisplayName())
if err != nil {
s.logger.Error().Err(err).Str("event", "ShareExpired").Msg("could not get render the email")
return
}
- s.send(ownerCtx, recipientList, owner.GetDisplayName())
+ s.send(ownerCtx, recipientList)
}
diff --git a/services/notifications/pkg/service/spaces.go b/services/notifications/pkg/service/spaces.go
index 78f6e59359d..37af8171330 100644
--- a/services/notifications/pkg/service/spaces.go
+++ b/services/notifications/pkg/service/spaces.go
@@ -61,12 +61,12 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
"SpaceSharer": sharerDisplayName,
"SpaceName": resourceInfo.GetSpace().GetName(),
"ShareLink": shareLink,
- }, spaceGrantee)
+ }, spaceGrantee, sharerDisplayName)
if err != nil {
s.logger.Error().Err(err).Str("event", "SharedSpace").Msg("could not get render the email")
return
}
- s.send(executantCtx, recipientList, sharerDisplayName)
+ s.send(executantCtx, recipientList)
}
func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
@@ -121,12 +121,12 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
"SpaceSharer": sharerDisplayName,
"SpaceName": resourceInfo.GetSpace().Name,
"ShareLink": shareLink,
- }, spaceGrantee)
+ }, spaceGrantee, sharerDisplayName)
if err != nil {
s.logger.Error().Err(err).Str("event", "UnsharedSpace").Msg("Could not get render the email")
return
}
- s.send(executantCtx, recipientList, sharerDisplayName)
+ s.send(executantCtx, recipientList)
}
func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExpired) {
@@ -152,10 +152,10 @@ func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExp
map[string]interface{}{
"SpaceName": e.SpaceName,
"ExpiredAt": e.ExpiredAt.Format("2006-01-02 15:04:05"),
- }, granteeList)
+ }, granteeList, owner.GetDisplayName())
if err != nil {
s.logger.Error().Err(err).Str("event", "SpaceUnshared").Msg("could not get render the email")
return
}
- s.send(ownerCtx, recipientList, owner.GetDisplayName())
+ s.send(ownerCtx, recipientList)
}