Added mail template for Welcome emails

This commit is contained in:
Mia Rose Winter 2024-02-14 12:22:35 +01:00
parent 52edae5470
commit fd43ead960
Signed by: miawinter
GPG key ID: 4B6F6A83178F595E
4 changed files with 106 additions and 17 deletions

View file

@ -7,7 +7,6 @@
@using System.Net @using System.Net
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using Wave.Services @using Wave.Services
@using Wave.Utilities
@inject ILogger<EmailSignup> Logger @inject ILogger<EmailSignup> Logger
@inject IStringLocalizer<EmailSignup> Localizer @inject IStringLocalizer<EmailSignup> Localizer
@ -82,27 +81,15 @@
Message = Localizer["Success_Message"]; Message = Localizer["Success_Message"];
await TemplateService.ValidateTokensAsync(Id, Token, deleteToken: true); await TemplateService.ValidateTokensAsync(Id, Token, deleteToken: true);
var articles = await context.Set<EmailNewsletter>() var articles = await context.Set<EmailNewsletter>()
.IgnoreAutoIncludes() .IgnoreAutoIncludes()
.Include(a => a.Article).ThenInclude(a => a.Author) .Include(a => a.Article).ThenInclude(a => a.Author)
.OrderByDescending(a => a.DistributionDateTime) .OrderByDescending(a => a.DistributionDateTime)
.Take(3) .Take(3)
.ToListAsync(); .ToListAsync();
string body = $"<p>{Localizer["WelcomeEmailBody"]}</p>\n"; await EmailSender.SendWelcomeMailAsync(subscriber,
Localizer["WelcomeEmailSubject"], Localizer["WelcomeEmailTitle"], Localizer["WelcomeEmailBody"], articles);
foreach (var n in articles) {
string articleLink = ArticleUtilities.GenerateArticleLink(n.Article, new Uri(Customizations.Value.AppUrl, UriKind.Absolute));
body = body +
"<div style=\"padding: 10px; background: #333; color: #fff; margin-bottom: 10px\">" +
$"<h3>{n.Article.Title}</h3>" +
$"<small>{n.Article.Author.Name}</small>" +
$"<p>{n.Article.Body[..Math.Min(100, n.Article.Body.Length)]}...</p>" +
$"<a href=\"{articleLink}\">Link</a>" +
"</div>";
}
await EmailSender.SendSubscribedMailAsync(subscriber,
Localizer["WelcomeEmailSubject"], Localizer["WelcomeEmailTitle"], body, subscribedRole: "welcome");
} catch (Exception ex) { } catch (Exception ex) {
Logger.LogError(ex, "Error trying to confirm subscriber."); Logger.LogError(ex, "Error trying to confirm subscriber.");
Message = Localizer["Failure_Message"]; Message = Localizer["Failure_Message"];

View file

@ -6,7 +6,7 @@ namespace Wave.Services;
public partial class EmailTemplateService(ILogger<EmailTemplateService> logger, IDistributedCache tokenCache, FileSystemService fileSystem) { public partial class EmailTemplateService(ILogger<EmailTemplateService> logger, IDistributedCache tokenCache, FileSystemService fileSystem) {
public enum Constants { public enum Constants {
BrowserLink, HomeLink, ContentLogo, ContentTitle, ContentBody, EmailUnsubscribeLink BrowserLink, HomeLink, ContentLogo, ContentTitle, ContentBody, EmailUnsubscribeLink, ArticleRecommendations
} }
private ILogger<EmailTemplateService> Logger { get; } = logger; private ILogger<EmailTemplateService> Logger { get; } = logger;
@ -62,9 +62,21 @@ public enum Constants {
}); });
} }
public string Welcome(string home, string logoLink, string title, string body, string unsubscribe, string articles) {
return Process("welcome", new Dictionary<Constants, object?> {
{ Constants.HomeLink, home },
{ Constants.ContentLogo, logoLink },
{ Constants.ContentTitle, title },
{ Constants.ContentBody, body },
{ Constants.EmailUnsubscribeLink, unsubscribe },
{ Constants.ArticleRecommendations, articles }
});
}
public void TryCreateDefaultTemplates() { public void TryCreateDefaultTemplates() {
FileSystem.GetEmailTemplate("default", DefaultTemplates["default"]); FileSystem.GetEmailTemplate("default", DefaultTemplates["default"]);
FileSystem.GetEmailTemplate("newsletter", DefaultTemplates["newsletter"]); FileSystem.GetEmailTemplate("newsletter", DefaultTemplates["newsletter"]);
FileSystem.GetEmailTemplate("welcome", DefaultTemplates["welcome"]);
} }
public string ApplyTokens(string template, Func<string, string?> replacer) { public string ApplyTokens(string template, Func<string, string?> replacer) {
@ -189,6 +201,60 @@ public enum Constants {
</mj-body> </mj-body>
</mjml> </mjml>
""" """
},
{
"welcome",
$"""
<mjml>
<mj-head>
<mj-preview />
</mj-head>
<mj-body>
<mj-section direction="rtl" padding-bottom="5px" padding-left="0px" padding-right="0px" padding-top="15px" padding="15px 0px 5px 0px">
<mj-column vertical-align="middle" width="33%">
<mj-image align="center" alt="" border-radius="0" border="none" container-background-color="transparent" height="auto" padding-bottom="5px" padding-left="5px" padding-right="5px" padding-top="5px" padding="5px 5px 5px 5px" href="[[{Constants.HomeLink}]]" src="[[{Constants.ContentLogo}]]"></mj-image>
</mj-column>
<mj-column vertical-align="middle" width="67%">
<mj-text font-size="13px" font-family="Ubuntu,Verdana">
<h1>[[{Constants.ContentTitle}]]</h1>
</mj-text>
</mj-column>
</mj-section>
<mj-section padding-top="5px" padding-bottom="5px" padding="5px 0 5px 0">
<mj-column>
<mj-divider border-color="#9f9f9f" border-width="1px"></mj-divider>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-text color="#55575d" font-size="13px" font-family="Ubuntu,Verdana">[[{Constants.ContentBody}]]</mj-text>
</mj-column>
</mj-section>
<mj-section padding-top="5px" padding-bottom="5px" padding="5px 0 5px 0">
<mj-column>
<mj-divider border-color="#9f9f9f" border-width="1px"></mj-divider>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-text color="#55575d" font-size="13px" font-family="Ubuntu,Verdana">[[{Constants.ArticleRecommendations}]]</mj-text>
</mj-column>
</mj-section>
<mj-section padding-top="5px" padding-bottom="5px" padding="5px 0 5px 0">
<mj-column>
<mj-divider border-color="#9f9f9f" border-width="1px"></mj-divider>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-text align="center" font-size="13px" font-family="Ubuntu,Verdana">
<a href="[[{Constants.EmailUnsubscribeLink}]]">Unsubscribe</a>
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
"""
} }
}; };
} }

