* The email HTML templates added #6146 * use a single palne text email template. use fs.FS * Update services/notifications/README.md Co-authored-by: Martin <github@diemattels.at> * Update services/notifications/README.md Co-authored-by: Martin <github@diemattels.at> * fix md --------- Co-authored-by: Roman Perekhod <rperekhod@owncloud.com> Co-authored-by: Martin <github@diemattels.at>
This commit is contained in:
co-authored by
Martin
Roman Perekhod
parent
77e773515d
commit
27322c5bbf
@@ -97,98 +97,95 @@ 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 := s.getUserLang(ctx, userID)
|
||||
grantee, err := s.getUserName(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields[granteeFieldName] = grantee
|
||||
messageList := make([]*channels.Message, len(granteeList))
|
||||
for i, usr := range granteeList {
|
||||
locale := s.getUserLang(ctx, usr.GetId())
|
||||
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, executant, u *user.UserId, g *group.GroupId) ([]*user.UserId, error) {
|
||||
func (s eventsNotifier) getGranteeList(ctx context.Context, executant, u *user.UserId, g *group.GroupId) ([]*user.User, error) {
|
||||
switch {
|
||||
case u != nil:
|
||||
if s.disableEmails(ctx, u) {
|
||||
return []*user.UserId{}, nil
|
||||
return nil, 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")
|
||||
}
|
||||
|
||||
var grantees []*user.UserId
|
||||
userList := make([]*user.User, 0, len(res.GetGroup().GetMembers()))
|
||||
for _, userID := range res.GetGroup().GetMembers() {
|
||||
// don't add the executant
|
||||
if userID.GetOpaqueId() == executant.GetOpaqueId() {
|
||||
continue
|
||||
}
|
||||
|
||||
// don't add users who opted out
|
||||
if s.disableEmails(ctx, userID) {
|
||||
continue
|
||||
}
|
||||
|
||||
grantees = append(grantees, userID)
|
||||
usr, err := s.getUser(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userList = append(userList, usr)
|
||||
}
|
||||
return grantees, 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) 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 {
|
||||
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 {
|
||||
@@ -226,8 +223,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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user