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/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/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..538b65e --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Sys/Mail.razor @@ -0,0 +1,180 @@ +@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.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) + { +
+
+ + Mail templates + +
+ + +
+
+
+
+ + + + + + + + +
+
+
+
+ } + else + { + + } +
+
+ +@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 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(); + } + + #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