diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index d24ff125f..5455b3686 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -2332,7 +2332,7 @@ namespace BTCPayServer.Tests var setting = await ctx.Settings.FirstOrDefaultAsync(c => c.Id == id); if (setting != null) ctx.Settings.Remove(setting); // 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); await ctx.SaveChangesAsync(); } diff --git a/BTCPayServer/Controllers/UIServerController.cs b/BTCPayServer/Controllers/UIServerController.cs index 71e39b11e..a49cd3a78 100644 --- a/BTCPayServer/Controllers/UIServerController.cs +++ b/BTCPayServer/Controllers/UIServerController.cs @@ -122,21 +122,24 @@ namespace BTCPayServer.Controllers _transactionLinkProviders = transactionLinkProviders; } - [Route("server/maintenance")] + [HttpGet("server/maintenance")] public IActionResult Maintenance() { - MaintenanceViewModel vm = new MaintenanceViewModel(); - vm.CanUseSSH = _sshState.CanUseSSH; + var vm = new MaintenanceViewModel + { + CanUseSSH = _sshState.CanUseSSH, + DNSDomain = Request.Host.Host + }; + if (!vm.CanUseSSH) 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)) vm.DNSDomain = null; + return View(vm); } - [Route("server/maintenance")] - [HttpPost] + [HttpPost("server/maintenance")] public async Task Maintenance(MaintenanceViewModel vm, string command) { vm.CanUseSSH = _sshState.CanUseSSH; @@ -302,8 +305,8 @@ namespace BTCPayServer.Controllers [Route("server/policies")] public async Task Policies() { - ViewBag.AppsList = await GetAppSelectList(); ViewBag.UpdateUrlPresent = _Options.UpdateUrl != null; + ViewBag.AppsList = await GetAppSelectList(); return View(_policiesSettings); } @@ -1134,13 +1137,17 @@ namespace BTCPayServer.Controllers [Route("server/emails")] public async Task Emails() { - var data = (await _SettingsRepository.GetSettingAsync()) ?? new EmailSettings(); - return View(new EmailsViewModel(data)); + var email = await _SettingsRepository.GetSettingAsync() ?? new EmailSettings(); + var vm = new ServerEmailsViewModel(email) + { + EnableStoresToUseServerEmailSettings = !_policiesSettings.DisableStoresToUseServerEmailSettings + }; + return View(vm); } [Route("server/emails")] [HttpPost] - public async Task Emails(EmailsViewModel model, string command) + public async Task Emails(ServerEmailsViewModel model, string command) { if (command == "Test") { @@ -1170,6 +1177,13 @@ namespace BTCPayServer.Controllers } return View(model); } + + if (_policiesSettings.DisableStoresToUseServerEmailSettings == model.EnableStoresToUseServerEmailSettings) + { + _policiesSettings.DisableStoresToUseServerEmailSettings = !model.EnableStoresToUseServerEmailSettings; + await _SettingsRepository.UpdateSetting(_policiesSettings); + } + if (command == "ResetPassword") { var settings = await _SettingsRepository.GetSettingAsync() ?? new EmailSettings(); @@ -1178,22 +1192,22 @@ namespace BTCPayServer.Controllers TempData[WellKnownTempData.SuccessMessage] = "Email server password reset"; 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); - } - var oldSettings = await _SettingsRepository.GetSettingAsync() ?? 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)); + ModelState.AddModelError("Settings.From", "Invalid email"); + return View(model); } + var oldSettings = await _SettingsRepository.GetSettingAsync() ?? 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?}")] diff --git a/BTCPayServer/Controllers/UIStoresController.Email.cs b/BTCPayServer/Controllers/UIStoresController.Email.cs index e1a1520aa..5cfd68b42 100644 --- a/BTCPayServer/Controllers/UIStoresController.Email.cs +++ b/BTCPayServer/Controllers/UIStoresController.Email.cs @@ -7,11 +7,9 @@ using System.Threading.Tasks; using BTCPayServer.Abstractions.Constants; using BTCPayServer.Abstractions.Extensions; using BTCPayServer.Abstractions.Models; -using BTCPayServer.Client.Models; using BTCPayServer.Data; -using BTCPayServer.Models.ServerViewModels; +using BTCPayServer.Models; using BTCPayServer.Services.Mails; -using BTCPayServer.Validation; using Microsoft.AspNetCore.Mvc; using MimeKit; diff --git a/BTCPayServer/Models/EmailsViewModel.cs b/BTCPayServer/Models/EmailsViewModel.cs new file mode 100644 index 000000000..028dda41e --- /dev/null +++ b/BTCPayServer/Models/EmailsViewModel.cs @@ -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; +} diff --git a/BTCPayServer/Models/ServerViewModels/EmailsViewModel.cs b/BTCPayServer/Models/ServerViewModels/EmailsViewModel.cs index c2f4ad35b..f710e6926 100644 --- a/BTCPayServer/Models/ServerViewModels/EmailsViewModel.cs +++ b/BTCPayServer/Models/ServerViewModels/EmailsViewModel.cs @@ -1,32 +1,18 @@ using System.ComponentModel.DataAnnotations; 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 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; + public ServerEmailsViewModel(EmailSettings settings) : base(settings) + { } } diff --git a/BTCPayServer/Models/ServerViewModels/MaintenanceViewModel.cs b/BTCPayServer/Models/ServerViewModels/MaintenanceViewModel.cs index c39050e92..8fd72ebc1 100644 --- a/BTCPayServer/Models/ServerViewModels/MaintenanceViewModel.cs +++ b/BTCPayServer/Models/ServerViewModels/MaintenanceViewModel.cs @@ -1,11 +1,10 @@ using System.ComponentModel.DataAnnotations; -namespace BTCPayServer.Models.ServerViewModels +namespace BTCPayServer.Models.ServerViewModels; + +public class MaintenanceViewModel { - public class MaintenanceViewModel - { - [Display(Name = "Change domain")] - public string DNSDomain { get; set; } - public bool CanUseSSH { get; internal set; } - } + [Display(Name = "Domain name")] + public string DNSDomain { get; set; } + public bool CanUseSSH { get; internal set; } } diff --git a/BTCPayServer/Services/PoliciesSettings.cs b/BTCPayServer/Services/PoliciesSettings.cs index eccb60667..34847dd4d 100644 --- a/BTCPayServer/Services/PoliciesSettings.cs +++ b/BTCPayServer/Services/PoliciesSettings.cs @@ -8,15 +8,15 @@ namespace BTCPayServer.Services { public class PoliciesSettings { - [Display(Name = "Require a confirmation email for registering")] + [Display(Name = "Email confirmation required")] public bool RequiresConfirmedEmail { get; set; } [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] - [Display(Name = "Disable new user registration on the server")] + [Display(Name = "Disable public user registration")] public bool LockSubscription { get; set; } [JsonIgnore] - [Display(Name = "Enable new user registration on the server")] + [Display(Name = "Enable public user registration")] public bool EnableRegistration { get => !LockSubscription; @@ -24,37 +24,41 @@ namespace BTCPayServer.Services } [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; } [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] [Display(Name = "Discourage search engines from indexing this site")] 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; } - [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; } - [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; } + [Display(Name = "Check releases on GitHub and notify when new BTCPay Server version is available")] 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")] 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; } [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 { get => !DisableNonAdminCreateUserApi; @@ -65,6 +69,7 @@ namespace BTCPayServer.Services [UriAttribute] [Display(Name = "Plugin server")] public string PluginSource { get; set; } + [Display(Name = "Show plugins in pre-release")] public bool PluginPreReleases { get; set; } diff --git a/BTCPayServer/Views/Shared/EmailsBody.cshtml b/BTCPayServer/Views/Shared/EmailsBody.cshtml index 593659727..1df37e4e9 100644 --- a/BTCPayServer/Views/Shared/EmailsBody.cshtml +++ b/BTCPayServer/Views/Shared/EmailsBody.cshtml @@ -1,98 +1,96 @@ -@model BTCPayServer.Models.ServerViewModels.EmailsViewModel +@model BTCPayServer.Models.EmailsViewModel -
-
-
- @if (!ViewContext.ModelState.IsValid) +
+
+ @if (!ViewContext.ModelState.IsValid) + { +
+ } +
+
+ + +
+ + +
+
+ + + +
+
+ + + +
+
+ + +
For many email providers (like Gmail) your login is your email address.
+ +
+
+ @if (!Model.PasswordSet) { -
+ + + } -
-
- - + else + { + +
+ +
- - -
-
- - - -
-
- - - -
-
- - -
For many email providers (like Gmail) your login is your email address.
- -
-
- @if (!Model.PasswordSet) - { - - - - } - else - { - -
- - -
- } -
- -
- -
-
-
-
- - -
+ } +
+ +
+ +
+
+
+
+ +
-
+
-
-
-
-

Testing

-

- To test your settings, enter an email address below. -

- - - -
- +
+
+
+
+

Testing

+

+ To test your settings, enter an email address below. +

+ + +
+
- +