Policies: Cleanup and improvements (#5731)

* Policies: Turn checkboxes into toggles

* Move email policy to server email settings

* Move maintenance policies to server maintenance settings

* Policies: Adjust spacings

* Policies: Remove DisableInstantNotifications setting

* Wording updates

* Move maintenance settings back
This commit is contained in:
d11n
2024-02-21 14:43:44 +01:00
committed by GitHub
parent 55a8ba0905
commit 9c95b98f3a
13 changed files with 356 additions and 311 deletions

View File

@@ -2332,7 +2332,7 @@ namespace BTCPayServer.Tests
var setting = await ctx.Settings.FirstOrDefaultAsync(c => c.Id == id); var setting = await ctx.Settings.FirstOrDefaultAsync(c => c.Id == id);
if (setting != null) ctx.Settings.Remove(setting); if (setting != null) ctx.Settings.Remove(setting);
// create legacy policies setting that needs migration // create legacy policies setting that needs migration
setting = new SettingData { Id = id, Value = JObject.Parse("{\"RootAppId\": null, \"RootAppType\": 1, \"Experimental\": false, \"PluginSource\": null, \"LockSubscription\": false, \"DisableSSHService\": false, \"PluginPreReleases\": false, \"BlockExplorerLinks\": [],\"DomainToAppMapping\": [{\"AppId\": \"87kj5yKay8mB4UUZcJhZH5TqDKMD3CznjwLjiu1oYZXe\", \"Domain\": \"donate.nicolas-dorier.com\", \"AppType\": 0}], \"CheckForNewVersions\": false, \"AllowHotWalletForAll\": false, \"RequiresConfirmedEmail\": false, \"DiscourageSearchEngines\": false, \"DisableInstantNotifications\": false, \"DisableNonAdminCreateUserApi\": false, \"AllowHotWalletRPCImportForAll\": false, \"AllowLightningInternalNodeForAll\": false, \"DisableStoresToUseServerEmailSettings\": false}").ToString() }; setting = new SettingData { Id = id, Value = JObject.Parse("{\"RootAppId\": null, \"RootAppType\": 1, \"Experimental\": false, \"PluginSource\": null, \"LockSubscription\": false, \"DisableSSHService\": false, \"PluginPreReleases\": false, \"BlockExplorerLinks\": [],\"DomainToAppMapping\": [{\"AppId\": \"87kj5yKay8mB4UUZcJhZH5TqDKMD3CznjwLjiu1oYZXe\", \"Domain\": \"donate.nicolas-dorier.com\", \"AppType\": 0}], \"CheckForNewVersions\": false, \"AllowHotWalletForAll\": false, \"RequiresConfirmedEmail\": false, \"DiscourageSearchEngines\": false, \"DisableNonAdminCreateUserApi\": false, \"AllowHotWalletRPCImportForAll\": false, \"AllowLightningInternalNodeForAll\": false, \"DisableStoresToUseServerEmailSettings\": false}").ToString() };
ctx.Settings.Add(setting); ctx.Settings.Add(setting);
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }

View File

