mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-28 04:35:04 -04:00
[GH-15906][MM-22844] Redesign message notification emails. (#17184)
* Redesign message notification emails. * Fix tests and linter. * Fix tests * Fix tests * gofmt. * Fix date separator * Update html files * Remove date for message notification email. * Modify subtitle for mentions and direct and group messages. * Fix lint error * Fix DM subtitle. * Fixing translations Co-authored-by: Mattermod <mattermod@users.noreply.github.com> Co-authored-by: Jesús Espino <jespinog@gmail.com>
This commit is contained in:
parent
bb35a29ed9
commit
d97daaa1de
22 changed files with 1265 additions and 666 deletions
|
|
@ -4,9 +4,11 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
|
@ -201,33 +203,61 @@ func (es *EmailService) sendBatchedEmailNotification(userID string, notification
|
|||
|
||||
translateFunc := i18n.GetUserTranslations(user.Locale)
|
||||
displayNameFormat := *es.srv.Config().TeamSettings.TeammateNameDisplay
|
||||
siteURL := *es.srv.Config().ServiceSettings.SiteURL
|
||||
|
||||
var contents string
|
||||
for _, notification := range notifications {
|
||||
sender, err := es.srv.Store.User().Get(context.Background(), notification.post.UserId)
|
||||
if err != nil {
|
||||
mlog.Warn("Unable to find sender of post for batched email notification")
|
||||
continue
|
||||
postsData := make([]*postData, 0 /* len */, len(notifications) /* cap */)
|
||||
embeddedFiles := make(map[string]io.Reader)
|
||||
|
||||
emailNotificationContentsType := model.EMAIL_NOTIFICATION_CONTENTS_FULL
|
||||
if license := es.srv.License(); license != nil && *license.Features.EmailNotificationContents {
|
||||
emailNotificationContentsType = *es.srv.Config().EmailSettings.EmailNotificationContentsType
|
||||
}
|
||||
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
for i, notification := range notifications {
|
||||
sender, errSender := es.srv.Store.User().Get(context.Background(), notification.post.UserId)
|
||||
if errSender != nil {
|
||||
mlog.Warn("Unable to find sender of post for batched email notification")
|
||||
}
|
||||
|
||||
channel, errCh := es.srv.Store.Channel().Get(notification.post.ChannelId, true)
|
||||
if errCh != nil {
|
||||
mlog.Warn("Unable to find channel of post for batched email notification")
|
||||
}
|
||||
|
||||
senderProfileImage, _, errProfileImage := es.srv.GetProfileImage(sender)
|
||||
if errProfileImage != nil {
|
||||
mlog.Warn("Unable to get the sender user profile image.", mlog.String("user_id", sender.Id), mlog.Err(errProfileImage))
|
||||
}
|
||||
|
||||
senderPhoto := fmt.Sprintf("user-avatar-%d.png", i)
|
||||
if senderProfileImage != nil {
|
||||
embeddedFiles[senderPhoto] = bytes.NewReader(senderProfileImage)
|
||||
}
|
||||
|
||||
tm := time.Unix(notification.post.CreateAt/1000, 0)
|
||||
timezone, _ := tm.Zone()
|
||||
|
||||
t := translateFunc("api.email_batching.send_batched_email_notification.time", map[string]interface{}{
|
||||
"Hour": tm.Hour(),
|
||||
"Minute": fmt.Sprintf("%02d", tm.Minute()),
|
||||
"Month": translateFunc(tm.Month().String()),
|
||||
"Day": tm.Day(),
|
||||
"Year": tm.Year(),
|
||||
"Timezone": timezone,
|
||||
})
|
||||
|
||||
MessageURL := siteURL + "/" + notification.teamName + "/pl/" + notification.post.Id
|
||||
|
||||
postsData = append(postsData, &postData{
|
||||
SenderPhoto: senderPhoto,
|
||||
SenderName: sender.GetDisplayName(displayNameFormat),
|
||||
Time: t,
|
||||
ChannelName: channel.DisplayName,
|
||||
Message: template.HTML(es.srv.GetMessageForNotification(notification.post, translateFunc)),
|
||||
MessageURL: MessageURL,
|
||||
})
|
||||
}
|
||||
|
||||
channel, errCh := es.srv.Store.Channel().Get(notification.post.ChannelId, true)
|
||||
if errCh != nil {
|
||||
mlog.Warn("Unable to find channel of post for batched email notification")
|
||||
continue
|
||||
}
|
||||
|
||||
emailNotificationContentsType := model.EMAIL_NOTIFICATION_CONTENTS_FULL
|
||||
if license := es.srv.License(); license != nil && *license.Features.EmailNotificationContents {
|
||||
emailNotificationContentsType = *es.srv.Config().EmailSettings.EmailNotificationContentsType
|
||||
}
|
||||
|
||||
postContent, err := es.renderBatchedPost(notification, channel, sender, *es.srv.Config().ServiceSettings.SiteURL, displayNameFormat, translateFunc, user.Locale, emailNotificationContentsType)
|
||||
if err != nil {
|
||||
mlog.Warn("Unable to render post for batched email notification template", mlog.Err(err))
|
||||
continue
|
||||
}
|
||||
|
||||
contents += postContent
|
||||
}
|
||||
|
||||
tm := time.Unix(notifications[0].post.CreateAt/1000, 0)
|
||||
|
|
@ -239,59 +269,31 @@ func (es *EmailService) sendBatchedEmailNotification(userID string, notification
|
|||
"Day": tm.Day(),
|
||||
})
|
||||
|
||||
data := es.newEmailTemplateData(user.Locale)
|
||||
data.Props["SiteURL"] = *es.srv.Config().ServiceSettings.SiteURL
|
||||
data.Props["Posts"] = template.HTML(contents)
|
||||
data.Props["BodyText"] = translateFunc("api.email_batching.send_batched_email_notification.body_text", len(notifications))
|
||||
|
||||
body, err2 := es.srv.TemplatesContainer().RenderToString("post_batched_body", data)
|
||||
if err2 != nil {
|
||||
mlog.Warn("Unable build the batched email notification template", mlog.Err(err2))
|
||||
return
|
||||
firstSender, err := es.srv.Store.User().Get(context.Background(), notifications[0].post.UserId)
|
||||
if err != nil {
|
||||
mlog.Warn("Unable to find sender of post for batched email notification")
|
||||
}
|
||||
|
||||
if nErr := es.sendNotificationMail(user.Email, subject, body); nErr != nil {
|
||||
data := es.newEmailTemplateData(user.Locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = translateFunc("api.email_batching.send_batched_email_notification.title", len(notifications)-1, map[string]interface{}{
|
||||
"SenderName": firstSender.GetDisplayName(displayNameFormat),
|
||||
})
|
||||
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["ButtonURL"] = siteURL
|
||||
data.Props["Posts"] = postsData
|
||||
data.Props["MessageButton"] = translateFunc("api.email_batching.send_batched_email_notification.messageButton")
|
||||
data.Props["NotificationFooterTitle"] = translateFunc("app.notification.footer.title")
|
||||
data.Props["NotificationFooterInfoLogin"] = translateFunc("app.notification.footer.infoLogin")
|
||||
data.Props["NotificationFooterInfo"] = translateFunc("app.notification.footer.info")
|
||||
|
||||
renderedPage, renderErr := es.srv.TemplatesContainer().RenderToString("messages_notification", data)
|
||||
if renderErr != nil {
|
||||
mlog.Error("Unable to render email", mlog.Err(renderErr))
|
||||
}
|
||||
|
||||
if nErr := es.sendNotificationMail(user.Email, subject, renderedPage); nErr != nil {
|
||||
mlog.Warn("Unable to send batched email notification", mlog.String("email", user.Email), mlog.Err(nErr))
|
||||
}
|
||||
}
|
||||
|
||||
func (es *EmailService) renderBatchedPost(notification *batchedNotification, channel *model.Channel, sender *model.User, siteURL string, displayNameFormat string, translateFunc i18n.TranslateFunc, userLocale string, emailNotificationContentsType string) (string, error) {
|
||||
// don't include message contents if email notification contents type is set to generic
|
||||
var templateName = "post_batched_post_generic"
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
templateName = "post_batched_post_full"
|
||||
}
|
||||
|
||||
data := es.newEmailTemplateData(userLocale)
|
||||
data.Props["Button"] = translateFunc("api.email_batching.render_batched_post.go_to_post")
|
||||
data.Props["PostMessage"] = es.srv.GetMessageForNotification(notification.post, translateFunc)
|
||||
data.Props["PostLink"] = siteURL + "/" + notification.teamName + "/pl/" + notification.post.Id
|
||||
data.Props["SenderName"] = sender.GetDisplayName(displayNameFormat)
|
||||
|
||||
tm := time.Unix(notification.post.CreateAt/1000, 0)
|
||||
timezone, _ := tm.Zone()
|
||||
|
||||
data.Props["Date"] = translateFunc("api.email_batching.render_batched_post.date", map[string]interface{}{
|
||||
"Year": tm.Year(),
|
||||
"Month": translateFunc(tm.Month().String()),
|
||||
"Day": tm.Day(),
|
||||
"Hour": tm.Hour(),
|
||||
"Minute": fmt.Sprintf("%02d", tm.Minute()),
|
||||
"Timezone": timezone,
|
||||
})
|
||||
|
||||
if channel.Type == model.CHANNEL_DIRECT {
|
||||
data.Props["ChannelName"] = translateFunc("api.email_batching.render_batched_post.direct_message")
|
||||
} else if channel.Type == model.CHANNEL_GROUP {
|
||||
data.Props["ChannelName"] = translateFunc("api.email_batching.render_batched_post.group_message")
|
||||
} else {
|
||||
// don't include channel name if email notification contents type is set to generic
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
data.Props["ChannelName"] = channel.DisplayName
|
||||
} else {
|
||||
data.Props["ChannelName"] = translateFunc("api.email_batching.render_batched_post.notification")
|
||||
}
|
||||
}
|
||||
|
||||
return es.srv.TemplatesContainer().RenderToString(templateName, data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -283,55 +283,3 @@ func TestCheckPendingNotificationsCantParseInterval(t *testing.T) {
|
|||
|
||||
require.Nil(t, job.pendingNotifications[th.BasicUser.Id], "should have sent queued post")
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures that post contents are not included in notification email when email notification content type is set to generic
|
||||
*/
|
||||
func TestRenderBatchedPostGeneric(t *testing.T) {
|
||||
th := SetupWithStoreMock(t)
|
||||
defer th.TearDown()
|
||||
|
||||
var post = &model.Post{}
|
||||
post.Message = "This is the message"
|
||||
var notification = &batchedNotification{}
|
||||
notification.post = post
|
||||
var channel = &model.Channel{}
|
||||
channel.DisplayName = "Some Test Channel"
|
||||
var sender = &model.User{}
|
||||
sender.Email = "sender@test.com"
|
||||
|
||||
translateFunc := func(translationID string, args ...interface{}) string {
|
||||
// mock translateFunc just returns the translation id - this is good enough for our purposes
|
||||
return translationID
|
||||
}
|
||||
|
||||
rendered, err := th.Server.EmailService.renderBatchedPost(notification, channel, sender, "http://localhost:8065", "", translateFunc, "en", model.EMAIL_NOTIFICATION_CONTENTS_GENERIC)
|
||||
require.NoError(t, err)
|
||||
require.NotContains(t, rendered, post.Message, "Rendered email should not contain post contents when email notification contents type is set to Generic.")
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures that post contents included in notification email when email notification content type is set to full
|
||||
*/
|
||||
func TestRenderBatchedPostFull(t *testing.T) {
|
||||
th := SetupWithStoreMock(t)
|
||||
defer th.TearDown()
|
||||
|
||||
var post = &model.Post{}
|
||||
post.Message = "This is the message"
|
||||
var notification = &batchedNotification{}
|
||||
notification.post = post
|
||||
var channel = &model.Channel{}
|
||||
channel.DisplayName = "Some Test Channel"
|
||||
var sender = &model.User{}
|
||||
sender.Email = "sender@test.com"
|
||||
|
||||
translateFunc := func(translationID string, args ...interface{}) string {
|
||||
// mock translateFunc just returns the translation id - this is good enough for our purposes
|
||||
return translationID
|
||||
}
|
||||
|
||||
rendered, err := th.Server.EmailService.renderBatchedPost(notification, channel, sender, "http://localhost:8065", "", translateFunc, "en", model.EMAIL_NOTIFICATION_CONTENTS_FULL)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, rendered, post.Message, "Rendered email should contain post contents when email notification contents type is set to Full.")
|
||||
}
|
||||
|
|
|
|||
21
app/file.go
21
app/file.go
|
|
@ -128,15 +128,7 @@ func (a *App) TestFileStoreConnectionWithConfig(cfg *model.FileSettings) *model.
|
|||
}
|
||||
|
||||
func (a *App) ReadFile(path string) ([]byte, *model.AppError) {
|
||||
backend, err := a.FileBackend()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, nErr := backend.ReadFile(path)
|
||||
if nErr != nil {
|
||||
return nil, model.NewAppError("ReadFile", "api.file.read_file.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return result, nil
|
||||
return a.srv.ReadFile(path)
|
||||
}
|
||||
|
||||
// Caller must close the first return value
|
||||
|
|
@ -202,16 +194,7 @@ func (a *App) MoveFile(oldPath, newPath string) *model.AppError {
|
|||
}
|
||||
|
||||
func (a *App) WriteFile(fr io.Reader, path string) (int64, *model.AppError) {
|
||||
backend, err := a.FileBackend()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
result, nErr := backend.WriteFile(fr, path)
|
||||
if nErr != nil {
|
||||
return result, model.NewAppError("WriteFile", "api.file.write_file.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return result, nil
|
||||
return a.srv.WriteFile(fr, path)
|
||||
}
|
||||
|
||||
func (a *App) AppendFile(fr io.Reader, path string) (int64, *model.AppError) {
|
||||
|
|
|
|||
|
|
@ -254,8 +254,11 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
|
|||
}
|
||||
|
||||
if a.userAllowsEmail(profileMap[id], channelMemberNotifyPropsMap[id], post) {
|
||||
err := a.sendNotificationEmail(notification, profileMap[id], team)
|
||||
senderProfileImage, _, err := a.GetProfileImage(sender)
|
||||
if err != nil {
|
||||
a.Log().Warn("Unable to get the sender user profile image.", mlog.String("user_id", sender.Id), mlog.Err(err))
|
||||
}
|
||||
if err := a.sendNotificationEmail(notification, profileMap[id], team, senderProfileImage); err != nil {
|
||||
mlog.Warn("Unable to send notification email.", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
|
@ -18,7 +20,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (a *App) sendNotificationEmail(notification *PostNotification, user *model.User, team *model.Team) error {
|
||||
func (a *App) sendNotificationEmail(notification *PostNotification, user *model.User, team *model.Team, senderProfileImage []byte) error {
|
||||
channel := notification.Channel
|
||||
post := notification.Post
|
||||
|
||||
|
|
@ -95,14 +97,24 @@ func (a *App) sendNotificationEmail(notification *PostNotification, user *model.
|
|||
subjectText = getNotificationEmailSubject(user, post, translateFunc, *a.Config().TeamSettings.SiteName, team.DisplayName, useMilitaryTime)
|
||||
}
|
||||
|
||||
senderPhoto := ""
|
||||
embeddedFiles := make(map[string]io.Reader)
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL && 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(user, post, channel, channelName, senderName, team.Name, landingURL, emailNotificationContentsType, useMilitaryTime, translateFunc)
|
||||
|
||||
var bodyText, err = a.getNotificationEmailBody(user, post, channel, channelName, senderName, team.Name, landingURL, emailNotificationContentsType, useMilitaryTime, translateFunc, senderPhoto)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to render the email notification template")
|
||||
}
|
||||
|
||||
a.Srv().Go(func() {
|
||||
if nErr := a.Srv().EmailService.sendNotificationMail(user.Email, html.UnescapeString(subjectText), bodyText); nErr != nil {
|
||||
if nErr := a.Srv().EmailService.sendMailWithEmbeddedFiles(user.Email, html.UnescapeString(subjectText), bodyText, embeddedFiles); nErr != nil {
|
||||
mlog.Error("Error while sending the email", mlog.String("user_email", user.Email), mlog.Err(nErr))
|
||||
}
|
||||
})
|
||||
|
|
@ -162,15 +174,33 @@ func getGroupMessageNotificationEmailSubject(user *model.User, post *model.Post,
|
|||
return translateFunc("app.notification.subject.group_message.generic", subjectParameters)
|
||||
}
|
||||
|
||||
type postData struct {
|
||||
SenderName string
|
||||
ChannelName string
|
||||
Message template.HTML
|
||||
MessageURL string
|
||||
SenderPhoto string
|
||||
PostPhoto string
|
||||
Time string
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the email body for notification messages
|
||||
*/
|
||||
func (a *App) getNotificationEmailBody(recipient *model.User, post *model.Post, channel *model.Channel, channelName string, senderName string, teamName string, landingURL string, emailNotificationContentsType string, useMilitaryTime bool, translateFunc i18n.TranslateFunc) (string, error) {
|
||||
// only include message contents in notification email if email notification contents type is set to full
|
||||
var templateName = "post_body_generic"
|
||||
data := a.Srv().EmailService.newEmailTemplateData(recipient.Locale)
|
||||
func (a *App) getNotificationEmailBody(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) {
|
||||
pData := postData{
|
||||
SenderName: senderName,
|
||||
SenderPhoto: senderPhoto,
|
||||
}
|
||||
|
||||
t := getFormattedPostTime(recipient, post, useMilitaryTime, translateFunc)
|
||||
messageTime := map[string]interface{}{
|
||||
"Hour": t.Hour,
|
||||
"Minute": t.Minute,
|
||||
"TimeZone": t.TimeZone,
|
||||
}
|
||||
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
templateName = "post_body_full"
|
||||
postMessage := a.GetMessageForNotification(post, translateFunc)
|
||||
postMessage = html.EscapeString(postMessage)
|
||||
normalizedPostMessage, err := a.generateHyperlinkForChannels(postMessage, teamName, landingURL)
|
||||
|
|
@ -178,72 +208,47 @@ func (a *App) getNotificationEmailBody(recipient *model.User, post *model.Post,
|
|||
mlog.Warn("Encountered error while generating hyperlink for channels", mlog.String("team_name", teamName), mlog.Err(err))
|
||||
normalizedPostMessage = postMessage
|
||||
}
|
||||
data.Props["PostMessage"] = template.HTML(normalizedPostMessage)
|
||||
pData.Message = template.HTML(normalizedPostMessage)
|
||||
pData.Time = translateFunc("app.notification.body.dm.time", messageTime)
|
||||
}
|
||||
|
||||
data := a.Srv().EmailService.newEmailTemplateData(recipient.Locale)
|
||||
data.Props["SiteURL"] = a.GetSiteURL()
|
||||
if teamName != "select_team" {
|
||||
data.Props["TeamLink"] = landingURL + "/pl/" + post.Id
|
||||
data.Props["ButtonURL"] = landingURL + "/pl/" + post.Id
|
||||
} else {
|
||||
data.Props["TeamLink"] = landingURL
|
||||
}
|
||||
|
||||
t := getFormattedPostTime(recipient, post, useMilitaryTime, translateFunc)
|
||||
|
||||
info := map[string]interface{}{
|
||||
"Hour": t.Hour,
|
||||
"Minute": t.Minute,
|
||||
"TimeZone": t.TimeZone,
|
||||
"Month": t.Month,
|
||||
"Day": t.Day,
|
||||
}
|
||||
if channel.Type == model.CHANNEL_DIRECT {
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
data.Props["BodyText"] = translateFunc("app.notification.body.intro.direct.full")
|
||||
data.Props["Info1"] = ""
|
||||
info["SenderName"] = senderName
|
||||
data.Props["Info2"] = translateFunc("app.notification.body.text.direct.full", info)
|
||||
} else {
|
||||
data.Props["BodyText"] = translateFunc("app.notification.body.intro.direct.generic", map[string]interface{}{
|
||||
"SenderName": senderName,
|
||||
})
|
||||
data.Props["Info"] = translateFunc("app.notification.body.text.direct.generic", info)
|
||||
}
|
||||
} else if channel.Type == model.CHANNEL_GROUP {
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
data.Props["BodyText"] = translateFunc("app.notification.body.intro.group_message.full")
|
||||
data.Props["Info1"] = translateFunc("app.notification.body.text.group_message.full",
|
||||
map[string]interface{}{
|
||||
"ChannelName": channelName,
|
||||
})
|
||||
info["SenderName"] = senderName
|
||||
data.Props["Info2"] = translateFunc("app.notification.body.text.group_message.full2", info)
|
||||
} else {
|
||||
data.Props["BodyText"] = translateFunc("app.notification.body.intro.group_message.generic", map[string]interface{}{
|
||||
"SenderName": senderName,
|
||||
})
|
||||
data.Props["Info"] = translateFunc("app.notification.body.text.group_message.generic", info)
|
||||
}
|
||||
} else {
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
data.Props["BodyText"] = translateFunc("app.notification.body.intro.notification.full")
|
||||
data.Props["Info1"] = translateFunc("app.notification.body.text.notification.full",
|
||||
map[string]interface{}{
|
||||
"ChannelName": channelName,
|
||||
})
|
||||
info["SenderName"] = senderName
|
||||
data.Props["Info2"] = translateFunc("app.notification.body.text.notification.full2", info)
|
||||
} else {
|
||||
data.Props["BodyText"] = translateFunc("app.notification.body.intro.notification.generic", map[string]interface{}{
|
||||
"SenderName": senderName,
|
||||
})
|
||||
data.Props["Info"] = translateFunc("app.notification.body.text.notification.generic", info)
|
||||
}
|
||||
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["NotificationFooterInfoLogin"] = translateFunc("app.notification.footer.infoLogin")
|
||||
data.Props["NotificationFooterInfo"] = translateFunc("app.notification.footer.info")
|
||||
|
||||
return a.Srv().TemplatesContainer().RenderToString(templateName, data)
|
||||
if channel.Type == model.CHANNEL_DIRECT {
|
||||
// Direct Messages
|
||||
data.Props["Title"] = translateFunc("app.notification.body.dm.title", map[string]interface{}{"SenderName": senderName})
|
||||
data.Props["SubTitle"] = translateFunc("app.notification.body.dm.subTitle", map[string]interface{}{"SenderName": senderName})
|
||||
} else if channel.Type == model.CHANNEL_GROUP {
|
||||
// Group Messages
|
||||
data.Props["Title"] = translateFunc("app.notification.body.group.title", map[string]interface{}{"SenderName": senderName})
|
||||
data.Props["SubTitle"] = translateFunc("app.notification.body.group.subTitle", map[string]interface{}{"SenderName": senderName})
|
||||
} else {
|
||||
// mentions
|
||||
data.Props["Title"] = translateFunc("app.notification.body.mention.title", map[string]interface{}{"SenderName": senderName})
|
||||
data.Props["SubTitle"] = translateFunc("app.notification.body.mention.subTitle", map[string]interface{}{"SenderName": senderName, "ChannelName": channelName})
|
||||
pData.ChannelName = channelName
|
||||
}
|
||||
|
||||
// only include posts in notification email if email notification contents type is set to full
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
data.Props["Posts"] = []postData{pData}
|
||||
} else {
|
||||
data.Props["Posts"] = []postData{}
|
||||
}
|
||||
|
||||
return a.Srv().TemplatesContainer().RenderToString("messages_notification", data)
|
||||
}
|
||||
|
||||
type formattedPostTime struct {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import (
|
|||
"fmt"
|
||||
"html/template"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -91,11 +90,9 @@ func TestGetNotificationEmailBodyFullNotificationPublicChannel(t *testing.T) {
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new notification.", fmt.Sprintf("Expected email text 'You have a new notification. Got %s", body))
|
||||
require.Contains(t, body, "Channel: "+channel.DisplayName, "Expected email text 'Channel: %s'. Got %s", channel.DisplayName, body)
|
||||
require.Contains(t, body, senderName+" - ", fmt.Sprintf("Expected email text '%s - '. Got %s", senderName, body))
|
||||
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))
|
||||
}
|
||||
|
|
@ -124,11 +121,9 @@ func TestGetNotificationEmailBodyFullNotificationGroupChannel(t *testing.T) {
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new Group Message.", fmt.Sprintf("Expected email text 'You have a new Group Message. Got "+body))
|
||||
require.Contains(t, body, "Channel: ChannelName", fmt.Sprintf("Expected email text 'Channel: ChannelName'. Got %s", body))
|
||||
require.Contains(t, body, senderName+" - ", fmt.Sprintf("Expected email text '%s - '. Got %s", senderName, body))
|
||||
require.Contains(t, body, "sent you a new message", fmt.Sprintf("Expected email text 'sent you a new message. Got "+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))
|
||||
}
|
||||
|
|
@ -157,11 +152,9 @@ func TestGetNotificationEmailBodyFullNotificationPrivateChannel(t *testing.T) {
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new notification.", fmt.Sprintf("Expected email text 'You have a new notification. Got "+body))
|
||||
require.Contains(t, body, "Channel: "+channel.DisplayName, fmt.Sprintf("Expected email text 'Channel: "+channel.DisplayName+"'. Got "+body))
|
||||
require.Contains(t, body, senderName+" - ", fmt.Sprintf("Expected email text '%s - '. Got %s", senderName, body))
|
||||
require.Contains(t, body, "mentioned you in a message", fmt.Sprintf("Expected email text 'mentioned you in a message. Got "+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))
|
||||
}
|
||||
|
|
@ -190,10 +183,9 @@ func TestGetNotificationEmailBodyFullNotificationDirectChannel(t *testing.T) {
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new Direct Message.", fmt.Sprintf("Expected email text 'You have a new Direct Message. Got "+body))
|
||||
require.Contains(t, body, senderName+" - ", fmt.Sprintf("Expected email text '%s - '. Got %s", senderName, body))
|
||||
require.Contains(t, body, "sent you a new message", fmt.Sprintf("Expected email text 'sent you a new message. Got "+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))
|
||||
}
|
||||
|
|
@ -226,11 +218,11 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTimeWithTimezone(t *testi
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, false, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, false, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
r, _ := regexp.Compile("E([S|D]+)T")
|
||||
zone := r.FindString(body)
|
||||
require.Contains(t, body, "sender - 9:43 AM "+zone+", April 25", fmt.Sprintf("Expected email text 'sender - 9:43 AM %s, April 25'. Got %s", zone, body))
|
||||
require.Contains(t, body, "9:43 AM "+zone, fmt.Sprintf("Expected email text '9:43 AM %s'. Got %s", zone, body))
|
||||
}
|
||||
|
||||
func TestGetNotificationEmailBodyFullNotificationLocaleTimeNoTimezone(t *testing.T) {
|
||||
|
|
@ -276,10 +268,10 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTimeNoTimezone(t *testing
|
|||
tmp, err := template.New("foo").Parse(`{{.}}`)
|
||||
require.NoError(t, err)
|
||||
var text bytes.Buffer
|
||||
err = tmp.Execute(&text, fmt.Sprintf("sender - %s:%s %s, %s %s", formattedTime.Hour, formattedTime.Minute, formattedTime.TimeZone, formattedTime.Month, formattedTime.Day))
|
||||
err = tmp.Execute(&text, fmt.Sprintf("%s:%s %s", formattedTime.Hour, formattedTime.Minute, formattedTime.TimeZone))
|
||||
require.NoError(t, err)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "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))
|
||||
|
|
@ -313,10 +305,9 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTime12Hour(t *testing.T)
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, false, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, false, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "sender - 2:30 PM", fmt.Sprintf("Expected email text 'sender - 2:30 PM'. Got %s", body))
|
||||
require.Contains(t, body, "April 25", fmt.Sprintf("Expected email text 'April 25'. Got %s", body))
|
||||
require.Contains(t, body, "2:30 PM", fmt.Sprintf("Expected email text '2:30 PM'. Got %s", body))
|
||||
}
|
||||
|
||||
func TestGetNotificationEmailBodyFullNotificationLocaleTime24Hour(t *testing.T) {
|
||||
|
|
@ -347,10 +338,9 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTime24Hour(t *testing.T)
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "sender - 14:30", fmt.Sprintf("Expected email text 'sender - 14:30'. Got %s", body))
|
||||
require.Contains(t, body, "April 25", fmt.Sprintf("Expected email text 'April 25'. Got %s", body))
|
||||
require.Contains(t, body, "14:30", fmt.Sprintf("Expected email text '14:30'. Got %s", body))
|
||||
}
|
||||
|
||||
// from here
|
||||
|
|
@ -378,11 +368,9 @@ func TestGetNotificationEmailBodyGenericNotificationPublicChannel(t *testing.T)
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new notification from "+senderName, fmt.Sprintf("Expected email text 'You have a new notification from %s'. Got %s", senderName, body))
|
||||
require.False(t, strings.Contains(body, "Channel: "+channel.DisplayName), fmt.Sprintf("Did not expect email text 'CHANNEL: %s'. Got %s", channel.DisplayName, body))
|
||||
require.False(t, strings.Contains(body, post.Message), fmt.Sprintf("Did not expect email text '%s'. Got %s", post.Message, body))
|
||||
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))
|
||||
}
|
||||
|
||||
|
|
@ -410,11 +398,9 @@ func TestGetNotificationEmailBodyGenericNotificationGroupChannel(t *testing.T) {
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new Group Message from "+senderName, fmt.Sprintf("Expected email text 'You have a new Group Message from %s'. Got %s", senderName, body))
|
||||
require.False(t, strings.Contains(body, "CHANNEL: "+channel.DisplayName), fmt.Sprintf("Did not expect email text 'CHANNEL: %s'. Got %s", channel.DisplayName, body))
|
||||
require.False(t, strings.Contains(body, post.Message), fmt.Sprintf("Did not expect email text '%s'. Got %s", post.Message, body))
|
||||
require.Contains(t, body, "sent you a new message", fmt.Sprintf("Expected email text 'sent you a new message. Got "+body))
|
||||
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
|
||||
}
|
||||
|
||||
|
|
@ -442,11 +428,9 @@ func TestGetNotificationEmailBodyGenericNotificationPrivateChannel(t *testing.T)
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new notification from "+senderName, fmt.Sprintf("Expected email text 'You have a new notification from %s'. Got %s", senderName, body))
|
||||
require.False(t, strings.Contains(body, "CHANNEL: "+channel.DisplayName), fmt.Sprintf("Did not expect email text 'CHANNEL: %s'. Got %s", channel.DisplayName, body))
|
||||
require.False(t, strings.Contains(body, post.Message), fmt.Sprintf("Did not expect email text '%s'. Got %s", post.Message, body))
|
||||
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))
|
||||
}
|
||||
|
||||
|
|
@ -474,11 +458,9 @@ func TestGetNotificationEmailBodyGenericNotificationDirectChannel(t *testing.T)
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new Direct Message from "+senderName, fmt.Sprintf("Expected email text 'You have a new Direct Message from "+senderName+"'. Got "+body))
|
||||
require.False(t, strings.Contains(body, "CHANNEL: "+channel.DisplayName), fmt.Sprintf("Did not expect email text 'CHANNEL: %s'. Got %s", channel.DisplayName, body))
|
||||
require.False(t, strings.Contains(body, post.Message), fmt.Sprintf("Did not expect email text '%s'. Got %s", post.Message, body))
|
||||
require.Contains(t, body, "sent you a new message", fmt.Sprintf("Expected email text 'sent you a new message. Got "+body))
|
||||
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
|
||||
}
|
||||
|
||||
|
|
@ -510,7 +492,7 @@ func TestGetNotificationEmailEscapingChars(t *testing.T) {
|
|||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, ch,
|
||||
channelName, senderName, teamName, teamURL,
|
||||
emailNotificationContentsType, true, translateFunc)
|
||||
emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotContains(t, body, message)
|
||||
|
|
@ -554,7 +536,7 @@ func TestGetNotificationEmailBodyPublicChannelMention(t *testing.T) {
|
|||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, ch,
|
||||
ch.Name, senderName, teamName, teamURL,
|
||||
emailNotificationContentsType, true, translateFunc)
|
||||
emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
channelURL := teamURL + "/channels/" + ch.Name
|
||||
mention := "~" + ch.Name
|
||||
|
|
@ -620,7 +602,7 @@ func TestGetNotificationEmailBodyMultiPublicChannelMention(t *testing.T) {
|
|||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, ch,
|
||||
ch.Name, senderName, teamName, teamURL,
|
||||
emailNotificationContentsType, true, translateFunc)
|
||||
emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
channelURL := teamURL + "/channels/" + ch.Name
|
||||
channelURL2 := teamURL + "/channels/" + ch2.Name
|
||||
|
|
@ -669,7 +651,7 @@ func TestGetNotificationEmailBodyPrivateChannelMention(t *testing.T) {
|
|||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, ch,
|
||||
ch.Name, senderName, teamName, teamURL,
|
||||
emailNotificationContentsType, true, translateFunc)
|
||||
emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
channelURL := teamURL + "/channels/" + ch.Name
|
||||
mention := "~" + ch.Name
|
||||
|
|
@ -813,7 +795,7 @@ func TestLandingLink(t *testing.T) {
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "user-avatar.png")
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
|
||||
}
|
||||
|
|
@ -843,7 +825,7 @@ func TestLandingLinkPermalink(t *testing.T) {
|
|||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc, "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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/maphash"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
|
|
@ -2114,6 +2116,76 @@ func (a *App) generateSupportPacketYaml() (*model.FileData, string) {
|
|||
return nil, warning
|
||||
}
|
||||
|
||||
func (s *Server) GetProfileImage(user *model.User) ([]byte, bool, *model.AppError) {
|
||||
if *s.Config().FileSettings.DriverName == "" {
|
||||
img, appErr := s.GetDefaultProfileImage(user)
|
||||
if appErr != nil {
|
||||
return nil, false, appErr
|
||||
}
|
||||
return img, false, nil
|
||||
}
|
||||
|
||||
path := "users/" + user.Id + "/profile.png"
|
||||
|
||||
data, err := s.ReadFile(path)
|
||||
if err != nil {
|
||||
img, appErr := s.GetDefaultProfileImage(user)
|
||||
if appErr != nil {
|
||||
return nil, false, appErr
|
||||
}
|
||||
|
||||
if user.LastPictureUpdate == 0 {
|
||||
if _, err := s.WriteFile(bytes.NewReader(img), path); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
return img, true, nil
|
||||
}
|
||||
|
||||
return data, false, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultProfileImage(user *model.User) ([]byte, *model.AppError) {
|
||||
var img []byte
|
||||
var appErr *model.AppError
|
||||
|
||||
if user.IsBot {
|
||||
img = model.BotDefaultImage
|
||||
appErr = nil
|
||||
} else {
|
||||
img, appErr = CreateProfileImage(user.Username, user.Id, *s.Config().FileSettings.InitialFont)
|
||||
}
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func (s *Server) ReadFile(path string) ([]byte, *model.AppError) {
|
||||
backend, err := s.FileBackend()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, nErr := backend.ReadFile(path)
|
||||
if nErr != nil {
|
||||
return nil, model.NewAppError("ReadFile", "api.file.read_file.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Server) WriteFile(fr io.Reader, path string) (int64, *model.AppError) {
|
||||
backend, err := s.FileBackend()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
result, nErr := backend.WriteFile(fr, path)
|
||||
if nErr != nil {
|
||||
return result, model.NewAppError("WriteFile", "api.file.write_file.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func runDNDStatusExpireJob(a *App) {
|
||||
if a.IsLeader() {
|
||||
a.srv.dndnTaskMut.Lock()
|
||||
|
|
|
|||
41
app/user.go
41
app/user.go
|
|
@ -875,48 +875,11 @@ func getFont(initialFont string) (*truetype.Font, error) {
|
|||
}
|
||||
|
||||
func (a *App) GetProfileImage(user *model.User) ([]byte, bool, *model.AppError) {
|
||||
if *a.Config().FileSettings.DriverName == "" {
|
||||
img, appErr := a.GetDefaultProfileImage(user)
|
||||
if appErr != nil {
|
||||
return nil, false, appErr
|
||||
}
|
||||
return img, false, nil
|
||||
}
|
||||
|
||||
path := "users/" + user.Id + "/profile.png"
|
||||
|
||||
data, err := a.ReadFile(path)
|
||||
if err != nil {
|
||||
img, appErr := a.GetDefaultProfileImage(user)
|
||||
if appErr != nil {
|
||||
return nil, false, appErr
|
||||
}
|
||||
|
||||
if user.LastPictureUpdate == 0 {
|
||||
if _, err := a.WriteFile(bytes.NewReader(img), path); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
return img, true, nil
|
||||
}
|
||||
|
||||
return data, false, nil
|
||||
return a.srv.GetProfileImage(user)
|
||||
}
|
||||
|
||||
func (a *App) GetDefaultProfileImage(user *model.User) ([]byte, *model.AppError) {
|
||||
var img []byte
|
||||
var appErr *model.AppError
|
||||
|
||||
if user.IsBot {
|
||||
img = model.BotDefaultImage
|
||||
appErr = nil
|
||||
} else {
|
||||
img, appErr = CreateProfileImage(user.Username, user.Id, *a.Config().FileSettings.InitialFont)
|
||||
}
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
return img, nil
|
||||
return a.srv.GetDefaultProfileImage(user)
|
||||
}
|
||||
|
||||
func (a *App) SetDefaultProfileImage(user *model.User) *model.AppError {
|
||||
|
|
|
|||
96
i18n/en.json
96
i18n/en.json
|
|
@ -1611,31 +1611,16 @@
|
|||
"translation": "Email batching has been disabled by the system administrator."
|
||||
},
|
||||
{
|
||||
"id": "api.email_batching.render_batched_post.date",
|
||||
"translation": "{{.Hour}}:{{.Minute}} {{.Timezone}}, {{.Month}} {{.Day}}"
|
||||
"id": "api.email_batching.send_batched_email_notification.button",
|
||||
"translation": "Open Mattermost"
|
||||
},
|
||||
{
|
||||
"id": "api.email_batching.render_batched_post.direct_message",
|
||||
"translation": "Direct Message from "
|
||||
"id": "api.email_batching.send_batched_email_notification.messageButton",
|
||||
"translation": "View this message"
|
||||
},
|
||||
{
|
||||
"id": "api.email_batching.render_batched_post.go_to_post",
|
||||
"translation": "Go to Post"
|
||||
},
|
||||
{
|
||||
"id": "api.email_batching.render_batched_post.group_message",
|
||||
"translation": "Group Message from "
|
||||
},
|
||||
{
|
||||
"id": "api.email_batching.render_batched_post.notification",
|
||||
"translation": "Notification from "
|
||||
},
|
||||
{
|
||||
"id": "api.email_batching.send_batched_email_notification.body_text",
|
||||
"translation": {
|
||||
"one": "You have a new notification.",
|
||||
"other": "You have {{.Count}} new notifications."
|
||||
}
|
||||
"id": "api.email_batching.send_batched_email_notification.subTitle",
|
||||
"translation": "See below for a summary of your new messages."
|
||||
},
|
||||
{
|
||||
"id": "api.email_batching.send_batched_email_notification.subject",
|
||||
|
|
@ -1644,6 +1629,17 @@
|
|||
"other": "[{{.SiteName}}] New Notifications for {{.Month}} {{.Day}}, {{.Year}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "api.email_batching.send_batched_email_notification.time",
|
||||
"translation": "{{.Hour}}:{{.Minute}} {{.TimeZone}}"
|
||||
},
|
||||
{
|
||||
"id": "api.email_batching.send_batched_email_notification.title",
|
||||
"translation": {
|
||||
"one": "{{ .SenderName }} sent you new message",
|
||||
"other": "{{ .SenderName }} and {{.Count}} others sent you new messages"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "api.emoji.create.duplicate.app_error",
|
||||
"translation": "Unable to create emoji. Another emoji with the same name already exists."
|
||||
|
|
@ -3488,7 +3484,7 @@
|
|||
},
|
||||
{
|
||||
"id": "api.templates.post_body.button",
|
||||
"translation": "Go To Post"
|
||||
"translation": "View Message"
|
||||
},
|
||||
{
|
||||
"id": "api.templates.questions_footer.info",
|
||||
|
|
@ -5447,60 +5443,44 @@
|
|||
"translation": "No license present"
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.intro.direct.full",
|
||||
"translation": "You have a new Direct Message."
|
||||
"id": "app.notification.body.dm.subTitle",
|
||||
"translation": "While you were away, {{.SenderName}} sent you a new Direct Message."
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.intro.direct.generic",
|
||||
"translation": "You have a new Direct Message from {{.SenderName}}"
|
||||
"id": "app.notification.body.dm.time",
|
||||
"translation": "{{.Hour}}:{{.Minute}} {{.TimeZone}}"
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.intro.group_message.full",
|
||||
"translation": "You have a new Group Message."
|
||||
"id": "app.notification.body.dm.title",
|
||||
"translation": "{{.SenderName}} sent you a new message"
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.intro.group_message.generic",
|
||||
"translation": "You have a new Group Message from {{.SenderName}}"
|
||||
"id": "app.notification.body.group.subTitle",
|
||||
"translation": "While you were away, {{.SenderName}} sent a message to your group."
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.intro.notification.full",
|
||||
"translation": "You have a new notification."
|
||||
"id": "app.notification.body.group.title",
|
||||
"translation": "{{.SenderName}} sent you a new message"
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.intro.notification.generic",
|
||||
"translation": "You have a new notification from {{.SenderName}}"
|
||||
"id": "app.notification.body.mention.subTitle",
|
||||
"translation": "While you were away, {{.SenderName}} mentioned you in the {{.ChannelName}} channel."
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.text.direct.full",
|
||||
"translation": "{{.SenderName}} - {{.Hour}}:{{.Minute}} {{.TimeZone}}, {{.Month}} {{.Day}}"
|
||||
"id": "app.notification.body.mention.title",
|
||||
"translation": "{{.SenderName}} mentioned you in a message"
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.text.direct.generic",
|
||||
"translation": "{{.Hour}}:{{.Minute}} {{.TimeZone}}, {{.Month}} {{.Day}}"
|
||||
"id": "app.notification.footer.info",
|
||||
"translation": " and go to Account Settings > Notifications"
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.text.group_message.full",
|
||||
"translation": "Channel: {{.ChannelName}}"
|
||||
"id": "app.notification.footer.infoLogin",
|
||||
"translation": "Login to Mattermost"
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.text.group_message.full2",
|
||||
"translation": "{{.SenderName}} - {{.Hour}}:{{.Minute}} {{.TimeZone}}, {{.Month}} {{.Day}}"
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.text.group_message.generic",
|
||||
"translation": "{{.Hour}}:{{.Minute}} {{.TimeZone}}, {{.Month}} {{.Day}}"
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.text.notification.full",
|
||||
"translation": "Channel: {{.ChannelName}}"
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.text.notification.full2",
|
||||
"translation": "{{.SenderName}} - {{.Hour}}:{{.Minute}} {{.TimeZone}}, {{.Month}} {{.Day}}"
|
||||
},
|
||||
{
|
||||
"id": "app.notification.body.text.notification.generic",
|
||||
"translation": "{{.Hour}}:{{.Minute}} {{.TimeZone}}, {{.Month}} {{.Day}}"
|
||||
"id": "app.notification.footer.title",
|
||||
"translation": "Want to change your notifications settings?"
|
||||
},
|
||||
{
|
||||
"id": "app.notification.subject.direct.full",
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@
|
|||
font-weight: 600 !important;
|
||||
font-size: 28px !important;
|
||||
line-height: 36px !important;
|
||||
letter-spacing: -0.02em !important;
|
||||
letter-spacing: -0.01em !important;
|
||||
color: #3D3C40 !important;
|
||||
}
|
||||
|
||||
|
|
@ -144,6 +144,17 @@
|
|||
padding: 15px 24px !important;
|
||||
}
|
||||
|
||||
.messageButton a {
|
||||
background-color: #FFFFFF !important;
|
||||
border: 1px solid #FFFFFF !important;
|
||||
box-sizing: border-box !important;
|
||||
color: #166DE0 !important;
|
||||
padding: 12px 20px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 14px !important;
|
||||
}
|
||||
|
||||
.info div {
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
|
|
@ -212,13 +223,52 @@
|
|||
width: 32px !important;
|
||||
}
|
||||
|
||||
.senderName div {
|
||||
.postNameAndTime {
|
||||
padding: 0px 0px 4px 0px !important;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.senderName {
|
||||
font-family: Open Sans, sans-serif;
|
||||
text-align: left !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
color: #3D3C40 !important;
|
||||
padding: 0px 0px 4px 0px !important;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-family: Open Sans, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: rgba(61, 60, 64, 0.56);
|
||||
padding: 2px 6px;
|
||||
align-items: center;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.channelBg {
|
||||
background: rgba(61, 60, 64, 0.08);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.channelLogo {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
padding: 5px 4px 5px 6px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
font-family: Open Sans, sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.01em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(61, 60, 64, 0.64);
|
||||
padding: 2px 6px 2px 0px;
|
||||
}
|
||||
|
||||
.senderMessage div {
|
||||
|
|
@ -383,7 +433,7 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td style="width:276px;">
|
||||
<img alt="" height="auto" src="cid:user-avatar.png" 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="276" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
@ -398,15 +448,41 @@
|
|||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" class="senderName" style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;">{{.Props.SenderName}}</div>
|
||||
<td>
|
||||
<div class="postNameAndTime">
|
||||
<div class="senderName">{{.SenderName}}</div>
|
||||
{{if .Time}}
|
||||
<div class="time">{{.Time}}</div>
|
||||
{{end}}
|
||||
{{if .ChannelName}}
|
||||
<div class="channelBg">
|
||||
<div class="channelLogo"><img src="{{$.Props.SiteURL}}/static/images/channel_icon.png" width=10px height=10px></img></div>
|
||||
<div class="channelName">{{.ChannelName}}</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" class="senderMessage" style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;">{{.Props.Message}}</div>
|
||||
<div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;">{{.Message}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{if .MessageURL}}
|
||||
<tr>
|
||||
<td align="center" vertical-align="middle" class="messageButton" style="font-size:0px;padding:16px 0px 0px 0px;word-break:break-word;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;line-height:100%;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#FFFFFF" role="presentation" style="border:none;border-radius:4px;cursor:auto;mso-padding-alt:10px 25px;background:#FFFFFF;" valign="middle">
|
||||
<a href="{{.MessageURL}}" style="display:inline-block;background:#FFFFFF;color:#ffffff;font-family:Open Sans, sans-serif;font-size:13px;font-weight:normal;line-height:120%;margin:0;text-decoration:none;text-transform:none;padding:10px 25px;mso-padding-alt:0px;border-radius:4px;" target="_blank">
|
||||
{{$.Props.MessageButton}}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
|||
550
templates/messages_notification.html
Normal file
550
templates/messages_notification.html
Normal file
|
|
@ -0,0 +1,550 @@
|
|||
{{define "messages_notification"}}
|
||||
|
||||
<!-- FILE: messages_notification.mjml -->
|
||||
<!doctype html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
</title>
|
||||
<!--[if !mso]><!-->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!--<![endif]-->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style type="text/css">
|
||||
#outlook a {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
table,
|
||||
td {
|
||||
border-collapse: collapse;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
height: auto;
|
||||
line-height: 100%;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
|
||||
p {
|
||||
display: block;
|
||||
margin: 13px 0;
|
||||
}
|
||||
</style>
|
||||
<!--[if mso]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
<!--[if lte mso 11]>
|
||||
<style type="text/css">
|
||||
.mj-outlook-group-fix { width:100% !important; }
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!--[if !mso]><!-->
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,700" rel="stylesheet" type="text/css">
|
||||
<style type="text/css">
|
||||
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,700);
|
||||
</style>
|
||||
<!--<![endif]-->
|
||||
<style type="text/css">
|
||||
@media only screen and (min-width:480px) {
|
||||
.mj-column-per-100 {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.mj-column-per-50 {
|
||||
width: 50% !important;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.mj-column-per-90 {
|
||||
width: 90% !important;
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
@media only screen and (max-width:480px) {
|
||||
table.mj-full-width-mobile {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
td.mj-full-width-mobile {
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
|
||||
|
||||
.emailBody {
|
||||
background: #F3F3F3 !important;
|
||||
}
|
||||
|
||||
.emailBody a {
|
||||
text-decoration: none !important;
|
||||
color: #166DE0 !important;
|
||||
}
|
||||
|
||||
.title div {
|
||||
font-weight: 600 !important;
|
||||
font-size: 28px !important;
|
||||
line-height: 36px !important;
|
||||
letter-spacing: -0.01em !important;
|
||||
color: #3D3C40 !important;
|
||||
}
|
||||
|
||||
.subTitle div {
|
||||
font-size: 16px !important;
|
||||
line-height: 24px !important;
|
||||
color: rgba(61, 60, 64, 0.64) !important;
|
||||
}
|
||||
|
||||
.button a {
|
||||
background-color: #166DE0 !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 16px !important;
|
||||
line-height: 18px !important;
|
||||
color: #FFFFFF !important;
|
||||
padding: 15px 24px !important;
|
||||
}
|
||||
|
||||
.messageButton a {
|
||||
background-color: #FFFFFF !important;
|
||||
border: 1px solid #FFFFFF !important;
|
||||
box-sizing: border-box !important;
|
||||
color: #166DE0 !important;
|
||||
padding: 12px 20px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 14px !important;
|
||||
}
|
||||
|
||||
.info div {
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
color: #3D3C40 !important;
|
||||
padding: 40px 0px !important;
|
||||
}
|
||||
|
||||
.footerTitle div {
|
||||
font-weight: 600 !important;
|
||||
font-size: 16px !important;
|
||||
line-height: 24px !important;
|
||||
color: #3D3C40 !important;
|
||||
padding: 0px 0px 4px 0px !important;
|
||||
}
|
||||
|
||||
.footerInfo div {
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
color: #3D3C40 !important;
|
||||
padding: 0px 48px 0px 48px !important;
|
||||
}
|
||||
|
||||
.footerInfo a {
|
||||
color: #166DE0 !important;
|
||||
}
|
||||
|
||||
.appDownloadButton a {
|
||||
background-color: #FFFFFF !important;
|
||||
border: 1px solid #166DE0 !important;
|
||||
box-sizing: border-box !important;
|
||||
color: #166DE0 !important;
|
||||
padding: 13px 20px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 14px !important;
|
||||
}
|
||||
|
||||
.emailFooter div {
|
||||
font-size: 12px !important;
|
||||
line-height: 16px !important;
|
||||
color: rgba(61, 60, 64, 0.56) !important;
|
||||
padding: 8px 24px 8px 24px !important;
|
||||
}
|
||||
|
||||
.postCard {
|
||||
padding: 0px 24px 40px 24px !important;
|
||||
}
|
||||
|
||||
.messageCard {
|
||||
background: #FFFFFF !important;
|
||||
border: 1px solid rgba(61, 60, 64, 0.08) !important;
|
||||
box-sizing: border-box !important;
|
||||
box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.12) !important;
|
||||
border-radius: 4px !important;
|
||||
padding: 32px !important;
|
||||
}
|
||||
|
||||
.messageAvatar img {
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
padding: 0px !important;
|
||||
border-radius: 32px !important;
|
||||
}
|
||||
|
||||
.messageAvatarCol {
|
||||
width: 32px !important;
|
||||
}
|
||||
|
||||
.postNameAndTime {
|
||||
padding: 0px 0px 4px 0px !important;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.senderName {
|
||||
font-family: Open Sans, sans-serif;
|
||||
text-align: left !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
color: #3D3C40 !important;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-family: Open Sans, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: rgba(61, 60, 64, 0.56);
|
||||
padding: 2px 6px;
|
||||
align-items: center;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.channelBg {
|
||||
background: rgba(61, 60, 64, 0.08);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.channelLogo {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
padding: 5px 4px 5px 6px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
font-family: Open Sans, sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.01em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(61, 60, 64, 0.64);
|
||||
padding: 2px 6px 2px 0px;
|
||||
}
|
||||
|
||||
.senderMessage div {
|
||||
text-align: left !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
color: #3D3C40 !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.senderInfoCol {
|
||||
width: 394px !important;
|
||||
padding: 0px 0px 0px 12px !important;
|
||||
}
|
||||
|
||||
@media all and (min-width: 541px) {
|
||||
.emailBody {
|
||||
padding: 32px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 540px) and (min-width: 401px) {
|
||||
.emailBody {
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.messageCard {
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.senderInfoCol {
|
||||
width: 80% !important;
|
||||
padding: 0px 0px 0px 12px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 400px) {
|
||||
.emailBody {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.footerInfo div {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.messageCard {
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.postCard {
|
||||
padding: 0px 0px 40px 0px !important;
|
||||
}
|
||||
|
||||
.senderInfoCol {
|
||||
width: 80% !important;
|
||||
padding: 0px 0px 0px 12px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="word-spacing:normal;">
|
||||
<div class="emailBody" style="">
|
||||
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="background:#FFFFFF;background-color:#FFFFFF;margin:0px auto;border-radius:8px;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#FFFFFF;background-color:#FFFFFF;width:100%;border-radius:8px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:24px;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:552px;" width="552" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin:0px auto;max-width:552px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:0px 0px 40px 0px;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" 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>
|
||||
<tr>
|
||||
<td align="center" style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width:132px;">
|
||||
<img alt="" height="21" src="{{.Props.SiteURL}}/static/images/logo_email_blue.png" style="border:0;display:block;outline:none;text-decoration:none;height:21.76px;width:100%;font-size:13px;" width="132" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table></td></tr><tr><td class="" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:552px;" width="552" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin:0px auto;max-width:552px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:0px 24px 40px 24px;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:504px;" ><![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>
|
||||
<tr>
|
||||
<td align="center" class="title" style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;">{{.Props.Title}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" class="subTitle" style="font-size:0px;padding:16px 24px 16px 24px;word-break:break-word;">
|
||||
<div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;">{{.Props.SubTitle}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" vertical-align="middle" class="button" style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;line-height:100%;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#FFFFFF" role="presentation" style="border:none;border-radius:4px;cursor:auto;mso-padding-alt:10px 25px;background:#FFFFFF;" valign="middle">
|
||||
<a href="{{.Props.ButtonURL}}" style="display:inline-block;background:#FFFFFF;color:#ffffff;font-family:Open Sans, sans-serif;font-size:13px;font-weight:normal;line-height:120%;margin:0;text-decoration:none;text-transform:none;padding:10px 25px;mso-padding-alt:0px;border-radius:4px;" target="_blank">
|
||||
{{.Props.Button}}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table></td></tr><![endif]-->
|
||||
{{range .Props.Posts}}
|
||||
<div class="postCard">
|
||||
<!--[if mso | IE]><tr><td class="messageCard-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="messageCard-outlook" style="width:552px;" width="552" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div class="messageCard" style="margin:0px auto;max-width:552px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<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]-->
|
||||
<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]-->
|
||||
<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%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" class="messageAvatar" style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width:276px;">
|
||||
<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" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td><td style="vertical-align:top;width:496px;" ><![endif]-->
|
||||
<div class="mj-column-per-90 mj-outlook-group-fix senderInfoCol" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:90%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="postNameAndTime">
|
||||
<div class="senderName">{{.SenderName}}</div>
|
||||
{{if .Time}}
|
||||
<div class="time">{{.Time}}</div>
|
||||
{{end}}
|
||||
{{if .ChannelName}}
|
||||
<div class="channelBg">
|
||||
<div class="channelLogo"><img src="{{$.Props.SiteURL}}/static/images/channel_icon.png" width=10px height=10px></img></div>
|
||||
<div class="channelName">{{.ChannelName}}</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" class="senderMessage" style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;">{{.Message}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{if .MessageURL}}
|
||||
<tr>
|
||||
<td align="center" vertical-align="middle" class="messageButton" style="font-size:0px;padding:16px 0px 0px 0px;word-break:break-word;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;line-height:100%;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#FFFFFF" role="presentation" style="border:none;border-radius:4px;cursor:auto;mso-padding-alt:10px 25px;background:#FFFFFF;" valign="middle">
|
||||
<a href="{{.MessageURL}}" style="display:inline-block;background:#FFFFFF;color:#ffffff;font-family:Open Sans, sans-serif;font-size:13px;font-weight:normal;line-height:120%;margin:0;text-decoration:none;text-transform:none;padding:10px 25px;mso-padding-alt:0px;border-radius:4px;" target="_blank">
|
||||
{{$.Props.MessageButton}}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table></td></tr><![endif]-->
|
||||
</div>{{end}}
|
||||
<!--[if mso | IE]><tr><td class="" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:552px;" width="552" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin:0px auto;max-width:552px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:16px 0px 40px 0px;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" 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>
|
||||
<tr>
|
||||
<td align="center" class="footerTitle" style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;">{{.Props.NotificationFooterTitle}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" class="footerInfo" style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;"><a href="{{.Props.SiteURL}}">{{.Props.NotificationFooterInfoLogin}}</a>{{.Props.NotificationFooterInfo}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table></td></tr><tr><td class="" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:552px;" width="552" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin:0px auto;max-width:552px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<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="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>
|
||||
<tr>
|
||||
<td align="center" class="emailFooter" style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;">{{.Props.Organization}}
|
||||
{{.Props.FooterV2}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
{{end}}
|
||||
25
templates/messages_notification.mjml
Normal file
25
templates/messages_notification.mjml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<mjml>
|
||||
<mj-head>
|
||||
<mj-include path="./partials/style.mjml" />
|
||||
</mj-head>
|
||||
<mj-body css-class="emailBody">
|
||||
<mj-wrapper mj-class="email">
|
||||
<mj-include path="./partials/logo.mjml" />
|
||||
<mj-include path="./partials/header.mjml" />
|
||||
<mj-raw>{{range .Props.Posts}}<div class="postCard"></mj-raw>
|
||||
<mj-include path="./partials/card.mjml" />
|
||||
<mj-raw></div>{{end}}</mj-raw>
|
||||
<mj-section padding="16px 0px 40px 0px">
|
||||
<mj-column>
|
||||
<mj-text css-class="footerTitle" padding="0px">
|
||||
{{.Props.NotificationFooterTitle}}
|
||||
</mj-text>
|
||||
<mj-text css-class="footerInfo" padding="0px">
|
||||
<a href="{{.Props.SiteURL}}">{{.Props.NotificationFooterInfoLogin}}</a>{{.Props.NotificationFooterInfo}}
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-include path="./partials/email_footer.mjml" />
|
||||
</mj-wrapper>
|
||||
</mj-body>
|
||||
</mjml>
|
||||
|
|
@ -1,15 +1,33 @@
|
|||
<mj-section css-class="messageCard" padding="0px">
|
||||
<mj-group>
|
||||
<mj-column css-class="messageAvatarCol">
|
||||
<mj-image css-class="messageAvatar" src="cid:user-avatar.png" padding="0px" />
|
||||
<mj-image css-class="messageAvatar" src="cid:{{.SenderPhoto}}" padding="0px" />
|
||||
</mj-column>
|
||||
<mj-column css-class="senderInfoCol" width=90% >
|
||||
<mj-text css-class="senderName" padding="0px">
|
||||
{{.Props.SenderName}}
|
||||
</mj-text>
|
||||
<mj-raw>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="postNameAndTime">
|
||||
<div class="senderName">{{.SenderName}}</div>
|
||||
{{if .Time}}
|
||||
<div class="time">{{.Time}}</div>
|
||||
{{end}}
|
||||
{{if .ChannelName}}
|
||||
<div class="channelBg">
|
||||
<div class="channelLogo"><img src="{{$.Props.SiteURL}}/static/images/channel_icon.png" width=10px height=10px></img></div>
|
||||
<div class="channelName">{{.ChannelName}}</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</mj-raw>
|
||||
<mj-text css-class="senderMessage" padding="0px">
|
||||
{{.Props.Message}}
|
||||
{{.Message}}
|
||||
</mj-text>
|
||||
<mj-raw>{{if .MessageURL}}</mj-raw>
|
||||
<mj-button href="{{.MessageURL}}" padding="16px 0px 0px 0px" css-class="messageButton">{{$.Props.MessageButton}}</mj-button>
|
||||
<mj-raw>{{end}}</mj-raw>
|
||||
</mj-column>
|
||||
</mj-group>
|
||||
</mj-section>
|
||||
</mj-section>
|
||||
|
|
|
|||
|
|
@ -11,168 +11,217 @@
|
|||
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
|
||||
|
||||
.emailBody {
|
||||
background:#F3F3F3 !important;
|
||||
background: #F3F3F3 !important;
|
||||
}
|
||||
|
||||
.emailBody a{
|
||||
text-decoration:none !important;
|
||||
color:#166DE0 !important;
|
||||
text-decoration: none !important;
|
||||
color: #166DE0 !important;
|
||||
}
|
||||
|
||||
.title div {
|
||||
font-weight:600 !important;
|
||||
font-size:28px !important;
|
||||
line-height:36px !important;
|
||||
letter-spacing:-0.02em !important;
|
||||
color:#3D3C40 !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 28px !important;
|
||||
line-height: 36px !important;
|
||||
letter-spacing: -0.01em !important;
|
||||
color: #3D3C40 !important;
|
||||
}
|
||||
|
||||
.subTitle div {
|
||||
font-size:16px !important;
|
||||
line-height:24px !important;
|
||||
color:rgba(61, 60, 64, 0.64) !important;
|
||||
font-size: 16px !important;
|
||||
line-height: 24px !important;
|
||||
color: rgba(61, 60, 64, 0.64) !important;
|
||||
}
|
||||
|
||||
.button a {
|
||||
background-color:#166DE0 !important;
|
||||
font-weight:600 !important;
|
||||
font-size:16px !important;
|
||||
line-height:18px !important;
|
||||
color:#FFFFFF !important;
|
||||
padding:15px 24px !important;
|
||||
background-color: #166DE0 !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 16px !important;
|
||||
line-height: 18px !important;
|
||||
color: #FFFFFF !important;
|
||||
padding: 15px 24px !important;
|
||||
}
|
||||
|
||||
.messageButton a{
|
||||
background-color: #FFFFFF !important;
|
||||
border: 1px solid #FFFFFF !important;
|
||||
box-sizing: border-box !important;
|
||||
color: #166DE0 !important;
|
||||
padding: 12px 20px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 14px !important;
|
||||
}
|
||||
|
||||
.info div {
|
||||
font-size:14px !important;
|
||||
line-height:20px !important;
|
||||
color:#3D3C40 !important;
|
||||
padding:40px 0px !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
color: #3D3C40 !important;
|
||||
padding: 40px 0px !important;
|
||||
}
|
||||
|
||||
.footerTitle div {
|
||||
font-weight:600 !important;
|
||||
font-size:16px !important;
|
||||
line-height:24px !important;
|
||||
color:#3D3C40 !important;
|
||||
padding:0px 0px 4px 0px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 16px !important;
|
||||
line-height: 24px !important;
|
||||
color: #3D3C40 !important;
|
||||
padding: 0px 0px 4px 0px !important;
|
||||
}
|
||||
|
||||
.footerInfo div {
|
||||
font-size:14px !important;
|
||||
line-height:20px !important;
|
||||
color:#3D3C40 !important;
|
||||
padding:0px 48px 0px 48px !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
color: #3D3C40 !important;
|
||||
padding: 0px 48px 0px 48px !important;
|
||||
}
|
||||
|
||||
.footerInfo a {
|
||||
color:#166DE0 !important;
|
||||
color: #166DE0 !important;
|
||||
}
|
||||
|
||||
.appDownloadButton a{
|
||||
background-color:#FFFFFF !important;
|
||||
border:1px solid #166DE0 !important;
|
||||
background-color: #FFFFFF !important;
|
||||
border: 1px solid #166DE0 !important;
|
||||
box-sizing: border-box !important;
|
||||
color:#166DE0 !important;
|
||||
padding:13px 20px !important;
|
||||
font-weight:600 !important;
|
||||
font-size:14px !important;
|
||||
line-height:14px !important;
|
||||
color: #166DE0 !important;
|
||||
padding: 13px 20px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 14px !important;
|
||||
}
|
||||
|
||||
.emailFooter div {
|
||||
font-size:12px !important;
|
||||
line-height:16px !important;
|
||||
color:rgba(61, 60, 64, 0.56) !important;
|
||||
padding:8px 24px 8px 24px !important;
|
||||
font-size: 12px !important;
|
||||
line-height: 16px !important;
|
||||
color: rgba(61, 60, 64, 0.56) !important;
|
||||
padding: 8px 24px 8px 24px !important;
|
||||
}
|
||||
|
||||
.postCard {
|
||||
padding:0px 24px 40px 24px !important;
|
||||
padding: 0px 24px 40px 24px !important;
|
||||
}
|
||||
|
||||
.messageCard {
|
||||
background:#FFFFFF !important;
|
||||
border:1px solid rgba(61, 60, 64, 0.08) !important;
|
||||
box-sizing:border-box !important;
|
||||
box-shadow:0px 8px 24px rgba(0, 0, 0, 0.12) !important;
|
||||
border-radius:4px !important;
|
||||
padding:32px !important;
|
||||
background: #FFFFFF !important;
|
||||
border: 1px solid rgba(61, 60, 64, 0.08) !important;
|
||||
box-sizing: border-box !important;
|
||||
box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.12) !important;
|
||||
border-radius: 4px !important;
|
||||
padding: 32px !important;
|
||||
}
|
||||
|
||||
.messageAvatar img {
|
||||
width:32px !important;
|
||||
height:32px !important;
|
||||
padding:0px !important;
|
||||
border-radius:32px !important;
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
padding: 0px !important;
|
||||
border-radius: 32px !important;
|
||||
}
|
||||
|
||||
.messageAvatarCol {
|
||||
width:32px !important;
|
||||
width: 32px !important;
|
||||
}
|
||||
|
||||
.senderName div {
|
||||
text-align:left !important;
|
||||
font-weight:600 !important;
|
||||
font-size:14px !important;
|
||||
line-height:20px !important;
|
||||
color:#3D3C40 !important;
|
||||
padding:0px 0px 4px 0px !important;
|
||||
.postNameAndTime {
|
||||
padding: 0px 0px 4px 0px !important;
|
||||
display: flex;
|
||||
}
|
||||
.senderName {
|
||||
font-family: Open Sans, sans-serif;
|
||||
text-align: left !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
color: #3D3C40 !important;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-family: Open Sans, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: rgba(61, 60, 64, 0.56);
|
||||
padding: 2px 6px;
|
||||
align-items: center;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.channelBg {
|
||||
background: rgba(61, 60, 64, 0.08);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.channelLogo {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
padding: 5px 4px 5px 6px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
font-family: Open Sans, sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.01em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(61, 60, 64, 0.64);
|
||||
padding: 2px 6px 2px 0px;
|
||||
}
|
||||
|
||||
.senderMessage div {
|
||||
text-align:left !important;
|
||||
font-size:14px !important;
|
||||
line-height:20px !important;
|
||||
color:#3D3C40 !important;
|
||||
padding:0px !important;
|
||||
text-align: left !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
color: #3D3C40 !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.senderInfoCol {
|
||||
width:394px !important;
|
||||
padding:0px 0px 0px 12px !important;
|
||||
width: 394px !important;
|
||||
padding: 0px 0px 0px 12px !important;
|
||||
}
|
||||
|
||||
@media all and (min-width: 541px) {
|
||||
.emailBody {
|
||||
padding:32px !important;
|
||||
padding: 32px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 540px) and (min-width: 401px) {
|
||||
.emailBody {
|
||||
padding:16px !important;
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.messageCard {
|
||||
padding:16px !important;
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.senderInfoCol {
|
||||
width:80% !important;
|
||||
padding:0px 0px 0px 12px !important;
|
||||
width: 80% !important;
|
||||
padding: 0px 0px 0px 12px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 400px) {
|
||||
.emailBody {
|
||||
padding:0px !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.footerInfo div {
|
||||
padding:0px !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.messageCard {
|
||||
padding:16px !important;
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.postCard {
|
||||
padding:0px 0px 40px 0px !important;
|
||||
padding: 0px 0px 40px 0px !important;
|
||||
}
|
||||
|
||||
.senderInfoCol {
|
||||
width:80% !important;
|
||||
padding:0px 0px 0px 12px !important;
|
||||
width: 80% !important;
|
||||
padding: 0px 0px 0px 12px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
{{define "post_batched_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; width: 100%;">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; margin: 10px 0 20px;">
|
||||
<p style="font-weight: normal; text-align: left;">
|
||||
{{.Props.BodyText}}
|
||||
</p>
|
||||
{{.Props.Posts}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
{{define "post_batched_post_full"}}
|
||||
|
||||
<style type="text/css">
|
||||
@media screen and (max-width: 480px){
|
||||
a[class="post_btn"] {
|
||||
float: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<table style="border-top: 1px solid #ddd; padding: 20px 0; width: 100%">
|
||||
<tr>
|
||||
<td style="text-align: left">
|
||||
<span style="font-size: 16px; font-weight: bold; color: #555; margin: 0 0 5px; display: inline-block;" >
|
||||
{{.Props.ChannelName}}
|
||||
</span>
|
||||
<br/>
|
||||
<div style="margin: 5px 0 0;">
|
||||
<span style="font-weight: bold; white-space: nowrap;">
|
||||
@{{.Props.SenderName}}
|
||||
</span>
|
||||
<span style="color: #AAA; font-size: 12px; margin-left: 2px;">
|
||||
{{.Props.Date}}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2>
|
||||
<pre style="text-align:left; font-family: 'Lato', sans-serif; margin: 0px; white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word; line-height: 20px;">{{.Props.PostMessage}}</pre>
|
||||
<a class="post_btn" href="{{.Props.PostLink}}" style="font-size: 13px; background: #2389D7; display: inline-block; border-radius: 2px; color: #fff; padding: 6px 0; width: 120px; text-decoration: none; float:left; text-align: center; margin: 15px 0 5px;">
|
||||
{{.Props.Button}}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
{{define "post_batched_post_generic"}}
|
||||
|
||||
<style type="text/css">
|
||||
@media screen and (max-width: 480px){
|
||||
a[class="post_btn"] {
|
||||
float: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<table style="border-top: 1px solid #ddd; padding: 20px 0; width: 100%">
|
||||
<tr>
|
||||
<td style="text-align: left">
|
||||
<span style="font-size: 16px; font-weight: bold; color: #555; margin: 0 0 5px; display: inline-block;" >
|
||||
{{.Props.ChannelName}}
|
||||
</span>
|
||||
<span style="font-weight: bold; white-space: nowrap; color: #555;">
|
||||
@{{.Props.SenderName}}
|
||||
</span>
|
||||
<br/>
|
||||
<div style="margin: 5px 0 0;">
|
||||
<span style="color: #AAA; font-size: 12px; margin-left: 2px;">
|
||||
{{.Props.Date}}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2>
|
||||
<a class="post_btn" href="{{.Props.PostLink}}" style="font-size: 13px; background: #2389D7; display: inline-block; border-radius: 2px; color: #fff; padding: 6px 0; width: 120px; text-decoration: none; float:left; text-align: center; margin: 15px 0 5px;">
|
||||
{{.Props.Button}}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
{{define "post_body_full"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.BodyText}}</h2>
|
||||
<p>{{.Props.Info1}}<br>{{.Props.Info2}}<br><pre style="text-align:left;font-family: 'Lato', sans-serif; white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word;">{{.Props.PostMessage}}</pre></p>
|
||||
<p style="margin: 20px 0 15px">
|
||||
<a href="{{.Props.TeamLink}}" style="background: #2389D7; display: inline-block; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 170px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">{{.Props.Button}}</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
{{define "post_body_generic"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.BodyText}}</h2>
|
||||
<p>{{.Props.Info}}</p>
|
||||
<p style="margin: 20px 0 15px">
|
||||
<a href="{{.Props.TeamLink}}" style="background: #2389D7; display: inline-block; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 170px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">{{.Props.Button}}</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
|
@ -105,7 +105,7 @@
|
|||
font-weight: 600 !important;
|
||||
font-size: 28px !important;
|
||||
line-height: 36px !important;
|
||||
letter-spacing: -0.02em !important;
|
||||
letter-spacing: -0.01em !important;
|
||||
color: #3D3C40 !important;
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +124,17 @@
|
|||
padding: 15px 24px !important;
|
||||
}
|
||||
|
||||
.messageButton a {
|
||||
background-color: #FFFFFF !important;
|
||||
border: 1px solid #FFFFFF !important;
|
||||
box-sizing: border-box !important;
|
||||
color: #166DE0 !important;
|
||||
padding: 12px 20px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 14px !important;
|
||||
}
|
||||
|
||||
.info div {
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
|
|
@ -192,13 +203,52 @@
|
|||
width: 32px !important;
|
||||
}
|
||||
|
||||
.senderName div {
|
||||
.postNameAndTime {
|
||||
padding: 0px 0px 4px 0px !important;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.senderName {
|
||||
font-family: Open Sans, sans-serif;
|
||||
text-align: left !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
color: #3D3C40 !important;
|
||||
padding: 0px 0px 4px 0px !important;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-family: Open Sans, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: rgba(61, 60, 64, 0.56);
|
||||
padding: 2px 6px;
|
||||
align-items: center;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.channelBg {
|
||||
background: rgba(61, 60, 64, 0.08);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.channelLogo {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
padding: 5px 4px 5px 6px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
font-family: Open Sans, sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.01em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(61, 60, 64, 0.64);
|
||||
padding: 2px 6px 2px 0px;
|
||||
}
|
||||
|
||||
.senderMessage div {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@
|
|||
font-weight: 600 !important;
|
||||
font-size: 28px !important;
|
||||
line-height: 36px !important;
|
||||
letter-spacing: -0.02em !important;
|
||||
letter-spacing: -0.01em !important;
|
||||
color: #3D3C40 !important;
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +124,17 @@
|
|||
padding: 15px 24px !important;
|
||||
}
|
||||
|
||||
.messageButton a {
|
||||
background-color: #FFFFFF !important;
|
||||
border: 1px solid #FFFFFF !important;
|
||||
box-sizing: border-box !important;
|
||||
color: #166DE0 !important;
|
||||
padding: 12px 20px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 14px !important;
|
||||
}
|
||||
|
||||
.info div {
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
|
|
@ -192,13 +203,52 @@
|
|||
width: 32px !important;
|
||||
}
|
||||
|
||||
.senderName div {
|
||||
.postNameAndTime {
|
||||
padding: 0px 0px 4px 0px !important;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.senderName {
|
||||
font-family: Open Sans, sans-serif;
|
||||
text-align: left !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
color: #3D3C40 !important;
|
||||
padding: 0px 0px 4px 0px !important;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-family: Open Sans, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: rgba(61, 60, 64, 0.56);
|
||||
padding: 2px 6px;
|
||||
align-items: center;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.channelBg {
|
||||
background: rgba(61, 60, 64, 0.08);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.channelLogo {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
padding: 5px 4px 5px 6px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
font-family: Open Sans, sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.01em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(61, 60, 64, 0.64);
|
||||
padding: 2px 6px 2px 0px;
|
||||
}
|
||||
|
||||
.senderMessage div {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@
|
|||
font-weight: 600 !important;
|
||||
font-size: 28px !important;
|
||||
line-height: 36px !important;
|
||||
letter-spacing: -0.02em !important;
|
||||
letter-spacing: -0.01em !important;
|
||||
color: #3D3C40 !important;
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +124,17 @@
|
|||
padding: 15px 24px !important;
|
||||
}
|
||||
|
||||
.messageButton a {
|
||||
background-color: #FFFFFF !important;
|
||||
border: 1px solid #FFFFFF !important;
|
||||
box-sizing: border-box !important;
|
||||
color: #166DE0 !important;
|
||||
padding: 12px 20px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 14px !important;
|
||||
}
|
||||
|
||||
.info div {
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
|
|
@ -192,13 +203,52 @@
|
|||
width: 32px !important;
|
||||
}
|
||||
|
||||
.senderName div {
|
||||
.postNameAndTime {
|
||||
padding: 0px 0px 4px 0px !important;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.senderName {
|
||||
font-family: Open Sans, sans-serif;
|
||||
text-align: left !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
color: #3D3C40 !important;
|
||||
padding: 0px 0px 4px 0px !important;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-family: Open Sans, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: rgba(61, 60, 64, 0.56);
|
||||
padding: 2px 6px;
|
||||
align-items: center;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.channelBg {
|
||||
background: rgba(61, 60, 64, 0.08);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.channelLogo {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
padding: 5px 4px 5px 6px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
font-family: Open Sans, sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.01em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(61, 60, 64, 0.64);
|
||||
padding: 2px 6px 2px 0px;
|
||||
}
|
||||
|
||||
.senderMessage div {
|
||||
|
|
|
|||
Loading…
Reference in a new issue