Implemented plain text content in newsletter emails
This commit is contained in:
parent
22c6018e14
commit
c8b7f9f44f
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Frozen;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
@ -12,31 +13,36 @@ public class EmailFactory(IOptions<Customization> customizations, EmailTemplateS
|
|||
private Customization Customizations { get; } = customizations.Value;
|
||||
private EmailTemplateService TemplateService { get; } = templateService;
|
||||
|
||||
public async ValueTask<IEmail> CreateDefaultEmail(string receiverMail, string? receiverName, string subject, string title, string bodyHtml) {
|
||||
public async ValueTask<IEmail> CreateDefaultEmail(string receiverMail, string? receiverName, string subject, string title, string bodyHtml, string bodyPlain = "") {
|
||||
(string host, string logo) = GetStaticData();
|
||||
string body = await TemplateService.DefaultAsync(host, logo, title, bodyHtml);
|
||||
|
||||
return new StaticEmail(receiverMail, receiverName, subject, title, body, FrozenDictionary<string, string>.Empty);
|
||||
return new StaticEmail(receiverMail, receiverName, subject, title, body, $"{title}\n\n{bodyPlain}", FrozenDictionary<string, string>.Empty);
|
||||
}
|
||||
|
||||
public async ValueTask<IEmail> CreateSubscribedEmail(EmailSubscriber subscriber, string browserLink, string subject, string title, string bodyHtml, string role = "unknown", string? replyTo = null) {
|
||||
public async ValueTask<IEmail> CreateSubscribedEmail(EmailSubscriber subscriber, string browserLink, string subject, string title, string bodyHtml, string bodyPlain = "", string role = "unknown", string? replyTo = null) {
|
||||
(string host, string logo) = GetStaticData();
|
||||
|
||||
string unsubscribeLink = await GetUnsubscribeLink(host, subscriber.Id, role);
|
||||
string body = await TemplateService.NewsletterAsync(host, browserLink, logo, title, bodyHtml, unsubscribeLink);
|
||||
string footer = await TemplateService.GetPartialAsync("email-plain-footer");
|
||||
bodyPlain += "\n\n" + footer.Replace(
|
||||
$"[[{EmailTemplateService.Constants.EmailUnsubscribeLink}]]",
|
||||
unsubscribeLink, true, CultureInfo.InvariantCulture);
|
||||
|
||||
var headers = new Dictionary<string, string>{
|
||||
{HeaderId.ListUnsubscribe.ToHeaderName(), $"<{unsubscribeLink}>"},
|
||||
{HeaderId.ListUnsubscribePost.ToHeaderName(), "One-Click"}
|
||||
};
|
||||
if (!string.IsNullOrWhiteSpace(replyTo)) headers.Add(HeaderId.ReplyTo.ToHeaderName(), replyTo);
|
||||
return new StaticEmail(subscriber.Email, subscriber.Name, subject, title, body, headers.ToFrozenDictionary());
|
||||
return new StaticEmail(subscriber.Email, subscriber.Name, subject, title, body, $"{title}\n\n{bodyPlain}", headers.ToFrozenDictionary());
|
||||
}
|
||||
|
||||
public async ValueTask<IEmail> CreateWelcomeEmail(EmailSubscriber subscriber, IEnumerable<EmailNewsletter> articles, string subject, string title, string bodyHtml) {
|
||||
public async ValueTask<IEmail> CreateWelcomeEmail(EmailSubscriber subscriber, IEnumerable<EmailNewsletter> articles, string subject, string title, string bodyHtml, string bodyPlain = "") {
|
||||
(string host, string logo) = GetStaticData();
|
||||
|
||||
string articlePartial = await TemplateService.GetPartialAsync("email-article");
|
||||
string footer = await TemplateService.GetPartialAsync("email-plain-footer");
|
||||
var articlesHtml = new StringBuilder("");
|
||||
foreach (var n in articles) {
|
||||
string articleLink = ArticleUtilities.GenerateArticleLink(n.Article, new Uri(Customizations.AppUrl, UriKind.Absolute));
|
||||
|
@ -47,8 +53,12 @@ public class EmailFactory(IOptions<Customization> customizations, EmailTemplateS
|
|||
|
||||
string unsubscribeLink = await GetUnsubscribeLink(host, subscriber.Id, "welcome");
|
||||
string body = TemplateService.Welcome(host, logo, title, bodyHtml, unsubscribeLink, articlesHtml.ToString());
|
||||
bodyPlain += "\n" + HtmlUtilities.GetPlainText(articlesHtml.ToString());
|
||||
bodyPlain += "\n\n" + footer.Replace(
|
||||
$"[[{EmailTemplateService.Constants.EmailUnsubscribeLink}]]",
|
||||
unsubscribeLink, true, CultureInfo.InvariantCulture);
|
||||
|
||||
return new StaticEmail(subscriber.Email, subscriber.Name, subject, title, body, new Dictionary<string, string>{
|
||||
return new StaticEmail(subscriber.Email, subscriber.Name, subject, title, body, $"{title}\n\n{bodyPlain}", new Dictionary<string, string>{
|
||||
{HeaderId.ListUnsubscribe.ToHeaderName(), $"<{unsubscribeLink}>"},
|
||||
{HeaderId.ListUnsubscribePost.ToHeaderName(), "One-Click"}
|
||||
}.ToFrozenDictionary());
|
||||
|
|
|
@ -97,6 +97,7 @@ public enum Constants {
|
|||
FileSystem.GetEmailTemplate("newsletter", DefaultTemplates["newsletter"]);
|
||||
FileSystem.GetEmailTemplate("welcome", DefaultTemplates["welcome"]);
|
||||
FileSystem.GetPartialTemplate("email-article", DefaultPartials["email-article"]);
|
||||
FileSystem.GetPartialTemplate("email-plain-footer", DefaultPartials["email-plain-footer"]);
|
||||
}
|
||||
|
||||
public string ApplyTokens(string template, Func<string, string?> replacer) {
|
||||
|
@ -307,6 +308,10 @@ public enum Constants {
|
|||
<a href="{3}">Link</a>
|
||||
</div>
|
||||
"""
|
||||
},
|
||||
{
|
||||
"email-plain-footer",
|
||||
$"Unsubscribe: [[{Constants.EmailUnsubscribeLink}]]"
|
||||
}
|
||||
};
|
||||
}
|
|
@ -8,5 +8,6 @@ public interface IEmail {
|
|||
string Subject { get; }
|
||||
string Title { get; }
|
||||
string ContentHtml { get; }
|
||||
string ContentPlain { get; }
|
||||
FrozenDictionary<string, string> Headers { get; }
|
||||
}
|
|
@ -52,7 +52,10 @@ public class LiveEmailService(ILogger<LiveEmailService> logger, IOptions<EmailCo
|
|||
To = { new MailboxAddress(email.ReceiverName, email.ReceiverEmail) },
|
||||
Subject = email.Subject
|
||||
};
|
||||
var builder = new BodyBuilder { HtmlBody = email.ContentHtml };
|
||||
var builder = new BodyBuilder {
|
||||
HtmlBody = email.ContentHtml,
|
||||
TextBody = email.ContentPlain
|
||||
};
|
||||
message.Body = builder.ToMessageBody();
|
||||
foreach ((string id, string value) in email.Headers) {
|
||||
if (id == HeaderId.ListUnsubscribe.ToHeaderName()) {
|
||||
|
|
|
@ -55,8 +55,12 @@ public class NewsletterBackgroundService(ILogger<NewsletterBackgroundService> lo
|
|||
last = subscribers.Last();
|
||||
|
||||
foreach (var subscriber in subscribers) {
|
||||
var email = await factory.CreateSubscribedEmail(subscriber, articleLink, newsletter.Article.Title,
|
||||
newsletter.Article.Title, newsletter.Article.BodyHtml, newsletter.Id.ToString(), replyTo);
|
||||
var email = await factory.CreateSubscribedEmail(subscriber, articleLink,
|
||||
newsletter.Article.Title,
|
||||
newsletter.Article.Title,
|
||||
newsletter.Article.BodyHtml,
|
||||
newsletter.Article.BodyPlain,
|
||||
"newsletter-" + newsletter.Id, replyTo);
|
||||
await client.SendEmailAsync(email);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Azure.Core;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Wave.Data;
|
||||
using Wave.Utilities;
|
||||
|
||||
namespace Wave.Services;
|
||||
|
||||
|
@ -11,16 +13,19 @@ public class SmtpEmailSender(EmailFactory email, [FromKeyedServices("live")]IEma
|
|||
#region IEmailSenderAsync<ApplicationUser>
|
||||
|
||||
public Task SendConfirmationLinkAsync(ApplicationUser user, string email, string confirmationLink) =>
|
||||
SendEmailAsync(email, user.FullName, "Confirm your email",
|
||||
$"Please confirm your account by <a href='{confirmationLink}'>clicking here</a>.");
|
||||
SendDefaultMailAsync(email, user.FullName, "Confirm your email", "Confirm your email",
|
||||
$"<p>Please confirm your account by <a href='{confirmationLink}'>clicking here</a>.</p>",
|
||||
$"Please confirm your account by clicking here: {confirmationLink}");
|
||||
|
||||
public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, string resetLink) =>
|
||||
SendEmailAsync(email, user.FullName, "Reset your password",
|
||||
$"Please reset your password by <a href='{resetLink}'>clicking here</a>.");
|
||||
SendDefaultMailAsync(email, user.FullName, "Reset your password", "Reset your password",
|
||||
$"<p>Please reset your password by <a href='{resetLink}'>clicking here</a>.</p>",
|
||||
$"Please reset your password by clicking here: {resetLink}");
|
||||
|
||||
public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, string resetCode) =>
|
||||
SendEmailAsync(email, user.FullName, "Reset your password",
|
||||
$"Please reset your password using the following code: {resetCode}");
|
||||
SendDefaultMailAsync(email, user.FullName, "Reset your password", "Reset your password",
|
||||
$"<p>Please reset your password using the following code: {resetCode}.</p>",
|
||||
$"Please reset your password using the following code: {resetCode}.");
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -35,27 +40,33 @@ public class SmtpEmailSender(EmailFactory email, [FromKeyedServices("live")]IEma
|
|||
|
||||
public async Task SendEmailAsync(string email, string? name, string subject, string htmlMessage) {
|
||||
await EmailService.ConnectAsync(CancellationToken.None);
|
||||
await EmailService.SendEmailAsync(await Email.CreateDefaultEmail(email, name, subject, subject, htmlMessage));
|
||||
await EmailService.SendEmailAsync(await Email.CreateDefaultEmail(email, name, subject, subject, htmlMessage, HtmlUtilities.GetPlainText(htmlMessage)));
|
||||
await EmailService.DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
public async Task SendDefaultMailAsync(string receiverMail, string? receiverName, string subject, string title, string bodyHtml) {
|
||||
await EmailService.ConnectAsync(CancellationToken.None);
|
||||
var email = await Email.CreateDefaultEmail(receiverMail, receiverName, subject, title, bodyHtml);
|
||||
var email = await Email.CreateDefaultEmail(receiverMail, receiverName, subject, title, bodyHtml, HtmlUtilities.GetPlainText(bodyHtml));
|
||||
await EmailService.SendEmailAsync(email);
|
||||
await EmailService.DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
public async Task SendDefaultMailAsync(string receiverMail, string? receiverName, string subject, string title, string bodyHtml, string bodyPlain) {
|
||||
await EmailService.ConnectAsync(CancellationToken.None);
|
||||
var email = await Email.CreateDefaultEmail(receiverMail, receiverName, subject, title, bodyHtml, bodyPlain);
|
||||
await EmailService.SendEmailAsync(email);
|
||||
await EmailService.DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
public async Task SendSubscribedMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml,
|
||||
string browserUrl = "", string subscribedRole = "-1") {
|
||||
var email = await Email.CreateSubscribedEmail(subscriber, browserUrl, subject, title, bodyHtml, subscribedRole);
|
||||
var email = await Email.CreateSubscribedEmail(subscriber, browserUrl, subject, title, bodyHtml, HtmlUtilities.GetPlainText(bodyHtml), subscribedRole);
|
||||
await BulkEmailService.ConnectAsync(CancellationToken.None);
|
||||
await BulkEmailService.SendEmailAsync(email);
|
||||
}
|
||||
|
||||
public async Task SendWelcomeMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml,
|
||||
IEnumerable<EmailNewsletter> articles) {
|
||||
var email = await Email.CreateWelcomeEmail(subscriber, articles, subject, title, bodyHtml);
|
||||
var email = await Email.CreateWelcomeEmail(subscriber, articles, subject, title, bodyHtml, HtmlUtilities.GetPlainText(bodyHtml));
|
||||
await BulkEmailService.ConnectAsync(CancellationToken.None);
|
||||
await BulkEmailService.SendEmailAsync(email);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
using System.Collections.Frozen;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Mjml.Net.Helpers;
|
||||
|
||||
namespace Wave.Services;
|
||||
|
||||
public record StaticEmail(string ReceiverEmail, string? ReceiverName, string Subject, string Title, string ContentHtml,
|
||||
FrozenDictionary<string, string> Headers) : IEmail;
|
||||
public record StaticEmail(string ReceiverEmail, string? ReceiverName, string Subject, string Title, string ContentHtml, string ContentPlain, FrozenDictionary<string, string> Headers) : IEmail;
|
Loading…
Reference in a new issue