mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 01:43:50 +01:00
Add more translations (#6302)
* Newlines * Dashboard * Add more translations * Moar * Remove from translated texts * Dictionary controller translations * Batch 1 of controller updates * Batch 2 of controller updates * Component translations * Batch 3 of controller updates * Fixes
This commit is contained in:
parent
7e1712c8cd
commit
77fba4aee3
@ -10,7 +10,7 @@ public static class SetStatusMessageModelExtensions
|
||||
{
|
||||
public static void SetStatusSuccess(this ITempDataDictionary tempData, string statusMessage)
|
||||
{
|
||||
tempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
tempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = statusMessage
|
||||
@ -34,19 +34,14 @@ public static class SetStatusMessageModelExtensions
|
||||
tempData.TryGetValue("StatusMessageModel", out var model);
|
||||
if (successMessage != null || errorMessage != null)
|
||||
{
|
||||
var parsedModel = new StatusMessageModel();
|
||||
parsedModel.Message = (string)successMessage ?? (string)errorMessage;
|
||||
if (successMessage != null)
|
||||
var parsedModel = new StatusMessageModel
|
||||
{
|
||||
parsedModel.Severity = StatusMessageModel.StatusSeverity.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
parsedModel.Severity = StatusMessageModel.StatusSeverity.Error;
|
||||
}
|
||||
Message = (string)successMessage ?? (string)errorMessage,
|
||||
Severity = successMessage != null ? StatusMessageModel.StatusSeverity.Success : StatusMessageModel.StatusSeverity.Error
|
||||
};
|
||||
return parsedModel;
|
||||
}
|
||||
else if (model != null && model is string str)
|
||||
if (model is string str)
|
||||
{
|
||||
return JObject.Parse(str).ToObject<StatusMessageModel>();
|
||||
}
|
||||
|
@ -648,7 +648,7 @@ namespace BTCPayServer.Tests
|
||||
var store2 = acc.GetController<UIStoresController>();
|
||||
await store2.Pair(pairingCode.ToString(), store2.CurrentStore.Id);
|
||||
Assert.Contains(nameof(PairingResult.ReusedKey),
|
||||
(string)store2.TempData[WellKnownTempData.ErrorMessage], StringComparison.CurrentCultureIgnoreCase);
|
||||
store2.TempData[WellKnownTempData.ErrorMessage].ToString(), StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact(Timeout = LongRunningTestTimeout * 2)]
|
||||
|
@ -2,23 +2,30 @@
|
||||
@using BTCPayServer.Abstractions.TagHelpers
|
||||
@using BTCPayServer.Plugins.Crowdfund
|
||||
@model BTCPayServer.Components.AppSales.AppSalesViewModel
|
||||
@{
|
||||
var label = Model.AppType == CrowdfundAppType.AppType ? "Contributions" : "Sales";
|
||||
}
|
||||
|
||||
<div id="AppSales-@Model.Id" class="widget app-sales">
|
||||
<header class="mb-3">
|
||||
<h3>@Model.Name @label</h3>
|
||||
<h3>
|
||||
@Model.Name
|
||||
@if (Model.AppType == CrowdfundAppType.AppType)
|
||||
{
|
||||
<span text-translate="true">Contributions</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span text-translate="true">Sales</span>
|
||||
}
|
||||
</h3>
|
||||
@if (!string.IsNullOrEmpty(Model.AppUrl))
|
||||
{
|
||||
<a href="@Model.AppUrl">Manage</a>
|
||||
<a href="@Model.AppUrl" text-translate="true">Manage</a>
|
||||
}
|
||||
</header>
|
||||
@if (Model.InitialRendering)
|
||||
{
|
||||
<div class="loading d-flex justify-content-center p-3">
|
||||
<div class="spinner-border text-light" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
<span class="visually-hidden" text-translate="true">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<script src="~/Components/AppSales/Default.cshtml.js" asp-append-version="true"></script>
|
||||
@ -39,7 +46,15 @@
|
||||
{
|
||||
<header class="mb-3">
|
||||
<span>
|
||||
<span class="sales-count">@Model.SalesCount</span> Total @label
|
||||
<span class="sales-count">@Model.SalesCount</span>
|
||||
@if (Model.AppType == CrowdfundAppType.AppType)
|
||||
{
|
||||
<span text-translate="true">Total Contributions</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span text-translate="true">Total Sales</span>
|
||||
}
|
||||
</span>
|
||||
<div class="btn-group only-for-js" role="group" aria-label="Filter">
|
||||
<input type="radio" class="btn-check" name="AppSalesPeriod-@Model.Id" id="AppSalesPeriodWeek-@Model.Id" value="@AppSalesPeriod.Week" @(Model.Period == AppSalesPeriod.Week ? "checked" : "")>
|
||||
|
@ -1,18 +1,24 @@
|
||||
@using BTCPayServer.Plugins.Crowdfund
|
||||
@model BTCPayServer.Components.AppTopItems.AppTopItemsViewModel
|
||||
@{
|
||||
var label = Model.AppType == CrowdfundAppType.AppType ? "contribution" : "sale";
|
||||
}
|
||||
|
||||
<div id="AppTopItems-@Model.Id" class="widget app-top-items">
|
||||
<header class="mb-3">
|
||||
<h3>Top @(Model.AppType == CrowdfundAppType.AppType ? "Perks" : "Items")</h3>
|
||||
<h3>
|
||||
@if (Model.AppType == CrowdfundAppType.AppType)
|
||||
{
|
||||
<span text-translate="true">Top Perks</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span text-translate="true">Top Items</span>
|
||||
}
|
||||
</h3>
|
||||
</header>
|
||||
@if (Model.InitialRendering)
|
||||
{
|
||||
<div class="loading d-flex justify-content-center p-3">
|
||||
<div class="spinner-border text-light" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
<span class="visually-hidden" text-translate="true">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<script src="~/Components/AppTopItems/Default.cshtml.js" asp-append-version="true"></script>
|
||||
@ -45,7 +51,31 @@
|
||||
@entry.Title
|
||||
</span>
|
||||
<span class="app-item-value" data-sensitive>
|
||||
<span class="text-muted">@entry.SalesCount @($"{label}{(entry.SalesCount == 1 ? "" : "s")}"),</span>
|
||||
<span class="text-muted">
|
||||
@entry.SalesCount
|
||||
@if (Model.AppType == CrowdfundAppType.AppType)
|
||||
{
|
||||
if (entry.SalesCount == 1)
|
||||
{
|
||||
<span text-translate="true">contribution</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span text-translate="true">contributions</span>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (entry.SalesCount == 1)
|
||||
{
|
||||
<span text-translate="true">sale</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span text-translate="true">sales</span>
|
||||
}
|
||||
},
|
||||
</span>
|
||||
@entry.TotalFormatted
|
||||
</span>
|
||||
</div>
|
||||
@ -55,7 +85,14 @@
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
No @($"{label}s") have been made yet.
|
||||
@if (Model.AppType == CrowdfundAppType.AppType)
|
||||
{
|
||||
<span text-translate="true">No contributions have been made yet.</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span text-translate="true">No sales have been made yet.</span>
|
||||
}
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
|
@ -10,7 +10,7 @@
|
||||
<div class="d-inline-flex align-items-center gap-2">
|
||||
@if (Model.IsArchived)
|
||||
{
|
||||
<span class="badge bg-warning">archived</span>
|
||||
<span class="badge bg-warning" text-translate="true">archived</span>
|
||||
}
|
||||
<div class="badge badge-@badgeClass" data-invoice-state-badge="@Model.InvoiceId">
|
||||
@if (canMark)
|
||||
@ -21,13 +21,13 @@
|
||||
<div class="dropdown-menu">
|
||||
@if (Model.State.CanMarkInvalid())
|
||||
{
|
||||
<button type="button" class="dropdown-item lh-base" data-invoice-id="@Model.InvoiceId" data-new-state="invalid">
|
||||
<button type="button" class="dropdown-item lh-base" data-invoice-id="@Model.InvoiceId" data-new-state="invalid" text-translate="true">
|
||||
Mark as invalid
|
||||
</button>
|
||||
}
|
||||
@if (Model.State.CanMarkComplete())
|
||||
{
|
||||
<button type="button" class="dropdown-item lh-base" data-invoice-id="@Model.InvoiceId" data-new-state="settled">
|
||||
<button type="button" class="dropdown-item lh-base" data-invoice-id="@Model.InvoiceId" data-new-state="settled" text-translate="true">
|
||||
Mark as settled
|
||||
</button>
|
||||
}
|
||||
@ -62,6 +62,6 @@
|
||||
}
|
||||
@if (Model.HasRefund)
|
||||
{
|
||||
<span class="badge bg-warning">Refund</span>
|
||||
<span class="badge bg-warning" text-translate="true">Refund</span>
|
||||
}
|
||||
</div>
|
||||
|
@ -11,7 +11,7 @@
|
||||
walletId = Model.WalletObjectId.WalletId
|
||||
}): string.Empty;
|
||||
}
|
||||
<input id="@elementId" placeholder="Select labels" autocomplete="off" value="@string.Join(",", Model.SelectedLabels)"
|
||||
<input id="@elementId" placeholder=@StringLocalizer["Select labels"] autocomplete="off" value="@string.Join(",", Model.SelectedLabels)"
|
||||
class="only-for-js form-control label-manager ts-wrapper @(Model.DisplayInline ? "ts-inline" : "")"
|
||||
data-fetch-url="@fetchUrl"
|
||||
data-update-url="@updateUrl"
|
||||
|
@ -13,7 +13,7 @@
|
||||
@if (Model.Skip > 0)
|
||||
{
|
||||
<li class="page-item">
|
||||
<a class="page-link" tabindex="-1" href="@NavigatePages(-1, Model.Count)">Prev</a>
|
||||
<a class="page-link" tabindex="-1" href="@NavigatePages(-1, Model.Count)" text-translate="true">Prev</a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item disabled">
|
||||
@ -35,7 +35,7 @@
|
||||
@if ((Model.Total is null && Model.CurrentPageCount >= Model.Count) || (Model.Total is not null && Model.Total.Value > Model.Skip + Model.Count))
|
||||
{
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="@NavigatePages(1, Model.Count)">Next</a>
|
||||
<a class="page-link" href="@NavigatePages(1, Model.Count)" text-translate="true">Next</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
@ -45,7 +45,7 @@
|
||||
{
|
||||
<ul class="pagination ms-auto">
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link">Page Size</span>
|
||||
<span class="page-link" text-translate="true">Page Size</span>
|
||||
</li>
|
||||
@foreach (var pageSize in pageSizeOptions)
|
||||
{
|
||||
|
@ -29,7 +29,7 @@
|
||||
<div class="d-flex align-items-baseline gap-1">
|
||||
<h3 class="d-inline-block me-1" data-balance="@Model.TotalOffchain" data-sensitive>@Model.TotalOffchain</h3>
|
||||
<span class="text-secondary fw-semibold text-nowrap">
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> in channels", @Model.CryptoCode]
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> in channels", Model.CryptoCode]
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
@Model.Balance.OffchainBalance.Opening
|
||||
</span>
|
||||
<span class="text-secondary text-nowrap">
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> opening channels", @Model.CryptoCode]
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> opening channels", Model.CryptoCode]
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@ -52,7 +52,7 @@
|
||||
@Model.Balance.OffchainBalance.Local
|
||||
</span>
|
||||
<span class="text-secondary text-nowrap">
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> local balance", @Model.CryptoCode]
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> local balance", Model.CryptoCode]
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@ -63,7 +63,7 @@
|
||||
@Model.Balance.OffchainBalance.Remote
|
||||
</span>
|
||||
<span class="text-secondary text-nowrap">
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> remote balance", @Model.CryptoCode]
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> remote balance", Model.CryptoCode]
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@ -74,7 +74,7 @@
|
||||
@Model.Balance.OffchainBalance.Closing
|
||||
</span>
|
||||
<span class="text-secondary text-nowrap">
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> closing channels", @Model.CryptoCode]
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> closing channels", Model.CryptoCode]
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@ -87,7 +87,7 @@
|
||||
<div class="d-flex align-items-baseline gap-1">
|
||||
<h3 class="d-inline-block me-1" data-balance="@Model.TotalOnchain" data-sensitive>@Model.TotalOnchain</h3>
|
||||
<span class="text-secondary fw-semibold text-nowrap">
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> on-chain", @Model.CryptoCode]
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> on-chain", Model.CryptoCode]
|
||||
</span>
|
||||
</div>
|
||||
<div class="balance-details collapse" id="balanceDetailsOnchain">
|
||||
@ -98,7 +98,7 @@
|
||||
@Model.Balance.OnchainBalance.Confirmed
|
||||
</span>
|
||||
<span class="text-secondary text-nowrap">
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> confirmed", @Model.CryptoCode]
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> confirmed", Model.CryptoCode]
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@ -109,7 +109,7 @@
|
||||
@Model.Balance.OnchainBalance.Unconfirmed
|
||||
</span>
|
||||
<span class="text-secondary text-nowrap">
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> unconfirmed", @Model.CryptoCode]
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> unconfirmed", Model.CryptoCode]
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@ -120,7 +120,7 @@
|
||||
@Model.Balance.OnchainBalance.Reserved
|
||||
</span>
|
||||
<span class="text-secondary text-nowrap">
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> reserved", @Model.CryptoCode]
|
||||
@ViewLocalizer["<span class=\"currency\">{0}</span> reserved", Model.CryptoCode]
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
asp-route-storeId="@Model.Store.Id"
|
||||
target="_blank"
|
||||
id="PublicNodeInfo"
|
||||
text-translate="true">
|
||||
text-translate="true">
|
||||
Node Info
|
||||
</a>
|
||||
</header>
|
||||
|
@ -6,7 +6,7 @@
|
||||
{
|
||||
<div class="loading d-flex justify-content-center p-3">
|
||||
<div class="spinner-border text-light" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
<span class="visually-hidden" text-translate="true">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
@ -23,6 +23,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NBitcoin.DataEncoders;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@ -47,6 +48,7 @@ namespace BTCPayServer.Controllers
|
||||
readonly ILogger _logger;
|
||||
|
||||
public PoliciesSettings PoliciesSettings { get; }
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
public Logs Logs { get; }
|
||||
|
||||
public UIAccountController(
|
||||
@ -62,6 +64,7 @@ namespace BTCPayServer.Controllers
|
||||
UserLoginCodeService userLoginCodeService,
|
||||
LnurlAuthService lnurlAuthService,
|
||||
LinkGenerator linkGenerator,
|
||||
IStringLocalizer stringLocalizer,
|
||||
Logs logs)
|
||||
{
|
||||
_userManager = userManager;
|
||||
@ -78,6 +81,7 @@ namespace BTCPayServer.Controllers
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logs.PayServer;
|
||||
Logs = logs;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
[TempData]
|
||||
@ -149,7 +153,7 @@ namespace BTCPayServer.Controllers
|
||||
var userId = _userLoginCodeService.Verify(code);
|
||||
if (userId is null)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "Login code was invalid";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Login code was invalid"].Value;
|
||||
return await Login(returnUrl);
|
||||
}
|
||||
|
||||
@ -187,7 +191,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
// Require the user to pass basic checks (approval, confirmed email, not disabled) before they can log on
|
||||
var user = await _userManager.FindByEmailAsync(model.Email);
|
||||
const string errorMessage = "Invalid login attempt.";
|
||||
var errorMessage = StringLocalizer["Invalid login attempt."].Value;
|
||||
if (!UserService.TryCanLogin(user, out var message))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
@ -311,7 +315,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
var errorMessage = "Invalid login attempt.";
|
||||
var errorMessage = StringLocalizer["Invalid login attempt."].Value;
|
||||
var user = await _userManager.FindByIdAsync(viewModel.UserId);
|
||||
if (!UserService.TryCanLogin(user, out var message))
|
||||
{
|
||||
@ -629,7 +633,7 @@ namespace BTCPayServer.Controllers
|
||||
});
|
||||
RegisteredUserId = user.Id;
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Account created.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Account created."].Value;
|
||||
var requiresConfirmedEmail = policies.RequiresConfirmedEmail && !user.EmailConfirmed;
|
||||
var requiresUserApproval = policies.RequiresUserApproval && !user.Approved;
|
||||
if (requiresConfirmedEmail)
|
||||
@ -704,7 +708,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = "Your email has been confirmed."
|
||||
Message = StringLocalizer["Your email has been confirmed."].Value
|
||||
});
|
||||
await FinalizeInvitationIfApplicable(user);
|
||||
return RedirectToAction(nameof(Login), new { email = user.Email });
|
||||
@ -713,7 +717,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Info,
|
||||
Message = "Your email has been confirmed. Please set your password."
|
||||
Message = StringLocalizer["Your email has been confirmed. Please set your password."].Value
|
||||
});
|
||||
return await RedirectToSetPassword(user);
|
||||
}
|
||||
@ -811,7 +815,9 @@ namespace BTCPayServer.Controllers
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = hasPassword ? "Password successfully set." : "Account successfully created."
|
||||
Message = hasPassword
|
||||
? StringLocalizer["Password successfully set."].Value
|
||||
: StringLocalizer["Account successfully created."].Value
|
||||
});
|
||||
if (!hasPassword) await FinalizeInvitationIfApplicable(user);
|
||||
return RedirectToAction(nameof(Login));
|
||||
@ -848,7 +854,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Info,
|
||||
Message = "Invitation accepted. Please set your password."
|
||||
Message = StringLocalizer["Invitation accepted. Please set your password."].Value
|
||||
});
|
||||
return await RedirectToSetPassword(user);
|
||||
}
|
||||
@ -857,7 +863,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Info,
|
||||
Message = "Your password has been set by the user who invited you."
|
||||
Message = StringLocalizer["Your password has been set by the user who invited you."].Value
|
||||
});
|
||||
|
||||
await FinalizeInvitationIfApplicable(user);
|
||||
@ -930,7 +936,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = "You cannot login over an insecure connection. Please use HTTPS or Tor."
|
||||
Message = StringLocalizer["You cannot login over an insecure connection. Please use HTTPS or Tor."].Value
|
||||
});
|
||||
|
||||
ViewData["disabled"] = true;
|
||||
|
@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
@ -30,6 +31,7 @@ namespace BTCPayServer.Controllers
|
||||
StoreRepository storeRepository,
|
||||
IFileService fileService,
|
||||
AppService appService,
|
||||
IStringLocalizer stringLocalizer,
|
||||
IHtmlHelper html)
|
||||
{
|
||||
_userManager = userManager;
|
||||
@ -39,6 +41,7 @@ namespace BTCPayServer.Controllers
|
||||
_fileService = fileService;
|
||||
_appService = appService;
|
||||
Html = html;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
@ -50,6 +53,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
public string CreatedAppId { get; set; }
|
||||
public IHtmlHelper Html { get; }
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public class AppUpdated
|
||||
{
|
||||
@ -158,7 +162,7 @@ namespace BTCPayServer.Controllers
|
||||
var type = _appService.GetAppType(vm.AppType ?? vm.SelectedAppType);
|
||||
if (type is null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.SelectedAppType), "Invalid App Type");
|
||||
ModelState.AddModelError(nameof(vm.SelectedAppType), StringLocalizer["Invalid App Type"]);
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
@ -177,7 +181,7 @@ namespace BTCPayServer.Controllers
|
||||
await _appService.SetDefaultSettings(appData, defaultCurrency);
|
||||
await _appService.UpdateOrCreateApp(appData);
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = "App successfully created";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["App successfully created"].Value;
|
||||
CreatedAppId = appData.Id;
|
||||
|
||||
var url = await type.ConfigureLink(appData);
|
||||
@ -192,7 +196,7 @@ namespace BTCPayServer.Controllers
|
||||
if (app == null)
|
||||
return NotFound();
|
||||
|
||||
return View("Confirm", new ConfirmModel("Delete app", $"The app <strong>{Html.Encode(app.Name)}</strong> and its settings will be permanently deleted. Are you sure?", "Delete"));
|
||||
return View("Confirm", new ConfirmModel(StringLocalizer["Delete app"], $"The app <strong>{Html.Encode(app.Name)}</strong> and its settings will be permanently deleted. Are you sure?", StringLocalizer["Delete"]));
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
@ -204,7 +208,7 @@ namespace BTCPayServer.Controllers
|
||||
return NotFound();
|
||||
|
||||
if (await _appService.DeleteApp(app))
|
||||
TempData[WellKnownTempData.SuccessMessage] = "App deleted successfully.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["App deleted successfully."].Value;
|
||||
|
||||
return RedirectToAction(nameof(UIStoresController.Dashboard), "UIStores", new { storeId = app.StoreDataId });
|
||||
}
|
||||
@ -227,12 +231,14 @@ namespace BTCPayServer.Controllers
|
||||
if (await _appService.SetArchived(app, archived))
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = archived
|
||||
? "The app has been archived and will no longer appear in the apps list by default."
|
||||
: "The app has been unarchived and will appear in the apps list by default again.";
|
||||
? StringLocalizer["The app has been archived and will no longer appear in the apps list by default."].Value
|
||||
: StringLocalizer["The app has been unarchived and will appear in the apps list by default again."].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"Failed to {(archived ? "archive" : "unarchive")} the app.";
|
||||
TempData[WellKnownTempData.ErrorMessage] = archived
|
||||
? StringLocalizer["Failed to archive the app."].Value
|
||||
: StringLocalizer["Failed to unarchive the app."].Value;
|
||||
}
|
||||
|
||||
var url = await type.ConfigureLink(app);
|
||||
|
@ -86,7 +86,7 @@ namespace BTCPayServer.Controllers
|
||||
var newDeliveryId = await WebhookNotificationManager.Redeliver(deliveryId);
|
||||
if (newDeliveryId is null)
|
||||
return NotFound();
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Successfully planned a redelivery";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Successfully planned a redelivery"].Value;
|
||||
return RedirectToAction(nameof(Invoice),
|
||||
new
|
||||
{
|
||||
@ -294,9 +294,9 @@ namespace BTCPayServer.Controllers
|
||||
var payoutMethodIds = _payoutHandlers.GetSupportedPayoutMethods(this.GetCurrentStore());
|
||||
if (!payoutMethodIds.Any())
|
||||
{
|
||||
var vm = new RefundModel { Title = "No matching payment method" };
|
||||
var vm = new RefundModel { Title = StringLocalizer["No matching payment method"] };
|
||||
ModelState.AddModelError(nameof(vm.AvailablePaymentMethods),
|
||||
"There are no payment methods available to provide refunds with for this invoice.");
|
||||
StringLocalizer["There are no payment methods available to provide refunds with for this invoice."]);
|
||||
return View("_RefundModal", vm);
|
||||
}
|
||||
|
||||
@ -306,7 +306,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
var refund = new RefundModel
|
||||
{
|
||||
Title = "Payment method",
|
||||
Title = StringLocalizer["Payment method"],
|
||||
AvailablePaymentMethods =
|
||||
new SelectList(payoutMethodIds.Select(id => new SelectListItem(id.ToString(), id.ToString())),
|
||||
"Value", "Text"),
|
||||
@ -344,7 +344,7 @@ namespace BTCPayServer.Controllers
|
||||
var pmis = _payoutHandlers.GetSupportedPayoutMethods(store);
|
||||
if (!pmis.Contains(pmi))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.SelectedPayoutMethod), $"Invalid payout method");
|
||||
ModelState.AddModelError(nameof(model.SelectedPayoutMethod), StringLocalizer["Invalid payout method"]);
|
||||
return View("_RefundModal", model);
|
||||
}
|
||||
|
||||
@ -353,7 +353,7 @@ namespace BTCPayServer.Controllers
|
||||
var paymentMethod = paymentMethodId is null ? null : invoice.GetPaymentPrompt(paymentMethodId);
|
||||
if (paymentMethod?.Currency is null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.SelectedPayoutMethod), $"Invalid payout method");
|
||||
ModelState.AddModelError(nameof(model.SelectedPayoutMethod), StringLocalizer["Invalid payout method"]);
|
||||
return View("_RefundModal", model);
|
||||
}
|
||||
|
||||
@ -377,7 +377,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
case RefundSteps.SelectPaymentMethod:
|
||||
model.RefundStep = RefundSteps.SelectRate;
|
||||
model.Title = "How much to refund?";
|
||||
model.Title = StringLocalizer["How much to refund?"];
|
||||
|
||||
var paidCurrency = Math.Round(cryptoPaid * paymentMethod.Rate, cdCurrency.Divisibility);
|
||||
model.CryptoAmountThen = cryptoPaid.RoundToSignificant(paymentMethod.Divisibility);
|
||||
@ -390,7 +390,7 @@ namespace BTCPayServer.Controllers
|
||||
if (rateResult.BidAsk is null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.SelectedRefundOption),
|
||||
$"Impossible to fetch rate: {rateResult.EvaluatedRule}");
|
||||
StringLocalizer["Impossible to fetch rate: {0}", rateResult.EvaluatedRule]);
|
||||
return View("_RefundModal", model);
|
||||
}
|
||||
|
||||
@ -413,7 +413,7 @@ namespace BTCPayServer.Controllers
|
||||
case RefundSteps.SelectRate:
|
||||
createPullPayment = new CreatePullPayment
|
||||
{
|
||||
Name = $"Refund {invoice.Id}",
|
||||
Name = StringLocalizer["Refund {0}", invoice.Id],
|
||||
PayoutMethods = new[] { pmi },
|
||||
StoreId = invoice.StoreId,
|
||||
BOLT11Expiration = store.GetStoreBlob().RefundBOLT11Expiration
|
||||
@ -423,7 +423,7 @@ namespace BTCPayServer.Controllers
|
||||
.Succeeded;
|
||||
if (model.SubtractPercentage is < 0 or > 100)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.SubtractPercentage), "Percentage must be a numeric value between 0 and 100");
|
||||
ModelState.AddModelError(nameof(model.SubtractPercentage), StringLocalizer["Percentage must be a numeric value between 0 and 100"]);
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@ -457,11 +457,11 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (!isPaidOver)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.SelectedRefundOption), "Invoice is not overpaid");
|
||||
ModelState.AddModelError(nameof(model.SelectedRefundOption), StringLocalizer["Invoice is not overpaid"]);
|
||||
}
|
||||
if (overpaidAmount == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.SelectedRefundOption), "Overpaid amount cannot be calculated");
|
||||
ModelState.AddModelError(nameof(model.SelectedRefundOption), StringLocalizer["Overpaid amount cannot be calculated"]);
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@ -474,17 +474,17 @@ namespace BTCPayServer.Controllers
|
||||
break;
|
||||
|
||||
case "Custom":
|
||||
model.Title = "How much to refund?";
|
||||
model.Title = StringLocalizer["How much to refund?"];
|
||||
model.RefundStep = RefundSteps.SelectRate;
|
||||
|
||||
if (model.CustomAmount <= 0)
|
||||
{
|
||||
model.AddModelError(refundModel => refundModel.CustomAmount, "Amount must be greater than 0", this);
|
||||
model.AddModelError(refundModel => refundModel.CustomAmount, StringLocalizer["Amount must be greater than 0"], this);
|
||||
}
|
||||
if (string.IsNullOrEmpty(model.CustomCurrency) ||
|
||||
_CurrencyNameTable.GetCurrencyData(model.CustomCurrency, false) == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.CustomCurrency), "Invalid currency");
|
||||
ModelState.AddModelError(nameof(model.CustomCurrency), StringLocalizer["Invalid currency"]);
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@ -500,7 +500,7 @@ namespace BTCPayServer.Controllers
|
||||
if (rateResult.BidAsk is null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.SelectedRefundOption),
|
||||
$"Impossible to fetch rate: {rateResult.EvaluatedRule}");
|
||||
StringLocalizer["Impossible to fetch rate: {0}", rateResult.EvaluatedRule]);
|
||||
return View("_RefundModal", model);
|
||||
}
|
||||
|
||||
@ -510,7 +510,7 @@ namespace BTCPayServer.Controllers
|
||||
break;
|
||||
|
||||
default:
|
||||
ModelState.AddModelError(nameof(model.SelectedRefundOption), "Please select an option before proceeding");
|
||||
ModelState.AddModelError(nameof(model.SelectedRefundOption), StringLocalizer["Please select an option before proceeding"]);
|
||||
return View("_RefundModal", model);
|
||||
}
|
||||
break;
|
||||
@ -608,10 +608,12 @@ namespace BTCPayServer.Controllers
|
||||
if (invoice == null)
|
||||
return NotFound();
|
||||
await _InvoiceRepository.ToggleInvoiceArchival(invoiceId, !invoice.Archived);
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = invoice.Archived ? "The invoice has been unarchived and will appear in the invoice list by default again." : "The invoice has been archived and will no longer appear in the invoice list by default."
|
||||
Message = invoice.Archived
|
||||
? StringLocalizer["The invoice has been unarchived and will appear in the invoice list by default again."].Value
|
||||
: StringLocalizer["The invoice has been archived and will no longer appear in the invoice list by default."].Value
|
||||
});
|
||||
return RedirectToAction(nameof(invoice), new { invoiceId });
|
||||
}
|
||||
@ -626,28 +628,32 @@ namespace BTCPayServer.Controllers
|
||||
return RedirectToAction(nameof(ListInvoices), new { storeId });
|
||||
}
|
||||
if (selectedItems.Length == 0)
|
||||
return NotSupported("No invoice has been selected");
|
||||
return NotSupported(StringLocalizer["No invoice has been selected"]);
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case "archive":
|
||||
await _InvoiceRepository.MassArchive(selectedItems);
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"{selectedItems.Length} invoice{(selectedItems.Length == 1 ? "" : "s")} archived.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = selectedItems.Length == 1
|
||||
? StringLocalizer["{0} invoice archived.", selectedItems.Length].Value
|
||||
: StringLocalizer["{0} invoices archived.", selectedItems.Length].Value;
|
||||
break;
|
||||
|
||||
case "unarchive":
|
||||
await _InvoiceRepository.MassArchive(selectedItems, false);
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"{selectedItems.Length} invoice{(selectedItems.Length == 1 ? "" : "s")} unarchived.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = selectedItems.Length == 1
|
||||
? StringLocalizer["{0} invoice unarchived.", selectedItems.Length].Value
|
||||
: StringLocalizer["{0} invoices unarchived.", selectedItems.Length].Value;
|
||||
break;
|
||||
case "cpfp" when storeId is not null:
|
||||
var network = _NetworkProvider.DefaultNetwork;
|
||||
var explorer = _ExplorerClients.GetExplorerClient(network);
|
||||
if (explorer is null)
|
||||
return NotSupported("This feature is only available to BTC wallets");
|
||||
return NotSupported(StringLocalizer["This feature is only available to BTC wallets"]);
|
||||
if (!GetCurrentStore().HasPermission(GetUserId(), Policies.CanModifyStoreSettings))
|
||||
return Forbid();
|
||||
|
||||
var derivationScheme = (this.GetCurrentStore().GetDerivationSchemeSettings(_handlers, network.CryptoCode))?.AccountDerivation;
|
||||
var derivationScheme = GetCurrentStore().GetDerivationSchemeSettings(_handlers, network.CryptoCode)?.AccountDerivation;
|
||||
if (derivationScheme is null)
|
||||
return NotSupported("This feature is only available to BTC wallets");
|
||||
var btc = PaymentTypes.CHAIN.GetPaymentMethodId("BTC");
|
||||
@ -657,7 +663,7 @@ namespace BTCPayServer.Controllers
|
||||
var parameters = new MultiValueDictionary<string, string>();
|
||||
foreach (var utxo in bumpableUTXOs)
|
||||
{
|
||||
parameters.Add($"outpoints[]", utxo.Outpoint.ToString());
|
||||
parameters.Add("outpoints[]", utxo.Outpoint.ToString());
|
||||
}
|
||||
return View("PostRedirect", new PostRedirectViewModel
|
||||
{
|
||||
@ -1154,7 +1160,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (string.IsNullOrEmpty(model?.StoreId))
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "You need to select a store before creating an invoice.";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["You need to select a store before creating an invoice."].Value;
|
||||
return RedirectToAction(nameof(UIHomeController.Index), "UIHome");
|
||||
}
|
||||
|
||||
@ -1202,7 +1208,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.Metadata), "Metadata was not valid JSON");
|
||||
ModelState.AddModelError(nameof(model.Metadata), StringLocalizer["Metadata was not valid JSON"]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1228,7 +1234,7 @@ namespace BTCPayServer.Controllers
|
||||
metadata.BuyerEmail = model.BuyerEmail;
|
||||
}
|
||||
|
||||
var result = await CreateInvoiceCoreRaw(new CreateInvoiceRequest()
|
||||
var result = await CreateInvoiceCoreRaw(new CreateInvoiceRequest
|
||||
{
|
||||
Amount = model.Amount,
|
||||
Currency = model.Currency,
|
||||
@ -1249,14 +1255,14 @@ namespace BTCPayServer.Controllers
|
||||
},
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Invoice {result.Id} just created!";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Invoice {0} just created!", result.Id].Value;
|
||||
CreatedInvoiceId = result.Id;
|
||||
|
||||
return RedirectToAction(nameof(Invoice), new { storeId = result.StoreId, invoiceId = result.Id });
|
||||
}
|
||||
catch (BitpayHttpException ex)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = ex.Message
|
||||
|
@ -35,6 +35,7 @@ using StoreData = BTCPayServer.Data.StoreData;
|
||||
using Serilog.Filters;
|
||||
using PeterO.Numbers;
|
||||
using BTCPayServer.Payouts;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
@ -69,6 +70,7 @@ namespace BTCPayServer.Controllers
|
||||
private readonly UriResolver _uriResolver;
|
||||
|
||||
public WebhookSender WebhookNotificationManager { get; }
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UIInvoiceController(
|
||||
InvoiceRepository invoiceRepository,
|
||||
@ -98,6 +100,7 @@ namespace BTCPayServer.Controllers
|
||||
IAuthorizationService authorizationService,
|
||||
TransactionLinkProviders transactionLinkProviders,
|
||||
Dictionary<PaymentMethodId, ICheckoutModelExtension> paymentModelExtensions,
|
||||
IStringLocalizer stringLocalizer,
|
||||
PrettyNameProvider prettyName)
|
||||
{
|
||||
_displayFormatter = displayFormatter;
|
||||
@ -127,6 +130,7 @@ namespace BTCPayServer.Controllers
|
||||
_uriResolver = uriResolver;
|
||||
_defaultRules = defaultRules;
|
||||
_appService = appService;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
internal async Task<InvoiceEntity> CreatePaymentRequestInvoice(Data.PaymentRequestData prData, decimal? amount, decimal amountDue, StoreData storeData, HttpRequest request, CancellationToken cancellationToken)
|
||||
|
@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NBitcoin;
|
||||
using NBitcoin.Crypto;
|
||||
using NBitcoin.DataEncoders;
|
||||
@ -23,22 +24,24 @@ namespace BTCPayServer
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly LnurlAuthService _lnurlAuthService;
|
||||
private readonly LinkGenerator _linkGenerator;
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UILNURLAuthController(UserManager<ApplicationUser> userManager, LnurlAuthService lnurlAuthService,
|
||||
LinkGenerator linkGenerator)
|
||||
IStringLocalizer stringLocalizer, LinkGenerator linkGenerator)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_lnurlAuthService = lnurlAuthService;
|
||||
_linkGenerator = linkGenerator;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
[HttpGet("{id}/delete")]
|
||||
public IActionResult Remove(string id)
|
||||
{
|
||||
return View("Confirm",
|
||||
new ConfirmModel("Remove LNURL Auth link",
|
||||
"Your account will no longer have this Lightning wallet as an option for two-factor authentication.",
|
||||
"Remove"));
|
||||
new ConfirmModel(StringLocalizer["Remove LNURL Auth link"],
|
||||
StringLocalizer["Your account will no longer have this Lightning wallet as an option for two-factor authentication."],
|
||||
StringLocalizer["Remove"]));
|
||||
}
|
||||
|
||||
[HttpPost("{id}/delete")]
|
||||
@ -49,7 +52,7 @@ namespace BTCPayServer
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Html = "LNURL Auth was removed successfully."
|
||||
Message = StringLocalizer["LNURL Auth was removed successfully."].Value
|
||||
});
|
||||
|
||||
return RedirectToList();
|
||||
@ -65,7 +68,7 @@ namespace BTCPayServer
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Html = "The Lightning node could not be registered."
|
||||
Html = StringLocalizer["The Lightning node could not be registered."].Value
|
||||
});
|
||||
|
||||
return RedirectToList();
|
||||
|
@ -36,12 +36,11 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NBitcoin;
|
||||
using NBitpayClient;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using LightningAddressData = BTCPayServer.Data.LightningAddressData;
|
||||
using MarkPayoutRequest = BTCPayServer.HostedServices.MarkPayoutRequest;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
@ -63,6 +62,7 @@ namespace BTCPayServer
|
||||
private readonly InvoiceActivator _invoiceActivator;
|
||||
private readonly PaymentMethodHandlerDictionary _handlers;
|
||||
private readonly PayoutProcessorService _payoutProcessorService;
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UILNURLController(InvoiceRepository invoiceRepository,
|
||||
EventAggregator eventAggregator,
|
||||
@ -77,6 +77,7 @@ namespace BTCPayServer
|
||||
PullPaymentHostedService pullPaymentHostedService,
|
||||
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings,
|
||||
IPluginHookService pluginHookService,
|
||||
IStringLocalizer stringLocalizer,
|
||||
InvoiceActivator invoiceActivator)
|
||||
{
|
||||
_invoiceRepository = invoiceRepository;
|
||||
@ -93,6 +94,7 @@ namespace BTCPayServer
|
||||
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
|
||||
_pluginHookService = pluginHookService;
|
||||
_invoiceActivator = invoiceActivator;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
[EnableCors(CorsPolicies.All)]
|
||||
@ -302,7 +304,7 @@ namespace BTCPayServer
|
||||
{
|
||||
var pmi = GetLNUrlPaymentMethodId(cryptoCode, store, out _);
|
||||
if (pmi is null)
|
||||
return NotFound("LNUrl or LN is disabled");
|
||||
return NotFound(StringLocalizer["LNURL or LN is disabled"]);
|
||||
var escapedItemId = Extensions.UnescapeBackSlashUriString(itemCode);
|
||||
item = items.FirstOrDefault(item1 =>
|
||||
item1.Id.Equals(itemCode, StringComparison.InvariantCultureIgnoreCase) ||
|
||||
@ -457,11 +459,11 @@ namespace BTCPayServer
|
||||
{
|
||||
var lightningAddressSettings = await _lightningAddressService.ResolveByAddress(username);
|
||||
if (lightningAddressSettings is null || username is null)
|
||||
return NotFound("Unknown username");
|
||||
return NotFound(StringLocalizer["Unknown username"]);
|
||||
var blob = lightningAddressSettings.GetBlob();
|
||||
var store = await _storeRepository.FindStore(lightningAddressSettings.StoreDataId);
|
||||
if (store is null)
|
||||
return NotFound("Unknown username");
|
||||
return NotFound(StringLocalizer["Unknown username"]);
|
||||
var result = await GetLNURLRequest(
|
||||
cryptoCode,
|
||||
store,
|
||||
@ -503,7 +505,7 @@ namespace BTCPayServer
|
||||
|
||||
var blob = store.GetStoreBlob();
|
||||
if (!blob.AnyoneCanInvoice)
|
||||
return NotFound("'Anyone can invoice' is turned off");
|
||||
return NotFound(StringLocalizer["'Anyone can invoice' is turned off"]);
|
||||
var metadata = new InvoiceMetadata();
|
||||
if (!string.IsNullOrEmpty(orderId))
|
||||
{
|
||||
@ -533,7 +535,7 @@ namespace BTCPayServer
|
||||
{
|
||||
var pmi = GetLNUrlPaymentMethodId(cryptoCode, store, out _);
|
||||
if (pmi is null)
|
||||
return NotFound("LNUrl or LN is disabled");
|
||||
return NotFound(StringLocalizer["LNURL or LN is disabled"]);
|
||||
|
||||
InvoiceEntity i;
|
||||
try
|
||||
@ -721,7 +723,7 @@ namespace BTCPayServer
|
||||
new LNURLPayRequest.LNURLPayRequestCallbackResponse.LNURLPayRequestSuccessActionUrl
|
||||
{
|
||||
Tag = "url",
|
||||
Description = "Thank you for your purchase. Here is your receipt",
|
||||
Description = StringLocalizer["Thank you for your purchase. Here is your receipt"],
|
||||
Url = _linkGenerator.GetUriByAction(
|
||||
nameof(UIInvoiceController.InvoiceReceipt),
|
||||
"UIInvoice",
|
||||
@ -833,7 +835,7 @@ namespace BTCPayServer
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = "LNURL is required for lightning addresses but has not yet been enabled.",
|
||||
Message = StringLocalizer["LNURL is required for lightning addresses but has not yet been enabled."].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
});
|
||||
return RedirectToAction(nameof(UIStoresController.GeneralSettings), "UIStores", new { storeId });
|
||||
@ -873,7 +875,7 @@ namespace BTCPayServer
|
||||
if (!string.IsNullOrEmpty(vm.Add.CurrencyCode) &&
|
||||
currencyNameTable.GetCurrencyData(vm.Add.CurrencyCode, false) is null)
|
||||
{
|
||||
vm.AddModelError(addressVm => addressVm.Add.CurrencyCode, "Currency is invalid", this);
|
||||
vm.AddModelError(addressVm => addressVm.Add.CurrencyCode, StringLocalizer["Currency is invalid"], this);
|
||||
}
|
||||
|
||||
JObject metadata = null;
|
||||
@ -885,7 +887,7 @@ namespace BTCPayServer
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
vm.AddModelError(addressVm => addressVm.Add.InvoiceMetadata, "Metadata must be a valid json object", this);
|
||||
vm.AddModelError(addressVm => addressVm.Add.InvoiceMetadata, StringLocalizer["Metadata must be a valid JSON object"], this);
|
||||
}
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
@ -909,12 +911,12 @@ namespace BTCPayServer
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = "Lightning address added successfully."
|
||||
Message = StringLocalizer["Lightning address added successfully."].Value
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
vm.AddModelError(addressVm => addressVm.Add.Username, "Username is already taken", this);
|
||||
vm.AddModelError(addressVm => addressVm.Add.Username, StringLocalizer["Username is already taken"], this);
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@ -932,13 +934,13 @@ namespace BTCPayServer
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = $"Lightning address {index} removed successfully."
|
||||
Message = StringLocalizer["Lightning address {0} removed successfully.", index].Value
|
||||
});
|
||||
return RedirectToAction("EditLightningAddress");
|
||||
}
|
||||
else
|
||||
{
|
||||
vm.AddModelError(addressVm => addressVm.Add.Username, "Username could not be removed", this);
|
||||
vm.AddModelError(addressVm => addressVm.Add.Username, StringLocalizer["Username could not be removed"], this);
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
|
@ -58,10 +58,10 @@ namespace BTCPayServer.Controllers
|
||||
return NotFound();
|
||||
}
|
||||
await _apiKeyRepository.Remove(id, _userManager.GetUserId(User));
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = "API Key removed"
|
||||
Message = StringLocalizer["API Key removed"].Value
|
||||
});
|
||||
return RedirectToAction("APIKeys");
|
||||
}
|
||||
@ -71,10 +71,10 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (!_btcPayServerEnvironment.IsSecure(HttpContext))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = "Cannot generate api keys while not on https or tor"
|
||||
Message = StringLocalizer["Cannot generate API keys while not using HTTPS or Tor"].Value
|
||||
});
|
||||
return RedirectToAction("APIKeys");
|
||||
}
|
||||
@ -91,7 +91,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = "Cannot generate API keys while not on https or using Tor"
|
||||
Message = StringLocalizer["Cannot generate API keys while not using HTTPS or Tor"].Value
|
||||
});
|
||||
return RedirectToAction("APIKeys");
|
||||
}
|
||||
@ -199,7 +199,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Html = $"API key generated! <code class='alert-link'>{key.Id}</code>"
|
||||
Html = StringLocalizer["API key generated!"].Value + $" <code class='alert-link'>{key.Id}</code>"
|
||||
});
|
||||
|
||||
return RedirectToAction("APIKeys", new { key = key.Id });
|
||||
@ -242,7 +242,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Html = $"API key generated! <code class='alert-link'>{key.Id}</code>"
|
||||
Html = StringLocalizer["API key generated!"].Value + $" <code class='alert-link'>{key.Id}</code>"
|
||||
});
|
||||
return RedirectToAction("APIKeys");
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
@ -56,7 +55,7 @@ namespace BTCPayServer.Controllers
|
||||
await _userManager.UpdateAsync(user);
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = "Updated successfully.",
|
||||
Message = StringLocalizer["Updated successfully."].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
return RedirectToAction("NotificationSettings");
|
||||
|
@ -7,25 +7,21 @@ using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Fido2;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.ManageViewModels;
|
||||
using BTCPayServer.Security.Greenfield;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Mails;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MimeKit;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanViewProfile)]
|
||||
[Route("account/{action:lowercase=Index}")]
|
||||
public partial class UIManageController : Controller
|
||||
@ -45,6 +41,7 @@ namespace BTCPayServer.Controllers
|
||||
private readonly UriResolver _uriResolver;
|
||||
private readonly IFileService _fileService;
|
||||
readonly StoreRepository _StoreRepository;
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UIManageController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
@ -61,6 +58,7 @@ namespace BTCPayServer.Controllers
|
||||
UserService userService,
|
||||
UriResolver uriResolver,
|
||||
IFileService fileService,
|
||||
IStringLocalizer stringLocalizer,
|
||||
IHtmlHelper htmlHelper
|
||||
)
|
||||
{
|
||||
@ -79,6 +77,7 @@ namespace BTCPayServer.Controllers
|
||||
_uriResolver = uriResolver;
|
||||
_fileService = fileService;
|
||||
_StoreRepository = storeRepository;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
@ -135,7 +134,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (!(await _userManager.FindByEmailAsync(model.Email) is null))
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "The email address is already in use with an other account.";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["The email address is already in use with an other account."].Value;
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
var setUserResult = await _userManager.SetUserNameAsync(user, model.Email);
|
||||
@ -207,11 +206,11 @@ namespace BTCPayServer.Controllers
|
||||
if (needUpdate is true)
|
||||
{
|
||||
needUpdate = await _userManager.UpdateAsync(user) is { Succeeded: true };
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Your profile has been updated";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Your profile has been updated"].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "Error updating profile";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Error updating profile"].Value;
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
@ -235,7 +234,7 @@ namespace BTCPayServer.Controllers
|
||||
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
var callbackUrl = _linkGenerator.EmailConfirmationLink(user.Id, code, Request.Scheme, Request.Host, Request.PathBase);
|
||||
(await _EmailSenderFactory.GetEmailSender()).SendEmailConfirmation(user.GetMailboxAddress(), callbackUrl);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Verification email sent. Please check your email.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Verification email sent. Please check your email."].Value;
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
@ -281,8 +280,8 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
await _signInManager.SignInAsync(user, isPersistent: false);
|
||||
_logger.LogInformation("User changed their password successfully.");
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Your password has been changed.";
|
||||
_logger.LogInformation("User changed their password successfully");
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Your password has been changed."].Value;
|
||||
|
||||
return RedirectToAction(nameof(ChangePassword));
|
||||
}
|
||||
@ -330,7 +329,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
await _signInManager.SignInAsync(user, isPersistent: false);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Your password has been set.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Your password has been set."].Value;
|
||||
|
||||
return RedirectToAction(nameof(SetPassword));
|
||||
}
|
||||
@ -345,7 +344,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
await _userService.DeleteUserAndAssociatedData(user);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Account successfully deleted.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Account successfully deleted."].Value;
|
||||
await _signInManager.SignOutAsync();
|
||||
return RedirectToAction(nameof(UIAccountController.Login), "UIAccount");
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using PaymentRequestData = BTCPayServer.Data.PaymentRequestData;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
@ -47,6 +48,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
private FormComponentProviders FormProviders { get; }
|
||||
public FormDataService FormDataService { get; }
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UIPaymentRequestController(
|
||||
UIInvoiceController invoiceController,
|
||||
@ -62,6 +64,7 @@ namespace BTCPayServer.Controllers
|
||||
InvoiceRepository invoiceRepository,
|
||||
FormComponentProviders formProviders,
|
||||
FormDataService formDataService,
|
||||
IStringLocalizer stringLocalizer,
|
||||
BTCPayNetworkProvider networkProvider)
|
||||
{
|
||||
_InvoiceController = invoiceController;
|
||||
@ -78,6 +81,7 @@ namespace BTCPayServer.Controllers
|
||||
FormProviders = formProviders;
|
||||
FormDataService = formDataService;
|
||||
_networkProvider = networkProvider;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
[HttpGet("/stores/{storeId}/payment-requests")]
|
||||
@ -169,7 +173,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (paymentRequest?.Archived is true && viewModel.Archived)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, "You cannot edit an archived payment request.");
|
||||
ModelState.AddModelError(string.Empty, StringLocalizer["You cannot edit an archived payment request."]);
|
||||
}
|
||||
var data = paymentRequest ?? new PaymentRequestData();
|
||||
data.StoreDataId = viewModel.StoreId;
|
||||
@ -180,7 +184,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
var prInvoices = (await _PaymentRequestService.GetPaymentRequest(payReqId, GetUserId())).Invoices;
|
||||
if (prInvoices.Any())
|
||||
ModelState.AddModelError(nameof(viewModel.Amount), "Amount and currency are not editable once payment request has invoices");
|
||||
ModelState.AddModelError(nameof(viewModel.Amount), StringLocalizer["Amount and currency are not editable once payment request has invoices"]);
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
@ -210,7 +214,9 @@ namespace BTCPayServer.Controllers
|
||||
data = await _PaymentRequestRepository.CreateOrUpdatePaymentRequest(data);
|
||||
_EventAggregator.Publish(new PaymentRequestUpdated { Data = data, PaymentRequestId = data.Id, });
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Payment request \"{viewModel.Title}\" {(isNewPaymentRequest ? "created" : "updated")} successfully";
|
||||
TempData[WellKnownTempData.SuccessMessage] = isNewPaymentRequest
|
||||
? StringLocalizer["Payment request \"{0}\" created successfully", viewModel.Title].Value
|
||||
: StringLocalizer["Payment request \"{0}\" updated successfully", viewModel.Title].Value;
|
||||
return RedirectToAction(nameof(GetPaymentRequests), new { storeId = store.Id, payReqId = data.Id });
|
||||
}
|
||||
|
||||
@ -302,7 +308,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (amount.HasValue && amount.Value <= 0)
|
||||
{
|
||||
return BadRequest("Please provide an amount greater than 0");
|
||||
return BadRequest(StringLocalizer["Please provide an amount greater than 0"]);
|
||||
}
|
||||
|
||||
var result = await _PaymentRequestService.GetPaymentRequest(payReqId, GetUserId());
|
||||
@ -318,7 +324,7 @@ namespace BTCPayServer.Controllers
|
||||
return RedirectToAction("ViewPaymentRequest", new { payReqId });
|
||||
}
|
||||
|
||||
return BadRequest("Payment Request cannot be paid as it has been archived");
|
||||
return BadRequest(StringLocalizer["Payment Request cannot be paid as it has been archived"]);
|
||||
}
|
||||
if (!result.FormSubmitted && !string.IsNullOrEmpty(result.FormId))
|
||||
{
|
||||
@ -337,7 +343,7 @@ namespace BTCPayServer.Controllers
|
||||
return RedirectToAction("ViewPaymentRequest", new { payReqId });
|
||||
}
|
||||
|
||||
return BadRequest("Payment Request has already been settled.");
|
||||
return BadRequest(StringLocalizer["Payment Request has already been settled."]);
|
||||
}
|
||||
|
||||
if (result.ExpiryDate.HasValue && DateTime.UtcNow >= result.ExpiryDate)
|
||||
@ -347,7 +353,7 @@ namespace BTCPayServer.Controllers
|
||||
return RedirectToAction("ViewPaymentRequest", new { payReqId });
|
||||
}
|
||||
|
||||
return BadRequest("Payment Request has expired");
|
||||
return BadRequest(StringLocalizer["Payment Request has expired"]);
|
||||
}
|
||||
|
||||
var currentInvoice = result.Invoices.GetReusableInvoice(amount);
|
||||
@ -391,7 +397,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (!result.AllowCustomPaymentAmounts)
|
||||
{
|
||||
return BadRequest("Not allowed to cancel this invoice");
|
||||
return BadRequest(StringLocalizer["Not allowed to cancel this invoice"]);
|
||||
}
|
||||
|
||||
var invoices = result.Invoices.Where(requestInvoice =>
|
||||
@ -399,7 +405,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (!invoices.Any())
|
||||
{
|
||||
return BadRequest("No unpaid pending invoice to cancel");
|
||||
return BadRequest(StringLocalizer["No unpaid pending invoice to cancel"]);
|
||||
}
|
||||
|
||||
foreach (var invoice in invoices)
|
||||
@ -409,11 +415,11 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (redirect)
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Payment cancelled";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Payment cancelled"].Value;
|
||||
return RedirectToAction(nameof(ViewPaymentRequest), new { payReqId });
|
||||
}
|
||||
|
||||
return Ok("Payment cancelled");
|
||||
return Ok(StringLocalizer["Payment cancelled"]);
|
||||
}
|
||||
|
||||
[HttpGet("{payReqId}/clone")]
|
||||
@ -446,8 +452,8 @@ namespace BTCPayServer.Controllers
|
||||
if(result is not null)
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = result.Value
|
||||
? "The payment request has been archived and will no longer appear in the payment request list by default again."
|
||||
: "The payment request has been unarchived and will appear in the payment request list by default.";
|
||||
? StringLocalizer["The payment request has been archived and will no longer appear in the payment request list by default again."].Value
|
||||
: StringLocalizer["The payment request has been unarchived and will appear in the payment request list by default."].Value;
|
||||
return RedirectToAction("GetPaymentRequests", new { storeId = store.Id });
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NicolasDorier.RateLimits;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
@ -21,16 +22,19 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
public UIPublicController(UIInvoiceController invoiceController,
|
||||
StoreRepository storeRepository,
|
||||
IStringLocalizer stringLocalizer,
|
||||
LinkGenerator linkGenerator)
|
||||
{
|
||||
_InvoiceController = invoiceController;
|
||||
_StoreRepository = storeRepository;
|
||||
_linkGenerator = linkGenerator;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
private readonly UIInvoiceController _InvoiceController;
|
||||
private readonly StoreRepository _StoreRepository;
|
||||
private readonly LinkGenerator _linkGenerator;
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
[HttpGet]
|
||||
[IgnoreAntiforgeryToken]
|
||||
@ -50,16 +54,16 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
var store = await _StoreRepository.FindStore(model.StoreId);
|
||||
if (store == null)
|
||||
ModelState.AddModelError("Store", "Invalid store");
|
||||
ModelState.AddModelError("Store", StringLocalizer["Invalid store"]);
|
||||
else
|
||||
{
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
if (!storeBlob.AnyoneCanInvoice)
|
||||
ModelState.AddModelError("Store", "Store has not enabled Pay Button");
|
||||
ModelState.AddModelError("Store", StringLocalizer["Store has not enabled Pay Button"]);
|
||||
}
|
||||
|
||||
if (model == null || (model.Price is decimal v ? v <= 0 : false))
|
||||
ModelState.AddModelError("Price", "Price must be greater than 0");
|
||||
ModelState.AddModelError("Price", StringLocalizer["Price must be greater than 0"]);
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return View();
|
||||
|
@ -1,14 +1,10 @@
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.NTag424;
|
||||
using Dapper;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NBitcoin.DataEncoders;
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
@ -23,7 +19,7 @@ namespace BTCPayServer.Controllers
|
||||
[HttpGet("pull-payments/{pullPaymentId}/boltcard/{command}")]
|
||||
public IActionResult SetupBoltcard(string pullPaymentId, string command)
|
||||
{
|
||||
return View(nameof(SetupBoltcard), new SetupBoltcardViewModel()
|
||||
return View(nameof(SetupBoltcard), new SetupBoltcardViewModel
|
||||
{
|
||||
ReturnUrl = Url.Action(nameof(ViewPullPayment), "UIPullPayment", new { pullPaymentId }),
|
||||
WebsocketPath = Url.Action(nameof(VaultNFCBridgeConnection), "UIPullPayment", new { pullPaymentId }),
|
||||
@ -34,7 +30,7 @@ namespace BTCPayServer.Controllers
|
||||
[HttpPost("pull-payments/{pullPaymentId}/boltcard/{command}")]
|
||||
public IActionResult SetupBoltcardPost(string pullPaymentId, string command)
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Boltcard is configured";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Boltcard is configured"].Value;
|
||||
return RedirectToAction(nameof(ViewPullPayment), new { pullPaymentId });
|
||||
}
|
||||
|
||||
@ -78,24 +74,24 @@ next:
|
||||
await vaultClient.Show(VaultMessageType.Error, "BTCPay Server Vault does not seem to be running, you can download it on <a target=\"_blank\" href=\"https://github.com/btcpayserver/BTCPayServer.Vault/releases/latest\">Github</a>.", cts.Token);
|
||||
goto next;
|
||||
}
|
||||
await vaultClient.Show(VaultMessageType.Ok, "BTCPayServer successfully connected to the vault.", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Ok, StringLocalizer["BTCPayServer successfully connected to the vault."], cts.Token);
|
||||
if (permission is false)
|
||||
{
|
||||
await vaultClient.Show(VaultMessageType.Error, "The user declined access to the vault.", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Error, StringLocalizer["The user declined access to the vault."], cts.Token);
|
||||
goto next;
|
||||
}
|
||||
await vaultClient.Show(VaultMessageType.Ok, "Access to vault granted by owner.", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Ok, StringLocalizer["Access to vault granted by owner."], cts.Token);
|
||||
|
||||
await vaultClient.Show(VaultMessageType.Processing, "Waiting for NFC to be presented...", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Processing, StringLocalizer["Waiting for NFC to be presented..."], cts.Token);
|
||||
await transport.WaitForCard(cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Ok, "NFC detected.", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Ok, StringLocalizer["NFC detected."], cts.Token);
|
||||
|
||||
var issuerKey = await _settingsRepository.GetIssuerKey(_env);
|
||||
CardOrigin cardOrigin = await GetCardOrigin(pullPaymentId, ntag, issuerKey, cts.Token);
|
||||
|
||||
if (cardOrigin is CardOrigin.OtherIssuer)
|
||||
{
|
||||
await vaultClient.Show(VaultMessageType.Error, "This card is already configured for another issuer", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Error, StringLocalizer["This card is already configured for another issuer"], cts.Token);
|
||||
goto next;
|
||||
}
|
||||
|
||||
@ -103,7 +99,7 @@ next:
|
||||
switch (command)
|
||||
{
|
||||
case "configure-boltcard":
|
||||
await vaultClient.Show(VaultMessageType.Processing, "Configuring Boltcard...", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Processing, StringLocalizer["Configuring Boltcard..."], cts.Token);
|
||||
if (cardOrigin is CardOrigin.Blank || cardOrigin is CardOrigin.ThisIssuerReset)
|
||||
{
|
||||
await ntag.AuthenticateEV2First(0, AESKey.Default, cts.Token);
|
||||
@ -119,35 +115,35 @@ next:
|
||||
await _dbContextFactory.SetBoltcardResetState(issuerKey, uid);
|
||||
throw;
|
||||
}
|
||||
await vaultClient.Show(VaultMessageType.Ok, "The card is now configured", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Ok, StringLocalizer["The card is now configured"], cts.Token);
|
||||
}
|
||||
else if (cardOrigin is CardOrigin.ThisIssuer)
|
||||
{
|
||||
await vaultClient.Show(VaultMessageType.Ok, "This card is already properly configured", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Ok, StringLocalizer["This card is already properly configured"], cts.Token);
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
case "reset-boltcard":
|
||||
await vaultClient.Show(VaultMessageType.Processing, "Resetting Boltcard...", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Processing, StringLocalizer["Resetting Boltcard..."], cts.Token);
|
||||
if (cardOrigin is CardOrigin.Blank)
|
||||
{
|
||||
await vaultClient.Show(VaultMessageType.Ok, "This card is already in a factory state", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Ok, StringLocalizer["This card is already in a factory state"], cts.Token);
|
||||
}
|
||||
else if (cardOrigin is CardOrigin.ThisIssuer thisIssuer)
|
||||
{
|
||||
var cardKey = issuerKey.CreatePullPaymentCardKey(thisIssuer.Registration.UId, thisIssuer.Registration.Version, pullPaymentId);
|
||||
await ntag.ResetCard(issuerKey, cardKey);
|
||||
await _dbContextFactory.SetBoltcardResetState(issuerKey, thisIssuer.Registration.UId);
|
||||
await vaultClient.Show(VaultMessageType.Ok, "Card reset succeed", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Ok, StringLocalizer["Card reset succeed"], cts.Token);
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
if (success)
|
||||
{
|
||||
await vaultClient.Show(VaultMessageType.Processing, "Please remove the NFC from the card reader", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Processing, StringLocalizer["Please remove the NFC from the card reader"], cts.Token);
|
||||
await transport.WaitForRemoved(cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Ok, "Thank you!", cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Ok, StringLocalizer["Thank you!"], cts.Token);
|
||||
await vaultClient.SendSimpleMessage("done", cts.Token);
|
||||
}
|
||||
}
|
||||
@ -159,7 +155,7 @@ next:
|
||||
{
|
||||
try
|
||||
{
|
||||
await vaultClient.Show(VaultMessageType.Error, "Unexpected error: " + ex.Message, ex.ToString(), cts.Token);
|
||||
await vaultClient.Show(VaultMessageType.Error, StringLocalizer["Unexpected error: {0}", ex.Message], ex.ToString(), cts.Token);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Amazon.S3.Model;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
@ -14,24 +10,17 @@ using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Controllers.Greenfield;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.ModelBinders;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.WalletViewModels;
|
||||
using BTCPayServer.NTag424;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payouts;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Dapper;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NBitcoin;
|
||||
using NBitcoin.DataEncoders;
|
||||
using NdefLibrary.Ndef;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
@ -48,6 +37,7 @@ namespace BTCPayServer.Controllers
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly BTCPayServerEnvironment _env;
|
||||
private readonly SettingsRepository _settingsRepository;
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UIPullPaymentController(ApplicationDbContextFactory dbContextFactory,
|
||||
CurrencyNameTable currencyNameTable,
|
||||
@ -59,6 +49,7 @@ namespace BTCPayServer.Controllers
|
||||
PayoutMethodHandlerDictionary payoutHandlers,
|
||||
StoreRepository storeRepository,
|
||||
BTCPayServerEnvironment env,
|
||||
IStringLocalizer stringLocalizer,
|
||||
SettingsRepository settingsRepository)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory;
|
||||
@ -72,6 +63,7 @@ namespace BTCPayServer.Controllers
|
||||
_env = env;
|
||||
_settingsRepository = settingsRepository;
|
||||
_networkProvider = networkProvider;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
@ -196,7 +188,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = "Pull payment updated successfully",
|
||||
Message = StringLocalizer["Pull payment updated successfully"].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
|
||||
@ -211,12 +203,12 @@ namespace BTCPayServer.Controllers
|
||||
var pp = await ctx.PullPayments.FindAsync(pullPaymentId);
|
||||
if (pp is null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(pullPaymentId), "This pull payment does not exists");
|
||||
ModelState.AddModelError(nameof(pullPaymentId), StringLocalizer["This pull payment does not exists"]);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(vm.Destination))
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.Destination), "Please provide a destination");
|
||||
ModelState.AddModelError(nameof(vm.Destination), StringLocalizer["Please provide a destination"]);
|
||||
return await ViewPullPayment(pullPaymentId);
|
||||
}
|
||||
|
||||
@ -232,7 +224,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
var handler = _payoutHandlers.TryGet(pmId);
|
||||
(IClaimDestination dst, string err) = handler == null
|
||||
? (null, "No payment handler found for this payment method")
|
||||
? (null, StringLocalizer["No payment handler found for this payment method"])
|
||||
: await handler.ParseAndValidateClaimDestination(vm.Destination, ppBlob, cancellationToken);
|
||||
error = err;
|
||||
if (dst is not null && err is null)
|
||||
@ -256,7 +248,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (destination is null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.Destination), error ?? "Invalid destination or payment method");
|
||||
ModelState.AddModelError(nameof(vm.Destination), error ?? StringLocalizer["Invalid destination or payment method"]);
|
||||
return await ViewPullPayment(pullPaymentId);
|
||||
}
|
||||
var amtError = ClaimRequest.IsPayoutAmountOk(destination, vm.ClaimedAmount == 0 ? null : vm.ClaimedAmount, payoutHandler.Currency, pp.Currency);
|
||||
|
@ -28,10 +28,10 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = "Remote plugins lookup failed. Try again later."
|
||||
Message = StringLocalizer["Remote plugins lookup failed. Try again later."].Value
|
||||
});
|
||||
availablePlugins = Array.Empty<PluginService.AvailablePlugin>();
|
||||
}
|
||||
@ -75,9 +75,9 @@ namespace BTCPayServer.Controllers
|
||||
[FromServices] PluginService pluginService, string plugin)
|
||||
{
|
||||
pluginService.UninstallPlugin(plugin);
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = "Plugin scheduled to be uninstalled.",
|
||||
Message = StringLocalizer["Plugin scheduled to be uninstalled."].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
|
||||
@ -89,9 +89,9 @@ namespace BTCPayServer.Controllers
|
||||
[FromServices] PluginService pluginService, string plugin)
|
||||
{
|
||||
pluginService.CancelCommands(plugin);
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = "Plugin action cancelled.",
|
||||
Message = StringLocalizer["Plugin action cancelled."].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
|
||||
@ -113,17 +113,17 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
pluginService.InstallPlugin(plugin);
|
||||
}
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = "Plugin scheduled to be installed.",
|
||||
Message = StringLocalizer["Plugin scheduled to be installed."].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = "The plugin could not be downloaded. Try again later.",
|
||||
Message = StringLocalizer["The plugin could not be downloaded. Try again later."].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
});
|
||||
}
|
||||
@ -142,9 +142,9 @@ namespace BTCPayServer.Controllers
|
||||
StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = "Files uploaded, restart server to load plugins",
|
||||
Message = StringLocalizer["Files uploaded, restart server to load plugins"].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
return RedirectToAction("ListPlugins");
|
||||
|
@ -67,12 +67,12 @@ namespace BTCPayServer.Controllers
|
||||
string successMessage = null;
|
||||
if (role == "create")
|
||||
{
|
||||
successMessage = "Role created";
|
||||
successMessage = StringLocalizer["Role created"];
|
||||
role = viewModel.Role;
|
||||
}
|
||||
else
|
||||
{
|
||||
successMessage = "Role updated";
|
||||
successMessage = StringLocalizer["Role updated"];
|
||||
var storeRole = await _StoreRepository.GetStoreRole(new StoreRoleId(role));
|
||||
if (storeRole == null)
|
||||
return NotFound();
|
||||
@ -86,15 +86,15 @@ namespace BTCPayServer.Controllers
|
||||
var r = await _StoreRepository.AddOrUpdateStoreRole(new StoreRoleId(role), viewModel.Policies);
|
||||
if (r is null)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = "Role could not be updated"
|
||||
Message = StringLocalizer["Role could not be updated"].Value
|
||||
});
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = successMessage
|
||||
@ -114,11 +114,11 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
return View("Confirm",
|
||||
roleData.IsUsed is true
|
||||
? new ConfirmModel("Delete role",
|
||||
? new ConfirmModel(StringLocalizer["Delete role"],
|
||||
$"Unable to proceed: The role <strong>{Html.Encode(roleData.Role)}</strong> is currently assigned to one or more users, it cannot be removed.")
|
||||
: new ConfirmModel("Delete role",
|
||||
: new ConfirmModel(StringLocalizer["Delete role"],
|
||||
$"The role <strong>{Html.Encode(roleData.Role)}</strong> will be permanently deleted. Are you sure?",
|
||||
"Delete"));
|
||||
StringLocalizer["Delete"]));
|
||||
}
|
||||
|
||||
[HttpPost("server/roles/{role}/delete")]
|
||||
@ -137,7 +137,7 @@ namespace BTCPayServer.Controllers
|
||||
if (errorMessage is null)
|
||||
{
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Role deleted";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Role deleted"].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -153,12 +153,12 @@ namespace BTCPayServer.Controllers
|
||||
var resolved = await _StoreRepository.ResolveStoreRoleId(null, role);
|
||||
if (resolved is null)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "Role could not be set as default";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Role could not be set as default"].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
await _StoreRepository.SetDefaultRole(role);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Role set default";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Role set default"].Value;
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(ListRoles));
|
||||
|
@ -52,9 +52,9 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (!allFilesExist)
|
||||
{
|
||||
this.TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = "Some of the files were not found",
|
||||
Message = StringLocalizer["Some of the files were not found"].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||
});
|
||||
}
|
||||
@ -75,12 +75,12 @@ namespace BTCPayServer.Controllers
|
||||
return RedirectToAction(nameof(Files), new
|
||||
{
|
||||
fileIds = Array.Empty<string>(),
|
||||
statusMessage = "File removed"
|
||||
statusMessage = StringLocalizer["File removed"].Value
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = e.Message
|
||||
@ -108,7 +108,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (viewModel.TimeAmount <= 0)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.TimeAmount), "Time must be at least 1");
|
||||
ModelState.AddModelError(nameof(viewModel.TimeAmount), StringLocalizer["Time must be at least 1"]);
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
@ -192,21 +192,21 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (invalidFileNameCount == 0)
|
||||
{
|
||||
statusMessage = "Files Added Successfully";
|
||||
statusMessage = StringLocalizer["Files added successfully"];
|
||||
statusMessageSeverity = StatusMessageModel.StatusSeverity.Success;
|
||||
}
|
||||
else if (invalidFileNameCount > 0 && invalidFileNameCount < files.Count)
|
||||
{
|
||||
statusMessage = $"{files.Count - invalidFileNameCount} files were added. {invalidFileNameCount} files had invalid names";
|
||||
statusMessage = StringLocalizer["{0} files were added. {1} files had invalid names", files.Count - invalidFileNameCount, invalidFileNameCount].Value;
|
||||
statusMessageSeverity = StatusMessageModel.StatusSeverity.Error;
|
||||
}
|
||||
else
|
||||
{
|
||||
statusMessage = $"Files could not be added due to invalid names";
|
||||
statusMessage = StringLocalizer["Files could not be added due to invalid names"].Value;
|
||||
statusMessageSeverity = StatusMessageModel.StatusSeverity.Error;
|
||||
}
|
||||
|
||||
this.TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = statusMessage,
|
||||
Severity = statusMessageSeverity
|
||||
@ -266,10 +266,10 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (!Enum.TryParse(typeof(StorageProvider), provider, out var storageProvider))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = $"{provider} provider is not supported"
|
||||
Message = StringLocalizer["{0} provider is not supported", provider].Value
|
||||
});
|
||||
return RedirectToAction(nameof(Storage));
|
||||
}
|
||||
@ -282,10 +282,10 @@ namespace BTCPayServer.Controllers
|
||||
switch (storageProviderService)
|
||||
{
|
||||
case null:
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = $"{storageProvider} is not supported"
|
||||
Message = StringLocalizer["{0} provider is not supported", storageProvider].Value
|
||||
});
|
||||
return RedirectToAction(nameof(Storage));
|
||||
case AzureBlobStorageFileProviderService fileProviderService:
|
||||
@ -350,10 +350,10 @@ namespace BTCPayServer.Controllers
|
||||
data.Provider = storageProvider;
|
||||
data.Configuration = JObject.FromObject(viewModel);
|
||||
await _SettingsRepository.UpdateSetting(data);
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = "Storage settings updated successfully"
|
||||
Message = StringLocalizer["Storage settings updated successfully"].Value
|
||||
});
|
||||
return View(viewModel);
|
||||
}
|
||||
|
@ -38,13 +38,14 @@ namespace BTCPayServer.Controllers
|
||||
[HttpGet("server/dictionaries/create")]
|
||||
public async Task<IActionResult> CreateDictionary(string fallback = null)
|
||||
{
|
||||
var dictionaries = await this._localizer.GetDictionaries();
|
||||
return View(new CreateDictionaryViewModel()
|
||||
var dictionaries = await _localizer.GetDictionaries();
|
||||
return View(new CreateDictionaryViewModel
|
||||
{
|
||||
Name = fallback is not null ? $"Clone of {fallback}" : "",
|
||||
Fallback = fallback ?? Translations.DefaultLanguage,
|
||||
}.SetDictionaries(dictionaries));
|
||||
}
|
||||
|
||||
[HttpPost("server/dictionaries/create")]
|
||||
public async Task<IActionResult> CreateDictionary(CreateDictionaryViewModel viewModel)
|
||||
{
|
||||
@ -52,23 +53,23 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
try
|
||||
{
|
||||
await this._localizer.CreateDictionary(viewModel.Name, viewModel.Fallback, "Custom");
|
||||
await _localizer.CreateDictionary(viewModel.Name, viewModel.Fallback, "Custom");
|
||||
}
|
||||
catch (DbException)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.Name), $"'{viewModel.Name}' already exists");
|
||||
ModelState.AddModelError(nameof(viewModel.Name), StringLocalizer["'{0}' already exists", viewModel.Name]);
|
||||
}
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
return View(viewModel.SetDictionaries(await this._localizer.GetDictionaries()));
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Dictionary created";
|
||||
return View(viewModel.SetDictionaries(await _localizer.GetDictionaries()));
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Dictionary created"].Value;
|
||||
return RedirectToAction(nameof(EditDictionary), new { dictionary = viewModel.Name });
|
||||
}
|
||||
|
||||
[HttpGet("server/dictionaries/{dictionary}")]
|
||||
public async Task<IActionResult> EditDictionary(string dictionary)
|
||||
{
|
||||
if ((await this._localizer.GetDictionary(dictionary)) is null)
|
||||
if ((await _localizer.GetDictionary(dictionary)) is null)
|
||||
return NotFound();
|
||||
var translations = await _localizer.GetTranslations(dictionary);
|
||||
return View(new EditDictionaryViewModel().SetTranslations(translations.Translations));
|
||||
@ -77,7 +78,7 @@ namespace BTCPayServer.Controllers
|
||||
[HttpPost("server/dictionaries/{dictionary}")]
|
||||
public async Task<IActionResult> EditDictionary(string dictionary, EditDictionaryViewModel viewModel)
|
||||
{
|
||||
var d = await this._localizer.GetDictionary(dictionary);
|
||||
var d = await _localizer.GetDictionary(dictionary);
|
||||
if (d is null)
|
||||
return NotFound();
|
||||
if (Environment.CheatMode && viewModel.Command == "Fake")
|
||||
@ -90,32 +91,32 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
viewModel.Translations = Translations.CreateFromJson(jobj.ToString()).ToJsonFormat();
|
||||
}
|
||||
|
||||
|
||||
if (!Translations.TryCreateFromJson(viewModel.Translations, out var translations))
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.Translations), "Syntax error");
|
||||
ModelState.AddModelError(nameof(viewModel.Translations), StringLocalizer["Syntax error"]);
|
||||
return View(viewModel);
|
||||
}
|
||||
await _localizer.Save(d, translations);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Dictionary updated";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Dictionary updated"].Value;
|
||||
return RedirectToAction(nameof(ListDictionaries));
|
||||
}
|
||||
|
||||
[HttpGet("server/dictionaries/{dictionary}/select")]
|
||||
public async Task<IActionResult> SelectDictionary(string dictionary)
|
||||
{
|
||||
var settings = await this._SettingsRepository.GetSettingAsync<PoliciesSettings>() ?? new();
|
||||
var settings = await _SettingsRepository.GetSettingAsync<PoliciesSettings>() ?? new();
|
||||
settings.LangDictionary = dictionary;
|
||||
await _SettingsRepository.UpdateSetting(settings);
|
||||
await _localizer.Load();
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Default dictionary changed to {dictionary}";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Default dictionary changed to {0}", dictionary].Value;
|
||||
return RedirectToAction(nameof(ListDictionaries));
|
||||
}
|
||||
|
||||
[HttpPost("server/dictionaries/{dictionary}/delete")]
|
||||
public async Task<IActionResult> DeleteDictionary(string dictionary)
|
||||
{
|
||||
await _localizer.DeleteDictionary(dictionary);
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Dictionary {dictionary} deleted";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Dictionary {0} deleted", dictionary].Value;
|
||||
return RedirectToAction(nameof(ListDictionaries));
|
||||
}
|
||||
}
|
||||
|
@ -139,18 +139,18 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (viewModel.ImageFile.Length > 1_000_000)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.ImageFile), "The uploaded image file should be less than 1MB");
|
||||
ModelState.AddModelError(nameof(viewModel.ImageFile), StringLocalizer["The uploaded image file should be less than {0}", "1MB"]);
|
||||
}
|
||||
else if (!viewModel.ImageFile.ContentType.StartsWith("image/", StringComparison.InvariantCulture))
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.ImageFile), "The uploaded file needs to be an image");
|
||||
ModelState.AddModelError(nameof(viewModel.ImageFile), StringLocalizer["The uploaded file needs to be an image"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var formFile = await viewModel.ImageFile.Bufferize();
|
||||
if (!FileTypeDetector.IsPicture(formFile.Buffer, formFile.FileName))
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.ImageFile), "The uploaded file needs to be an image");
|
||||
ModelState.AddModelError(nameof(viewModel.ImageFile), StringLocalizer["The uploaded file needs to be an image"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -165,7 +165,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.ImageFile), $"Could not save image: {e.Message}");
|
||||
ModelState.AddModelError(nameof(viewModel.ImageFile), StringLocalizer["Could not save image: {0}", e.Message]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -181,7 +181,7 @@ namespace BTCPayServer.Controllers
|
||||
var wasAdmin = Roles.HasServerAdmin(roles);
|
||||
if (!viewModel.IsAdmin && admins.Count == 1 && wasAdmin)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "This is the only Admin, so their role can't be removed until another Admin is added.";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["This is the only admin, so their role can't be removed until another Admin is added."].Value;
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
@ -199,11 +199,11 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (propertiesChanged is not false && adminStatusChanged is not false && approvalStatusChanged is not false)
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = "User successfully updated";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["User successfully updated"].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "Error updating user";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Error updating user"].Value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,7 +231,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = result.Succeeded ? StatusMessageModel.StatusSeverity.Success : StatusMessageModel.StatusSeverity.Error,
|
||||
Message = result.Succeeded ? "Password successfully set" : "An error occurred while resetting user password"
|
||||
Message = result.Succeeded ? StringLocalizer["Password successfully set"].Value : StringLocalizer["An error occurred while resetting user password"].Value
|
||||
});
|
||||
return RedirectToAction(nameof(ListUsers));
|
||||
}
|
||||
@ -326,16 +326,16 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (await _userService.IsUserTheOnlyOneAdmin(user))
|
||||
{
|
||||
return View("Confirm", new ConfirmModel("Delete admin",
|
||||
return View("Confirm", new ConfirmModel(StringLocalizer["Delete admin"],
|
||||
$"Unable to proceed: As the user <strong>{Html.Encode(user.Email)}</strong> is the last enabled admin, it cannot be removed."));
|
||||
}
|
||||
|
||||
return View("Confirm", new ConfirmModel("Delete admin",
|
||||
return View("Confirm", new ConfirmModel(StringLocalizer["Delete admin"],
|
||||
$"The admin <strong>{Html.Encode(user.Email)}</strong> will be permanently deleted. This action will also delete all accounts, users and data associated with the server account. Are you sure?",
|
||||
"Delete"));
|
||||
}
|
||||
|
||||
return View("Confirm", new ConfirmModel("Delete user", $"The user <strong>{Html.Encode(user.Email)}</strong> will be permanently deleted. Are you sure?", "Delete"));
|
||||
return View("Confirm", new ConfirmModel(StringLocalizer["Delete user"], $"The user <strong>{Html.Encode(user.Email)}</strong> will be permanently deleted. Are you sure?", "Delete"));
|
||||
}
|
||||
|
||||
[HttpPost("server/users/{userId}/delete")]
|
||||
@ -347,7 +347,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
await _userService.DeleteUserAndAssociatedData(user);
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = "User deleted";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["User deleted"].Value;
|
||||
return RedirectToAction(nameof(ListUsers));
|
||||
}
|
||||
|
||||
@ -360,7 +360,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (!enable && await _userService.IsUserTheOnlyOneAdmin(user))
|
||||
{
|
||||
return View("Confirm", new ConfirmModel("Disable admin",
|
||||
return View("Confirm", new ConfirmModel(StringLocalizer["Disable admin"],
|
||||
$"Unable to proceed: As the user <strong>{Html.Encode(user.Email)}</strong> is the last enabled admin, it cannot be disabled."));
|
||||
}
|
||||
return View("Confirm", new ConfirmModel($"{(enable ? "Enable" : "Disable")} user", $"The user <strong>{Html.Encode(user.Email)}</strong> will be {(enable ? "enabled" : "disabled")}. Are you sure?", (enable ? "Enable" : "Disable")));
|
||||
@ -374,12 +374,14 @@ namespace BTCPayServer.Controllers
|
||||
return NotFound();
|
||||
if (!enable && await _userService.IsUserTheOnlyOneAdmin(user))
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"User was the last enabled admin and could not be disabled.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["User was the last enabled admin and could not be disabled."].Value;
|
||||
return RedirectToAction(nameof(ListUsers));
|
||||
}
|
||||
await _userService.ToggleUser(userId, enable ? null : DateTimeOffset.MaxValue);
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"User {(enable ? "enabled" : "disabled")}";
|
||||
TempData[WellKnownTempData.SuccessMessage] = enable
|
||||
? StringLocalizer["User enabled"].Value
|
||||
: StringLocalizer["User disabled"].Value;
|
||||
return RedirectToAction(nameof(ListUsers));
|
||||
}
|
||||
|
||||
@ -402,7 +404,9 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
await _userService.SetUserApproval(userId, approved, Request.GetAbsoluteRootUri());
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"User {(approved ? "approved" : "unapproved")}";
|
||||
TempData[WellKnownTempData.SuccessMessage] = approved
|
||||
? StringLocalizer["User approved"].Value
|
||||
: StringLocalizer["User unapproved"].Value;
|
||||
return RedirectToAction(nameof(ListUsers));
|
||||
}
|
||||
|
||||
@ -413,7 +417,7 @@ namespace BTCPayServer.Controllers
|
||||
if (user == null)
|
||||
return NotFound();
|
||||
|
||||
return View("Confirm", new ConfirmModel("Send verification email", $"This will send a verification email to <strong>{Html.Encode(user.Email)}</strong>.", "Send"));
|
||||
return View("Confirm", new ConfirmModel(StringLocalizer["Send verification email"], $"This will send a verification email to <strong>{Html.Encode(user.Email)}</strong>.", "Send"));
|
||||
}
|
||||
|
||||
[HttpPost("server/users/{userId}/verification-email")]
|
||||
@ -430,7 +434,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
(await _emailSenderFactory.GetEmailSender()).SendEmailConfirmation(user.GetMailboxAddress(), callbackUrl);
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Verification email sent";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Verification email sent"].Value;
|
||||
return RedirectToAction(nameof(ListUsers));
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
@ -69,6 +70,7 @@ namespace BTCPayServer.Controllers
|
||||
private readonly EmailSenderFactory _emailSenderFactory;
|
||||
private readonly TransactionLinkProviders _transactionLinkProviders;
|
||||
private readonly LocalizerService _localizer;
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UIServerController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
@ -96,6 +98,7 @@ namespace BTCPayServer.Controllers
|
||||
IHtmlHelper html,
|
||||
TransactionLinkProviders transactionLinkProviders,
|
||||
LocalizerService localizer,
|
||||
IStringLocalizer stringLocalizer,
|
||||
BTCPayServerEnvironment environment
|
||||
)
|
||||
{
|
||||
@ -125,6 +128,7 @@ namespace BTCPayServer.Controllers
|
||||
_transactionLinkProviders = transactionLinkProviders;
|
||||
_localizer = localizer;
|
||||
Environment = environment;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
[HttpGet("server/stores")]
|
||||
@ -157,7 +161,7 @@ namespace BTCPayServer.Controllers
|
||||
};
|
||||
|
||||
if (!vm.CanUseSSH)
|
||||
TempData[WellKnownTempData.ErrorMessage] = "Maintenance feature requires access to SSH properly configured in BTCPay Server configuration.";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Maintenance feature requires access to SSH properly configured in BTCPay Server configuration."].Value;
|
||||
if (IPAddress.TryParse(vm.DNSDomain, out var unused))
|
||||
vm.DNSDomain = null;
|
||||
|
||||
@ -170,7 +174,7 @@ namespace BTCPayServer.Controllers
|
||||
vm.CanUseSSH = _sshState.CanUseSSH;
|
||||
if (command != "soft-restart" && !vm.CanUseSSH)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "Maintenance feature requires access to SSH properly configured in BTCPay Server configuration.";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Maintenance feature requires access to SSH properly configured in BTCPay Server configuration."].Value;
|
||||
return View(vm);
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
@ -229,21 +233,21 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
builder.Path = null;
|
||||
builder.Query = null;
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Domain name changing... the server will restart, please use \"{builder.Uri.AbsoluteUri}\" (this page won't reload automatically)";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Domain name changing... the server will restart, please use \"{0}\" (this page won't reload automatically)", builder.Uri.AbsoluteUri].Value;
|
||||
}
|
||||
else if (command == "update")
|
||||
{
|
||||
var error = await RunSSH(vm, $"btcpay-update.sh");
|
||||
if (error != null)
|
||||
return error;
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"The server might restart soon if an update is available... (this page won't reload automatically)";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["The server might restart soon if an update is available... (this page won't reload automatically)"].Value;
|
||||
}
|
||||
else if (command == "clean")
|
||||
{
|
||||
var error = await RunSSH(vm, $"btcpay-clean.sh");
|
||||
if (error != null)
|
||||
return error;
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"The old docker images will be cleaned soon...";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["The old docker images will be cleaned soon..."].Value;
|
||||
}
|
||||
else if (command == "restart")
|
||||
{
|
||||
@ -251,11 +255,11 @@ namespace BTCPayServer.Controllers
|
||||
if (error != null)
|
||||
return error;
|
||||
Logs.PayServer.LogInformation("A hard restart has been requested");
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"BTCPay will restart momentarily.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["BTCPay will restart momentarily."].Value;
|
||||
}
|
||||
else if (command == "soft-restart")
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"BTCPay will restart momentarily.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["BTCPay will restart momentarily."].Value;
|
||||
Logs.PayServer.LogInformation("A soft restart has been requested");
|
||||
_ = Task.Delay(3000).ContinueWith((t) => ApplicationLifetime.StopApplication());
|
||||
}
|
||||
@ -401,7 +405,7 @@ namespace BTCPayServer.Controllers
|
||||
_ = _transactionLinkProviders.RefreshTransactionLinkTemplates();
|
||||
if (_policiesSettings.LangDictionary != settings.LangDictionary)
|
||||
await _localizer.Load();
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Policies updated successfully";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Policies updated successfully"].Value;
|
||||
return RedirectToAction(nameof(Policies));
|
||||
}
|
||||
|
||||
@ -525,7 +529,7 @@ namespace BTCPayServer.Controllers
|
||||
return NotFound();
|
||||
if (!string.IsNullOrEmpty(cryptoCode) && !_dashBoard.IsFullySynched(cryptoCode, out _) && service.Type != ExternalServiceTypes.RPC)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"{cryptoCode} is not fully synched";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["{0} is not fully synched", cryptoCode].Value;
|
||||
return RedirectToAction(nameof(Services));
|
||||
}
|
||||
try
|
||||
@ -575,7 +579,7 @@ namespace BTCPayServer.Controllers
|
||||
case ExternalServiceTypes.Torq:
|
||||
if (connectionString.AccessKey == null)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"The access key of the service is not set";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["The access key of the service is not set"].Value;
|
||||
return RedirectToAction(nameof(Services));
|
||||
}
|
||||
LightningWalletServices vm = new LightningWalletServices();
|
||||
@ -613,7 +617,7 @@ namespace BTCPayServer.Controllers
|
||||
[HttpGet("server/services/{serviceName}/{cryptoCode}/removelndseed")]
|
||||
public IActionResult RemoveLndSeed(string serviceName, string cryptoCode)
|
||||
{
|
||||
return View("Confirm", new ConfirmModel("Delete LND seed", "This action will permanently delete your LND seed and password. You will not be able to recover them if you don't have a backup. Are you sure?", "Delete"));
|
||||
return View("Confirm", new ConfirmModel(StringLocalizer["Delete LND seed"], StringLocalizer["This action will permanently delete your LND seed and password. You will not be able to recover them if you don't have a backup. Are you sure?"], StringLocalizer["Delete"]));
|
||||
}
|
||||
|
||||
[HttpPost("server/services/{serviceName}/{cryptoCode}/removelndseed")]
|
||||
@ -626,24 +630,24 @@ namespace BTCPayServer.Controllers
|
||||
var model = LndSeedBackupViewModel.Parse(service.ConnectionString.CookieFilePath);
|
||||
if (!model.IsWalletUnlockPresent)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"File with wallet password and seed info not present";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["File with wallet password and seed info not present"].Value;
|
||||
return RedirectToAction(nameof(Services));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(model.Seed))
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"Seed information was already removed";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Seed information was already removed"].Value;
|
||||
return RedirectToAction(nameof(Services));
|
||||
}
|
||||
|
||||
if (await model.RemoveSeedAndWrite(service.ConnectionString.CookieFilePath))
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Seed successfully removed";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Seed successfully removed"].Value;
|
||||
return RedirectToAction(nameof(Service), new { serviceName, cryptoCode });
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"Seed removal failed";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Seed removal failed"].Value;
|
||||
return RedirectToAction(nameof(Services));
|
||||
}
|
||||
}
|
||||
@ -725,7 +729,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (!_dashBoard.IsFullySynched(cryptoCode, out _))
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"{cryptoCode} is not fully synched";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["{0} is not fully synched", cryptoCode].Value;
|
||||
return RedirectToAction(nameof(Services));
|
||||
}
|
||||
var service = GetService(serviceName, cryptoCode);
|
||||
@ -820,7 +824,7 @@ namespace BTCPayServer.Controllers
|
||||
string errorMessage = await viewModel.Settings.SendUpdateRequest(HttpClientFactory.CreateClient());
|
||||
if (errorMessage == null)
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"The Dynamic DNS has been successfully queried, your configuration is saved";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["The Dynamic DNS has been successfully queried, your configuration is saved"].Value;
|
||||
viewModel.Settings.LastUpdated = DateTimeOffset.UtcNow;
|
||||
settings.Services.Add(viewModel.Settings);
|
||||
await _SettingsRepository.UpdateSetting(settings);
|
||||
@ -856,7 +860,7 @@ namespace BTCPayServer.Controllers
|
||||
viewModel.Settings.Hostname = viewModel.Settings.Hostname.Trim().ToLowerInvariant();
|
||||
if (!viewModel.Settings.Enabled)
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"The Dynamic DNS service has been disabled";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["The Dynamic DNS service has been disabled"].Value;
|
||||
viewModel.Settings.LastUpdated = null;
|
||||
}
|
||||
else
|
||||
@ -864,7 +868,7 @@ namespace BTCPayServer.Controllers
|
||||
string errorMessage = await viewModel.Settings.SendUpdateRequest(HttpClientFactory.CreateClient());
|
||||
if (errorMessage == null)
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"The Dynamic DNS has been successfully queried, your configuration is saved";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["The Dynamic DNS has been successfully queried, your configuration is saved"].Value;
|
||||
viewModel.Settings.LastUpdated = DateTimeOffset.UtcNow;
|
||||
}
|
||||
else
|
||||
@ -900,7 +904,7 @@ namespace BTCPayServer.Controllers
|
||||
return NotFound();
|
||||
settings.Services.RemoveAt(i);
|
||||
await _SettingsRepository.UpdateSetting(settings);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Dynamic DNS service successfully removed";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Dynamic DNS service successfully removed"].Value;
|
||||
RouteData.Values.Remove(nameof(hostname));
|
||||
return RedirectToAction(nameof(DynamicDnsServices));
|
||||
}
|
||||
@ -974,7 +978,7 @@ namespace BTCPayServer.Controllers
|
||||
try
|
||||
{
|
||||
await System.IO.File.WriteAllTextAsync(_Options.SSHSettings.AuthorizedKeysFile, newContent);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "authorized_keys has been updated";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["authorized_keys has been updated"].Value;
|
||||
updated = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -1003,7 +1007,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (exception is null)
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = "authorized_keys has been updated";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["authorized_keys has been updated"].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1032,7 +1036,7 @@ namespace BTCPayServer.Controllers
|
||||
var policies = await _SettingsRepository.GetSettingAsync<PoliciesSettings>() ?? new PoliciesSettings();
|
||||
policies.DisableSSHService = true;
|
||||
await _SettingsRepository.UpdateSetting(policies);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Changes to the SSH settings are now permanently disabled in the BTCPay Server user interface";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Changes to the SSH settings are now permanently disabled in the BTCPay Server user interface"].Value;
|
||||
return RedirectToAction(nameof(Services));
|
||||
}
|
||||
|
||||
@ -1186,7 +1190,7 @@ namespace BTCPayServer.Controllers
|
||||
if (settingsChanged)
|
||||
{
|
||||
await _SettingsRepository.UpdateSetting(theme);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Settings updated successfully";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Settings updated successfully"].Value;
|
||||
return RedirectToAction(nameof(Branding));
|
||||
}
|
||||
|
||||
@ -1229,7 +1233,7 @@ namespace BTCPayServer.Controllers
|
||||
await client.SendAsync(message);
|
||||
await client.DisconnectAsync(true);
|
||||
}
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Email sent to {model.TestEmail}. Please verify you received it.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Email sent to {0}. Please verify you received it.", model.TestEmail].Value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -1249,14 +1253,14 @@ namespace BTCPayServer.Controllers
|
||||
var settings = await _SettingsRepository.GetSettingAsync<EmailSettings>() ?? new EmailSettings();
|
||||
settings.Password = null;
|
||||
await _SettingsRepository.UpdateSetting(settings);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Email server password reset";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Email server password reset"].Value;
|
||||
return RedirectToAction(nameof(Emails));
|
||||
}
|
||||
|
||||
// save
|
||||
if (model.Settings.From is not null && !MailboxAddressValidator.IsMailboxAddress(model.Settings.From))
|
||||
{
|
||||
ModelState.AddModelError("Settings.From", "Invalid email");
|
||||
ModelState.AddModelError("Settings.From", StringLocalizer["Invalid email"]);
|
||||
return View(model);
|
||||
}
|
||||
var oldSettings = await _SettingsRepository.GetSettingAsync<EmailSettings>() ?? new EmailSettings();
|
||||
@ -1266,7 +1270,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
await _SettingsRepository.UpdateSetting(model.Settings);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Email settings saved";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Email settings saved"].Value;
|
||||
return RedirectToAction(nameof(Emails));
|
||||
}
|
||||
|
||||
@ -1282,16 +1286,14 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (string.IsNullOrEmpty(_Options.LogFile))
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "File Logging Option not specified. " +
|
||||
"You need to set debuglog and optionally " +
|
||||
"debugloglevel in the configuration or through runtime arguments";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["File Logging Option not specified. You need to set debuglog and optionally debugloglevel in the configuration or through runtime arguments"].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var di = Directory.GetParent(_Options.LogFile);
|
||||
if (di is null)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "Could not load log files";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Could not load log files"].Value;
|
||||
return View("Logs", vm);
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = StringLocalizer["You must enable at least one payment method before creating a pull payment."],
|
||||
Message = StringLocalizer["You must enable at least one payment method before creating a pull payment."].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
});
|
||||
return RedirectToAction(nameof(UIStoresController.Index), "UIStores", new { storeId });
|
||||
@ -162,7 +162,7 @@ namespace BTCPayServer.Controllers
|
||||
});
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = StringLocalizer["Pull payment request created"],
|
||||
Message = StringLocalizer["Pull payment request created"].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
return RedirectToAction(nameof(PullPayments), new { storeId });
|
||||
@ -204,7 +204,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = StringLocalizer["You must enable at least one payment method before creating a pull payment."],
|
||||
Message = StringLocalizer["You must enable at least one payment method before creating a pull payment."].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
});
|
||||
return RedirectToAction(nameof(UIStoresController.Index), "UIStores", new { storeId });
|
||||
@ -275,9 +275,9 @@ namespace BTCPayServer.Controllers
|
||||
string pullPaymentId)
|
||||
{
|
||||
await _pullPaymentService.Cancel(new PullPaymentHostedService.CancelRequest(pullPaymentId));
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = "Pull payment archived",
|
||||
Message = StringLocalizer["Pull payment archived"].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
return RedirectToAction(nameof(PullPayments), new { storeId });
|
||||
@ -302,9 +302,9 @@ namespace BTCPayServer.Controllers
|
||||
var payoutIds = vm.GetSelectedPayouts(commandState);
|
||||
if (payoutIds.Length == 0)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = StringLocalizer["No payout selected"],
|
||||
Message = StringLocalizer["No payout selected"].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
});
|
||||
return RedirectToAction(nameof(Payouts),
|
||||
@ -345,9 +345,9 @@ namespace BTCPayServer.Controllers
|
||||
var rateResult = await _pullPaymentService.GetRate(payout, null, cancellationToken);
|
||||
if (rateResult.BidAsk == null)
|
||||
{
|
||||
this.TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
{
|
||||
Message = StringLocalizer["Rate unavailable: {0}", rateResult.EvaluatedRule],
|
||||
Message = StringLocalizer["Rate unavailable: {0}", rateResult.EvaluatedRule].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
});
|
||||
failed = true;
|
||||
@ -355,7 +355,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
var approveResult = await _pullPaymentService.Approve(
|
||||
new HostedServices.PullPaymentHostedService.PayoutApproval()
|
||||
new PullPaymentHostedService.PayoutApproval
|
||||
{
|
||||
PayoutId = payout.Id,
|
||||
Revision = payout.GetBlob(_jsonSerializerSettings).Revision,
|
||||
@ -363,7 +363,7 @@ namespace BTCPayServer.Controllers
|
||||
});
|
||||
if (approveResult.Result != PullPaymentHostedService.PayoutApproval.Result.Ok)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = PullPaymentHostedService.PayoutApproval.GetErrorMessage(approveResult.Result),
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
@ -383,9 +383,9 @@ namespace BTCPayServer.Controllers
|
||||
goto case "pay";
|
||||
}
|
||||
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = StringLocalizer["Payouts approved"],
|
||||
Message = StringLocalizer["Payouts approved"].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
break;
|
||||
@ -395,9 +395,9 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (handler is { })
|
||||
return await handler.InitiatePayment(payoutIds);
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = StringLocalizer["Paying via this payment method is not supported"],
|
||||
Message = StringLocalizer["Paying via this payment method is not supported"].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
});
|
||||
break;
|
||||
@ -416,10 +416,10 @@ namespace BTCPayServer.Controllers
|
||||
continue;
|
||||
|
||||
var result =
|
||||
await _pullPaymentService.MarkPaid(new MarkPayoutRequest() { PayoutId = payout.Id });
|
||||
await _pullPaymentService.MarkPaid(new MarkPayoutRequest { PayoutId = payout.Id });
|
||||
if (result != MarkPayoutRequest.PayoutPaidResult.Ok)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = MarkPayoutRequest.GetErrorMessage(result),
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
@ -434,9 +434,9 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = StringLocalizer["Payouts marked as paid"],
|
||||
Message = StringLocalizer["Payouts marked as paid"].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
break;
|
||||
@ -445,9 +445,9 @@ namespace BTCPayServer.Controllers
|
||||
case "cancel":
|
||||
await _pullPaymentService.Cancel(
|
||||
new PullPaymentHostedService.CancelRequest(payoutIds, new[] { storeId }));
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = StringLocalizer["Payouts archived"],
|
||||
Message = StringLocalizer["Payouts archived"].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
break;
|
||||
@ -488,7 +488,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = StringLocalizer["You must enable at least one payment method before creating a payout."],
|
||||
Message = StringLocalizer["You must enable at least one payment method before creating a payout."].Value,
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
});
|
||||
return RedirectToAction(nameof(UIStoresController.Index), "UIStores", new { storeId });
|
||||
|
@ -35,7 +35,8 @@ public partial class UIStoresController
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||
Html = $"You need to configure email settings before this feature works. <a class='alert-link' href='{Url.Action("StoreEmailSettings", new { storeId })}'>Configure store email settings</a>."
|
||||
Html = "You need to configure email settings before this feature works." +
|
||||
$" <a class='alert-link' href='{Url.Action("StoreEmailSettings", new { storeId })}'>Configure store email settings</a>."
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -76,11 +77,11 @@ public partial class UIStoresController
|
||||
.Any(s => !MailboxAddressValidator.TryParse(s, out _)))
|
||||
{
|
||||
ModelState.AddModelError($"{nameof(vm.Rules)}[{i}].{nameof(rule.To)}",
|
||||
"Invalid mailbox address provided. Valid formats are: 'test@example.com' or 'Firstname Lastname <test@example.com>'");
|
||||
StringLocalizer["Invalid mailbox address provided. Valid formats are: '{0}' or '{1}'", "test@example.com", "Firstname Lastname <test@example.com>"]);
|
||||
}
|
||||
else if (!rule.CustomerEmail && string.IsNullOrEmpty(rule.To))
|
||||
ModelState.AddModelError($"{nameof(vm.Rules)}[{i}].{nameof(rule.To)}",
|
||||
"Either recipient or \"Send the email to the buyer\" is required");
|
||||
StringLocalizer["Either recipient or \"Send the email to the buyer\" is required"]);
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
@ -101,7 +102,7 @@ public partial class UIStoresController
|
||||
if (store.SetStoreBlob(blob))
|
||||
{
|
||||
await _storeRepo.UpdateStore(store);
|
||||
message += "Store email rules saved. ";
|
||||
message += StringLocalizer["Store email rules saved."] + " ";
|
||||
}
|
||||
|
||||
if (command.StartsWith("test", StringComparison.InvariantCultureIgnoreCase))
|
||||
@ -122,16 +123,16 @@ public partial class UIStoresController
|
||||
.ToArray();
|
||||
|
||||
emailSender.SendEmail(recipients.ToArray(), null, null, $"[TEST] {rule.Subject}", rule.Body);
|
||||
message += "Test email sent — please verify you received it.";
|
||||
message += StringLocalizer["Test email sent — please verify you received it."];
|
||||
}
|
||||
else
|
||||
{
|
||||
message += "Complete the email setup to send test emails.";
|
||||
message += StringLocalizer["Complete the email setup to send test emails."];
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = message + "Error sending test email: " + ex.Message;
|
||||
TempData[WellKnownTempData.ErrorMessage] = message + StringLocalizer["Error sending test email: {0}", ex.Message].Value;
|
||||
return RedirectToAction("StoreEmails", new { storeId });
|
||||
}
|
||||
}
|
||||
@ -222,14 +223,14 @@ public partial class UIStoresController
|
||||
return View(model);
|
||||
var settings = useCustomSMTP ? model.Settings : model.FallbackSettings;
|
||||
using var client = await settings.CreateSmtpClient();
|
||||
var message = settings.CreateMailMessage(MailboxAddress.Parse(model.TestEmail), $"{store.StoreName}: Email test", "You received it, the BTCPay Server SMTP settings work.", false);
|
||||
var message = settings.CreateMailMessage(MailboxAddress.Parse(model.TestEmail), $"{store.StoreName}: Email test", StringLocalizer["You received it, the BTCPay Server SMTP settings work."], false);
|
||||
await client.SendAsync(message);
|
||||
await client.DisconnectAsync(true);
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Email sent to {model.TestEmail}. Please verify you received it.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Email sent to {0}. Please verify you received it.", model.TestEmail].Value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "Error: " + ex.Message;
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Error: {0}", ex.Message].Value;
|
||||
}
|
||||
return View(model);
|
||||
}
|
||||
@ -239,13 +240,13 @@ public partial class UIStoresController
|
||||
storeBlob.EmailSettings.Password = null;
|
||||
store.SetStoreBlob(storeBlob);
|
||||
await _storeRepo.UpdateStore(store);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Email server password reset";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Email server password reset"].Value;
|
||||
}
|
||||
if (useCustomSMTP)
|
||||
{
|
||||
if (model.Settings.From is not null && !MailboxAddressValidator.IsMailboxAddress(model.Settings.From))
|
||||
{
|
||||
ModelState.AddModelError("Settings.From", "Invalid email");
|
||||
ModelState.AddModelError("Settings.From", StringLocalizer["Invalid email"]);
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
return View(model);
|
||||
@ -257,7 +258,7 @@ public partial class UIStoresController
|
||||
storeBlob.EmailSettings = model.Settings;
|
||||
store.SetStoreBlob(storeBlob);
|
||||
await _storeRepo.UpdateStore(store);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Email settings modified";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Email settings modified"].Value;
|
||||
}
|
||||
return RedirectToAction(nameof(StoreEmailSettings), new { storeId });
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ public partial class UIStoresController
|
||||
if (webhook is null)
|
||||
return NotFound();
|
||||
|
||||
return View("Confirm", new ConfirmModel("Delete webhook", "This webhook will be removed from this store. Are you sure?", "Delete"));
|
||||
return View("Confirm", new ConfirmModel(StringLocalizer["Delete webhook"], StringLocalizer["This webhook will be removed from this store. Are you sure?"], StringLocalizer["Delete"]));
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/webhooks/{webhookId}/remove")]
|
||||
@ -79,7 +79,7 @@ public partial class UIStoresController
|
||||
return NotFound();
|
||||
|
||||
await _storeRepo.DeleteWebhook(CurrentStore.Id, webhookId);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Webhook successfully deleted";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Webhook successfully deleted"].Value;
|
||||
return RedirectToAction(nameof(Webhooks), new { storeId = CurrentStore.Id });
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ public partial class UIStoresController
|
||||
return View(nameof(ModifyWebhook), viewModel);
|
||||
|
||||
await _storeRepo.CreateWebhook(CurrentStore.Id, viewModel.CreateBlob());
|
||||
TempData[WellKnownTempData.SuccessMessage] = "The webhook has been created";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["The webhook has been created"].Value;
|
||||
return RedirectToAction(nameof(Webhooks), new { storeId });
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@ public partial class UIStoresController
|
||||
return View(nameof(ModifyWebhook), viewModel);
|
||||
|
||||
await _storeRepo.UpdateWebhook(CurrentStore.Id, webhookId, viewModel.CreateBlob());
|
||||
TempData[WellKnownTempData.SuccessMessage] = "The webhook has been updated";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["The webhook has been updated"].Value;
|
||||
return RedirectToAction(nameof(Webhooks), new { storeId = CurrentStore.Id });
|
||||
}
|
||||
|
||||
@ -146,11 +146,11 @@ public partial class UIStoresController
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"{viewModel.Type} event delivered successfully! Delivery ID is {result.DeliveryId}";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["{0} event delivered successfully! Delivery ID is {1}", viewModel.Type, result.DeliveryId!].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"{viewModel.Type} event could not be delivered. Error message received: {(result.ErrorMessage ?? "unknown")}";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["{0} event could not be delivered. Error message received: {1}", viewModel.Type, result.ErrorMessage ?? StringLocalizer["unknown"].Value].Value;
|
||||
}
|
||||
|
||||
return View(nameof(TestWebhook));
|
||||
@ -168,7 +168,7 @@ public partial class UIStoresController
|
||||
if (newDeliveryId is null)
|
||||
return NotFound();
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Successfully planned a redelivery";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Successfully planned a redelivery"].Value;
|
||||
return RedirectToAction(nameof(ModifyWebhook),
|
||||
new
|
||||
{
|
||||
|
@ -115,7 +115,7 @@ public partial class UIStoresController
|
||||
|
||||
if (vm.CryptoCode == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.CryptoCode), "Invalid network");
|
||||
ModelState.AddModelError(nameof(vm.CryptoCode), StringLocalizer["Invalid network"]);
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ public partial class UIStoresController
|
||||
{
|
||||
if (string.IsNullOrEmpty(vm.ConnectionString))
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.ConnectionString), "Please provide a connection string");
|
||||
ModelState.AddModelError(nameof(vm.ConnectionString), StringLocalizer["Please provide a connection string"]);
|
||||
return View(vm);
|
||||
}
|
||||
paymentMethod = new LightningPaymentMethodConfig { ConnectionString = vm.ConnectionString };
|
||||
@ -143,7 +143,7 @@ public partial class UIStoresController
|
||||
JToken.FromObject(paymentMethod, handler.Serializer), User, oldConf is null ? null : JToken.FromObject(oldConf, handler.Serializer));
|
||||
await handler.ValidatePaymentMethodConfig(ctx);
|
||||
if (ctx.MissingPermission is not null)
|
||||
ModelState.AddModelError(nameof(vm.ConnectionString), "You do not have the permissions to change this settings");
|
||||
ModelState.AddModelError(nameof(vm.ConnectionString), StringLocalizer["You do not have the permissions to change this settings"]);
|
||||
if (!ModelState.IsValid)
|
||||
return View(vm);
|
||||
|
||||
@ -159,7 +159,7 @@ public partial class UIStoresController
|
||||
});
|
||||
|
||||
await _storeRepo.UpdateStore(store);
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"{network.CryptoCode} Lightning node updated.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["{0} Lightning node updated.", network.CryptoCode].Value;
|
||||
return RedirectToAction(nameof(LightningSettings), new { storeId, cryptoCode });
|
||||
|
||||
case "test":
|
||||
@ -172,9 +172,10 @@ public partial class UIStoresController
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20));
|
||||
await handler.TestConnection(info.First(), cts.Token);
|
||||
}
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Connection to the Lightning node successful" + (hasPublicAddress
|
||||
? $". Your node address: {info.First()}"
|
||||
: ", but no public address has been configured");
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Connection to the Lightning node successful."].Value + " " +
|
||||
(hasPublicAddress
|
||||
? StringLocalizer["Your node address: {0}", info.First()].Value
|
||||
: StringLocalizer["No public address has been configured."].Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -202,7 +203,7 @@ public partial class UIStoresController
|
||||
var lightning = GetConfig<LightningPaymentMethodConfig>(lnId, store);
|
||||
if (lightning == null)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "You need to connect to a Lightning node before adjusting its settings.";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["You need to connect to a Lightning node before adjusting its settings."].Value;
|
||||
|
||||
return RedirectToAction(nameof(SetupLightningNode), new { storeId, cryptoCode });
|
||||
}
|
||||
@ -241,7 +242,7 @@ public partial class UIStoresController
|
||||
|
||||
if (vm.CryptoCode == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.CryptoCode), "Invalid network");
|
||||
ModelState.AddModelError(nameof(vm.CryptoCode), StringLocalizer["Invalid network"]);
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
@ -289,7 +290,7 @@ public partial class UIStoresController
|
||||
{
|
||||
await _storeRepo.UpdateStore(store);
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"{network.CryptoCode} Lightning settings successfully updated.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["{0} Lightning settings successfully updated.", network.CryptoCode].Value;
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(LightningSettings), new { vm.StoreId, vm.CryptoCode });
|
||||
|
@ -108,7 +108,7 @@ public partial class UIStoresController
|
||||
|
||||
if (fileContent is null || !_onChainWalletParsers.TryParseWalletFile(fileContent, network, out strategy, out _))
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.WalletFile), $"Import failed, make sure you import a compatible wallet format");
|
||||
ModelState.AddModelError(nameof(vm.WalletFile), StringLocalizer["Import failed, make sure you import a compatible wallet format"]);
|
||||
return View(vm.ViewName, vm);
|
||||
}
|
||||
}
|
||||
@ -116,7 +116,7 @@ public partial class UIStoresController
|
||||
{
|
||||
if (!_onChainWalletParsers.TryParseWalletFile(vm.WalletFileContent, network, out strategy, out var error))
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.WalletFileContent), $"QR import failed: {error}");
|
||||
ModelState.AddModelError(nameof(vm.WalletFileContent), StringLocalizer["QR import failed: {0}", error]);
|
||||
return View(vm.ViewName, vm);
|
||||
}
|
||||
}
|
||||
@ -145,7 +145,7 @@ public partial class UIStoresController
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.DerivationScheme), $"Invalid wallet format: {ex.Message}");
|
||||
ModelState.AddModelError(nameof(vm.DerivationScheme), StringLocalizer["Invalid wallet format: {0}", ex.Message]);
|
||||
return View(vm.ViewName, vm);
|
||||
}
|
||||
}
|
||||
@ -157,14 +157,14 @@ public partial class UIStoresController
|
||||
}
|
||||
catch
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.Config), "Config file was not in the correct format");
|
||||
ModelState.AddModelError(nameof(vm.Config), StringLocalizer["Config file was not in the correct format"]);
|
||||
return View(vm.ViewName, vm);
|
||||
}
|
||||
}
|
||||
|
||||
if (strategy is null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.DerivationScheme), "Please provide your extended public key");
|
||||
ModelState.AddModelError(nameof(vm.DerivationScheme), StringLocalizer["Please provide your extended public key"]);
|
||||
return View(vm.ViewName, vm);
|
||||
}
|
||||
|
||||
@ -184,13 +184,13 @@ public partial class UIStoresController
|
||||
}
|
||||
catch
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid derivation scheme");
|
||||
ModelState.AddModelError(nameof(vm.DerivationScheme), StringLocalizer["Invalid derivation scheme"]);
|
||||
return View(vm.ViewName, vm);
|
||||
}
|
||||
await _storeRepo.UpdateStore(store);
|
||||
_eventAggregator.Publish(new WalletChangedEvent { WalletId = new WalletId(vm.StoreId, vm.CryptoCode) });
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Wallet settings for {network.CryptoCode} have been updated.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Wallet settings for {0} have been updated.", network.CryptoCode].Value;
|
||||
|
||||
// This is success case when derivation scheme is added to the store
|
||||
return RedirectToAction(nameof(WalletSettings), new { storeId = vm.StoreId, cryptoCode = vm.CryptoCode });
|
||||
@ -287,7 +287,7 @@ public partial class UIStoresController
|
||||
|
||||
if (isImport && string.IsNullOrEmpty(request.ExistingMnemonic))
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.ExistingMnemonic), "Please provide your existing seed");
|
||||
ModelState.AddModelError(nameof(request.ExistingMnemonic), StringLocalizer["Please provide your existing seed"]);
|
||||
return View(vm.ViewName, vm);
|
||||
}
|
||||
|
||||
@ -305,7 +305,7 @@ public partial class UIStoresController
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Html = $"There was an error generating your wallet: {e.Message}"
|
||||
Message = StringLocalizer["There was an error generating your wallet: {0}", e.Message].Value
|
||||
});
|
||||
return View(vm.ViewName, vm);
|
||||
}
|
||||
@ -343,7 +343,7 @@ public partial class UIStoresController
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Html = "<span class='text-centered'>Your wallet has been generated.</span>"
|
||||
Html = "<span class='text-centered'>" + StringLocalizer["Your wallet has been generated."].Value + "</span>"
|
||||
});
|
||||
var seedVm = new RecoverySeedBackupViewModel
|
||||
{
|
||||
@ -363,7 +363,7 @@ public partial class UIStoresController
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||
Html = "Please check your addresses and confirm."
|
||||
Message = StringLocalizer["Please check your addresses and confirm."].Value
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@ -380,7 +380,7 @@ public partial class UIStoresController
|
||||
return checkResult;
|
||||
}
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Wallet settings for {network.CryptoCode} have been updated.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Wallet settings for {0} have been updated.", network.CryptoCode].Value;
|
||||
|
||||
var walletId = new WalletId(storeId, cryptoCode);
|
||||
return RedirectToAction(nameof(UIWalletsController.WalletTransactions), "UIWallets", new { walletId });
|
||||
@ -608,7 +608,7 @@ public partial class UIStoresController
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = "The seed was not found"
|
||||
Message = StringLocalizer["The seed was not found"].Value
|
||||
});
|
||||
|
||||
return RedirectToAction(nameof(WalletSettings));
|
||||
@ -628,9 +628,9 @@ public partial class UIStoresController
|
||||
|
||||
return View("Confirm", new ConfirmModel
|
||||
{
|
||||
Title = $"Replace {network.CryptoCode} wallet",
|
||||
Title = StringLocalizer["Replace {0} wallet", network.CryptoCode],
|
||||
Description = WalletReplaceWarning(derivation.IsHotWallet),
|
||||
Action = "Setup new wallet"
|
||||
Action = StringLocalizer["Setup new wallet"]
|
||||
});
|
||||
}
|
||||
|
||||
@ -667,9 +667,9 @@ public partial class UIStoresController
|
||||
|
||||
return View("Confirm", new ConfirmModel
|
||||
{
|
||||
Title = $"Remove {network.CryptoCode} wallet",
|
||||
Title = StringLocalizer["Remove {0} wallet", network.CryptoCode],
|
||||
Description = WalletRemoveWarning(derivation.IsHotWallet, network.CryptoCode),
|
||||
Action = "Remove"
|
||||
Action = StringLocalizer["Remove"]
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ public partial class UIStoresController
|
||||
}
|
||||
catch
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.DefaultCurrencyPairs), "Invalid currency pairs (should be for example: BTC_USD,BTC_CAD,BTC_JPY)");
|
||||
ModelState.AddModelError(nameof(model.DefaultCurrencyPairs), StringLocalizer["Invalid currency pairs (should be for example: {0})", "BTC_USD,BTC_CAD,BTC_JPY"]);
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@ -71,7 +71,7 @@ public partial class UIStoresController
|
||||
{
|
||||
errors ??= [];
|
||||
var errorString = string.Join(", ", errors.ToArray());
|
||||
ModelState.AddModelError(nameof(model.Script), $"Parsing error ({errorString})");
|
||||
ModelState.AddModelError(nameof(model.Script), StringLocalizer["Parsing error: {0}", errorString]);
|
||||
FillFromStore(model, blob);
|
||||
return View(model);
|
||||
}
|
||||
@ -90,7 +90,7 @@ public partial class UIStoresController
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(model.ScriptTest))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.ScriptTest), "Fill out currency pair to test for (like BTC_USD,BTC_CAD)");
|
||||
ModelState.AddModelError(nameof(model.ScriptTest), StringLocalizer["Fill out currency pair to test for (like {0})", "BTC_USD,BTC_CAD"]);
|
||||
return View(model);
|
||||
}
|
||||
var splitted = model.ScriptTest.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||
@ -100,7 +100,7 @@ public partial class UIStoresController
|
||||
{
|
||||
if (!CurrencyPair.TryParse(pair, out var currencyPair))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.ScriptTest), $"Invalid currency pair '{pair}' (it should be formatted like BTC_USD,BTC_CAD)");
|
||||
ModelState.AddModelError(nameof(model.ScriptTest), StringLocalizer["Invalid currency pair '{0}' (it should be formatted like {1})", pair, "BTC_USD,BTC_CAD"]);
|
||||
return View(model);
|
||||
}
|
||||
pairs.Add(currencyPair);
|
||||
@ -125,7 +125,7 @@ public partial class UIStoresController
|
||||
|
||||
if (model.PreferredExchange is not null && !model.AvailableExchanges.Any(a => a.Id == model.PreferredExchange))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.PreferredExchange), $"Unsupported exchange");
|
||||
ModelState.AddModelError(nameof(model.PreferredExchange), StringLocalizer["Unsupported exchange"]);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
@ -147,11 +147,11 @@ public partial class UIStoresController
|
||||
{
|
||||
return View("Confirm", new ConfirmModel
|
||||
{
|
||||
Action = "Continue",
|
||||
Title = "Rate rule scripting",
|
||||
Description = scripting ?
|
||||
"This action will modify your current rate sources. Are you sure to turn on rate rules scripting? (Advanced users)"
|
||||
: "This action will delete your rate script. Are you sure to turn off rate rules scripting?",
|
||||
Action = StringLocalizer["Continue"],
|
||||
Title = StringLocalizer["Rate rule scripting"],
|
||||
Description = scripting
|
||||
? StringLocalizer["This action will modify your current rate sources. Are you sure to turn on rate rules scripting? (Advanced users)"]
|
||||
: StringLocalizer["This action will delete your rate script. Are you sure to turn off rate rules scripting?"],
|
||||
ButtonClass = scripting ? "btn-primary" : "btn-danger"
|
||||
});
|
||||
}
|
||||
|
@ -77,13 +77,13 @@ public partial class UIStoresController
|
||||
StoreRoleId roleId;
|
||||
if (role == "create")
|
||||
{
|
||||
successMessage = "Role created";
|
||||
successMessage = StringLocalizer["Role created"];
|
||||
role = viewModel.Role;
|
||||
roleId = new StoreRoleId(storeId, role);
|
||||
}
|
||||
else
|
||||
{
|
||||
successMessage = "Role updated";
|
||||
successMessage = StringLocalizer["Role updated"];
|
||||
roleId = new StoreRoleId(storeId, role);
|
||||
var storeRole = await storeRepository.GetStoreRole(roleId);
|
||||
if (storeRole == null)
|
||||
@ -98,15 +98,15 @@ public partial class UIStoresController
|
||||
var r = await storeRepository.AddOrUpdateStoreRole(roleId, viewModel.Policies);
|
||||
if (r is null)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = "Role could not be updated"
|
||||
Message = StringLocalizer["Role could not be updated"].Value
|
||||
});
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = successMessage
|
||||
@ -128,11 +128,11 @@ public partial class UIStoresController
|
||||
|
||||
return View("Confirm",
|
||||
roleData.IsUsed is true
|
||||
? new ConfirmModel("Delete role",
|
||||
? new ConfirmModel(StringLocalizer["Delete role"],
|
||||
$"Unable to proceed: The role <strong>{_html.Encode(roleData.Role)}</strong> is currently assigned to one or more users, it cannot be removed.")
|
||||
: new ConfirmModel("Delete role",
|
||||
: new ConfirmModel(StringLocalizer["Delete role"],
|
||||
$"The role <strong>{_html.Encode(roleData.Role)}</strong> will be permanently deleted. Are you sure?",
|
||||
"Delete"));
|
||||
StringLocalizer["Delete"]));
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/roles/{role}/delete")]
|
||||
@ -152,7 +152,7 @@ public partial class UIStoresController
|
||||
}
|
||||
await storeRepository.RemoveStoreRole(roleId);
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Role deleted";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Role deleted"].Value;
|
||||
return RedirectToAction(nameof(ListRoles), new { storeId });
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ public partial class UIStoresController
|
||||
blob.MonitoringExpiration = TimeSpan.FromMinutes(model.MonitoringExpiration);
|
||||
if (!string.IsNullOrEmpty(model.BrandColor) && !ColorPalette.IsValid(model.BrandColor))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.BrandColor), "The brand color needs to be a valid hex color code");
|
||||
ModelState.AddModelError(nameof(model.BrandColor), StringLocalizer["The brand color needs to be a valid hex color code"]);
|
||||
return View(model);
|
||||
}
|
||||
blob.BrandColor = model.BrandColor;
|
||||
@ -103,18 +103,18 @@ public partial class UIStoresController
|
||||
{
|
||||
if (model.LogoFile.Length > 1_000_000)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.LogoFile), "The uploaded logo file should be less than 1MB");
|
||||
ModelState.AddModelError(nameof(model.LogoFile), StringLocalizer["The uploaded logo file should be less than {0}", "1MB"]);
|
||||
}
|
||||
else if (!model.LogoFile.ContentType.StartsWith("image/", StringComparison.InvariantCulture))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.LogoFile), "The uploaded logo file needs to be an image");
|
||||
ModelState.AddModelError(nameof(model.LogoFile), StringLocalizer["The uploaded logo file needs to be an image"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var formFile = await model.LogoFile.Bufferize();
|
||||
if (!FileTypeDetector.IsPicture(formFile.Buffer, formFile.FileName))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.LogoFile), "The uploaded logo file needs to be an image");
|
||||
ModelState.AddModelError(nameof(model.LogoFile), StringLocalizer["The uploaded logo file needs to be an image"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -127,7 +127,7 @@ public partial class UIStoresController
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.LogoFile), $"Could not save logo: {e.Message}");
|
||||
ModelState.AddModelError(nameof(model.LogoFile), StringLocalizer["Could not save logo: {0}", e.Message]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -142,15 +142,15 @@ public partial class UIStoresController
|
||||
{
|
||||
if (model.CssFile.Length > 1_000_000)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.CssFile), "The uploaded file should be less than 1MB");
|
||||
ModelState.AddModelError(nameof(model.CssFile), StringLocalizer["The uploaded file should be less than {0}", "1MB"]);
|
||||
}
|
||||
else if (!model.CssFile.ContentType.Equals("text/css", StringComparison.InvariantCulture))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.CssFile), "The uploaded file needs to be a CSS file");
|
||||
ModelState.AddModelError(nameof(model.CssFile), StringLocalizer["The uploaded file needs to be a CSS file"]);
|
||||
}
|
||||
else if (!model.CssFile.FileName.EndsWith(".css", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.CssFile), "The uploaded file needs to be a CSS file");
|
||||
ModelState.AddModelError(nameof(model.CssFile), StringLocalizer["The uploaded file needs to be a CSS file"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -162,7 +162,7 @@ public partial class UIStoresController
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.CssFile), $"Could not save CSS file: {e.Message}");
|
||||
ModelState.AddModelError(nameof(model.CssFile), StringLocalizer["Could not save CSS file: {0}", e.Message]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -211,7 +211,7 @@ public partial class UIStoresController
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
public IActionResult DeleteStore(string storeId)
|
||||
{
|
||||
return View("Confirm", new ConfirmModel("Delete store", "The store will be permanently deleted. This action will also delete all invoices, apps and data associated with the store. Are you sure?", "Delete"));
|
||||
return View("Confirm", new ConfirmModel(StringLocalizer["Delete store"], StringLocalizer["The store will be permanently deleted. This action will also delete all invoices, apps and data associated with the store. Are you sure?"], StringLocalizer["Delete"]));
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/delete")]
|
||||
@ -305,18 +305,18 @@ public partial class UIStoresController
|
||||
{
|
||||
if (model.SoundFile.Length > 1_000_000)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.SoundFile), "The uploaded sound file should be less than 1MB");
|
||||
ModelState.AddModelError(nameof(model.SoundFile), StringLocalizer["The uploaded sound file should be less than {0}", "1MB"]);
|
||||
}
|
||||
else if (!model.SoundFile.ContentType.StartsWith("audio/", StringComparison.InvariantCulture))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.SoundFile), "The uploaded sound file needs to be an audio file");
|
||||
ModelState.AddModelError(nameof(model.SoundFile), StringLocalizer["The uploaded sound file needs to be an audio file"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var formFile = await model.SoundFile.Bufferize();
|
||||
if (!FileTypeDetector.IsAudio(formFile.Buffer, formFile.FileName))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.SoundFile), "The uploaded sound file needs to be an audio file");
|
||||
ModelState.AddModelError(nameof(model.SoundFile), StringLocalizer["The uploaded sound file needs to be an audio file"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -330,7 +330,7 @@ public partial class UIStoresController
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.SoundFile), $"Could not save sound: {e.Message}");
|
||||
ModelState.AddModelError(nameof(model.SoundFile), StringLocalizer["Could not save sound: {0}", e.Message]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public partial class UIStoresController
|
||||
var token = await _tokenRepository.GetToken(tokenId);
|
||||
if (token == null || token.StoreId != CurrentStore.Id)
|
||||
return NotFound();
|
||||
return View("Confirm", new ConfirmModel("Revoke the token", $"The access token with the label <strong>{_html.Encode(token.Label)}</strong> will be revoked. Do you wish to continue?", "Revoke"));
|
||||
return View("Confirm", new ConfirmModel(StringLocalizer["Revoke the token"], $"The access token with the label <strong>{_html.Encode(token.Label)}</strong> will be revoked. Do you wish to continue?", "Revoke"));
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/tokens/{tokenId}/revoke")]
|
||||
@ -243,14 +243,14 @@ public partial class UIStoresController
|
||||
StoreNotConfigured = store.GetPaymentMethodConfigs(_handlers).All(p => excludeFilter.Match(p.Key));
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Pairing is successful";
|
||||
if (pairingResult == PairingResult.Partial)
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Server initiated pairing code: " + pairingCode;
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Server initiated pairing code: {pairingCode}";
|
||||
return RedirectToAction(nameof(ListTokens), new
|
||||
{
|
||||
storeId = store.Id, pairingCode
|
||||
});
|
||||
}
|
||||
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"Pairing failed ({pairingResult})";
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"Pairing failed: {pairingResult}";
|
||||
return RedirectToAction(nameof(ListTokens), new
|
||||
{
|
||||
storeId = store.Id
|
||||
|
@ -39,7 +39,7 @@ public partial class UIStoresController
|
||||
var roles = await _storeRepo.GetStoreRoles(CurrentStore.Id);
|
||||
if (roles.All(role => role.Id != vm.Role))
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.Role), "Invalid role");
|
||||
ModelState.AddModelError(nameof(vm.Role), StringLocalizer["Invalid role"]);
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
@ -116,9 +116,9 @@ public partial class UIStoresController
|
||||
var isOwner = user.StoreRole.Id == StoreRoleId.Owner.Id;
|
||||
var isLastOwner = isOwner && storeUsers.Count(u => u.StoreRole.Id == StoreRoleId.Owner.Id) == 1;
|
||||
if (isLastOwner && roleId != StoreRoleId.Owner)
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"User {user.Email} is the last owner. Their role cannot be changed.";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["User {0} is the last owner. Their role cannot be changed.", user.Email].Value;
|
||||
else if (await _storeRepo.AddOrUpdateStoreUser(storeId, userId, roleId))
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"The role of {user.Email} has been changed to {vm.Role}.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["The role of {0} has been changed to {1}.", user.Email, vm.Role].Value;
|
||||
return RedirectToAction(nameof(StoreUsers), new { storeId, userId });
|
||||
}
|
||||
|
||||
@ -127,9 +127,9 @@ public partial class UIStoresController
|
||||
public async Task<IActionResult> DeleteStoreUser(string storeId, string userId)
|
||||
{
|
||||
if (await _storeRepo.RemoveStoreUser(storeId, userId))
|
||||
TempData[WellKnownTempData.SuccessMessage] = "User removed successfully.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["User removed successfully."].Value;
|
||||
else
|
||||
TempData[WellKnownTempData.ErrorMessage] = "Removing this user would result in the store having no owner.";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Removing this user would result in the store having no owner."].Value;
|
||||
return RedirectToAction(nameof(StoreUsers), new { storeId, userId });
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
@ -62,6 +63,7 @@ public partial class UIStoresController : Controller
|
||||
UriResolver uriResolver,
|
||||
SettingsRepository settingsRepository,
|
||||
CurrencyNameTable currencyNameTable,
|
||||
IStringLocalizer stringLocalizer,
|
||||
EventAggregator eventAggregator)
|
||||
{
|
||||
_rateFactory = rateFactory;
|
||||
@ -93,6 +95,7 @@ public partial class UIStoresController : Controller
|
||||
_dataProtector = dataProtector.CreateProtector("ConfigProtector");
|
||||
_webhookNotificationManager = webhookNotificationManager;
|
||||
_lightningNetworkOptions = lightningNetworkOptions.Value;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
private readonly BTCPayServerOptions _btcpayServerOptions;
|
||||
@ -126,6 +129,7 @@ public partial class UIStoresController : Controller
|
||||
private readonly IDataProtector _dataProtector;
|
||||
|
||||
public string? GeneratedPairingCode { get; set; }
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
[TempData]
|
||||
private bool StoreNotConfigured { get; set; }
|
||||
|
@ -13,7 +13,6 @@ using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Localization;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
|
@ -58,7 +58,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
var psbt = (await nbx.CreatePSBTAsync(derivationSettings.AccountDerivation, psbtRequest, cancellationToken));
|
||||
if (psbt == null)
|
||||
throw new NotSupportedException("You need to update your version of NBXplorer");
|
||||
throw new NotSupportedException(StringLocalizer["You need to update your version of NBXplorer"]);
|
||||
// Not supported by coldcard, remove when they do support it
|
||||
psbt.PSBT.GlobalXPubs.Clear();
|
||||
return psbt;
|
||||
@ -92,7 +92,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (bumpableUTXOs.Length == 0)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "There isn't any UTXO available to bump fee";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["There isn't any UTXO available to bump fee"].Value;
|
||||
return LocalRedirect(returnUrl);
|
||||
}
|
||||
Money bumpFee = Money.Zero;
|
||||
@ -267,10 +267,10 @@ namespace BTCPayServer.Controllers
|
||||
psbt = await ExplorerClientProvider.UpdatePSBT(derivationSchemeSettings, psbt);
|
||||
if (psbt == null)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "You need to update your version of NBXplorer";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["You need to update your version of NBXplorer"].Value;
|
||||
return View(vm);
|
||||
}
|
||||
TempData[WellKnownTempData.SuccessMessage] = "PSBT updated!";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["PSBT updated!"].Value;
|
||||
return RedirectToWalletPSBT(new WalletPSBTViewModel
|
||||
{
|
||||
PSBT = psbt.ToBase64(),
|
||||
@ -479,7 +479,7 @@ namespace BTCPayServer.Controllers
|
||||
if (vm.InvalidPSBT || psbt is null)
|
||||
{
|
||||
if (vm.InvalidPSBT)
|
||||
vm.Errors.Add("Invalid PSBT");
|
||||
vm.Errors.Add(StringLocalizer["Invalid PSBT"]);
|
||||
return View(nameof(WalletPSBT), vm);
|
||||
}
|
||||
DerivationSchemeSettings derivationSchemeSettings = GetDerivationSchemeSettings(walletId);
|
||||
@ -537,15 +537,15 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
catch (PayjoinReceiverException ex)
|
||||
{
|
||||
error = $"The payjoin receiver could not complete the payjoin: {ex.Message}";
|
||||
error = StringLocalizer["The payjoin receiver could not complete the payjoin: {0}", ex.Message];
|
||||
}
|
||||
catch (PayjoinSenderException ex)
|
||||
{
|
||||
error = $"We rejected the receiver's payjoin proposal: {ex.Message}";
|
||||
error = StringLocalizer["We rejected the receiver's payjoin proposal: {0}", ex.Message];
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = $"Unexpected payjoin error: {ex.Message}";
|
||||
error = StringLocalizer["Unexpected payjoin error: {0}", ex.Message];
|
||||
}
|
||||
|
||||
//we possibly exposed the tx to the receiver, so we need to broadcast straight away
|
||||
@ -554,9 +554,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||
AllowDismiss = false,
|
||||
Html = $"The payjoin transaction could not be created.<br/>" +
|
||||
$"The original transaction was broadcasted instead. ({psbt.ExtractTransaction().GetHash()})<br/><br/>" +
|
||||
$"{error}"
|
||||
Html = $"The payjoin transaction could not be created.<br/>The original transaction was broadcasted instead ({psbt.ExtractTransaction().GetHash()})<br/><br/>" + error
|
||||
});
|
||||
return await WalletPSBTReady(walletId, vm, "broadcast");
|
||||
case "broadcast" when !psbt.IsAllFinalized() && !psbt.TryFinalize(out var errors):
|
||||
@ -576,14 +574,14 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||
AllowDismiss = false,
|
||||
Html = $"The payjoin transaction could not be broadcasted.<br/>({broadcastResult.RPCCode} {broadcastResult.RPCCodeMessage} {broadcastResult.RPCMessage}).<br/>The transaction has been reverted back to its original format and has been broadcast."
|
||||
Html = $"The payjoin transaction could not be broadcasted: {broadcastResult.RPCCode} {broadcastResult.RPCCodeMessage} {broadcastResult.RPCMessage}<br/>The transaction has been reverted back to its original format and has been broadcast."
|
||||
});
|
||||
vm.SigningContext.PSBT = vm.SigningContext.OriginalPSBT;
|
||||
vm.SigningContext.OriginalPSBT = null;
|
||||
return await WalletPSBTReady(walletId, vm, "broadcast");
|
||||
}
|
||||
|
||||
vm.Errors.Add($"RPC Error while broadcasting: {broadcastResult.RPCCode} {broadcastResult.RPCCodeMessage} {broadcastResult.RPCMessage}");
|
||||
vm.Errors.Add(StringLocalizer["RPC Error while broadcasting: {0}", $"{broadcastResult.RPCCode} {broadcastResult.RPCCodeMessage} {broadcastResult.RPCMessage}"]);
|
||||
return View(nameof(WalletPSBT), vm);
|
||||
}
|
||||
else
|
||||
@ -595,13 +593,13 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
vm.Errors.Add("Error while broadcasting: " + ex.Message);
|
||||
vm.Errors.Add(StringLocalizer["Error while broadcasting: {0}", ex.Message]);
|
||||
return View(nameof(WalletPSBT), vm);
|
||||
}
|
||||
|
||||
if (!TempData.HasStatusMessage())
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Transaction broadcasted successfully ({transaction.GetHash()})";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Transaction broadcasted successfully ({0})", transaction.GetHash()].Value;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(vm.ReturnUrl))
|
||||
{
|
||||
@ -620,7 +618,7 @@ namespace BTCPayServer.Controllers
|
||||
await FetchTransactionDetails(walletId, derivationSchemeSettings, vm, network);
|
||||
return View("WalletPSBTDecoded", vm);
|
||||
default:
|
||||
vm.Errors.Add("Unknown command");
|
||||
vm.Errors.Add(StringLocalizer["Unknown command"]);
|
||||
return View(nameof(WalletPSBT), vm);
|
||||
}
|
||||
}
|
||||
@ -646,7 +644,7 @@ namespace BTCPayServer.Controllers
|
||||
return View(vm);
|
||||
}
|
||||
sourcePSBT = sourcePSBT.Combine(psbt);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "PSBT Successfully combined!";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["PSBT Successfully combined!"].Value;
|
||||
return RedirectToWalletPSBT(new WalletPSBTViewModel
|
||||
{
|
||||
PSBT = sourcePSBT.ToBase64(),
|
||||
|
@ -35,6 +35,7 @@ using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
@ -57,6 +58,7 @@ namespace BTCPayServer.Controllers
|
||||
private ExplorerClientProvider ExplorerClientProvider { get; }
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
public RateFetcher RateFetcher { get; }
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly NBXplorerDashboard _dashboard;
|
||||
@ -99,6 +101,7 @@ namespace BTCPayServer.Controllers
|
||||
DefaultRulesCollection defaultRules,
|
||||
PaymentMethodHandlerDictionary handlers,
|
||||
Dictionary<PaymentMethodId, ICheckoutModelExtension> paymentModelExtensions,
|
||||
IStringLocalizer stringLocalizer,
|
||||
TransactionLinkProviders transactionLinkProviders)
|
||||
{
|
||||
_currencyTable = currencyTable;
|
||||
@ -124,6 +127,7 @@ namespace BTCPayServer.Controllers
|
||||
_pullPaymentHostedService = pullPaymentHostedService;
|
||||
ServiceProvider = serviceProvider;
|
||||
_walletHistogramService = walletHistogramService;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -908,18 +912,17 @@ namespace BTCPayServer.Controllers
|
||||
try
|
||||
{
|
||||
address = BitcoinAddress.Create(bip21, network.NBitcoinNetwork);
|
||||
vm.Outputs.Add(new WalletSendModel.TransactionOutput()
|
||||
vm.Outputs.Add(new WalletSendModel.TransactionOutput
|
||||
{
|
||||
DestinationAddress = address.ToString()
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = "The provided BIP21 payment URI was malformed"
|
||||
Message = StringLocalizer["The provided BIP21 payment URI was malformed"].Value
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1256,7 +1259,7 @@ namespace BTCPayServer.Controllers
|
||||
selectedTransactions ??= Array.Empty<string>();
|
||||
if (selectedTransactions.Length == 0)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"No transaction selected";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["No transaction selected"].Value;
|
||||
return RedirectToAction(nameof(WalletTransactions), new { walletId });
|
||||
}
|
||||
|
||||
@ -1287,12 +1290,12 @@ namespace BTCPayServer.Controllers
|
||||
.PruneAsync(derivationScheme.AccountDerivation, new PruneRequest(), cancellationToken);
|
||||
if (result.TotalPruned == 0)
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = "The wallet is already pruned";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["The wallet is already pruned"].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] =
|
||||
$"The wallet has been successfully pruned ({result.TotalPruned} transactions have been removed from the history)";
|
||||
StringLocalizer["The wallet has been successfully pruned ({0} transactions have been removed from the history)", result.TotalPruned].Value;
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(WalletTransactions), new { walletId });
|
||||
@ -1455,11 +1458,11 @@ namespace BTCPayServer.Controllers
|
||||
;
|
||||
if (await WalletRepository.RemoveWalletLabels(walletId, labels))
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = "The label has been successfully removed.";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["The label has been successfully removed."].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "The label could not be removed.";
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["The label could not be removed."].Value;
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(WalletLabels), new { walletId });
|
||||
|
@ -237,7 +237,7 @@ public class BitcoinLikePayoutHandler : IPayoutHandler, IHasNetwork
|
||||
}
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
return new StatusMessageModel()
|
||||
return new StatusMessageModel
|
||||
{
|
||||
Message = "Payout payments have been marked confirmed",
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
|
@ -10,6 +10,7 @@ using Fido2NetLib;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Fido2
|
||||
@ -20,17 +21,22 @@ namespace BTCPayServer.Fido2
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly Fido2Service _fido2Service;
|
||||
private IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UIFido2Controller(UserManager<ApplicationUser> userManager, Fido2Service fido2Service)
|
||||
public UIFido2Controller(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
Fido2Service fido2Service,
|
||||
IStringLocalizer stringLocalizer)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_fido2Service = fido2Service;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
[HttpGet("{id}/delete")]
|
||||
public IActionResult Remove(string id)
|
||||
{
|
||||
return View("Confirm", new ConfirmModel("Remove security device", "Your account will no longer have this security device as an option for two-factor authentication.", "Remove"));
|
||||
return View("Confirm", new ConfirmModel(StringLocalizer["Remove security device"], StringLocalizer["Your account will no longer have this security device as an option for two-factor authentication."], StringLocalizer["Remove"]));
|
||||
}
|
||||
|
||||
[HttpPost("{id}/delete")]
|
||||
@ -41,7 +47,7 @@ namespace BTCPayServer.Fido2
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Html = "The security device was removed successfully."
|
||||
Html = StringLocalizer["The security device was removed successfully."].Value
|
||||
});
|
||||
|
||||
return RedirectToList();
|
||||
@ -56,7 +62,7 @@ namespace BTCPayServer.Fido2
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Html = "The security device could not be registered."
|
||||
Html = StringLocalizer["The security device could not be registered."].Value
|
||||
});
|
||||
|
||||
return RedirectToList();
|
||||
@ -75,7 +81,7 @@ namespace BTCPayServer.Fido2
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Html = "The security device was registered successfully."
|
||||
Html = StringLocalizer["The security device was registered successfully."].Value
|
||||
});
|
||||
}
|
||||
else
|
||||
@ -83,7 +89,7 @@ namespace BTCPayServer.Fido2
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Html = "The security device could not be registered."
|
||||
Html = StringLocalizer["The security device could not be registered."].Value
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Forms;
|
||||
@ -31,9 +32,11 @@ public class UIFormsController : Controller
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private FormComponentProviders FormProviders { get; }
|
||||
private IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UIFormsController(FormComponentProviders formProviders, FormDataService formDataService,
|
||||
UriResolver uriResolver,
|
||||
IStringLocalizer stringLocalizer,
|
||||
StoreRepository storeRepository, IAuthorizationService authorizationService)
|
||||
{
|
||||
FormProviders = formProviders;
|
||||
@ -41,6 +44,7 @@ public class UIFormsController : Controller
|
||||
_uriResolver = uriResolver;
|
||||
_authorizationService = authorizationService;
|
||||
_storeRepository = storeRepository;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
[HttpGet("~/stores/{storeId}/forms")]
|
||||
@ -136,7 +140,7 @@ public class UIFormsController : Controller
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = "Form removed"
|
||||
Message = StringLocalizer["Form removed"].Value
|
||||
});
|
||||
return RedirectToAction("FormsList", new { storeId });
|
||||
}
|
||||
@ -223,10 +227,10 @@ public class UIFormsController : Controller
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = "Could not generate invoice: "+ e.Message
|
||||
Message = StringLocalizer["Could not generate invoice: {0}", e.Message].Value
|
||||
});
|
||||
return await GetFormView(formData, form);
|
||||
}
|
||||
|
@ -8,9 +8,11 @@ namespace BTCPayServer.Models.ManageViewModels
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
[MaxLength(50)]
|
||||
[Display(Name = "Email")]
|
||||
public string Email { get; set; }
|
||||
public bool EmailConfirmed { get; set; }
|
||||
public bool RequiresEmailConfirmation { get; set; }
|
||||
[Display(Name = "Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Display(Name = "Profile Picture")]
|
||||
|
@ -13,6 +13,7 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Display(Name = "Label")]
|
||||
public string Label
|
||||
{
|
||||
get; set;
|
||||
|
@ -14,6 +14,7 @@ using BTCPayServer.Payouts;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace BTCPayServer.PayoutProcessors.Lightning;
|
||||
|
||||
@ -22,15 +23,18 @@ public class UILightningAutomatedPayoutProcessorsController : Controller
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private readonly LightningAutomatedPayoutSenderFactory _lightningAutomatedPayoutSenderFactory;
|
||||
private readonly PayoutProcessorService _payoutProcessorService;
|
||||
private IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UILightningAutomatedPayoutProcessorsController(
|
||||
EventAggregator eventAggregator,
|
||||
LightningAutomatedPayoutSenderFactory lightningAutomatedPayoutSenderFactory,
|
||||
PayoutProcessorService payoutProcessorService)
|
||||
PayoutProcessorService payoutProcessorService,
|
||||
IStringLocalizer stringLocalizer)
|
||||
{
|
||||
_eventAggregator = eventAggregator;
|
||||
_lightningAutomatedPayoutSenderFactory = lightningAutomatedPayoutSenderFactory;
|
||||
_payoutProcessorService = payoutProcessorService;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
[HttpGet("~/stores/{storeId}/payout-processors/lightning-automated/{cryptocode}")]
|
||||
@ -41,10 +45,10 @@ public class UILightningAutomatedPayoutProcessorsController : Controller
|
||||
var id = GetPayoutMethodId(cryptoCode);
|
||||
if (!_lightningAutomatedPayoutSenderFactory.GetSupportedPayoutMethods().Any(i => id == i))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = $"This processor cannot handle {cryptoCode}."
|
||||
Message = StringLocalizer["This processor cannot handle {0}.", cryptoCode].Value
|
||||
});
|
||||
return RedirectToAction("ConfigureStorePayoutProcessors", "UiPayoutProcessors");
|
||||
}
|
||||
@ -75,10 +79,10 @@ public class UILightningAutomatedPayoutProcessorsController : Controller
|
||||
var id = GetPayoutMethodId(cryptoCode);
|
||||
if (!_lightningAutomatedPayoutSenderFactory.GetSupportedPayoutMethods().Any(i => id == i))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = $"This processor cannot handle {cryptoCode}."
|
||||
Message = StringLocalizer["This processor cannot handle {0}.", cryptoCode].Value
|
||||
});
|
||||
return RedirectToAction("ConfigureStorePayoutProcessors", "UiPayoutProcessors");
|
||||
}
|
||||
@ -109,7 +113,7 @@ public class UILightningAutomatedPayoutProcessorsController : Controller
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = "Processor updated."
|
||||
Message = StringLocalizer["Processor updated."].Value
|
||||
});
|
||||
await tcs.Task;
|
||||
return RedirectToAction("ConfigureStorePayoutProcessors", "UiPayoutProcessors", new { storeId });
|
||||
|
@ -13,6 +13,7 @@ using BTCPayServer.Payouts;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace BTCPayServer.PayoutProcessors.OnChain;
|
||||
|
||||
@ -23,6 +24,8 @@ public class UIOnChainAutomatedPayoutProcessorsController : Controller
|
||||
private readonly OnChainAutomatedPayoutSenderFactory _onChainAutomatedPayoutSenderFactory;
|
||||
private readonly PayoutProcessorService _payoutProcessorService;
|
||||
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UIOnChainAutomatedPayoutProcessorsController(
|
||||
EventAggregator eventAggregator,
|
||||
PaymentMethodHandlerDictionary handlers,
|
||||
@ -43,20 +46,20 @@ public class UIOnChainAutomatedPayoutProcessorsController : Controller
|
||||
var id = GetPayoutMethod(cryptoCode);
|
||||
if (!_onChainAutomatedPayoutSenderFactory.GetSupportedPayoutMethods().Any(i => id == i))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = $"This processor cannot handle {cryptoCode}."
|
||||
Message = StringLocalizer["This processor cannot handle {0}.", cryptoCode].Value
|
||||
});
|
||||
return RedirectToAction("ConfigureStorePayoutProcessors", "UiPayoutProcessors");
|
||||
}
|
||||
var wallet = HttpContext.GetStoreData().GetDerivationSchemeSettings(_handlers, cryptoCode);
|
||||
if (wallet?.IsHotWallet is not true)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = $"Either your {cryptoCode} wallet is not configured, or it is not a hot wallet. This processor cannot function until a hot wallet is configured in your store."
|
||||
Message = StringLocalizer["Either your {0} wallet is not configured, or it is not a hot wallet. This processor cannot function until a hot wallet is configured in your store.", cryptoCode].Value
|
||||
});
|
||||
}
|
||||
var activeProcessor =
|
||||
@ -85,10 +88,10 @@ public class UIOnChainAutomatedPayoutProcessorsController : Controller
|
||||
var id = GetPayoutMethod(cryptoCode);
|
||||
if (!_onChainAutomatedPayoutSenderFactory.GetSupportedPayoutMethods().Any(i => id == i))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = $"This processor cannot handle {cryptoCode}."
|
||||
Message = StringLocalizer["This processor cannot handle {0}.", cryptoCode].Value
|
||||
});
|
||||
return RedirectToAction("ConfigureStorePayoutProcessors", "UiPayoutProcessors");
|
||||
}
|
||||
@ -119,7 +122,7 @@ public class UIOnChainAutomatedPayoutProcessorsController : Controller
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = "Processor updated."
|
||||
Message = StringLocalizer["Processor updated."].Value
|
||||
});
|
||||
await tcs.Task;
|
||||
return RedirectToAction("ConfigureStorePayoutProcessors", "UiPayoutProcessors", new { storeId });
|
||||
|
@ -10,27 +10,27 @@ using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payouts;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace BTCPayServer.PayoutProcessors;
|
||||
|
||||
public class UIPayoutProcessorsController : Controller
|
||||
{
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||
private readonly IEnumerable<IPayoutProcessorFactory> _payoutProcessorFactories;
|
||||
private readonly PayoutProcessorService _payoutProcessorService;
|
||||
private IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UIPayoutProcessorsController(
|
||||
EventAggregator eventAggregator,
|
||||
BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
IEnumerable<IPayoutProcessorFactory> payoutProcessorFactories,
|
||||
PayoutProcessorService payoutProcessorService)
|
||||
PayoutProcessorService payoutProcessorService,
|
||||
IStringLocalizer stringLocalizer)
|
||||
{
|
||||
_eventAggregator = eventAggregator;
|
||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||
_payoutProcessorFactories = payoutProcessorFactories;
|
||||
_payoutProcessorService = payoutProcessorService;
|
||||
;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
[HttpGet("~/stores/{storeId}/payout-processors")]
|
||||
@ -69,10 +69,10 @@ public class UIPayoutProcessorsController : Controller
|
||||
Id = id,
|
||||
Processed = tcs
|
||||
});
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = "Payout Processor removed"
|
||||
Message = StringLocalizer["Payout Processor removed"].Value
|
||||
});
|
||||
await tcs.Task;
|
||||
return RedirectToAction("ConfigureStorePayoutProcessors", new { storeId });
|
||||
|
@ -27,6 +27,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NBitcoin;
|
||||
using NBitcoin.DataEncoders;
|
||||
using NBitpayClient;
|
||||
@ -50,6 +51,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
||||
UIInvoiceController invoiceController,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
FormDataService formDataService,
|
||||
IStringLocalizer stringLocalizer,
|
||||
CrowdfundAppType app)
|
||||
{
|
||||
_currencies = currencies;
|
||||
@ -62,6 +64,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
||||
_uriResolver = uriResolver;
|
||||
_invoiceController = invoiceController;
|
||||
FormDataService = formDataService;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
@ -74,6 +77,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly CrowdfundAppType _app;
|
||||
public FormDataService FormDataService { get; }
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
[HttpGet("/")]
|
||||
[HttpGet("/apps/{appId}/crowdfund")]
|
||||
@ -581,7 +585,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
||||
StoreId = app.StoreDataId,
|
||||
Settings = newSettings
|
||||
});
|
||||
TempData[WellKnownTempData.SuccessMessage] = "App updated";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["App updated"].Value;
|
||||
return RedirectToAction(nameof(UpdateCrowdfund), new { appId });
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
namespace BTCPayServer.Plugins.PayButton.Controllers
|
||||
@ -25,18 +26,21 @@ namespace BTCPayServer.Plugins.PayButton.Controllers
|
||||
StoreRepository repo,
|
||||
UIStoresController storesController,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
IStringLocalizer stringLocalizer,
|
||||
AppService appService)
|
||||
{
|
||||
_repo = repo;
|
||||
_userManager = userManager;
|
||||
_appService = appService;
|
||||
_storesController = storesController;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
readonly StoreRepository _repo;
|
||||
readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly AppService _appService;
|
||||
private readonly UIStoresController _storesController;
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
[HttpPost("{storeId}/disable-anyone-can-pay")]
|
||||
public async Task<IActionResult> DisableAnyoneCanCreateInvoice(string storeId)
|
||||
@ -44,7 +48,7 @@ namespace BTCPayServer.Plugins.PayButton.Controllers
|
||||
var blob = GetCurrentStore.GetStoreBlob();
|
||||
blob.AnyoneCanInvoice = false;
|
||||
GetCurrentStore.SetStoreBlob(blob);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Feature disabled";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Feature disabled"].Value;
|
||||
await _repo.UpdateStore(GetCurrentStore);
|
||||
return RedirectToAction(nameof(PayButton), new { storeId });
|
||||
}
|
||||
@ -93,7 +97,7 @@ namespace BTCPayServer.Plugins.PayButton.Controllers
|
||||
if (GetCurrentStore.SetStoreBlob(blob))
|
||||
{
|
||||
await _repo.UpdateStore(GetCurrentStore);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Store successfully updated";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Store successfully updated"].Value;
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(PayButton), new
|
||||
|
@ -30,6 +30,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NBitcoin;
|
||||
using NBitcoin.DataEncoders;
|
||||
using NBitpayClient;
|
||||
@ -51,6 +52,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
InvoiceRepository invoiceRepository,
|
||||
UIInvoiceController invoiceController,
|
||||
FormDataService formDataService,
|
||||
IStringLocalizer stringLocalizer,
|
||||
DisplayFormatter displayFormatter)
|
||||
{
|
||||
_currencies = currencies;
|
||||
@ -60,6 +62,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_invoiceController = invoiceController;
|
||||
_displayFormatter = displayFormatter;
|
||||
StringLocalizer = stringLocalizer;
|
||||
FormDataService = formDataService;
|
||||
}
|
||||
|
||||
@ -71,6 +74,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
private readonly UIInvoiceController _invoiceController;
|
||||
private readonly DisplayFormatter _displayFormatter;
|
||||
public FormDataService FormDataService { get; }
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
[HttpGet("/")]
|
||||
[HttpGet("/apps/{appId}/pos")]
|
||||
@ -688,7 +692,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
app.Archived = vm.Archived;
|
||||
app.SetSettings(settings);
|
||||
await _appService.UpdateOrCreateApp(app);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "App updated";
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["App updated"].Value;
|
||||
return RedirectToAction(nameof(UpdatePointOfSale), new { appId });
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,7 @@ using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Filters;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.Altcoins.Monero.Configuration;
|
||||
using BTCPayServer.Services.Altcoins.Monero.Payments;
|
||||
using BTCPayServer.Services.Altcoins.Monero.RPC.Models;
|
||||
@ -25,6 +23,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace BTCPayServer.Services.Altcoins.Monero.UI
|
||||
{
|
||||
@ -39,18 +38,18 @@ namespace BTCPayServer.Services.Altcoins.Monero.UI
|
||||
private readonly StoreRepository _StoreRepository;
|
||||
private readonly MoneroRPCProvider _MoneroRpcProvider;
|
||||
private readonly PaymentMethodHandlerDictionary _handlers;
|
||||
private readonly BTCPayNetworkProvider _BtcPayNetworkProvider;
|
||||
private IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UIMoneroLikeStoreController(MoneroLikeConfiguration moneroLikeConfiguration,
|
||||
StoreRepository storeRepository, MoneroRPCProvider moneroRpcProvider,
|
||||
PaymentMethodHandlerDictionary handlers,
|
||||
BTCPayNetworkProvider btcPayNetworkProvider)
|
||||
IStringLocalizer stringLocalizer)
|
||||
{
|
||||
_MoneroLikeConfiguration = moneroLikeConfiguration;
|
||||
_StoreRepository = storeRepository;
|
||||
_MoneroRpcProvider = moneroRpcProvider;
|
||||
_handlers = handlers;
|
||||
_BtcPayNetworkProvider = btcPayNetworkProvider;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
public StoreData StoreData => HttpContext.GetStoreData();
|
||||
@ -178,7 +177,7 @@ namespace BTCPayServer.Services.Altcoins.Monero.UI
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.AccountIndex), "Could not create a new account.");
|
||||
ModelState.AddModelError(nameof(viewModel.AccountIndex), StringLocalizer["Could not create a new account."]);
|
||||
}
|
||||
|
||||
}
|
||||
@ -187,12 +186,12 @@ namespace BTCPayServer.Services.Altcoins.Monero.UI
|
||||
var valid = true;
|
||||
if (viewModel.WalletFile == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.WalletFile), "Please select the view-only wallet file");
|
||||
ModelState.AddModelError(nameof(viewModel.WalletFile), StringLocalizer["Please select the view-only wallet file"]);
|
||||
valid = false;
|
||||
}
|
||||
if (viewModel.WalletKeysFile == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.WalletKeysFile), "Please select the view-only wallet keys file");
|
||||
ModelState.AddModelError(nameof(viewModel.WalletKeysFile), StringLocalizer["Please select the view-only wallet keys file"]);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
@ -202,10 +201,10 @@ namespace BTCPayServer.Services.Altcoins.Monero.UI
|
||||
{
|
||||
if (summary.WalletAvailable)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = $"There is already an active wallet configured for {cryptoCode}. Replacing it would break any existing invoices!"
|
||||
Message = StringLocalizer["There is already an active wallet configured for {0}. Replacing it would break any existing invoices!", cryptoCode].Value
|
||||
});
|
||||
return RedirectToAction(nameof(GetStoreMoneroLikePaymentMethod),
|
||||
new { cryptoCode });
|
||||
@ -266,14 +265,14 @@ namespace BTCPayServer.Services.Altcoins.Monero.UI
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.AccountIndex), $"Could not open the wallet: {ex.Message}");
|
||||
ModelState.AddModelError(nameof(viewModel.AccountIndex), StringLocalizer["Could not open the wallet: {0}", ex.Message]);
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Info,
|
||||
Message = $"View-only wallet files uploaded. The wallet will soon become available."
|
||||
Message = StringLocalizer["View-only wallet files uploaded. The wallet will soon become available."].Value
|
||||
});
|
||||
return RedirectToAction(nameof(GetStoreMoneroLikePaymentMethod), new { cryptoCode });
|
||||
}
|
||||
|
@ -12,10 +12,8 @@ using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Filters;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Bitcoin;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.Altcoins.Zcash.Configuration;
|
||||
using BTCPayServer.Services.Altcoins.Zcash.Payments;
|
||||
using BTCPayServer.Services.Altcoins.Zcash.RPC.Models;
|
||||
@ -26,6 +24,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace BTCPayServer.Services.Altcoins.Zcash.UI
|
||||
{
|
||||
@ -40,15 +39,18 @@ namespace BTCPayServer.Services.Altcoins.Zcash.UI
|
||||
private readonly StoreRepository _StoreRepository;
|
||||
private readonly ZcashRPCProvider _ZcashRpcProvider;
|
||||
private readonly PaymentMethodHandlerDictionary _handlers;
|
||||
private IStringLocalizer StringLocalizer { get; }
|
||||
|
||||
public UIZcashLikeStoreController(ZcashLikeConfiguration ZcashLikeConfiguration,
|
||||
StoreRepository storeRepository, ZcashRPCProvider ZcashRpcProvider,
|
||||
PaymentMethodHandlerDictionary handlers)
|
||||
PaymentMethodHandlerDictionary handlers,
|
||||
IStringLocalizer stringLocalizer)
|
||||
{
|
||||
_ZcashLikeConfiguration = ZcashLikeConfiguration;
|
||||
_StoreRepository = storeRepository;
|
||||
_ZcashRpcProvider = ZcashRpcProvider;
|
||||
_handlers = handlers;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
public StoreData StoreData => HttpContext.GetStoreData();
|
||||
@ -151,7 +153,7 @@ namespace BTCPayServer.Services.Altcoins.Zcash.UI
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.AccountIndex), "Could not create new account.");
|
||||
ModelState.AddModelError(nameof(viewModel.AccountIndex), StringLocalizer["Could not create new account."]);
|
||||
}
|
||||
|
||||
}
|
||||
@ -160,12 +162,12 @@ namespace BTCPayServer.Services.Altcoins.Zcash.UI
|
||||
var valid = true;
|
||||
if (viewModel.WalletFile == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.WalletFile), "Please select the wallet file");
|
||||
ModelState.AddModelError(nameof(viewModel.WalletFile), StringLocalizer["Please select the wallet file"]);
|
||||
valid = false;
|
||||
}
|
||||
if (viewModel.WalletKeysFile == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.WalletKeysFile), "Please select the wallet.keys file");
|
||||
ModelState.AddModelError(nameof(viewModel.WalletKeysFile), StringLocalizer["Please select the wallet.keys file"]);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
@ -175,10 +177,10 @@ namespace BTCPayServer.Services.Altcoins.Zcash.UI
|
||||
{
|
||||
if (summary.WalletAvailable)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = $"There is already an active wallet configured for {cryptoCode}. Replacing it would break any existing invoices"
|
||||
Message = StringLocalizer["There is already an active wallet configured for {0}. Replacing it would break any existing invoices!", cryptoCode].Value
|
||||
});
|
||||
return RedirectToAction(nameof(GetStoreZcashLikePaymentMethod),
|
||||
new { cryptoCode });
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,10 @@
|
||||
#nullable enable
|
||||
using System.Collections;
|
||||
using System.Collections.Frozen;
|
||||
using Dapper;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
using YamlDotNet.Core.Tokens;
|
||||
|
||||
namespace BTCPayServer.Services
|
||||
{
|
||||
|
@ -14,10 +14,10 @@
|
||||
{
|
||||
Title(line.Network.CryptoCode, "disabled");
|
||||
<ul>
|
||||
<li>The node is offline</li>
|
||||
<li text-translate="true">The node is offline</li>
|
||||
@if (line.Error != null)
|
||||
{
|
||||
<li>Last error: @line.Error</li>
|
||||
<li>@StringLocalizer["Last error:"] @line.Error</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
@ -29,19 +29,19 @@
|
||||
{
|
||||
Title(line.Network.CryptoCode, "pending");
|
||||
<ul>
|
||||
<li>NBXplorer headers height: @line.Status.ChainHeight</li>
|
||||
<li>The node is starting...</li>
|
||||
<li>@StringLocalizer["NBXplorer headers height: {0}", line.Status.ChainHeight]</li>
|
||||
<li text-translate="true">The node is starting...</li>
|
||||
</ul>
|
||||
}
|
||||
else
|
||||
{
|
||||
Title(line.Network.CryptoCode, "disabled");
|
||||
<ul>
|
||||
<li>NBXplorer headers height: @line.Status.ChainHeight</li>
|
||||
<li>The node is offline</li>
|
||||
<li>@StringLocalizer["NBXplorer headers height: {0}", line.Status.ChainHeight]</li>
|
||||
<li text-translate="true">The node is offline</li>
|
||||
@if (line.Error != null)
|
||||
{
|
||||
<li>Last error: @line.Error</li>
|
||||
<li>@StringLocalizer["Last error:"] line.Error</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
@ -50,12 +50,12 @@
|
||||
{
|
||||
Title(line.Network.CryptoCode, "enabled");
|
||||
<ul>
|
||||
<li>The node is synchronized (Height: @line.Status.BitcoinStatus.Headers)</li>
|
||||
<li>@StringLocalizer["The node is synchronized (Height: {0})", line.Status.BitcoinStatus.Headers]</li>
|
||||
@if (line.Status.BitcoinStatus.IsSynched &&
|
||||
line.Status.SyncHeight.HasValue &&
|
||||
line.Status.SyncHeight.Value < line.Status.BitcoinStatus.Headers)
|
||||
{
|
||||
<li>NBXplorer is synchronizing... (Height: @line.Status.SyncHeight.Value)</li>
|
||||
<li>@StringLocalizer["NBXplorer is synchronizing... (Height: {0})", line.Status.SyncHeight.Value]</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
@ -63,8 +63,8 @@
|
||||
{
|
||||
Title(line.Network.CryptoCode, "enabled");
|
||||
<ul>
|
||||
<li>Node headers height: @line.Status.BitcoinStatus.Headers</li>
|
||||
<li>Validated blocks: @line.Status.BitcoinStatus.Blocks</li>
|
||||
<li>@StringLocalizer["Node headers height: {0}", line.Status.BitcoinStatus.Headers]</li>
|
||||
<li>@StringLocalizer["Validated blocks: {0}", line.Status.BitcoinStatus.Blocks]</li>
|
||||
</ul>
|
||||
}
|
||||
@if (!line.Status.IsFullySynched && line.Status.BitcoinStatus != null)
|
||||
|
@ -28,12 +28,12 @@
|
||||
if (onChainPaymentData.PayjoinInformation is PayjoinInformation pj)
|
||||
{
|
||||
payjoinInformation = pj;
|
||||
m.AdditionalInformation = "Original transaction";
|
||||
m.AdditionalInformation = StringLocalizer["Original transaction"];
|
||||
}
|
||||
if (payjoinInformation is PayjoinInformation &&
|
||||
payjoinInformation.CoinjoinTransactionHash == onChainPaymentData?.Outpoint.Hash)
|
||||
{
|
||||
m.AdditionalInformation = "Payjoin transaction";
|
||||
m.AdditionalInformation = StringLocalizer["Payjoin transaction"];
|
||||
}
|
||||
m.TransactionId = onChainPaymentData.Outpoint.Hash.ToString();
|
||||
m.ReceivedTime = payment.ReceivedTime;
|
||||
@ -54,26 +54,26 @@
|
||||
{
|
||||
var hasNetworkFee = payments.Sum(a => a.NetworkFee) > 0;
|
||||
<section>
|
||||
<h5>On-Chain Payments</h5>
|
||||
<h5 text-translate="true">On-Chain Payments</h5>
|
||||
<div class="invoice-payments table-responsive mt-0">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-75px">Payment Method</th>
|
||||
<th class="w-100px">Index</th>
|
||||
<th class="w-175px">Destination</th>
|
||||
<th class="text-nowrap">Payment Proof</th>
|
||||
<th text-translate="true" class="w-75px">Payment Method</th>
|
||||
<th text-translate="true" class="w-100px">Index</th>
|
||||
<th text-translate="true" class="w-175px">Destination</th>
|
||||
<th text-translate="true" class="text-nowrap">Payment Proof</th>
|
||||
@if (hasNetworkFee)
|
||||
{
|
||||
<th class="text-end">
|
||||
Network Fee
|
||||
<span text-translate="true">Network Fee</span>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#allow-anyone-to-create-invoice" target="_blank" rel="noreferrer noopener" title="More information...">
|
||||
<vc:icon symbol="info" />
|
||||
</a>
|
||||
</th>
|
||||
}
|
||||
<th class="text-end">Confirmations</th>
|
||||
<th class="w-150px text-end">Paid</th>
|
||||
<th text-translate="true" class="text-end">Confirmations</th>
|
||||
<th text-translate="true" class="w-150px text-end">Paid</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -81,7 +81,7 @@
|
||||
{
|
||||
<tr style="@(payment.Replaced ? "text-decoration: line-through" : "")">
|
||||
<td>@payment.PaymentMethodId</td>
|
||||
<td>@(payment.CryptoPaymentData.KeyPath?.ToString()?? "Unknown")</td>
|
||||
<td>@(payment.CryptoPaymentData.KeyPath?.ToString()?? StringLocalizer["Unknown"])</td>
|
||||
<td>
|
||||
<vc:truncate-center text="@payment.DepositAddress.ToString()" classes="truncate-center-id" />
|
||||
</td>
|
||||
|
@ -24,7 +24,7 @@
|
||||
<li class="nav-item" not-permission="@Policies.CanModifyStoreSettings" permission="@Policies.CanViewStoreSettings">
|
||||
<span class="nav-link">
|
||||
<vc:icon symbol="nav-crowdfund" />
|
||||
<span>Crowdfund</span>
|
||||
<span text-translate="true">Crowdfund</span>
|
||||
</span>
|
||||
</li>
|
||||
}
|
||||
|
@ -34,15 +34,16 @@
|
||||
|
||||
if (item.PriceType == ViewPointOfSaleViewModel.ItemPriceType.Minimum)
|
||||
{
|
||||
@Safe.Raw("or more")
|
||||
@Safe.Raw(StringLocalizer["or more"])
|
||||
}
|
||||
}
|
||||
else if (item.PriceType == ViewPointOfSaleViewModel.ItemPriceType.Topup )
|
||||
{
|
||||
@Safe.Raw("Any amount")
|
||||
}else if (item.PriceType == ViewPointOfSaleViewModel.ItemPriceType.Fixed)
|
||||
@Safe.Raw(StringLocalizer["Any amount"])
|
||||
}
|
||||
else if (item.PriceType == ViewPointOfSaleViewModel.ItemPriceType.Fixed)
|
||||
{
|
||||
@Safe.Raw("Free")
|
||||
@Safe.Raw(StringLocalizer["Free"])
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
@ -56,10 +57,10 @@
|
||||
case null:
|
||||
break;
|
||||
case <= 0:
|
||||
<span>Sold out</span>
|
||||
<span text-translate="true">Sold out</span>
|
||||
break;
|
||||
default:
|
||||
<span>@item.Inventory left</span>
|
||||
<span>@StringLocalizer["{0} left", item.Inventory]</span>
|
||||
break;
|
||||
}
|
||||
@if (hasCount)
|
||||
@ -91,6 +92,6 @@
|
||||
<span asp-validation-for="Amount" class="text-danger"></span>
|
||||
</div>
|
||||
<input type="hidden" asp-for="RedirectToCheckout"/>
|
||||
<button type="submit" class="btn btn-primary">Contribute</button>
|
||||
<button type="submit" class="btn btn-primary" text-translate="true">Contribute</button>
|
||||
}
|
||||
</form>
|
||||
|
@ -40,8 +40,8 @@
|
||||
<body class="min-vh-100 p-2">
|
||||
@if (!Model.Enabled)
|
||||
{
|
||||
<div class="alert alert-warning text-center sticky-top mb-0 rounded-0" role="alert">
|
||||
This crowdfund page is not publically viewable!
|
||||
<div class="alert alert-warning text-center sticky-top mb-0 rounded-0" role="alert" text-translate="true">
|
||||
This crowdfund page is not publicly viewable!
|
||||
</div>
|
||||
}
|
||||
@if (Model.AnimationsEnabled)
|
||||
@ -71,19 +71,19 @@
|
||||
<span v-if="srvModel.resetEvery !== 'Never'"
|
||||
class="h5 ms-2"
|
||||
v-b-tooltip
|
||||
:title="'Goal resets every ' + srvModel.resetEveryAmount + ' ' + srvModel.resetEvery + ((srvModel.resetEveryAmount>1)?'s': '')">
|
||||
:title="'Goal resets every ' + srvModel.resetEveryAmount + ' ' + srvModel.resetEvery + ((srvModel.resetEveryAmount>1)?'s': '')" text-translate="true">
|
||||
Dynamic
|
||||
</span>
|
||||
}
|
||||
@if (Model.EnforceTargetAmount)
|
||||
{
|
||||
<span v-if="srvModel.enforceTargetAmount" class="h5 ms-2" v-b-tooltip title="No contributions allowed after the goal has been reached">
|
||||
<span v-if="srvModel.enforceTargetAmount" class="h5 ms-2" v-b-tooltip title=@StringLocalizer["No contributions allowed after the goal has been reached"] text-translate="true">
|
||||
Hardcap Goal
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span v-if="!srvModel.enforceTargetAmount" class="h5 ms-2" v-b-tooltip title="Contributions allowed even after goal is reached">
|
||||
<span v-if="!srvModel.enforceTargetAmount" class="h5 ms-2" v-b-tooltip title=@StringLocalizer["Contributions allowed even after goal is reached"] text-translate="true">
|
||||
Softcap Goal
|
||||
</span>
|
||||
}
|
||||
@ -92,18 +92,18 @@
|
||||
@if (!Model.Started && Model.StartDate.HasValue)
|
||||
{
|
||||
<h6 class="text-muted fst-italic mt-3" v-if="!started && srvModel.startDate" v-b-tooltip :title="startDate" v-text="`Starts in ${startDiff}`" data-test="time-state">
|
||||
Starts @TimeZoneInfo.ConvertTimeFromUtc(Model.StartDate.Value, TimeZoneInfo.Local)
|
||||
@StringLocalizer["Starts {0}", TimeZoneInfo.ConvertTimeFromUtc(Model.StartDate.Value, TimeZoneInfo.Local)]
|
||||
</h6>
|
||||
}
|
||||
else if (Model.Started && !Model.Ended && Model.EndDate.HasValue)
|
||||
{
|
||||
<h6 class="text-muted fst-italic mt-3" v-if="started && !ended && srvModel.endDate" v-b-tooltip :title="endDate" v-text="`Ends in ${endDiff}`" data-test="time-state">
|
||||
Ends @TimeZoneInfo.ConvertTimeFromUtc(Model.EndDate.Value, TimeZoneInfo.Local)
|
||||
@StringLocalizer["Ends {0}", TimeZoneInfo.ConvertTimeFromUtc(Model.EndDate.Value, TimeZoneInfo.Local)]
|
||||
</h6>
|
||||
}
|
||||
else if (Model.Started && !Model.Ended && !Model.EndDate.HasValue)
|
||||
{
|
||||
<h6 class="text-muted fst-italic mt-3" v-if="started && !ended && !srvModel.endDate" v-b-tooltip title="No set end date" data-test="time-state">
|
||||
<h6 class="text-muted fst-italic mt-3" v-if="started && !ended && !srvModel.endDate" v-b-tooltip title="No set end date" data-test="time-state" text-translate="true">
|
||||
Currently active!
|
||||
</h6>
|
||||
}
|
||||
@ -160,7 +160,7 @@
|
||||
|
||||
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-total-contributors">
|
||||
<h3 v-text="new Intl.NumberFormat().format(srvModel.info.totalContributors)">@Model.Info.TotalContributors</h3>
|
||||
<h5 class="text-muted fst-italic mb-0">Contributors</h5>
|
||||
<h5 class="text-muted fst-italic mb-0" text-translate="true">Contributors</h5>
|
||||
</div>
|
||||
|
||||
@if (Model.StartDate.HasValue || Model.EndDate.HasValue)
|
||||
@ -170,20 +170,20 @@
|
||||
{
|
||||
<div v-if="startDiff">
|
||||
<h3 v-text="startDiff">@TimeZoneInfo.ConvertTimeFromUtc(Model.StartDate.Value, TimeZoneInfo.Local)</h3>
|
||||
<h5 class="text-muted fst-italic mb-0" v-text="'Left to start'">Start Date</h5>
|
||||
<h5 class="text-muted fst-italic mb-0" v-text="'Left to start'" text-translate="true">Start Date</h5>
|
||||
</div>
|
||||
}
|
||||
else if (Model.Started && !Model.Ended && Model.EndDate.HasValue)
|
||||
{
|
||||
<div v-if="!startDiff && endDiff">
|
||||
<h3 v-text="endDiff">@TimeZoneInfo.ConvertTimeFromUtc(Model.EndDate.Value, TimeZoneInfo.Local)</h3>
|
||||
<h5 class="text-muted fst-italic mb-0" v-text="'Left'">End Date</h5>
|
||||
<h5 class="text-muted fst-italic mb-0" v-text="'Left'" text-translate="true">End Date</h5>
|
||||
</div>
|
||||
}
|
||||
else if (Model.Ended)
|
||||
{
|
||||
<div v-if="ended">
|
||||
<h3 class="mb-0">Campaign not active</h3>
|
||||
<h3 class="mb-0" text-translate="true">Campaign not active</h3>
|
||||
</div>
|
||||
}
|
||||
<b-tooltip v-if="startDate || endDate" target="crowdfund-body-campaign-dates" class="only-for-js">
|
||||
@ -191,13 +191,13 @@
|
||||
@if (Model.StartDate.HasValue)
|
||||
{
|
||||
<li v-if="startDate" class="list-unstyled">
|
||||
{{started? "Started" : "Starts"}} {{startDate}}
|
||||
{{started ? "Started" : "Starts"}} {{startDate}}
|
||||
</li>
|
||||
}
|
||||
@if (Model.EndDate.HasValue)
|
||||
{
|
||||
<li v-if="endDate" class="list-unstyled">
|
||||
{{ended? "Ended" : "Ends"}} {{endDate}}
|
||||
{{ended ? "Ended" : "Ends"}} {{endDate}}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
@ -207,7 +207,7 @@
|
||||
</div>
|
||||
|
||||
<div class="text-center mb-4" id="crowdfund-body-header">
|
||||
<button v-if="active" id="crowdfund-body-header-cta" class="btn btn-lg btn-primary py-2 px-5 only-for-js" v-on:click="contribute">Contribute</button>
|
||||
<button v-if="active" id="crowdfund-body-header-cta" class="btn btn-lg btn-primary py-2 px-5 only-for-js" v-on:click="contribute" text-translate="true">Contribute</button>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4 justify-content-between gap-5">
|
||||
@ -245,10 +245,7 @@
|
||||
<div class="overflow-hidden">@Safe.Raw(Model.Description)</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<partial
|
||||
name="Crowdfund/Public/ContributeForm"
|
||||
model="@(new ContributeToCrowdfund { ViewCrowdfundViewModel = Model, RedirectToCheckout = true })">
|
||||
</partial>
|
||||
<partial name="Crowdfund/Public/ContributeForm" model="@(new ContributeToCrowdfund { ViewCrowdfundViewModel = Model, RedirectToCheckout = true })" />
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
@ -264,7 +261,7 @@
|
||||
<footer class="store-footer">
|
||||
<p class="text-muted" v-text="`Updated ${lastUpdated}`">Updated @Model.Info.LastUpdated</p>
|
||||
<a class="store-powered-by" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
|
||||
Powered by <partial name="_StoreFooterLogo" />
|
||||
<span text-translate="true">Powered by</span> <partial name="_StoreFooterLogo" />
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
@ -300,7 +297,7 @@
|
||||
</span>
|
||||
<div class="perk-zoom" v-if="canExpand">
|
||||
<div class="perk-zoom-bg"></div>
|
||||
<div class="perk-zoom-text w-100 py-2 px-4 text-center text-primary fw-semibold fs-5 lh-sm">
|
||||
<div class="perk-zoom-text w-100 py-2 px-4 text-center text-primary fw-semibold fs-5 lh-sm" text-translate="true">
|
||||
Select this contribution perk
|
||||
</div>
|
||||
</div>
|
||||
@ -311,17 +308,15 @@
|
||||
<div class="card-title d-flex justify-content-between" :class="{ 'mb-0': !perk.description }">
|
||||
<span class="h5" :class="{ 'mb-0': !perk.description }">{{perk.title ? perk.title : perk.id}}</span>
|
||||
<span class="text-muted">
|
||||
|
||||
<template v-if="perk.priceType === 'Fixed' && amount ==0">
|
||||
<template v-if="perk.priceType === 'Fixed' && amount == 0" text-translate="true">
|
||||
Free
|
||||
</template>
|
||||
<template v-else-if="amount">
|
||||
{{formatAmount(perk.price.noExponents(), srvModel.currencyData.divisibility)}}
|
||||
{{targetCurrency}}
|
||||
<template v-if="perk.price.type === 'Minimum'">or more</template>
|
||||
<template v-if="perk.price.type === 'Minimum'" text-translate="true">or more</template>
|
||||
</template>
|
||||
|
||||
<template v-else-if="perk.priceType === 'Topup' || (!amount && perk.priceType === 'Minimum')">
|
||||
<template v-else-if="perk.priceType === 'Topup' || (!amount && perk.priceType === 'Minimum')" text-translate="true">
|
||||
Any amount
|
||||
</template>
|
||||
</span>
|
||||
@ -343,7 +338,7 @@
|
||||
:class="{'btn-disabled': loading}"
|
||||
type="submit">
|
||||
<div v-if="loading" class="spinner-grow spinner-grow-sm me-2" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
<span class="visually-hidden" text-translate="true">Loading...</span>
|
||||
</div>
|
||||
{{perk.buyButtonText || 'Continue'}}
|
||||
</button>
|
||||
@ -361,7 +356,7 @@
|
||||
|
||||
<template id="contribute-template">
|
||||
<div>
|
||||
<h3 v-if="!inModal" class="mb-3">Contribute</h3>
|
||||
<h3 v-if="!inModal" class="mb-3" text-translate="true">Contribute</h3>
|
||||
<perks :perks="perks"
|
||||
:loading="loading"
|
||||
:in-modal="inModal"
|
||||
|
@ -109,7 +109,7 @@
|
||||
<div>
|
||||
<label asp-for="Enabled" class="form-check-label"></label>
|
||||
<span asp-validation-for="Enabled" class="text-danger"></span>
|
||||
<div class="text-muted">The crowdfund will be visible to anyone.</div>
|
||||
<div class="text-muted" text-translate="true">The crowdfund will be visible to anyone.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -173,9 +173,9 @@
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<input asp-for="IsRecurring" type="checkbox" class="btcpay-toggle me-3" data-bs-toggle="collapse" data-bs-target="#ResetEverySettings" aria-expanded="@(Model.IsRecurring)" aria-controls="ResetEverySettings" />
|
||||
<div>
|
||||
<label asp-for="IsRecurring" class="form-check-label">Recurring Goal</label>
|
||||
<label asp-for="IsRecurring" class="form-check-label" text-translate="true">Recurring Goal</label>
|
||||
<span asp-validation-for="IsRecurring" class="text-danger"></span>
|
||||
<div class="text-muted">Reset goal after a specific period of time, based on your crowdfund's start date.</div>
|
||||
<div class="text-muted" text-translate="true">Reset goal after a specific period of time, based on your crowdfund's start date.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -204,7 +204,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-8 col-xxl-constrain">
|
||||
<h3 class="mt-5 mb-4">Contributions</h3>
|
||||
<h3 class="mt-5 mb-4" text-translate="true">Contributions</h3>
|
||||
<div class="d-flex mb-3">
|
||||
<input asp-for="SortPerksByPopularity" type="checkbox" class="btcpay-toggle me-3" />
|
||||
<label asp-for="SortPerksByPopularity" class="form-check-label"></label>
|
||||
@ -226,27 +226,27 @@
|
||||
<span asp-validation-for="EnforceTargetAmount" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<h3 class="mt-5 mb-4">Crowdfund Behavior</h3>
|
||||
<h3 class="mt-5 mb-4" text-translate="true">Crowdfund Behavior</h3>
|
||||
<div class="d-flex">
|
||||
<input asp-for="UseAllStoreInvoices" type="checkbox" class="btcpay-toggle me-3" />
|
||||
<label asp-for="UseAllStoreInvoices" class="form-check-label"></label>
|
||||
<span asp-validation-for="UseAllStoreInvoices" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<h3 class="mt-5 mb-4">Checkout</h3>
|
||||
<h3 class="mt-5 mb-4" text-translate="true">Checkout</h3>
|
||||
<div class="form-group">
|
||||
<label asp-for="FormId" class="form-label"></label>
|
||||
<select asp-for="FormId" class="form-select w-auto" asp-items="@checkoutFormOptions"></select>
|
||||
<span asp-validation-for="FormId" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<h3 class="mt-5 mb-2">Additional Options</h3>
|
||||
<h3 class="mt-5 mb-2" text-translate="true">Additional Options</h3>
|
||||
<div class="form-group">
|
||||
<div class="accordion" id="additional">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-sound-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-sound" aria-expanded="false" aria-controls="additional-sound">
|
||||
Sound
|
||||
<span text-translate="true">Sound</span>
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
@ -272,7 +272,7 @@
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-animation-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-animation" aria-expanded="false" aria-controls="additional-animation">
|
||||
Animation
|
||||
<span text-translate="true">Animation</span>
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
|
@ -12,9 +12,9 @@
|
||||
}
|
||||
<div class="form-group">
|
||||
<div class="d-flex flex-wrap gap-2 align-items-center justify-content-between">
|
||||
<label asp-for="Settings.Server" class="form-label">SMTP Server</label>
|
||||
<label asp-for="Settings.Server" class="form-label" text-translate="true">SMTP Server</label>
|
||||
<div class="dropdown only-for-js mt-n2" id="quick-fill">
|
||||
<button class="btn btn-link p-0 dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="QuickFillDropdownToggle">
|
||||
<button class="btn btn-link p-0 dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="QuickFillDropdownToggle" text-translate="true">
|
||||
Quick Fill
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="QuickFillDropdownToggle">
|
||||
@ -30,31 +30,30 @@
|
||||
<span asp-validation-for="Settings.Server" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.Port" class="form-label"></label>
|
||||
<label asp-for="Settings.Port" class="form-label" text-translate="true">Port</label>
|
||||
<input asp-for="Settings.Port" data-fill="port" class="form-control"/>
|
||||
<span asp-validation-for="Settings.Port" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.From" class="form-label">Sender's Email Address</label>
|
||||
<label asp-for="Settings.From" class="form-label" text-translate="true">Sender's Email Address</label>
|
||||
<input asp-for="Settings.From" class="form-control" placeholder="Firstname Lastname <email@example.com>" />
|
||||
<span asp-validation-for="Settings.From" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.Login" class="form-label"></label>
|
||||
<label asp-for="Settings.Login" class="form-label" text-translate="true">Login</label>
|
||||
<input asp-for="Settings.Login" class="form-control"/>
|
||||
<div class="form-text">For many email providers (like Gmail) your login is your email address.</div>
|
||||
<div class="form-text" text-translate="true">For many email providers (like Gmail) your login is your email address.</div>
|
||||
<span asp-validation-for="Settings.Login" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group" permission="@(Model is ServerEmailsViewModel ? Policies.CanModifyServerSettings : Policies.CanModifyStoreSettings)">
|
||||
<label asp-for="Settings.Password" class="form-label" text-translate="true">Password</label>
|
||||
@if (!Model.PasswordSet)
|
||||
{
|
||||
<label asp-for="Settings.Password" class="form-label"></label>
|
||||
<input asp-for="Settings.Password" type="password" class="form-control"/>
|
||||
<span asp-validation-for="Settings.Password" class="text-danger"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<label asp-for="Settings.Password" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input value="Configured" type="text" readonly class="form-control"/>
|
||||
<button type="submit" class="btn btn-danger" name="command" value="ResetPassword" id="ResetPassword">Reset</button>
|
||||
@ -65,13 +64,13 @@
|
||||
<div class="my-4">
|
||||
<button class="d-inline-flex align-items-center btn btn-link text-primary fw-semibold p-0" type="button" id="AdvancedSettingsButton" data-bs-toggle="collapse" data-bs-target="#AdvancedSettings" aria-expanded="false" aria-controls="AdvancedSettings">
|
||||
<vc:icon symbol="caret-down"/>
|
||||
<span class="ms-1">Advanced settings</span>
|
||||
<span class="ms-1" text-translate="true">Advanced settings</span>
|
||||
</button>
|
||||
<div id="AdvancedSettings" class="collapse">
|
||||
<div class="pt-3 pb-1">
|
||||
<div class="d-flex">
|
||||
<input asp-for="Settings.EnabledCertificateCheck" type="checkbox" class="btcpay-toggle me-3" />
|
||||
<label asp-for="Settings.EnabledCertificateCheck" class="form-check-label">TLS certificate security checks</label>
|
||||
<label asp-for="Settings.EnabledCertificateCheck" class="form-check-label" text-translate="true">TLS certificate security checks</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,13 +1,13 @@
|
||||
@model BTCPayServer.Models.EmailsViewModel
|
||||
|
||||
<h3 class="my-3">Testing</h3>
|
||||
<h3 class="my-3" text-translate="true">Testing</h3>
|
||||
<div class="row">
|
||||
<div class="col-xl-10 col-xxl-constrain">
|
||||
<div class="form-group">
|
||||
<label asp-for="TestEmail" class="form-label">To test your settings, enter an email address</label>
|
||||
<label asp-for="TestEmail" class="form-label" text-translate="true">To test your settings, enter an email address</label>
|
||||
<input asp-for="TestEmail" placeholder="Firstname Lastname <email@example.com>" class="form-control" />
|
||||
<span asp-validation-for="TestEmail" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary mt-2" name="command" value="Test" id="Test">Send Test Email</button>
|
||||
<button type="submit" class="btn btn-secondary mt-2" name="command" value="Test" id="Test" text-translate="true">Send Test Email</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -42,17 +42,17 @@
|
||||
asp-route-sortOrder="@(nextRoleSortOrder ?? "asc")"
|
||||
class="text-nowrap"
|
||||
title="@(nextRoleSortOrder == "desc" ? sortByAsc : sortByDesc)">
|
||||
Role
|
||||
<span text-translate="true">Role</span>
|
||||
<vc:icon symbol="actions-sort-alpha-@(roleSortOrder ?? nextRoleSortOrder ?? "desc")" />
|
||||
</a>
|
||||
</th>
|
||||
<th>Scope</th>
|
||||
<th>Permissions</th>
|
||||
<th text-translate="true">Scope</th>
|
||||
<th text-translate="true">Permissions</th>
|
||||
@if (showInUseColumn)
|
||||
{
|
||||
<th class="text-center w-75px">In use</th>
|
||||
<th class="text-center w-75px" text-translate="true">In use</th>
|
||||
}
|
||||
<th class="actions-col" permission="@permission">Actions</th>
|
||||
<th class="actions-col" permission="@permission" text-translate="true">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -64,7 +64,7 @@
|
||||
<span>@role.Role</span>
|
||||
@if (Model.DefaultRole == role.Id)
|
||||
{
|
||||
<span class="badge bg-info">
|
||||
<span class="badge bg-info" text-translate="true">
|
||||
Default
|
||||
</span>
|
||||
}
|
||||
@ -73,13 +73,13 @@
|
||||
<td>
|
||||
@if (role.IsServerRole)
|
||||
{
|
||||
<span class="badge bg-dark">
|
||||
<span class="badge bg-dark" text-translate="true">
|
||||
Server-wide
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-light">
|
||||
<span class="badge bg-light" text-translate="true">
|
||||
Store-level
|
||||
</span>
|
||||
}
|
||||
@ -89,7 +89,7 @@
|
||||
{
|
||||
<span class="info-note text-warning">
|
||||
<vc:icon symbol="warning"/>
|
||||
No policies
|
||||
<span text-translate="true">No policies</span>
|
||||
</span>
|
||||
}
|
||||
else
|
||||
@ -117,10 +117,10 @@
|
||||
<div class="d-inline-flex align-items-center gap-3">
|
||||
@if (role.IsServerRole && Model.DefaultRole != role.Id)
|
||||
{
|
||||
<a permission="@Policies.CanModifyServerSettings" asp-action="SetDefaultRole" asp-route-role="@role.Role" asp-controller="UIServer" id="SetDefault">Set as default</a>
|
||||
<a permission="@Policies.CanModifyServerSettings" asp-action="SetDefaultRole" asp-route-role="@role.Role" asp-controller="UIServer" id="SetDefault" text-translate="true">Set as default</a>
|
||||
}
|
||||
<a permission="@(role.IsServerRole ? Policies.CanModifyServerSettings : Policies.CanModifyStoreSettings)" asp-action="CreateOrEditRole" asp-route-storeId="@storeId" asp-route-role="@role.Role" asp-controller="@(role.IsServerRole ? "UIServer" : "UIStores")">Edit</a>
|
||||
<a permission="@(role.IsServerRole ? Policies.CanModifyServerSettings : Policies.CanModifyStoreSettings)" asp-action="DeleteRole" asp-route-storeId="@storeId" asp-route-role="@role.Role" asp-controller="@(role.IsServerRole ? "UIServer" : "UIStores")">Remove</a>
|
||||
<a permission="@(role.IsServerRole ? Policies.CanModifyServerSettings : Policies.CanModifyStoreSettings)" asp-action="CreateOrEditRole" asp-route-storeId="@storeId" asp-route-role="@role.Role" asp-controller="@(role.IsServerRole ? "UIServer" : "UIStores")" text-translate="true">Edit</a>
|
||||
<a permission="@(role.IsServerRole ? Policies.CanModifyServerSettings : Policies.CanModifyStoreSettings)" asp-action="DeleteRole" asp-route-storeId="@storeId" asp-route-role="@role.Role" asp-controller="@(role.IsServerRole ? "UIServer" : "UIStores")" text-translate="true">Remove</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -129,7 +129,7 @@
|
||||
</main>
|
||||
<footer class="store-footer">
|
||||
<a class="store-powered-by" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
|
||||
Powered by <partial name="_StoreFooterLogo" />
|
||||
<span text-translate="true">Powered by</span> <partial name="_StoreFooterLogo" />
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
@ -244,7 +244,7 @@
|
||||
<td colspan="2" class="pt-4">
|
||||
<button id="CartSubmit" class="btn btn-primary btn-lg w-100" :disabled="payButtonLoading" type="submit">
|
||||
<div v-if="payButtonLoading" class="spinner-border spinner-border-sm" id="pay-button-spinner" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
<span class="visually-hidden" text-translate="true">Loading...</span>
|
||||
</div>
|
||||
<template v-else>Pay</template>
|
||||
</button>
|
||||
|
@ -29,7 +29,7 @@
|
||||
}
|
||||
<footer class="store-footer">
|
||||
<a class="store-powered-by" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
|
||||
Powered by <partial name="_StoreFooterLogo" />
|
||||
<span text-translate="true">Powered by</span> <partial name="_StoreFooterLogo" />
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">@Model.CurrencySymbol</span>
|
||||
<input class="form-control" type="number" step="@Model.Step" name="amount" placeholder="Amount">
|
||||
<button class="btn btn-primary" type="submit">Pay</button>
|
||||
<button class="btn btn-primary" type="submit" text-translate="true">Pay</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -133,7 +133,7 @@ else
|
||||
</main>
|
||||
<footer class="store-footer d-print-none">
|
||||
<a class="store-powered-by" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
|
||||
Powered by <partial name="_StoreFooterLogo" />
|
||||
<span text-translate="true">Powered by</span> <partial name="_StoreFooterLogo" />
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<h5 class="modal-title">Recent Transactions</h5>
|
||||
<button type="button" class="btn btn-link px-3 py-0" aria-label="Refresh" v-on:click="loadRecentTransactions" :disabled="recentTransactionsLoading" id="RecentTransactionsRefresh">
|
||||
<vc:icon symbol="actions-refresh"/>
|
||||
<span v-if="recentTransactionsLoading" class="visually-hidden">Loading...</span>
|
||||
<span v-if="recentTransactionsLoading" class="visually-hidden" text-translate="true">Loading...</span>
|
||||
</button>
|
||||
<button type="button" class="btn-close py-3" aria-label="Close" v-on:click="hideRecentTransactions">
|
||||
<vc:icon symbol="close"/>
|
||||
|
@ -55,7 +55,14 @@
|
||||
@if (item.Inventory.HasValue)
|
||||
{
|
||||
<span class="badge text-bg-warning">
|
||||
@(item.Inventory > 0 ? $"{item.Inventory} left" : "Sold out")
|
||||
@if (item.Inventory > 0)
|
||||
{
|
||||
<span text-translate="true">@ViewLocalizer["{0} left", item.Inventory.ToString()]</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span text-translate="true">Sold out</span>
|
||||
}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
@ -88,8 +95,8 @@
|
||||
<div class="col posItem posItem--displayed posItem--last@(Model.Items.Length == 0 ? " posItem--first" : null)">
|
||||
<div class="card h-100 px-0">
|
||||
<div class="card-body p-3 d-flex flex-column gap-2 mb-auto">
|
||||
<h5 class="card-title">Custom Amount</h5>
|
||||
<p class="card-text">Create invoice to pay custom amount</p>
|
||||
<h5 class="card-title" text-translate="true">Custom Amount</h5>
|
||||
<p class="card-text" text-translate="true">Create invoice to pay custom amount</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-0 pb-3">
|
||||
<form method="post" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false" autocomplete="off">
|
||||
@ -107,7 +114,7 @@
|
||||
</main>
|
||||
<footer class="store-footer">
|
||||
<a class="store-powered-by" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
|
||||
Powered by <partial name="_StoreFooterLogo" />
|
||||
<span text-translate="true">Powered by</span> <partial name="_StoreFooterLogo" />
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
|
@ -69,9 +69,11 @@
|
||||
</div>
|
||||
<button class="btn btn-lg btn-primary mx-3" type="submit" :disabled="payButtonLoading || totalNumeric <= 0" id="pay-button">
|
||||
<div v-if="payButtonLoading" class="spinner-border spinner-border-sm" id="pay-button-spinner" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
<span class="visually-hidden" text-translate="true">Loading...</span>
|
||||
</div>
|
||||
<template v-else>Charge</template>
|
||||
<template v-else>
|
||||
<span text-translate="true">Charge</span>
|
||||
</template>
|
||||
</button>
|
||||
<partial name="PointOfSale/Public/RecentTransactions" model="Model"/>
|
||||
<button type="button" class="btn btn-link p-1" data-bs-toggle="modal" data-bs-target="#RecentTransactions" id="RecentTransactionsToggle" permission="@Policies.CanViewInvoices">
|
||||
|
@ -7,11 +7,11 @@
|
||||
|
||||
@if (Model.StoreId is not null)
|
||||
{
|
||||
<h1 text-translate="true">@ViewLocalizer["Store: {0}", @Model.StoreId]</h1>
|
||||
<h1 text-translate="true">@ViewLocalizer["Store: {0}", Model.StoreId]</h1>
|
||||
}
|
||||
else
|
||||
{
|
||||
<h1 text-translate="true">No scope</h1>
|
||||
<h1 text-translate="true" text-translate="true">No scope</h1>
|
||||
}
|
||||
|
||||
<ul>
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
@if (isEmailConfigured)
|
||||
{
|
||||
<p>
|
||||
<p text-translate="true">
|
||||
We all forget passwords every now and then. Just provide email address tied to
|
||||
your account and we'll start the process of helping you recover your account.
|
||||
</p>
|
||||
@ -27,13 +27,13 @@
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mt-4">
|
||||
<button type="submit" class="btn btn-primary btn-lg w-100">Submit</button>
|
||||
<button type="submit" class="btn btn-primary btn-lg w-100" text-translate="true">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>Email password reset functionality is not configured for this server. Please contact the server administrator to assist with account recovery.</p>
|
||||
<p text-translate="true">Email password reset functionality is not configured for this server. Please contact the server administrator to assist with account recovery.</p>
|
||||
<p>
|
||||
If you are the administrator, please follow these steps to
|
||||
<a href="https://docs.btcpayserver.org/Notifications/#smtp-email-setup" target="_blank" rel="noreferrer noopener">configure email password resets</a>
|
||||
@ -43,5 +43,5 @@ else
|
||||
}
|
||||
|
||||
<p class="text-center mt-2 mb-0">
|
||||
<a id="Login" style="font-size:1.15rem" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]">Log in</a>
|
||||
<a id="Login" style="font-size:1.15rem" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" text-translate="true">Log in</a>
|
||||
</p>
|
||||
|
@ -3,8 +3,8 @@
|
||||
Layout = "_LayoutSignedOut";
|
||||
}
|
||||
|
||||
<p>Please check your email to reset your password.</p>
|
||||
<p text-translate="true">Please check your email to reset your password.</p>
|
||||
|
||||
<p class="text-center mt-2 mb-0">
|
||||
<a id="Login" style="font-size:1.15rem" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]">Log in</a>
|
||||
<a id="Login" style="font-size:1.15rem" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" text-translate="true">Log in</a>
|
||||
</p>
|
||||
|
@ -6,13 +6,13 @@
|
||||
|
||||
@if (Model is null)
|
||||
{
|
||||
<p class="mb-0">This account has been locked out because of multiple invalid login attempts. Please try again later.</p>
|
||||
<p class="mb-0" text-translate="true">This account has been locked out because of multiple invalid login attempts. Please try again later.</p>
|
||||
}
|
||||
else if (DateTimeOffset.MaxValue - Model.Value < TimeSpan.FromSeconds(1))
|
||||
{
|
||||
<p class="mb-0">Your account has been disabled. Please contact server administrator.</p>
|
||||
<p class="mb-0" text-translate="true">Your account has been disabled. Please contact server administrator.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="mb-0">This account has been locked out. Please try again @Model.Value.ToBrowserDate(ViewsRazor.DateDisplayFormat.Relative).</p>
|
||||
<p class="mb-0"><span text-translate="true">This account has been locked out. Please try again</span> @Model.Value.ToBrowserDate(ViewsRazor.DateDisplayFormat.Relative).</p>
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
<div class="form-group">
|
||||
<div class="d-flex justify-content-between">
|
||||
<label asp-for="Password" class="form-label"></label>
|
||||
<a asp-action="ForgotPassword" text-translate="true" tabindex="-1">Forgot password?</a>
|
||||
<a asp-action="ForgotPassword" text-translate="true" tabindex="-1" text-translate="true">Forgot password?</a>
|
||||
</div>
|
||||
<div class="input-group d-flex">
|
||||
<input asp-for="Password" class="form-control" required />
|
||||
@ -35,7 +35,7 @@
|
||||
</div>
|
||||
<div class="form-group mt-4">
|
||||
<div class="btn-group w-100">
|
||||
<button type="submit" class="btn btn-primary btn-lg w-100" id="LoginButton"><span class="ps-3" text-translate="true">Sign in</span></button>
|
||||
<button type="submit" class="btn btn-primary btn-lg w-100" id="LoginButton"><span class="ps-3" text-translate="true" text-translate="true">Sign in</span></button>
|
||||
<button type="button" class="btn btn-outline-primary btn-lg w-auto only-for-js" data-bs-toggle="modal" data-bs-target="#scanModal" title="Scan Login code with camera">
|
||||
<vc:icon symbol="scan-qr" />
|
||||
</button>
|
||||
@ -49,7 +49,7 @@
|
||||
@if (!PoliciesSettings.LockSubscription)
|
||||
{
|
||||
<p class="text-center mt-2 mb-0">
|
||||
<a id="Register" style="font-size:1.15rem" asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]" text-translate="true">Create your account</a>
|
||||
<a id="Register" style="font-size:1.15rem" asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]" text-translate="true" text-translate="true">Create your account</a>
|
||||
</p>
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
@model LoginWith2faViewModel
|
||||
|
||||
<div class="twoFaBox">
|
||||
<h2 class="h3 mb-3">Two-Factor Authentication</h2>
|
||||
<h2 class="h3 mb-3" text-translate="true">Two-Factor Authentication</h2>
|
||||
<form method="post" asp-route-returnUrl="@ViewData["ReturnUrl"]" asp-action="LoginWith2fa">
|
||||
@if (!ViewContext.ModelState.IsValid)
|
||||
{
|
||||
@ -19,7 +19,7 @@
|
||||
<span asp-validation-for="RememberMachine" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">Log in</button>
|
||||
<button type="submit" class="btn btn-primary" text-translate="true">Log in</button>
|
||||
</div>
|
||||
</form>
|
||||
<p class="text-secondary mt-4 mb-0">
|
||||
|
@ -7,19 +7,19 @@
|
||||
<input type="hidden" asp-for="UserId"/>
|
||||
<input type="hidden" asp-for="RememberMe"/>
|
||||
</form>
|
||||
<h2 class="h3 mb-3">FIDO2 Authentication</h2>
|
||||
<p>Insert your security device and proceed.</p>
|
||||
<h2 class="h3 mb-3" text-translate="true">FIDO2 Authentication</h2>
|
||||
<p text-translate="true">Insert your security device and proceed.</p>
|
||||
<div id="info-message" class="alert alert-info mb-0 d-none">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="spinner-border spinner-border-sm me-2 fido-running" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
<span class="visually-hidden" text-translate="true">Loading...</span>
|
||||
</div>
|
||||
<span>If your security device has a button, tap on it.</span>
|
||||
<span text-translate="true">If your security device has a button, tap on it.</span>
|
||||
</div>
|
||||
</div>
|
||||
<button id="btn-start" class="btn btn-primary d-none" type="button">Start</button>
|
||||
<button id="btn-start" class="btn btn-primary d-none" type="button" text-translate="true">Start</button>
|
||||
<p id="error-message" class="d-none alert alert-danger mb-4"></p>
|
||||
<button id="btn-retry" class="btn btn-secondary d-none" type="button">Retry</button>
|
||||
<button id="btn-retry" class="btn btn-secondary d-none" type="button" text-translate="true">Retry</button>
|
||||
|
||||
<script>
|
||||
document.getElementById('btn-retry').addEventListener('click', () => window.location.reload())
|
||||
|
@ -12,8 +12,8 @@
|
||||
<input type="hidden" asp-for="LNURLEndpoint"/>
|
||||
<input type="hidden" asp-for="UserId"/>
|
||||
</form>
|
||||
<h2 class="h3 mb-3">LNURL Authentication</h2>
|
||||
<p>Scan the QR code with your Lightning wallet to sign in.</p>
|
||||
<h2 class="h3 mb-3" text-translate="true">LNURL Authentication</h2>
|
||||
<p text-translate="true">Scan the QR code with your Lightning wallet to sign in.</p>
|
||||
<div class="align-items-center" style="width:256px">
|
||||
<ul class="nav my-3 btcpay-pills align-items-center gap-2">
|
||||
@for (var i = 0; i < formats.Count; i++)
|
||||
@ -34,7 +34,7 @@
|
||||
<div class="qr-container" style="min-height:256px">
|
||||
<vc:qr-code data="@mode.Value" />
|
||||
</div>
|
||||
<a href="@mode.Value" class="btn btn-primary mt-3" rel="noreferrer noopener">
|
||||
<a href="@mode.Value" class="btn btn-primary mt-3" rel="noreferrer noopener" text-translate="true">
|
||||
Open in wallet
|
||||
</a>
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@
|
||||
ViewData["Title"] = "Recovery code verification";
|
||||
}
|
||||
|
||||
<p>
|
||||
<p text-translate="true">
|
||||
You have requested to login with a recovery code. This login will not be remembered until you provide
|
||||
an authenticator app code at login or disable 2FA and login again.
|
||||
</p>
|
||||
@ -14,7 +14,7 @@
|
||||
<input asp-for="RecoveryCode" class="form-control" autocomplete="off" />
|
||||
<span asp-validation-for="RecoveryCode" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Log in</button>
|
||||
<button type="submit" class="btn btn-primary" text-translate="true">Log in</button>
|
||||
</form>
|
||||
|
||||
@section PageFootContent {
|
||||
|
@ -41,5 +41,5 @@
|
||||
</fieldset>
|
||||
</form>
|
||||
<p class="text-center mt-2 mb-0">
|
||||
<a id="Login" style="font-size:1.15rem" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]">Log in</a>
|
||||
<a id="Login" style="font-size:1.15rem" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" text-translate="true">Log in</a>
|
||||
</p>
|
||||
|
@ -21,7 +21,7 @@ else if (Model.LoginWith2FaViewModel == null && Model.LoginWithFido2ViewModel ==
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h2 class="bg-danger">2FA and U2F/FIDO2 and LNURL-Auth Authentication Methods are not available. Please go to the https endpoint.</h2>
|
||||
<h2 class="bg-danger" text-translate="true">2FA and U2F/FIDO2 and LNURL-Auth Authentication Methods are not available. Please go to the https endpoint.</h2>
|
||||
<hr class="danger">
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,6 +3,6 @@
|
||||
}
|
||||
|
||||
<h2 text-translate="true">@ViewData["Title"]</h2>
|
||||
<p>
|
||||
<p text-translate="true">
|
||||
You have successfully signed out.
|
||||
</p>
|
||||
|
@ -59,7 +59,7 @@
|
||||
class="text-nowrap"
|
||||
title="@(appNameSortOrder == "desc" ? sortByDesc : sortByAsc)"
|
||||
>
|
||||
Name
|
||||
<span text-translate="true">Name</span>
|
||||
<vc:icon symbol="actions-sort-alpha-@(appNameSortOrder ?? nextAppNameSortOrder ?? "desc")" />
|
||||
</a>
|
||||
</th>
|
||||
@ -72,7 +72,7 @@
|
||||
class="text-nowrap"
|
||||
title="@(appTypeSortOrder == "desc" ? sortByDesc : sortByAsc)"
|
||||
>
|
||||
App Type
|
||||
<span text-translate="true">App Type</span>
|
||||
<vc:icon symbol="actions-sort-alpha-@(appTypeSortOrder ?? nextAppTypeSortOrder ?? "desc")" />
|
||||
</a>
|
||||
</th>
|
||||
@ -85,7 +85,7 @@
|
||||
class="text-nowrap"
|
||||
title="@(storeNameSortOrder == "desc" ? sortByDesc : sortByAsc)"
|
||||
>
|
||||
Store
|
||||
<span text-translate="true">Store</span>
|
||||
<vc:icon symbol="actions-sort-alpha-@(storeNameSortOrder ?? nextStoreNameSortOrder ?? "desc")" />
|
||||
</a>
|
||||
</th>
|
||||
@ -103,7 +103,7 @@
|
||||
<span not-permission="@Policies.CanModifyStoreSettings">@app.AppName</span>
|
||||
@if (app.Archived)
|
||||
{
|
||||
<span class="badge bg-info ms-2">archived</span>
|
||||
<span class="badge bg-info ms-2" text-translate="true">archived</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@ -130,7 +130,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
<p class="text-secondary mt-3" text-translate="true">
|
||||
There are no apps yet.
|
||||
</p>
|
||||
}
|
||||
|
@ -13,5 +13,5 @@
|
||||
}
|
||||
|
||||
<p class="mt-4">A generic error occurred (HTTP Code: @Model)</p>
|
||||
<p>Please consult the server log for more details.</p>
|
||||
<a href="/">Navigate back to home</a>
|
||||
<p text-translate="true">Please consult the server log for more details.</p>
|
||||
<a href="/" text-translate="true">Navigate back to home</a>
|
||||
|
@ -7,16 +7,16 @@
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item">
|
||||
<a asp-controller="UIManage" asp-action="TwoFactorAuthentication">Two Factor Authentication</a>
|
||||
<a asp-controller="UIManage" asp-action="TwoFactorAuthentication" text-translate="true">Two Factor Authentication</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Register Device</li>
|
||||
<li class="breadcrumb-item active" aria-current="page" text-translate="true">Register Device</li>
|
||||
</ol>
|
||||
<h2 text-translate="true">@ViewData["Title"]</h2>
|
||||
</nav>
|
||||
</div>
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<p>Insert your security device and proceed.</p>
|
||||
<p text-translate="true">Insert your security device and proceed.</p>
|
||||
|
||||
<form asp-action="CreateResponse" id="registerForm">
|
||||
<input type="hidden" name="data" id="data"/>
|
||||
@ -27,14 +27,14 @@
|
||||
<div id="info-message" class="alert alert-info mb-3 d-none">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="spinner-border spinner-border-sm me-2 fido-running" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
<span class="visually-hidden" text-translate="true">Loading...</span>
|
||||
</div>
|
||||
<span>If your security device has a button, tap on it.</span>
|
||||
<span text-translate="true">If your security device has a button, tap on it.</span>
|
||||
</div>
|
||||
</div>
|
||||
<button id="btn-start" class="btn btn-primary d-none" type="button">Start</button>
|
||||
<button id="btn-start" class="btn btn-primary d-none" type="button" text-translate="true">Start</button>
|
||||
<p id="error-message" class="d-none alert alert-danger"></p>
|
||||
<a id="btn-retry" class="btn btn-secondary d-none">Retry</a>
|
||||
<a id="btn-retry" class="btn btn-secondary d-none" text-translate="true">Retry</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
<div class="sticky-header">
|
||||
<h2>
|
||||
<span>@ViewData["Title"]</span>
|
||||
<span text-translate="true">@ViewData["Title"]</span>
|
||||
<a href="https://docs.btcpayserver.org/Forms" target="_blank" rel="noreferrer noopener" title="More information...">
|
||||
<vc:icon symbol="info" />
|
||||
</a>
|
||||
@ -27,8 +27,8 @@
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th class="actions-col" permission="@Policies.CanModifyStoreSettings">Actions</th>
|
||||
<th text-translate="true">Name</th>
|
||||
<th text-translate="true" class="actions-col" permission="@Policies.CanModifyStoreSettings">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -40,8 +40,8 @@
|
||||
<a asp-action="ViewPublicForm" asp-route-formId="@item.Id" id="View-@item.Name" not-permission="@Policies.CanModifyStoreSettings">@item.Name</a>
|
||||
</td>
|
||||
<td class="actions-col" permission="@Policies.CanModifyStoreSettings">
|
||||
<a asp-action="Remove" asp-route-storeId="@item.StoreId" asp-route-id="@item.Id" id="Remove-@item.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-confirm-input="DELETE">Remove</a> -
|
||||
<a asp-action="ViewPublicForm" asp-route-formId="@item.Id" id="View-@item.Name">View</a>
|
||||
<a asp-action="Remove" asp-route-storeId="@item.StoreId" asp-route-id="@item.Id" id="Remove-@item.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-confirm-input="DELETE" text-translate="true">Remove</a> -
|
||||
<a asp-action="ViewPublicForm" asp-route-formId="@item.Id" id="View-@item.Name" text-translate="true">View</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@ -51,7 +51,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary">
|
||||
<p class="text-secondary" text-translate="true">
|
||||
There are no forms yet.
|
||||
</p>
|
||||
}
|
||||
|
@ -16,10 +16,10 @@
|
||||
|
||||
@section PageFootContent {
|
||||
<datalist id="special-field-names">
|
||||
<option value="invoice_amount">Determine the generated invoice amount</option>
|
||||
<option value="invoice_currency">Determine the generated invoice currency</option>
|
||||
<option value="invoice_amount_adjustment">Adjusts the generated invoice amount — use as a prefix to have multiple adjustment fields</option>
|
||||
<option value="invoice_amount_multiply_adjustment">Adjusts the generated invoice amount by multiplying with this value — use as a prefix to have multiple adjustment fields</option>
|
||||
<option text-translate="true" value="invoice_amount">Determine the generated invoice amount</option>
|
||||
<option text-translate="true" value="invoice_currency">Determine the generated invoice currency</option>
|
||||
<option text-translate="true" value="invoice_amount_adjustment">Adjusts the generated invoice amount — use as a prefix to have multiple adjustment fields</option>
|
||||
<option text-translate="true" value="invoice_amount_multiply_adjustment">Adjusts the generated invoice amount by multiplying with this value — use as a prefix to have multiple adjustment fields</option>
|
||||
</datalist>
|
||||
<template id="form-template-email">
|
||||
@FormDataService.StaticFormEmail
|
||||
@ -42,14 +42,14 @@
|
||||
<div class="form-group">
|
||||
<label for="field-editor-field-name" class="form-label" data-required>Name</label>
|
||||
<input id="field-editor-field-name" class="form-control" list="special-field-names" required v-model="field.name" />
|
||||
<div class="form-text">The name of the field in the invoice's metadata.</div>
|
||||
<div class="form-text text-info" v-if="field.name === 'invoice_currency'">The configured name means the value of this field will determine the invoice currency for public forms.</div>
|
||||
<div class="form-text text-info" v-if="field.name === 'invoice_amount'">The configured name means the value of this field will determine the invoice amount for public forms.</div>
|
||||
<div class="form-text text-info" v-if="field.name && field.name.startsWith('invoice_amount_adjustment')">The configured name means the value of this field will adjust the invoice amount for public forms and the point of sale app.</div>
|
||||
<div class="form-text text-info" v-if="field.name && field.name.startsWith('invoice_amount_multiply_adjustment')">The configured name means the value of this field will adjust the invoice amount by multiplying it for public forms and the point of sale app.</div>
|
||||
<div text-translate="true" class="form-text">The name of the field in the invoice's metadata.</div>
|
||||
<div text-translate="true" class="form-text text-info" v-if="field.name === 'invoice_currency'">The configured name means the value of this field will determine the invoice currency for public forms.</div>
|
||||
<div text-translate="true" class="form-text text-info" v-if="field.name === 'invoice_amount'">The configured name means the value of this field will determine the invoice amount for public forms.</div>
|
||||
<div text-translate="true" class="form-text text-info" v-if="field.name && field.name.startsWith('invoice_amount_adjustment')">The configured name means the value of this field will adjust the invoice amount for public forms and the point of sale app.</div>
|
||||
<div text-translate="true" class="form-text text-info" v-if="field.name && field.name.startsWith('invoice_amount_multiply_adjustment')">The configured name means the value of this field will adjust the invoice amount by multiplying it for public forms and the point of sale app.</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="field.type === 'select'">
|
||||
<h5 class="mt-2">Options</h5>
|
||||
<h5 class="mt-2" text-translate="true">Options</h5>
|
||||
<div class="options" v-sortable="{ handle: '.drag', onUpdate: sortOptions }">
|
||||
<div v-for="(option, index) in field.options" :key="option.value" class="d-flex align-items-start gap-2 pt-3">
|
||||
<button type="button" class="btn b-0 control drag">
|
||||
@ -70,23 +70,23 @@
|
||||
</div>
|
||||
<button type="button" class="btn btn-link px-1 py-2 gap-1 add fw-semibold d-inline-flex align-items-center" v-on:click.stop="addOption($event)">
|
||||
<vc:icon symbol="actions-add" />
|
||||
Add Option
|
||||
<span text-translate="true">Add Option</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group" v-if="field.type !== 'fieldset' && field.type !== 'mirror'">
|
||||
<label for="field-editor-field-value" class="form-label">Default Value</label>
|
||||
<label for="field-editor-field-value" class="form-label" text-translate="true">Default Value</label>
|
||||
<input id="field-editor-field-value" class="form-control" v-model="field.value" />
|
||||
</div>
|
||||
<div class="form-group" v-if="field.type === 'mirror'">
|
||||
<label for="field-editor-field-mirror" class="form-label">Field to mirror</label>
|
||||
<label for="field-editor-field-mirror" class="form-label" text-translate="true">Field to mirror</label>
|
||||
<select id="field-editor-field-mirror" class="form-select" v-model="field.value">
|
||||
<option v-for="option in $root.allFields" v-if="option.name && option.name !== field.name" :key="option.name" :value="option.name" :selected="option.name === field.value" v-text="option.label || option.name"></option>
|
||||
</select>
|
||||
<div class="form-text">The chosen field's selected value will be copied to this field upon submission.</div>
|
||||
<div class="form-text" text-translate="true">The chosen field's selected value will be copied to this field upon submission.</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="field.type === 'mirror'">
|
||||
<h5 class="mt-2">Value Mapper</h5>
|
||||
<div class="form-text">The values being mirrored from another field will be mapped to another value if configured.</div>
|
||||
<h5 class="mt-2" text-translate="true">Value Mapper</h5>
|
||||
<div class="form-text" text-translate="true">The values being mirrored from another field will be mapped to another value if configured.</div>
|
||||
<div class="options">
|
||||
<div v-if="field.valuemap" v-for="(v, k, index) in field.valuemap" :key="k" class="d-flex align-items-start gap-2 pt-3">
|
||||
<div class="field flex-grow-1">
|
||||
@ -107,25 +107,25 @@
|
||||
</div>
|
||||
<button type="button" class="btn btn-link px-1 py-2 gap-1 add fw-semibold d-inline-flex align-items-center" v-on:click.stop="addValueMap($event)">
|
||||
<vc:icon symbol="actions-add" />
|
||||
Add mapped value
|
||||
<span text-translate="true">Add mapped value</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group" v-if="field.type !== 'fieldset' && field.type !== 'mirror'">
|
||||
<label for="field-editor-field-helpText" class="form-label">Helper Text</label>
|
||||
<label for="field-editor-field-helpText" class="form-label" text-translate="true">Helper Text</label>
|
||||
<input id="field-editor-field-helpText" class="form-control" v-model="field.helpText" />
|
||||
<div class="form-text">Additional text to provide an explanation for the field</div>
|
||||
<div class="form-text" text-translate="true">Additional text to provide an explanation for the field</div>
|
||||
</div>
|
||||
<div class="form-group form-check" v-if="field.type !== 'fieldset' && field.type !== 'mirror'">
|
||||
<input id="field-editor-field-required" type="checkbox" class="form-check-input" v-model="field.required" />
|
||||
<label for="field-editor-field-required" class="form-check-label">Required Field</label>
|
||||
<label for="field-editor-field-required" class="form-check-label" text-translate="true">Required Field</label>
|
||||
</div>
|
||||
<div class="form-group form-check" v-if="field.type !== 'fieldset' && field.type !== 'select' && field.type !== 'mirror'">
|
||||
<input id="field-editor-field-constant" type="checkbox" class="form-check-input" v-model="field.constant" />
|
||||
<label for="field-editor-field-constant" class="form-check-label">Constant</label>
|
||||
<div class="form-text">The user will not be able to change the field's value</div>
|
||||
<label for="field-editor-field-constant" class="form-check-label" text-translate="true">Constant</label>
|
||||
<div class="form-text" text-translate="true">The user will not be able to change the field's value</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>Select a field to edit</div>
|
||||
<div v-else text-translate="true">Select a field to edit</div>
|
||||
</template>
|
||||
<template id="fields-editor">
|
||||
<div>
|
||||
@ -144,7 +144,7 @@
|
||||
</div>
|
||||
<button type="button" class="btn btn-link py-0 px-2 mt-2 mb-2 gap-1 add fw-semibold d-inline-flex align-items-center" v-on:click.stop="$emit('add-field', $event, path)">
|
||||
<vc:icon symbol="actions-add" />
|
||||
Add Form Field
|
||||
<span text-translate="true">Add Form Field</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@ -174,7 +174,7 @@
|
||||
<template id="field-type-mirror">
|
||||
<div class="form-group mb-0">
|
||||
<label class="form-label" v-text="label" v-if="label"></label>
|
||||
<div class="form-text">Mirror of {{value}}</div>
|
||||
<div class="form-text"><span text-translate="true">Mirror of</span> {{value}}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template id="field-type-fieldset">
|
||||
@ -196,9 +196,9 @@
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item">
|
||||
<a asp-controller="UIForms" asp-action="FormsList" asp-route-storeId="@storeId">Forms</a>
|
||||
<a asp-controller="UIForms" asp-action="FormsList" asp-route-storeId="@storeId" text-translate="true">Forms</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
|
||||
<li class="breadcrumb-item active" aria-current="page" text-translate="true">@ViewData["Title"]</li>
|
||||
</ol>
|
||||
<h2>
|
||||
@ViewData["Title"]
|
||||
@ -211,7 +211,7 @@
|
||||
<button id="page-primary" type="submit" class="btn btn-primary order-sm-1">Save</button>
|
||||
@if (!isNew)
|
||||
{
|
||||
<a class="btn btn-secondary" asp-action="ViewPublicForm" asp-route-formId="@formId" id="ViewForm">View</a>
|
||||
<a class="btn btn-secondary" asp-action="ViewPublicForm" asp-route-formId="@formId" id="ViewForm" text-translate="true">View</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@ -230,7 +230,7 @@
|
||||
<input asp-for="Public" type="checkbox" class="btcpay-toggle" />
|
||||
<div>
|
||||
<label asp-for="Public"></label>
|
||||
<div class="form-text">
|
||||
<div class="form-text" text-translate="true">
|
||||
Standalone mode, which can be used to generate invoices
|
||||
independent of payment requests or apps.
|
||||
</div>
|
||||
@ -243,16 +243,16 @@
|
||||
<div class="d-flex flex-wrap align-items-end justify-content-between gap-3 mb-3">
|
||||
<ul class="nav nav-pills gap-4" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="EditorTabButton" data-bs-toggle="pill" data-bs-target="#EditorTabPane" type="button" role="tab" aria-controls="EditorTabPane" aria-selected="true">Editor</button>
|
||||
<button class="nav-link active" id="EditorTabButton" data-bs-toggle="pill" data-bs-target="#EditorTabPane" type="button" role="tab" aria-controls="EditorTabPane" aria-selected="true" text-translate="true">Editor</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="CodeTabButton" data-bs-toggle="pill" data-bs-target="#CodeTabPane" type="button" role="tab" aria-controls="CodeTabPane" aria-selected="false">Code</button>
|
||||
<button class="nav-link" id="CodeTabButton" data-bs-toggle="pill" data-bs-target="#CodeTabPane" type="button" role="tab" aria-controls="CodeTabPane" aria-selected="false" text-translate="true">Code</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="d-flex align-items-center gap-2 mb-1">
|
||||
<span class="fw-semibold">Templates</span>
|
||||
<button type="button" class="btn btn-link p-0 fw-semibold" v-on:click="applyTemplate('email')" id="ApplyEmailTemplate">Email</button>
|
||||
<button type="button" class="btn btn-link p-0 fw-semibold" v-on:click="applyTemplate('address')" id="ApplyAddressTemplate">Address</button>
|
||||
<span class="fw-semibold" text-translate="true">Templates</span>
|
||||
<button type="button" class="btn btn-link p-0 fw-semibold" v-on:click="applyTemplate('email')" id="ApplyEmailTemplate" text-translate="true">Email</button>
|
||||
<button type="button" class="btn btn-link p-0 fw-semibold" v-on:click="applyTemplate('address')" id="ApplyAddressTemplate" text-translate="true">Address</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
@ -271,7 +271,7 @@
|
||||
</div>
|
||||
<div class="col-xl-5 offcanvas-xl offcanvas-end" tabindex="-1" ref="editorOffcanvas">
|
||||
<div class="offcanvas-header justify-content-between p-3">
|
||||
<h5 class="offcanvas-title">Edit Field</h5>
|
||||
<h5 class="offcanvas-title" text-translate="true">Edit Field</h5>
|
||||
<button type="button" class="btn-close" aria-label="Close" v-on:click="hideOffcanvas">
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
@ -283,7 +283,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="CodeTabPane" role="tabpanel" aria-labelledby="CodeTabButton" tabindex="0">
|
||||
<label asp-for="FormConfig" class="form-label" data-required>Form JSON</label>
|
||||
<label asp-for="FormConfig" class="form-label" data-required text-translate="true">Form JSON</label>
|
||||
<textarea asp-for="FormConfig" class="form-control font-monospace" style="font-size:.85rem" rows="21" cols="21" v-model="configJSON" v-on:change="updateFromJSON"></textarea>
|
||||
<span asp-validation-for="FormConfig" class="text-danger"></span>
|
||||
</div>
|
||||
|
@ -48,7 +48,7 @@
|
||||
</main>
|
||||
<footer class="store-footer">
|
||||
<a class="store-powered-by" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
|
||||
Powered by <partial name="_StoreFooterLogo" />
|
||||
<span text-translate="true">Powered by</span> <partial name="_StoreFooterLogo" />
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
|
@ -13,13 +13,13 @@
|
||||
|
||||
@if (!Model.HasStore)
|
||||
{
|
||||
<p class="lead text-secondary">To start accepting payments, set up a store.</p>
|
||||
<p class="lead text-secondary" text-translate="true">To start accepting payments, set up a store.</p>
|
||||
|
||||
<div class="list-group mt-4" id="SetupGuide">
|
||||
<a asp-controller="UIUserStores" asp-action="CreateStore" id="SetupGuide-Store" class="list-group-item list-group-item-action d-flex align-items-center">
|
||||
<vc:icon symbol="wallet-new"/>
|
||||
<div class="content">
|
||||
<h5 class="mb-0">Create your store</h5>
|
||||
<h5 class="mb-0" text-translate="true">Create your store</h5>
|
||||
</div>
|
||||
<vc:icon symbol="caret-right"/>
|
||||
</a>
|
||||
|
@ -20,23 +20,23 @@
|
||||
</div>
|
||||
</form>
|
||||
<form id="mine-block" :action="`/i/${invoiceId}/mine-blocks`" method="post" v-on:submit.prevent="handleFormSubmit($event, 'mining')" v-if="displayMine">
|
||||
<label for="BlockCount" class="control-label form-label">Mine to test processing and settlement</label>
|
||||
<label for="BlockCount" class="control-label form-label" text-translate="true">Mine to test processing and settlement</label>
|
||||
<div class="d-flex gap-2">
|
||||
<div class="input-group">
|
||||
<input id="BlockCount" name="BlockCount" type="number" step="1" min="1" class="form-control" value="1"/>
|
||||
<div class="input-group-addon input-group-text">blocks</div>
|
||||
<div class="input-group-addon input-group-text" text-translate="true">blocks</div>
|
||||
</div>
|
||||
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="mining" id="mine-block">Mine</button>
|
||||
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="mining" id="mine-block" text-translate="true">Mine</button>
|
||||
</div>
|
||||
</form>
|
||||
<form id="expire-invoice" :action="`/i/${invoiceId}/expire`" method="post" v-on:submit.prevent="handleFormSubmit($event, 'expiring')" v-if="displayExpire">
|
||||
<label for="ExpirySeconds" class="control-label form-label">Expire invoice in …</label>
|
||||
<label for="ExpirySeconds" class="control-label form-label" text-translate="true">Expire invoice in …</label>
|
||||
<div class="d-flex gap-2">
|
||||
<div class="input-group">
|
||||
<input id="ExpirySeconds" name="Seconds" type="number" step="1" min="0" class="form-control" value="20" />
|
||||
<div class="input-group-addon input-group-text">seconds</div>
|
||||
<div class="input-group-addon input-group-text" text-translate="true">seconds</div>
|
||||
</div>
|
||||
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="expiring" id="Expire">Expire</button>
|
||||
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="expiring" id="Expire" text-translate="true">Expire</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
@ -16,30 +16,30 @@
|
||||
<h1>Pay with @Model.StoreName</h1>
|
||||
@if (Model.Status == "new")
|
||||
{
|
||||
<h1 class="text-danger">This payment method requires javascript.</h1>
|
||||
<h1 class="text-danger" text-translate="true">This payment method requires javascript.</h1>
|
||||
}
|
||||
else if (Model.Status == "paid" || Model.Status == "complete" || Model.Status == "confirmed")
|
||||
{
|
||||
<div>
|
||||
<p>This invoice has been paid</p>
|
||||
<p text-translate="true">This invoice has been paid</p>
|
||||
</div>
|
||||
}
|
||||
else if (Model.Status == "expired" || Model.Status == "invalid")
|
||||
{
|
||||
<div>
|
||||
<p>This invoice has expired</p>
|
||||
<p text-translate="true">This invoice has expired</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div>
|
||||
<p>Non-supported state of invoice</p>
|
||||
<p text-translate="true">Non-supported state of invoice</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<hr />
|
||||
<p>
|
||||
<a asp-action="Checkout" asp-route-invoiceId="@Model.InvoiceId">Go back to Javascript enabled invoice</a>
|
||||
<a asp-action="Checkout" asp-route-invoiceId="@Model.InvoiceId" text-translate="true">Go back to Javascript enabled invoice</a>
|
||||
</p>
|
||||
@await Component.InvokeAsync("UiExtensionPoint", new { location = "checkout-noscript-end", model = Model })
|
||||
</body>
|
||||
|
@ -32,9 +32,9 @@
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item">
|
||||
<a asp-action="ListInvoices" asp-route-storeId="@Model.StoreId">Invoices</a>
|
||||
<a asp-action="ListInvoices" asp-route-storeId="@Model.StoreId" text-translate="true">Invoices</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
|
||||
<li class="breadcrumb-item active" aria-current="page" text-translate="true">@ViewData["Title"]</li>
|
||||
</ol>
|
||||
<h2 text-translate="true">@ViewData["Title"]</h2>
|
||||
</nav>
|
||||
@ -91,29 +91,29 @@
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultPaymentMethod" class="form-label"></label>
|
||||
<select asp-for="DefaultPaymentMethod" asp-items="Model.AvailablePaymentMethods" class="form-select">
|
||||
<option value="" selected>Use the store’s default</option>
|
||||
<option value="" selected text-translate="true">Use the store’s default</option>
|
||||
</select>
|
||||
<span asp-validation-for="DefaultPaymentMethod" class="text-danger"></span>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Customer Information</h4>
|
||||
<h4 class="mt-5 mb-4" text-translate="true">Customer Information</h4>
|
||||
<div class="form-group">
|
||||
<label asp-for="BuyerEmail" class="form-label"></label>
|
||||
<input asp-for="BuyerEmail" class="form-control"/>
|
||||
<span asp-validation-for="BuyerEmail" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
||||
<h4 class="mt-5 mb-2" text-translate="true">Additional Options</h4>
|
||||
<div class="form-group">
|
||||
<div class="accordion" id="additional">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-pos-data-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-pos-data" aria-expanded="false" aria-controls="additional-pos-data">
|
||||
Metadata
|
||||
<span text-translate="true">Metadata</span>
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-pos-data" class="accordion-collapse collapse" aria-labelledby="additional-pos-data-header">
|
||||
<p>Custom data to expand the invoice. This data is a JSON object, e.g. <code>{ "orderId": 615, "product": "Pizza" }</code></p>
|
||||
<p html-translate="true">Custom data to expand the invoice. This data is a JSON object, e.g. <code>{ "orderId": 615, "product": "Pizza" }</code></p>
|
||||
<div class="form-group">
|
||||
<label asp-for="Metadata" class="form-label"></label>
|
||||
<textarea asp-for="Metadata" class="form-control" rows="10" cols="40"></textarea>
|
||||
@ -124,7 +124,7 @@
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-notifications-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-notifications" aria-expanded="false" aria-controls="additional-notifications">
|
||||
Invoice Notifications
|
||||
<span text-translate="true">Invoice Notifications</span>
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
@ -139,7 +139,7 @@
|
||||
<label asp-for="NotificationEmail" class="form-label"></label>
|
||||
<input asp-for="NotificationEmail" class="form-control" />
|
||||
<span asp-validation-for="NotificationEmail" class="text-danger"></span>
|
||||
<div id="InvoiceEmailHelpBlock" class="form-text">Receive updates for this invoice.</div>
|
||||
<div id="InvoiceEmailHelpBlock" class="form-text" text-translate="true">Receive updates for this invoice.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -161,7 +161,7 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
<span class="visually-hidden" text-translate="true">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -186,7 +186,7 @@
|
||||
</a>
|
||||
</p>
|
||||
<a class="store-powered-by" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
|
||||
Powered by <partial name="_StoreFooterLogo" />
|
||||
<span text-translate="true">Powered by</span> <partial name="_StoreFooterLogo" />
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
|
@ -251,7 +251,7 @@
|
||||
}
|
||||
</div>
|
||||
<div class="store-footer p-3">
|
||||
<a class="store-powered-by" style="color:#000;">Powered by <partial name="_StoreFooterLogo" /></a>
|
||||
<a class="store-powered-by" style="color:#000;"><span text-translate="true">Powered by</span> <partial name="_StoreFooterLogo" /></a>
|
||||
</div>
|
||||
<hr class="w-100 my-0 bg-none"/>
|
||||
</center>
|
||||
|
@ -9,29 +9,29 @@
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
<h5 class="alert-heading">Updated in v1.4.0</h5>
|
||||
<p class="mb-2">Invoice states have been updated to match the Greenfield API:</p>
|
||||
<p class="mb-2" text-translate="true">Invoice states have been updated to match the Greenfield API:</p>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<ul class="list-unstyled mb-md-0">
|
||||
<li>
|
||||
<span class="badge badge-processing">Paid</span>
|
||||
<span class="mx-1">is now shown as</span>
|
||||
<span class="badge badge-processing">Processing</span>
|
||||
<span text-translate="true" class="badge badge-processing">Paid</span>
|
||||
<span text-translate="true" class="mx-1">is now shown as</span>
|
||||
<span text-translate="true" class="badge badge-processing">Processing</span>
|
||||
</li>
|
||||
<li class="mt-2">
|
||||
<span class="badge badge-settled">Completed</span>
|
||||
<span class="mx-1">is now shown as</span>
|
||||
<span class="badge badge-settled">Settled</span>
|
||||
<span text-translate="true" class="badge badge-settled">Completed</span>
|
||||
<span text-translate="true" class="mx-1">is now shown as</span>
|
||||
<span text-translate="true" class="badge badge-settled">Settled</span>
|
||||
</li>
|
||||
<li class="mt-2">
|
||||
<span class="badge badge-settled">Confirmed</span>
|
||||
<span class="mx-1">is now shown as</span>
|
||||
<span class="badge badge-settled">Settled</span>
|
||||
<span text-translate="true" class="badge badge-settled">Confirmed</span>
|
||||
<span text-translate="true" class="mx-1">is now shown as</span>
|
||||
<span text-translate="true" class="badge badge-settled">Settled</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 d-flex justify-content-md-end align-items-md-end">
|
||||
<button name="command" type="submit" value="save" class="btn btn-sm btn-outline-secondary" data-bs-dismiss="alert">Don't Show Again</button>
|
||||
<button name="command" type="submit" value="save" class="btn btn-sm btn-outline-secondary" data-bs-dismiss="alert" text-translate="true">Don't Show Again</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user