[MM-37730] - Self-managed: Update email template to new branding - Batch Email (#18221)
* [MM-37730] - Self-managed: Update email template to new branding - Batch Email * batch email fixes * simplify email title * More fixes * fix tests * fix group message name on card * fix bracket issue * style the count * account for merged changes * change truncating rule
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
acec8db7c5
Коммит
ad5591cf75
@@ -10,6 +10,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -30,6 +31,8 @@ type postData struct {
|
|||||||
SenderPhoto string
|
SenderPhoto string
|
||||||
PostPhoto string
|
PostPhoto string
|
||||||
Time string
|
Time string
|
||||||
|
ShowChannelIcon bool
|
||||||
|
OtherChannelMembersCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *Service) InitEmailBatching() {
|
func (es *Service) InitEmailBatching() {
|
||||||
@@ -206,6 +209,18 @@ func (job *EmailBatchingJob) checkPendingNotifications(now time.Time, handler fu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the name is longer than i characters, replace remaining characters with ...
|
||||||
|
*/
|
||||||
|
func truncateUserNames(name string, i int) string {
|
||||||
|
runes := []rune(name)
|
||||||
|
if len(runes) > i {
|
||||||
|
newString := string(runes[:i])
|
||||||
|
return newString + "..."
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
func (es *Service) sendBatchedEmailNotification(userID string, notifications []*batchedNotification) {
|
func (es *Service) sendBatchedEmailNotification(userID string, notifications []*batchedNotification) {
|
||||||
user, err := es.userService.GetUser(userID)
|
user, err := es.userService.GetUser(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -266,27 +281,38 @@ func (es *Service) sendBatchedEmailNotification(userID string, notifications []*
|
|||||||
"Month": translateFunc(tm.Month().String()),
|
"Month": translateFunc(tm.Month().String()),
|
||||||
"Day": tm.Day(),
|
"Day": tm.Day(),
|
||||||
"Year": tm.Year(),
|
"Year": tm.Year(),
|
||||||
"Timezone": timezone,
|
"TimeZone": timezone,
|
||||||
})
|
})
|
||||||
|
|
||||||
MessageURL := siteURL + "/" + notification.teamName + "/pl/" + notification.post.Id
|
MessageURL := siteURL + "/" + notification.teamName + "/pl/" + notification.post.Id
|
||||||
|
|
||||||
channelName := channel.DisplayName
|
channelDisplayName := channel.DisplayName
|
||||||
|
showChannelIcon := true
|
||||||
|
otherChannelMembersCount := 0
|
||||||
|
|
||||||
if threadsEnabled && notification.post.RootId != "" {
|
if threadsEnabled && notification.post.RootId != "" {
|
||||||
props := map[string]interface{}{"channelName": channelName}
|
props := map[string]interface{}{"channelName": channelDisplayName}
|
||||||
channelName = translateFunc("api.push_notification.title.collapsed_threads", props)
|
channelDisplayName = translateFunc("api.push_notification.title.collapsed_threads", props)
|
||||||
if channel.Type == model.ChannelTypeDirect {
|
if channel.Type == model.ChannelTypeDirect {
|
||||||
channelName = translateFunc("api.push_notification.title.collapsed_threads_dm")
|
channelDisplayName = translateFunc("api.push_notification.title.collapsed_threads_dm")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if channel.Type == model.ChannelTypeGroup {
|
||||||
|
otherChannelMembersCount = len(strings.Split(channelDisplayName, ",")) - 1
|
||||||
|
showChannelIcon = false
|
||||||
|
channelDisplayName = truncateUserNames(channel.DisplayName, 11)
|
||||||
|
}
|
||||||
|
|
||||||
postsData = append(postsData, &postData{
|
postsData = append(postsData, &postData{
|
||||||
SenderPhoto: senderPhoto,
|
SenderPhoto: senderPhoto,
|
||||||
SenderName: sender.GetDisplayName(displayNameFormat),
|
SenderName: truncateUserNames(sender.GetDisplayName(displayNameFormat), 22),
|
||||||
Time: t,
|
Time: t,
|
||||||
ChannelName: channelName,
|
ChannelName: channelDisplayName,
|
||||||
Message: template.HTML(es.GetMessageForNotification(notification.post, translateFunc)),
|
Message: template.HTML(es.GetMessageForNotification(notification.post, translateFunc)),
|
||||||
MessageURL: MessageURL,
|
MessageURL: MessageURL,
|
||||||
|
ShowChannelIcon: showChannelIcon,
|
||||||
|
OtherChannelMembersCount: otherChannelMembersCount,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -300,16 +326,9 @@ func (es *Service) sendBatchedEmailNotification(userID string, notifications []*
|
|||||||
"Day": tm.Day(),
|
"Day": tm.Day(),
|
||||||
})
|
})
|
||||||
|
|
||||||
firstSender, err := es.userService.GetUser(notifications[0].post.UserId)
|
|
||||||
if err != nil {
|
|
||||||
mlog.Warn("Unable to find sender of post for batched email notification")
|
|
||||||
}
|
|
||||||
|
|
||||||
data := es.NewEmailTemplateData(user.Locale)
|
data := es.NewEmailTemplateData(user.Locale)
|
||||||
data.Props["SiteURL"] = siteURL
|
data.Props["SiteURL"] = siteURL
|
||||||
data.Props["Title"] = translateFunc("api.email_batching.send_batched_email_notification.title", len(notifications)-1, map[string]interface{}{
|
data.Props["Title"] = translateFunc("api.email_batching.send_batched_email_notification.title")
|
||||||
"SenderName": firstSender.GetDisplayName(displayNameFormat),
|
|
||||||
})
|
|
||||||
data.Props["SubTitle"] = translateFunc("api.email_batching.send_batched_email_notification.subTitle")
|
data.Props["SubTitle"] = translateFunc("api.email_batching.send_batched_email_notification.subTitle")
|
||||||
data.Props["Button"] = translateFunc("api.email_batching.send_batched_email_notification.button")
|
data.Props["Button"] = translateFunc("api.email_batching.send_batched_email_notification.button")
|
||||||
data.Props["ButtonURL"] = siteURL
|
data.Props["ButtonURL"] = siteURL
|
||||||
@@ -324,7 +343,7 @@ func (es *Service) sendBatchedEmailNotification(userID string, notifications []*
|
|||||||
mlog.Error("Unable to render email", mlog.Err(renderErr))
|
mlog.Error("Unable to render email", mlog.Err(renderErr))
|
||||||
}
|
}
|
||||||
|
|
||||||
if nErr := es.SendNotificationMail(user.Email, subject, renderedPage); nErr != nil {
|
if nErr := es.SendMailWithEmbeddedFiles(user.Email, subject, renderedPage, embeddedFiles); nErr != nil {
|
||||||
mlog.Warn("Unable to send batched email notification", mlog.String("email", user.Email), mlog.Err(nErr))
|
mlog.Warn("Unable to send batched email notification", mlog.String("email", user.Email), mlog.Err(nErr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,6 +193,8 @@ type postData struct {
|
|||||||
SenderPhoto string
|
SenderPhoto string
|
||||||
PostPhoto string
|
PostPhoto string
|
||||||
Time string
|
Time string
|
||||||
|
ShowChannelIcon bool
|
||||||
|
OtherChannelMembersCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1611,10 +1611,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "api.email_batching.send_batched_email_notification.title",
|
"id": "api.email_batching.send_batched_email_notification.title",
|
||||||
"translation": {
|
"translation": "You have new messages"
|
||||||
"one": "{{ .SenderName }} sent you new message",
|
|
||||||
"other": "{{ .SenderName }} and {{.Count}} others sent you new messages"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "api.emoji.create.duplicate.app_error",
|
"id": "api.emoji.create.duplicate.app_error",
|
||||||
|
|||||||
@@ -71,9 +71,9 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mj-column-per-50 {
|
.mj-column-per-33-333333333333336 {
|
||||||
width: 50% !important;
|
width: 33.333333333333336% !important;
|
||||||
max-width: 50%;
|
max-width: 33.333333333333336%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mj-column-per-90 {
|
.mj-column-per-90 {
|
||||||
@@ -88,9 +88,9 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.moz-text-html .mj-column-per-50 {
|
.moz-text-html .mj-column-per-33-333333333333336 {
|
||||||
width: 50% !important;
|
width: 33.333333333333336% !important;
|
||||||
max-width: 50%;
|
max-width: 33.333333333333336%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.moz-text-html .mj-column-per-90 {
|
.moz-text-html .mj-column-per-90 {
|
||||||
@@ -255,6 +255,7 @@
|
|||||||
background: rgba(63, 67, 80, 0.08);
|
background: rgba(63, 67, 80, 0.08);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
padding-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.channelLogo {
|
.channelLogo {
|
||||||
@@ -275,6 +276,13 @@
|
|||||||
padding: 2px 6px 2px 0px;
|
padding: 2px 6px 2px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gmChannelCount {
|
||||||
|
background-color: rgba(63, 67, 80, 0.2);
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.senderMessage div {
|
.senderMessage div {
|
||||||
text-align: left !important;
|
text-align: left !important;
|
||||||
font-size: 14px !important;
|
font-size: 14px !important;
|
||||||
@@ -427,8 +435,8 @@
|
|||||||
<td style="direction:ltr;font-size:0px;padding:0px;text-align:center;">
|
<td style="direction:ltr;font-size:0px;padding:0px;text-align:center;">
|
||||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="width:552px;" ><![endif]-->
|
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="width:552px;" ><![endif]-->
|
||||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0;line-height:0;text-align:left;display:inline-block;width:100%;direction:ltr;">
|
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0;line-height:0;text-align:left;display:inline-block;width:100%;direction:ltr;">
|
||||||
<!--[if mso | IE]><table border="0" cellpadding="0" cellspacing="0" role="presentation" ><tr><td style="vertical-align:top;width:276px;" ><![endif]-->
|
<!--[if mso | IE]><table border="0" cellpadding="0" cellspacing="0" role="presentation" ><tr><td style="vertical-align:top;width:184px;" ><![endif]-->
|
||||||
<div class="mj-column-per-50 mj-outlook-group-fix messageAvatarCol" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;">
|
<div class="mj-column-per-33-333333333333336 mj-outlook-group-fix messageAvatarCol" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:33%;">
|
||||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -436,8 +444,8 @@
|
|||||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
|
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width:276px;">
|
<td style="width:184px;">
|
||||||
<img alt="" height="auto" src="cid:{{.SenderPhoto}}" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="276" />
|
<img alt="" height="auto" src="cid:{{.SenderPhoto}}" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="184" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -460,8 +468,15 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{if .ChannelName}}
|
{{if .ChannelName}}
|
||||||
<div class="channelBg">
|
<div class="channelBg">
|
||||||
|
{{if .ShowChannelIcon}}
|
||||||
<div class="channelLogo"><img src="{{$.Props.SiteURL}}/static/images/channel_icon.png" width=10px height=10px></img></div>
|
<div class="channelLogo"><img src="{{$.Props.SiteURL}}/static/images/channel_icon.png" width=10px height=10px></img></div>
|
||||||
<div class="channelName">{{.ChannelName}}</div>
|
{{end}}
|
||||||
|
<div class="channelName">
|
||||||
|
{{if .OtherChannelMembersCount}}
|
||||||
|
<span class="gmChannelCount">{{.OtherChannelMembersCount}}</span>
|
||||||
|
{{end}}
|
||||||
|
{{.ChannelName}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
@@ -472,6 +487,13 @@
|
|||||||
<div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;">{{.Message}}</div>
|
<div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;">{{.Message}}</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!--[if mso | IE]></td><td style="vertical-align:top;width:552px;" ><![endif]-->
|
||||||
|
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||||
|
<tbody>
|
||||||
{{if .MessageURL}}
|
{{if .MessageURL}}
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" vertical-align="middle" class="messageButton" style="font-size:0px;padding:16px 0px 0px 0px;word-break:break-word;">
|
<td align="center" vertical-align="middle" class="messageButton" style="font-size:0px;padding:16px 0px 0px 0px;word-break:break-word;">
|
||||||
|
|||||||
@@ -14,8 +14,15 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{if .ChannelName}}
|
{{if .ChannelName}}
|
||||||
<div class="channelBg">
|
<div class="channelBg">
|
||||||
|
{{if .ShowChannelIcon}}
|
||||||
<div class="channelLogo"><img src="{{$.Props.SiteURL}}/static/images/channel_icon.png" width=10px height=10px></img></div>
|
<div class="channelLogo"><img src="{{$.Props.SiteURL}}/static/images/channel_icon.png" width=10px height=10px></img></div>
|
||||||
<div class="channelName">{{.ChannelName}}</div>
|
{{end}}
|
||||||
|
<div class="channelName">
|
||||||
|
{{if .OtherChannelMembersCount}}
|
||||||
|
<span class="gmChannelCount">{{.OtherChannelMembersCount}}</span>
|
||||||
|
{{end}}
|
||||||
|
{{.ChannelName}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
@@ -25,6 +32,8 @@
|
|||||||
<mj-text css-class="senderMessage" padding="0px">
|
<mj-text css-class="senderMessage" padding="0px">
|
||||||
{{.Message}}
|
{{.Message}}
|
||||||
</mj-text>
|
</mj-text>
|
||||||
|
</mj-column>
|
||||||
|
<mj-column width=100%>
|
||||||
<mj-raw>{{if .MessageURL}}</mj-raw>
|
<mj-raw>{{if .MessageURL}}</mj-raw>
|
||||||
<mj-button href="{{.MessageURL}}" padding="16px 0px 0px 0px" css-class="messageButton">{{$.Props.MessageButton}}</mj-button>
|
<mj-button href="{{.MessageURL}}" padding="16px 0px 0px 0px" css-class="messageButton">{{$.Props.MessageButton}}</mj-button>
|
||||||
<mj-raw>{{end}}</mj-raw>
|
<mj-raw>{{end}}</mj-raw>
|
||||||
|
|||||||
@@ -152,6 +152,7 @@
|
|||||||
background: rgba(63, 67, 80, 0.08);
|
background: rgba(63, 67, 80, 0.08);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
padding-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.channelLogo {
|
.channelLogo {
|
||||||
@@ -172,6 +173,13 @@
|
|||||||
padding: 2px 6px 2px 0px;
|
padding: 2px 6px 2px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gmChannelCount {
|
||||||
|
background-color: rgba(63, 67, 80, 0.2);
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.senderMessage div {
|
.senderMessage div {
|
||||||
text-align: left !important;
|
text-align: left !important;
|
||||||
font-size: 14px !important;
|
font-size: 14px !important;
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user