diff --git a/Wave/Components/Pages/EmailSignup.razor b/Wave/Components/Pages/EmailSignup.razor index 6b33ebd..713a542 100644 --- a/Wave/Components/Pages/EmailSignup.razor +++ b/Wave/Components/Pages/EmailSignup.razor @@ -7,7 +7,6 @@ @using System.Net @using Microsoft.EntityFrameworkCore @using Wave.Services -@using Wave.Utilities @inject ILogger Logger @inject IStringLocalizer Localizer @@ -82,27 +81,15 @@ Message = Localizer["Success_Message"]; await TemplateService.ValidateTokensAsync(Id, Token, deleteToken: true); - + var articles = await context.Set() .IgnoreAutoIncludes() .Include(a => a.Article).ThenInclude(a => a.Author) .OrderByDescending(a => a.DistributionDateTime) .Take(3) .ToListAsync(); - string body = $"

{Localizer["WelcomeEmailBody"]}

\n"; - - foreach (var n in articles) { - string articleLink = ArticleUtilities.GenerateArticleLink(n.Article, new Uri(Customizations.Value.AppUrl, UriKind.Absolute)); - body = body + - "
" + - $"

{n.Article.Title}

" + - $"{n.Article.Author.Name}" + - $"

{n.Article.Body[..Math.Min(100, n.Article.Body.Length)]}...

" + - $"Link" + - "
"; - } - await EmailSender.SendSubscribedMailAsync(subscriber, - Localizer["WelcomeEmailSubject"], Localizer["WelcomeEmailTitle"], body, subscribedRole: "welcome"); + await EmailSender.SendWelcomeMailAsync(subscriber, + Localizer["WelcomeEmailSubject"], Localizer["WelcomeEmailTitle"], Localizer["WelcomeEmailBody"], articles); } catch (Exception ex) { Logger.LogError(ex, "Error trying to confirm subscriber."); Message = Localizer["Failure_Message"]; diff --git a/Wave/Services/EmailTemplateService.cs b/Wave/Services/EmailTemplateService.cs index 1f05b53..bc12b97 100644 --- a/Wave/Services/EmailTemplateService.cs +++ b/Wave/Services/EmailTemplateService.cs @@ -6,7 +6,7 @@ namespace Wave.Services; public partial class EmailTemplateService(ILogger logger, IDistributedCache tokenCache, FileSystemService fileSystem) { public enum Constants { - BrowserLink, HomeLink, ContentLogo, ContentTitle, ContentBody, EmailUnsubscribeLink + BrowserLink, HomeLink, ContentLogo, ContentTitle, ContentBody, EmailUnsubscribeLink, ArticleRecommendations } private ILogger 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.HomeLink, home }, + { Constants.ContentLogo, logoLink }, + { Constants.ContentTitle, title }, + { Constants.ContentBody, body }, + { Constants.EmailUnsubscribeLink, unsubscribe }, + { Constants.ArticleRecommendations, articles } + }); + } + public void TryCreateDefaultTemplates() { FileSystem.GetEmailTemplate("default", DefaultTemplates["default"]); FileSystem.GetEmailTemplate("newsletter", DefaultTemplates["newsletter"]); + FileSystem.GetEmailTemplate("welcome", DefaultTemplates["welcome"]); } public string ApplyTokens(string template, Func replacer) { @@ -189,6 +201,60 @@ public enum Constants { """ + }, + { + "welcome", + $""" + + + + + + + + + + + +

[[{Constants.ContentTitle}]]

+
+
+
+ + + + + + + + [[{Constants.ContentBody}]] + + + + + + + + + + [[{Constants.ArticleRecommendations}]] + + + + + + + + + + + Unsubscribe + + + +
+
+ """ } }; } \ No newline at end of file diff --git a/Wave/Services/IAdvancedEmailSender.cs b/Wave/Services/IAdvancedEmailSender.cs index 852cd9a..354e051 100644 --- a/Wave/Services/IAdvancedEmailSender.cs +++ b/Wave/Services/IAdvancedEmailSender.cs @@ -8,4 +8,6 @@ public interface IAdvancedEmailSender : IEmailSender { 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 articles); } \ No newline at end of file diff --git a/Wave/Services/SmtpEmailSender.cs b/Wave/Services/SmtpEmailSender.cs index 0229d45..555d419 100644 --- a/Wave/Services/SmtpEmailSender.cs +++ b/Wave/Services/SmtpEmailSender.cs @@ -1,10 +1,12 @@ using System.Net; +using System.Text; using MailKit.Net.Smtp; using MailKit.Security; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; using MimeKit; using Wave.Data; +using Wave.Utilities; using Uri = System.Uri; 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.ListUnsubscribePost, "One-Click")); } + + public async Task SendWelcomeMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml, + IEnumerable 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( + """ +
+

{0}

+ {1} +

{2}

+ Link +
+ """, + 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); \ No newline at end of file