[MM-64840] Add EmailNotificationWillBeSent Plugin Hook (#33421)

This commit is contained in:
David Krauser 2025-08-05 09:09:07 -04:00 committed by GitHub
parent 98ad2adb06
commit c0ff672afb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 796 additions and 297 deletions

View file

@ -417,19 +417,21 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea
if err != nil {
c.Logger().Warn("Unable to get the sender user profile image.", mlog.String("user_id", sender.Id), mlog.Err(err))
}
if err := a.sendNotificationEmail(c, notification, profileMap[id], team, senderProfileImage); err != nil {
a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeEmail, model.NotificationReasonEmailSendError, model.NotificationNoPlatform)
a.NotificationsLog().Error("Error sending email notification",
mlog.String("type", model.NotificationTypeEmail),
mlog.String("post_id", post.Id),
mlog.String("status", model.NotificationStatusError),
mlog.String("reason", model.NotificationReasonEmailSendError),
mlog.String("sender_id", sender.Id),
mlog.String("receiver_id", id),
mlog.Err(err),
)
c.Logger().Warn("Unable to send notification email.", mlog.Err(err))
}
a.Srv().Go(func() {
if _, err := a.sendNotificationEmail(c, notification, profileMap[id], team, senderProfileImage); err != nil {
a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeEmail, model.NotificationReasonEmailSendError, model.NotificationNoPlatform)
a.NotificationsLog().Error("Error sending email notification",
mlog.String("type", model.NotificationTypeEmail),
mlog.String("post_id", post.Id),
mlog.String("status", model.NotificationStatusError),
mlog.String("reason", model.NotificationReasonEmailSendError),
mlog.String("sender_id", sender.Id),
mlog.String("receiver_id", id),
mlog.Err(err),
)
c.Logger().Warn("Unable to send notification email.", mlog.Err(err))
}
})
} else {
a.NotificationsLog().Debug("Email disallowed by user",
mlog.String("type", model.NotificationTypeEmail),

View file

@ -13,6 +13,7 @@ import (
"github.com/pkg/errors"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/plugin"
"github.com/mattermost/mattermost/server/public/shared/i18n"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/public/shared/request"
@ -20,14 +21,134 @@ import (
"github.com/mattermost/mattermost/server/v8/channels/utils"
)
func (a *App) sendNotificationEmail(c request.CTX, notification *PostNotification, user *model.User, team *model.Team, senderProfileImage []byte) error {
func (a *App) buildEmailNotification(
ctx request.CTX,
notification *PostNotification,
user *model.User,
team *model.Team,
) *model.EmailNotification {
channel := notification.Channel
post := notification.Post
sender := notification.Sender
translateFunc := i18n.GetUserTranslations(user.Locale)
nameFormat := a.GetNotificationNameFormat(user)
var useMilitaryTime bool
if data, err := a.Srv().Store().Preference().Get(
user.Id, model.PreferenceCategoryDisplaySettings, model.PreferenceNameUseMilitaryTime,
); err != nil {
ctx.Logger().Debug("Failed to retrieve user military time preference, defaulting to false",
mlog.String("user_id", user.Id), mlog.Err(err))
useMilitaryTime = false
} else {
useMilitaryTime = data.Value == "true"
}
channelName := notification.GetChannelName(nameFormat, "")
senderName := notification.GetSenderName(nameFormat,
*a.Config().ServiceSettings.EnablePostUsernameOverride)
emailNotificationContentsType := model.EmailNotificationContentsFull
if license := a.Srv().License(); license != nil && *license.Features.EmailNotificationContents {
emailNotificationContentsType = *a.Config().EmailSettings.EmailNotificationContentsType
}
var subject string
if channel.Type == model.ChannelTypeDirect {
subject = getDirectMessageNotificationEmailSubject(
user, post, translateFunc, *a.Config().TeamSettings.SiteName, senderName, useMilitaryTime)
} else if channel.Type == model.ChannelTypeGroup {
subject = getGroupMessageNotificationEmailSubject(
user, post, translateFunc, *a.Config().TeamSettings.SiteName, channelName, emailNotificationContentsType, useMilitaryTime)
} else if *a.Config().EmailSettings.UseChannelInEmailNotifications {
subject = getNotificationEmailSubject(
user, post, translateFunc, *a.Config().TeamSettings.SiteName, team.DisplayName+" ("+channelName+")", useMilitaryTime)
} else {
subject = getNotificationEmailSubject(
user, post, translateFunc, *a.Config().TeamSettings.SiteName, team.DisplayName, useMilitaryTime)
}
var title, subtitle string
if channel.Type == model.ChannelTypeDirect {
title = translateFunc("app.notification.body.dm.title", map[string]any{"SenderName": senderName})
subtitle = translateFunc("app.notification.body.dm.subTitle", map[string]any{"SenderName": senderName})
} else if channel.Type == model.ChannelTypeGroup {
title = translateFunc("app.notification.body.group.title", map[string]any{"SenderName": senderName})
subtitle = translateFunc("app.notification.body.group.subTitle", map[string]any{"SenderName": senderName})
} else {
title = translateFunc("app.notification.body.mention.title", map[string]any{"SenderName": senderName})
subtitle = translateFunc("app.notification.body.mention.subTitle", map[string]any{"SenderName": senderName, "ChannelName": channelName})
}
if a.IsCRTEnabledForUser(ctx, user.Id) && post.RootId != "" {
title = translateFunc("app.notification.body.thread.title", map[string]any{"SenderName": senderName})
if channel.Type == model.ChannelTypeDirect {
subtitle = translateFunc("app.notification.body.thread_dm.subTitle", map[string]any{"SenderName": senderName})
} else if channel.Type == model.ChannelTypeGroup {
subtitle = translateFunc("app.notification.body.thread_gm.subTitle", map[string]any{"SenderName": senderName})
} else if emailNotificationContentsType == model.EmailNotificationContentsFull {
subtitle = translateFunc("app.notification.body.thread_channel_full.subTitle", map[string]any{"SenderName": senderName, "ChannelName": channelName})
} else {
subtitle = translateFunc("app.notification.body.thread_channel.subTitle", map[string]any{"SenderName": senderName})
}
}
var messageHTML, messageText string
if emailNotificationContentsType == model.EmailNotificationContentsFull {
messageHTML = a.GetMessageForNotification(post, team.Name, a.GetSiteURL(), translateFunc)
messageText = post.Message
}
landingURL := a.GetSiteURL() + "/landing#/" + team.Name
buttonURL := landingURL
if team.Name != "select_team" {
buttonURL = landingURL + "/pl/" + post.Id
}
return &model.EmailNotification{
// Core identifiers (immutable)
PostId: post.Id,
ChannelId: channel.Id,
TeamId: team.Id,
SenderId: sender.Id,
SenderDisplayName: senderName,
RecipientId: user.Id,
RootId: post.RootId,
// Context for plugin decision-making (immutable)
ChannelType: string(channel.Type),
ChannelName: channelName,
TeamName: team.DisplayName,
SenderUsername: sender.Username,
IsDirectMessage: channel.Type == model.ChannelTypeDirect,
IsGroupMessage: channel.Type == model.ChannelTypeGroup,
IsThreadReply: post.RootId != "",
IsCRTEnabled: a.IsCRTEnabledForUser(ctx, user.Id),
UseMilitaryTime: useMilitaryTime,
// Customizable content fields
EmailNotificationContent: model.EmailNotificationContent{
Subject: subject,
Title: title,
SubTitle: subtitle,
MessageHTML: messageHTML,
MessageText: messageText,
ButtonText: translateFunc("api.templates.post_body.button"),
ButtonURL: buttonURL,
FooterText: translateFunc("app.notification.footer.title"),
},
}
}
func (a *App) sendNotificationEmail(c request.CTX, notification *PostNotification, user *model.User, team *model.Team, senderProfileImage []byte) (*model.EmailNotification, error) {
channel := notification.Channel
post := notification.Post
if channel.IsGroupOrDirect() {
teams, err := a.Srv().Store().Team().GetTeamsByUserId(user.Id)
if err != nil {
return errors.Wrap(err, "unable to get user teams")
return nil, errors.Wrap(err, "unable to get user teams")
}
// if the recipient isn't in the current user's team, just pick one
@ -48,6 +169,41 @@ func (a *App) sendNotificationEmail(c request.CTX, notification *PostNotificatio
}
}
// Create EmailNotification object for plugin customization
emailNotification := a.buildEmailNotification(c, notification, user, team)
// Call plugin hook to allow customization of emailNotification
rejectionReason := ""
a.ch.RunMultiHook(func(hooks plugin.Hooks, manifest *model.Manifest) bool {
var replacementContent *model.EmailNotificationContent
replacementContent, rejectionReason = hooks.EmailNotificationWillBeSent(emailNotification)
if rejectionReason != "" {
c.Logger().Info("Email notification cancelled by plugin.",
mlog.String("rejection_reason", rejectionReason),
mlog.String("plugin_id", manifest.Id),
mlog.String("plugin_name", manifest.Name))
return false
}
if replacementContent != nil {
emailNotification.EmailNotificationContent = *replacementContent
}
return true
}, plugin.EmailNotificationWillBeSentID)
if rejectionReason != "" {
// Email notification rejected by plugin
a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypeEmail, model.NotificationReasonRejectedByPlugin, model.NotificationNoPlatform)
a.NotificationsLog().Debug("Email notification rejected by plugin",
mlog.String("type", model.NotificationTypeEmail),
mlog.String("status", model.NotificationStatusNotSent),
mlog.String("reason", model.NotificationReasonRejectedByPlugin),
mlog.String("rejection_reason", rejectionReason),
mlog.String("user_id", user.Id),
mlog.String("post_id", post.Id),
)
return nil, nil
}
if *a.Config().EmailSettings.EnableEmailBatching {
var sendBatched bool
if data, err := a.Srv().Store().Preference().Get(user.Id, model.PreferenceCategoryNotifications, model.PreferenceNameEmailInterval); err != nil {
@ -60,57 +216,27 @@ func (a *App) sendNotificationEmail(c request.CTX, notification *PostNotificatio
if sendBatched {
if err := a.Srv().EmailService.AddNotificationEmailToBatch(user, post, team); err == nil {
return nil
return emailNotification, nil
}
}
// fall back to sending a single email if we can't batch it for some reason
}
translateFunc := i18n.GetUserTranslations(user.Locale)
var useMilitaryTime bool
if data, err := a.Srv().Store().Preference().Get(user.Id, model.PreferenceCategoryDisplaySettings, model.PreferenceNameUseMilitaryTime); err != nil {
useMilitaryTime = false
} else {
useMilitaryTime = data.Value == "true"
}
nameFormat := a.GetNotificationNameFormat(user)
channelName := notification.GetChannelName(nameFormat, "")
senderName := notification.GetSenderName(nameFormat, *a.Config().ServiceSettings.EnablePostUsernameOverride)
emailNotificationContentsType := model.EmailNotificationContentsFull
if license := a.Srv().License(); license != nil && *license.Features.EmailNotificationContents {
emailNotificationContentsType = *a.Config().EmailSettings.EmailNotificationContentsType
}
var subjectText string
if channel.Type == model.ChannelTypeDirect {
subjectText = getDirectMessageNotificationEmailSubject(user, post, translateFunc, *a.Config().TeamSettings.SiteName, senderName, useMilitaryTime)
} else if channel.Type == model.ChannelTypeGroup {
subjectText = getGroupMessageNotificationEmailSubject(user, post, translateFunc, *a.Config().TeamSettings.SiteName, channelName, emailNotificationContentsType, useMilitaryTime)
} else if *a.Config().EmailSettings.UseChannelInEmailNotifications {
subjectText = getNotificationEmailSubject(user, post, translateFunc, *a.Config().TeamSettings.SiteName, team.DisplayName+" ("+channelName+")", useMilitaryTime)
} else {
subjectText = getNotificationEmailSubject(user, post, translateFunc, *a.Config().TeamSettings.SiteName, team.DisplayName, useMilitaryTime)
}
// Handle sender photo
senderPhoto := ""
embeddedFiles := make(map[string]io.Reader)
if emailNotificationContentsType == model.EmailNotificationContentsFull && senderProfileImage != nil {
if emailNotification.MessageHTML != "" && senderProfileImage != nil {
senderPhoto = "user-avatar.png"
embeddedFiles = map[string]io.Reader{
senderPhoto: bytes.NewReader(senderProfileImage),
}
}
landingURL := a.GetSiteURL() + "/landing#/" + team.Name
var bodyText, err = a.getNotificationEmailBody(c, user, post, channel, channelName, senderName, team.Name, landingURL, emailNotificationContentsType, useMilitaryTime, translateFunc, senderPhoto)
// Build email body using EmailNotification data
var bodyText, err = a.getNotificationEmailBodyFromEmailNotification(c, user, emailNotification, post, senderPhoto)
if err != nil {
return errors.Wrap(err, "unable to render the email notification template")
return nil, errors.Wrap(err, "unable to render the email notification template")
}
templateString := "<%s@" + utils.GetHostnameFromSiteURL(a.GetSiteURL()) + ">"
@ -118,18 +244,18 @@ func (a *App) sendNotificationEmail(c request.CTX, notification *PostNotificatio
inReplyTo := ""
references := ""
if post.Id != "" {
messageID = fmt.Sprintf(templateString, post.Id)
if emailNotification.PostId != "" {
messageID = fmt.Sprintf(templateString, emailNotification.PostId)
}
if post.RootId != "" {
referencesVal := fmt.Sprintf(templateString, post.RootId)
if emailNotification.RootId != "" {
referencesVal := fmt.Sprintf(templateString, emailNotification.RootId)
inReplyTo = referencesVal
references = referencesVal
}
a.Srv().Go(func() {
if nErr := a.Srv().EmailService.SendMailWithEmbeddedFiles(user.Email, html.UnescapeString(subjectText), bodyText, embeddedFiles, messageID, inReplyTo, references, "Notification"); nErr != nil {
if nErr := a.Srv().EmailService.SendMailWithEmbeddedFiles(user.Email, html.UnescapeString(emailNotification.Subject), bodyText, embeddedFiles, messageID, inReplyTo, references, "Notification"); nErr != nil {
c.Logger().Error("Error while sending the email", mlog.String("user_email", user.Email), mlog.Err(nErr))
}
})
@ -138,7 +264,7 @@ func (a *App) sendNotificationEmail(c request.CTX, notification *PostNotificatio
a.Metrics().IncrementPostSentEmail()
}
return nil
return emailNotification, nil
}
/**
@ -214,80 +340,53 @@ type postData struct {
MessageAttachments []*email.EmailMessageAttachment
}
/**
* Computes the email body for notification messages
*/
func (a *App) getNotificationEmailBody(c request.CTX, recipient *model.User, post *model.Post, channel *model.Channel, channelName string, senderName string, teamName string, landingURL string, emailNotificationContentsType string, useMilitaryTime bool, translateFunc i18n.TranslateFunc, senderPhoto string) (string, error) {
func (a *App) GetMessageForNotification(post *model.Post, teamName, siteUrl string, translateFunc i18n.TranslateFunc) string {
return a.Srv().EmailService.GetMessageForNotification(post, teamName, siteUrl, translateFunc)
}
func (a *App) getNotificationEmailBodyFromEmailNotification(c request.CTX, recipient *model.User, emailNotification *model.EmailNotification, post *model.Post, senderPhoto string) (string, error) {
translateFunc := i18n.GetUserTranslations(recipient.Locale)
pData := postData{
SenderName: truncateUserNames(senderName, 22),
SenderName: truncateUserNames(emailNotification.SenderDisplayName, 22),
SenderPhoto: senderPhoto,
}
t := utils.GetFormattedPostTime(recipient, post, useMilitaryTime, translateFunc)
messageTime := map[string]any{
"Hour": t.Hour,
"Minute": t.Minute,
"TimeZone": t.TimeZone,
}
if emailNotification.MessageHTML != "" {
pData.Message = template.HTML(emailNotification.MessageHTML)
if emailNotificationContentsType == model.EmailNotificationContentsFull {
postMessage := a.GetMessageForNotification(post, teamName, a.GetSiteURL(), translateFunc)
pData.Message = template.HTML(postMessage)
// Get formatted time for message using the UseMilitaryTime field
t := utils.GetFormattedPostTime(recipient, post, emailNotification.UseMilitaryTime, translateFunc)
messageTime := map[string]any{
"Hour": t.Hour,
"Minute": t.Minute,
"TimeZone": t.TimeZone,
}
pData.Time = translateFunc("app.notification.body.dm.time", messageTime)
// Process message attachments
pData.MessageAttachments = email.ProcessMessageAttachments(post, a.GetSiteURL())
}
data := a.Srv().EmailService.NewEmailTemplateData(recipient.Locale)
data.Props["SiteURL"] = a.GetSiteURL()
if teamName != "select_team" {
data.Props["ButtonURL"] = landingURL + "/pl/" + post.Id
} else {
data.Props["ButtonURL"] = landingURL
}
data.Props["SenderName"] = senderName
data.Props["Button"] = translateFunc("api.templates.post_body.button")
data.Props["NotificationFooterTitle"] = translateFunc("app.notification.footer.title")
data.Props["ButtonURL"] = emailNotification.ButtonURL
data.Props["SenderName"] = emailNotification.SenderDisplayName
data.Props["Button"] = emailNotification.ButtonText
data.Props["NotificationFooterTitle"] = emailNotification.FooterText
data.Props["NotificationFooterInfoLogin"] = translateFunc("app.notification.footer.infoLogin")
data.Props["NotificationFooterInfo"] = translateFunc("app.notification.footer.info")
data.Props["Title"] = emailNotification.Title
data.Props["SubTitle"] = emailNotification.SubTitle
if channel.Type == model.ChannelTypeDirect {
// Direct Messages
data.Props["Title"] = translateFunc("app.notification.body.dm.title", map[string]any{"SenderName": senderName})
data.Props["SubTitle"] = translateFunc("app.notification.body.dm.subTitle", map[string]any{"SenderName": senderName})
} else if channel.Type == model.ChannelTypeGroup {
// Group Messages
data.Props["Title"] = translateFunc("app.notification.body.group.title", map[string]any{"SenderName": senderName})
data.Props["SubTitle"] = translateFunc("app.notification.body.group.subTitle", map[string]any{"SenderName": senderName})
if emailNotification.IsDirectMessage || emailNotification.IsGroupMessage {
// No channel name for DM/GM
} else {
// mentions
data.Props["Title"] = translateFunc("app.notification.body.mention.title", map[string]any{"SenderName": senderName})
data.Props["SubTitle"] = translateFunc("app.notification.body.mention.subTitle", map[string]any{"SenderName": senderName, "ChannelName": channelName})
pData.ChannelName = channelName
pData.ChannelName = emailNotification.ChannelName
}
// Override title and subtile for replies with CRT enabled
if a.IsCRTEnabledForUser(c, recipient.Id) && post.RootId != "" {
// Title is the same in all cases
data.Props["Title"] = translateFunc("app.notification.body.thread.title", map[string]any{"SenderName": senderName})
if channel.Type == model.ChannelTypeDirect {
// Direct Reply
data.Props["SubTitle"] = translateFunc("app.notification.body.thread_dm.subTitle", map[string]any{"SenderName": senderName})
} else if channel.Type == model.ChannelTypeGroup {
// Group Reply
data.Props["SubTitle"] = translateFunc("app.notification.body.thread_gm.subTitle", map[string]any{"SenderName": senderName})
} else if emailNotificationContentsType == model.EmailNotificationContentsFull {
// Channel Reply with full content
data.Props["SubTitle"] = translateFunc("app.notification.body.thread_channel_full.subTitle", map[string]any{"SenderName": senderName, "ChannelName": channelName})
} else {
// Channel Reply with generic content
data.Props["SubTitle"] = translateFunc("app.notification.body.thread_channel.subTitle", map[string]any{"SenderName": senderName})
}
}
// only include posts in notification email if email notification contents type is set to full
if emailNotificationContentsType == model.EmailNotificationContentsFull {
// Only include posts in notification email if message content is available
if emailNotification.MessageHTML != "" {
data.Props["Posts"] = []postData{pData}
} else {
data.Props["Posts"] = []postData{}
@ -295,7 +394,3 @@ func (a *App) getNotificationEmailBody(c request.CTX, recipient *model.User, pos
return a.Srv().TemplatesContainer().RenderToString("messages_notification", data)
}
func (a *App) GetMessageForNotification(post *model.Post, teamName, siteUrl string, translateFunc i18n.TranslateFunc) string {
return a.Srv().EmailService.GetMessageForNotification(post, teamName, siteUrl, translateFunc)
}

View file

@ -18,9 +18,52 @@ import (
"github.com/mattermost/mattermost/server/public/shared/i18n"
"github.com/mattermost/mattermost/server/public/shared/timezones"
"github.com/mattermost/mattermost/server/v8/channels/store/storetest/mocks"
"github.com/mattermost/mattermost/server/v8/channels/testlib"
"github.com/mattermost/mattermost/server/v8/channels/utils"
)
// Helper function to create PostNotification for testing
func buildTestPostNotification(post *model.Post, channel *model.Channel, sender *model.User) *PostNotification {
return &PostNotification{
Channel: channel,
Post: post,
Sender: sender,
ProfileMap: make(map[string]*model.User),
}
}
// Helper function to create test user
func buildTestUser(id, username, displayName string, useMilitaryTime bool) *model.User {
return &model.User{
Id: id,
Username: username,
Nickname: displayName,
Locale: "en",
}
}
// Helper function to create test team
func buildTestTeam(id, name, displayName string) *model.Team {
return &model.Team{
Id: id,
Name: name,
DisplayName: displayName,
}
}
// Helper function to set up preference mocks
func setupPreferenceMocks(th *TestHelper, userId string, useMilitaryTime bool) {
preferenceStoreMock := mocks.PreferenceStore{}
if useMilitaryTime {
preferenceStoreMock.On("Get", userId, model.PreferenceCategoryDisplaySettings, model.PreferenceNameUseMilitaryTime).Return(&model.Preference{Value: "true"}, nil)
} else {
preferenceStoreMock.On("Get", userId, model.PreferenceCategoryDisplaySettings, model.PreferenceNameUseMilitaryTime).Return(&model.Preference{Value: "false"}, nil)
}
// Mock the name format preference as well
preferenceStoreMock.On("Get", userId, model.PreferenceCategoryDisplaySettings, model.PreferenceNameNameFormat).Return(&model.Preference{Value: model.ShowUsername}, nil)
th.App.Srv().Store().(*mocks.Store).On("Preference").Return(&preferenceStoreMock)
}
func TestGetDirectMessageNotificationEmailSubject(t *testing.T) {
mainHelper.Parallel(t)
expectedPrefix := "[http://localhost:8065] New Direct Message from @sender on"
@ -76,31 +119,34 @@ func TestGetNotificationEmailBodyFullNotificationPublicChannel(t *testing.T) {
th := SetupWithStoreMock(t)
defer th.TearDown()
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
post := &model.Post{
Id: "test-post-id",
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeOpen,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, "mentioned you in a message", fmt.Sprintf("Expected email text 'mentioned you in a message. Got %s", body))
require.Contains(t, body, post.Message, fmt.Sprintf("Expected email text '%s'. Got %s", post.Message, body))
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
require.Contains(t, body, team.Name, fmt.Sprintf("Expected email text '%s'. Got %s", team.Name, body))
}
func TestGetNotificationEmailBodyFullNotificationGroupChannel(t *testing.T) {
@ -108,31 +154,34 @@ func TestGetNotificationEmailBodyFullNotificationGroupChannel(t *testing.T) {
th := SetupWithStoreMock(t)
defer th.TearDown()
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
post := &model.Post{
Id: "test-post-id",
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeGroup,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, "sent you a new message", fmt.Sprintf("Expected email text 'sent you a new message. Got %s", body))
require.Contains(t, body, post.Message, fmt.Sprintf("Expected email text '%s'. Got %s", post.Message, body))
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
require.Contains(t, body, team.Name, fmt.Sprintf("Expected email text '%s'. Got %s", team.Name, body))
}
func TestGetNotificationEmailBodyFullNotificationPrivateChannel(t *testing.T) {
@ -140,31 +189,34 @@ func TestGetNotificationEmailBodyFullNotificationPrivateChannel(t *testing.T) {
th := SetupWithStoreMock(t)
defer th.TearDown()
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
post := &model.Post{
Id: "test-post-id",
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypePrivate,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, "mentioned you in a message", fmt.Sprintf("Expected email text 'mentioned you in a message. Got %s", body))
require.Contains(t, body, post.Message, fmt.Sprintf("Expected email text '%s'. Got %s", post.Message, body))
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
require.Contains(t, body, team.Name, fmt.Sprintf("Expected email text '%s'. Got %s", team.Name, body))
}
func TestGetNotificationEmailBodyFullNotificationDirectChannel(t *testing.T) {
@ -172,31 +224,34 @@ func TestGetNotificationEmailBodyFullNotificationDirectChannel(t *testing.T) {
th := SetupWithStoreMock(t)
defer th.TearDown()
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
post := &model.Post{
Id: "test-post-id",
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeDirect,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, "sent you a new message", fmt.Sprintf("Expected email text 'sent you a new message. Got %s", body))
require.Contains(t, body, post.Message, fmt.Sprintf("Expected email text '%s'. Got %s", post.Message, body))
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
require.Contains(t, body, team.Name, fmt.Sprintf("Expected email text '%s'. Got %s", team.Name, body))
}
func TestGetNotificationEmailBodyFullNotificationLocaleTimeWithTimezone(t *testing.T) {
@ -205,30 +260,37 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTimeWithTimezone(t *testi
defer th.TearDown()
recipient := &model.User{
Id: "test-recipient-id",
Username: "recipient",
Nickname: "Recipient User",
Locale: "en",
Timezone: timezones.DefaultUserTimezone(),
}
recipient.Timezone["automaticTimezone"] = "America/New_York"
post := &model.Post{
Id: "test-post-id",
CreateAt: 1524663790000,
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeDirect,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, false, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, false)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
r, _ := regexp.Compile("E([S|D]+)T")
zone := r.FindString(body)
@ -241,28 +303,33 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTimeNoTimezone(t *testing
defer th.TearDown()
recipient := &model.User{
Id: "test-recipient-id",
Username: "recipient",
Nickname: "Recipient User",
Locale: "en",
Timezone: timezones.DefaultUserTimezone(),
}
post := &model.Post{
Id: "test-post-id",
CreateAt: 1524681000000,
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeDirect,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
setupPreferenceMocks(th, recipient.Id, true)
tm := time.Unix(post.CreateAt/1000, 0)
zone, _ := tm.Zone()
@ -278,7 +345,9 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTimeNoTimezone(t *testing
err = tmp.Execute(&text, fmt.Sprintf("%s:%s %s", formattedTime.Hour, formattedTime.Minute, formattedTime.TimeZone))
require.NoError(t, err)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
postTimeLine := text.String()
require.Contains(t, body, postTimeLine, fmt.Sprintf("Expected email text '%s'. Got %s", postTimeLine, body))
@ -290,30 +359,37 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTime12Hour(t *testing.T)
defer th.TearDown()
recipient := &model.User{
Id: "test-recipient-id",
Username: "recipient",
Nickname: "Recipient User",
Locale: "en",
Timezone: timezones.DefaultUserTimezone(),
}
recipient.Timezone["automaticTimezone"] = "America/New_York"
post := &model.Post{
Id: "test-post-id",
CreateAt: 1524681000000, // 1524681000 // 1524681000000
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeDirect,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, false, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, false)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, "2:30 PM", fmt.Sprintf("Expected email text '2:30 PM'. Got %s", body))
}
@ -324,30 +400,37 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTime24Hour(t *testing.T)
defer th.TearDown()
recipient := &model.User{
Id: "test-recipient-id",
Username: "recipient",
Nickname: "Recipient User",
Locale: "en",
Timezone: timezones.DefaultUserTimezone(),
}
recipient.Timezone["automaticTimezone"] = "America/New_York"
post := &model.Post{
Id: "test-post-id",
CreateAt: 1524681000000,
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeDirect,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, "14:30", fmt.Sprintf("Expected email text '14:30'. Got %s", body))
}
@ -358,26 +441,29 @@ func TestGetNotificationEmailBodyWithUserPreference(t *testing.T) {
defer th.TearDown()
recipient := &model.User{
Id: "test-recipient-id",
Username: "recipient",
Nickname: "Recipient User",
Locale: "en",
Timezone: timezones.DefaultUserTimezone(),
}
recipient.Timezone["automaticTimezone"] = "America/New_York"
post := &model.Post{
Id: "test-post-id",
CreateAt: 1524681000000,
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeDirect,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
@ -392,7 +478,11 @@ func TestGetNotificationEmailBodyWithUserPreference(t *testing.T) {
expectedTimeFormat = "14:30"
}
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, is24HourFormat, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, is24HourFormat)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, expectedTimeFormat, fmt.Sprintf("Expected email text '%s'. Got %s", expectedTimeFormat, body))
}
@ -402,8 +492,9 @@ func TestGetNotificationEmailBodyFullNotificationWithSlackAttachments(t *testing
th := SetupWithStoreMock(t)
defer th.TearDown()
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
post := &model.Post{
Id: "test-post-id",
Message: "This is the message",
}
@ -453,23 +544,25 @@ func TestGetNotificationEmailBodyFullNotificationWithSlackAttachments(t *testing
model.ParseSlackAttachment(post, messageAttachments)
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeOpen,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, "#FF0000")
require.Contains(t, body, "message attachment 1 pretext")
@ -501,30 +594,33 @@ func TestGetNotificationEmailBodyGenericNotificationPublicChannel(t *testing.T)
th := SetupWithStoreMock(t)
defer th.TearDown()
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
post := &model.Post{
Id: "test-post-id",
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeOpen,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsGeneric
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, "mentioned you in a message", fmt.Sprintf("Expected email text 'mentioned you in a message. Got %s", body))
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
require.Contains(t, body, team.Name, fmt.Sprintf("Expected email text '%s'. Got %s", team.Name, body))
}
func TestGetNotificationEmailBodyGenericNotificationGroupChannel(t *testing.T) {
@ -532,30 +628,33 @@ func TestGetNotificationEmailBodyGenericNotificationGroupChannel(t *testing.T) {
th := SetupWithStoreMock(t)
defer th.TearDown()
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
post := &model.Post{
Id: "test-post-id",
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeGroup,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsGeneric
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, "sent you a new message", fmt.Sprintf("Expected email text 'sent you a new message. Got %s", body))
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
require.Contains(t, body, team.Name, fmt.Sprintf("Expected email text '%s'. Got %s", team.Name, body))
}
func TestGetNotificationEmailBodyGenericNotificationPrivateChannel(t *testing.T) {
@ -563,30 +662,33 @@ func TestGetNotificationEmailBodyGenericNotificationPrivateChannel(t *testing.T)
th := SetupWithStoreMock(t)
defer th.TearDown()
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
post := &model.Post{
Id: "test-post-id",
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypePrivate,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsGeneric
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, "mentioned you in a message", fmt.Sprintf("Expected email text 'mentioned you in a message. Got %s", body))
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
require.Contains(t, body, team.Name, fmt.Sprintf("Expected email text '%s'. Got %s", team.Name, body))
}
func TestGetNotificationEmailBodyGenericNotificationDirectChannel(t *testing.T) {
@ -594,30 +696,33 @@ func TestGetNotificationEmailBodyGenericNotificationDirectChannel(t *testing.T)
th := SetupWithStoreMock(t)
defer th.TearDown()
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
post := &model.Post{
Id: "test-post-id",
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeDirect,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsGeneric
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, "sent you a new message", fmt.Sprintf("Expected email text 'sent you a new message. Got %s", body))
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
require.Contains(t, body, team.Name, fmt.Sprintf("Expected email text '%s'. Got %s", team.Name, body))
}
func TestGetNotificationEmailEscapingChars(t *testing.T) {
@ -625,31 +730,32 @@ func TestGetNotificationEmailEscapingChars(t *testing.T) {
th := SetupWithStoreMock(t)
defer th.TearDown()
ch := &model.Channel{
DisplayName: "ChannelName",
Type: model.ChannelTypeOpen,
}
channelName := "ChannelName"
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
message := "<b>Bold Test</b>"
post := &model.Post{
Id: "test-post-id",
Message: message,
}
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
ch := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeOpen,
}
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, ch,
channelName, senderName, teamName, teamURL,
emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, ch, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
assert.NotContains(t, body, message)
@ -661,27 +767,29 @@ func TestGetNotificationEmailBodyPublicChannelMention(t *testing.T) {
defer th.TearDown()
ch := &model.Channel{
Id: "test-channel-id",
Name: "channelname",
DisplayName: "ChannelName",
Type: model.ChannelTypeOpen,
}
id := model.NewId()
recipient := &model.User{
Id: "test-recipient-id",
Email: "success+" + id + "@simulator.amazonses.com",
Username: "un_" + id,
Nickname: "nn_" + id,
Password: "Password1",
EmailVerified: true,
Locale: "en",
}
post := &model.Post{
Id: "test-post-id",
Message: "This is the message ~" + ch.Name,
}
senderName := "user1"
teamName := "testteam"
sender := buildTestUser("test-sender-id", "user1", "user1", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
teamURL := th.App.GetSiteURL() + "/landing#" + "/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
@ -692,11 +800,13 @@ func TestGetNotificationEmailBodyPublicChannelMention(t *testing.T) {
channelStoreMock.On("GetByNames", "test", []string{ch.Name}, true).Return([]*model.Channel{ch}, nil)
storeMock.On("Channel").Return(&channelStoreMock)
setupPreferenceMocks(th, recipient.Id, true)
th.App.Srv().EmailService.SetStore(storeMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, ch,
ch.Name, senderName, teamName, teamURL,
emailNotificationContentsType, true, translateFunc, "user-avatar.png")
notification := buildTestPostNotification(post, ch, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
channelURL := teamURL + "/channels/" + ch.Name
mention := "~" + ch.Name
@ -734,23 +844,11 @@ func TestGetNotificationEmailBodyMultiPublicChannelMention(t *testing.T) {
message := fmt.Sprintf("This is the message Channel1: %s; Channel2: %s;"+
" Channel3: %s", mention, mention2, mention3)
id := model.NewId()
recipient := &model.User{
Email: "success+" + id + "@simulator.amazonses.com",
Username: "un_" + id,
Nickname: "nn_" + id,
Password: "Password1",
EmailVerified: true,
}
post := &model.Post{
Message: message,
}
senderName := "user1"
teamName := "testteam"
teamURL := th.App.GetSiteURL() + "/landing#" + "/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
@ -763,16 +861,22 @@ func TestGetNotificationEmailBodyMultiPublicChannelMention(t *testing.T) {
th.App.Srv().EmailService.SetStore(storeMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, ch,
ch.Name, senderName, teamName, teamURL,
emailNotificationContentsType, true, translateFunc, "user-avatar.png")
require.NoError(t, err)
channelURL := teamURL + "/channels/" + ch.Name
channelURL2 := teamURL + "/channels/" + ch2.Name
channelURL3 := teamURL + "/channels/" + ch3.Name
expMessage := fmt.Sprintf("This is the message Channel1: <a href='%s'>%s</a>;"+
" Channel2: <a href='%s'>%s</a>; Channel3: <a href='%s'>%s</a>",
channelURL, mention, channelURL2, mention2, channelURL3, mention3)
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
sender := buildTestUser("test-sender-id", "user1", "user1", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, ch, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
assert.Contains(t, body, expMessage)
}
@ -782,27 +886,29 @@ func TestGetNotificationEmailBodyPrivateChannelMention(t *testing.T) {
defer th.TearDown()
ch := &model.Channel{
Id: "test-channel-id",
Name: "channelname",
DisplayName: "ChannelName",
Type: model.ChannelTypePrivate,
}
id := model.NewId()
recipient := &model.User{
Id: "test-recipient-id",
Email: "success+" + id + "@simulator.amazonses.com",
Username: "un_" + id,
Nickname: "nn_" + id,
Password: "Password1",
EmailVerified: true,
Locale: "en",
}
post := &model.Post{
Id: "test-post-id",
Message: "This is the message ~" + ch.Name,
}
senderName := "user1"
teamName := "testteam"
sender := buildTestUser("test-sender-id", "user1", "user1", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
teamURL := "http://localhost:8065/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
@ -813,11 +919,13 @@ func TestGetNotificationEmailBodyPrivateChannelMention(t *testing.T) {
channelStoreMock.On("GetByNames", "test", []string{ch.Name}, true).Return([]*model.Channel{ch}, nil)
storeMock.On("Channel").Return(&channelStoreMock)
setupPreferenceMocks(th, recipient.Id, true)
th.App.Srv().EmailService.SetStore(storeMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, ch,
ch.Name, senderName, teamName, teamURL,
emailNotificationContentsType, true, translateFunc, "user-avatar.png")
notification := buildTestPostNotification(post, ch, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
channelURL := teamURL + "/channels/" + ch.Name
mention := "~" + ch.Name
@ -945,63 +1053,81 @@ func TestGenerateHyperlinkForChannelsPrivate(t *testing.T) {
func TestLandingLink(t *testing.T) {
mainHelper.Parallel(t)
th := SetupWithStoreMock(t)
// Create a minimal helper that sets the site URL
mockStore := testlib.GetMockStoreForSetupFunctions()
th := setupTestHelper(mockStore, mainHelper.GetSQLStore(), mainHelper.GetSQLSettings(), mainHelper.GetSearchEngine(), false, false,
func(cfg *model.Config) {
cfg.ServiceSettings.SiteURL = model.NewPointer("http://localhost:8065")
}, nil, t)
defer th.TearDown()
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
post := &model.Post{
Id: "test-post-id",
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeOpen,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
teamURL := "http://localhost:8065/landing#/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
}
func TestLandingLinkPermalink(t *testing.T) {
mainHelper.Parallel(t)
th := SetupWithStoreMock(t)
// Create a minimal helper that sets the site URL
mockStore := testlib.GetMockStoreForSetupFunctions()
th := setupTestHelper(mockStore, mainHelper.GetSQLStore(), mainHelper.GetSQLSettings(), mainHelper.GetSearchEngine(), false, false,
func(cfg *model.Config) {
cfg.ServiceSettings.SiteURL = model.NewPointer("http://localhost:8065")
}, nil, t)
defer th.TearDown()
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
post := &model.Post{
Id: "Test_id",
Message: "This is the message",
}
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeOpen,
}
channelName := "ChannelName"
senderName := "sender"
teamName := "testteam"
teamURL := "http://localhost:8065/landing#/testteam"
emailNotificationContentsType := model.EmailNotificationContentsFull
translateFunc := i18n.GetUserTranslations("en")
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
body, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
setupPreferenceMocks(th, recipient.Id, true)
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, body, teamURL+"/pl/"+post.Id, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
require.Contains(t, body, "/pl/"+post.Id, fmt.Sprintf("Expected email text to contain permalink '/pl/%s'. Got %s", post.Id, body))
}
func TestMarkdownConversion(t *testing.T) {
@ -1091,15 +1217,21 @@ func TestMarkdownConversion(t *testing.T) {
th := SetupWithStoreMock(t)
defer th.TearDown()
recipient := &model.User{}
recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true)
storeMock := th.App.Srv().Store().(*mocks.Store)
teamStoreMock := mocks.TeamStore{}
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
storeMock.On("Team").Return(&teamStoreMock)
channel := &model.Channel{
Id: "test-channel-id",
Name: "testchannel",
DisplayName: "ChannelName",
Type: model.ChannelTypeOpen,
}
sender := buildTestUser("test-sender-id", "sender", "sender", true)
team := buildTestTeam("test-team-id", "testteam", "testteam")
setupPreferenceMocks(th, recipient.Id, true)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -1107,7 +1239,9 @@ func TestMarkdownConversion(t *testing.T) {
Id: "Test_id",
Message: tt.args,
}
got, err := th.App.getNotificationEmailBody(th.Context, recipient, post, channel, "ChannelName", "sender", "testteam", "http://localhost:8065/landing#/testteam", model.EmailNotificationContentsFull, true, i18n.GetUserTranslations("en"), "user-avatar.png")
notification := buildTestPostNotification(post, channel, sender)
emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team)
got, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "user-avatar.png")
require.NoError(t, err)
require.Contains(t, got, tt.want)
})

View file

@ -1617,6 +1617,131 @@ func TestHookNotificationWillBePushed(t *testing.T) {
}
}
//go:embed test_templates/hook_email_notification_will_be_sent.tmpl
var hookEmailNotificationWillBeSentTmpl string
func TestHookEmailNotificationWillBeSent(t *testing.T) {
mainHelper.Parallel(t)
tests := []struct {
name string
testCode string
expectedNotificationSubject string
expectedNotificationTitle string
expectedButtonText string
expectedFooterText string
}{
{
name: "successfully sent",
testCode: `return nil, ""`,
},
{
name: "email notification rejected",
testCode: `return nil, "rejected"`,
},
{
name: "email notification modified",
testCode: `content := &model.EmailNotificationContent{
Subject: "Modified Subject by Plugin",
Title: "Modified Title by Plugin",
ButtonText: "Modified Button by Plugin",
FooterText: "Modified Footer by Plugin",
}
return content, ""`,
expectedNotificationSubject: "Modified Subject by Plugin",
expectedNotificationTitle: "Modified Title by Plugin",
expectedButtonText: "Modified Button by Plugin",
expectedFooterText: "Modified Footer by Plugin",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic()
defer th.TearDown()
// Create a test user for email notifications
user := th.CreateUser()
th.LinkUserToTeam(user, th.BasicTeam)
th.AddUserToChannel(user, th.BasicChannel)
// Set up email notification preferences to disable batching
appErr := th.App.UpdatePreferences(th.Context, user.Id, model.Preferences{
{
UserId: user.Id,
Category: model.PreferenceCategoryNotifications,
Name: model.PreferenceNameEmailInterval,
Value: model.PreferenceEmailIntervalNoBatchingSeconds,
},
})
require.Nil(t, appErr)
// Disable email batching in config
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.EmailSettings.EnableEmailBatching = false
})
// Create and set up plugin
templatedPlugin := fmt.Sprintf(hookEmailNotificationWillBeSentTmpl, tt.testCode)
tearDown, _, _ := SetAppEnvironmentWithPlugins(t, []string{templatedPlugin}, th.App, th.NewPluginAPI)
defer tearDown()
// For the modification test, create a simple test that verifies the hook is called
// The detailed verification would require more complex mocking which is beyond this test's scope
// Create a post that will trigger email notification
post := &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: "@" + user.Username + " test message",
CreateAt: model.GetMillis(),
}
// Create notification
notification := &PostNotification{
Post: post,
Channel: th.BasicChannel,
ProfileMap: map[string]*model.User{
user.Id: user,
},
Sender: th.BasicUser,
}
// Send email notification (this will trigger the hook)
// Use assert.Eventually to handle any potential race conditions with plugin activation/deactivation
assert.Eventually(t, func() bool {
modifiedNotification, err := th.App.sendNotificationEmail(th.Context, notification, user, th.BasicTeam, nil)
// For the rejected test case, we expect the notification to be rejected
if tt.name == "email notification rejected" {
// When rejected, sendNotificationEmail returns nil for the notification
return modifiedNotification == nil && err == nil
}
if err != nil || modifiedNotification == nil {
return false
}
// Verify the modified notification fields
if tt.expectedNotificationSubject != "" && modifiedNotification.Subject != tt.expectedNotificationSubject {
return false
}
if tt.expectedNotificationTitle != "" && modifiedNotification.Title != tt.expectedNotificationTitle {
return false
}
if tt.expectedButtonText != "" && modifiedNotification.ButtonText != tt.expectedButtonText {
return false
}
if tt.expectedFooterText != "" && modifiedNotification.FooterText != tt.expectedFooterText {
return false
}
return true
}, 2*time.Second, 100*time.Millisecond)
})
}
}
func TestHookMessagesWillBeConsumed(t *testing.T) {
mainHelper.Parallel(t)

View file

@ -0,0 +1,18 @@
package main
import (
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/plugin"
)
type MyPlugin struct {
plugin.MattermostPlugin
}
func (p *MyPlugin) EmailNotificationWillBeSent(emailNotification *model.EmailNotification) (*model.EmailNotificationContent, string) {
%s
}
func main() {
plugin.ClientMain(&MyPlugin{})
}

View file

@ -0,0 +1,37 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
type EmailNotificationContent struct {
Subject string `json:"subject,omitempty"`
Title string `json:"title,omitempty"`
SubTitle string `json:"subtitle,omitempty"`
MessageHTML string `json:"message_html,omitempty"`
MessageText string `json:"message_text,omitempty"`
ButtonText string `json:"button_text,omitempty"`
ButtonURL string `json:"button_url,omitempty"`
FooterText string `json:"footer_text,omitempty"`
}
type EmailNotification struct {
PostId string `json:"post_id"`
ChannelId string `json:"channel_id"`
TeamId string `json:"team_id"`
SenderId string `json:"sender_id"`
SenderDisplayName string `json:"sender_display_name,omitempty"`
RecipientId string `json:"recipient_id"`
RootId string `json:"root_id,omitempty"`
ChannelType string `json:"channel_type"`
ChannelName string `json:"channel_name"`
TeamName string `json:"team_name"`
SenderUsername string `json:"sender_username"`
IsDirectMessage bool `json:"is_direct_message"`
IsGroupMessage bool `json:"is_group_message"`
IsThreadReply bool `json:"is_thread_reply"`
IsCRTEnabled bool `json:"is_crt_enabled"`
UseMilitaryTime bool `json:"use_military_time"`
EmailNotificationContent
}

View file

@ -878,6 +878,41 @@ func (s *hooksRPCServer) ConfigurationWillBeSaved(args *Z_ConfigurationWillBeSav
return nil
}
func init() {
hookNameToId["EmailNotificationWillBeSent"] = EmailNotificationWillBeSentID
}
type Z_EmailNotificationWillBeSentArgs struct {
A *model.EmailNotification
}
type Z_EmailNotificationWillBeSentReturns struct {
A *model.EmailNotificationContent
B string
}
func (g *hooksRPCClient) EmailNotificationWillBeSent(emailNotification *model.EmailNotification) (*model.EmailNotificationContent, string) {
_args := &Z_EmailNotificationWillBeSentArgs{emailNotification}
_returns := &Z_EmailNotificationWillBeSentReturns{}
if g.implemented[EmailNotificationWillBeSentID] {
if err := g.client.Call("Plugin.EmailNotificationWillBeSent", _args, _returns); err != nil {
g.log.Error("RPC call EmailNotificationWillBeSent to plugin failed.", mlog.Err(err))
}
}
return _returns.A, _returns.B
}
func (s *hooksRPCServer) EmailNotificationWillBeSent(args *Z_EmailNotificationWillBeSentArgs, returns *Z_EmailNotificationWillBeSentReturns) error {
if hook, ok := s.impl.(interface {
EmailNotificationWillBeSent(emailNotification *model.EmailNotification) (*model.EmailNotificationContent, string)
}); ok {
returns.A, returns.B = hook.EmailNotificationWillBeSent(args.A)
} else {
return encodableError(fmt.Errorf("Hook EmailNotificationWillBeSent called but not implemented."))
}
return nil
}
func init() {
hookNameToId["NotificationWillBePushed"] = NotificationWillBePushedID
}

View file

@ -63,6 +63,7 @@ const (
OnSharedChannelsProfileImageSyncMsgID = 44
GenerateSupportDataID = 45
OnSAMLLoginID = 46
EmailNotificationWillBeSentID = 47
TotalHooksID = iota
)
@ -314,6 +315,21 @@ type Hooks interface {
// Minimum server version: 8.0
ConfigurationWillBeSaved(newCfg *model.Config) (*model.Config, error)
// EmailNotificationWillBeSent is invoked before an email notification is sent to a user.
// This allows plugins to customize the email notification content including subject,
// title, subtitle, message content, buttons, and other email properties.
//
// To reject an email notification, return an non-empty string describing why the notification was rejected.
// To modify the notification, return the replacement, non-nil *model.EmailNotificationContent and an empty string.
// To allow the notification without modification, return a nil *model.EmailNotificationContent and an empty string.
//
// Note that core identifiers (PostId, ChannelId, TeamId, SenderId, RecipientId, RootId) and
// context fields (ChannelType, IsDirectMessage, etc.) are immutable and changes to them will be ignored.
// Only customizable content fields can be modified.
//
// Minimum server version: 11.00
EmailNotificationWillBeSent(emailNotification *model.EmailNotification) (*model.EmailNotificationContent, string)
// NotificationWillBePushed is invoked before a push notification is sent to the push
// notification server.
//

View file

@ -233,6 +233,13 @@ func (hooks *hooksTimerLayer) ConfigurationWillBeSaved(newCfg *model.Config) (*m
return _returnsA, _returnsB
}
func (hooks *hooksTimerLayer) EmailNotificationWillBeSent(emailNotification *model.EmailNotification) (*model.EmailNotificationContent, string) {
startTime := timePkg.Now()
_returnsA, _returnsB := hooks.hooksImpl.EmailNotificationWillBeSent(emailNotification)
hooks.recordTime(startTime, "EmailNotificationWillBeSent", true)
return _returnsA, _returnsB
}
func (hooks *hooksTimerLayer) NotificationWillBePushed(pushNotification *model.PushNotification, userID string) (*model.PushNotification, string) {
startTime := timePkg.Now()
_returnsA, _returnsB := hooks.hooksImpl.NotificationWillBePushed(pushNotification, userID)

View file

@ -57,6 +57,36 @@ func (_m *Hooks) ConfigurationWillBeSaved(newCfg *model.Config) (*model.Config,
return r0, r1
}
// EmailNotificationWillBeSent provides a mock function with given fields: emailNotification
func (_m *Hooks) EmailNotificationWillBeSent(emailNotification *model.EmailNotification) (*model.EmailNotificationContent, string) {
ret := _m.Called(emailNotification)
if len(ret) == 0 {
panic("no return value specified for EmailNotificationWillBeSent")
}
var r0 *model.EmailNotificationContent
var r1 string
if rf, ok := ret.Get(0).(func(*model.EmailNotification) (*model.EmailNotificationContent, string)); ok {
return rf(emailNotification)
}
if rf, ok := ret.Get(0).(func(*model.EmailNotification) *model.EmailNotificationContent); ok {
r0 = rf(emailNotification)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.EmailNotificationContent)
}
}
if rf, ok := ret.Get(1).(func(*model.EmailNotification) string); ok {
r1 = rf(emailNotification)
} else {
r1 = ret.Get(1).(string)
}
return r0, r1
}
// ExecuteCommand provides a mock function with given fields: c, args
func (_m *Hooks) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
ret := _m.Called(c, args)