diff --git a/BTCPayServer.Tests/SeleniumTests.cs b/BTCPayServer.Tests/SeleniumTests.cs index fcfbfc0c0..160f6bb12 100644 --- a/BTCPayServer.Tests/SeleniumTests.cs +++ b/BTCPayServer.Tests/SeleniumTests.cs @@ -1960,7 +1960,7 @@ namespace BTCPayServer.Tests s.Driver.FindElement(By.Id("SignTransaction")).Click(); // Back button should lead back to the previous page inside the send wizard var backUrl = s.Driver.FindElement(By.Id("GoBack")).GetAttribute("href"); - Assert.EndsWith($"/send?returnUrl={walletTransactionUri.AbsolutePath}", backUrl); + Assert.EndsWith($"/send?returnUrl={Uri.EscapeDataString(walletTransactionUri.AbsolutePath)}", backUrl); // Cancel button should lead to the page that referred to the send wizard var cancelUrl = s.Driver.FindElement(By.Id("CancelWizard")).GetAttribute("href"); Assert.EndsWith(walletTransactionUri.AbsolutePath, cancelUrl); diff --git a/BTCPayServer/Extensions/UrlHelperExtensions.cs b/BTCPayServer/Extensions/UrlHelperExtensions.cs index ae218e7cf..c30867714 100644 --- a/BTCPayServer/Extensions/UrlHelperExtensions.cs +++ b/BTCPayServer/Extensions/UrlHelperExtensions.cs @@ -12,6 +12,10 @@ namespace Microsoft.AspNetCore.Mvc public static class UrlHelperExtensions { #nullable enable + public static string? WalletSend(this IUrlHelper helper, WalletId walletId) => helper.Action(nameof(UIWalletsController.WalletSend), new { walletId }); + public static string? WalletTransactions(this IUrlHelper helper, string walletId) => WalletTransactions(helper, WalletId.Parse(walletId)); + public static string? WalletTransactions(this IUrlHelper helper, WalletId walletId) + => helper.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); public static Uri ActionAbsolute(this IUrlHelper helper, HttpRequest request, string? action, string? controller, object? values) => request.GetAbsoluteUriNoPathBase(new Uri(helper.Action(action, controller, values) ?? "", UriKind.Relative)); public static Uri ActionAbsolute(this IUrlHelper helper, HttpRequest request, string? action, string? controller) diff --git a/BTCPayServer/Models/IHasBackAndReturnUrl.cs b/BTCPayServer/Models/IHasBackAndReturnUrl.cs new file mode 100644 index 000000000..b4924ebe7 --- /dev/null +++ b/BTCPayServer/Models/IHasBackAndReturnUrl.cs @@ -0,0 +1,25 @@ +#nullable enable +using System; +using BTCPayServer.Controllers; + +namespace BTCPayServer.Models +{ + public interface IHasBackAndReturnUrl + { + string? BackUrl { get; set; } + string? ReturnUrl { get; set; } + (string? backUrl, string? returnUrl) NormalizeBackAndReturnUrl() + { + var backUrl = BackUrl; + if (backUrl is not null && ReturnUrl is not null) + { + var queryParam = $"returnUrl={Uri.EscapeDataString(ReturnUrl)}"; + if (backUrl.Contains('?')) + backUrl = $"{backUrl}&{queryParam}"; + else + backUrl = $"{backUrl}?{queryParam}"; + } + return (backUrl, ReturnUrl); + } + } +} diff --git a/BTCPayServer/Models/WalletViewModels/SignWithSeedViewModel.cs b/BTCPayServer/Models/WalletViewModels/SignWithSeedViewModel.cs index 27b607a9e..49e6d9cef 100644 --- a/BTCPayServer/Models/WalletViewModels/SignWithSeedViewModel.cs +++ b/BTCPayServer/Models/WalletViewModels/SignWithSeedViewModel.cs @@ -4,7 +4,7 @@ using NBitcoin; namespace BTCPayServer.Models.WalletViewModels { - public class SignWithSeedViewModel + public class SignWithSeedViewModel : IHasBackAndReturnUrl { public SigningContextModel SigningContext { get; set; } = new SigningContextModel(); diff --git a/BTCPayServer/Models/WalletViewModels/WalletPSBTCombineViewModel.cs b/BTCPayServer/Models/WalletViewModels/WalletPSBTCombineViewModel.cs index f89ba085d..937d299cb 100644 --- a/BTCPayServer/Models/WalletViewModels/WalletPSBTCombineViewModel.cs +++ b/BTCPayServer/Models/WalletViewModels/WalletPSBTCombineViewModel.cs @@ -7,7 +7,7 @@ using NBitcoin; namespace BTCPayServer.Models.WalletViewModels { - public class WalletPSBTCombineViewModel + public class WalletPSBTCombineViewModel : IHasBackAndReturnUrl { public string OtherPSBT { get; set; } [Display(Name = "PSBT to combine with…")] diff --git a/BTCPayServer/Models/WalletViewModels/WalletPSBTReadyViewModel.cs b/BTCPayServer/Models/WalletViewModels/WalletPSBTReadyViewModel.cs index 9014d4f5f..2c0e03552 100644 --- a/BTCPayServer/Models/WalletViewModels/WalletPSBTReadyViewModel.cs +++ b/BTCPayServer/Models/WalletViewModels/WalletPSBTReadyViewModel.cs @@ -5,7 +5,7 @@ using NBitcoin; namespace BTCPayServer.Models.WalletViewModels { - public class WalletPSBTReadyViewModel + public class WalletPSBTReadyViewModel : IHasBackAndReturnUrl { public SigningContextModel SigningContext { get; set; } = new SigningContextModel(); public string SigningKey { get; set; } @@ -27,6 +27,12 @@ namespace BTCPayServer.Models.WalletViewModels public string BalanceChange { get; set; } public IEnumerable Labels { get; set; } = new List(); } + public class AmountViewModel + { + public bool Positive { get; set; } + public string BalanceChange { get; set; } + } + public AmountViewModel ReplacementBalanceChange { get; set; } public bool HasErrors => Inputs.Count == 0 || Inputs.Any(i => !string.IsNullOrEmpty(i.Error)); public string BalanceChange { get; set; } public bool CanCalculateBalance { get; set; } diff --git a/BTCPayServer/Models/WalletViewModels/WalletSendModel.cs b/BTCPayServer/Models/WalletViewModels/WalletSendModel.cs index ab2f82fcd..52b0ac1aa 100644 --- a/BTCPayServer/Models/WalletViewModels/WalletSendModel.cs +++ b/BTCPayServer/Models/WalletViewModels/WalletSendModel.cs @@ -5,7 +5,7 @@ using BTCPayServer.Services.Labels; namespace BTCPayServer.Models.WalletViewModels { - public class WalletSendModel + public class WalletSendModel : IHasBackAndReturnUrl { public enum ThreeStateBool { diff --git a/BTCPayServer/Models/WalletViewModels/WalletSendVaultModel.cs b/BTCPayServer/Models/WalletViewModels/WalletSendVaultModel.cs index 93b21eb07..010ae9d0c 100644 --- a/BTCPayServer/Models/WalletViewModels/WalletSendVaultModel.cs +++ b/BTCPayServer/Models/WalletViewModels/WalletSendVaultModel.cs @@ -1,6 +1,6 @@ namespace BTCPayServer.Models.WalletViewModels { - public class WalletSendVaultModel + public class WalletSendVaultModel : IHasBackAndReturnUrl { public string WalletId { get; set; } public string WebsocketPath { get; set; } diff --git a/BTCPayServer/Models/WalletViewModels/WalletSigningOptionsModel.cs b/BTCPayServer/Models/WalletViewModels/WalletSigningOptionsModel.cs index b8eecd149..37cc1ccea 100644 --- a/BTCPayServer/Models/WalletViewModels/WalletSigningOptionsModel.cs +++ b/BTCPayServer/Models/WalletViewModels/WalletSigningOptionsModel.cs @@ -2,7 +2,7 @@ using System; namespace BTCPayServer.Models.WalletViewModels { - public class WalletSigningOptionsModel + public class WalletSigningOptionsModel : IHasBackAndReturnUrl { public SigningContextModel SigningContext { get; set; } public string BackUrl { get; set; } diff --git a/BTCPayServer/Views/Shared/_BackAndReturn.cshtml b/BTCPayServer/Views/Shared/_BackAndReturn.cshtml new file mode 100644 index 000000000..4eb685968 --- /dev/null +++ b/BTCPayServer/Views/Shared/_BackAndReturn.cshtml @@ -0,0 +1,15 @@ +@model BTCPayServer.Models.IHasBackAndReturnUrl +@{ + (var backUrl, var cancelUrl) = this.Model.NormalizeBackAndReturnUrl(); + cancelUrl ??= Context.Request.Query["returnUrl"].ToString(); +} + +@if (backUrl != null) +{ + + + +} + + + diff --git a/BTCPayServer/Views/UIWallets/SignWithSeed.cshtml b/BTCPayServer/Views/UIWallets/SignWithSeed.cshtml index c925acae9..021c96284 100644 --- a/BTCPayServer/Views/UIWallets/SignWithSeed.cshtml +++ b/BTCPayServer/Views/UIWallets/SignWithSeed.cshtml @@ -1,23 +1,14 @@ @using BTCPayServer.Controllers @model SignWithSeedViewModel @{ - var walletId = Context.GetRouteValue("walletId").ToString(); - var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); - var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null; + var walletId = Context.GetRouteValue("walletId").ToString(); + Model.ReturnUrl ??= Url.WalletTransactions(walletId); Layout = "_LayoutWizard"; ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Sign PSBT"], walletId); } @section Navbar { - @if (backUrl != null) - { - - - - } - - - + }
diff --git a/BTCPayServer/Views/UIWallets/WalletPSBT.cshtml b/BTCPayServer/Views/UIWallets/WalletPSBT.cshtml index 3a485b0f1..ebe03c14a 100644 --- a/BTCPayServer/Views/UIWallets/WalletPSBT.cshtml +++ b/BTCPayServer/Views/UIWallets/WalletPSBT.cshtml @@ -3,23 +3,14 @@ @model WalletPSBTViewModel @{ var walletId = Context.GetRouteValue("walletId").ToString(); - var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); - var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null; + Model.ReturnUrl ??= Url.WalletTransactions(walletId); Layout = "_LayoutWizard"; ViewData.SetActivePage(WalletsNavPages.PSBT, StringLocalizer["Decode PSBT"], walletId); Csp.UnsafeEval(); } @section Navbar { - @if (backUrl != null) - { - - - - } - - - + } @section PageHeadContent { diff --git a/BTCPayServer/Views/UIWallets/WalletPSBTCombine.cshtml b/BTCPayServer/Views/UIWallets/WalletPSBTCombine.cshtml index 7aad6c523..aeadd6da1 100644 --- a/BTCPayServer/Views/UIWallets/WalletPSBTCombine.cshtml +++ b/BTCPayServer/Views/UIWallets/WalletPSBTCombine.cshtml @@ -2,22 +2,13 @@ @model WalletPSBTCombineViewModel @{ var walletId = Context.GetRouteValue("walletId").ToString(); - var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); - var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null; + Model.ReturnUrl ??= Url.WalletTransactions(walletId); Layout = "_LayoutWizard"; ViewData.SetActivePage(WalletsNavPages.PSBT, StringLocalizer["Combine PSBT"], walletId); } @section Navbar { - @if (backUrl != null) - { - - - - } - - - + }
diff --git a/BTCPayServer/Views/UIWallets/WalletPSBTDecoded.cshtml b/BTCPayServer/Views/UIWallets/WalletPSBTDecoded.cshtml index 61cb07a78..92f09117f 100644 --- a/BTCPayServer/Views/UIWallets/WalletPSBTDecoded.cshtml +++ b/BTCPayServer/Views/UIWallets/WalletPSBTDecoded.cshtml @@ -2,10 +2,9 @@ @inject BTCPayServer.Security.ContentSecurityPolicies Csp @model WalletPSBTViewModel @{ - var walletId = Context.GetRouteValue("walletId").ToString(); - var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new {walletId}); - var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null; - var isReady = !Model.HasErrors; + var walletId = Context.GetRouteValue("walletId").ToString(); + Model.ReturnUrl ??= Url.WalletTransactions(walletId); + var isReady = !Model.HasErrors; var isSignable = !isReady; var needsExport = !isSignable && !isReady; Layout = "_LayoutWizard"; @@ -78,15 +77,7 @@ } @section Navbar { - @if (backUrl != null) - { - - - - } - - - + }
diff --git a/BTCPayServer/Views/UIWallets/WalletSend.cshtml b/BTCPayServer/Views/UIWallets/WalletSend.cshtml index 59b33653f..77571610b 100644 --- a/BTCPayServer/Views/UIWallets/WalletSend.cshtml +++ b/BTCPayServer/Views/UIWallets/WalletSend.cshtml @@ -7,8 +7,7 @@ @model WalletSendModel @{ var walletId = Context.GetRouteValue("walletId").ToString(); - var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); - var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null; + Model.ReturnUrl ??= Url.WalletTransactions(walletId); Layout = "_LayoutWizard"; ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Send {0}", Model.CryptoCode], walletId); Csp.Add("worker-src", "blob:"); @@ -16,15 +15,7 @@ } @section Navbar { - @if (backUrl != null) - { - - - - } - - - + } @section PageHeadContent diff --git a/BTCPayServer/Views/UIWallets/WalletSendVault.cshtml b/BTCPayServer/Views/UIWallets/WalletSendVault.cshtml index d7e154c42..07368739a 100644 --- a/BTCPayServer/Views/UIWallets/WalletSendVault.cshtml +++ b/BTCPayServer/Views/UIWallets/WalletSendVault.cshtml @@ -2,22 +2,13 @@ @model WalletSendVaultModel @{ var walletId = Context.GetRouteValue("walletId").ToString(); - var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); - var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null; + Model.ReturnUrl ??= Url.WalletTransactions(walletId); Layout = "_LayoutWizard"; ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Sign the transaction"], walletId); } @section Navbar { - @if (backUrl != null) - { - - - - } - - - + }
diff --git a/BTCPayServer/Views/UIWallets/WalletSigningOptions.cshtml b/BTCPayServer/Views/UIWallets/WalletSigningOptions.cshtml index e8ed7fcbe..d211d6720 100644 --- a/BTCPayServer/Views/UIWallets/WalletSigningOptions.cshtml +++ b/BTCPayServer/Views/UIWallets/WalletSigningOptions.cshtml @@ -3,22 +3,13 @@ @inject BTCPayNetworkProvider BTCPayNetworkProvider @{ var walletId = WalletId.Parse(Context.GetRouteValue("walletId").ToString()); - var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); - var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null; + Model.ReturnUrl ??= Url.WalletTransactions(walletId); Layout = "_LayoutWizard"; ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Sign the transaction"], walletId.ToString()); } @section Navbar { - @if (backUrl != null) - { - - - - } - - - + }