Skip to content

Commit f97d823

Browse files
paskalumputun
authored andcommitted
Add subscription link support to notification email verification template (#573)
* add subscription link support to notification email verification template * always show token for email subscription in verification email * hide SubscribeURL from users
1 parent 091ac9b commit f97d823

File tree

3 files changed

+63
-9
lines changed

3 files changed

+63
-9
lines changed

backend/app/cmd/server.go

+2
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,8 @@ func (s *ServerCommand) makeNotify(dataStore *service.DataStore, authenticator *
766766
From: s.Notify.Email.From,
767767
VerificationSubject: s.Notify.Email.VerificationSubject,
768768
UnsubscribeURL: s.RemarkURL + "/email/unsubscribe.html",
769+
// TODO: uncomment after #560 frontend part is ready and URL is known
770+
//SubscribeURL: s.RemarkURL + "/subscribe.html?token=",
769771
TokenGenFn: func(userID, email, site string) (string, error) {
770772
claims := token.Claims{
771773
Handshake: &token.Handshake{ID: userID + "::" + email},

backend/app/notify/email.go

+15-8
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type EmailParams struct {
2323
MsgTemplate string // request message template
2424
VerificationSubject string // verification message subject
2525
VerificationTemplate string // verification message template
26+
SubscribeURL string // full subscribe handler URL
2627
UnsubscribeURL string // full unsubscribe handler URL
2728

2829
TokenGenFn func(userID, email, site string) (string, error) // Unsubscribe token generation function
@@ -91,10 +92,11 @@ type msgTmplData struct {
9192

9293
// verifyTmplData store data for verification message template execution
9394
type verifyTmplData struct {
94-
User string
95-
Token string
96-
Email string
97-
Site string
95+
User string
96+
Token string
97+
Email string
98+
Site string
99+
SubscribeURL string
98100
}
99101

100102
const (
@@ -178,6 +180,10 @@ const (
178180
<div style="text-align: center; font-family: Helvetica, Arial, sans-serif; font-size: 18px;">
179181
<h1 style="position: relative; color: #4fbbd6; margin-top: 0.2em;">Remark42</h1>
180182
<p style="position: relative; max-width: 20em; margin: 0 auto 1em auto; line-height: 1.4em; color:#000!important;">Confirmation for <b>{{.User}}</b> on site <b>{{.Site}}</b></p>
183+
{{if .SubscribeURL}}
184+
<p style="position: relative; margin: 0 0 0.5em 0;color:#000!important;"><a href="{{.SubscribeURL}}{{.Token}}">Click here to subscribe to email notifications</a></p>
185+
<p style="position: relative; margin: 0 0 0.5em 0;color:#000!important;">Alternatively, you can use code below for subscription.</p>
186+
{{ end }}
181187
<div style="background-color: #eee; max-width: 20em; margin: 0 auto; border-radius: 0.4em; padding: 0.5em;">
182188
<p style="position: relative; margin: 0 0 0.5em 0;color:#000!important;">TOKEN</p>
183189
<p style="position: relative; font-size: 0.7em; opacity: 0.8;"><i style="color:#000!important;">Copy and paste this text into “token” field on comments page</i></p>
@@ -273,10 +279,11 @@ func (e *Email) buildVerificationMessage(user, email, token, site string) (strin
273279
subject := e.VerificationSubject
274280
msg := bytes.Buffer{}
275281
err := e.verifyTmpl.Execute(&msg, verifyTmplData{
276-
User: user,
277-
Token: token,
278-
Email: email,
279-
Site: site,
282+
User: user,
283+
Token: token,
284+
Email: email,
285+
Site: site,
286+
SubscribeURL: e.SubscribeURL,
280287
})
281288
if err != nil {
282289
return "", errors.Wrapf(err, "error executing template to build verification message")

backend/app/notify/email_test.go

+46-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ func TestEmail_Send(t *testing.T) {
189189
req := Request{
190190
Comment: store.Comment{ID: "999", User: store.User{ID: "1", Name: "test_user"}, PostTitle: "test_title"},
191191
parent: store.Comment{ID: "1", User: store.User{ID: "999", Name: "parent_user"}},
192-
Email: "test@example.org"}
192+
Email: "test@example.org",
193+
}
193194
assert.NoError(t, email.Send(context.TODO(), req))
194195
assert.Equal(t, "from@example.org", fakeSmtp.readMail())
195196
assert.Equal(t, 1, fakeSmtp.readQuitCount())
@@ -208,6 +209,50 @@ List-Unsubscribe: <https://remark42.com/api/v1/email/unsubscribe?site=&tkn=token
208209
Date: `)
209210
}
210211

212+
func TestEmail_SendVerification(t *testing.T) {
213+
email, err := NewEmail(EmailParams{From: "from@example.org"}, SmtpParams{})
214+
assert.NoError(t, err)
215+
assert.NotNil(t, email)
216+
fakeSmtp := fakeTestSMTP{}
217+
email.smtp = &fakeSmtp
218+
email.TokenGenFn = TokenGenFn
219+
req := Request{
220+
Email: "test@example.org",
221+
Verification: VerificationMetadata{
222+
SiteID: "remark",
223+
User: "test_username",
224+
Token: "secret_",
225+
},
226+
}
227+
assert.NoError(t, email.Send(context.TODO(), req))
228+
assert.Equal(t, "from@example.org", fakeSmtp.readMail())
229+
assert.Equal(t, 1, fakeSmtp.readQuitCount())
230+
assert.Equal(t, "test@example.org", fakeSmtp.readRcpt())
231+
// test buildMessageFromRequest separately for message text
232+
res, err := email.buildVerificationMessage(req.Verification.User, req.Email, req.Verification.Token, req.Verification.SiteID)
233+
assert.NoError(t, err)
234+
assert.Contains(t, res, `From: from@example.org
235+
To: test@example.org
236+
Subject: Email verification
237+
Content-Transfer-Encoding: quoted-printable
238+
MIME-version: 1.0
239+
Content-Type: text/html; charset="UTF-8"
240+
Date: `)
241+
assert.Contains(t, res, `secret_`)
242+
assert.NotContains(t, res, `https://example.org/`)
243+
email.SubscribeURL = "https://example.org/subscribe.html?token="
244+
res, err = email.buildVerificationMessage(req.Verification.User, req.Email, req.Verification.Token, req.Verification.SiteID)
245+
assert.NoError(t, err)
246+
assert.Contains(t, res, `From: from@example.org
247+
To: test@example.org
248+
Subject: Email verification
249+
Content-Transfer-Encoding: quoted-printable
250+
MIME-version: 1.0
251+
Content-Type: text/html; charset="UTF-8"
252+
Date: `)
253+
assert.Contains(t, res, `https://example.org/subscribe.html?token=3Dsecret_`)
254+
}
255+
211256
func Test_emailClient_Create(t *testing.T) {
212257
creator := emailClient{}
213258
client, err := creator.Create(SmtpParams{})

0 commit comments

Comments
 (0)