mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 01:43:50 +01:00
Branding updates for 2.0 (#5947)
* Remove deprecated CSS options Closes #5945. * Greenfield: Add brandColor to store APIs Closes #5946. * Migrate file IDs to URLs Closes #5953. * Greenfield: Add CSS and logo URL to store settings API Closes #5945. * Add migration test * Store and Server branding can reference file's via fileid:ID * Add PaymentSoundUrl to Store API --------- Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
This commit is contained in:
parent
eba3475a1b
commit
4c303d358b
@ -35,13 +35,11 @@ namespace BTCPayServer.Client.Models
|
||||
public string CustomAmountPayButtonText { get; set; } = null;
|
||||
public string FixedAmountPayButtonText { get; set; } = null;
|
||||
public string TipText { get; set; } = null;
|
||||
public string CustomCSSLink { get; set; } = null;
|
||||
public string NotificationUrl { get; set; } = null;
|
||||
public string RedirectUrl { get; set; } = null;
|
||||
public bool? RedirectAutomatically { get; set; } = null;
|
||||
public bool? Archived { get; set; } = null;
|
||||
public string FormId { get; set; } = null;
|
||||
public string EmbeddedCSS { get; set; } = null;
|
||||
}
|
||||
|
||||
public enum CrowdfundResetEvery
|
||||
@ -65,9 +63,7 @@ namespace BTCPayServer.Client.Models
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset? EndDate { get; set; } = null;
|
||||
public decimal? TargetAmount { get; set; } = null;
|
||||
public string CustomCSSLink { get; set; } = null;
|
||||
public string MainImageUrl { get; set; } = null;
|
||||
public string EmbeddedCSS { get; set; } = null;
|
||||
public string NotificationUrl { get; set; } = null;
|
||||
public string Tagline { get; set; } = null;
|
||||
public string PerksTemplate { get; set; } = null;
|
||||
|
@ -17,9 +17,6 @@ namespace BTCPayServer.Client.Models
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Email { get; set; }
|
||||
|
||||
public string EmbeddedCSS { get; set; }
|
||||
public string CustomCSSLink { get; set; }
|
||||
public bool AllowCustomPaymentAmounts { get; set; }
|
||||
|
||||
[JsonExtensionData]
|
||||
|
@ -30,11 +30,9 @@ namespace BTCPayServer.Client.Models
|
||||
public string FixedAmountPayButtonText { get; set; }
|
||||
public string CustomAmountPayButtonText { get; set; }
|
||||
public string TipText { get; set; }
|
||||
public string CustomCSSLink { get; set; }
|
||||
public string NotificationUrl { get; set; }
|
||||
public string RedirectUrl { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string EmbeddedCSS { get; set; }
|
||||
public bool? RedirectAutomatically { get; set; }
|
||||
}
|
||||
|
||||
@ -50,9 +48,7 @@ namespace BTCPayServer.Client.Models
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset? EndDate { get; set; }
|
||||
public decimal? TargetAmount { get; set; }
|
||||
public string CustomCSSLink { get; set; }
|
||||
public string MainImageUrl { get; set; }
|
||||
public string EmbeddedCSS { get; set; }
|
||||
public string NotificationUrl { get; set; }
|
||||
public string Tagline { get; set; }
|
||||
public object Perks { get; set; }
|
||||
|
@ -16,6 +16,11 @@ namespace BTCPayServer.Client.Models
|
||||
|
||||
public string Website { get; set; }
|
||||
|
||||
public string BrandColor { get; set; }
|
||||
public string LogoUrl { get; set; }
|
||||
public string CssUrl { get; set; }
|
||||
public string PaymentSoundUrl { get; set; }
|
||||
|
||||
public string SupportUrl { get; set; }
|
||||
|
||||
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
|
||||
|
61
BTCPayServer.Data/Migrations/20240508015052_fileid.cs
Normal file
61
BTCPayServer.Data/Migrations/20240508015052_fileid.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20240508015052_fileid")]
|
||||
public partial class fileid : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql("""
|
||||
UPDATE "Settings"
|
||||
SET "Value" = jsonb_set(
|
||||
"Value",
|
||||
'{LogoUrl}',
|
||||
to_jsonb('fileid:' || ("Value"->>'LogoFileId'))) - 'LogoFileId'
|
||||
WHERE "Id" = 'BTCPayServer.Services.ThemeSettings'
|
||||
AND "Value"->>'LogoFileId' IS NOT NULL;
|
||||
|
||||
UPDATE "Settings"
|
||||
SET "Value" = jsonb_set(
|
||||
"Value",
|
||||
'{CustomThemeCssUrl}',
|
||||
to_jsonb('fileid:' || ("Value"->>'CustomThemeFileId'))) - 'CustomThemeFileId'
|
||||
WHERE "Id" = 'BTCPayServer.Services.ThemeSettings'
|
||||
AND "Value"->>'CustomThemeFileId' IS NOT NULL;
|
||||
|
||||
UPDATE "Stores"
|
||||
SET "StoreBlob" = jsonb_set(
|
||||
"StoreBlob",
|
||||
'{logoUrl}',
|
||||
to_jsonb('fileid:' || ("StoreBlob"->>'logoFileId'))) - 'logoFileId'
|
||||
WHERE "StoreBlob"->>'logoFileId' IS NOT NULL;
|
||||
|
||||
UPDATE "Stores"
|
||||
SET "StoreBlob" = jsonb_set(
|
||||
"StoreBlob",
|
||||
'{cssUrl}',
|
||||
to_jsonb('fileid:' || ("StoreBlob"->>'cssFileId'))) - 'cssFileId'
|
||||
WHERE "StoreBlob"->>'cssFileId' IS NOT NULL;
|
||||
|
||||
UPDATE "Stores"
|
||||
SET "StoreBlob" = jsonb_set(
|
||||
"StoreBlob",
|
||||
'{paymentSoundUrl}',
|
||||
to_jsonb('fileid:' || ("StoreBlob"->>'soundFileId'))) - 'soundFileId'
|
||||
WHERE "StoreBlob"->>'soundFileId' IS NOT NULL;
|
||||
""");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -64,6 +64,11 @@ namespace BTCPayServer.Tests
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public Uri ServerUriWithIP
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string MySQL
|
||||
{
|
||||
@ -164,6 +169,7 @@ namespace BTCPayServer.Tests
|
||||
await File.WriteAllTextAsync(confPath, config.ToString());
|
||||
|
||||
ServerUri = new Uri("http://" + HostName + ":" + Port + "/");
|
||||
ServerUriWithIP = new Uri("http://127.0.0.1:" + Port + "/");
|
||||
HttpClient = new HttpClient();
|
||||
HttpClient.BaseAddress = ServerUri;
|
||||
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
|
||||
|
@ -1365,6 +1365,15 @@ namespace BTCPayServer.Tests
|
||||
//create store
|
||||
var newStore = await client.CreateStore(new CreateStoreRequest { Name = "A" });
|
||||
Assert.Equal("A", newStore.Name);
|
||||
|
||||
// validate
|
||||
await AssertValidationError(["CssUrl", "LogoUrl", "BrandColor"], async () =>
|
||||
await client.UpdateStore(newStore.Id, new UpdateStoreRequest
|
||||
{
|
||||
CssUrl = "style.css",
|
||||
LogoUrl = "logo.svg",
|
||||
BrandColor = "invalid"
|
||||
}));
|
||||
|
||||
//update store
|
||||
Assert.Empty(newStore.PaymentMethodCriteria);
|
||||
@ -1372,6 +1381,9 @@ namespace BTCPayServer.Tests
|
||||
var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest
|
||||
{
|
||||
Name = "B",
|
||||
CssUrl = "https://example.org/style.css",
|
||||
LogoUrl = "https://example.org/logo.svg",
|
||||
BrandColor = "#003366",
|
||||
PaymentMethodCriteria = new List<PaymentMethodCriteriaData>
|
||||
{
|
||||
new()
|
||||
@ -1384,6 +1396,9 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
});
|
||||
Assert.Equal("B", updatedStore.Name);
|
||||
Assert.Equal("https://example.org/style.css", updatedStore.CssUrl);
|
||||
Assert.Equal("https://example.org/logo.svg", updatedStore.LogoUrl);
|
||||
Assert.Equal("#003366", updatedStore.BrandColor);
|
||||
var s = (await client.GetStore(newStore.Id));
|
||||
Assert.Equal("B", s.Name);
|
||||
var pmc = Assert.Single(s.PaymentMethodCriteria);
|
||||
|
@ -146,7 +146,7 @@ namespace BTCPayServer.Tests
|
||||
public async Task ModifyPayment(Action<GeneralSettingsViewModel> modify)
|
||||
{
|
||||
var storeController = GetController<UIStoresController>();
|
||||
var response = storeController.GeneralSettings();
|
||||
var response = await storeController.GeneralSettings();
|
||||
GeneralSettingsViewModel settings = (GeneralSettingsViewModel)((ViewResult)response).Model;
|
||||
modify(settings);
|
||||
await storeController.GeneralSettings(settings);
|
||||
|
@ -57,7 +57,6 @@ namespace BTCPayServer.Tests
|
||||
Assert.IsType<ViewResult>(
|
||||
await controller.EditAzureBlobStorageStorageProvider(azureBlobStorageConfiguration));
|
||||
|
||||
|
||||
var shouldBeRedirectingToAzureStorageConfigPage =
|
||||
Assert.IsType<RedirectToActionResult>(await controller.Storage());
|
||||
Assert.Equal(nameof(StorageProvider), shouldBeRedirectingToAzureStorageConfigPage.ActionName);
|
||||
@ -72,9 +71,8 @@ namespace BTCPayServer.Tests
|
||||
await controller.StorageProvider(StorageProvider.AzureBlobStorage.ToString()))
|
||||
.Model).ConnectionString);
|
||||
|
||||
|
||||
|
||||
await UnitTest1.CanUploadRemoveFiles(controller);
|
||||
var fileId = await UnitTest1.CanUploadFile(controller);
|
||||
await UnitTest1.CanRemoveFile(controller, fileId);
|
||||
}
|
||||
|
||||
[Fact(Skip = "Fail on CI")]
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Dapper;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
@ -53,7 +54,6 @@ using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Storage.Models;
|
||||
using BTCPayServer.Storage.Services.Providers.FileSystemStorage.Configuration;
|
||||
using BTCPayServer.Storage.ViewModels;
|
||||
using ExchangeSharp;
|
||||
using Fido2NetLib;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -79,6 +79,7 @@ using CreatePaymentRequestRequest = BTCPayServer.Client.Models.CreatePaymentRequ
|
||||
using MarkPayoutRequest = BTCPayServer.Client.Models.MarkPayoutRequest;
|
||||
using PaymentRequestData = BTCPayServer.Client.Models.PaymentRequestData;
|
||||
using RatesViewModel = BTCPayServer.Models.StoreViewModels.RatesViewModel;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
@ -297,7 +298,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// Set tolerance to 50%
|
||||
var stores = user.GetController<UIStoresController>();
|
||||
var response = stores.GeneralSettings();
|
||||
var response = await stores.GeneralSettings();
|
||||
var vm = Assert.IsType<GeneralSettingsViewModel>(Assert.IsType<ViewResult>(response).Model);
|
||||
Assert.Equal(0.0, vm.PaymentTolerance);
|
||||
vm.PaymentTolerance = 50.0;
|
||||
@ -439,7 +440,7 @@ namespace BTCPayServer.Tests
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync(true);
|
||||
var storeController = user.GetController<UIStoresController>();
|
||||
var storeResponse = storeController.GeneralSettings();
|
||||
var storeResponse = await storeController.GeneralSettings();
|
||||
Assert.IsType<ViewResult>(storeResponse);
|
||||
Assert.IsType<ViewResult>(storeController.SetupLightningNode(user.StoreId, "BTC"));
|
||||
|
||||
@ -838,7 +839,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var time = invoice.InvoiceTime;
|
||||
AssertSearchInvoice(acc, true, invoice.Id, $"startdate:{time.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
AssertSearchInvoice(acc, true, invoice.Id, $"enddate:{time.ToStringLowerInvariant()}");
|
||||
AssertSearchInvoice(acc, true, invoice.Id, $"enddate:{time.ToString().ToLowerInvariant()}");
|
||||
AssertSearchInvoice(acc, false, invoice.Id,
|
||||
$"startdate:{time.AddSeconds(1).ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
AssertSearchInvoice(acc, false, invoice.Id,
|
||||
@ -1499,8 +1500,7 @@ namespace BTCPayServer.Tests
|
||||
var btcMethod = PaymentTypes.CHAIN.GetPaymentMethodId("BTC").ToString();
|
||||
|
||||
// We allow BTC and LN, but not BTC under 5 USD, so only LN should be in the invoice
|
||||
var vm = Assert.IsType<CheckoutAppearanceViewModel>(Assert
|
||||
.IsType<ViewResult>(user.GetController<UIStoresController>().CheckoutAppearance()).Model);
|
||||
var vm = await user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModelAsync<CheckoutAppearanceViewModel>();
|
||||
Assert.Equal(2, vm.PaymentMethodCriteria.Count);
|
||||
var criteria = Assert.Single(vm.PaymentMethodCriteria.Where(m => m.PaymentMethod == btcMethod.ToString()));
|
||||
Assert.Equal(PaymentTypes.CHAIN.GetPaymentMethodId("BTC").ToString(), criteria.PaymentMethod);
|
||||
@ -1527,8 +1527,7 @@ namespace BTCPayServer.Tests
|
||||
// Let's replicate https://github.com/btcpayserver/btcpayserver/issues/2963
|
||||
// We allow BTC for more than 5 USD, and LN for less than 150. The default is LN, so the default
|
||||
// payment method should be LN.
|
||||
vm = Assert.IsType<CheckoutAppearanceViewModel>(Assert
|
||||
.IsType<ViewResult>(user.GetController<UIStoresController>().CheckoutAppearance()).Model);
|
||||
vm = await user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModelAsync<CheckoutAppearanceViewModel>();
|
||||
vm.DefaultPaymentMethod = lnMethod;
|
||||
criteria = vm.PaymentMethodCriteria.First();
|
||||
criteria.Value = "150 USD";
|
||||
@ -1640,7 +1639,7 @@ namespace BTCPayServer.Tests
|
||||
user.GrantAccess(true);
|
||||
user.RegisterLightningNode(cryptoCode);
|
||||
user.SetLNUrl(cryptoCode, false);
|
||||
var vm = user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModel<CheckoutAppearanceViewModel>();
|
||||
var vm = await user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModelAsync<CheckoutAppearanceViewModel>();
|
||||
var criteria = Assert.Single(vm.PaymentMethodCriteria);
|
||||
Assert.Equal(PaymentTypes.LN.GetPaymentMethodId(cryptoCode).ToString(), criteria.PaymentMethod);
|
||||
criteria.Value = "2 USD";
|
||||
@ -1660,7 +1659,7 @@ namespace BTCPayServer.Tests
|
||||
// Activating LNUrl, we should still have only 1 payment criteria that can be set.
|
||||
user.RegisterLightningNode(cryptoCode);
|
||||
user.SetLNUrl(cryptoCode, true);
|
||||
vm = user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModel<CheckoutAppearanceViewModel>();
|
||||
vm = await user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModelAsync<CheckoutAppearanceViewModel>();
|
||||
criteria = Assert.Single(vm.PaymentMethodCriteria);
|
||||
Assert.Equal(PaymentTypes.LN.GetPaymentMethodId(cryptoCode).ToString(), criteria.PaymentMethod);
|
||||
Assert.IsType<RedirectToActionResult>(user.GetController<UIStoresController>().CheckoutAppearance(vm).Result);
|
||||
@ -2507,6 +2506,90 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact(Timeout = LongRunningTestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanMigrateFileIds()
|
||||
{
|
||||
using var tester = CreateServerTester(newDb: true);
|
||||
tester.DeleteStore = false;
|
||||
await tester.StartAsync();
|
||||
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
|
||||
using (var ctx = tester.PayTester.GetService<ApplicationDbContextFactory>().CreateContext())
|
||||
{
|
||||
var storeConfig = """
|
||||
{
|
||||
"spread": 0.0,
|
||||
"cssFileId": "2a51c49a-9d54-4013-80a2-3f6e69d08523",
|
||||
"logoFileId": "8f890691-87f9-4c65-80e5-3b7ffaa3551f",
|
||||
"soundFileId": "62bc4757-b92b-4a3b-a8ab-0e9b693d6a29",
|
||||
"networkFeeMode": "MultiplePaymentsOnly",
|
||||
"defaultCurrency": "USD",
|
||||
"showStoreHeader": true,
|
||||
"celebratePayment": true,
|
||||
"paymentTolerance": 0.0,
|
||||
"invoiceExpiration": 15,
|
||||
"preferredExchange": "kraken",
|
||||
"showRecommendedFee": true,
|
||||
"monitoringExpiration": 1440,
|
||||
"showPayInWalletButton": true,
|
||||
"displayExpirationTimer": 5,
|
||||
"excludedPaymentMethods": null,
|
||||
"recommendedFeeBlockTarget": 1
|
||||
}
|
||||
""";
|
||||
var serverConfig = """
|
||||
{
|
||||
"CssUri": null,
|
||||
"FirstRun": false,
|
||||
"LogoFileId": "ce71d90a-dd90-40a3-b1f0-96d00c9abb52",
|
||||
"CustomTheme": true,
|
||||
"CustomThemeCssUri": null,
|
||||
"CustomThemeFileId": "9b00f4ed-914b-437b-abd2-9a90c1b22c34",
|
||||
"CustomThemeExtension": 0
|
||||
}
|
||||
""";
|
||||
await ctx.Database.GetDbConnection().ExecuteAsync("""
|
||||
UPDATE "Stores" SET "StoreBlob"=@storeConfig::JSONB WHERE "Id"=@storeId;
|
||||
""", new { storeId = user.StoreId, storeConfig });
|
||||
await ctx.Database.GetDbConnection().ExecuteAsync("""
|
||||
UPDATE "Settings" SET "Value"=@serverConfig::JSONB WHERE "Id"='BTCPayServer.Services.ThemeSettings';
|
||||
""", new { serverConfig });
|
||||
await ctx.Database.GetDbConnection().ExecuteAsync("""
|
||||
INSERT INTO "Files" VALUES (@id, @fileName, @id || '-' || @fileName, NOW(), @userId);
|
||||
""",
|
||||
new[]
|
||||
{
|
||||
new { id = "2a51c49a-9d54-4013-80a2-3f6e69d08523", fileName = "store.css", userId = user.UserId },
|
||||
new { id = "8f890691-87f9-4c65-80e5-3b7ffaa3551f", fileName = "store.png", userId = user.UserId },
|
||||
new { id = "ce71d90a-dd90-40a3-b1f0-96d00c9abb52", fileName = "admin.png", userId = user.UserId },
|
||||
new { id = "9b00f4ed-914b-437b-abd2-9a90c1b22c34", fileName = "admin.css", userId = user.UserId },
|
||||
new { id = "62bc4757-b92b-4a3b-a8ab-0e9b693d6a29", fileName = "store.mp3", userId = user.UserId },
|
||||
});
|
||||
await ctx.Database.GetDbConnection().ExecuteAsync("""
|
||||
DELETE FROM "__EFMigrationsHistory" WHERE "MigrationId"='20240508015052_fileid'
|
||||
""");
|
||||
await ctx.Database.MigrateAsync();
|
||||
((MemoryCache)tester.PayTester.GetService<IMemoryCache>()).Clear();
|
||||
}
|
||||
|
||||
var controller = tester.PayTester.GetController<UIStoresController>(user.UserId, user.StoreId);
|
||||
var vm = await controller.GeneralSettings().AssertViewModelAsync<GeneralSettingsViewModel>();
|
||||
Assert.Equal(tester.PayTester.ServerUriWithIP + "LocalStorage/8f890691-87f9-4c65-80e5-3b7ffaa3551f-store.png", vm.LogoUrl);
|
||||
Assert.Equal(tester.PayTester.ServerUriWithIP + "LocalStorage/2a51c49a-9d54-4013-80a2-3f6e69d08523-store.css", vm.CssUrl);
|
||||
|
||||
var vm2 = await controller.CheckoutAppearance().AssertViewModelAsync<CheckoutAppearanceViewModel>();
|
||||
Assert.Equal(tester.PayTester.ServerUriWithIP + "LocalStorage/62bc4757-b92b-4a3b-a8ab-0e9b693d6a29-store.mp3", vm2.PaymentSoundUrl);
|
||||
|
||||
var serverController = tester.PayTester.GetController<UIServerController>();
|
||||
var branding = await serverController.Branding().AssertViewModelAsync<BrandingViewModel>();
|
||||
|
||||
Assert.Equal(tester.PayTester.ServerUriWithIP + "LocalStorage/ce71d90a-dd90-40a3-b1f0-96d00c9abb52-admin.png", branding.LogoUrl);
|
||||
Assert.Equal(tester.PayTester.ServerUriWithIP + "LocalStorage/9b00f4ed-914b-437b-abd2-9a90c1b22c34-admin.css", branding.CustomThemeCssUrl);
|
||||
}
|
||||
|
||||
[Fact(Timeout = LongRunningTestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanDoLightningInternalNodeMigration()
|
||||
@ -2943,14 +3026,14 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(StorageProvider.FileSystem,
|
||||
shouldBeRedirectingToLocalStorageConfigPage.RouteValues["provider"]);
|
||||
|
||||
await CanUploadRemoveFiles(controller);
|
||||
var fileId = await CanUploadFile(controller);
|
||||
await CanRemoveFile(controller, fileId);
|
||||
}
|
||||
|
||||
internal static async Task CanUploadRemoveFiles(UIServerController controller)
|
||||
internal static async Task<string> CanUploadFile(UIServerController controller)
|
||||
{
|
||||
var fileContent = "content";
|
||||
List<IFormFile> fileList = new List<IFormFile>();
|
||||
fileList.Add(TestUtils.GetFormFile("uploadtestfile1.txt", fileContent));
|
||||
var fileList = new List<IFormFile> { TestUtils.GetFormFile("uploadtestfile1.txt", fileContent) };
|
||||
|
||||
var uploadFormFileResult = Assert.IsType<RedirectToActionResult>(await controller.CreateFiles(fileList));
|
||||
Assert.True(uploadFormFileResult.RouteValues.ContainsKey("fileIds"));
|
||||
@ -2966,7 +3049,6 @@ namespace BTCPayServer.Tests
|
||||
Assert.True(viewFilesViewModel.DirectUrlByFiles.ContainsKey(fileId));
|
||||
Assert.NotEmpty(viewFilesViewModel.DirectUrlByFiles[fileId]);
|
||||
|
||||
|
||||
//verify file is available and the same
|
||||
using var net = new HttpClient();
|
||||
var data = await net.GetStringAsync(new Uri(viewFilesViewModel.DirectUrlByFiles[fileId]));
|
||||
@ -2991,17 +3073,20 @@ namespace BTCPayServer.Tests
|
||||
data = await net.GetStringAsync(new Uri(url));
|
||||
Assert.Equal(fileContent, data);
|
||||
|
||||
return fileId;
|
||||
}
|
||||
|
||||
internal static async Task CanRemoveFile(UIServerController controller, string fileId)
|
||||
{
|
||||
//delete file
|
||||
Assert.IsType<RedirectToActionResult>(await controller.DeleteFile(fileId));
|
||||
statusMessageModel = controller.TempData.GetStatusMessageModel();
|
||||
var statusMessageModel = controller.TempData.GetStatusMessageModel();
|
||||
Assert.NotNull(statusMessageModel);
|
||||
|
||||
Assert.Equal(StatusMessageModel.StatusSeverity.Success, statusMessageModel.Severity);
|
||||
|
||||
//attempt to fetch deleted file
|
||||
viewFilesViewModel =
|
||||
Assert.IsType<ViewFilesViewModel>(Assert.IsType<ViewResult>(await controller.Files(new string[] { fileId })).Model);
|
||||
var viewFilesViewModel =
|
||||
Assert.IsType<ViewFilesViewModel>(Assert.IsType<ViewResult>(await controller.Files([fileId])).Model);
|
||||
Assert.Null(viewFilesViewModel.DirectUrlByFiles);
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
@using BTCPayServer.Services
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@inject ThemeSettings Theme
|
||||
@inject IFileService FileService
|
||||
@inject UriResolver UriResolver
|
||||
@model BTCPayServer.Components.MainLogo.MainLogoViewModel
|
||||
|
||||
@if (!string.IsNullOrEmpty(Theme.LogoFileId))
|
||||
@if (Theme.LogoUrl is not null)
|
||||
{
|
||||
var logoSrc = await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Theme.LogoFileId);
|
||||
<img src="@logoSrc" alt="BTCPay Server" class="main-logo main-logo-custom @Model.CssClass" />
|
||||
var logoUrl = await UriResolver.Resolve(this.Context.Request.GetAbsoluteRootUri(), Theme.LogoUrl);
|
||||
<img src="@logoUrl" alt="BTCPay Server" class="main-logo main-logo-custom @Model.CssClass" />
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,11 +1,8 @@
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@using BTCPayServer.Client
|
||||
@using BTCPayServer.Components.MainLogo
|
||||
@using BTCPayServer.Services
|
||||
@using BTCPayServer.Views.Server
|
||||
@using BTCPayServer.Views.Stores
|
||||
@inject BTCPayServerEnvironment Env
|
||||
@inject IFileService FileService
|
||||
@model BTCPayServer.Components.StoreSelector.StoreSelectorViewModel
|
||||
@functions {
|
||||
@* ReSharper disable once CSharpWarnings::CS1998 *@
|
||||
@ -39,9 +36,9 @@ else
|
||||
<div id="StoreSelector">
|
||||
<div id="StoreSelectorDropdown" class="dropdown only-for-js">
|
||||
<button id="StoreSelectorToggle" class="btn btn-secondary dropdown-toggle rounded-pill px-3 @(Model.CurrentStoreId == null ? "empty-state" : "")" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
@if (!string.IsNullOrEmpty(Model.CurrentStoreLogoFileId))
|
||||
@if (!string.IsNullOrEmpty(Model.CurrentStoreLogoUrl))
|
||||
{
|
||||
<img class="logo" src="@(await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.CurrentStoreLogoFileId))" alt="@Model.CurrentDisplayName" />
|
||||
<img class="logo" src="@Model.CurrentStoreLogoUrl" alt="@Model.CurrentDisplayName" />
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,8 +1,11 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Payments.Bitcoin;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
@ -14,13 +17,16 @@ namespace BTCPayServer.Components.StoreSelector
|
||||
public class StoreSelector : ViewComponent
|
||||
{
|
||||
private readonly StoreRepository _storeRepo;
|
||||
private readonly UriResolver _uriResolver;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public StoreSelector(
|
||||
StoreRepository storeRepo,
|
||||
UriResolver uriResolver,
|
||||
UserManager<ApplicationUser> userManager)
|
||||
{
|
||||
_storeRepo = storeRepo;
|
||||
_uriResolver = uriResolver;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
@ -50,7 +56,7 @@ namespace BTCPayServer.Components.StoreSelector
|
||||
Options = options,
|
||||
CurrentStoreId = currentStore?.Id,
|
||||
CurrentDisplayName = currentStore?.StoreName,
|
||||
CurrentStoreLogoFileId = blob?.LogoFileId,
|
||||
CurrentStoreLogoUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), blob?.LogoUrl),
|
||||
ArchivedCount = archivedCount
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,7 @@ namespace BTCPayServer.Components.StoreSelector
|
||||
{
|
||||
public List<StoreSelectorOption> Options { get; set; }
|
||||
public string CurrentStoreId { get; set; }
|
||||
public string CurrentStoreLogoFileId { get; set; }
|
||||
public string CurrentStoreLogoUrl { get; set; }
|
||||
public string CurrentDisplayName { get; set; }
|
||||
public int ArchivedCount { get; set; }
|
||||
}
|
||||
|
@ -233,9 +233,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
Description = request.Description?.Trim(),
|
||||
EndDate = request.EndDate?.UtcDateTime,
|
||||
TargetAmount = request.TargetAmount,
|
||||
CustomCSSLink = request.CustomCSSLink?.Trim(),
|
||||
MainImageUrl = request.MainImageUrl?.Trim(),
|
||||
EmbeddedCSS = request.EmbeddedCSS?.Trim(),
|
||||
NotificationUrl = request.NotificationUrl?.Trim(),
|
||||
Tagline = request.Tagline?.Trim(),
|
||||
PerksTemplate = request.PerksTemplate is not null ? AppService.SerializeTemplate(AppService.Parse(request.PerksTemplate.Trim())) : null,
|
||||
@ -272,11 +270,9 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
ButtonText = request.FixedAmountPayButtonText ?? PointOfSaleSettings.BUTTON_TEXT_DEF,
|
||||
CustomButtonText = request.CustomAmountPayButtonText ?? PointOfSaleSettings.CUSTOM_BUTTON_TEXT_DEF,
|
||||
CustomTipText = request.TipText ?? PointOfSaleSettings.CUSTOM_TIP_TEXT_DEF,
|
||||
CustomCSSLink = request.CustomCSSLink,
|
||||
NotificationUrl = request.NotificationUrl,
|
||||
RedirectUrl = request.RedirectUrl,
|
||||
Description = request.Description,
|
||||
EmbeddedCSS = request.EmbeddedCSS,
|
||||
RedirectAutomatically = request.RedirectAutomatically,
|
||||
FormId = request.FormId
|
||||
};
|
||||
@ -341,11 +337,9 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
FixedAmountPayButtonText = settings.ButtonText,
|
||||
CustomAmountPayButtonText = settings.CustomButtonText,
|
||||
TipText = settings.CustomTipText,
|
||||
CustomCSSLink = settings.CustomCSSLink,
|
||||
NotificationUrl = settings.NotificationUrl,
|
||||
RedirectUrl = settings.RedirectUrl,
|
||||
Description = settings.Description,
|
||||
EmbeddedCSS = settings.EmbeddedCSS,
|
||||
RedirectAutomatically = settings.RedirectAutomatically ?? false,
|
||||
};
|
||||
}
|
||||
@ -399,9 +393,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
Description = settings.Description,
|
||||
EndDate = settings.EndDate,
|
||||
TargetAmount = settings.TargetAmount,
|
||||
CustomCSSLink = settings.CustomCSSLink,
|
||||
MainImageUrl = settings.MainImageUrl,
|
||||
EmbeddedCSS = settings.EmbeddedCSS,
|
||||
NotificationUrl = settings.NotificationUrl,
|
||||
Tagline = settings.Tagline,
|
||||
Perks = JsonConvert.DeserializeObject(
|
||||
|
@ -234,9 +234,6 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
if (string.IsNullOrEmpty(data.Title))
|
||||
ModelState.AddModelError(nameof(data.Title), "Title is required");
|
||||
|
||||
if (!string.IsNullOrEmpty(data.CustomCSSLink) && data.CustomCSSLink.Length > 500)
|
||||
ModelState.AddModelError(nameof(data.CustomCSSLink), "CustomCSSLink is 500 chars max");
|
||||
|
||||
return !ModelState.IsValid ? this.CreateValidationError(ModelState) : null;
|
||||
}
|
||||
|
||||
@ -257,8 +254,6 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
ExpiryDate = blob.ExpiryDate,
|
||||
Email = blob.Email,
|
||||
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts,
|
||||
EmbeddedCSS = blob.EmbeddedCSS,
|
||||
CustomCSSLink = blob.CustomCSSLink,
|
||||
FormResponse = blob.FormResponse,
|
||||
FormId = blob.FormId
|
||||
};
|
||||
|
@ -116,6 +116,10 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
Name = data.StoreName,
|
||||
Website = data.StoreWebsite,
|
||||
Archived = data.Archived,
|
||||
BrandColor = storeBlob.BrandColor,
|
||||
CssUrl = storeBlob.CssUrl?.ToString(),
|
||||
LogoUrl = storeBlob.LogoUrl?.ToString(),
|
||||
PaymentSoundUrl = storeBlob.PaymentSoundUrl?.ToString(),
|
||||
SupportUrl = storeBlob.StoreSupportUrl,
|
||||
SpeedPolicy = data.SpeedPolicy,
|
||||
DefaultPaymentMethod = data.GetDefaultPaymentId()?.ToString(),
|
||||
@ -192,6 +196,10 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
blob.LightningDescriptionTemplate = restModel.LightningDescriptionTemplate;
|
||||
blob.PaymentTolerance = restModel.PaymentTolerance;
|
||||
blob.PayJoinEnabled = restModel.PayJoinEnabled;
|
||||
blob.BrandColor = restModel.BrandColor;
|
||||
blob.LogoUrl = restModel.LogoUrl is null ? null : UnresolvedUri.Create(restModel.LogoUrl);
|
||||
blob.CssUrl = restModel.CssUrl is null ? null : UnresolvedUri.Create(restModel.CssUrl);
|
||||
blob.PaymentSoundUrl = restModel.PaymentSoundUrl is null ? null : UnresolvedUri.Create(restModel.PaymentSoundUrl);
|
||||
if (restModel.AutoDetectLanguage.HasValue)
|
||||
blob.AutoDetectLanguage = restModel.AutoDetectLanguage.Value;
|
||||
if (restModel.ShowPayInWalletButton.HasValue)
|
||||
@ -237,6 +245,18 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.Website), "Website is not a valid url");
|
||||
}
|
||||
if (!string.IsNullOrEmpty(request.LogoUrl) && !Uri.TryCreate(request.LogoUrl, UriKind.Absolute, out _))
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.LogoUrl), "Logo is not a valid url");
|
||||
}
|
||||
if (!string.IsNullOrEmpty(request.CssUrl) && !Uri.TryCreate(request.CssUrl, UriKind.Absolute, out _))
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.CssUrl), "CSS is not a valid url");
|
||||
}
|
||||
if (!string.IsNullOrEmpty(request.BrandColor) && !ColorPalette.IsValid(request.BrandColor))
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.BrandColor), "Brand color is not a valid HEX Color (e.g. #F7931A)");
|
||||
}
|
||||
if (request.InvoiceExpiration < TimeSpan.FromMinutes(1) && request.InvoiceExpiration > TimeSpan.FromMinutes(60 * 24 * 24))
|
||||
ModelState.AddModelError(nameof(request.InvoiceExpiration), "InvoiceExpiration can only be between 1 and 34560 mins");
|
||||
if (request.DisplayExpirationTimer < TimeSpan.FromMinutes(1) && request.DisplayExpirationTimer > TimeSpan.FromMinutes(60 * 24 * 24))
|
||||
|
@ -219,7 +219,7 @@ namespace BTCPayServer.Controllers
|
||||
Currency = i.Currency,
|
||||
Timestamp = i.InvoiceTime,
|
||||
StoreName = store.StoreName,
|
||||
StoreBranding = new StoreBrandingViewModel(storeBlob),
|
||||
StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob),
|
||||
ReceiptOptions = receipt
|
||||
};
|
||||
|
||||
@ -889,7 +889,7 @@ namespace BTCPayServer.Controllers
|
||||
DefaultLang = lang ?? invoice.DefaultLanguage ?? storeBlob.DefaultLang ?? "en",
|
||||
ShowPayInWalletButton = storeBlob.ShowPayInWalletButton,
|
||||
ShowStoreHeader = storeBlob.ShowStoreHeader,
|
||||
StoreBranding = new StoreBrandingViewModel(storeBlob),
|
||||
StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob),
|
||||
HtmlTitle = storeBlob.HtmlTitle ?? "BTCPay Invoice",
|
||||
CelebratePayment = storeBlob.CelebratePayment,
|
||||
OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback,
|
||||
@ -978,9 +978,9 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (storeBlob.PlaySoundOnPayment)
|
||||
{
|
||||
model.PaymentSoundUrl = string.IsNullOrEmpty(storeBlob.SoundFileId)
|
||||
model.PaymentSoundUrl = storeBlob.PaymentSoundUrl is null
|
||||
? string.Concat(Request.GetAbsoluteRootUri().ToString(), "checkout/payment.mp3")
|
||||
: await _fileService.GetFileUrl(Request.GetAbsoluteRootUri(), storeBlob.SoundFileId);
|
||||
: await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), storeBlob.PaymentSoundUrl);
|
||||
model.ErrorSoundUrl = string.Concat(Request.GetAbsoluteRootUri().ToString(), "checkout/error.mp3");
|
||||
model.NfcReadSoundUrl = string.Concat(Request.GetAbsoluteRootUri().ToString(), "checkout/nfcread.mp3");
|
||||
}
|
||||
|
@ -65,6 +65,7 @@ namespace BTCPayServer.Controllers
|
||||
private readonly PaymentMethodViewProvider _viewProvider;
|
||||
private readonly AppService _appService;
|
||||
private readonly IFileService _fileService;
|
||||
private readonly UriResolver _uriResolver;
|
||||
|
||||
public WebhookSender WebhookNotificationManager { get; }
|
||||
|
||||
@ -91,6 +92,7 @@ namespace BTCPayServer.Controllers
|
||||
LinkGenerator linkGenerator,
|
||||
AppService appService,
|
||||
IFileService fileService,
|
||||
UriResolver uriResolver,
|
||||
IAuthorizationService authorizationService,
|
||||
TransactionLinkProviders transactionLinkProviders,
|
||||
Dictionary<PaymentMethodId, IPaymentModelExtension> paymentModelExtensions,
|
||||
@ -120,6 +122,7 @@ namespace BTCPayServer.Controllers
|
||||
_paymentModelExtensions = paymentModelExtensions;
|
||||
_viewProvider = viewProvider;
|
||||
_fileService = fileService;
|
||||
_uriResolver = uriResolver;
|
||||
_appService = appService;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ namespace BTCPayServer.Controllers
|
||||
private readonly DisplayFormatter _displayFormatter;
|
||||
private readonly InvoiceRepository _InvoiceRepository;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly UriResolver _uriResolver;
|
||||
private readonly BTCPayNetworkProvider _networkProvider;
|
||||
|
||||
private FormComponentProviders FormProviders { get; }
|
||||
@ -55,6 +56,7 @@ namespace BTCPayServer.Controllers
|
||||
CurrencyNameTable currencies,
|
||||
DisplayFormatter displayFormatter,
|
||||
StoreRepository storeRepository,
|
||||
UriResolver uriResolver,
|
||||
InvoiceRepository invoiceRepository,
|
||||
FormComponentProviders formProviders,
|
||||
FormDataService formDataService,
|
||||
@ -68,6 +70,7 @@ namespace BTCPayServer.Controllers
|
||||
_Currencies = currencies;
|
||||
_displayFormatter = displayFormatter;
|
||||
_storeRepository = storeRepository;
|
||||
_uriResolver = uriResolver;
|
||||
_InvoiceRepository = invoiceRepository;
|
||||
FormProviders = formProviders;
|
||||
FormDataService = formDataService;
|
||||
@ -191,8 +194,6 @@ namespace BTCPayServer.Controllers
|
||||
blob.Amount = viewModel.Amount;
|
||||
blob.ExpiryDate = viewModel.ExpiryDate?.ToUniversalTime();
|
||||
blob.Currency = viewModel.Currency ?? store.GetStoreBlob().DefaultCurrency;
|
||||
blob.EmbeddedCSS = viewModel.EmbeddedCSS;
|
||||
blob.CustomCSSLink = viewModel.CustomCSSLink;
|
||||
blob.AllowCustomPaymentAmounts = viewModel.AllowCustomPaymentAmounts;
|
||||
blob.FormId = viewModel.FormId;
|
||||
|
||||
@ -229,11 +230,7 @@ namespace BTCPayServer.Controllers
|
||||
vm.HubPath = PaymentRequestHub.GetHubPath(Request);
|
||||
vm.StoreName = store.StoreName;
|
||||
vm.StoreWebsite = store.StoreWebsite;
|
||||
vm.StoreBranding = new StoreBrandingViewModel(storeBlob)
|
||||
{
|
||||
EmbeddedCSS = vm.EmbeddedCSS,
|
||||
CustomCSSLink = vm.CustomCSSLink
|
||||
};
|
||||
vm.StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob);
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
@ -290,11 +287,8 @@ namespace BTCPayServer.Controllers
|
||||
viewModel.Form = form;
|
||||
|
||||
var storeBlob = result.StoreData.GetStoreBlob();
|
||||
viewModel.StoreBranding = new StoreBrandingViewModel(storeBlob)
|
||||
{
|
||||
EmbeddedCSS = prBlob.EmbeddedCSS,
|
||||
CustomCSSLink = prBlob.CustomCSSLink
|
||||
};
|
||||
viewModel.StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob);
|
||||
|
||||
return View("Views/UIForms/View", viewModel);
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -21,16 +22,19 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
private readonly BTCPayNetworkProvider _BtcPayNetworkProvider;
|
||||
private readonly Dictionary<PaymentMethodId, IPaymentModelExtension> _paymentModelExtensions;
|
||||
private readonly UriResolver _uriResolver;
|
||||
private readonly PaymentMethodHandlerDictionary _handlers;
|
||||
private readonly StoreRepository _StoreRepository;
|
||||
|
||||
public UIPublicLightningNodeInfoController(BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
Dictionary<PaymentMethodId, IPaymentModelExtension> paymentModelExtensions,
|
||||
UriResolver uriResolver,
|
||||
PaymentMethodHandlerDictionary handlers,
|
||||
StoreRepository storeRepository)
|
||||
{
|
||||
_BtcPayNetworkProvider = btcPayNetworkProvider;
|
||||
_paymentModelExtensions = paymentModelExtensions;
|
||||
_uriResolver = uriResolver;
|
||||
_handlers = handlers;
|
||||
_StoreRepository = storeRepository;
|
||||
}
|
||||
@ -49,7 +53,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
CryptoCode = cryptoCode,
|
||||
StoreName = store.StoreName,
|
||||
StoreBranding = new StoreBrandingViewModel(storeBlob)
|
||||
StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob)
|
||||
};
|
||||
try
|
||||
{
|
||||
|
@ -40,6 +40,7 @@ namespace BTCPayServer.Controllers
|
||||
private readonly ApplicationDbContextFactory _dbContextFactory;
|
||||
private readonly CurrencyNameTable _currencyNameTable;
|
||||
private readonly DisplayFormatter _displayFormatter;
|
||||
private readonly UriResolver _uriResolver;
|
||||
private readonly PullPaymentHostedService _pullPaymentHostedService;
|
||||
private readonly BTCPayNetworkProvider _networkProvider;
|
||||
private readonly BTCPayNetworkJsonSerializerSettings _serializerSettings;
|
||||
@ -51,6 +52,7 @@ namespace BTCPayServer.Controllers
|
||||
public UIPullPaymentController(ApplicationDbContextFactory dbContextFactory,
|
||||
CurrencyNameTable currencyNameTable,
|
||||
DisplayFormatter displayFormatter,
|
||||
UriResolver uriResolver,
|
||||
PullPaymentHostedService pullPaymentHostedService,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
BTCPayNetworkJsonSerializerSettings serializerSettings,
|
||||
@ -62,6 +64,7 @@ namespace BTCPayServer.Controllers
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_currencyNameTable = currencyNameTable;
|
||||
_displayFormatter = displayFormatter;
|
||||
_uriResolver = uriResolver;
|
||||
_pullPaymentHostedService = pullPaymentHostedService;
|
||||
_serializerSettings = serializerSettings;
|
||||
_payoutHandlers = payoutHandlers;
|
||||
@ -121,11 +124,7 @@ namespace BTCPayServer.Controllers
|
||||
}).ToList()
|
||||
};
|
||||
vm.IsPending &= vm.AmountDue > 0.0m;
|
||||
vm.StoreBranding = new StoreBrandingViewModel(storeBlob)
|
||||
{
|
||||
EmbeddedCSS = blob.View.EmbeddedCSS,
|
||||
CustomCSSLink = blob.View.CustomCSSLink
|
||||
};
|
||||
vm.StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob);
|
||||
|
||||
if (_pullPaymentHostedService.SupportsLNURL(blob))
|
||||
{
|
||||
@ -185,13 +184,11 @@ namespace BTCPayServer.Controllers
|
||||
var blob = pp.GetBlob();
|
||||
blob.Description = viewModel.Description ?? string.Empty;
|
||||
blob.Name = viewModel.Name ?? string.Empty;
|
||||
blob.View = new PullPaymentBlob.PullPaymentView()
|
||||
blob.View = new PullPaymentBlob.PullPaymentView
|
||||
{
|
||||
Title = viewModel.Name ?? string.Empty,
|
||||
Description = viewModel.Description ?? string.Empty,
|
||||
CustomCSSLink = viewModel.CustomCSSLink,
|
||||
Email = null,
|
||||
EmbeddedCSS = viewModel.EmbeddedCSS,
|
||||
Email = null
|
||||
};
|
||||
|
||||
pp.SetBlob(blob);
|
||||
|
@ -65,6 +65,7 @@ namespace BTCPayServer.Controllers
|
||||
private readonly IFileService _fileService;
|
||||
private readonly IEnumerable<IStorageProviderService> _StorageProviderServices;
|
||||
private readonly LinkGenerator _linkGenerator;
|
||||
private readonly UriResolver _uriResolver;
|
||||
private readonly EmailSenderFactory _emailSenderFactory;
|
||||
private readonly TransactionLinkProviders _transactionLinkProviders;
|
||||
|
||||
@ -88,6 +89,7 @@ namespace BTCPayServer.Controllers
|
||||
IOptions<ExternalServicesOptions> externalServiceOptions,
|
||||
Logs logs,
|
||||
LinkGenerator linkGenerator,
|
||||
UriResolver uriResolver,
|
||||
EmailSenderFactory emailSenderFactory,
|
||||
IHostApplicationLifetime applicationLifetime,
|
||||
IHtmlHelper html,
|
||||
@ -113,6 +115,7 @@ namespace BTCPayServer.Controllers
|
||||
_externalServiceOptions = externalServiceOptions;
|
||||
Logs = logs;
|
||||
_linkGenerator = linkGenerator;
|
||||
_uriResolver = uriResolver;
|
||||
_emailSenderFactory = emailSenderFactory;
|
||||
ApplicationLifetime = applicationLifetime;
|
||||
Html = html;
|
||||
@ -1025,9 +1028,8 @@ namespace BTCPayServer.Controllers
|
||||
ContactUrl = server.ContactUrl,
|
||||
CustomTheme = theme.CustomTheme,
|
||||
CustomThemeExtension = theme.CustomThemeExtension,
|
||||
CustomThemeCssUri = theme.CustomThemeCssUri,
|
||||
CustomThemeFileId = theme.CustomThemeFileId,
|
||||
LogoFileId = theme.LogoFileId
|
||||
CustomThemeCssUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), theme.CustomThemeCssUrl),
|
||||
LogoUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), theme.LogoUrl)
|
||||
};
|
||||
return View(vm);
|
||||
}
|
||||
@ -1046,8 +1048,8 @@ namespace BTCPayServer.Controllers
|
||||
if (userId is null)
|
||||
return NotFound();
|
||||
|
||||
vm.LogoFileId = theme.LogoFileId;
|
||||
vm.CustomThemeFileId = theme.CustomThemeFileId;
|
||||
vm.LogoUrl = await _uriResolver.Resolve(this.Request.GetAbsoluteRootUri(), theme.LogoUrl);
|
||||
vm.CustomThemeCssUrl = await _uriResolver.Resolve(this.Request.GetAbsoluteRootUri(), theme.CustomThemeCssUrl);
|
||||
|
||||
if (server.ServerName != vm.ServerName)
|
||||
{
|
||||
@ -1072,17 +1074,12 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (vm.CustomThemeFile.ContentType.Equals("text/css", StringComparison.InvariantCulture))
|
||||
{
|
||||
// delete existing file
|
||||
if (!string.IsNullOrEmpty(theme.CustomThemeFileId))
|
||||
{
|
||||
await _fileService.RemoveFile(theme.CustomThemeFileId, userId);
|
||||
}
|
||||
|
||||
// add new file
|
||||
try
|
||||
{
|
||||
var storedFile = await _fileService.AddFile(vm.CustomThemeFile, userId);
|
||||
vm.CustomThemeFileId = theme.CustomThemeFileId = storedFile.Id;
|
||||
theme.CustomThemeCssUrl = new UnresolvedUri.FileIdUri(storedFile.Id);
|
||||
vm.CustomThemeCssUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), theme.CustomThemeCssUrl);
|
||||
settingsChanged = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -1095,10 +1092,12 @@ namespace BTCPayServer.Controllers
|
||||
ModelState.AddModelError(nameof(vm.CustomThemeFile), "The uploaded theme file needs to be a CSS file");
|
||||
}
|
||||
}
|
||||
else if (RemoveCustomThemeFile && !string.IsNullOrEmpty(theme.CustomThemeFileId))
|
||||
else if (RemoveCustomThemeFile && theme.CustomThemeCssUrl is not null)
|
||||
{
|
||||
await _fileService.RemoveFile(theme.CustomThemeFileId, userId);
|
||||
vm.CustomThemeFileId = theme.CustomThemeFileId = null;
|
||||
vm.CustomThemeCssUrl = null;
|
||||
theme.CustomThemeCssUrl = null;
|
||||
theme.CustomTheme = false;
|
||||
theme.CustomThemeExtension = ThemeExtension.Custom;
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
@ -1122,16 +1121,12 @@ namespace BTCPayServer.Controllers
|
||||
else
|
||||
{
|
||||
vm.LogoFile = formFile;
|
||||
// delete existing file
|
||||
if (!string.IsNullOrEmpty(theme.LogoFileId))
|
||||
{
|
||||
await _fileService.RemoveFile(theme.LogoFileId, userId);
|
||||
}
|
||||
// add new file
|
||||
try
|
||||
{
|
||||
var storedFile = await _fileService.AddFile(vm.LogoFile, userId);
|
||||
vm.LogoFileId = theme.LogoFileId = storedFile.Id;
|
||||
theme.LogoUrl = new UnresolvedUri.FileIdUri(storedFile.Id);
|
||||
vm.LogoUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), theme.LogoUrl);
|
||||
settingsChanged = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -1141,29 +1136,19 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (RemoveLogoFile && !string.IsNullOrEmpty(theme.LogoFileId))
|
||||
else if (RemoveLogoFile && theme.LogoUrl is not null)
|
||||
{
|
||||
await _fileService.RemoveFile(theme.LogoFileId, userId);
|
||||
vm.LogoFileId = theme.LogoFileId = null;
|
||||
vm.LogoUrl = null;
|
||||
theme.LogoUrl = null;
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
if (vm.CustomTheme && !string.IsNullOrEmpty(vm.CustomThemeCssUri) && !Uri.IsWellFormedUriString(vm.CustomThemeCssUri, UriKind.RelativeOrAbsolute))
|
||||
{
|
||||
ModelState.AddModelError(nameof(theme.CustomThemeCssUri), "Please provide a non-empty theme URI");
|
||||
}
|
||||
else if (theme.CustomThemeCssUri != vm.CustomThemeCssUri)
|
||||
{
|
||||
theme.CustomThemeCssUri = vm.CustomThemeCssUri;
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
if (theme.CustomThemeExtension != vm.CustomThemeExtension)
|
||||
if (vm.CustomTheme && theme.CustomThemeExtension != vm.CustomThemeExtension)
|
||||
{
|
||||
// Require a custom theme to be defined in that case
|
||||
if (string.IsNullOrEmpty(vm.CustomThemeCssUri) && string.IsNullOrEmpty(theme.CustomThemeFileId))
|
||||
if (string.IsNullOrEmpty(vm.CustomThemeCssUrl) && theme.CustomThemeCssUrl is null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.CustomThemeFile), "Please provide a custom theme");
|
||||
ModelState.AddModelError(nameof(vm.CustomThemeCssUrl), "Please provide a custom theme");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1172,7 +1157,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
if (theme.CustomTheme != vm.CustomTheme)
|
||||
if (theme.CustomTheme != vm.CustomTheme && !RemoveCustomThemeFile)
|
||||
{
|
||||
theme.CustomTheme = vm.CustomTheme;
|
||||
settingsChanged = true;
|
||||
@ -1182,6 +1167,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
await _SettingsRepository.UpdateSetting(theme);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Settings updated successfully";
|
||||
return RedirectToAction(nameof(Branding));
|
||||
}
|
||||
|
||||
return View(vm);
|
||||
|
@ -95,8 +95,6 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
Name = "",
|
||||
Currency = CurrentStore.GetStoreBlob().DefaultCurrency,
|
||||
CustomCSSLink = "",
|
||||
EmbeddedCSS = "",
|
||||
PayoutMethodsItem =
|
||||
paymentMethods.Select(id => new SelectListItem(id.ToString(), id.ToString(), true))
|
||||
});
|
||||
@ -145,7 +143,7 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
model.AutoApproveClaims = model.AutoApproveClaims && (await
|
||||
_authorizationService.AuthorizeAsync(User, storeId, Policies.CanCreatePullPayments)).Succeeded;
|
||||
await _pullPaymentService.CreatePullPayment(new HostedServices.CreatePullPayment()
|
||||
await _pullPaymentService.CreatePullPayment(new CreatePullPayment
|
||||
{
|
||||
Name = model.Name,
|
||||
Description = model.Description,
|
||||
@ -153,17 +151,15 @@ namespace BTCPayServer.Controllers
|
||||
Currency = model.Currency,
|
||||
StoreId = storeId,
|
||||
PayoutMethodIds = selectedPaymentMethodIds,
|
||||
EmbeddedCSS = model.EmbeddedCSS,
|
||||
CustomCSSLink = model.CustomCSSLink,
|
||||
BOLT11Expiration = TimeSpan.FromDays(model.BOLT11Expiration),
|
||||
AutoApproveClaims = model.AutoApproveClaims
|
||||
});
|
||||
this.TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = "Pull payment request created",
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
return RedirectToAction(nameof(PullPayments), new { storeId = storeId });
|
||||
return RedirectToAction(nameof(PullPayments), new { storeId });
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanViewPullPayments, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data;
|
||||
@ -19,20 +20,19 @@ namespace BTCPayServer.Controllers;
|
||||
public partial class UIStoresController
|
||||
{
|
||||
[HttpGet("{storeId}/settings")]
|
||||
public IActionResult GeneralSettings()
|
||||
public async Task<IActionResult> GeneralSettings()
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var vm = new GeneralSettingsViewModel
|
||||
{
|
||||
Id = store.Id,
|
||||
StoreName = store.StoreName,
|
||||
StoreWebsite = store.StoreWebsite,
|
||||
LogoFileId = storeBlob.LogoFileId,
|
||||
CssFileId = storeBlob.CssFileId,
|
||||
LogoUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), storeBlob.LogoUrl),
|
||||
CssUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), storeBlob.CssUrl),
|
||||
BrandColor = storeBlob.BrandColor,
|
||||
NetworkFeeMode = storeBlob.NetworkFeeMode,
|
||||
AnyoneCanCreateInvoice = storeBlob.AnyoneCanInvoice,
|
||||
@ -104,16 +104,11 @@ public partial class UIStoresController
|
||||
else
|
||||
{
|
||||
model.LogoFile = formFile;
|
||||
// delete existing file
|
||||
if (!string.IsNullOrEmpty(blob.LogoFileId))
|
||||
{
|
||||
await _fileService.RemoveFile(blob.LogoFileId, userId);
|
||||
}
|
||||
// add new image
|
||||
try
|
||||
{
|
||||
var storedFile = await _fileService.AddFile(model.LogoFile, userId);
|
||||
blob.LogoFileId = storedFile.Id;
|
||||
blob.LogoUrl = new UnresolvedUri.FileIdUri(storedFile.Id);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -122,10 +117,9 @@ public partial class UIStoresController
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (RemoveLogoFile && !string.IsNullOrEmpty(blob.LogoFileId))
|
||||
else if (RemoveLogoFile && blob.LogoUrl is not null)
|
||||
{
|
||||
await _fileService.RemoveFile(blob.LogoFileId, userId);
|
||||
blob.LogoFileId = null;
|
||||
blob.LogoUrl = null;
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
@ -145,16 +139,11 @@ public partial class UIStoresController
|
||||
}
|
||||
else
|
||||
{
|
||||
// delete existing file
|
||||
if (!string.IsNullOrEmpty(blob.CssFileId))
|
||||
{
|
||||
await _fileService.RemoveFile(blob.CssFileId, userId);
|
||||
}
|
||||
// add new file
|
||||
try
|
||||
{
|
||||
var storedFile = await _fileService.AddFile(model.CssFile, userId);
|
||||
blob.CssFileId = storedFile.Id;
|
||||
blob.CssUrl = new UnresolvedUri.FileIdUri(storedFile.Id);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -162,10 +151,9 @@ public partial class UIStoresController
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (RemoveCssFile && !string.IsNullOrEmpty(blob.CssFileId))
|
||||
else if (RemoveCssFile && blob.CssUrl is not null)
|
||||
{
|
||||
await _fileService.RemoveFile(blob.CssFileId, userId);
|
||||
blob.CssFileId = null;
|
||||
blob.CssUrl = null;
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
@ -221,7 +209,7 @@ public partial class UIStoresController
|
||||
}
|
||||
|
||||
[HttpGet("{storeId}/checkout")]
|
||||
public IActionResult CheckoutAppearance()
|
||||
public async Task<IActionResult> CheckoutAppearance()
|
||||
{
|
||||
var storeBlob = CurrentStore.GetStoreBlob();
|
||||
var vm = new CheckoutAppearanceViewModel();
|
||||
@ -253,7 +241,9 @@ public partial class UIStoresController
|
||||
vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi;
|
||||
vm.LazyPaymentMethods = storeBlob.LazyPaymentMethods;
|
||||
vm.RedirectAutomatically = storeBlob.RedirectAutomatically;
|
||||
vm.SoundFileId = storeBlob.SoundFileId;
|
||||
vm.PaymentSoundUrl = storeBlob.PaymentSoundUrl is null
|
||||
? string.Concat(Request.GetAbsoluteRootUri().ToString(), "checkout/payment.mp3")
|
||||
: await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), storeBlob.PaymentSoundUrl);
|
||||
vm.HtmlTitle = storeBlob.HtmlTitle;
|
||||
vm.SupportUrl = storeBlob.StoreSupportUrl;
|
||||
vm.DisplayExpirationTimer = (int)storeBlob.DisplayExpirationTimer.TotalMinutes;
|
||||
@ -316,17 +306,11 @@ public partial class UIStoresController
|
||||
else
|
||||
{
|
||||
model.SoundFile = formFile;
|
||||
// delete existing file
|
||||
if (!string.IsNullOrEmpty(blob.SoundFileId))
|
||||
{
|
||||
await _fileService.RemoveFile(blob.SoundFileId, userId);
|
||||
}
|
||||
|
||||
// add new file
|
||||
try
|
||||
{
|
||||
var storedFile = await _fileService.AddFile(model.SoundFile, userId);
|
||||
blob.SoundFileId = storedFile.Id;
|
||||
blob.PaymentSoundUrl = new UnresolvedUri.FileIdUri(storedFile.Id);
|
||||
needUpdate = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -336,10 +320,9 @@ public partial class UIStoresController
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (RemoveSoundFile && !string.IsNullOrEmpty(blob.SoundFileId))
|
||||
else if (RemoveSoundFile && blob.PaymentSoundUrl is not null)
|
||||
{
|
||||
await _fileService.RemoveFile(blob.SoundFileId, userId);
|
||||
blob.SoundFileId = null;
|
||||
blob.PaymentSoundUrl = null;
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,7 @@ public partial class UIStoresController : Controller
|
||||
IHtmlHelper html,
|
||||
EmailSenderFactory emailSenderFactory,
|
||||
WalletFileParsers onChainWalletParsers,
|
||||
UriResolver uriResolver,
|
||||
SettingsRepository settingsRepository,
|
||||
EventAggregator eventAggregator)
|
||||
{
|
||||
@ -78,6 +79,7 @@ public partial class UIStoresController : Controller
|
||||
_externalServiceOptions = externalServiceOptions;
|
||||
_emailSenderFactory = emailSenderFactory;
|
||||
_onChainWalletParsers = onChainWalletParsers;
|
||||
_uriResolver = uriResolver;
|
||||
_settingsRepository = settingsRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
_html = html;
|
||||
@ -106,6 +108,7 @@ public partial class UIStoresController : Controller
|
||||
private readonly IOptions<ExternalServicesOptions> _externalServiceOptions;
|
||||
private readonly EmailSenderFactory _emailSenderFactory;
|
||||
private readonly WalletFileParsers _onChainWalletParsers;
|
||||
private readonly UriResolver _uriResolver;
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private readonly IHtmlHelper _html;
|
||||
private readonly WebhookSender _webhookNotificationManager;
|
||||
|
@ -35,9 +35,7 @@ namespace BTCPayServer.Data
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string EmbeddedCSS { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string CustomCSSLink { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,6 @@ using BTCPayServer.JsonConverters;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Services.Mails;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
@ -217,8 +215,10 @@ namespace BTCPayServer.Data
|
||||
|
||||
public List<UIStoresController.StoreEmailRule> EmailRules { get; set; }
|
||||
public string BrandColor { get; set; }
|
||||
public string LogoFileId { get; set; }
|
||||
public string CssFileId { get; set; }
|
||||
[JsonConverter(typeof(UnresolvedUriJsonConverter))]
|
||||
public UnresolvedUri LogoUrl { get; set; }
|
||||
[JsonConverter(typeof(UnresolvedUriJsonConverter))]
|
||||
public UnresolvedUri CssUrl { get; set; }
|
||||
|
||||
[DefaultValue(true)]
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||
@ -236,7 +236,8 @@ namespace BTCPayServer.Data
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||
public bool PlaySoundOnPayment { get; set; }
|
||||
|
||||
public string SoundFileId { get; set; }
|
||||
[JsonConverter(typeof(UnresolvedUriJsonConverter))]
|
||||
public UnresolvedUri PaymentSoundUrl { get; set; }
|
||||
|
||||
public IPaymentFilter GetExcludedPaymentMethods()
|
||||
{
|
||||
|
@ -14,6 +14,7 @@ using BTCPayServer.Data;
|
||||
using BTCPayServer.Filters;
|
||||
using BTCPayServer.Forms.Models;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@ -26,15 +27,18 @@ namespace BTCPayServer.Forms;
|
||||
public class UIFormsController : Controller
|
||||
{
|
||||
private readonly FormDataService _formDataService;
|
||||
private readonly UriResolver _uriResolver;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private FormComponentProviders FormProviders { get; }
|
||||
|
||||
public UIFormsController(FormComponentProviders formProviders, FormDataService formDataService,
|
||||
UriResolver uriResolver,
|
||||
StoreRepository storeRepository, IAuthorizationService authorizationService)
|
||||
{
|
||||
FormProviders = formProviders;
|
||||
_formDataService = formDataService;
|
||||
_uriResolver = uriResolver;
|
||||
_authorizationService = authorizationService;
|
||||
_storeRepository = storeRepository;
|
||||
}
|
||||
@ -169,7 +173,7 @@ public class UIFormsController : Controller
|
||||
FormName = formData.Name,
|
||||
Form = form,
|
||||
StoreName = store?.StoreName,
|
||||
StoreBranding = new StoreBrandingViewModel(storeBlob)
|
||||
StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob)
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -38,8 +38,6 @@ namespace BTCPayServer.HostedServices
|
||||
public string Description { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public string CustomCSSLink { get; set; }
|
||||
public string EmbeddedCSS { get; set; }
|
||||
public PayoutMethodId[] PayoutMethodIds { get; set; }
|
||||
public bool AutoApproveClaims { get; set; }
|
||||
public TimeSpan? BOLT11Expiration { get; set; }
|
||||
@ -131,13 +129,11 @@ namespace BTCPayServer.HostedServices
|
||||
Limit = create.Amount,
|
||||
SupportedPaymentMethods = create.PayoutMethodIds,
|
||||
AutoApproveClaims = create.AutoApproveClaims,
|
||||
View = new PullPaymentBlob.PullPaymentView()
|
||||
View = new PullPaymentBlob.PullPaymentView
|
||||
{
|
||||
Title = create.Name ?? string.Empty,
|
||||
Description = create.Description ?? string.Empty,
|
||||
CustomCSSLink = create.CustomCSSLink,
|
||||
Email = null,
|
||||
EmbeddedCSS = create.EmbeddedCSS,
|
||||
Email = null
|
||||
},
|
||||
BOLT11Expiration = create.BOLT11Expiration ?? TimeSpan.FromDays(30.0)
|
||||
});
|
||||
|
@ -168,6 +168,7 @@ namespace BTCPayServer.Hosting
|
||||
services.TryAddSingleton<EventAggregator>();
|
||||
services.TryAddSingleton<PaymentRequestService>();
|
||||
services.TryAddSingleton<UserService>();
|
||||
services.TryAddSingleton<UriResolver>();
|
||||
services.TryAddSingleton<WalletHistogramService>();
|
||||
services.AddSingleton<ApplicationDbContextFactory>();
|
||||
services.AddOptions<BTCPayServerOptions>().Configure(
|
||||
|
@ -4,9 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Common;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Fido2;
|
||||
@ -26,17 +24,14 @@ using BTCPayServer.Services.Stores;
|
||||
using BTCPayServer.Storage.Models;
|
||||
using BTCPayServer.Storage.Services.Providers.FileSystemStorage.Configuration;
|
||||
using Fido2NetLib.Objects;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PeterO.Cbor;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
using LightningAddressData = BTCPayServer.Data.LightningAddressData;
|
||||
using Serializer = NBXplorer.Serializer;
|
||||
|
||||
namespace BTCPayServer.Hosting
|
||||
{
|
||||
@ -53,6 +48,7 @@ namespace BTCPayServer.Hosting
|
||||
private readonly LightningAddressService _lightningAddressService;
|
||||
private readonly ILogger<MigrationStartupTask> _logger;
|
||||
private readonly LightningClientFactoryService _lightningClientFactoryService;
|
||||
private readonly IFileService _fileService;
|
||||
|
||||
public IOptions<LightningNetworkOptions> LightningOptions { get; }
|
||||
|
||||
@ -67,6 +63,7 @@ namespace BTCPayServer.Hosting
|
||||
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings,
|
||||
LightningAddressService lightningAddressService,
|
||||
ILogger<MigrationStartupTask> logger,
|
||||
IFileService fileService,
|
||||
LightningClientFactoryService lightningClientFactoryService)
|
||||
{
|
||||
_handlers = handlers;
|
||||
@ -78,6 +75,7 @@ namespace BTCPayServer.Hosting
|
||||
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
|
||||
_lightningAddressService = lightningAddressService;
|
||||
_logger = logger;
|
||||
_fileService = fileService;
|
||||
_lightningClientFactoryService = lightningClientFactoryService;
|
||||
LightningOptions = lightningOptions;
|
||||
}
|
||||
|
29
BTCPayServer/JsonConverters/UnresolvedUriJsonConverter.cs
Normal file
29
BTCPayServer/JsonConverters/UnresolvedUriJsonConverter.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using BTCPayServer.Payouts;
|
||||
using NBitcoin.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.JsonConverters
|
||||
{
|
||||
public class UnresolvedUriJsonConverter : JsonConverter<UnresolvedUri>
|
||||
{
|
||||
public override UnresolvedUri ReadJson(JsonReader reader, Type objectType, UnresolvedUri existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
return null;
|
||||
if (reader.TokenType != JsonToken.String)
|
||||
throw new JsonObjectException("A UnresolvedUri should be a string", reader);
|
||||
var str = (string)reader.Value;
|
||||
if (str.Length == 0)
|
||||
return null;
|
||||
return UnresolvedUri.Create(str);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, UnresolvedUri value, JsonSerializer serializer)
|
||||
{
|
||||
if (value != null)
|
||||
writer.WriteValue(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
@ -48,8 +48,6 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
||||
Description = blob.Description;
|
||||
ExpiryDate = blob.ExpiryDate?.UtcDateTime;
|
||||
Email = blob.Email;
|
||||
CustomCSSLink = blob.CustomCSSLink;
|
||||
EmbeddedCSS = blob.EmbeddedCSS;
|
||||
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts;
|
||||
FormResponse = blob.FormResponse is null
|
||||
? null
|
||||
@ -85,13 +83,6 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
||||
|
||||
[MailboxAddress]
|
||||
public string Email { get; set; }
|
||||
|
||||
[MaxLength(500)]
|
||||
[Display(Name = "Custom CSS URL")]
|
||||
public string CustomCSSLink { get; set; }
|
||||
|
||||
[Display(Name = "Custom CSS Code")]
|
||||
public string EmbeddedCSS { get; set; }
|
||||
|
||||
[Display(Name = "Allow payee to create invoices with custom amounts")]
|
||||
public bool AllowCustomPaymentAmounts { get; set; }
|
||||
@ -115,8 +106,6 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
||||
Description = blob.Description;
|
||||
ExpiryDate = blob.ExpiryDate?.UtcDateTime;
|
||||
Email = blob.Email;
|
||||
EmbeddedCSS = blob.EmbeddedCSS;
|
||||
CustomCSSLink = blob.CustomCSSLink;
|
||||
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts;
|
||||
switch (data.Status)
|
||||
{
|
||||
@ -154,8 +143,6 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
||||
public string Description { get; set; }
|
||||
public string StoreName { get; set; }
|
||||
public string StoreWebsite { get; set; }
|
||||
public string EmbeddedCSS { get; set; }
|
||||
public string CustomCSSLink { get; set; }
|
||||
|
||||
#nullable enable
|
||||
public class InvoiceList : List<PaymentRequestInvoice>
|
||||
|
@ -21,20 +21,15 @@ public class BrandingViewModel
|
||||
[Display(Name = "Custom Theme Extension Type")]
|
||||
public ThemeExtension CustomThemeExtension { get; set; }
|
||||
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||
[MaxLength(500)]
|
||||
[Display(Name = "Custom Theme CSS URL")]
|
||||
public string CustomThemeCssUri { get; set; }
|
||||
|
||||
[Display(Name = "Custom Theme File")]
|
||||
[JsonIgnore]
|
||||
public IFormFile CustomThemeFile { get; set; }
|
||||
|
||||
public string CustomThemeFileId { get; set; }
|
||||
public string CustomThemeCssUrl { get; set; }
|
||||
|
||||
[Display(Name = "Logo")]
|
||||
[JsonIgnore]
|
||||
public IFormFile LogoFile { get; set; }
|
||||
|
||||
public string LogoFileId { get; set; }
|
||||
public string LogoUrl { get; set; }
|
||||
}
|
||||
|
@ -1,24 +1,32 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace BTCPayServer.Models;
|
||||
|
||||
public class StoreBrandingViewModel
|
||||
{
|
||||
public string BrandColor { get; set; }
|
||||
public string LogoFileId { get; set; }
|
||||
public string CssFileId { get; set; }
|
||||
public string CustomCSSLink { get; set; }
|
||||
public string EmbeddedCSS { get; set; }
|
||||
public string LogoUrl { get; set; }
|
||||
public string CssUrl { get; set; }
|
||||
|
||||
public StoreBrandingViewModel()
|
||||
{
|
||||
}
|
||||
|
||||
public StoreBrandingViewModel(StoreBlob storeBlob)
|
||||
public static async Task<StoreBrandingViewModel> CreateAsync(HttpRequest request, UriResolver uriResolver, StoreBlob storeBlob)
|
||||
{
|
||||
if (storeBlob == null)
|
||||
return new StoreBrandingViewModel();
|
||||
var result = new StoreBrandingViewModel(storeBlob);
|
||||
result.LogoUrl = await uriResolver.Resolve(request.GetAbsoluteRootUri(), storeBlob.LogoUrl);
|
||||
result.CssUrl = await uriResolver.Resolve(request.GetAbsoluteRootUri(), storeBlob.CssUrl);
|
||||
return result;
|
||||
}
|
||||
private StoreBrandingViewModel(StoreBlob storeBlob)
|
||||
{
|
||||
if (storeBlob == null) return;
|
||||
BrandColor = storeBlob.BrandColor;
|
||||
LogoFileId = storeBlob.LogoFileId;
|
||||
CssFileId = storeBlob.CssFileId;
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
|
||||
[Display(Name = "Custom sound file for successful payment")]
|
||||
public IFormFile SoundFile { get; set; }
|
||||
public string SoundFileId { get; set; }
|
||||
public string PaymentSoundUrl { get; set; }
|
||||
|
||||
[Display(Name = "Custom HTML title to display on Checkout page")]
|
||||
public string HtmlTitle { get; set; }
|
||||
|
@ -27,11 +27,11 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
|
||||
[Display(Name = "Logo")]
|
||||
public IFormFile LogoFile { get; set; }
|
||||
public string LogoFileId { get; set; }
|
||||
public string LogoUrl { get; set; }
|
||||
|
||||
[Display(Name = "Custom CSS")]
|
||||
public IFormFile CssFile { get; set; }
|
||||
public string CssFileId { get; set; }
|
||||
public string CssUrl { get; set; }
|
||||
|
||||
public bool Archived { get; set; }
|
||||
|
||||
|
@ -55,11 +55,6 @@ namespace BTCPayServer.Models.WalletViewModels
|
||||
[Required]
|
||||
[ReadOnly(true)]
|
||||
public string Currency { get; set; }
|
||||
[MaxLength(500)]
|
||||
[Display(Name = "Custom CSS URL")]
|
||||
public string CustomCSSLink { get; set; }
|
||||
[Display(Name = "Custom CSS Code")]
|
||||
public string EmbeddedCSS { get; set; }
|
||||
|
||||
[Display(Name = "Payout Methods")]
|
||||
public IEnumerable<string> PayoutMethods { get; set; }
|
||||
@ -91,8 +86,6 @@ namespace BTCPayServer.Models.WalletViewModels
|
||||
var blob = data.GetBlob();
|
||||
Name = blob.Name;
|
||||
Description = blob.Description;
|
||||
CustomCSSLink = blob.View.CustomCSSLink;
|
||||
EmbeddedCSS = blob.View.EmbeddedCSS;
|
||||
}
|
||||
|
||||
[MaxLength(30)]
|
||||
@ -100,11 +93,5 @@ namespace BTCPayServer.Models.WalletViewModels
|
||||
|
||||
[Display(Name = "Memo")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[Display(Name = "Custom CSS URL")]
|
||||
public string CustomCSSLink { get; set; }
|
||||
|
||||
[Display(Name = "Custom CSS Code")]
|
||||
public string EmbeddedCSS { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ using BTCPayServer.Forms.Models;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Plugins.Crowdfund.Models;
|
||||
using BTCPayServer.Plugins.PointOfSale.Models;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Rates;
|
||||
@ -41,6 +42,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
||||
AppService appService,
|
||||
CurrencyNameTable currencies,
|
||||
EventAggregator eventAggregator,
|
||||
UriResolver uriResolver,
|
||||
StoreRepository storeRepository,
|
||||
UIInvoiceController invoiceController,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
@ -53,11 +55,13 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
||||
_app = app;
|
||||
_storeRepository = storeRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
_uriResolver = uriResolver;
|
||||
_invoiceController = invoiceController;
|
||||
FormDataService = formDataService;
|
||||
}
|
||||
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private readonly UriResolver _uriResolver;
|
||||
private readonly CurrencyNameTable _currencies;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly AppService _appService;
|
||||
@ -315,7 +319,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
||||
var vm = new FormViewModel
|
||||
{
|
||||
StoreName = store.StoreName,
|
||||
StoreBranding = new StoreBrandingViewModel(storeBlob),
|
||||
StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob),
|
||||
FormName = formData.Name,
|
||||
Form = form,
|
||||
AspController = controller,
|
||||
@ -403,10 +407,8 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
||||
TargetCurrency = settings.TargetCurrency,
|
||||
Description = settings.Description,
|
||||
MainImageUrl = settings.MainImageUrl,
|
||||
EmbeddedCSS = settings.EmbeddedCSS,
|
||||
EndDate = settings.EndDate,
|
||||
TargetAmount = settings.TargetAmount,
|
||||
CustomCSSLink = settings.CustomCSSLink,
|
||||
NotificationUrl = settings.NotificationUrl,
|
||||
Tagline = settings.Tagline,
|
||||
PerksTemplate = settings.PerksTemplate,
|
||||
@ -518,9 +520,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
||||
Description = vm.Description,
|
||||
EndDate = vm.EndDate?.ToUniversalTime(),
|
||||
TargetAmount = vm.TargetAmount,
|
||||
CustomCSSLink = vm.CustomCSSLink,
|
||||
MainImageUrl = vm.MainImageUrl,
|
||||
EmbeddedCSS = vm.EmbeddedCSS,
|
||||
NotificationUrl = vm.NotificationUrl,
|
||||
Tagline = vm.Tagline,
|
||||
PerksTemplate = vm.PerksTemplate,
|
||||
@ -580,6 +580,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
||||
return null;
|
||||
}
|
||||
var info = (ViewCrowdfundViewModel)await _app.GetInfo(app);
|
||||
info.StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, app.StoreData.GetStoreBlob());
|
||||
info.HubPath = AppHub.GetHubPath(Request);
|
||||
info.SimpleDisplay = Request.Query.ContainsKey("simple");
|
||||
return info;
|
||||
|
@ -18,6 +18,7 @@ using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using Ganss.Xss;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
@ -180,11 +181,6 @@ namespace BTCPayServer.Plugins.Crowdfund
|
||||
|
||||
var store = appData.StoreData;
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var storeBranding = new StoreBrandingViewModel(storeBlob)
|
||||
{
|
||||
CustomCSSLink = settings.CustomCSSLink,
|
||||
EmbeddedCSS = settings.EmbeddedCSS
|
||||
};
|
||||
var formUrl = settings.FormId != null
|
||||
? _linkGenerator.GetPathByAction(nameof(UICrowdfundController.CrowdfundForm), "UICrowdfund",
|
||||
new { appId = appData.Id }, _options.Value.RootPath)
|
||||
@ -196,7 +192,6 @@ namespace BTCPayServer.Plugins.Crowdfund
|
||||
Description = settings.Description,
|
||||
MainImageUrl = settings.MainImageUrl,
|
||||
StoreName = store.StoreName,
|
||||
StoreBranding = storeBranding,
|
||||
StoreId = appData.StoreDataId,
|
||||
AppId = appData.Id,
|
||||
StartDate = settings.StartDate?.ToUniversalTime(),
|
||||
|
@ -86,13 +86,6 @@ namespace BTCPayServer.Plugins.Crowdfund.Models
|
||||
[Display(Name = "Contribution Perks Template")]
|
||||
public string PerksTemplate { get; set; }
|
||||
|
||||
[MaxLength(500)]
|
||||
[Display(Name = "Custom CSS URL")]
|
||||
public string CustomCSSLink { get; set; }
|
||||
|
||||
[Display(Name = "Custom CSS Code")]
|
||||
public string EmbeddedCSS { get; set; }
|
||||
|
||||
[Display(Name = "Count all invoices created on the store as part of the goal")]
|
||||
public bool UseAllStoreInvoices { get; set; }
|
||||
|
||||
|
@ -47,6 +47,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
AppService appService,
|
||||
CurrencyNameTable currencies,
|
||||
StoreRepository storeRepository,
|
||||
UriResolver uriResolver,
|
||||
InvoiceRepository invoiceRepository,
|
||||
UIInvoiceController invoiceController,
|
||||
FormDataService formDataService,
|
||||
@ -55,6 +56,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
_currencies = currencies;
|
||||
_appService = appService;
|
||||
_storeRepository = storeRepository;
|
||||
_uriResolver = uriResolver;
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_invoiceController = invoiceController;
|
||||
_displayFormatter = displayFormatter;
|
||||
@ -64,6 +66,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
private readonly CurrencyNameTable _currencies;
|
||||
private readonly InvoiceRepository _invoiceRepository;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly UriResolver _uriResolver;
|
||||
private readonly AppService _appService;
|
||||
private readonly UIInvoiceController _invoiceController;
|
||||
private readonly DisplayFormatter _displayFormatter;
|
||||
@ -86,13 +89,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
viewType ??= settings.EnableShoppingCart ? PosViewType.Cart : settings.DefaultView;
|
||||
var store = await _appService.GetStore(app);
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
|
||||
var storeBranding = new StoreBrandingViewModel(storeBlob)
|
||||
{
|
||||
EmbeddedCSS = settings.EmbeddedCSS,
|
||||
CustomCSSLink = settings.CustomCSSLink
|
||||
};
|
||||
// Check if the currency is COP or ARS (exclude decimal places)
|
||||
var storeBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob);
|
||||
|
||||
return View($"PointOfSale/Public/{viewType}", new ViewPointOfSaleViewModel
|
||||
{
|
||||
@ -466,7 +463,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
var vm = new FormViewModel
|
||||
{
|
||||
StoreName = store.StoreName,
|
||||
StoreBranding = new StoreBrandingViewModel(storeBlob),
|
||||
StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob),
|
||||
FormName = formData.Name,
|
||||
Form = form,
|
||||
AspController = controller,
|
||||
@ -529,7 +526,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
viewModel.FormName = formData.Name;
|
||||
viewModel.Form = form;
|
||||
viewModel.FormParameters = formParameters;
|
||||
viewModel.StoreBranding = new StoreBrandingViewModel(storeBlob);
|
||||
viewModel.StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob);
|
||||
return View("Views/UIForms/View", viewModel);
|
||||
}
|
||||
|
||||
@ -598,8 +595,6 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
CustomButtonText = settings.CustomButtonText ?? PointOfSaleSettings.CUSTOM_BUTTON_TEXT_DEF,
|
||||
CustomTipText = settings.CustomTipText ?? PointOfSaleSettings.CUSTOM_TIP_TEXT_DEF,
|
||||
CustomTipPercentages = settings.CustomTipPercentages != null ? string.Join(",", settings.CustomTipPercentages) : string.Join(",", PointOfSaleSettings.CUSTOM_TIP_PERCENTAGES_DEF),
|
||||
CustomCSSLink = settings.CustomCSSLink,
|
||||
EmbeddedCSS = settings.EmbeddedCSS,
|
||||
Description = settings.Description,
|
||||
NotificationUrl = settings.NotificationUrl,
|
||||
RedirectUrl = settings.RedirectUrl,
|
||||
@ -689,11 +684,9 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
CustomButtonText = vm.CustomButtonText,
|
||||
CustomTipText = vm.CustomTipText,
|
||||
CustomTipPercentages = ListSplit(vm.CustomTipPercentages),
|
||||
CustomCSSLink = vm.CustomCSSLink,
|
||||
NotificationUrl = vm.NotificationUrl,
|
||||
RedirectUrl = vm.RedirectUrl,
|
||||
Description = vm.Description,
|
||||
EmbeddedCSS = vm.EmbeddedCSS,
|
||||
RedirectAutomatically = string.IsNullOrEmpty(vm.RedirectAutomatically) ? null : bool.Parse(vm.RedirectAutomatically),
|
||||
FormId = vm.FormId
|
||||
};
|
||||
|
@ -67,10 +67,6 @@ namespace BTCPayServer.Plugins.PointOfSale.Models
|
||||
[Display(Name = "Tip percentage amounts (comma separated)")]
|
||||
public string CustomTipPercentages { get; set; }
|
||||
|
||||
[MaxLength(500)]
|
||||
[Display(Name = "Custom CSS URL")]
|
||||
public string CustomCSSLink { get; set; }
|
||||
|
||||
public string Id { get; set; }
|
||||
|
||||
[Display(Name = "Redirect invoice to redirect url automatically after paid")]
|
||||
@ -99,8 +95,6 @@ namespace BTCPayServer.Plugins.PointOfSale.Models
|
||||
}
|
||||
}, nameof(SelectListItem.Value), nameof(SelectListItem.Text), RedirectAutomatically);
|
||||
|
||||
[Display(Name = "Custom CSS Code")]
|
||||
public string EmbeddedCSS { get; set; }
|
||||
public string Description { get; set; }
|
||||
|
||||
[Display(Name = "Request customer data on checkout")]
|
||||
|
@ -17,6 +17,7 @@ using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Dapper;
|
||||
using Ganss.Xss;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NBitcoin;
|
||||
using NBitcoin.DataEncoders;
|
||||
|
@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace BTCPayServer.Services.Apps
|
||||
{
|
||||
|
@ -28,11 +28,9 @@ namespace BTCPayServer.Services.Apps
|
||||
}
|
||||
|
||||
public bool EnforceTargetAmount { get; set; }
|
||||
public string CustomCSSLink { get; set; }
|
||||
public string MainImageUrl { get; set; }
|
||||
public string NotificationUrl { get; set; }
|
||||
public string Tagline { get; set; }
|
||||
public string EmbeddedCSS { get; set; }
|
||||
public string PerksTemplate { get; set; }
|
||||
public bool DisqusEnabled { get; set; }
|
||||
public bool SoundsEnabled { get; set; }
|
||||
|
@ -103,11 +103,6 @@ namespace BTCPayServer.Services.Apps
|
||||
public string CustomTipText { get; set; } = CUSTOM_TIP_TEXT_DEF;
|
||||
public static readonly int[] CUSTOM_TIP_PERCENTAGES_DEF = new int[] { 15, 18, 20 };
|
||||
public int[] CustomTipPercentages { get; set; } = CUSTOM_TIP_PERCENTAGES_DEF;
|
||||
|
||||
public string CustomCSSLink { get; set; }
|
||||
|
||||
public string EmbeddedCSS { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
public string NotificationUrl { get; set; }
|
||||
public string RedirectUrl { get; set; }
|
||||
|
@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using BTCPayServer.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Services;
|
||||
@ -21,14 +23,11 @@ public class ThemeSettings
|
||||
[Display(Name = "Custom Theme Extension Type")]
|
||||
public ThemeExtension CustomThemeExtension { get; set; }
|
||||
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||
[MaxLength(500)]
|
||||
[Display(Name = "Custom Theme CSS URL")]
|
||||
public string CustomThemeCssUri { get; set; }
|
||||
[JsonConverter(typeof(UnresolvedUriJsonConverter))]
|
||||
public UnresolvedUri CustomThemeCssUrl { get; set; }
|
||||
|
||||
public string CustomThemeFileId { get; set; }
|
||||
|
||||
public string LogoFileId { get; set; }
|
||||
[JsonConverter(typeof(UnresolvedUriJsonConverter))]
|
||||
public UnresolvedUri LogoUrl { get; set; }
|
||||
|
||||
public bool FirstRun { get; set; } = true;
|
||||
|
||||
@ -37,9 +36,4 @@ public class ThemeSettings
|
||||
// no logs
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public string CssUri
|
||||
{
|
||||
get => CustomTheme ? CustomThemeCssUri : "/main/themes/default.css";
|
||||
}
|
||||
}
|
||||
|
37
BTCPayServer/Services/UriResolver.cs
Normal file
37
BTCPayServer/Services/UriResolver.cs
Normal file
@ -0,0 +1,37 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security.AccessControl;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
|
||||
namespace BTCPayServer.Services
|
||||
{
|
||||
public class UriResolver
|
||||
{
|
||||
private readonly IFileService _fileService;
|
||||
|
||||
public UriResolver(IFileService fileService)
|
||||
{
|
||||
_fileService = fileService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If <paramref name="url"/> is an absolute URL, returns it as is.
|
||||
/// If <paramref name="url"/> starts with "fileid:ID", returns the URL of the file with the ID.
|
||||
/// </summary>
|
||||
/// <param name="baseUri"><see cref="BTCPayServer.Abstractions.Extensions.HttpRequestExtensions.GetAbsoluteRootUri"/></param>
|
||||
/// <param name="uri"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<string?> Resolve(Uri baseUri, UnresolvedUri? uri)
|
||||
{
|
||||
return uri switch
|
||||
{
|
||||
null => null,
|
||||
UnresolvedUri.FileIdUri fileId => await _fileService.GetFileUrl(baseUri, fileId.FileId),
|
||||
UnresolvedUri.Raw raw => raw.Uri,
|
||||
_ => throw new NotSupportedException(uri.GetType().Name)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
25
BTCPayServer/UnresolvedUri.cs
Normal file
25
BTCPayServer/UnresolvedUri.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public record UnresolvedUri
|
||||
{
|
||||
public static UnresolvedUri Create(string str)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(str);
|
||||
if (str.StartsWith("fileid:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new FileIdUri(str.Substring("fileid:".Length));
|
||||
}
|
||||
return new Raw(str);
|
||||
}
|
||||
public record FileIdUri(string FileId) : UnresolvedUri
|
||||
{
|
||||
public override string ToString() => $"fileid:{FileId}";
|
||||
}
|
||||
public record Raw(string Uri) : UnresolvedUri
|
||||
{
|
||||
public override string ToString() => Uri;
|
||||
}
|
||||
}
|
||||
}
|
@ -311,36 +311,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@* We are deprecating the custom CSS options in favor of the store branding approach.
|
||||
Display this section only if these values are set. *@
|
||||
@if (!string.IsNullOrWhiteSpace(Model.CustomCSSLink) || !string.IsNullOrWhiteSpace(Model.EmbeddedCSS))
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
||||
Custom CSS
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Development/Theme/" target="_blank" rel="noreferrer noopener" title="More information...">
|
||||
<vc:icon symbol="info" />
|
||||
</a>
|
||||
<input asp-for="CustomCSSLink" class="form-control" />
|
||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mb-4">
|
||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,14 +1,4 @@
|
||||
@model StoreBrandingViewModel
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@inject IFileService FileService
|
||||
@{
|
||||
var logoUrl = !string.IsNullOrEmpty(Model.LogoFileId)
|
||||
? await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.LogoFileId)
|
||||
: null;
|
||||
var cssUrl = !string.IsNullOrEmpty(Model.CssFileId)
|
||||
? await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.CssFileId)
|
||||
: null;
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.BrandColor))
|
||||
{
|
||||
var brand = Model.BrandColor;
|
||||
@ -39,23 +29,12 @@
|
||||
</style>
|
||||
<meta name="theme-color" content="@brand">
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(cssUrl))
|
||||
@if (!string.IsNullOrEmpty(Model.CssUrl))
|
||||
{
|
||||
<link href="@cssUrl" asp-append-version="true" rel="stylesheet" />
|
||||
<link href="@Model.CssUrl!" asp-append-version="true" rel="stylesheet" />
|
||||
}
|
||||
@* Deprecated, but added for backwards-compatibility *@
|
||||
@if (!string.IsNullOrEmpty(Model.CustomCSSLink))
|
||||
@if (!string.IsNullOrEmpty(Model.LogoUrl))
|
||||
{
|
||||
<link href="@Model.CustomCSSLink" asp-append-version="true" rel="stylesheet" />
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.EmbeddedCSS))
|
||||
{
|
||||
<style>
|
||||
@Safe.Raw(Model.EmbeddedCSS)
|
||||
</style>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(logoUrl))
|
||||
{
|
||||
<link rel="icon" href="@logoUrl">
|
||||
<link rel="apple-touch-icon" href="@logoUrl">
|
||||
<link rel="icon" href="@Model.LogoUrl">
|
||||
<link rel="apple-touch-icon" href="@Model.LogoUrl">
|
||||
}
|
||||
|
@ -1,14 +1,9 @@
|
||||
@using BTCPayServer.Services
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@inject ThemeSettings Theme
|
||||
@inject IFileService FileService
|
||||
@inject UriResolver UriResolver
|
||||
|
||||
<script>if (window.localStorage.getItem('btcpay-hide-sensitive-info') === 'true') { document.documentElement.setAttribute('data-hide-sensitive-info', 'true')}</script>
|
||||
@if (Theme.CustomTheme && !string.IsNullOrEmpty(Theme.CssUri))
|
||||
{ // legacy customization with CSS URI - keep it for backwards-compatibility
|
||||
<link href="@Context.Request.GetRelativePathOrAbsolute(Theme.CssUri)" rel="stylesheet" asp-append-version="true" />
|
||||
}
|
||||
else if (Theme.CustomTheme && !string.IsNullOrEmpty(Theme.CustomThemeFileId))
|
||||
@if (Theme.CustomTheme && Theme.CustomThemeCssUrl is not null)
|
||||
{ // new customization uses theme file id provided by upload
|
||||
@if (Theme.CustomThemeExtension != ThemeExtension.Custom)
|
||||
{ // needs to be added for light and dark, because dark extends light
|
||||
@ -17,8 +12,9 @@ else if (Theme.CustomTheme && !string.IsNullOrEmpty(Theme.CustomThemeFileId))
|
||||
@if (Theme.CustomThemeExtension == ThemeExtension.Dark)
|
||||
{
|
||||
<link href="~/main/themes/default-dark.css" rel="stylesheet" asp-append-version="true" />
|
||||
}
|
||||
<link href="@(await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Theme.CustomThemeFileId))" rel="stylesheet" asp-append-version="true" />
|
||||
}
|
||||
var themeUrl = await UriResolver.Resolve(this.Context.Request.GetAbsoluteRootUri(), Theme.CustomThemeCssUrl);
|
||||
<link href="@themeUrl" rel="stylesheet" asp-append-version="true" />
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -289,36 +289,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@* We are deprecating the custom CSS options in favor of the store branding approach.
|
||||
Display this section only if these values are set. *@
|
||||
@if (!string.IsNullOrWhiteSpace(Model.CustomCSSLink) || !string.IsNullOrWhiteSpace(Model.EmbeddedCSS))
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
||||
Custom CSS
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Development/Theme/" target="_blank" rel="noreferrer noopener" title="More information...">
|
||||
<vc:icon symbol="info" />
|
||||
</a>
|
||||
<input asp-for="CustomCSSLink" class="form-control" />
|
||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,15 +1,9 @@
|
||||
@inject IFileService FileService
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@model (string Title, StoreBrandingViewModel StoreBranding)
|
||||
@{
|
||||
var logoUrl = !string.IsNullOrEmpty(Model.StoreBranding.LogoFileId)
|
||||
? await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.StoreBranding.LogoFileId)
|
||||
: null;
|
||||
}
|
||||
|
||||
<header class="store-header" v-pre>
|
||||
@if (!string.IsNullOrEmpty(logoUrl))
|
||||
@if (!string.IsNullOrEmpty(Model.StoreBranding.LogoUrl))
|
||||
{
|
||||
<img src="@logoUrl" alt="@Model.Title" class="store-logo"/>
|
||||
<img src="@Model.StoreBranding.LogoUrl" alt="@Model.Title" class="store-logo"/>
|
||||
}
|
||||
<h1 class="store-name">@Model.Title</h1>
|
||||
</header>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<body class="min-vh-100">
|
||||
<div id="FormView" class="public-page-wrap">
|
||||
<partial name="_StatusMessage" model="@(new ViewDataDictionary(ViewData) { { "Margin", "mb-4" } })" />
|
||||
@if (!string.IsNullOrEmpty(Model.StoreName) || !string.IsNullOrEmpty(Model.StoreBranding.LogoFileId))
|
||||
@if (!string.IsNullOrEmpty(Model.StoreName) || !string.IsNullOrEmpty(Model.StoreBranding.LogoUrl))
|
||||
{
|
||||
<partial name="_StoreHeader" model="(Model.StoreName, Model.StoreBranding)" />
|
||||
}
|
||||
|
@ -123,46 +123,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* We are deprecating the custom CSS options in favor of the store branding approach.
|
||||
Display this section only if these values are set. *@
|
||||
@if (!string.IsNullOrWhiteSpace(Model.CustomCSSLink) || !string.IsNullOrWhiteSpace(Model.EmbeddedCSS))
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-xl-8 col-xxl-constrain">
|
||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
||||
<div class="form-group">
|
||||
<div class="accordion" id="additional">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
||||
Custom CSS
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Development/Theme/" target="_blank" rel="noreferrer noopener" title="More information...">
|
||||
<vc:icon symbol="info" />
|
||||
</a>
|
||||
<input asp-for="CustomCSSLink" class="form-control" />
|
||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.Id))
|
||||
|
@ -1,5 +1,4 @@
|
||||
@using BTCPayServer.Views.Stores
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@model BTCPayServer.Models.WalletViewModels.UpdatePullPaymentModel
|
||||
|
||||
@{
|
||||
@ -53,44 +52,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* We are deprecating the custom CSS options in favor of the store branding approach.
|
||||
Display this section only if these values are set. *@
|
||||
@if (!string.IsNullOrWhiteSpace(Model.CustomCSSLink) || !string.IsNullOrWhiteSpace(Model.EmbeddedCSS))
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-xl-8 col-xxl-constrain">
|
||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
||||
<div class="form-group">
|
||||
<div class="accordion" id="additional">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
||||
Custom CSS
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Development/Theme/" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="info" />
|
||||
</a>
|
||||
<input asp-for="CustomCSSLink" class="form-control" />
|
||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
|
@ -36,7 +36,7 @@
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center justify-content-between gap-2">
|
||||
<label asp-for="LogoFile" class="form-label"></label>
|
||||
@if (!string.IsNullOrEmpty(Model.LogoFileId))
|
||||
@if (!string.IsNullOrEmpty(Model.LogoUrl))
|
||||
{
|
||||
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveLogoFile" value="true">
|
||||
<span class="fa fa-times"></span> Remove
|
||||
@ -47,9 +47,9 @@
|
||||
{
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<input asp-for="LogoFile" type="file" class="form-control flex-grow">
|
||||
@if (!string.IsNullOrEmpty(Model.LogoFileId))
|
||||
@if (!string.IsNullOrEmpty(Model.LogoUrl))
|
||||
{
|
||||
<img src="@(await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.LogoFileId))" alt="Logo" style="height:2.1rem;max-width:10.5rem;"/>
|
||||
<img src="@Model.LogoUrl" alt="Logo" style="height:2.1rem;max-width:10.5rem;"/>
|
||||
}
|
||||
</div>
|
||||
<span asp-validation-for="LogoFile" class="text-danger"></span>
|
||||
@ -75,48 +75,37 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse @(Model.CustomTheme ? "show" : "")" id="CustomThemeSettings">
|
||||
@if (!string.IsNullOrEmpty(Model.CustomThemeCssUri))
|
||||
{
|
||||
<div class="form-group mb-0 pt-2">
|
||||
<label asp-for="CustomThemeCssUri" class="form-label" data-required></label>
|
||||
<input asp-for="CustomThemeCssUri" class="form-control"/>
|
||||
<span asp-validation-for="CustomThemeCssUri" class="text-danger"></span>
|
||||
<div class="form-group pt-2">
|
||||
<label asp-for="CustomThemeExtension" class="form-label" data-required></label>
|
||||
<select asp-for="CustomThemeExtension" asp-items="@themeExtension" class="form-select w-auto"></select>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="d-flex align-items-center justify-content-between gap-2">
|
||||
<label asp-for="CustomThemeFile" class="form-label" data-required></label>
|
||||
@if (!string.IsNullOrEmpty(Model.CustomThemeCssUrl))
|
||||
{
|
||||
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveCustomThemeFile" value="true">
|
||||
<span class="fa fa-times"></span> Remove
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="form-group pt-2">
|
||||
<label asp-for="CustomThemeExtension" class="form-label" data-required></label>
|
||||
<select asp-for="CustomThemeExtension" asp-items="@themeExtension" class="form-select w-auto"></select>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="d-flex align-items-center justify-content-between gap-2">
|
||||
<label asp-for="CustomThemeFile" class="form-label" data-required></label>
|
||||
@if (!string.IsNullOrEmpty(Model.CustomThemeFileId))
|
||||
@if (canUpload)
|
||||
{
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<input asp-for="CustomThemeFile" type="file" class="form-control flex-grow">
|
||||
@if (!string.IsNullOrEmpty(Model.CustomThemeCssUrl))
|
||||
{
|
||||
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveCustomThemeFile" value="true">
|
||||
<span class="fa fa-times"></span> Remove
|
||||
</button>
|
||||
<a href="@Model.CustomThemeCssUrl" target="_blank" rel="noreferrer noopener" class="text-nowrap">Custom CSS</a>
|
||||
}
|
||||
</div>
|
||||
@if (canUpload)
|
||||
{
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<input asp-for="CustomThemeFile" type="file" class="form-control flex-grow">
|
||||
@if (!string.IsNullOrEmpty(Model.CustomThemeFileId))
|
||||
{
|
||||
<a href="@(await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.CustomThemeFileId))" target="_blank" rel="noreferrer noopener" class="text-nowrap">Custom CSS</a>
|
||||
}
|
||||
</div>
|
||||
<span asp-validation-for="CustomThemeFile" class="text-danger"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input asp-for="CustomThemeFile" type="file" class="form-control" disabled>
|
||||
<p class="form-text text-muted">In order to upload a theme file, a <a asp-controller="UIServer" asp-action="Files">file storage</a> must be configured.</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<span asp-validation-for="CustomThemeFile" class="text-danger"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input asp-for="CustomThemeFile" type="file" class="form-control" disabled>
|
||||
<p class="form-text text-muted">In order to upload a theme file, a <a asp-controller="UIServer" asp-action="Files">file storage</a> must be configured.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary mt-2" name="command" value="Save">Save</button>
|
||||
|
@ -95,36 +95,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@* We are deprecating the custom CSS options in favor of the store branding approach.
|
||||
Display this section only if these values are set. *@
|
||||
@if (!string.IsNullOrWhiteSpace(Model.CustomCSSLink) || !string.IsNullOrWhiteSpace(Model.EmbeddedCSS))
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
||||
Custom CSS
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Development/Theme/" target="_blank" rel="noreferrer noopener" title="More information...">
|
||||
<vc:icon symbol="info" />
|
||||
</a>
|
||||
<input asp-for="CustomCSSLink" class="form-control" />
|
||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -143,7 +143,7 @@
|
||||
<div class="form-group mb-0 py-3">
|
||||
<div class="d-flex align-items-center justify-content-between gap-2">
|
||||
<label asp-for="SoundFile" class="form-label"></label>
|
||||
@if (!string.IsNullOrEmpty(Model.SoundFileId))
|
||||
@if (!string.IsNullOrEmpty(Model.PaymentSoundUrl))
|
||||
{
|
||||
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveSoundFile" value="true" permission="@Policies.CanModifyStoreSettings">
|
||||
<span class="fa fa-times"></span> Remove
|
||||
@ -154,12 +154,7 @@
|
||||
{
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<input asp-for="SoundFile" type="file" class="form-control flex-grow">
|
||||
@{
|
||||
var soundUrl = string.IsNullOrEmpty(Model.SoundFileId)
|
||||
? string.Concat(Context.Request.GetAbsoluteRootUri().ToString(), "checkout/payment.mp3")
|
||||
: await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.SoundFileId);
|
||||
}
|
||||
<audio controls src="@soundUrl" style="height:2.1rem;max-width:10.5rem;"></audio>
|
||||
<audio controls src="@Model.PaymentSoundUrl" style="height:2.1rem;max-width:10.5rem;"></audio>
|
||||
</div>
|
||||
<span asp-validation-for="SoundFile" class="text-danger"></span>
|
||||
}
|
||||
|
@ -46,7 +46,7 @@
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center justify-content-between gap-2">
|
||||
<label asp-for="LogoFile" class="form-label"></label>
|
||||
@if (!string.IsNullOrEmpty(Model.LogoFileId))
|
||||
@if (!string.IsNullOrEmpty(Model.LogoUrl))
|
||||
{
|
||||
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveLogoFile" value="true">
|
||||
<span class="fa fa-times"></span> Remove
|
||||
@ -57,9 +57,9 @@
|
||||
{
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<input asp-for="LogoFile" type="file" class="form-control flex-grow">
|
||||
@if (!string.IsNullOrEmpty(Model.LogoFileId))
|
||||
@if (!string.IsNullOrEmpty(Model.LogoUrl))
|
||||
{
|
||||
<img src="@(await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.LogoFileId))" alt="@Model.StoreName Logo" style="height:2.1rem;max-width:10.5rem;"/>
|
||||
<img src="@Model.LogoUrl" alt="@Model.StoreName Logo" style="height:2.1rem;max-width:10.5rem;"/>
|
||||
}
|
||||
</div>
|
||||
<span asp-validation-for="LogoFile" class="text-danger"></span>
|
||||
@ -73,7 +73,7 @@
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center justify-content-between gap-2">
|
||||
<label asp-for="CssFile" class="form-label"></label>
|
||||
@if (!string.IsNullOrEmpty(Model.CssFileId))
|
||||
@if (!string.IsNullOrEmpty(Model.CssUrl))
|
||||
{
|
||||
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveCssFile" value="true">
|
||||
<span class="fa fa-times"></span> Remove
|
||||
@ -84,9 +84,9 @@
|
||||
{
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<input asp-for="CssFile" type="file" class="form-control flex-grow">
|
||||
@if (!string.IsNullOrEmpty(Model.CssFileId))
|
||||
@if (!string.IsNullOrEmpty(Model.CssUrl))
|
||||
{
|
||||
<a href="@(await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.CssFileId))" target="_blank" rel="noreferrer noopener" class="text-nowrap">Custom CSS</a>
|
||||
<a href="@Model.CssUrl" target="_blank" rel="noreferrer noopener" class="text-nowrap">Custom CSS</a>
|
||||
}
|
||||
</div>
|
||||
<span asp-validation-for="LogoFile" class="text-danger"></span>
|
||||
|
@ -551,11 +551,6 @@
|
||||
"description": "Prompt which appears next to the tip amount field if tipping is enabled",
|
||||
"example": "Do you want to leave a tip?"
|
||||
},
|
||||
"customCSSLink": {
|
||||
"type": "string",
|
||||
"description": "Link to a custom CSS stylesheet to be used in the app",
|
||||
"example": "https://bootswatch.com/4/slate/bootstrap.min.css"
|
||||
},
|
||||
"notificationUrl": {
|
||||
"type": "string",
|
||||
"description": "Callback notification url to POST to once when invoice is paid for and once when there are enough blockchain confirmations"
|
||||
@ -564,10 +559,6 @@
|
||||
"type": "string",
|
||||
"description": "URL user is redirected to once invoice is paid"
|
||||
},
|
||||
"embeddedCSS": {
|
||||
"type": "string",
|
||||
"description": "Custom CSS embedded into the app"
|
||||
},
|
||||
"redirectAutomatically": {
|
||||
"type": "boolean",
|
||||
"description": "Whether user is redirected to specified redirect URL automatically after the invoice is paid",
|
||||
@ -627,18 +618,10 @@
|
||||
"description": "Target amount for the crowdfund",
|
||||
"example": 420.69
|
||||
},
|
||||
"customCSSLink": {
|
||||
"type": "string",
|
||||
"description": "Link to a custom CSS stylesheet to be used in the app"
|
||||
},
|
||||
"mainImageUrl": {
|
||||
"type": "string",
|
||||
"description": "URL for image used as a cover image for the app"
|
||||
},
|
||||
"embeddedCSS": {
|
||||
"type": "string",
|
||||
"description": "Custom CSS embedded into the app"
|
||||
},
|
||||
"perks": {
|
||||
"type": "object",
|
||||
"description": "JSON of perks available in the app",
|
||||
@ -875,16 +858,6 @@
|
||||
"default": "Do you want to leave a tip?",
|
||||
"nullable": true
|
||||
},
|
||||
"customCSSLink": {
|
||||
"type": "string",
|
||||
"description": "Link to a custom CSS stylesheet to be used in the app",
|
||||
"nullable": true
|
||||
},
|
||||
"embeddedCSS": {
|
||||
"type": "string",
|
||||
"description": "Custom CSS to embed into the app",
|
||||
"nullable": true
|
||||
},
|
||||
"notificationUrl": {
|
||||
"type": "string",
|
||||
"description": "Callback notification url to POST to once when invoice is paid for and once when there are enough blockchain confirmations",
|
||||
@ -966,21 +939,11 @@
|
||||
"example": 420,
|
||||
"nullable": true
|
||||
},
|
||||
"customCSSLink": {
|
||||
"type": "string",
|
||||
"description": "Link to a custom CSS stylesheet to be used in the app",
|
||||
"nullable": true
|
||||
},
|
||||
"mainImageUrl": {
|
||||
"type": "string",
|
||||
"description": "URL for image to be used as a cover image for the app",
|
||||
"nullable": true
|
||||
},
|
||||
"embeddedCSS": {
|
||||
"type": "string",
|
||||
"description": "Custom CSS to embed into the app",
|
||||
"nullable": true
|
||||
},
|
||||
"perksTemplate": {
|
||||
"type": "string",
|
||||
"description": "YAML template of perks available in the app",
|
||||
|
@ -454,19 +454,6 @@
|
||||
"nullable": true,
|
||||
"allOf": [ { "$ref": "#/components/schemas/UnixTimestamp" } ]
|
||||
},
|
||||
"embeddedCSS": {
|
||||
"type": "string",
|
||||
"description": "Custom CSS styling for the payment request",
|
||||
"nullable": true,
|
||||
"format": "css",
|
||||
"maximum": 500
|
||||
},
|
||||
"customCSSLink": {
|
||||
"type": "string",
|
||||
"description": "Custom CSS link for styling the payment request",
|
||||
"nullable": true,
|
||||
"format": "uri"
|
||||
},
|
||||
"allowCustomPaymentAmounts": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to allow users to create invoices that partially pay the payment request ",
|
||||
|
@ -411,6 +411,30 @@
|
||||
"description": "The support URI of the store, can contain the placeholders `{OrderId}` and `{InvoiceId}`. Can be any valid URI, such as a website, email, and nostr.",
|
||||
"format": "uri"
|
||||
},
|
||||
"logoUrl": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Absolute URL to a logo file or a reference to an uploaded file id with `fileid:ID`",
|
||||
"format": "uri"
|
||||
},
|
||||
"cssUrl": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Absolute URL to CSS file to customize the public/customer-facing pages of the store. (Invoice, Payment Request, Pull Payment, etc.) or a reference to an uploaded file id with `fileid:ID`",
|
||||
"format": "uri"
|
||||
},
|
||||
"paymentSoundUrl": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Absolute URL to a sound file or a reference to an uploaded file id with `fileid:ID`",
|
||||
"format": "uri"
|
||||
},
|
||||
"brandColor": {
|
||||
"type": "string",
|
||||
"description": "The brand color of the store in HEX format",
|
||||
"nullable": true,
|
||||
"example": "#F7931A"
|
||||
},
|
||||
"defaultCurrency": {
|
||||
"type": "string",
|
||||
"description": "The default currency of the store",
|
||||
|
Loading…
Reference in New Issue
Block a user