Improved newsletter subscribe, welcome and unsubscribe Mails
This commit is contained in:
parent
461ac238ac
commit
17ab022bbe
|
@ -1,7 +1,6 @@
|
|||
@page "/Email/Unsubscribe"
|
||||
@using Microsoft.AspNetCore.Identity.UI.Services
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Microsoft.Extensions.Options
|
||||
@using Wave.Data
|
||||
@using Wave.Services
|
||||
@using Wave.Utilities
|
||||
|
@ -9,8 +8,6 @@
|
|||
@inject ILogger<EmailEdit> Logger
|
||||
@inject IStringLocalizer<EmailEdit> Localizer
|
||||
@inject IDbContextFactory<ApplicationDbContext> ContextFactory
|
||||
@inject IOptions<Customization> Customizations
|
||||
@inject NavigationManager Navigation
|
||||
@inject IEmailSender EmailSender
|
||||
@inject EmailTemplateService TemplateService
|
||||
@inject IMessageDisplay Messages
|
||||
|
@ -78,13 +75,7 @@
|
|||
await context.SaveChangesAsync();
|
||||
Messages.ShowSuccess(Localizer["Unsubscribe_Success"]);
|
||||
|
||||
var customization = Customizations.Value;
|
||||
string body = TemplateService.Default(
|
||||
Navigation.BaseUri,
|
||||
!string.IsNullOrWhiteSpace(customization.LogoLink) ? customization.LogoLink : Navigation.ToAbsoluteUri("/img/logo.png").AbsoluteUri,
|
||||
Localizer["Unsubscribe_ConfirmEmailTitle"],
|
||||
Localizer["Unsubscribe_ConfirmEmailBody"]);
|
||||
await EmailSender.SendEmailAsync(subscriber.Email, Localizer["ConfirmEmailSubject"], body);
|
||||
await EmailSender.SendEmailAsync(subscriber.Email, Localizer["ConfirmEmailSubject"], Localizer["Unsubscribe_ConfirmEmailBody"]);
|
||||
await TemplateService.ValidateTokensAsync(Id!, Token!, "unsubscribe-" + Newsletter); // delete token
|
||||
} catch (EmailNotSendException ex) {
|
||||
Logger.LogWarning(ex, "Failed to send unsubscribe confirm email. The user has been unsubscribed anyway.");
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
@using Microsoft.Extensions.Options
|
||||
@using Wave.Data
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@using System.Net
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Wave.Services
|
||||
@using Wave.Utilities
|
||||
|
@ -13,10 +12,8 @@
|
|||
@inject IStringLocalizer<EmailSignup> Localizer
|
||||
@inject IDbContextFactory<ApplicationDbContext> ContextFactory
|
||||
@inject IOptions<Features> Features
|
||||
@inject IOptions<Customization> Customizations
|
||||
@inject NavigationManager Navigation
|
||||
@inject IAdvancedEmailSender EmailSender
|
||||
@inject EmailTemplateService TemplateService
|
||||
@inject IEmailService EmailService
|
||||
@inject EmailFactory Email
|
||||
@inject IMessageDisplay Messages
|
||||
|
||||
<PageTitle>@(TitlePrefix + Localizer["Title"])</PageTitle>
|
||||
|
@ -57,7 +54,7 @@
|
|||
|
||||
if (Id is null || Token is null) return;
|
||||
try {
|
||||
var id = await TemplateService.ValidateTokensAsync(Id, Token, deleteToken: false);
|
||||
var id = await Email.IsTokenValid(Id, Token);
|
||||
|
||||
if (id is null) {
|
||||
Messages.ShowError(Localizer["Failure_Message"]);
|
||||
|
@ -74,16 +71,24 @@
|
|||
await context.SaveChangesAsync();
|
||||
Messages.ShowSuccess(Localizer["Success_Message"]);
|
||||
|
||||
await TemplateService.ValidateTokensAsync(Id, Token, deleteToken: true);
|
||||
|
||||
await Email.ClearToken(Id, Token);
|
||||
|
||||
var articles = await context.Set<EmailNewsletter>()
|
||||
.IgnoreAutoIncludes()
|
||||
.IgnoreAutoIncludes().IgnoreQueryFilters().Where(n => n.IsSend)
|
||||
.Include(a => a.Article).ThenInclude(a => a.Author)
|
||||
.OrderByDescending(a => a.DistributionDateTime)
|
||||
.Take(3)
|
||||
.ToListAsync();
|
||||
await EmailSender.SendWelcomeMailAsync(subscriber,
|
||||
Localizer["WelcomeEmailSubject"], Localizer["WelcomeEmailTitle"], Localizer["WelcomeEmailBody"], articles);
|
||||
var mail = await Email.CreateWelcomeEmail(
|
||||
subscriber, articles,
|
||||
Localizer["WelcomeEmailSubject"],
|
||||
Localizer["WelcomeEmailTitle"],
|
||||
Localizer["WelcomeEmailBody"],
|
||||
Localizer["WelcomeEmailBody"]);
|
||||
|
||||
await EmailService.ConnectAsync(CancellationToken.None);
|
||||
await EmailService.SendEmailAsync(mail);
|
||||
await EmailService.DisconnectAsync(CancellationToken.None);
|
||||
} catch (Exception ex) {
|
||||
Logger.LogError(ex, "Error trying to confirm subscriber.");
|
||||
Messages.ShowError(Localizer["Failure_Message"]);
|
||||
|
@ -110,15 +115,19 @@
|
|||
await context.SaveChangesAsync();
|
||||
|
||||
if (subscriber.Unsubscribed) {
|
||||
(string id, string token) = await TemplateService.CreateConfirmTokensAsync(subscriber.Id);
|
||||
var email = await Email.CreateConfirmationEmail(subscriber,
|
||||
Localizer["ConfirmEmailSubject"],
|
||||
Localizer["ConfirmEmailTitle"],
|
||||
Localizer["ConfirmEmailBody"],
|
||||
Localizer["ConfirmEmailBody"],
|
||||
Localizer["Submit"]);
|
||||
|
||||
string confirmLink = Navigation.ToAbsoluteUri(
|
||||
$"/Email/Confirm?user={WebUtility.UrlEncode(id)}&token={WebUtility.UrlEncode(token)}").AbsoluteUri;
|
||||
string body = string.Format(Localizer["ConfirmEmailBody"], Customizations.Value.AppName) +
|
||||
$"""<p style="text-align: center"><a href="{confirmLink}">{Localizer["Submit"]}</a></p>""";
|
||||
await EmailSender.SendDefaultMailAsync(subscriber.Email, subscriber.Name, Localizer["ConfirmEmailSubject"],
|
||||
Localizer["ConfirmEmailTitle"], body);
|
||||
await EmailService.ConnectAsync(CancellationToken.None);
|
||||
await EmailService.SendEmailAsync(email);
|
||||
await EmailService.DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
Model = new();
|
||||
} catch (Exception ex) {
|
||||
Logger.LogError(ex, "Failed to create subscriber/send confirmation mail.");
|
||||
}
|
||||
|
|
|
@ -149,7 +149,6 @@
|
|||
if (emailConfig.Smtp.Keys.Any(k => k.Equals("live", StringComparison.CurrentCultureIgnoreCase))) {
|
||||
builder.Services.AddScoped(sp => sp.GetKeyedService<IEmailService>("live")!);
|
||||
builder.Services.AddScoped<IEmailSender, SmtpEmailSender>();
|
||||
builder.Services.AddScoped<IAdvancedEmailSender, SmtpEmailSender>();
|
||||
builder.Services.AddScoped<IEmailSender<ApplicationUser>, SmtpEmailSender>();
|
||||
} else {
|
||||
builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
using System.Text;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
using StackExchange.Redis;
|
||||
using Wave.Data;
|
||||
using Wave.Utilities;
|
||||
|
||||
|
@ -20,6 +21,20 @@ public class EmailFactory(IOptions<Customization> customizations, EmailTemplateS
|
|||
return new StaticEmail(receiverMail, receiverName, subject, title, body, $"{title}\n\n{bodyPlain}", FrozenDictionary<string, string>.Empty);
|
||||
}
|
||||
|
||||
public async ValueTask<IEmail> CreateConfirmationEmail(EmailSubscriber subscriber, string subject, string title, string bodyHtml, string bodyPlain = "", string confirmLabel = "confirm") {
|
||||
(string user, string token) = await TemplateService.CreateConfirmTokensAsync(subscriber.Id);
|
||||
(string? host, string _) = GetStaticData();
|
||||
string confirmLink = new Uri(
|
||||
new Uri(host, UriKind.Absolute),
|
||||
new Uri($"/Email/Confirm?user={WebUtility.UrlEncode(user)}&token={WebUtility.UrlEncode(token)}", UriKind.Relative))
|
||||
.AbsoluteUri;
|
||||
|
||||
return await CreateDefaultEmail(subscriber.Email, subscriber.Name, subject, title,
|
||||
string.Format(bodyHtml, Customizations.AppName)
|
||||
+ $"""<p style="text-align: center"><a href="{confirmLink}">{confirmLabel}</a></p>""",
|
||||
string.Format(bodyPlain, Customizations.AppName) + $"\n\n{confirmLabel}: {confirmLink}");
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
|
@ -44,16 +59,19 @@ public class EmailFactory(IOptions<Customization> customizations, EmailTemplateS
|
|||
string articlePartial = await TemplateService.GetPartialAsync("email-article");
|
||||
string footer = await TemplateService.GetPartialAsync("email-plain-footer");
|
||||
var articlesHtml = new StringBuilder("");
|
||||
var articlesPlain = new StringBuilder("");
|
||||
foreach (var n in articles) {
|
||||
string articleLink = ArticleUtilities.GenerateArticleLink(n.Article, new Uri(Customizations.AppUrl, UriKind.Absolute));
|
||||
articlesHtml.AppendFormat(
|
||||
articlePartial,
|
||||
n.Article.Title, n.Article.Author.Name, n.Article.Body[..Math.Min(250, n.Article.Body.Length)], articleLink);
|
||||
articlesPlain.AppendFormat("{0}\n\n{1}\n{2}\n{3}",
|
||||
n.Article.Title, n.Article.Author.Name, n.Article.Body[..Math.Min(250, n.Article.Body.Length)], articleLink);
|
||||
}
|
||||
|
||||
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\n" + articlesPlain;
|
||||
bodyPlain += "\n\n" + footer.Replace(
|
||||
$"[[{EmailTemplateService.Constants.EmailUnsubscribeLink}]]",
|
||||
unsubscribeLink, true, CultureInfo.InvariantCulture);
|
||||
|
@ -64,6 +82,14 @@ public class EmailFactory(IOptions<Customization> customizations, EmailTemplateS
|
|||
}.ToFrozenDictionary());
|
||||
}
|
||||
|
||||
public async ValueTask<Guid?> IsTokenValid(string id, string token) {
|
||||
return await TemplateService.ValidateTokensAsync(id, token, deleteToken: false);
|
||||
}
|
||||
|
||||
public async ValueTask ClearToken(string id, string token) {
|
||||
await TemplateService.ValidateTokensAsync(id, token, deleteToken: true);
|
||||
}
|
||||
|
||||
private (string host, string logo) GetStaticData() {
|
||||
var host = new Uri(string.IsNullOrWhiteSpace(Customizations.AppUrl) ? "" : Customizations.AppUrl); // TODO get link
|
||||
string logo = !string.IsNullOrWhiteSpace(Customizations.LogoLink)
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||
using Wave.Data;
|
||||
|
||||
namespace Wave.Services;
|
||||
|
||||
[Obsolete]
|
||||
public interface IAdvancedEmailSender : IEmailSender {
|
||||
Task SendEmailAsync(string email, string? name, string subject, string htmlMessage);
|
||||
Task SendDefaultMailAsync(string receiverMail, string? receiverName, string subject, string title, string bodyHtml);
|
||||
Task SendSubscribedMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml,
|
||||
string browserUrl = "", string subscribedRole = "-1");
|
||||
Task SendWelcomeMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml,
|
||||
IEnumerable<EmailNewsletter> articles);
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
using Azure.Core;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||
using Wave.Data;
|
||||
using Wave.Utilities;
|
||||
|
||||
namespace Wave.Services;
|
||||
|
||||
public class SmtpEmailSender(EmailFactory email, [FromKeyedServices("live")]IEmailService emailService, [FromKeyedServices("bulk")]IEmailService bulkEmailService) : IEmailSender<ApplicationUser>, IAdvancedEmailSender, IAsyncDisposable {
|
||||
public class SmtpEmailSender(EmailFactory email, [FromKeyedServices("live")]IEmailService emailService, [FromKeyedServices("bulk")]IEmailService bulkEmailService) : IEmailSender<ApplicationUser>, IEmailSender, IAsyncDisposable {
|
||||
private EmailFactory Email { get; } = email;
|
||||
private IEmailService EmailService { get; } = emailService;
|
||||
private IEmailService BulkEmailService { get; } = bulkEmailService;
|
||||
|
@ -28,28 +28,15 @@ public class SmtpEmailSender(EmailFactory email, [FromKeyedServices("live")]IEma
|
|||
$"Please reset your password using the following code: {resetCode}.");
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region IEmailSender
|
||||
|
||||
public Task SendEmailAsync(string email, string subject, string htmlMessage) {
|
||||
return SendEmailAsync(email, null, subject, htmlMessage);
|
||||
return SendDefaultMailAsync(email, null, subject, subject, htmlMessage, HtmlUtilities.GetPlainText(htmlMessage));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
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, 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, 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);
|
||||
|
@ -57,20 +44,6 @@ public class SmtpEmailSender(EmailFactory email, [FromKeyedServices("live")]IEma
|
|||
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, 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, HtmlUtilities.GetPlainText(bodyHtml));
|
||||
await BulkEmailService.ConnectAsync(CancellationToken.None);
|
||||
await BulkEmailService.SendEmailAsync(email);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync() {
|
||||
GC.SuppressFinalize(this);
|
||||
await EmailService.DisposeAsync();
|
||||
|
|
Loading…
Reference in a new issue