@@ -122,21 +122,24 @@ namespace BTCPayServer.Controllers
_transactionLinkProviders = transactionLinkProviders; _transactionLinkProviders = transactionLinkProviders;
} }
[Route("server/maintenance")] [HttpGet("server/maintenance")]
public IActionResult Maintenance() public IActionResult Maintenance()
{ {
MaintenanceViewModel vm = new MaintenanceViewModel(); var vm = new MaintenanceViewModel
vm.CanUseSSH = _sshState.CanUseSSH; {
CanUseSSH = _sshState.CanUseSSH,
DNSDomain = Request.Host.Host
};
if (!vm.CanUseSSH) if (!vm.CanUseSSH)
TempData[WellKnownTempData.ErrorMessage] = "Maintenance feature requires access to SSH properly configured in BTCPay Server configuration."; TempData[WellKnownTempData.ErrorMessage] = "Maintenance feature requires access to SSH properly configured in BTCPay Server configuration.";
vm.DNSDomain = this.Request.Host.Host;
if (IPAddress.TryParse(vm.DNSDomain, out var unused)) if (IPAddress.TryParse(vm.DNSDomain, out var unused))
vm.DNSDomain = null; vm.DNSDomain = null;
return View(vm); return View(vm);
} }
[Route("server/maintenance")] [HttpPost("server/maintenance")]
[HttpPost]
public async Task<IActionResult> Maintenance(MaintenanceViewModel vm, string command) public async Task<IActionResult> Maintenance(MaintenanceViewModel vm, string command)
{ {
vm.CanUseSSH = _sshState.CanUseSSH; vm.CanUseSSH = _sshState.CanUseSSH;
@@ -302,8 +305,8 @@ namespace BTCPayServer.Controllers
[Route("server/policies")] [Route("server/policies")]
public async Task<IActionResult> Policies() public async Task<IActionResult> Policies()
{ {
ViewBag.AppsList = await GetAppSelectList();
ViewBag.UpdateUrlPresent = _Options.UpdateUrl != null; ViewBag.UpdateUrlPresent = _Options.UpdateUrl != null;
ViewBag.AppsList = await GetAppSelectList();
return View(_policiesSettings); return View(_policiesSettings);
} }
@@ -1134,13 +1137,17 @@ namespace BTCPayServer.Controllers
[Route("server/emails")] [Route("server/emails")]
public async Task<IActionResult> Emails() public async Task<IActionResult> Emails()
{ {
var data = (await _SettingsRepository.GetSettingAsync<EmailSettings>()) ?? new EmailSettings(); var email = await _SettingsRepository.GetSettingAsync<EmailSettings>() ?? new EmailSettings();
return View(new EmailsViewModel(data)); var vm = new ServerEmailsViewModel(email)
{
EnableStoresToUseServerEmailSettings = !_policiesSettings.DisableStoresToUseServerEmailSettings
};
return View(vm);
} }
[Route("server/emails")] [Route("server/emails")]
[HttpPost] [HttpPost]
public async Task<IActionResult> Emails(EmailsViewModel model, string command) public async Task<IActionResult> Emails(ServerEmailsViewModel model, string command)
{ {
if (command == "Test") if (command == "Test")
{ {
@@ -1170,6 +1177,13 @@ namespace BTCPayServer.Controllers
} }
return View(model); return View(model);
} }
if (_policiesSettings.DisableStoresToUseServerEmailSettings == model.EnableStoresToUseServerEmailSettings)
{
_policiesSettings.DisableStoresToUseServerEmailSettings = !model.EnableStoresToUseServerEmailSettings;
await _SettingsRepository.UpdateSetting(_policiesSettings);
}
if (command == "ResetPassword") if (command == "ResetPassword")
{ {
var settings = await _SettingsRepository.GetSettingAsync<EmailSettings>() ?? new EmailSettings(); var settings = await _SettingsRepository.GetSettingAsync<EmailSettings>() ?? new EmailSettings();
@@ -1178,22 +1192,22 @@ namespace BTCPayServer.Controllers
TempData[WellKnownTempData.SuccessMessage] = "Email server password reset"; TempData[WellKnownTempData.SuccessMessage] = "Email server password reset";
return RedirectToAction(nameof(Emails)); return RedirectToAction(nameof(Emails));
} }
else // if (command == "Save")
// save
if (model.Settings.From is not null && !MailboxAddressValidator.IsMailboxAddress(model.Settings.From))
{ {
if (model.Settings.From is not null && !MailboxAddressValidator.IsMailboxAddress(model.Settings.From)) ModelState.AddModelError("Settings.From", "Invalid email");
{ return View(model);
ModelState.AddModelError("Settings.From", "Invalid email");
return View(model);
}
var oldSettings = await _SettingsRepository.GetSettingAsync<EmailSettings>() ?? new EmailSettings();
if (new EmailsViewModel(oldSettings).PasswordSet)
{
model.Settings.Password = oldSettings.Password;
}
await _SettingsRepository.UpdateSetting(model.Settings);
TempData[WellKnownTempData.SuccessMessage] = "Email settings saved";
return RedirectToAction(nameof(Emails));
} }
var oldSettings = await _SettingsRepository.GetSettingAsync<EmailSettings>() ?? new EmailSettings();
if (new EmailsViewModel(oldSettings).PasswordSet)
{
model.Settings.Password = oldSettings.Password;
}
await _SettingsRepository.UpdateSetting(model.Settings);
TempData[WellKnownTempData.SuccessMessage] = "Email settings saved";
return RedirectToAction(nameof(Emails));
} }
[Route("server/logs/{file?}")] [Route("server/logs/{file?}")]

View File

@@ -7,11 +7,9 @@ using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants; using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions; using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models; using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client.Models;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.Models.ServerViewModels; using BTCPayServer.Models;
using BTCPayServer.Services.Mails; using BTCPayServer.Services.Mails;
using BTCPayServer.Validation;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MimeKit; using MimeKit;

View File

@@ -0,0 +1,31 @@
using System.ComponentModel.DataAnnotations;
using BTCPayServer.Services.Mails;
using BTCPayServer.Validation;
namespace BTCPayServer.Models;
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, EmailSettings fallbackSettings = null)
{
Settings = settings;
FallbackSettings = fallbackSettings;
PasswordSet = !string.IsNullOrEmpty(settings?.Password);
}
public bool IsSetup() => Settings?.IsComplete() is true;
public bool IsFallbackSetup() => FallbackSettings?.IsComplete() is true;
public bool UsesFallback() => IsFallbackSetup() && Settings == FallbackSettings;
}

View File

