mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Store Email Settings: Improve configuration (#5629)
* Store Email Settings: Improve configuration This works with the existing settings and provides better guidance about the different store email cases. Closes #5623. * Split email and notification settings
This commit is contained in:
parent
2111b67e2c
commit
b174977bc7
9 changed files with 117 additions and 71 deletions
|
@ -468,8 +468,16 @@ namespace BTCPayServer.Tests
|
|||
s.RegisterNewUser(true);
|
||||
s.CreateNewStore();
|
||||
|
||||
// Ensure empty server settings
|
||||
s.Driver.Navigate().GoToUrl(s.Link("/server/emails"));
|
||||
s.Driver.FindElement(By.Id("Settings_Login")).Clear();
|
||||
s.Driver.FindElement(By.Id("Settings_Password")).Clear();
|
||||
s.Driver.FindElement(By.Id("Settings_From")).Clear();
|
||||
s.Driver.FindElement(By.Id("Save")).Submit();
|
||||
|
||||
// Store Emails without server fallback
|
||||
s.GoToStore(StoreNavPages.Emails);
|
||||
s.Driver.ElementDoesNotExist(By.Id("UseCustomSMTP"));
|
||||
s.Driver.FindElement(By.Id("ConfigureEmailRules")).Click();
|
||||
Assert.Contains("You need to configure email settings before this feature works", s.Driver.PageSource);
|
||||
|
||||
|
@ -481,13 +489,16 @@ namespace BTCPayServer.Tests
|
|||
s.FindAlertMessage();
|
||||
}
|
||||
CanSetupEmailCore(s);
|
||||
|
||||
|
||||
// Store Emails with server fallback
|
||||
s.GoToStore(StoreNavPages.Emails);
|
||||
Assert.False(s.Driver.FindElement(By.Id("UseCustomSMTP")).Selected);
|
||||
s.Driver.FindElement(By.Id("ConfigureEmailRules")).Click();
|
||||
Assert.Contains("Emails will be sent with the email settings of the server", s.Driver.PageSource);
|
||||
Assert.DoesNotContain("You need to configure email settings before this feature works", s.Driver.PageSource);
|
||||
|
||||
s.GoToStore(StoreNavPages.Emails);
|
||||
s.Driver.FindElement(By.Id("UseCustomSMTP")).Click();
|
||||
Thread.Sleep(250);
|
||||
CanSetupEmailCore(s);
|
||||
|
||||
// Store Email Rules
|
||||
|
@ -495,7 +506,6 @@ namespace BTCPayServer.Tests
|
|||
Assert.Contains("There are no rules yet.", s.Driver.PageSource);
|
||||
Assert.DoesNotContain("id=\"SaveEmailRules\"", s.Driver.PageSource);
|
||||
Assert.DoesNotContain("You need to configure email settings before this feature works", s.Driver.PageSource);
|
||||
Assert.DoesNotContain("Emails will be sent with the email settings of the server", s.Driver.PageSource);
|
||||
|
||||
s.Driver.FindElement(By.Id("CreateEmailRule")).Click();
|
||||
var select = new SelectElement(s.Driver.FindElement(By.Id("Rules_0__Trigger")));
|
||||
|
@ -506,6 +516,9 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.FindElement(By.ClassName("note-editable")).SendKeys("Your invoice is settled");
|
||||
s.Driver.FindElement(By.Id("SaveEmailRules")).Click();
|
||||
Assert.Contains("Store email rules saved", s.FindAlertMessage().Text);
|
||||
|
||||
s.GoToStore(StoreNavPages.Emails);
|
||||
Assert.True(s.Driver.FindElement(By.Id("UseCustomSMTP")).Selected);
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
|
@ -3159,15 +3172,13 @@ retry:
|
|||
|
||||
private static void CanSetupEmailCore(SeleniumTester s)
|
||||
{
|
||||
s.Driver.ScrollTo(By.Id("QuickFillDropdownToggle"));
|
||||
s.Driver.FindElement(By.Id("QuickFillDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("#quick-fill .dropdown-menu .dropdown-item:first-child")).Click();
|
||||
s.Driver.FindElement(By.Id("Settings_Login")).Clear();
|
||||
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("test@gmail.com");
|
||||
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
|
||||
s.FindAlertMessage();
|
||||
s.Driver.FindElement(By.Id("Settings_Password")).Clear();
|
||||
s.Driver.FindElement(By.Id("Settings_Password")).SendKeys("mypassword");
|
||||
|
||||
s.Driver.FindElement(By.Id("Settings_From")).Clear();
|
||||
s.Driver.FindElement(By.Id("Settings_From")).SendKeys("Firstname Lastname <email@example.com>");
|
||||
s.Driver.FindElement(By.Id("Save")).SendKeys(Keys.Enter);
|
||||
|
|
|
@ -27,22 +27,20 @@ namespace BTCPayServer.Controllers
|
|||
return NotFound();
|
||||
|
||||
var blob = store.GetStoreBlob();
|
||||
var storeSetupComplete = blob.EmailSettings?.IsComplete() is true;
|
||||
if (!storeSetupComplete && !TempData.HasStatusMessage())
|
||||
if (blob.EmailSettings?.IsComplete() is not true && !TempData.HasStatusMessage())
|
||||
{
|
||||
var emailSender = await _emailSenderFactory.GetEmailSender(store.Id) as StoreEmailSender;
|
||||
var hasServerFallback = await IsSetupComplete(emailSender?.FallbackSender);
|
||||
var message = hasServerFallback
|
||||
? "Emails will be sent with the email settings of the server"
|
||||
: "You need to configure email settings before this feature works";
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
if (!await IsSetupComplete(emailSender?.FallbackSender))
|
||||
{
|
||||
Severity = hasServerFallback ? StatusMessageModel.StatusSeverity.Info : StatusMessageModel.StatusSeverity.Warning,
|
||||
Html = $"{message}. <a class='alert-link' href='{Url.Action("StoreEmailSettings", new { storeId })}'>Configure store email settings</a>."
|
||||
});
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||
Html = $"You need to configure email settings before this feature works. <a class='alert-link' href='{Url.Action("StoreEmailSettings", new { storeId })}'>Configure store email settings</a>."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var vm = new StoreEmailRuleViewModel { Rules = blob.EmailRules ?? new List<StoreEmailRule>() };
|
||||
var vm = new StoreEmailRuleViewModel { Rules = blob.EmailRules ?? [] };
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
|
@ -172,13 +170,20 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("{storeId}/email-settings")]
|
||||
public IActionResult StoreEmailSettings()
|
||||
public async Task<IActionResult> StoreEmailSettings(string storeId)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
var data = store.GetStoreBlob().EmailSettings ?? new EmailSettings();
|
||||
return View(new EmailsViewModel(data));
|
||||
|
||||
var blob = store.GetStoreBlob();
|
||||
var data = blob.EmailSettings ?? new EmailSettings();
|
||||
var fallbackSettings = await _emailSenderFactory.GetEmailSender(store.Id) is StoreEmailSender { FallbackSender: not null } storeSender
|
||||
? await storeSender.FallbackSender.GetEmailSettings()
|
||||
: null;
|
||||
var vm = new EmailsViewModel(data, fallbackSettings);
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/email-settings")]
|
||||
|
@ -187,7 +192,13 @@ namespace BTCPayServer.Controllers
|
|||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
|
||||
|
||||
var emailSender = await _emailSenderFactory.GetEmailSender(store.Id) as StoreEmailSender;
|
||||
var fallbackSettings = await _emailSenderFactory.GetEmailSender(store.Id) is StoreEmailSender { FallbackSender: not null } storeSender
|
||||
? await storeSender.FallbackSender.GetEmailSettings()
|
||||
: null;
|
||||
model.FallbackSettings = fallbackSettings;
|
||||
|
||||
if (command == "Test")
|
||||
{
|
||||
try
|
||||
|
@ -230,7 +241,7 @@ namespace BTCPayServer.Controllers
|
|||
return View(model);
|
||||
}
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
if (new EmailsViewModel(storeBlob.EmailSettings).PasswordSet && storeBlob.EmailSettings != null)
|
||||
if (storeBlob.EmailSettings != null && new EmailsViewModel(storeBlob.EmailSettings, fallbackSettings).PasswordSet)
|
||||
{
|
||||
model.Settings.Password = storeBlob.EmailSettings.Password;
|
||||
}
|
||||
|
|
|
@ -6,25 +6,27 @@ namespace BTCPayServer.Models.ServerViewModels
|
|||
{
|
||||
public class EmailsViewModel
|
||||
{
|
||||
public EmailSettings Settings { get; set; }
|
||||
public EmailSettings FallbackSettings { get; set; }
|
||||
public bool PasswordSet { get; set; }
|
||||
|
||||
[MailboxAddress]
|
||||
[Display(Name = "Test Email")]
|
||||
public string TestEmail { get; set; }
|
||||
|
||||
public EmailsViewModel()
|
||||
{
|
||||
|
||||
}
|
||||
public EmailsViewModel(EmailSettings settings)
|
||||
|
||||
public EmailsViewModel(EmailSettings settings, EmailSettings fallbackSettings = null)
|
||||
{
|
||||
Settings = settings;
|
||||
FallbackSettings = fallbackSettings;
|
||||
PasswordSet = !string.IsNullOrEmpty(settings?.Password);
|
||||
}
|
||||
public EmailSettings Settings
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public bool PasswordSet { get; set; }
|
||||
[MailboxAddressAttribute]
|
||||
[Display(Name = "Test Email")]
|
||||
public string TestEmail
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public bool IsSetup() => Settings?.IsComplete() is true;
|
||||
public bool IsFallbackSetup() => FallbackSettings?.IsComplete() is true;
|
||||
public bool UsesFallback() => IsFallbackSetup() && Settings == FallbackSettings;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,14 @@ namespace BTCPayServer.Services
|
|||
public bool DisableInstantNotifications { get; set; }
|
||||
[Display(Name = "Disable stores from using the server's email settings as backup")]
|
||||
public bool DisableStoresToUseServerEmailSettings { get; set; }
|
||||
[JsonIgnore]
|
||||
[Display(Name = "Allow stores to use the server's SMTP email settings as a default")]
|
||||
public bool EnableStoresToUseServerEmailSettings
|
||||
{
|
||||
get => !DisableStoresToUseServerEmailSettings;
|
||||
set { DisableStoresToUseServerEmailSettings = !value; }
|
||||
}
|
||||
|
||||
[Display(Name = "Disable non-admins access to the user creation API endpoint")]
|
||||
public bool DisableNonAdminCreateUserApi { get; set; }
|
||||
|
||||
|
|
|
@ -1,27 +1,5 @@
|
|||
@model BTCPayServer.Models.ServerViewModels.EmailsViewModel
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xl-10 col-xxl-constrain">
|
||||
<div class="d-flex flex-wrap gap-3 align-items-center justify-content-between mt-n1 mb-4">
|
||||
<h3 class="mb-0">Email Server</h3>
|
||||
<div class="d-flex">
|
||||
<div class="dropdown only-for-js" id="quick-fill">
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="QuickFillDropdownToggle">
|
||||
Quick Fill
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="QuickFillDropdownToggle">
|
||||
<a class="dropdown-item" href="" data-server="smtp.gmail.com" data-port="587">Gmail.com</a>
|
||||
<a class="dropdown-item" href="" data-server="mail.yahoo.com" data-port="587">Yahoo.com</a>
|
||||
<a class="dropdown-item" href="" data-server="smtp.mailgun.org" data-port="587">Mailgun</a>
|
||||
<a class="dropdown-item" href="" data-server="smtp.office365.com" data-port="587">Office365</a>
|
||||
<a class="dropdown-item" href="" data-server="smtp.sendgrid.net" data-port="587">SendGrid</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" autocomplete="off">
|
||||
<div class="row">
|
||||
<div class="col-xl-10 col-xxl-constrain">
|
||||
|
@ -30,8 +8,22 @@
|
|||
<div asp-validation-summary="All"></div>
|
||||
}
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.Server" class="form-label">SMTP Server</label>
|
||||
<input asp-for="Settings.Server" data-fill="server" class="form-control"/>
|
||||
<div class="d-flex flex-wrap gap-2 align-items-center justify-content-between">
|
||||
<label asp-for="Settings.Server" class="form-label">SMTP Server</label>
|
||||
<div class="dropdown only-for-js mt-n2" id="quick-fill">
|
||||
<button class="btn btn-link p-0 dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="QuickFillDropdownToggle">
|
||||
Quick Fill
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="QuickFillDropdownToggle">
|
||||
<a class="dropdown-item" href="" data-server="smtp.gmail.com" data-port="587">Gmail.com</a>
|
||||
<a class="dropdown-item" href="" data-server="mail.yahoo.com" data-port="587">Yahoo.com</a>
|
||||
<a class="dropdown-item" href="" data-server="smtp.mailgun.org" data-port="587">Mailgun</a>
|
||||
<a class="dropdown-item" href="" data-server="smtp.office365.com" data-port="587">Office365</a>
|
||||
<a class="dropdown-item" href="" data-server="smtp.sendgrid.net" data-port="587">SendGrid</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input asp-for="Settings.Server" data-fill="server" class="form-control" />
|
||||
<span asp-validation-for="Settings.Server" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -53,7 +45,6 @@
|
|||
<div class="form-group">
|
||||
@if (!Model.PasswordSet)
|
||||
{
|
||||
|
||||
<label asp-for="Settings.Password" class="form-label"></label>
|
||||
<input asp-for="Settings.Password" type="password" class="form-control"/>
|
||||
<span asp-validation-for="Settings.Password" class="text-danger"></span>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
ViewData.SetActivePage(ServerNavPages.Emails, "Emails");
|
||||
}
|
||||
|
||||
<h3 class="mb-4">Email Server</h3>
|
||||
<partial name="EmailsBody" model="Model" />
|
||||
|
||||
@section PageFootContent {
|
||||
|
|
|
@ -113,6 +113,18 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-5">
|
||||
<h4 class="mb-3">Email Settings</h4>
|
||||
<div class="form-check my-3">
|
||||
<input asp-for="EnableStoresToUseServerEmailSettings" type="checkbox" class="form-check-input"/>
|
||||
<label asp-for="EnableStoresToUseServerEmailSettings" class="form-check-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Notifications/#server-emails" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="info" />
|
||||
</a>
|
||||
<span asp-validation-for="EnableStoresToUseServerEmailSettings" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-5">
|
||||
<h4 class="mb-3">Notification Settings</h4>
|
||||
<div class="form-check my-3">
|
||||
|
@ -123,14 +135,6 @@
|
|||
</a>
|
||||
<span asp-validation-for="DisableInstantNotifications" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check my-3">
|
||||
<input asp-for="DisableStoresToUseServerEmailSettings" type="checkbox" class="form-check-input"/>
|
||||
<label asp-for="DisableStoresToUseServerEmailSettings" class="form-check-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Notifications/#server-emails" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="info" />
|
||||
</a>
|
||||
<span asp-validation-for="DisableStoresToUseServerEmailSettings" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-5">
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.Emails, "Emails", Context.GetStoreData().Id);
|
||||
var hasCustomSettings = Model.IsSetup() && !Model.UsesFallback();
|
||||
}
|
||||
|
||||
<div class="row mb-4">
|
||||
|
@ -19,7 +20,25 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<partial name="EmailsBody" model="Model" />
|
||||
<h3 class="mb-4">Email Server</h3>
|
||||
@if (Model.IsFallbackSetup())
|
||||
{
|
||||
<label class="d-flex align-items-center mb-4">
|
||||
<input type="checkbox" id="UseCustomSMTP" checked="@hasCustomSettings" class="btcpay-toggle me-3" data-bs-toggle="collapse" data-bs-target="#SmtpSettings" aria-expanded="@hasCustomSettings" aria-controls="SmtpSettings" />
|
||||
<div>
|
||||
<span>Use custom SMTP settings for this store</span>
|
||||
<div class="form-text">Otherwise, the server's SMTP settings will be used to send emails.</div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div class="checkout-settings collapse @(hasCustomSettings ? "show" : "")" id="SmtpSettings">
|
||||
<partial name="EmailsBody" model="Model" />
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<partial name="EmailsBody" model="Model" />
|
||||
}
|
||||
|
||||
@section PageFootContent {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
|
|
|
@ -117,8 +117,7 @@
|
|||
<code>{Payout.Metadata}*</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td colspan="2">* These fields are JSON objects. You can access properties within them using <a href="https://www.newtonsoft.com/json/help/html/SelectToken.htm#SelectTokenJSONPath" rel="noreferrer noopener" target="_blank">this syntax</a>. One example is <code>{Invoice.Metadata.itemCode}</code>
|
||||
</td></tr>
|
||||
<tr><td colspan="2">* These fields are JSON objects. You can access properties within them using <a href="https://www.newtonsoft.com/json/help/html/SelectToken.htm#SelectTokenJSONPath" rel="noreferrer noopener" target="_blank">this syntax</a>. One example is <code>{Invoice.Metadata.itemCode}</code></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Reference in a new issue