Make CSSThemeManager really only focus on theme (#2457)

* Make Settings Repository cache in memory

* Make use of SettingsRepo directly instead of CssThemeManager

* Completely remove CssThemeManager
This commit is contained in:
Andrew Camilleri
2021-07-27 14:08:54 +02:00
committed by GitHub
parent d505771d96
commit 14e4d2d675
33 changed files with 209 additions and 292 deletions

View File

@@ -70,7 +70,7 @@ namespace BTCPayServer.Tests
Assert.Equal(3, invoice.CryptoInfo.Length);
var controller = user.GetController<StoresController>();
var lightningVm = (LightningNodeViewModel)Assert.IsType<ViewResult>(controller.SetupLightningNode(user.StoreId, cryptoCode)).Model;
var lightningVm = (LightningNodeViewModel)Assert.IsType<ViewResult>(await controller.SetupLightningNode(user.StoreId, cryptoCode)).Model;
Assert.True(lightningVm.Enabled);
var response = await controller.SetLightningNodeEnabled(user.StoreId, cryptoCode, false);
Assert.IsType<RedirectToActionResult>(response);

View File

@@ -1065,7 +1065,7 @@ namespace BTCPayServer.Tests
var storeController = user.GetController<StoresController>();
var storeResponse = await storeController.UpdateStore();
Assert.IsType<ViewResult>(storeResponse);
Assert.IsType<ViewResult>(storeController.SetupLightningNode(user.StoreId, "BTC"));
Assert.IsType<ViewResult>(await storeController.SetupLightningNode(user.StoreId, "BTC"));
var testResult = storeController.SetupLightningNode(user.StoreId, new LightningNodeViewModel
{
@@ -3736,8 +3736,8 @@ namespace BTCPayServer.Tests
var settings = tester.PayTester.GetService<SettingsRepository>();
var emailSenderFactory = tester.PayTester.GetService<EmailSenderFactory>();
Assert.Null(await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings());
Assert.Null(await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings());
Assert.Null(await Assert.IsType<ServerEmailSender>(await emailSenderFactory.GetEmailSender()).GetEmailSettings());
Assert.Null(await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings());
await settings.UpdateSetting(new PoliciesSettings() { DisableStoresToUseServerEmailSettings = false });
@@ -3750,12 +3750,12 @@ namespace BTCPayServer.Tests
Server = "admin.com",
EnableSSL = true
});
Assert.Equal("admin@admin.com",(await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
Assert.Equal("admin@admin.com",(await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
Assert.Equal("admin@admin.com",(await Assert.IsType<ServerEmailSender>(await emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
Assert.Equal("admin@admin.com",(await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
await settings.UpdateSetting(new PoliciesSettings() { DisableStoresToUseServerEmailSettings = true });
Assert.Equal("admin@admin.com",(await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
Assert.Null(await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings());
Assert.Equal("admin@admin.com",(await Assert.IsType<ServerEmailSender>(await emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
Assert.Null(await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings());
Assert.IsType<RedirectToActionResult>(await acc.GetController<StoresController>().Emails(acc.StoreId, new EmailsViewModel(new EmailSettings()
{
@@ -3767,7 +3767,7 @@ namespace BTCPayServer.Tests
EnableSSL = true
}), ""));
Assert.Equal("store@store.com",(await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
Assert.Equal("store@store.com",(await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
}
}

View File

@@ -1,8 +1,11 @@
@inject LinkGenerator linkGenerator
@inject UserManager<ApplicationUser> UserManager
@inject CssThemeManager CssThemeManager
@inject ISettingsRepository SettingsRepository
@using BTCPayServer.HostedServices
@using BTCPayServer.Views.Notifications
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Client
@using BTCPayServer.Services
@model BTCPayServer.Components.NotificationsDropdown.NotificationSummaryViewModel
@addTagHelper *, BundlerMinifier.TagHelpers
@@ -53,7 +56,7 @@ else
</li>
}
@{
var disabled = CssThemeManager.Policies.DisableInstantNotifications;
var disabled = (await SettingsRepository.GetPolicies()).DisableInstantNotifications;
if (!disabled)
{
var user = await UserManager.GetUserAsync(User);

View File

@@ -202,10 +202,5 @@ namespace BTCPayServer.Controllers
{
return _UserManager.GetUserId(User);
}
private async Task<bool> IsEmailConfigured(string storeId)
{
return (await (_emailSenderFactory.GetEmailSender(storeId) as EmailSender)?.GetEmailSettings())?.IsComplete() is true;
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Configuration;
@@ -26,11 +27,10 @@ namespace BTCPayServer.Controllers.GreenField
public InternalLightningNodeApiController(
BTCPayNetworkProvider btcPayNetworkProvider, BTCPayServerEnvironment btcPayServerEnvironment,
CssThemeManager cssThemeManager, LightningClientFactoryService lightningClientFactory,
BTCPayNetworkProvider btcPayNetworkProvider, ISettingsRepository settingsRepository, LightningClientFactoryService lightningClientFactory,
IOptions<LightningNetworkOptions> lightningNetworkOptions,
IAuthorizationService authorizationService) : base(
btcPayNetworkProvider, btcPayServerEnvironment, cssThemeManager, authorizationService)
btcPayNetworkProvider, settingsRepository, authorizationService)
{
_btcPayNetworkProvider = btcPayNetworkProvider;
_lightningClientFactory = lightningClientFactory;

View File

@@ -1,6 +1,7 @@
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Configuration;
@@ -31,9 +32,9 @@ namespace BTCPayServer.Controllers.GreenField
public StoreLightningNodeApiController(
IOptions<LightningNetworkOptions> lightningNetworkOptions,
LightningClientFactoryService lightningClientFactory, BTCPayNetworkProvider btcPayNetworkProvider,
BTCPayServerEnvironment btcPayServerEnvironment, CssThemeManager cssThemeManager,
ISettingsRepository settingsRepository,
IAuthorizationService authorizationService) : base(
btcPayNetworkProvider, btcPayServerEnvironment, cssThemeManager, authorizationService)
btcPayNetworkProvider, settingsRepository, authorizationService)
{
_lightningNetworkOptions = lightningNetworkOptions;
_lightningClientFactory = lightningClientFactory;

View File

@@ -2,6 +2,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.HostedServices;
@@ -33,17 +34,15 @@ namespace BTCPayServer.Controllers.GreenField
public abstract class LightningNodeApiController : Controller
{
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly BTCPayServerEnvironment _btcPayServerEnvironment;
private readonly CssThemeManager _cssThemeManager;
private readonly ISettingsRepository _settingsRepository;
private readonly IAuthorizationService _authorizationService;
protected LightningNodeApiController(BTCPayNetworkProvider btcPayNetworkProvider,
BTCPayServerEnvironment btcPayServerEnvironment, CssThemeManager cssThemeManager,
ISettingsRepository settingsRepository,
IAuthorizationService authorizationService)
{
_btcPayNetworkProvider = btcPayNetworkProvider;
_btcPayServerEnvironment = btcPayServerEnvironment;
_cssThemeManager = cssThemeManager;
_settingsRepository = settingsRepository;
_authorizationService = authorizationService;
}
@@ -302,7 +301,8 @@ namespace BTCPayServer.Controllers.GreenField
protected async Task<bool> CanUseInternalLightning(bool doingAdminThings)
{
return (!doingAdminThings && _cssThemeManager.AllowLightningInternalNodeForAll) ||
return (!doingAdminThings && (await _settingsRepository.GetPolicies()).AllowLightningInternalNodeForAll) ||
(await _authorizationService.AuthorizeAsync(User, null,
new PolicyRequirement(Policies.CanUseInternalLightningNode))).Succeeded;
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Configuration;
@@ -28,18 +29,18 @@ namespace BTCPayServer.Controllers.GreenField
private readonly StoreRepository _storeRepository;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly IAuthorizationService _authorizationService;
private readonly CssThemeManager _cssThemeManager;
private readonly ISettingsRepository _settingsRepository;
public StoreLightningNetworkPaymentMethodsController(
StoreRepository storeRepository,
BTCPayNetworkProvider btcPayNetworkProvider,
IAuthorizationService authorizationService,
CssThemeManager cssThemeManager)
ISettingsRepository settingsRepository)
{
_storeRepository = storeRepository;
_btcPayNetworkProvider = btcPayNetworkProvider;
_authorizationService = authorizationService;
_cssThemeManager = cssThemeManager;
_settingsRepository = settingsRepository;
}
public static IEnumerable<LightningNetworkPaymentMethodData> GetLightningPaymentMethods(StoreData store,
@@ -213,7 +214,7 @@ namespace BTCPayServer.Controllers.GreenField
private async Task<bool> CanUseInternalLightning()
{
return _cssThemeManager.AllowLightningInternalNodeForAll ||
return (await _settingsRepository.GetPolicies()).AllowLightningInternalNodeForAll ||
(await _authorizationService.AuthorizeAsync(User, null,
new PolicyRequirement(Policies.CanUseInternalLightningNode))).Succeeded;
}

View File

@@ -6,6 +6,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.BIP78.Sender;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
@@ -39,7 +40,7 @@ namespace BTCPayServer.Controllers.GreenField
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly WalletRepository _walletRepository;
private readonly ExplorerClientProvider _explorerClientProvider;
private readonly CssThemeManager _cssThemeManager;
private readonly ISettingsRepository _settingsRepository;
private readonly NBXplorerDashboard _nbXplorerDashboard;
private readonly WalletsController _walletsController;
private readonly PayjoinClient _payjoinClient;
@@ -54,7 +55,7 @@ namespace BTCPayServer.Controllers.GreenField
BTCPayNetworkProvider btcPayNetworkProvider,
WalletRepository walletRepository,
ExplorerClientProvider explorerClientProvider,
CssThemeManager cssThemeManager,
ISettingsRepository settingsRepository,
NBXplorerDashboard nbXplorerDashboard,
WalletsController walletsController,
PayjoinClient payjoinClient,
@@ -68,7 +69,7 @@ namespace BTCPayServer.Controllers.GreenField
_btcPayNetworkProvider = btcPayNetworkProvider;
_walletRepository = walletRepository;
_explorerClientProvider = explorerClientProvider;
_cssThemeManager = cssThemeManager;
_settingsRepository = settingsRepository;
_nbXplorerDashboard = nbXplorerDashboard;
_walletsController = walletsController;
_payjoinClient = payjoinClient;
@@ -504,7 +505,7 @@ namespace BTCPayServer.Controllers.GreenField
private async Task<(bool HotWallet, bool RPCImport)> CanUseHotWallet()
{
return await _authorizationService.CanUseHotWallet(_cssThemeManager.Policies, User);
return await _authorizationService.CanUseHotWallet(await _settingsRepository.GetPolicies(), User);
}
private bool IsInvalidWalletRequest(string cryptoCode, out BTCPayNetwork network,

View File

@@ -36,7 +36,6 @@ namespace BTCPayServer.Controllers.GreenField
private readonly RateLimitService _throttleService;
private readonly BTCPayServerOptions _options;
private readonly IAuthorizationService _authorizationService;
private readonly CssThemeManager _themeManager;
private readonly UserService _userService;
public UsersController(UserManager<ApplicationUser> userManager,
@@ -47,7 +46,6 @@ namespace BTCPayServer.Controllers.GreenField
RateLimitService throttleService,
BTCPayServerOptions options,
IAuthorizationService authorizationService,
CssThemeManager themeManager,
UserService userService)
{
_userManager = userManager;
@@ -58,7 +56,6 @@ namespace BTCPayServer.Controllers.GreenField
_throttleService = throttleService;
_options = options;
_authorizationService = authorizationService;
_themeManager = themeManager;
_userService = userService;
}
@@ -114,7 +111,7 @@ namespace BTCPayServer.Controllers.GreenField
if (request.IsAdministrator is true && !isAdmin)
return Forbid(AuthenticationSchemes.GreenfieldBasic);
if (!isAdmin && (policies.LockSubscription || _themeManager.Policies.DisableNonAdminCreateUserApi))
if (!isAdmin && (policies.LockSubscription || (await _settingsRepository.GetPolicies()).DisableNonAdminCreateUserApi))
{
// If we are not admin and subscriptions are locked, we need to check the Policies.CanCreateUser.Key permission
var canCreateUser = (await _authorizationService.AuthorizeAsync(User, null, new PolicyRequirement(Policies.CanCreateUser))).Succeeded;

View File

@@ -6,6 +6,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Filters;
@@ -32,7 +33,7 @@ namespace BTCPayServer.Controllers
{
public class HomeController : Controller
{
private readonly CssThemeManager _cachedServerSettings;
private readonly ISettingsRepository _settingsRepository;
private readonly IFileProvider _fileProvider;
public IHttpClientFactory HttpClientFactory { get; }
@@ -40,13 +41,13 @@ namespace BTCPayServer.Controllers
SignInManager<ApplicationUser> SignInManager { get; }
public HomeController(IHttpClientFactory httpClientFactory,
CssThemeManager cachedServerSettings,
ISettingsRepository settingsRepository,
IWebHostEnvironment webHostEnvironment,
LanguageService languageService,
SignInManager<ApplicationUser> signInManager)
{
_settingsRepository = settingsRepository;
HttpClientFactory = httpClientFactory;
_cachedServerSettings = cachedServerSettings;
LanguageService = languageService;
_fileProvider = webHostEnvironment.WebRootFileProvider;
SignInManager = signInManager;
@@ -54,9 +55,9 @@ namespace BTCPayServer.Controllers
[Route("")]
[DomainMappingConstraint()]
public IActionResult Index()
public async Task<IActionResult> Index()
{
if (_cachedServerSettings.FirstRun)
if ((await _settingsRepository.GetTheme()).FirstRun)
{
return RedirectToAction(nameof(AccountController.Register), "Account");
}

View File

@@ -136,7 +136,7 @@ namespace BTCPayServer.Controllers
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = _linkGenerator.EmailConfirmationLink(user.Id, code, Request.Scheme, Request.Host, Request.PathBase);
var email = user.Email;
_EmailSenderFactory.GetEmailSender().SendEmailConfirmation(email, callbackUrl);
(await _EmailSenderFactory.GetEmailSender()).SendEmailConfirmation(email, callbackUrl);
TempData[WellKnownTempData.SuccessMessage] = "Verification email sent. Please check your email.";
return RedirectToAction(nameof(Index));
}

View File

@@ -116,10 +116,10 @@ namespace BTCPayServer.Controllers
[Route("server/users/new")]
[HttpGet]
public IActionResult CreateUser()
public async Task<IActionResult> CreateUser()
{
ViewData["AllowIsAdmin"] = _Options.AllowAdminRegistration;
ViewData["AllowRequestEmailConfirmation"] = _cssThemeManager.Policies.RequiresConfirmedEmail;
ViewData["AllowRequestEmailConfirmation"] = (await _SettingsRepository.GetPolicies()).RequiresConfirmedEmail;
return View();
}
@@ -129,13 +129,14 @@ namespace BTCPayServer.Controllers
public async Task<IActionResult> CreateUser(RegisterFromAdminViewModel model)
{
ViewData["AllowIsAdmin"] = _Options.AllowAdminRegistration;
ViewData["AllowRequestEmailConfirmation"] = _cssThemeManager.Policies.RequiresConfirmedEmail;
var requiresConfirmedEmail = (await _SettingsRepository.GetPolicies()).RequiresConfirmedEmail;
ViewData["AllowRequestEmailConfirmation"] = requiresConfirmedEmail;
if (!_Options.AllowAdminRegistration)
model.IsAdmin = false;
if (ModelState.IsValid)
{
IdentityResult result;
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, EmailConfirmed = model.EmailConfirmed, RequiresEmailConfirmation = _cssThemeManager.Policies.RequiresConfirmedEmail,
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, EmailConfirmed = model.EmailConfirmed, RequiresEmailConfirmation = requiresConfirmedEmail,
Created = DateTimeOffset.UtcNow };
if (!string.IsNullOrEmpty(model.Password))

View File

@@ -56,7 +56,6 @@ namespace BTCPayServer.Controllers
private readonly AppService _AppService;
private readonly CheckConfigurationHostedService _sshState;
private readonly EventAggregator _eventAggregator;
private readonly CssThemeManager _cssThemeManager;
private readonly IOptions<ExternalServicesOptions> _externalServiceOptions;
private readonly StoredFileRepository _StoredFileRepository;
private readonly FileService _FileService;
@@ -78,7 +77,6 @@ namespace BTCPayServer.Controllers
AppService appService,
CheckConfigurationHostedService sshState,
EventAggregator eventAggregator,
CssThemeManager cssThemeManager,
IOptions<ExternalServicesOptions> externalServiceOptions)
{
_Options = options;
@@ -96,7 +94,6 @@ namespace BTCPayServer.Controllers
_AppService = appService;
_sshState = sshState;
_eventAggregator = eventAggregator;
_cssThemeManager = cssThemeManager;
_externalServiceOptions = externalServiceOptions;
}

View File

@@ -15,7 +15,7 @@ namespace BTCPayServer.Controllers
public partial class StoresController
{
[HttpGet("{storeId}/lightning/{cryptoCode}")]
public IActionResult SetupLightningNode(string storeId, string cryptoCode)
public async Task<IActionResult> SetupLightningNode(string storeId, string cryptoCode)
{
var store = HttpContext.GetStoreData();
if (store == null)
@@ -26,7 +26,7 @@ namespace BTCPayServer.Controllers
CryptoCode = cryptoCode,
StoreId = storeId
};
SetExistingValues(store, vm);
await SetExistingValues(store, vm);
return View(vm);
}
@@ -38,7 +38,7 @@ namespace BTCPayServer.Controllers
if (store == null)
return NotFound();
vm.CanUseInternalNode = CanUseInternalLightning();
vm.CanUseInternalNode = await CanUseInternalLightning();
var network = vm.CryptoCode == null ? null : _ExplorerProvider.GetNetwork(vm.CryptoCode);
if (network == null)
@@ -53,7 +53,7 @@ namespace BTCPayServer.Controllers
LightningSupportedPaymentMethod paymentMethod = null;
if (vm.LightningNodeType == LightningNodeType.Internal)
{
if (!CanUseInternalLightning())
if (!await CanUseInternalLightning())
{
ModelState.AddModelError(nameof(vm.ConnectionString), "You are not authorized to use the internal lightning node");
return View(vm);
@@ -154,14 +154,14 @@ namespace BTCPayServer.Controllers
return RedirectToAction(nameof(UpdateStore), new { storeId });
}
private bool CanUseInternalLightning()
private async Task<bool> CanUseInternalLightning()
{
return User.IsInRole(Roles.ServerAdmin) || _CssThemeManager.AllowLightningInternalNodeForAll;
return User.IsInRole(Roles.ServerAdmin) || (await _settingsRepository.GetPolicies()).AllowLightningInternalNodeForAll;
}
private void SetExistingValues(StoreData store, LightningNodeViewModel vm)
private async Task SetExistingValues(StoreData store, LightningNodeViewModel vm)
{
vm.CanUseInternalNode = CanUseInternalLightning();
vm.CanUseInternalNode = await CanUseInternalLightning();
var lightning = GetExistingLightningSupportedPaymentMethod(vm.CryptoCode, store);
if (lightning != null)
{

View File

@@ -66,11 +66,8 @@ namespace BTCPayServer.Controllers
SettingsRepository settingsRepository,
IAuthorizationService authorizationService,
EventAggregator eventAggregator,
CssThemeManager cssThemeManager,
AppService appService,
IWebHostEnvironment webHostEnvironment,
WebhookNotificationManager webhookNotificationManager,
IOptions<LightningNetworkOptions> lightningNetworkOptions,
IDataProtectionProvider dataProtector)
{
_RateFactory = rateFactory;
@@ -83,10 +80,7 @@ namespace BTCPayServer.Controllers
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_settingsRepository = settingsRepository;
_authorizationService = authorizationService;
_CssThemeManager = cssThemeManager;
_appService = appService;
_webHostEnvironment = webHostEnvironment;
_lightningNetworkOptions = lightningNetworkOptions;
DataProtector = dataProtector.CreateProtector("ConfigProtector");
WebhookNotificationManager = webhookNotificationManager;
_EventAggregator = eventAggregator;
@@ -111,10 +105,7 @@ namespace BTCPayServer.Controllers
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private readonly SettingsRepository _settingsRepository;
private readonly IAuthorizationService _authorizationService;
private readonly CssThemeManager _CssThemeManager;
private readonly AppService _appService;
private readonly IWebHostEnvironment _webHostEnvironment;
private readonly IOptions<LightningNetworkOptions> _lightningNetworkOptions;
private readonly EventAggregator _EventAggregator;
[TempData]

View File

@@ -0,0 +1,23 @@
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Services;
namespace BTCPayServer
{
public static class SettingsRepositoryExtensions
{
public static async Task<PoliciesSettings> GetPolicies(this ISettingsRepository settingsRepository)
{
return (await settingsRepository.GetSettingAsync<PoliciesSettings>()) ?? new PoliciesSettings();
}
public static async Task<ThemeSettings> GetTheme(this ISettingsRepository settingsRepository)
{
var result = await settingsRepository.GetSettingAsync<ThemeSettings>() ?? new ThemeSettings();
result.ThemeCssUri = string.IsNullOrWhiteSpace(result.ThemeCssUri) ? "/main/themes/default.css" : result.ThemeCssUri;
result.CustomThemeCssUri = string.IsNullOrWhiteSpace(result.CustomThemeCssUri) ? null : result.CustomThemeCssUri;
result.BootstrapCssUri = string.IsNullOrWhiteSpace(result.BootstrapCssUri) ? "/main/bootstrap/bootstrap.css" : result.BootstrapCssUri;
result.CreativeStartCssUri = string.IsNullOrWhiteSpace(result.CreativeStartCssUri) ? "/main/bootstrap/creative.css" : result.CreativeStartCssUri;
return result;
}
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.HostedServices;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
@@ -27,14 +28,15 @@ namespace BTCPayServer.Filters
{
if (context.RouteContext.RouteData.Values.ContainsKey("appId"))
return true;
var css = context.RouteContext.HttpContext.RequestServices.GetService<CssThemeManager>();
if (css?.DomainToAppMapping is List<PoliciesSettings.DomainToAppMappingItem> mapping)
var settingsRepository = context.RouteContext.HttpContext.RequestServices.GetService<ISettingsRepository>();
var policies = settingsRepository.GetPolicies().GetAwaiter().GetResult();
if (policies?.DomainToAppMapping is { } mapping && mapping.Any())
{
var matchedDomainMapping = css.DomainToAppMapping.FirstOrDefault(item =>
var matchedDomainMapping = policies.DomainToAppMapping.FirstOrDefault(item =>
item.Domain.Equals(context.RouteContext.HttpContext.Request.Host.Host, StringComparison.InvariantCultureIgnoreCase));
if (matchedDomainMapping is PoliciesSettings.DomainToAppMappingItem)
if (matchedDomainMapping != null)
{
if (!(AppType is AppType appType))
if (!(AppType is { } appType))
return false;
if (appType != matchedDomainMapping.AppType)
return false;
@@ -42,18 +44,16 @@ namespace BTCPayServer.Filters
return true;
}
if (AppType == css.RootAppType) {
context.RouteContext.RouteData.Values.Add("appId", css.RootAppId);
if (AppType == policies.RootAppType) {
context.RouteContext.RouteData.Values.Add("appId", policies.RootAppId);
return true;
}
return AppType is null;
}
else
{
return AppType is null;
}
return AppType is null;
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Security;
using Microsoft.AspNetCore.Mvc.Filters;
namespace BTCPayServer.HostedServices
{
public class ContentSecurityPolicyCssThemeManager : Attribute, IActionFilter, IOrderedFilter
{
public int Order => 1001;
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
var settingsRepository = context.HttpContext.RequestServices.GetService(typeof(ISettingsRepository)) as ISettingsRepository;
var policies = context.HttpContext.RequestServices.GetService(typeof(ContentSecurityPolicies)) as ContentSecurityPolicies;
if (policies != null)
{
var theme = settingsRepository.GetTheme().GetAwaiter().GetResult();
if (theme.CreativeStartCssUri != null && Uri.TryCreate(theme.CreativeStartCssUri, UriKind.Absolute, out var uri))
{
policies.Clear();
}
if (theme.BootstrapCssUri != null && Uri.TryCreate(theme.BootstrapCssUri, UriKind.Absolute, out uri))
{
policies.Clear();
}
if (theme.ThemeCssUri != null && Uri.TryCreate(theme.ThemeCssUri, UriKind.Absolute, out uri))
{
policies.Clear();
}
if (theme.CustomThemeCssUri != null && Uri.TryCreate(theme.CustomThemeCssUri, UriKind.Absolute, out uri))
{
policies.Clear();
}
}
}
}
}

View File

@@ -1,155 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using BTCPayServer.Security;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using Microsoft.AspNetCore.Mvc.Filters;
namespace BTCPayServer.HostedServices
{
public class CssThemeManager
{
public void Update(ThemeSettings data)
{
if (String.IsNullOrWhiteSpace(data.ThemeCssUri))
_themeUri = "/main/themes/default.css";
else
_themeUri = data.ThemeCssUri;
if (String.IsNullOrWhiteSpace(data.CustomThemeCssUri))
_customThemeUri = null;
else
_customThemeUri = data.CustomThemeCssUri;
if (String.IsNullOrWhiteSpace(data.BootstrapCssUri))
_bootstrapUri = "/main/bootstrap/bootstrap.css";
else
_bootstrapUri = data.BootstrapCssUri;
if (String.IsNullOrWhiteSpace(data.CreativeStartCssUri))
_creativeStartUri = "/main/bootstrap/creative.css";
else
_creativeStartUri = data.CreativeStartCssUri;
FirstRun = data.FirstRun;
}
private string _themeUri;
public string ThemeUri
{
get { return _themeUri; }
}
private string _customThemeUri;
public string CustomThemeUri
{
get { return _customThemeUri; }
}
private string _bootstrapUri;
public string BootstrapUri
{
get { return _bootstrapUri; }
}
private string _creativeStartUri;
private PoliciesSettings _policies = new PoliciesSettings();
public PoliciesSettings Policies { get { return _policies; } }
public string CreativeStartUri
{
get { return _creativeStartUri; }
}
public bool ShowRegister { get { return !_policies.LockSubscription; } }
public bool DiscourageSearchEngines { get { return _policies.DiscourageSearchEngines; } }
public AppType? RootAppType { get { return _policies.RootAppType; } }
public string RootAppId { get { return _policies.RootAppId; } }
public bool FirstRun { get; set; }
public List<PoliciesSettings.DomainToAppMappingItem> DomainToAppMapping { get { return _policies.DomainToAppMapping; } }
internal void Update(PoliciesSettings data)
{
_policies = data;
}
public bool AllowLightningInternalNodeForAll { get { return _policies.AllowLightningInternalNodeForAll; } }
}
public class ContentSecurityPolicyCssThemeManager : Attribute, IActionFilter, IOrderedFilter
{
public int Order => 1001;
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
var manager = context.HttpContext.RequestServices.GetService(typeof(CssThemeManager)) as CssThemeManager;
var policies = context.HttpContext.RequestServices.GetService(typeof(ContentSecurityPolicies)) as ContentSecurityPolicies;
if (manager != null && policies != null)
{
if (manager.CreativeStartUri != null && Uri.TryCreate(manager.CreativeStartUri, UriKind.Absolute, out var uri))
{
policies.Clear();
}
if (manager.BootstrapUri != null && Uri.TryCreate(manager.BootstrapUri, UriKind.Absolute, out uri))
{
policies.Clear();
}
if (manager.ThemeUri != null && Uri.TryCreate(manager.ThemeUri, UriKind.Absolute, out uri))
{
policies.Clear();
}
if (manager.CustomThemeUri != null && Uri.TryCreate(manager.CustomThemeUri, UriKind.Absolute, out uri))
{
policies.Clear();
}
}
}
}
public class CssThemeManagerHostedService : BaseAsyncService
{
private readonly SettingsRepository _SettingsRepository;
private readonly CssThemeManager _CssThemeManager;
public CssThemeManagerHostedService(SettingsRepository settingsRepository, CssThemeManager cssThemeManager)
{
_SettingsRepository = settingsRepository;
_CssThemeManager = cssThemeManager;
}
internal override Task[] InitializeTasks()
{
return new[]
{
CreateLoopTask(ListenForThemeChanges),
CreateLoopTask(ListenForPoliciesChanges),
};
}
async Task ListenForPoliciesChanges()
{
var data = (await _SettingsRepository.GetSettingAsync<PoliciesSettings>()) ?? new PoliciesSettings();
_CssThemeManager.Update(data);
await _SettingsRepository.WaitSettingsChanged<PoliciesSettings>(Cancellation);
}
async Task ListenForThemeChanges()
{
var data = (await _SettingsRepository.GetSettingAsync<ThemeSettings>()) ?? new ThemeSettings();
_CssThemeManager.Update(data);
await _SettingsRepository.WaitSettingsChanged<ThemeSettings>(Cancellation);
}
}
}

View File

@@ -132,7 +132,7 @@ namespace BTCPayServer.HostedServices
$"Amount: {notification.Data.Price} {notification.Data.Currency}<br>" +
$"<br><details><summary>Details</summary><pre>{json}</pre></details>";
_EmailSenderFactory.GetEmailSender(invoice.StoreId).SendEmail(
(await _EmailSenderFactory.GetEmailSender(invoice.StoreId)).SendEmail(
invoice.NotificationEmail,
$"{storeName} Invoice Notification - ${invoice.StoreId}",
emailBody);

View File

@@ -52,7 +52,7 @@ namespace BTCPayServer.HostedServices
new HostString(userRegisteredEvent.RequestUri.Host, userRegisteredEvent.RequestUri.Port),
userRegisteredEvent.RequestUri.PathAndQuery);
userRegisteredEvent.CallbackUrlGenerated?.SetResult(new Uri(callbackUrl));
_emailSenderFactory.GetEmailSender()
(await _emailSenderFactory.GetEmailSender())
.SendEmailConfirmation(userRegisteredEvent.User.Email, callbackUrl);
}
else if (!await _userManager.HasPasswordAsync(userRegisteredEvent.User))
@@ -82,7 +82,7 @@ namespace BTCPayServer.HostedServices
userPasswordResetRequestedEvent.RequestUri.Port),
userPasswordResetRequestedEvent.RequestUri.PathAndQuery);
userPasswordResetRequestedEvent.CallbackUrlGenerated?.SetResult(new Uri(callbackUrl));
_emailSenderFactory.GetEmailSender()
(await _emailSenderFactory.GetEmailSender())
.SendSetPasswordConfirmation(userPasswordResetRequestedEvent.User.Email, callbackUrl,
newPassword);
break;

View File

@@ -298,14 +298,12 @@ namespace BTCPayServer.Hosting
Fallback = new FeeRate(100L, 1)
});
services.AddSingleton<CssThemeManager>();
services.Configure<MvcOptions>((o) =>
{
o.Filters.Add(new ContentSecurityPolicyCssThemeManager());
o.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(WalletId)));
o.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(DerivationStrategyBase)));
});
services.AddSingleton<IHostedService, CssThemeManagerHostedService>();
services.AddSingleton<HostedServices.CheckConfigurationHostedService>();
services.AddSingleton<IHostedService, HostedServices.CheckConfigurationHostedService>(o => o.GetRequiredService<CheckConfigurationHostedService>());

View File

@@ -1,3 +1,4 @@
using System.Threading.Tasks;
using BTCPayServer.HostedServices;
using BTCPayServer.Services.Stores;
@@ -6,28 +7,25 @@ namespace BTCPayServer.Services.Mails
public class EmailSenderFactory
{
private readonly IBackgroundJobClient _jobClient;
private readonly SettingsRepository _repository;
private readonly SettingsRepository _settingsRepository;
private readonly StoreRepository _storeRepository;
private readonly CssThemeManager _cssThemeManager;
public EmailSenderFactory(IBackgroundJobClient jobClient,
SettingsRepository repository,
StoreRepository storeRepository,
CssThemeManager cssThemeManager)
SettingsRepository settingsSettingsRepository,
StoreRepository storeRepository)
{
_jobClient = jobClient;
_repository = repository;
_settingsRepository = settingsSettingsRepository;
_storeRepository = storeRepository;
_cssThemeManager = cssThemeManager;
}
public IEmailSender GetEmailSender(string storeId = null)
public async Task<IEmailSender> GetEmailSender(string storeId = null)
{
var serverSender = new ServerEmailSender(_repository, _jobClient);
var serverSender = new ServerEmailSender(_settingsRepository, _jobClient);
if (string.IsNullOrEmpty(storeId))
return serverSender;
return new StoreEmailSender(_storeRepository,
!_cssThemeManager.Policies.DisableStoresToUseServerEmailSettings ? serverSender : null, _jobClient,
!(await _settingsRepository.GetPolicies()).DisableStoresToUseServerEmailSettings ? serverSender : null, _jobClient,
storeId);
}
}

View File

@@ -5,6 +5,7 @@ using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Data;
using BTCPayServer.Events;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
namespace BTCPayServer.Services
@@ -13,27 +14,29 @@ namespace BTCPayServer.Services
{
private readonly ApplicationDbContextFactory _ContextFactory;
private readonly EventAggregator _EventAggregator;
private readonly IMemoryCache _memoryCache;
public SettingsRepository(ApplicationDbContextFactory contextFactory, EventAggregator eventAggregator)
public SettingsRepository(ApplicationDbContextFactory contextFactory, EventAggregator eventAggregator, IMemoryCache memoryCache)
{
_ContextFactory = contextFactory;
_EventAggregator = eventAggregator;
_memoryCache = memoryCache;
}
public async Task<T?> GetSettingAsync<T>(string? name = null) where T : class
{
name ??= typeof(T).FullName ?? string.Empty;
using (var ctx = _ContextFactory.CreateContext())
return await _memoryCache.GetOrCreateAsync(GetCacheKey(name), async entry =>
{
await using var ctx = _ContextFactory.CreateContext();
var data = await ctx.Settings.Where(s => s.Id == name).FirstOrDefaultAsync();
if (data == null)
return default(T);
return Deserialize<T>(data.Value);
}
return data == null ? default : Deserialize<T>(data.Value);
});
}
public async Task UpdateSetting<T>(T obj, string? name = null) where T : class
{
using (var ctx = _ContextFactory.CreateContext())
name ??= typeof(T).FullName ?? string.Empty;
await using (var ctx = _ContextFactory.CreateContext())
{
var settings = UpdateSettingInContext<T>(ctx, obj, name);
try
@@ -46,6 +49,7 @@ namespace BTCPayServer.Services
await ctx.SaveChangesAsync();
}
}
_memoryCache.Set(GetCacheKey(name),obj);
_EventAggregator.Publish(new SettingsChanged<T>()
{
Settings = obj
@@ -55,13 +59,10 @@ namespace BTCPayServer.Services
public SettingData UpdateSettingInContext<T>(ApplicationDbContext ctx, T obj, string? name = null) where T : class
{
name ??= obj.GetType().FullName ?? string.Empty;
var settings = new SettingData();
settings.Id = name;
settings.Value = Serialize(obj);
_memoryCache.Remove(GetCacheKey(name));
var settings = new SettingData {Id = name, Value = Serialize(obj)};
ctx.Attach(settings);
ctx.Entry(settings).State = EntityState.Modified;
return settings;
}
@@ -75,6 +76,11 @@ namespace BTCPayServer.Services
return JsonConvert.SerializeObject(obj);
}
private string GetCacheKey(string name)
{
return $"{nameof(SettingsRepository)}_{name}";
}
public async Task<T> WaitSettingsChanged<T>(CancellationToken cancellationToken = default) where T : class
{
return (await _EventAggregator.WaitNext<SettingsChanged<T>>(cancellationToken)).Settings;

View File

@@ -1,6 +1,7 @@
@model LoginViewModel
@using BTCPayServer.Abstractions.Contracts
@model LoginViewModel
@inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
@inject ISettingsRepository _settingsRepository
@{
ViewData["Title"] = "Log in";
Layout = "_LayoutSimple";
@@ -50,7 +51,7 @@
</div>
</fieldset>
</form>
@if (themeManager.ShowRegister)
@if (!(await _settingsRepository.GetPolicies()).LockSubscription)
{
<p class="text-center mt-2 mb-0">
<a id="Register" style="font-size:1.15rem" asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]" tabindex="4">Create your account</a>

View File

@@ -1,9 +1,11 @@
@addTagHelper *, BundlerMinifier.TagHelpers
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
@inject ISettingsRepository _settingsRepository
@using BTCPayServer.Abstractions.Contracts
@model BTCPayServer.Models.AppViewModels.ViewCrowdfundViewModel
@{
ViewData["Title"] = Model.Title;
Layout = null;
var theme = await _settingsRepository.GetTheme();
}
<!DOCTYPE html>
@@ -13,8 +15,8 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.BootstrapUri)" rel="stylesheet" />
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.ThemeUri)" rel="stylesheet" />
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.BootstrapCssUri)" rel="stylesheet" asp-append-version="true"/>
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.ThemeCssUri)" rel="stylesheet" asp-append-version="true"/>
@if (Model.CustomCSSLink != null)
{
<link href="@Model.CustomCSSLink" rel="stylesheet" />

View File

@@ -1,13 +1,14 @@
@using BTCPayServer.Services.Invoices
@using BTCPayServer.Client.Models
@using BTCPayServer.Abstractions.Contracts
@model BTCPayServer.Models.PaymentRequestViewModels.ViewPaymentRequestViewModel
@addTagHelper *, BundlerMinifier.TagHelpers
@inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
@inject ISettingsRepository _settingsRepository
@{
ViewData["Title"] = Model.Title;
Layout = null;
var theme = await _settingsRepository.GetTheme();
string StatusTextClass(InvoiceState state)
{
switch (state.Status.ToModernStatus())
@@ -41,8 +42,8 @@
<meta name="robots" content="noindex,nofollow">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.BootstrapUri)" rel="stylesheet" />
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.ThemeUri)" rel="stylesheet" />
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.BootstrapCssUri)" rel="stylesheet" asp-append-version="true"/>
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.ThemeCssUri)" rel="stylesheet" asp-append-version="true"/>
<bundle name="wwwroot/bundles/payment-request-bundle.min.css" asp-append-version="true"></bundle>
@if (Model.CustomCSSLink != null)
{

View File

@@ -1,9 +1,11 @@
@addTagHelper *, BundlerMinifier.TagHelpers
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
@inject ISettingsRepository _settingsRepository
@using BTCPayServer.Abstractions.Contracts
@model BTCPayServer.Controllers.ShowLightningNodeInfoViewModel
@{
Layout = null;
var theme = await _settingsRepository.GetTheme();
}
<!DOCTYPE html>
<html>
@@ -15,8 +17,8 @@
<link rel="apple-touch-icon" href="~/img/icons/icon-512x512.png" asp-append-version="true">
<link rel="apple-touch-startup-image" href="~/img/splash.png" asp-append-version="true">
<link rel="manifest" href="~/manifest.json">
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.BootstrapUri)" rel="stylesheet" asp-append-version="true" />
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.ThemeUri)" rel="stylesheet" asp-append-version="true" />
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.BootstrapCssUri)" rel="stylesheet" asp-append-version="true"/>
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.ThemeCssUri)" rel="stylesheet" asp-append-version="true"/>
<link href="~/vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" asp-append-version="true" />
<link href="~/main/qrcode.css" rel="stylesheet" asp-append-version="true" />
<script src="~/js/copy-to-clipboard.js"></script>

View File

@@ -1,13 +1,14 @@
@using NUglify.Helpers
@using BTCPayServer.Abstractions.Contracts
@model BTCPayServer.Models.ViewPullPaymentModel
@addTagHelper *, BundlerMinifier.TagHelpers
@inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
@inject ISettingsRepository _settingsRepository
@{
ViewData["Title"] = Model.Title;
Layout = null;
var theme = await _settingsRepository.GetTheme();
string StatusTextClass(string status)
{
switch (status)
@@ -30,8 +31,8 @@
<meta name="robots" content="noindex, nofollow">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.BootstrapUri)" rel="stylesheet" />
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.ThemeUri)" rel="stylesheet" />
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.BootstrapCssUri)" rel="stylesheet" asp-append-version="true"/>
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.ThemeCssUri)" rel="stylesheet" asp-append-version="true"/>
<bundle name="wwwroot/bundles/payment-request-bundle.min.css" asp-append-version="true"></bundle>
@if (Model.CustomCSSLink != null)
{

View File

@@ -1,22 +1,26 @@
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
@using BTCPayServer.Abstractions.Contracts
@inject ISettingsRepository _settingsRepository
@addTagHelper *, BundlerMinifier.TagHelpers
@{
var theme = await _settingsRepository.GetTheme();
var policies = await _settingsRepository.GetPolicies();
}
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="">
<meta name="author" content="">
@if (themeManager.DiscourageSearchEngines)
@if (policies.DiscourageSearchEngines)
{
<meta name="robots" content="noindex">
}
<title>@ViewData["Title"]</title>
@* CSS *@
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.BootstrapUri)" rel="stylesheet" asp-append-version="true" />
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.CreativeStartUri)" rel="stylesheet" asp-append-version="true" />
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.ThemeUri)" rel="stylesheet" asp-append-version="true" />
@if (!string.IsNullOrWhiteSpace(themeManager.CustomThemeUri))
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.BootstrapCssUri)" rel="stylesheet" asp-append-version="true" />
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.CreativeStartCssUri)" rel="stylesheet" asp-append-version="true" />
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.ThemeCssUri)" rel="stylesheet" asp-append-version="true" />
@if (!string.IsNullOrWhiteSpace(theme.CustomThemeCssUri))
{
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.CustomThemeUri)" rel="stylesheet" asp-append-version="true" />
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.CustomThemeCssUri)" rel="stylesheet" asp-append-version="true" />
}
<bundle name="wwwroot/bundles/main-bundle.min.css" asp-append-version="true" />
@* Non-JS *@

View File

@@ -5,11 +5,12 @@
@using BTCPayServer.Views.Manage
@using BTCPayServer.Views.PaymentRequest
@using BTCPayServer.Views.Wallets
@using BTCPayServer.Abstractions.Contracts
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
@inject RoleManager<IdentityRole> RoleManager
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
@inject BTCPayServer.HostedServices.CssThemeManager ThemeManager
@inject ISettingsRepository SettingsRepository
<!DOCTYPE html>
<html lang="en"@(Env.IsDeveloping ? " data-devenv" : "")>
@@ -76,7 +77,7 @@
else if (Env.IsSecure)
{
<ul class="navbar-nav">
@if (ThemeManager.ShowRegister)
@if (!(await SettingsRepository.GetPolicies()).LockSubscription)
{
<li class="nav-item"><a asp-area="" asp-controller="Account" asp-action="Register" class="nav-link js-scroll-trigger" id="Register">Register</a></li>
}

View File

@@ -1,11 +1,13 @@
@addTagHelper *, BundlerMinifier.TagHelpers
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
@inject ISettingsRepository _settingsRepository
@using BTCPayServer.Services.Apps
@using BTCPayServer.Abstractions.Contracts
@model BTCPayServer.Models.AppViewModels.ViewPointOfSaleViewModel
@{
ViewData["Title"] = Model.Title;
Layout = null;
var theme = await _settingsRepository.GetTheme();
}
<!DOCTYPE html>
@@ -20,8 +22,9 @@
<link rel="manifest" href="~/manifest.json">
<link href="@this.Context.Request.GetRelativePathOrAbsolute(themeManager.BootstrapUri)" rel="stylesheet" asp-append-version="true" />
<link href="@this.Context.Request.GetRelativePathOrAbsolute(themeManager.ThemeUri)" rel="stylesheet" asp-append-version="true" />
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.BootstrapCssUri)" rel="stylesheet" asp-append-version="true"/>
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.ThemeCssUri)" rel="stylesheet" asp-append-version="true"/>
@if (Model.CustomCSSLink != null)
{
<link href="@Model.CustomCSSLink" rel="stylesheet" />