@@ -1,32 +1,18 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using BTCPayServer.Services.Mails; using BTCPayServer.Services.Mails;
using BTCPayServer.Validation;
namespace BTCPayServer.Models.ServerViewModels namespace BTCPayServer.Models.ServerViewModels;
public class ServerEmailsViewModel : EmailsViewModel
{ {
public class EmailsViewModel [Display(Name = "Allow Stores use the Server's SMTP email settings as their default")]
public bool EnableStoresToUseServerEmailSettings { get; set; }
public ServerEmailsViewModel()
{ {
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 ServerEmailsViewModel(EmailSettings settings) : base(settings)
{ {
}
public EmailsViewModel(EmailSettings settings, EmailSettings fallbackSettings = null)
{
Settings = settings;
FallbackSettings = fallbackSettings;
PasswordSet = !string.IsNullOrEmpty(settings?.Password);
}
public bool IsSetup() => Settings?.IsComplete() is true;
public bool IsFallbackSetup() => FallbackSettings?.IsComplete() is true;
public bool UsesFallback() => IsFallbackSetup() && Settings == FallbackSettings;
} }
} }

View File

@@ -1,11 +1,10 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace BTCPayServer.Models.ServerViewModels namespace BTCPayServer.Models.ServerViewModels;
public class MaintenanceViewModel
{ {
public class MaintenanceViewModel [Display(Name = "Domain name")]
{ public string DNSDomain { get; set; }
[Display(Name = "Change domain")] public bool CanUseSSH { get; internal set; }
public string DNSDomain { get; set; }
public bool CanUseSSH { get; internal set; }
}
} }

View File

@@ -8,15 +8,15 @@ namespace BTCPayServer.Services
{ {
public class PoliciesSettings public class PoliciesSettings
{ {
[Display(Name = "Require a confirmation email for registering")] [Display(Name = "Email confirmation required")]
public bool RequiresConfirmedEmail { get; set; } public bool RequiresConfirmedEmail { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[Display(Name = "Disable new user registration on the server")] [Display(Name = "Disable public user registration")]
public bool LockSubscription { get; set; } public bool LockSubscription { get; set; }
[JsonIgnore] [JsonIgnore]
[Display(Name = "Enable new user registration on the server")] [Display(Name = "Enable public user registration")]
public bool EnableRegistration public bool EnableRegistration
{ {
get => !LockSubscription; get => !LockSubscription;
@@ -24,37 +24,41 @@ namespace BTCPayServer.Services
} }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[Display(Name = "Require new users to be approved by an admin after registration")] [Display(Name = "Admin must approve new users")]
public bool RequiresUserApproval { get; set; } public bool RequiresUserApproval { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[Display(Name = "Discourage search engines from indexing this site")] [Display(Name = "Discourage search engines from indexing this site")]
public bool DiscourageSearchEngines { get; set; } public bool DiscourageSearchEngines { get; set; }
[Display(Name = "Allow non-admins to use the internal lightning node in their stores")]
[JsonIgnore]
[Display(Name = "Search engines can index this site")]
public bool AllowSearchEngines
{
get => !DiscourageSearchEngines;
set { DiscourageSearchEngines = !value; }
}
[Display(Name = "Non-admins can use the Internal Lightning Node for their Store")]
public bool AllowLightningInternalNodeForAll { get; set; } public bool AllowLightningInternalNodeForAll { get; set; }
[Display(Name = "Allow non-admins to create hot wallets for their stores")]
[Display(Name = "Non-admins can create Hot Wallets for their Store")]
public bool AllowHotWalletForAll { get; set; } public bool AllowHotWalletForAll { get; set; }
[Display(Name = "Allow non-admins to import hot wallets for their stores")]
[Display(Name = "Non-admins can import Hot Wallets for their Store")]
public bool AllowHotWalletRPCImportForAll { get; set; } public bool AllowHotWalletRPCImportForAll { get; set; }
[Display(Name = "Check releases on GitHub and notify when new BTCPay Server version is available")] [Display(Name = "Check releases on GitHub and notify when new BTCPay Server version is available")]
public bool CheckForNewVersions { get; set; } public bool CheckForNewVersions { get; set; }
[Display(Name = "Disable notifications from automatically showing (no websockets)")]
public bool DisableInstantNotifications { get; set; }
[Display(Name = "Disable stores from using the server's email settings as backup")] [Display(Name = "Disable stores from using the server's email settings as backup")]
public bool DisableStoresToUseServerEmailSettings { get; set; } 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")] [Display(Name = "Non-admins cannot access the User Creation API Endpoint")]
public bool DisableNonAdminCreateUserApi { get; set; } public bool DisableNonAdminCreateUserApi { get; set; }
[JsonIgnore] [JsonIgnore]
[Display(Name = "Non-admins can access the user creation API endpoint")] [Display(Name = "Non-admins can access the User Creation API Endpoint")]
public bool EnableNonAdminCreateUserApi public bool EnableNonAdminCreateUserApi
{ {
get => !DisableNonAdminCreateUserApi; get => !DisableNonAdminCreateUserApi;
@@ -65,6 +69,7 @@ namespace BTCPayServer.Services
[UriAttribute] [UriAttribute]
[Display(Name = "Plugin server")] [Display(Name = "Plugin server")]
public string PluginSource { get; set; } public string PluginSource { get; set; }
[Display(Name = "Show plugins in pre-release")] [Display(Name = "Show plugins in pre-release")]
public bool PluginPreReleases { get; set; } public bool PluginPreReleases { get; set; }

View File

@@ -1,98 +1,96 @@
@model BTCPayServer.Models.ServerViewModels.EmailsViewModel @model BTCPayServer.Models.EmailsViewModel
<form method="post" autocomplete="off"> <div class="row">
<div class="row"> <div class="col-xl-10 col-xxl-constrain">
<div class="col-xl-10 col-xxl-constrain"> @if (!ViewContext.ModelState.IsValid)
@if (!ViewContext.ModelState.IsValid) {
<div asp-validation-summary="All"></div>
}
<div class="form-group">
<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">
<label asp-for="Settings.Port" class="form-label"></label>
<input asp-for="Settings.Port" data-fill="port" class="form-control"/>
<span asp-validation-for="Settings.Port" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Settings.From" class="form-label">Sender's Email Address</label>
<input asp-for="Settings.From" class="form-control" placeholder="Firstname Lastname <email@example.com>" />
<span asp-validation-for="Settings.From" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Settings.Login" class="form-label"></label>
<input asp-for="Settings.Login" class="form-control"/>
<div class="form-text">For many email providers (like Gmail) your login is your email address.</div>
<span asp-validation-for="Settings.Login" class="text-danger"></span>
</div>
<div class="form-group">
@if (!Model.PasswordSet)
{ {
<div asp-validation-summary="All"></div> <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>
} }
<div class="form-group"> else
<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> <label asp-for="Settings.Password" class="form-label"></label>
<div class="dropdown only-for-js mt-n2" id="quick-fill"> <div class="input-group">
<button class="btn btn-link p-0 dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="QuickFillDropdownToggle"> <input value="Configured" type="text" readonly class="form-control"/>
Quick Fill <button type="submit" class="btn btn-danger" name="command" value="ResetPassword" id="ResetPassword">Reset</button>
</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>
<input asp-for="Settings.Server" data-fill="server" class="form-control" /> }
<span asp-validation-for="Settings.Server" class="text-danger"></span> </div>
</div> <input asp-for="PasswordSet" type="hidden"/>
<div class="form-group"> <div class="my-4">
<label asp-for="Settings.Port" class="form-label"></label> <button class="d-inline-flex align-items-center btn btn-link text-primary fw-semibold p-0" type="button" id="AdvancedSettingsButton" data-bs-toggle="collapse" data-bs-target="#AdvancedSettings" aria-expanded="false" aria-controls="AdvancedSettings">
<input asp-for="Settings.Port" data-fill="port" class="form-control"/> <vc:icon symbol="caret-down"/>
<span asp-validation-for="Settings.Port" class="text-danger"></span> <span class="ms-1">Advanced settings</span>
</div> </button>
<div class="form-group"> <div id="AdvancedSettings" class="collapse">
<label asp-for="Settings.From" class="form-label">Sender's Email Address</label> <div class="pt-3 pb-1">
<input asp-for="Settings.From" class="form-control" placeholder="Firstname Lastname <email@example.com>" /> <div class="form-group">
<span asp-validation-for="Settings.From" class="text-danger"></span> <div class="form-check">
</div> <input asp-for="Settings.DisableCertificateCheck" class="form-check-input" />
<div class="form-group"> <label asp-for="Settings.DisableCertificateCheck" class="form-check-label">Disable TLS certificate security checks</label>
<label asp-for="Settings.Login" class="form-label"></label>
<input asp-for="Settings.Login" class="form-control"/>
<div class="form-text">For many email providers (like Gmail) your login is your email address.</div>
<span asp-validation-for="Settings.Login" class="text-danger"></span>
</div>
<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>
}
else
{
<label asp-for="Settings.Password" class="form-label"></label>
<div class="input-group">
<input value="Configured" type="text" readonly class="form-control"/>
<button type="submit" class="btn btn-danger" name="command" value="ResetPassword" id="ResetPassword">Reset</button>
</div>
}
</div>
<input asp-for="PasswordSet" type="hidden"/>
<div class="my-4">
<button class="d-inline-flex align-items-center btn btn-link text-primary fw-semibold p-0" type="button" id="AdvancedSettingsButton" data-bs-toggle="collapse" data-bs-target="#AdvancedSettings" aria-expanded="false" aria-controls="AdvancedSettings">
<vc:icon symbol="caret-down"/>
<span class="ms-1">Advanced settings</span>
</button>
<div id="AdvancedSettings" class="collapse">
<div class="pt-3 pb-1">
<div class="form-group">
<div class="form-check">
<input asp-for="Settings.DisableCertificateCheck" class="form-check-input" />
<label asp-for="Settings.DisableCertificateCheck" class="form-check-label">Disable TLS certificate security checks</label>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<button type="submit" class="btn btn-primary mt-2" name="command" value="Save" id="Save">Save</button>
</div> </div>
<button type="submit" class="btn btn-primary mt-2" name="command" value="Save" id="Save">Save</button>
</div> </div>
<div class="row"> </div>
<div class="col-xl-8 col-xxl-constrain"> <div class="row">
<div class="form-group"> <div class="col-xl-10 col-xxl-constrain">
<h3 class="mt-5 mb-3">Testing</h3> <div class="form-group">
<p> <h3 class="mt-5 mb-3">Testing</h3>
To test your settings, enter an email address below. <p>
</p> To test your settings, enter an email address below.
<label asp-for="TestEmail" class="form-label"></label> </p>
<input asp-for="TestEmail" placeholder="Firstname Lastname <email@example.com>" class="form-control" /> <label asp-for="TestEmail" class="form-label"></label>
<span asp-validation-for="TestEmail" class="text-danger"></span> <input asp-for="TestEmail" placeholder="Firstname Lastname <email@example.com>" class="form-control" />
</div> <span asp-validation-for="TestEmail" class="text-danger"></span>
<button type="submit" class="btn btn-secondary mt-2" name="command" value="Test" id="Test">Send Test Email</button>
</div> </div>
<button type="submit" class="btn btn-secondary mt-2" name="command" value="Test" id="Test">Send Test Email</button>
</div> </div>
</form> </div>
<script> <script>
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {

View File

@@ -4,15 +4,10 @@
@inject BTCPayServer.Services.BTCPayServerEnvironment _env @inject BTCPayServer.Services.BTCPayServerEnvironment _env
@inject SignInManager<ApplicationUser> _signInManager @inject SignInManager<ApplicationUser> _signInManager
@inject UserManager<ApplicationUser> _userManager @inject UserManager<ApplicationUser> _userManager
@inject BTCPayServer.Services.PoliciesSettings PoliciesSettings
@{ @{
var notificationDisabled = PoliciesSettings.DisableInstantNotifications; var user = await _userManager.GetUserAsync(User);
if (!notificationDisabled) var notificationDisabled = user?.DisabledNotifications == "all";
{
var user = await _userManager.GetUserAsync(User);
notificationDisabled = user?.DisabledNotifications == "all";
}
var expectedScheme = _context.HttpContext.Request.Scheme; var expectedScheme = _context.HttpContext.Request.Scheme;
var expectedHost = _context.HttpContext.Request.Host.ToString().ToLower(); var expectedHost = _context.HttpContext.Request.Host.ToString().ToLower();
} }

View File

@@ -1,10 +1,26 @@
@model EmailsViewModel @model ServerEmailsViewModel
@{ @{
ViewData.SetActivePage(ServerNavPages.Emails, "Emails"); ViewData.SetActivePage(ServerNavPages.Emails, "Emails");
} }
<h3 class="mb-4">Email Server</h3> <h3 class="mb-4">Email Server</h3>
<partial name="EmailsBody" model="Model" /> <form method="post" autocomplete="off">
<div class="form-group" style="margin:1rem 0 2rem">
<div class="d-flex align-items-center">
<input asp-for="EnableStoresToUseServerEmailSettings" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="EnableStoresToUseServerEmailSettings" class="form-label mb-0"></label>
<div class="text-muted">
This can be overridden at the Store level.
<a href="https://docs.btcpayserver.org/Notifications/#server-emails" target="_blank" rel="noreferrer noopener">
<vc:icon symbol="info" />
</a>
</div>
</div>
</div>
</div>
<partial name="EmailsBody" model="Model" />
</form>
@section PageFootContent { @section PageFootContent {
<partial name="_ValidationScriptsPartial" /> <partial name="_ValidationScriptsPartial" />

View File

@@ -3,18 +3,17 @@
ViewData.SetActivePage(ServerNavPages.Maintenance, "Maintenance"); ViewData.SetActivePage(ServerNavPages.Maintenance, "Maintenance");
} }
<h3 class="mb-4">@ViewData["Title"]</h3> <h3 class="mb-3">@ViewData["Title"]</h3>
<form method="post"> <form method="post">
<div class="row mb-5"> <div class="row mb-5">
<div class="col-xl-8 col-xxl-constrain"> <div class="col-xl-8 col-xxl-constrain">
<h4 class="mb-2">Domain name</h4> <div class="form-group">
<p>You can change the domain name of your server by following <a href="https://docs.btcpayserver.org/FAQ/Deployment/#how-to-change-your-btcpay-server-domain-name" target="_blank" rel="noreferrer noopener">this guide</a>.</p> <label asp-for="DNSDomain" class="form-label"></label>
<input asp-for="DNSDomain" class="form-control" disabled="@(Model.CanUseSSH ? null : "disabled")" />
<div class="d-flex"> <div class="form-text">You can change the domain name of your server by following <a href="https://docs.btcpayserver.org/FAQ/Deployment/#how-to-change-your-btcpay-server-domain-name" target="_blank" rel="noreferrer noopener">this guide</a>.</div>
<input asp-for="DNSDomain" class="form-control flex-fill" disabled="@(Model.CanUseSSH ? null : "disabled")" />
<span asp-validation-for="DNSDomain" class="text-danger"></span> <span asp-validation-for="DNSDomain" class="text-danger"></span>
<button name="command" type="submit" class="btn btn-secondary ms-3" value="changedomain" title="Change domain" disabled="@(Model.CanUseSSH ? null : "disabled")"> <button name="command" type="submit" class="btn btn-secondary mt-3" value="changedomain" title="Change domain" disabled="@(Model.CanUseSSH ? null : "disabled")">
Confirm Confirm
</button> </button>
</div> </div>

View File

@@ -11,10 +11,11 @@
@section PageHeadContent { @section PageHeadContent {
<style> <style>
input[type="checkbox"] ~ .info-note, input[type="checkbox"] ~ div > .info-note,
input[type="checkbox"] ~ .subsettings { display: none; } input[type="checkbox"] ~ div > .subsettings { display: none; }
input[type="checkbox"]:checked ~ .info-note { display: flex; max-width: 44em; } input[type="checkbox"]:checked ~ div > .info-note { display: flex; max-width: 44em; }
input[type="checkbox"]:checked ~ .subsettings { display: block; } input[type="checkbox"]:checked ~ div > .subsettings { display: block; }
.subsettings > :last-child { margin-bottom: 0 !important; }
</style> </style>
} }
@@ -29,136 +30,136 @@
<div class="col-xl-10 col-xxl-constrain"> <div class="col-xl-10 col-xxl-constrain">
<form method="post" class="d-flex flex-column"> <form method="post" class="d-flex flex-column">
<div class="form-group mb-5"> <div class="form-group mb-5">
<h4 class="mb-3">Registration Settings</h4> <h4 class="mb-3">Registration</h4>
<div class="form-check my-3"> <div class="d-flex gap-3">
<input asp-for="EnableRegistration" type="checkbox" class="form-check-input"/> <input asp-for="EnableRegistration" type="checkbox" class="btcpay-toggle" />
<label asp-for="EnableRegistration" class="form-check-label"></label> <div>
<span asp-validation-for="EnableRegistration" class="text-danger"></span> <label asp-for="EnableRegistration" class="form-label mb-0"></label>
<div class="info-note mt-2 text-warning" role="alert"> <span asp-validation-for="EnableRegistration" class="text-danger"></span>
<vc:icon symbol="warning"/> <div class="info-note mt-2 text-warning" role="alert">
Caution: Enabling public user registration means anyone can register to your server and may expose your BTCPay Server instance to potential security risks from unknown users. <vc:icon symbol="warning" />
</div> Caution: Enabling public user registration means anyone can register to your server and may expose your BTCPay Server instance to potential security risks from unknown users.
<div class="subsettings"> </div>
<div class="form-check my-3"> <div class="subsettings">
@{ <div class="d-flex my-3">
var emailSettings = (await _SettingsRepository.GetSettingAsync<EmailSettings>()) ?? new EmailSettings(); @{
/* The "|| Model.RequiresConfirmedEmail" check is for the case when a user had checked var emailSettings = (await _SettingsRepository.GetSettingAsync<EmailSettings>()) ?? new EmailSettings();
the checkbox without first configuring the e-mail settings so that they can uncheck it. */ /* The "|| Model.RequiresConfirmedEmail" check is for the case when a user had checked
var isEmailConfigured = emailSettings.IsComplete() || Model.RequiresConfirmedEmail; the checkbox without first configuring the e-mail settings so that they can uncheck it. */
} var isEmailConfigured = emailSettings.IsComplete() || Model.RequiresConfirmedEmail;
<input asp-for="RequiresConfirmedEmail" type="checkbox" class="form-check-input" disabled="@(isEmailConfigured ? null : "disabled")"/> }
<label asp-for="RequiresConfirmedEmail" class="form-check-label"></label> <input asp-for="RequiresConfirmedEmail" type="checkbox" class="btcpay-toggle me-3" disabled="@(isEmailConfigured ? null : "disabled")" />
<a href="https://docs.btcpayserver.org/FAQ/ServerSettings/#how-to-allow-registration-on-my-btcpay-server" target="_blank" rel="noreferrer noopener"> <div>
<vc:icon symbol="info" /> <label asp-for="RequiresConfirmedEmail" class="form-label mb-0"></label>
</a> <a href="https://docs.btcpayserver.org/FAQ/ServerSettings/#how-to-allow-registration-on-my-btcpay-server" target="_blank" rel="noreferrer noopener">
<span asp-validation-for="RequiresConfirmedEmail" class="text-danger"></span> <vc:icon symbol="info" />
@if (!isEmailConfigured) </a>
{ <span asp-validation-for="RequiresConfirmedEmail" class="text-danger"></span>
<div class="mb-2"> @if (!isEmailConfigured)
<span class="text-secondary">Your email server has not been configured. <a asp-controller="UIServer" asp-action="Emails">Please configure it first.</a></span> {
<div class="text-secondary">Your email server has not been configured. <a asp-controller="UIServer" asp-action="Emails">Please configure it first.</a></div>
}
</div>
</div>
<div class="d-flex my-3">
<input asp-for="RequiresUserApproval" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="RequiresUserApproval" class="form-label mb-0"></label>
<span asp-validation-for="RequiresUserApproval" class="text-danger"></span>
</div>
</div>
<div class="d-flex my-3">
<input asp-for="EnableNonAdminCreateUserApi" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="EnableNonAdminCreateUserApi" class="form-label mb-0"></label>
<span asp-validation-for="EnableNonAdminCreateUserApi" class="text-danger"></span>
<div class="info-note mt-2 text-warning" role="alert">
<vc:icon symbol="warning" />
Caution: Allowing non-admins to have access to API endpoints may expose your BTCPay Server instance to potential security risks from unknown users.
</div>
</div> </div>
}
</div>
<div class="form-check my-3">
<input asp-for="RequiresUserApproval" type="checkbox" class="form-check-input"/>
<label asp-for="RequiresUserApproval" class="form-check-label"></label>
<span asp-validation-for="RequiresUserApproval" class="text-danger"></span>
</div>
<div class="form-check my-3">
<input asp-for="EnableNonAdminCreateUserApi" type="checkbox" class="form-check-input"/>
<label asp-for="EnableNonAdminCreateUserApi" class="form-check-label"></label>
<span asp-validation-for="EnableNonAdminCreateUserApi" class="text-danger"></span>
<div class="info-note mt-2 text-warning" role="alert">
<vc:icon symbol="warning"/>
Caution: Allowing non-admins to have access to API endpoints may expose your BTCPay Server instance to potential security risks from unknown users.
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="form-group mb-5"> <div class="mb-5">
<h4 class="mb-3">User Settings</h4> <h4 class="mb-3">Users</h4>
<div class="form-check my-3"> <div class="d-flex my-3">
<input asp-for="AllowLightningInternalNodeForAll" type="checkbox" class="form-check-input"/> <input asp-for="AllowLightningInternalNodeForAll" type="checkbox" class="btcpay-toggle me-3"/>
<label asp-for="AllowLightningInternalNodeForAll" class="form-check-label"></label> <div>
<a href="https://docs.btcpayserver.org/FAQ/LightningNetwork/#how-many-users-can-use-lightning-network-in-btcpay" target="_blank" rel="noreferrer noopener"> <label asp-for="AllowLightningInternalNodeForAll" class="form-label mb-0"></label>
<vc:icon symbol="info" /> <a href="https://docs.btcpayserver.org/FAQ/LightningNetwork/#how-many-users-can-use-lightning-network-in-btcpay" target="_blank" rel="noreferrer noopener">
</a> <vc:icon symbol="info" />
<span asp-validation-for="AllowLightningInternalNodeForAll" class="text-danger"></span> </a>
<div class="info-note mt-2 text-warning" role="alert"> <span asp-validation-for="AllowLightningInternalNodeForAll" class="text-danger"></span>
<vc:icon symbol="warning"/> <div class="info-note mt-2 text-warning" role="alert">
Caution: Enabling this option, may simplify the onboarding for third-parties but carries liabilities and security risks associated with sharing the lightning node with other users. <vc:icon symbol="warning"/>
Caution: Enabling this option, may simplify the onboarding for third-parties but carries liabilities and security risks associated with sharing the lightning node with other users.
</div>
</div> </div>
</div> </div>
<div class="form-check my-3"> <div class="d-flex my-3">
<input asp-for="AllowHotWalletForAll" type="checkbox" class="form-check-input"/> <input asp-for="AllowHotWalletForAll" type="checkbox" class="btcpay-toggle me-3"/>
<label asp-for="AllowHotWalletForAll" class="form-check-label"></label> <div>
<a href="https://docs.btcpayserver.org/CreateWallet/#requirements-to-create-wallets" target="_blank" rel="noreferrer noopener"> <label asp-for="AllowHotWalletForAll" class="form-label mb-0"></label>
<vc:icon symbol="info" /> <a href="https://docs.btcpayserver.org/CreateWallet/#requirements-to-create-wallets" target="_blank" rel="noreferrer noopener">
</a> <vc:icon symbol="info" />
<span asp-validation-for="AllowHotWalletForAll" class="text-danger"></span> </a>
<div class="info-note mt-2 text-warning" role="alert"> <span asp-validation-for="AllowHotWalletForAll" class="text-danger"></span>
<vc:icon symbol="warning"/> <div class="info-note mt-2 text-warning" role="alert">
Caution: Enabling this option, may simplify the onboarding and spending for third-parties but carries liabilities and security risks associated to storing private keys of third parties on a server. <vc:icon symbol="warning"/>
Caution: Enabling this option, may simplify the onboarding and spending for third-parties but carries liabilities and security risks associated to storing private keys of third parties on a server.
</div>
</div> </div>
</div> </div>
<div class="form-check my-3"> <div class="d-flex my-3">
<input asp-for="AllowHotWalletRPCImportForAll" type="checkbox" class="form-check-input"/> <input asp-for="AllowHotWalletRPCImportForAll" type="checkbox" class="btcpay-toggle me-3"/>
<label asp-for="AllowHotWalletRPCImportForAll" class="form-check-label"></label> <div>
<span asp-validation-for="AllowHotWalletRPCImportForAll" class="text-danger"></span> <label asp-for="AllowHotWalletRPCImportForAll" class="form-label mb-0"></label>
<div class="info-note mt-2 text-warning" role="alert"> <span asp-validation-for="AllowHotWalletRPCImportForAll" class="text-danger"></span>
<vc:icon symbol="warning"/> <div class="info-note mt-2 text-warning" role="alert">
Caution: Enabling this option, may simplify the onboarding and spending for third-parties but carries liabilities and security risks associated to storing private keys of third parties on a server. <vc:icon symbol="warning"/>
Caution: Enabling this option, may simplify the onboarding and spending for third-parties but carries liabilities and security risks associated to storing private keys of third parties on a server.
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="form-group mb-5"> <div class="mb-5">
<h4 class="mb-3">Email Settings</h4> <h4 class="mb-3">Server</h4>
<div class="form-check my-3"> <div class="d-flex my-3">
<input asp-for="EnableStoresToUseServerEmailSettings" type="checkbox" class="form-check-input"/> <input asp-for="AllowSearchEngines" type="checkbox" class="btcpay-toggle me-3"/>
<label asp-for="EnableStoresToUseServerEmailSettings" class="form-check-label"></label> <div>
<a href="https://docs.btcpayserver.org/Notifications/#server-emails" target="_blank" rel="noreferrer noopener"> <label asp-for="AllowSearchEngines" class="form-label mb-0"></label>
<vc:icon symbol="info" /> <a href="https://docs.btcpayserver.org/FAQ/ServerSettings/#how-to-hide-my-btcpay-server-from-search-engines" target="_blank" rel="noreferrer noopener">
</a> <vc:icon symbol="info" />
<span asp-validation-for="EnableStoresToUseServerEmailSettings" class="text-danger"></span> </a>
<span asp-validation-for="AllowSearchEngines" class="text-danger"></span>
</div>
</div> </div>
</div>
<div class="form-group mb-5">
<h4 class="mb-3">Notification Settings</h4>
<div class="form-check my-3">
<input asp-for="DisableInstantNotifications" type="checkbox" class="form-check-input"/>
<label asp-for="DisableInstantNotifications" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/Notifications/#notifications" target="_blank" rel="noreferrer noopener">
<vc:icon symbol="info" />
</a>
<span asp-validation-for="DisableInstantNotifications" class="text-danger"></span>
</div>
</div>
<div class="form-group mb-5">
<h4 class="mb-3">Maintenance Settings</h4>
@if (ViewBag.UpdateUrlPresent) @if (ViewBag.UpdateUrlPresent)
{ {
<div class="form-check my-3"> <div class="d-flex my-3">
<input asp-for="CheckForNewVersions" type="checkbox" class="form-check-input"/> <input asp-for="CheckForNewVersions" type="checkbox" class="btcpay-toggle me-3"/>
<label asp-for="CheckForNewVersions" class="form-check-label"></label> <div>
<span asp-validation-for="CheckForNewVersions" class="text-danger"></span> <label asp-for="CheckForNewVersions" class="form-label mb-0"></label>
<span asp-validation-for="CheckForNewVersions" class="text-danger"></span>
</div>
</div> </div>
} }
<div class="form-check my-3"> <div class="d-flex align-items-center my-3">
<input asp-for="DiscourageSearchEngines" type="checkbox" class="form-check-input"/> <input asp-for="Experimental" type="checkbox" class="btcpay-toggle me-3" />
<label asp-for="DiscourageSearchEngines" class="form-check-label"></label> <div>
<a href="https://docs.btcpayserver.org/FAQ/ServerSettings/#how-to-hide-my-btcpay-server-from-search-engines" target="_blank" rel="noreferrer noopener"> <label asp-for="Experimental" class="form-label mb-0"></label>
<vc:icon symbol="info" /> <div class="text-muted">
</a> Will allow you to use the custodian account feature.
<span asp-validation-for="DiscourageSearchEngines" class="text-danger"></span> <a href="https://blog.btcpayserver.org/btcpay-server-1-9-0/#-exchange-integration-via-plugins-" target="_blank" rel="noreferrer noopener">
</div> <vc:icon symbol="info" />
<div class="form-check my-3"> </a>
<input asp-for="Experimental" type="checkbox" class="form-check-input"/> </div>
<label asp-for="Experimental" class="form-check-label"></label> </div>
</div> </div>
</div> </div>
@@ -168,9 +169,9 @@
<input asp-for="PluginSource" placeholder="@PoliciesSettings.DefaultPluginSource" class="form-control"/> <input asp-for="PluginSource" placeholder="@PoliciesSettings.DefaultPluginSource" class="form-control"/>
<span asp-validation-for="PluginSource" class="text-danger"></span> <span asp-validation-for="PluginSource" class="text-danger"></span>
</div> </div>
<div class="form-check"> <div class="d-flex mt-n2">
<input asp-for="PluginPreReleases" type="checkbox" class="form-check-input"/> <input asp-for="PluginPreReleases" type="checkbox" class="btcpay-toggle me-3"/>
<label asp-for="PluginPreReleases" class="form-check-label"></label> <label asp-for="PluginPreReleases" class="form-label mb-0"></label>
</div> </div>
@* To handle the multiple submit buttons on this page: Give this button @* To handle the multiple submit buttons on this page: Give this button
@@ -179,7 +180,7 @@
<button id="SaveButton" type="submit" class="btn btn-primary" name="command" value="Save">Save</button> <button id="SaveButton" type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
</div> </div>
<h4 class="mt-5">Customization Settings</h4> <h4 class="mt-5">Customization</h4>
<div class="form-group mb-5"> <div class="form-group mb-5">
<label asp-for="RootAppId" class="form-label"></label> <label asp-for="RootAppId" class="form-label"></label>
<select asp-for="RootAppId" asp-items="@(new SelectList(ViewBag.AppsList, nameof(SelectListItem.Value), nameof(SelectListItem.Text), Model.RootAppId))" class="form-select"></select> <select asp-for="RootAppId" asp-items="@(new SelectList(ViewBag.AppsList, nameof(SelectListItem.Value), nameof(SelectListItem.Text), Model.RootAppId))" class="form-select"></select>

View File

@@ -1,4 +1,4 @@
@model BTCPayServer.Models.ServerViewModels.EmailsViewModel @model BTCPayServer.Models.EmailsViewModel
@{ @{
Layout = "../Shared/_NavLayout.cshtml"; Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePage(StoreNavPages.Emails, "Emails", Context.GetStoreData().Id); ViewData.SetActivePage(StoreNavPages.Emails, "Emails", Context.GetStoreData().Id);
@@ -21,24 +21,27 @@
</div> </div>
<h3 class="mb-4">Email Server</h3> <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"> <form method="post" autocomplete="off">
@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" /> <partial name="EmailsBody" model="Model" />
</div> }
} </form>
else
{
<partial name="EmailsBody" model="Model" />
}
@section PageFootContent { @section PageFootContent {
<partial name="_ValidationScriptsPartial" /> <partial name="_ValidationScriptsPartial" />