View file

@ -8,4 +8,6 @@ public interface IAdvancedEmailSender : IEmailSender {
Task SendDefaultMailAsync(string receiverMail, string? receiverName, string subject, string title, string bodyHtml); Task SendDefaultMailAsync(string receiverMail, string? receiverName, string subject, string title, string bodyHtml);
Task SendSubscribedMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml, Task SendSubscribedMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml,
string browserUrl = "", string subscribedRole = "-1"); string browserUrl = "", string subscribedRole = "-1");
Task SendWelcomeMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml,
IEnumerable<EmailNewsletter> articles);
} }

View file

@ -1,10 +1,12 @@
using System.Net; using System.Net;
using System.Text;
using MailKit.Net.Smtp; using MailKit.Net.Smtp;
using MailKit.Security; using MailKit.Security;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using MimeKit; using MimeKit;
using Wave.Data; using Wave.Data;
using Wave.Utilities;
using Uri = System.Uri; using Uri = System.Uri;
namespace Wave.Services; namespace Wave.Services;
@ -97,6 +99,38 @@ public Task SendEmailAsync(string email, string? name, string subject, string ht
new Header(HeaderId.ListUnsubscribe, $"<{unsubscribeLink}>"), new Header(HeaderId.ListUnsubscribe, $"<{unsubscribeLink}>"),
new Header(HeaderId.ListUnsubscribePost, "One-Click")); new Header(HeaderId.ListUnsubscribePost, "One-Click"));
} }
public async Task SendWelcomeMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml,
IEnumerable<EmailNewsletter> articles) {
(string user, string token) = await TemplateService
.CreateConfirmTokensAsync(subscriber.Id, "unsubscribe-welcome", TimeSpan.FromDays(30));
var host = new Uri(string.IsNullOrWhiteSpace(Customizations.AppUrl) ? "" : Customizations.AppUrl); // TODO get link
var articlesHtml = new StringBuilder("");
foreach (var n in articles) {
string articleLink = ArticleUtilities.GenerateArticleLink(n.Article, new Uri(Customizations.AppUrl, UriKind.Absolute));
articlesHtml.AppendFormat(
"""
<div style="padding: 10px; background: #333; color: #fff; margin-bottom: 10px">
<h3>{0}</h3>
<small>{1}</small>
<p>{2}</p>
<a href="{3}">Link</a>
</div>
""",
n.Article.Title, n.Article.Author.Name, n.Article.Body[..Math.Min(100, n.Article.Body.Length)], articleLink);
}
string logo = !string.IsNullOrWhiteSpace(Customizations.LogoLink)
? Customizations.LogoLink
: new Uri(host, "/img/logo.png").AbsoluteUri;
string unsubscribeLink = new Uri(host,
$"/Email/Unsubscribe?newsletter=welcome&user={WebUtility.UrlEncode(user)}&token={WebUtility.UrlEncode(token)}").AbsoluteUri;
string body = TemplateService.Welcome(host.AbsoluteUri, logo, title, bodyHtml, unsubscribeLink, articlesHtml.ToString());
await SendEmailAsync(subscriber.Email, subscriber.Name, subject, body,
new Header(HeaderId.ListUnsubscribe, $"<{unsubscribeLink}>"),
new Header(HeaderId.ListUnsubscribePost, "One-Click"));
}
} }
public class EmailNotSendException(string message, Exception exception) : ApplicationException(message, exception); public class EmailNotSendException(string message, Exception exception) : ApplicationException(message, exception);