fix(notifications): rendering of plaintext mails
This commit is contained in:
parent
191a476823
commit
5892622676
@ -31,7 +31,7 @@ type Mail struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mailLine struct {
|
type mailLine struct {
|
||||||
text string
|
Text string
|
||||||
isHTML bool
|
isHTML bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ func (m *Mail) Action(text, url string) *Mail {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// Line adds a line of text to the mail
|
// Line adds a line of Text to the mail
|
||||||
func (m *Mail) Line(line string) *Mail {
|
func (m *Mail) Line(line string) *Mail {
|
||||||
return m.appendLine(line, false)
|
return m.appendLine(line, false)
|
||||||
}
|
}
|
||||||
@ -83,14 +83,14 @@ func (m *Mail) HTML(line string) *Mail {
|
|||||||
func (m *Mail) appendLine(line string, isHTML bool) *Mail {
|
func (m *Mail) appendLine(line string, isHTML bool) *Mail {
|
||||||
if m.actionURL == "" {
|
if m.actionURL == "" {
|
||||||
m.introLines = append(m.introLines, &mailLine{
|
m.introLines = append(m.introLines, &mailLine{
|
||||||
text: line,
|
Text: line,
|
||||||
isHTML: isHTML,
|
isHTML: isHTML,
|
||||||
})
|
})
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
m.outroLines = append(m.outroLines, &mailLine{
|
m.outroLines = append(m.outroLines, &mailLine{
|
||||||
text: line,
|
Text: line,
|
||||||
isHTML: isHTML,
|
isHTML: isHTML,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -35,12 +35,12 @@ import (
|
|||||||
const mailTemplatePlain = `
|
const mailTemplatePlain = `
|
||||||
{{ .Greeting }}
|
{{ .Greeting }}
|
||||||
{{ range $line := .IntroLines}}
|
{{ range $line := .IntroLines}}
|
||||||
{{ $line }}
|
{{ $line.Text }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if .ActionURL }}{{ .ActionText }}:
|
{{ if .ActionURL }}{{ .ActionText }}:
|
||||||
{{ .ActionURL }}{{end}}
|
{{ .ActionURL }}{{end}}
|
||||||
{{ range $line := .OutroLines}}
|
{{ range $line := .OutroLines}}
|
||||||
{{ $line }}
|
{{ $line.Text }}
|
||||||
{{ end }}`
|
{{ end }}`
|
||||||
|
|
||||||
const mailTemplateHTML = `
|
const mailTemplateHTML = `
|
||||||
@ -50,9 +50,9 @@ const mailTemplateHTML = `
|
|||||||
<meta name="viewport" content="width: display-width;">
|
<meta name="viewport" content="width: display-width;">
|
||||||
</head>
|
</head>
|
||||||
<body style="width: 100%; padding: 0; margin: 0; background: #f3f4f6">
|
<body style="width: 100%; padding: 0; margin: 0; background: #f3f4f6">
|
||||||
<div style="width: 100%; font-family: 'Open Sans', sans-serif; text-rendering: optimizeLegibility">
|
<div style="width: 100%; font-family: 'Open Sans', sans-serif; Text-rendering: optimizeLegibility">
|
||||||
<div style="width: 600px; margin: 0 auto; text-align: justify;">
|
<div style="width: 600px; margin: 0 auto; Text-align: justify;">
|
||||||
<h1 style="font-size: 30px; text-align: center;">
|
<h1 style="font-size: 30px; Text-align: center;">
|
||||||
<img src="cid:logo.png" style="height: 75px;" alt="Vikunja"/>
|
<img src="cid:logo.png" style="height: 75px;" alt="Vikunja"/>
|
||||||
</h1>
|
</h1>
|
||||||
<div style="border: 1px solid #dbdbdb; -webkit-box-shadow: 0.3em 0.3em 0.8em #e6e6e6; box-shadow: 0.3em 0.3em 0.8em #e6e6e6; color: #4a4a4a; padding: 5px 25px; border-radius: 3px; background: #fff;">
|
<div style="border: 1px solid #dbdbdb; -webkit-box-shadow: 0.3em 0.3em 0.8em #e6e6e6; box-shadow: 0.3em 0.3em 0.8em #e6e6e6; color: #4a4a4a; padding: 5px 25px; border-radius: 3px; background: #fff;">
|
||||||
@ -66,7 +66,7 @@ const mailTemplateHTML = `
|
|||||||
|
|
||||||
{{ if .ActionURL }}
|
{{ if .ActionURL }}
|
||||||
<a href="{{ .ActionURL }}" title="{{ .ActionText }}"
|
<a href="{{ .ActionURL }}" title="{{ .ActionText }}"
|
||||||
style="position: relative;text-decoration:none;display: block;border-radius: 4px;cursor: pointer;padding-bottom: 8px;padding-left: 14px;padding-right: 14px;padding-top: 8px;width:280px;margin:10px auto;text-align: center;white-space: nowrap;border: 0;text-transform: uppercase;font-size: 14px;font-weight: 700;-webkit-box-shadow: 0 3px 6px rgba(107,114,128,.12),0 2px 4px rgba(107,114,128,.1);box-shadow: 0 3px 6px rgba(107,114,128,.12),0 2px 4px rgba(107,114,128,.1);background-color: #1973ff;border-color: transparent;color: #fff;">
|
style="position: relative;Text-decoration:none;display: block;border-radius: 4px;cursor: pointer;padding-bottom: 8px;padding-left: 14px;padding-right: 14px;padding-top: 8px;width:280px;margin:10px auto;Text-align: center;white-space: nowrap;border: 0;Text-transform: uppercase;font-size: 14px;font-weight: 700;-webkit-box-shadow: 0 3px 6px rgba(107,114,128,.12),0 2px 4px rgba(107,114,128,.1);box-shadow: 0 3px 6px rgba(107,114,128,.12),0 2px 4px rgba(107,114,128,.1);background-color: #1973ff;border-color: transparent;color: #fff;">
|
||||||
{{ .ActionText }}
|
{{ .ActionText }}
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
@ -125,11 +125,11 @@ func RenderMail(m *Mail) (mailOpts *mail.Opts, err error) {
|
|||||||
for _, line := range m.introLines {
|
for _, line := range m.introLines {
|
||||||
if line.isHTML {
|
if line.isHTML {
|
||||||
// #nosec G203 -- the html is sanitized
|
// #nosec G203 -- the html is sanitized
|
||||||
introLinesHTML = append(introLinesHTML, templatehtml.HTML(p.Sanitize(line.text)))
|
introLinesHTML = append(introLinesHTML, templatehtml.HTML(p.Sanitize(line.Text)))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
md := []byte(templatehtml.HTMLEscapeString(line.text))
|
md := []byte(templatehtml.HTMLEscapeString(line.Text))
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err = goldmark.Convert(md, &buf)
|
err = goldmark.Convert(md, &buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -144,11 +144,11 @@ func RenderMail(m *Mail) (mailOpts *mail.Opts, err error) {
|
|||||||
for _, line := range m.outroLines {
|
for _, line := range m.outroLines {
|
||||||
if line.isHTML {
|
if line.isHTML {
|
||||||
// #nosec G203 -- the html is sanitized
|
// #nosec G203 -- the html is sanitized
|
||||||
outroLinesHTML = append(outroLinesHTML, templatehtml.HTML(p.Sanitize(line.text)))
|
outroLinesHTML = append(outroLinesHTML, templatehtml.HTML(p.Sanitize(line.Text)))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
md := []byte(templatehtml.HTMLEscapeString(line.text))
|
md := []byte(templatehtml.HTMLEscapeString(line.Text))
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err = goldmark.Convert(md, &buf)
|
err = goldmark.Convert(md, &buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -41,11 +41,15 @@ func TestNewMail(t *testing.T) {
|
|||||||
assert.Equal(t, "Testmail", mail.subject)
|
assert.Equal(t, "Testmail", mail.subject)
|
||||||
assert.Equal(t, "Hi there,", mail.greeting)
|
assert.Equal(t, "Hi there,", mail.greeting)
|
||||||
assert.Len(t, mail.introLines, 2)
|
assert.Len(t, mail.introLines, 2)
|
||||||
assert.Equal(t, "This is a line", mail.introLines[0])
|
assert.Equal(t, "This is a line", mail.introLines[0].Text)
|
||||||
assert.Equal(t, "And another one", mail.introLines[1])
|
assert.False(t, mail.introLines[0].isHTML)
|
||||||
|
assert.Equal(t, "And another one", mail.introLines[1].Text)
|
||||||
|
assert.False(t, mail.introLines[1].isHTML)
|
||||||
assert.Len(t, mail.outroLines, 2)
|
assert.Len(t, mail.outroLines, 2)
|
||||||
assert.Equal(t, "This should be an outro line", mail.outroLines[0])
|
assert.Equal(t, "This should be an outro line", mail.outroLines[0].Text)
|
||||||
assert.Equal(t, "And one more, because why not?", mail.outroLines[1])
|
assert.False(t, mail.outroLines[0].isHTML)
|
||||||
|
assert.Equal(t, "And one more, because why not?", mail.outroLines[1].Text)
|
||||||
|
assert.False(t, mail.outroLines[1].isHTML)
|
||||||
})
|
})
|
||||||
t.Run("No greeting", func(t *testing.T) {
|
t.Run("No greeting", func(t *testing.T) {
|
||||||
mail := NewMail().
|
mail := NewMail().
|
||||||
@ -60,8 +64,8 @@ func TestNewMail(t *testing.T) {
|
|||||||
assert.Equal(t, "Testmail", mail.subject)
|
assert.Equal(t, "Testmail", mail.subject)
|
||||||
assert.Equal(t, "", mail.greeting)
|
assert.Equal(t, "", mail.greeting)
|
||||||
assert.Len(t, mail.introLines, 2)
|
assert.Len(t, mail.introLines, 2)
|
||||||
assert.Equal(t, "This is a line", mail.introLines[0])
|
assert.Equal(t, "This is a line", mail.introLines[0].Text)
|
||||||
assert.Equal(t, "And another one", mail.introLines[1])
|
assert.Equal(t, "And another one", mail.introLines[1].Text)
|
||||||
})
|
})
|
||||||
t.Run("No action", func(t *testing.T) {
|
t.Run("No action", func(t *testing.T) {
|
||||||
mail := NewMail().
|
mail := NewMail().
|
||||||
@ -77,10 +81,10 @@ func TestNewMail(t *testing.T) {
|
|||||||
assert.Equal(t, "test@otherdomain.com", mail.to)
|
assert.Equal(t, "test@otherdomain.com", mail.to)
|
||||||
assert.Equal(t, "Testmail", mail.subject)
|
assert.Equal(t, "Testmail", mail.subject)
|
||||||
assert.Len(t, mail.introLines, 4)
|
assert.Len(t, mail.introLines, 4)
|
||||||
assert.Equal(t, "This is a line", mail.introLines[0])
|
assert.Equal(t, "This is a line", mail.introLines[0].Text)
|
||||||
assert.Equal(t, "And another one", mail.introLines[1])
|
assert.Equal(t, "And another one", mail.introLines[1].Text)
|
||||||
assert.Equal(t, "This should be an outro line", mail.introLines[2])
|
assert.Equal(t, "This should be an outro line", mail.introLines[2].Text)
|
||||||
assert.Equal(t, "And one more, because why not?", mail.introLines[3])
|
assert.Equal(t, "And one more, because why not?", mail.introLines[3].Text)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,9 +129,9 @@ And one more, because why not?
|
|||||||
<meta name="viewport" content="width: display-width;">
|
<meta name="viewport" content="width: display-width;">
|
||||||
</head>
|
</head>
|
||||||
<body style="width: 100%; padding: 0; margin: 0; background: #f3f4f6">
|
<body style="width: 100%; padding: 0; margin: 0; background: #f3f4f6">
|
||||||
<div style="width: 100%; font-family: 'Open Sans', sans-serif; text-rendering: optimizeLegibility">
|
<div style="width: 100%; font-family: 'Open Sans', sans-serif; Text-rendering: optimizeLegibility">
|
||||||
<div style="width: 600px; margin: 0 auto; text-align: justify;">
|
<div style="width: 600px; margin: 0 auto; Text-align: justify;">
|
||||||
<h1 style="font-size: 30px; text-align: center;">
|
<h1 style="font-size: 30px; Text-align: center;">
|
||||||
<img src="cid:logo.png" style="height: 75px;" alt="Vikunja"/>
|
<img src="cid:logo.png" style="height: 75px;" alt="Vikunja"/>
|
||||||
</h1>
|
</h1>
|
||||||
<div style="border: 1px solid #dbdbdb; -webkit-box-shadow: 0.3em 0.3em 0.8em #e6e6e6; box-shadow: 0.3em 0.3em 0.8em #e6e6e6; color: #4a4a4a; padding: 5px 25px; border-radius: 3px; background: #fff;">
|
<div style="border: 1px solid #dbdbdb; -webkit-box-shadow: 0.3em 0.3em 0.8em #e6e6e6; box-shadow: 0.3em 0.3em 0.8em #e6e6e6; color: #4a4a4a; padding: 5px 25px; border-radius: 3px; background: #fff;">
|
||||||
@ -139,7 +143,7 @@ And one more, because why not?
|
|||||||
<p>This is a line</p>
|
<p>This is a line</p>
|
||||||
|
|
||||||
|
|
||||||
<p>This <strong>line</strong> contains <a href="https://vikunja.io">a link</a></p>
|
<p>This <strong>line</strong> contains <a href="https://vikunja.io" rel="nofollow">a link</a></p>
|
||||||
|
|
||||||
|
|
||||||
<p>And another one</p>
|
<p>And another one</p>
|
||||||
@ -148,7 +152,7 @@ And one more, because why not?
|
|||||||
|
|
||||||
|
|
||||||
<a href="https://example.com" title="The action"
|
<a href="https://example.com" title="The action"
|
||||||
style="position: relative;text-decoration:none;display: block;border-radius: 4px;cursor: pointer;padding-bottom: 8px;padding-left: 14px;padding-right: 14px;padding-top: 8px;width:280px;margin:10px auto;text-align: center;white-space: nowrap;border: 0;text-transform: uppercase;font-size: 14px;font-weight: 700;-webkit-box-shadow: 0 3px 6px rgba(107,114,128,.12),0 2px 4px rgba(107,114,128,.1);box-shadow: 0 3px 6px rgba(107,114,128,.12),0 2px 4px rgba(107,114,128,.1);background-color: #1973ff;border-color: transparent;color: #fff;">
|
style="position: relative;Text-decoration:none;display: block;border-radius: 4px;cursor: pointer;padding-bottom: 8px;padding-left: 14px;padding-right: 14px;padding-top: 8px;width:280px;margin:10px auto;Text-align: center;white-space: nowrap;border: 0;Text-transform: uppercase;font-size: 14px;font-weight: 700;-webkit-box-shadow: 0 3px 6px rgba(107,114,128,.12),0 2px 4px rgba(107,114,128,.1);box-shadow: 0 3px 6px rgba(107,114,128,.12),0 2px 4px rgba(107,114,128,.1);background-color: #1973ff;border-color: transparent;color: #fff;">
|
||||||
The action
|
The action
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user