diff --git a/Lombiq.Hosting.Tenants.Admin.Login/Lombiq.Hosting.Tenants.Admin.Login.csproj b/Lombiq.Hosting.Tenants.Admin.Login/Lombiq.Hosting.Tenants.Admin.Login.csproj index c6963a43..13d94f6f 100644 --- a/Lombiq.Hosting.Tenants.Admin.Login/Lombiq.Hosting.Tenants.Admin.Login.csproj +++ b/Lombiq.Hosting.Tenants.Admin.Login/Lombiq.Hosting.Tenants.Admin.Login.csproj @@ -34,10 +34,10 @@ - - - - + + + + diff --git a/Lombiq.Hosting.Tenants.EmailQuotaManagement.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs b/Lombiq.Hosting.Tenants.EmailQuotaManagement.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs index 48b3aafe..30ee233a 100644 --- a/Lombiq.Hosting.Tenants.EmailQuotaManagement.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs +++ b/Lombiq.Hosting.Tenants.EmailQuotaManagement.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs @@ -5,6 +5,7 @@ using Shouldly; using System; using System.Collections.Generic; +using System.Globalization; using System.Threading.Tasks; namespace Lombiq.Hosting.Tenants.EmailQuotaManagement.Tests.UI.Extensions; @@ -23,18 +24,19 @@ public static async Task TestEmailQuotaManagementBehaviorAsync( bool moduleShouldInterfere = true) { await context.SignInDirectlyAndGoToDashboardAsync(); - context.Missing(By.XPath(DashboardExceededMessage)); - await context.GoToAdminRelativeUrlAsync("/Settings/email"); - + await context.ConfigureSmtpPortAsync(publish: false); CheckEmailsSentWarningMessage(context, exists: moduleShouldInterfere, maximumEmailQuota, 0); + await context.ClickReliablyOnAsync(By.ClassName("save")); var warningEmails = new List(); for (int i = 0; i < maximumEmailQuota; i++) { - await SendTestEmailAsync(context, SuccessfulSubject); + await context.GoToEmailTestAsync(); + await context.FillEmailTestFormAsync(SuccessfulSubject); context.SuccessMessageExists(); + CheckEmailsSentWarningMessage(context, exists: moduleShouldInterfere, maximumEmailQuota, i + 1); var warningLevel = Convert.ToInt32(Math.Round((double)(i + 1) / maximumEmailQuota * 100, 0)); @@ -63,7 +65,8 @@ public static async Task TestEmailQuotaManagementBehaviorAsync( } } - await SendTestEmailAsync(context, UnSuccessfulSubject); + await context.GoToEmailTestAsync(); + await context.FillEmailTestFormAsync(UnSuccessfulSubject); await context.GoToSmtpWebUIAsync(); context.CheckExistence(ByHelper.SmtpInboxRow(SuccessfulSubject), exists: true); context.CheckExistence( @@ -88,31 +91,21 @@ private static void CheckMessageExistence(UITestContext context, string warningL $"[contains(.,'It seems that your site sent out {warningLevel}% of e-mail')]"), exists: true); - private static void CheckEmailsSentWarningMessage(UITestContext context, bool exists, int maximumEmailQuota, int currentEmailCount) => - context.CheckExistence( - By.XPath($"//p[contains(@class,'alert-warning')][contains(.,'{currentEmailCount.ToTechnicalString()} emails" + - $" from the total of {maximumEmailQuota.ToTechnicalString()} this month.')]"), - exists); - - private static async Task SendTestEmailAsync(UITestContext context, string subject) + private static void CheckEmailsSentWarningMessage(UITestContext context, bool exists, int maximumEmailQuota, int currentEmailCount) { - await context.GoToAdminRelativeUrlAsync("/Email/Index"); - await context.FillInWithRetriesAsync(By.Id("To"), "recipient@example.com"); - await context.FillInWithRetriesAsync(By.Id("Subject"), subject); - await context.FillInWithRetriesAsync(By.Id("Body"), "Hi, this is a test."); + var by = By.CssSelector(".alert-warning[data-smtp-quota-max][data-smtp-quota-used]"); - await ReliabilityHelper.DoWithRetriesOrFailAsync( - async () => - { - try - { - await context.ClickReliablyOnAsync(By.Id("emailtestsend")); // #spell-check-ignore-line - return true; - } - catch (WebDriverException ex) when (ex.Message.Contains("move target out of bounds")) - { - return false; - } - }); + if (!exists) + { + context.Missing(by); + return; + } + + var element = context.Get(by); + var max = int.Parse(element.GetAttribute("data-smtp-quota-max"), CultureInfo.InvariantCulture); + var used = int.Parse(element.GetAttribute("data-smtp-quota-used"), CultureInfo.InvariantCulture); + + max.ShouldBe(maximumEmailQuota); + used.ShouldBe(currentEmailCount); } } diff --git a/Lombiq.Hosting.Tenants.EmailQuotaManagement/Filters/EmailSettingsQuotaFilter.cs b/Lombiq.Hosting.Tenants.EmailQuotaManagement/Filters/EmailSettingsQuotaFilter.cs index b4543ede..8b01a175 100644 --- a/Lombiq.Hosting.Tenants.EmailQuotaManagement/Filters/EmailSettingsQuotaFilter.cs +++ b/Lombiq.Hosting.Tenants.EmailQuotaManagement/Filters/EmailSettingsQuotaFilter.cs @@ -4,8 +4,8 @@ using OrchardCore.DisplayManagement; using OrchardCore.DisplayManagement.Layout; using OrchardCore.Mvc.Core.Utilities; -using OrchardCore.Queries.Controllers; using System.Threading.Tasks; +using EmailAdminController = OrchardCore.Email.Controllers.AdminController; namespace Lombiq.Hosting.Tenants.EmailQuotaManagement.Filters; @@ -33,17 +33,12 @@ public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultE return; } - var actionRouteController = context.ActionDescriptor.RouteValues["Controller"]; - var actionRouteArea = context.ActionDescriptor.RouteValues["Area"]; - var actionRouteValue = context.ActionDescriptor.RouteValues["Action"]; - - if (actionRouteController == typeof(AdminController).ControllerName() && - actionRouteArea == $"{nameof(OrchardCore)}.{nameof(OrchardCore.Settings)}" && - actionRouteValue is nameof(AdminController.Index) && - context.Result is ViewResult && - context.RouteData.Values.TryGetValue("GroupId", out var groupId) && - (string)groupId == "email" && - _emailQuotaService.ShouldLimitEmails()) + var isEmailTestPage = context.IsMvcRoute( + nameof(EmailAdminController.Test), + typeof(EmailAdminController).ControllerName(), + $"{nameof(OrchardCore)}.{nameof(OrchardCore.Email)}"); + + if ((isEmailTestPage || context.IsSiteSettingsPage("email")) && _emailQuotaService.ShouldLimitEmails()) { var layout = await _layoutAccessor.GetLayoutAsync(); var contentZone = layout.Zones["Content"]; diff --git a/Lombiq.Hosting.Tenants.EmailQuotaManagement/Lombiq.Hosting.Tenants.EmailQuotaManagement.csproj b/Lombiq.Hosting.Tenants.EmailQuotaManagement/Lombiq.Hosting.Tenants.EmailQuotaManagement.csproj index 9f40a4b1..20095792 100644 --- a/Lombiq.Hosting.Tenants.EmailQuotaManagement/Lombiq.Hosting.Tenants.EmailQuotaManagement.csproj +++ b/Lombiq.Hosting.Tenants.EmailQuotaManagement/Lombiq.Hosting.Tenants.EmailQuotaManagement.csproj @@ -34,16 +34,15 @@ - - - - - - - - + + + + + + + + - diff --git a/Lombiq.Hosting.Tenants.EmailQuotaManagement/Services/QuotaManagingSmtpServiceDecorator.cs b/Lombiq.Hosting.Tenants.EmailQuotaManagement/Services/QuotaManagingSmtpServiceDecorator.cs index 703472e8..de6a0807 100644 --- a/Lombiq.Hosting.Tenants.EmailQuotaManagement/Services/QuotaManagingSmtpServiceDecorator.cs +++ b/Lombiq.Hosting.Tenants.EmailQuotaManagement/Services/QuotaManagingSmtpServiceDecorator.cs @@ -10,24 +10,24 @@ namespace Lombiq.Hosting.Tenants.EmailQuotaManagement.Services; -public class QuotaManagingSmtpServiceDecorator : ISmtpService +public class QuotaManagingSmtpServiceDecorator : IEmailService { private readonly IStringLocalizer T; - private readonly ISmtpService _smtpService; + private readonly IEmailService _emailService; private readonly IEmailQuotaService _emailQuotaService; private readonly ShellSettings _shellSettings; private readonly IEmailTemplateService _emailTemplateService; private readonly IEmailQuotaSubjectService _emailQuotaSubjectService; public QuotaManagingSmtpServiceDecorator( - ISmtpService smtpService, + IEmailService emailService, IStringLocalizer stringLocalizer, IEmailQuotaService emailQuotaService, ShellSettings shellSettings, IEmailTemplateService emailTemplateService, IEmailQuotaSubjectService emailQuotaSubjectService) { - _smtpService = smtpService; + _emailService = emailService; T = stringLocalizer; _emailQuotaService = emailQuotaService; _shellSettings = shellSettings; @@ -35,11 +35,11 @@ public QuotaManagingSmtpServiceDecorator( _emailQuotaSubjectService = emailQuotaSubjectService; } - public async Task SendAsync(MailMessage message) + public async Task SendAsync(MailMessage message, string providerName = null) { if (!_emailQuotaService.ShouldLimitEmails()) { - return await _smtpService.SendAsync(message); + return await _emailService.SendAsync(message, providerName); } var isQuotaOverResult = await _emailQuotaService.IsQuotaOverTheLimitAsync(); @@ -48,14 +48,11 @@ public async Task SendAsync(MailMessage message) // Should send the email if the quota is not over the limit. if (isQuotaOverResult.IsOverQuota) { - return SmtpResult.Failed(T["The email quota for the site has been exceeded."]); + return EmailResult.FailedResult(T["The email quota for the site has been exceeded."]); } - var emailResult = await _smtpService.SendAsync(message); - if (emailResult == SmtpResult.Success) - { - await _emailQuotaService.IncreaseEmailUsageAsync(isQuotaOverResult.EmailQuota); - } + var emailResult = await _emailService.SendAsync(message, providerName); + if (emailResult.Succeeded) await _emailQuotaService.IncreaseEmailUsageAsync(isQuotaOverResult.EmailQuota); return emailResult; } @@ -109,7 +106,7 @@ private Task SendQuotaEmailAsync( }); // ISmtpService must be used within this class otherwise it won't call the original ISmtpService // implementation, but loop back to here. - await _smtpService.SendAsync(emailMessage); + await _emailService.SendAsync(emailMessage); }); } diff --git a/Lombiq.Hosting.Tenants.EmailQuotaManagement/Startup.cs b/Lombiq.Hosting.Tenants.EmailQuotaManagement/Startup.cs index abeebcbf..1ff026d9 100644 --- a/Lombiq.Hosting.Tenants.EmailQuotaManagement/Startup.cs +++ b/Lombiq.Hosting.Tenants.EmailQuotaManagement/Startup.cs @@ -42,6 +42,6 @@ public override void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); - services.Decorate(); + services.Decorate(); } } diff --git a/Lombiq.Hosting.Tenants.EmailQuotaManagement/Views/EmailSettingsQuotaMessage.cshtml b/Lombiq.Hosting.Tenants.EmailQuotaManagement/Views/EmailSettingsQuotaMessage.cshtml index e180df88..3453cf92 100644 --- a/Lombiq.Hosting.Tenants.EmailQuotaManagement/Views/EmailSettingsQuotaMessage.cshtml +++ b/Lombiq.Hosting.Tenants.EmailQuotaManagement/Views/EmailSettingsQuotaMessage.cshtml @@ -1,6 +1,11 @@ -

- @T["Unless you use your own SMTP server you have limited e-mails to send. " + - "You've sent {0} emails from the total of {1} this month.", - Model.CurrentEmailUsageCount, - Model.EmailQuotaPerMonth] +@{ + int currentEmailUsageCount = Model.CurrentEmailUsageCount; + int emailQuotaPerMonth = Model.EmailQuotaPerMonth; +} + +

+ @T["Unless you use your own SMTP server you have limited e-mails to send."] + @T["You've sent {0} emails from the total of {1} this month.", currentEmailUsageCount, emailQuotaPerMonth]

diff --git a/Lombiq.Hosting.Tenants.EnvironmentRobots/Lombiq.Hosting.Tenants.EnvironmentRobots.csproj b/Lombiq.Hosting.Tenants.EnvironmentRobots/Lombiq.Hosting.Tenants.EnvironmentRobots.csproj index d0c29dee..5674f178 100644 --- a/Lombiq.Hosting.Tenants.EnvironmentRobots/Lombiq.Hosting.Tenants.EnvironmentRobots.csproj +++ b/Lombiq.Hosting.Tenants.EnvironmentRobots/Lombiq.Hosting.Tenants.EnvironmentRobots.csproj @@ -34,8 +34,8 @@
- - - + + + diff --git a/Lombiq.Hosting.Tenants.FeaturesGuard/Lombiq.Hosting.Tenants.FeaturesGuard.csproj b/Lombiq.Hosting.Tenants.FeaturesGuard/Lombiq.Hosting.Tenants.FeaturesGuard.csproj index 37418954..3f19a458 100644 --- a/Lombiq.Hosting.Tenants.FeaturesGuard/Lombiq.Hosting.Tenants.FeaturesGuard.csproj +++ b/Lombiq.Hosting.Tenants.FeaturesGuard/Lombiq.Hosting.Tenants.FeaturesGuard.csproj @@ -34,11 +34,11 @@ - - - - - + + + + + diff --git a/Lombiq.Hosting.Tenants.IdleTenantManagement/Lombiq.Hosting.Tenants.IdleTenantManagement.csproj b/Lombiq.Hosting.Tenants.IdleTenantManagement/Lombiq.Hosting.Tenants.IdleTenantManagement.csproj index 6bad9d1e..2584d554 100644 --- a/Lombiq.Hosting.Tenants.IdleTenantManagement/Lombiq.Hosting.Tenants.IdleTenantManagement.csproj +++ b/Lombiq.Hosting.Tenants.IdleTenantManagement/Lombiq.Hosting.Tenants.IdleTenantManagement.csproj @@ -24,9 +24,9 @@ - - - + + + diff --git a/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj b/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj index 80c66734..d8aa29f5 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj +++ b/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj @@ -23,13 +23,13 @@ - - - - - - - + + + + + + + diff --git a/Lombiq.Hosting.Tenants.Management.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs b/Lombiq.Hosting.Tenants.Management.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs index c4665351..b447d646 100644 --- a/Lombiq.Hosting.Tenants.Management.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs +++ b/Lombiq.Hosting.Tenants.Management.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs @@ -1,8 +1,8 @@ using Lombiq.Tests.UI.Extensions; using Lombiq.Tests.UI.Services; -using Newtonsoft.Json.Linq; using OpenQA.Selenium; using Shouldly; +using System.Text.Json.Nodes; using System.Threading.Tasks; namespace Lombiq.Hosting.Tenants.Management.Tests.UI.Extensions; @@ -58,6 +58,6 @@ private static void CheckEditorValue(this UITestContext context, string keyToChe var editorJson = string.IsNullOrEmpty(editorText) ? "{}" : editorText; var editorValue = JObject.Parse(editorJson); - editorValue.SelectToken($"TestKey.TestSubKey.TestSubOptions.{keyToCheck}")?.ToString().ShouldBeAsString(expectedValue); + editorValue.SelectNode($"TestKey.TestSubKey.TestSubOptions.{keyToCheck}")?.ToString().ShouldBeAsString(expectedValue); } } diff --git a/Lombiq.Hosting.Tenants.Management/Lombiq.Hosting.Tenants.Management.csproj b/Lombiq.Hosting.Tenants.Management/Lombiq.Hosting.Tenants.Management.csproj index 0bbd333a..8860c804 100644 --- a/Lombiq.Hosting.Tenants.Management/Lombiq.Hosting.Tenants.Management.csproj +++ b/Lombiq.Hosting.Tenants.Management/Lombiq.Hosting.Tenants.Management.csproj @@ -34,9 +34,9 @@ - - - + + + diff --git a/Lombiq.Hosting.Tenants.Management/Views/ShellSettingsEditor.cshtml b/Lombiq.Hosting.Tenants.Management/Views/ShellSettingsEditor.cshtml index 0a20b765..1ee456f1 100644 --- a/Lombiq.Hosting.Tenants.Management/Views/ShellSettingsEditor.cshtml +++ b/Lombiq.Hosting.Tenants.Management/Views/ShellSettingsEditor.cshtml @@ -1,5 +1,4 @@ @using Lombiq.Hosting.Tenants.Management.Controllers -@using Newtonsoft.Json @using OrchardCore @using OrchardCore.Mvc.Core.Utilities @using static Lombiq.Hosting.Tenants.Management.Constants.FeatureNames @@ -31,11 +30,11 @@