From 5bd6f152037be2c2eb2963a99d6a31929035071d Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Wed, 12 Jul 2023 15:48:30 +0200 Subject: [PATCH 1/2] Added a mail template editor --- Moonlight/App/Models/Misc/MailTemplate.cs | 9 ++ .../Navigations/AdminSystemNavigation.razor | 5 + Moonlight/Shared/Views/Admin/Sys/Mail.razor | 149 ++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 Moonlight/App/Models/Misc/MailTemplate.cs create mode 100644 Moonlight/Shared/Views/Admin/Sys/Mail.razor diff --git a/Moonlight/App/Models/Misc/MailTemplate.cs b/Moonlight/App/Models/Misc/MailTemplate.cs new file mode 100644 index 0000000..1d67cdc --- /dev/null +++ b/Moonlight/App/Models/Misc/MailTemplate.cs @@ -0,0 +1,9 @@ +using Moonlight.App.Helpers.Files; + +namespace Moonlight.App.Models.Misc; + +public class MailTemplate // This is just for the blazor table at /admin/system/mail +{ + public string Name { get; set; } = ""; + public FileData File { get; set; } +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor index e7f7424..1a92236 100644 --- a/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor +++ b/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor @@ -44,6 +44,11 @@ Configuration + diff --git a/Moonlight/Shared/Views/Admin/Sys/Mail.razor b/Moonlight/Shared/Views/Admin/Sys/Mail.razor new file mode 100644 index 0000000..92e2e07 --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Sys/Mail.razor @@ -0,0 +1,149 @@ +@page "/admin/system/mail" + +@using Moonlight.Shared.Components.Navigations +@using Moonlight.Shared.Components.FileManagerPartials +@using Moonlight.App.Helpers.Files +@using Moonlight.App.Helpers +@using BlazorTable +@using Moonlight.App.Models.Misc +@using Moonlight.App.Services +@using Moonlight.App.Services.Interop + +@inject SmartTranslateService SmartTranslateService +@inject ToastService ToastService +@inject AlertService AlertService + + + + + + @if (CurrentMailTemplate == null) + { +
+
+ + Mail templates + +
+ + +
+
+
+
+ + + + + + + + +
+
+
+
+ } + else + { + + } +
+
+ +@code +{ + private MailTemplate[] MailTemplateFiles; + private FileAccess FileAccess; + private LazyLoader LazyLoader; + + private MailTemplate? CurrentMailTemplate; + private string CurrentMailTemplateContent; + + private async Task Load(LazyLoader arg) + { + FileAccess = new HostFileAccess(PathBuilder.Dir("storage")); + + await FileAccess.Cd("resources"); + await FileAccess.Cd("mail"); + + MailTemplateFiles = (await FileAccess.Ls()) + .Where(x => x.IsFile) + .Select(x => new MailTemplate() + { + Name = x.Name, + File = x + }) + .ToArray(); + } + + private async Task EditTemplate(MailTemplate mailTemplate) + { + CurrentMailTemplate = mailTemplate; + + CurrentMailTemplateContent = await FileAccess + .Read(CurrentMailTemplate.File); + + await InvokeAsync(StateHasChanged); + } + + private async Task DeleteTemplate(MailTemplate mailTemplate) + { + await FileAccess.Delete(mailTemplate.File); + await LazyLoader.Reload(); + } + + private async void OnCancelTemplateEdit() + { + CurrentMailTemplate = null; + await InvokeAsync(StateHasChanged); + } + + private async void OnSubmitTemplateEdit(string text) + { + await FileAccess.Write(CurrentMailTemplate!.File, text); + + await ToastService.Success( + SmartTranslateService.Translate("Successfully saved file")); + } + + private async Task CreateNewMailTemplate() + { + var name = await AlertService.Text( + SmartTranslateService.Translate("New mail template"), + SmartTranslateService.Translate("Enter the name of the new template"), + "" + ); + + if(string.IsNullOrEmpty(name)) + return; + + await FileAccess.Write(new() + { + Name = name + ".html" + }, ""); + + await LazyLoader.Reload(); + } +} \ No newline at end of file From 591da6de5c09336243ad8bc91df54aaffda2ac17 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 13 Jul 2023 20:39:02 +0200 Subject: [PATCH 2/2] Implemented new mail system components --- Moonlight/App/Services/Mail/MailService.cs | 95 ++++++++++++++------- Moonlight/Shared/Views/Admin/Sys/Mail.razor | 51 ++++++++--- 2 files changed, 105 insertions(+), 41 deletions(-) diff --git a/Moonlight/App/Services/Mail/MailService.cs b/Moonlight/App/Services/Mail/MailService.cs index 23d425b..f362b6d 100644 --- a/Moonlight/App/Services/Mail/MailService.cs +++ b/Moonlight/App/Services/Mail/MailService.cs @@ -2,6 +2,7 @@ using Moonlight.App.Database.Entities; using Moonlight.App.Exceptions; using Moonlight.App.Helpers; +using Moonlight.App.Repositories; using SmtpClient = MailKit.Net.Smtp.SmtpClient; namespace Moonlight.App.Services.Mail; @@ -14,8 +15,14 @@ public class MailService private readonly int Port; private readonly bool Ssl; - public MailService(ConfigService configService) + private readonly Repository UserRepository; + + public MailService( + ConfigService configService, + Repository userRepository) { + UserRepository = userRepository; + var mailConfig = configService .Get() .Moonlight.Mail; @@ -26,29 +33,9 @@ public class MailService Port = mailConfig.Port; Ssl = mailConfig.Ssl; } - - public async Task SendMail( - User user, - string name, - Action> values - ) + + public Task SendMailRaw(User user, string html) { - if (!File.Exists(PathBuilder.File("storage", "resources", "mail", $"{name}.html"))) - { - Logger.Warn($"Mail template '{name}' not found. Make sure to place one in the resources folder"); - throw new DisplayException("Mail template not found"); - } - - var rawHtml = await File.ReadAllTextAsync(PathBuilder.File("storage", "resources", "mail", $"{name}.html")); - - var val = new Dictionary(); - values.Invoke(val); - - val.Add("FirstName", user.FirstName); - val.Add("LastName", user.LastName); - - var parsed = ParseMail(rawHtml, val); - Task.Run(async () => { try @@ -62,17 +49,15 @@ public class MailService var body = new BodyBuilder { - HtmlBody = parsed + HtmlBody = html }; mailMessage.Body = body.ToMessageBody(); - using (var smtpClient = new SmtpClient()) - { - await smtpClient.ConnectAsync(Server, Port, Ssl); - await smtpClient.AuthenticateAsync(Email, Password); - await smtpClient.SendAsync(mailMessage); - await smtpClient.DisconnectAsync(true); - } + using var smtpClient = new SmtpClient(); + await smtpClient.ConnectAsync(Server, Port, Ssl); + await smtpClient.AuthenticateAsync(Email, Password); + await smtpClient.SendAsync(mailMessage); + await smtpClient.DisconnectAsync(true); } catch (Exception e) { @@ -80,6 +65,54 @@ public class MailService Logger.Warn(e); } }); + + return Task.CompletedTask; + } + + public async Task SendMail(User user, string template, Action> values) + { + if (!File.Exists(PathBuilder.File("storage", "resources", "mail", $"{template}.html"))) + { + Logger.Warn($"Mail template '{template}' not found. Make sure to place one in the resources folder"); + throw new DisplayException("Mail template not found"); + } + + var rawHtml = await File.ReadAllTextAsync(PathBuilder.File("storage", "resources", "mail", $"{template}.html")); + + var val = new Dictionary(); + values.Invoke(val); + + val.Add("FirstName", user.FirstName); + val.Add("LastName", user.LastName); + + var parsed = ParseMail(rawHtml, val); + + await SendMailRaw(user, parsed); + } + + public async Task SendEmailToAll(string template, Action> values) + { + var users = UserRepository + .Get() + .ToArray(); + + foreach (var user in users) + { + await SendMail(user, template, values); + } + } + + public async Task SendEmailToAllAdmins(string template, Action> values) + { + var users = UserRepository + .Get() + .Where(x => x.Admin) + .ToArray(); + + foreach (var user in users) + { + await SendMail(user, template, values); + } } private string ParseMail(string html, Dictionary values) diff --git a/Moonlight/Shared/Views/Admin/Sys/Mail.razor b/Moonlight/Shared/Views/Admin/Sys/Mail.razor index 92e2e07..538b65e 100644 --- a/Moonlight/Shared/Views/Admin/Sys/Mail.razor +++ b/Moonlight/Shared/Views/Admin/Sys/Mail.razor @@ -5,17 +5,35 @@ @using Moonlight.App.Helpers.Files @using Moonlight.App.Helpers @using BlazorTable +@using Moonlight.App.Database.Entities @using Moonlight.App.Models.Misc @using Moonlight.App.Services @using Moonlight.App.Services.Interop +@using Moonlight.App.Services.Mail @inject SmartTranslateService SmartTranslateService @inject ToastService ToastService @inject AlertService AlertService +@inject MailService MailService +
+
+ + Actions + +
+
+ + +
+
+ @if (CurrentMailTemplate == null) { @@ -63,9 +81,9 @@ } else { - } @@ -74,12 +92,17 @@ @code { + [CascadingParameter] + public User User { get; set; } + private MailTemplate[] MailTemplateFiles; private FileAccess FileAccess; private LazyLoader LazyLoader; - + + #region Template Editor + private MailTemplate? CurrentMailTemplate; - private string CurrentMailTemplateContent; + private string CurrentMailTemplateContent = ""; private async Task Load(LazyLoader arg) { @@ -101,10 +124,10 @@ private async Task EditTemplate(MailTemplate mailTemplate) { CurrentMailTemplate = mailTemplate; - + CurrentMailTemplateContent = await FileAccess .Read(CurrentMailTemplate.File); - + await InvokeAsync(StateHasChanged); } @@ -135,15 +158,23 @@ SmartTranslateService.Translate("Enter the name of the new template"), "" ); - - if(string.IsNullOrEmpty(name)) + + if (string.IsNullOrEmpty(name)) return; await FileAccess.Write(new() { Name = name + ".html" }, ""); - + await LazyLoader.Reload(); } + + #endregion + + private async Task SendTestMail() + { + await MailService.SendMailRaw(User, "If you see this mail, your moonlight mail configuration is ready to use"); + await AlertService.Info(SmartTranslateService.Translate("A test mail has been sent to the email address of your account")); + } } \ No newline at end of file