Files
opencloud/services/notifications/pkg/email/email.go
Roman Perekhod 04a5ee283e fix default language fallback (#7479)
* fix default language fallback

* Update services/userlog/pkg/config/config.go

Co-authored-by: Martin <github@diemattels.at>

* Update services/notifications/pkg/config/config.go

Co-authored-by: Martin <github@diemattels.at>

* readme updated. local env vars removed

* Update changelog/unreleased/fix-default-mail-language-fallback.md

Co-authored-by: Martin <github@diemattels.at>

* update readme's and envvar texts

* fix changelog text

---------

Co-authored-by: Roman Perekhod <rperekhod@owncloud.com>
Co-authored-by: Martin <github@diemattels.at>
2023-10-17 09:56:48 +02:00

152 lines
3.7 KiB
Go

// Package email implements utility for rendering the Email.
//
// The email package supports transifex translation for email templates.
package email
import (
"bytes"
"embed"
"errors"
"html"
"html/template"
"io/fs"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/owncloud/ocis/v2/services/notifications/pkg/channels"
)
var (
//go:embed templates
templatesFS embed.FS
imgDir = filepath.Join("templates", "html", "img")
)
// RenderEmailTemplate renders the email template for a new share
func RenderEmailTemplate(mt MessageTemplate, locale, defaultLocale string, emailTemplatePath string, translationPath string, vars map[string]string) (*channels.Message, error) {
textMt, err := NewTextTemplate(mt, locale, defaultLocale, translationPath, vars)
if err != nil {
return nil, err
}
tpl, err := parseTemplate(emailTemplatePath, mt.textTemplate)
if err != nil {
return nil, err
}
textBody, err := emailTemplate(tpl, textMt)
if err != nil {
return nil, err
}
htmlMt, err := NewHTMLTemplate(mt, locale, defaultLocale, translationPath, escapeStringMap(vars))
if err != nil {
return nil, err
}
htmlTpl, err := parseTemplate(emailTemplatePath, mt.htmlTemplate)
if err != nil {
return nil, err
}
htmlBody, err := emailTemplate(htmlTpl, htmlMt)
if err != nil {
return nil, err
}
var data map[string][]byte
if emailTemplatePath != "" {
data, err = readImages(emailTemplatePath)
if err != nil {
return nil, err
}
}
return &channels.Message{
Subject: textMt.Subject,
TextBody: textBody,
HTMLBody: htmlBody,
AttachInline: data,
}, nil
}
func emailTemplate(tpl *template.Template, mt MessageTemplate) (string, error) {
str, err := executeTemplate(tpl, map[string]interface{}{
"Greeting": template.HTML(strings.TrimSpace(mt.Greeting)),
"MessageBody": template.HTML(strings.TrimSpace(mt.MessageBody)),
"CallToAction": template.HTML(strings.TrimSpace(mt.CallToAction)),
})
if err != nil {
return "", err
}
return str, err
}
func parseTemplate(emailTemplatePath string, file string) (*template.Template, error) {
if emailTemplatePath != "" {
return template.ParseFiles(filepath.Join(emailTemplatePath, file))
}
return template.ParseFS(templatesFS, filepath.Join(file))
}
func executeTemplate(tpl *template.Template, vars any) (string, error) {
var writer bytes.Buffer
if err := tpl.Execute(&writer, vars); err != nil {
return "", err
}
return writer.String(), nil
}
func readImages(emailTemplatePath string) (map[string][]byte, error) {
dir := filepath.Join(emailTemplatePath, imgDir)
entries, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
return read(entries, os.DirFS(emailTemplatePath))
}
func read(entries []fs.DirEntry, fsys fs.FS) (map[string][]byte, error) {
list := make(map[string][]byte)
for _, e := range entries {
if e.IsDir() {
continue
}
file, err := fs.ReadFile(fsys, filepath.Join(imgDir, e.Name()))
if err != nil {
// skip if the symbolic is a directory
if errors.Is(err, syscall.EISDIR) {
continue
}
return nil, err
}
if !validateMime(file) {
continue
}
list[e.Name()] = file
}
return list, nil
}
// signature image formats signature https://go.dev/src/net/http/sniff.go #L:118
var signature = map[string]string{
"\xff\xd8\xff": "image/jpeg",
"\x89PNG\r\n\x1a\n": "image/png",
"GIF87a": "image/gif",
"GIF89a": "image/gif",
}
// validateMime validate the mime type of image file from its first few bytes
func validateMime(incipit []byte) bool {
for s := range signature {
if strings.HasPrefix(string(incipit), s) {
return true
}
}
return false
}
func escapeStringMap(vars map[string]string) map[string]string {
for k := range vars {
vars[k] = html.EscapeString(vars[k])
}
return vars
}