Unify delete confirmation confirm dialog (#6965)

This commit is contained in:
Nicolas Dorier
2025-10-26 12:37:55 +09:00
committed by GitHub
parent 3697790c81
commit ff02c0f5d7
28 changed files with 72 additions and 73 deletions

View File

@@ -762,5 +762,11 @@ namespace BTCPayServer.Tests
}); });
return await new StreamReader(await download.CreateReadStreamAsync()).ReadToEndAsync(); return await new StreamReader(await download.CreateReadStreamAsync()).ReadToEndAsync();
} }
public async Task ConfirmDeleteModal()
{
await Page.FillAsync("#ConfirmInput", "DELETE");
await Page.ClickAsync("#ConfirmContinue");
}
} }
} }

View File

@@ -144,8 +144,7 @@ namespace BTCPayServer.Tests
await s.GoToStore(StoreNavPages.Forms); await s.GoToStore(StoreNavPages.Forms);
Assert.Contains("Custom Form 1", await s.Page.ContentAsync()); Assert.Contains("Custom Form 1", await s.Page.ContentAsync());
await s.Page.GetByRole(AriaRole.Link, new() { Name = "Remove" }).ClickAsync(); await s.Page.GetByRole(AriaRole.Link, new() { Name = "Remove" }).ClickAsync();
await s.Page.FillAsync("#ConfirmInput", "DELETE"); await s.ConfirmDeleteModal();
await s.Page.ClickAsync("#ConfirmContinue");
Assert.DoesNotContain("Custom Form 1", await s.Page.ContentAsync()); Assert.DoesNotContain("Custom Form 1", await s.Page.ContentAsync());
await s.ClickPagePrimary(); await s.ClickPagePrimary();
await s.Page.FillAsync("[name='Name']", "Custom Form 2"); await s.Page.FillAsync("[name='Name']", "Custom Form 2");
@@ -491,8 +490,7 @@ namespace BTCPayServer.Tests
//let's test delete user quickly while we're at it //let's test delete user quickly while we're at it
await s.GoToProfile(); await s.GoToProfile();
await s.Page.ClickAsync("#delete-user"); await s.Page.ClickAsync("#delete-user");
await s.Page.FillAsync("#ConfirmInput", "DELETE"); await s.ConfirmDeleteModal();
await s.Page.ClickAsync("#ConfirmContinue");
Assert.Contains("/login", s.Page.Url); Assert.Contains("/login", s.Page.Url);
} }
@@ -1488,16 +1486,14 @@ namespace BTCPayServer.Tests
Assert.Equal(2, await deleteLinks.CountAsync()); Assert.Equal(2, await deleteLinks.CountAsync());
await deleteLinks.First.ClickAsync(); await deleteLinks.First.ClickAsync();
await s.Page.FillAsync("#ConfirmInput", "REMOVE"); await s.ConfirmDeleteModal();
await s.Page.ClickAsync("#ConfirmContinue");
await s.FindAlertMessage(); await s.FindAlertMessage();
deleteLinks = s.Page.GetByRole(AriaRole.Link, new() { Name = "Remove" }); deleteLinks = s.Page.GetByRole(AriaRole.Link, new() { Name = "Remove" });
Assert.Equal(1, await deleteLinks.CountAsync()); Assert.Equal(1, await deleteLinks.CountAsync());
await deleteLinks.First.ClickAsync(); await deleteLinks.First.ClickAsync();
await s.Page.FillAsync("#ConfirmInput", "REMOVE"); await s.ConfirmDeleteModal();
await s.Page.ClickAsync("#ConfirmContinue");
await s.FindAlertMessage(); await s.FindAlertMessage();
Assert.Contains("There are no rules yet.", await s.Page.ContentAsync()); Assert.Contains("There are no rules yet.", await s.Page.ContentAsync());
@@ -1712,8 +1708,7 @@ namespace BTCPayServer.Tests
Assert.Contains(await passEl.TextContentAsync(), "hellorockstar", StringComparison.OrdinalIgnoreCase); Assert.Contains(await passEl.TextContentAsync(), "hellorockstar", StringComparison.OrdinalIgnoreCase);
await s.Page.ClickAsync("#delete"); await s.Page.ClickAsync("#delete");
await s.Page.WaitForSelectorAsync("#ConfirmInput"); await s.Page.WaitForSelectorAsync("#ConfirmInput");
await s.Page.FillAsync("#ConfirmInput", "DELETE"); await s.ConfirmDeleteModal();
await s.Page.ClickAsync("#ConfirmContinue");
await s.FindAlertMessage(); await s.FindAlertMessage();
seedEl = s.Page.Locator("#Seed"); seedEl = s.Page.Locator("#Seed");
Assert.Contains("Seed removed", await seedEl.TextContentAsync(), StringComparison.OrdinalIgnoreCase); Assert.Contains("Seed removed", await seedEl.TextContentAsync(), StringComparison.OrdinalIgnoreCase);
@@ -2131,8 +2126,7 @@ namespace BTCPayServer.Tests
var deleteLinks = await s.Page.Locator("a:has-text('Delete')").AllAsync(); var deleteLinks = await s.Page.Locator("a:has-text('Delete')").AllAsync();
Assert.Equal(2, deleteLinks.Count); Assert.Equal(2, deleteLinks.Count);
await deleteLinks[0].ClickAsync(); await deleteLinks[0].ClickAsync();
await s.Page.FillAsync("#ConfirmInput", "DELETE"); await s.ConfirmDeleteModal();
await s.Page.ClickAsync("#ConfirmContinue");
deleteLinks = await s.Page.Locator("a:has-text('Delete')").AllAsync(); deleteLinks = await s.Page.Locator("a:has-text('Delete')").AllAsync();
Assert.Single(deleteLinks); Assert.Single(deleteLinks);
await s.FindAlertMessage(); await s.FindAlertMessage();
@@ -2213,8 +2207,7 @@ namespace BTCPayServer.Tests
TestLogs.LogInformation("Let's see if we can delete store with some webhooks inside"); TestLogs.LogInformation("Let's see if we can delete store with some webhooks inside");
await s.GoToStore(); await s.GoToStore();
await s.Page.ClickAsync("#DeleteStore"); await s.Page.ClickAsync("#DeleteStore");
await s.Page.FillAsync("#ConfirmInput", "DELETE"); await s.ConfirmDeleteModal();
await s.Page.ClickAsync("#ConfirmContinue");
await s.FindAlertMessage(); await s.FindAlertMessage();
} }

View File

@@ -41,7 +41,7 @@ namespace BTCPayServer
return View("Confirm", return View("Confirm",
new ConfirmModel(StringLocalizer["Remove LNURL Auth link"], 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["Your account will no longer have this Lightning wallet as an option for two-factor authentication."],
StringLocalizer["Remove"])); StringLocalizer["Delete"]));
} }
[HttpPost("{id}/delete")] [HttpPost("{id}/delete")]

View File

@@ -309,7 +309,7 @@ namespace BTCPayServer.Controllers
StringLocalizer["Delete"])); StringLocalizer["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")); return View("Confirm", new ConfirmModel(StringLocalizer["Delete user"], $"The user <strong>{Html.Encode(user.Email)}</strong> will be permanently deleted. Are you sure?", StringLocalizer["Delete"]));
} }
[HttpPost("server/users/{userId}/delete")] [HttpPost("server/users/{userId}/delete")]
@@ -337,7 +337,7 @@ namespace BTCPayServer.Controllers
return View("Confirm", new ConfirmModel(StringLocalizer["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.")); $"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"))); 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 ? StringLocalizer["Enable"] : StringLocalizer["Disable"])));
} }
[HttpPost("server/users/{userId}/toggle")] [HttpPost("server/users/{userId}/toggle")]
@@ -366,7 +366,7 @@ namespace BTCPayServer.Controllers
if (user == null) if (user == null)
return NotFound(); return NotFound();
return View("Confirm", new ConfirmModel($"{(approved ? "Approve" : "Unapprove")} user", $"The user <strong>{Html.Encode(user.Email)}</strong> will be {(approved ? "approved" : "unapproved")}. Are you sure?", (approved ? "Approve" : "Unapprove"))); return View("Confirm", new ConfirmModel($"{(approved ? StringLocalizer["Approve"] : StringLocalizer["Unapprove"])} user", $"The user <strong>{Html.Encode(user.Email)}</strong> will be {(approved ? "approved" : "unapproved")}. Are you sure?", (approved ? StringLocalizer["Approve"] : StringLocalizer["Unapprove"])));
} }
[HttpPost("server/users/{userId}/approve")] [HttpPost("server/users/{userId}/approve")]
@@ -392,7 +392,7 @@ namespace BTCPayServer.Controllers
if (user == null) if (user == null)
return NotFound(); return NotFound();
return View("Confirm", new ConfirmModel(StringLocalizer["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>.", StringLocalizer["Send"]));
} }
[HttpPost("server/users/{userId}/verification-email")] [HttpPost("server/users/{userId}/verification-email")]

View File

@@ -919,7 +919,7 @@ namespace BTCPayServer.Controllers
return NotFound(); return NotFound();
return View("Confirm", return View("Confirm",
new ConfirmModel("Delete dynamic DNS service", new ConfirmModel("Delete dynamic DNS service",
$"Deleting the dynamic DNS service for <strong>{Html.Encode(hostname)}</strong> means your BTCPay Server will stop updating the associated DNS record periodically.", "Delete")); $"Deleting the dynamic DNS service for <strong>{Html.Encode(hostname)}</strong> means your BTCPay Server will stop updating the associated DNS record periodically.", StringLocalizer["Delete"]));
} }
[HttpPost("server/services/dynamic-dns/{hostname}/delete")] [HttpPost("server/services/dynamic-dns/{hostname}/delete")]
@@ -1054,7 +1054,7 @@ namespace BTCPayServer.Controllers
[HttpGet("server/services/ssh/disable")] [HttpGet("server/services/ssh/disable")]
public IActionResult SSHServiceDisable() public IActionResult SSHServiceDisable()
{ {
return View("Confirm", new ConfirmModel("Disable modification of SSH settings", "This action is permanent and will remove the ability to change the SSH settings via the BTCPay Server user interface.", "Disable")); return View("Confirm", new ConfirmModel(StringLocalizer["Disable modification of SSH settings"], StringLocalizer["This action is permanent and will remove the ability to change the SSH settings via the BTCPay Server user interface."], StringLocalizer["Disable"]));
} }
[HttpPost("server/services/ssh/disable")] [HttpPost("server/services/ssh/disable")]

View File

@@ -76,7 +76,7 @@ namespace BTCPayServer.Controllers
_payoutProcessorService = payoutProcessorService; _payoutProcessorService = payoutProcessorService;
_payoutProcessorFactories = payoutProcessorFactories; _payoutProcessorFactories = payoutProcessorFactories;
} }
[HttpGet("stores/{storeId}/pull-payments/new")] [HttpGet("stores/{storeId}/pull-payments/new")]
[Authorize(Policy = Policies.CanCreateNonApprovedPullPayments, AuthenticationSchemes = AuthenticationSchemes.Cookie)] [Authorize(Policy = Policies.CanCreateNonApprovedPullPayments, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public IActionResult NewPullPayment(string storeId) public IActionResult NewPullPayment(string storeId)
@@ -119,7 +119,7 @@ namespace BTCPayServer.Controllers
model.PayoutMethods ??= new List<string>(); model.PayoutMethods ??= new List<string>();
if (!model.PayoutMethods.Any()) if (!model.PayoutMethods.Any())
{ {
// Since we assign all payment methods to be selected by default above we need to update // Since we assign all payment methods to be selected by default above we need to update
// them here to reflect user's selection so that they can correct their mistake // them here to reflect user's selection so that they can correct their mistake
model.PayoutMethodsItem = model.PayoutMethodsItem =
paymentMethodOptions.Select(id => new SelectListItem(id.ToString(), id.ToString(), false)); paymentMethodOptions.Select(id => new SelectListItem(id.ToString(), id.ToString(), false));
@@ -263,7 +263,7 @@ namespace BTCPayServer.Controllers
string pullPaymentId) string pullPaymentId)
{ {
return View("Confirm", return View("Confirm",
new ConfirmModel(StringLocalizer["Archive pull payment"], StringLocalizer["Do you really want to archive the pull payment?"], "Archive")); new ConfirmModel(StringLocalizer["Archive pull payment"], StringLocalizer["Do you really want to archive the pull payment?"], StringLocalizer["Archive"]));
} }
[HttpPost("stores/{storeId}/pull-payments/{pullPaymentId}/archive")] [HttpPost("stores/{storeId}/pull-payments/{pullPaymentId}/archive")]

View File

@@ -651,7 +651,7 @@ public partial class UIStoresController
{ {
Title = StringLocalizer["Remove {0} wallet", network.CryptoCode], Title = StringLocalizer["Remove {0} wallet", network.CryptoCode],
Description = WalletRemoveWarning(derivation.IsHotWallet, network.CryptoCode), Description = WalletRemoveWarning(derivation.IsHotWallet, network.CryptoCode),
Action = StringLocalizer["Remove"] Action = StringLocalizer["Delete"]
}); });
} }

View File

@@ -125,7 +125,7 @@ namespace BTCPayServer.Controllers
var store = HttpContext.GetStoreData(); var store = HttpContext.GetStoreData();
if (store == null) if (store == null)
return NotFound(); return NotFound();
return View("Confirm", new ConfirmModel(StringLocalizer["Delete store {0}", store.StoreName], StringLocalizer["This store will still be accessible to users sharing it"], "Delete")); return View("Confirm", new ConfirmModel(StringLocalizer["Delete store {0}", store.StoreName], StringLocalizer["This store will still be accessible to users sharing it"], StringLocalizer["Delete"]));
} }
[HttpPost("{storeId}/me/delete")] [HttpPost("{storeId}/me/delete")]

View File

@@ -33,7 +33,7 @@ namespace BTCPayServer.Fido2
[HttpGet("{id}/delete")] [HttpGet("{id}/delete")]
public IActionResult Remove(string id) public IActionResult Remove(string id)
{ {
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"])); 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["Delete"]));
} }
[HttpPost("{id}/delete")] [HttpPost("{id}/delete")]

View File

@@ -49,7 +49,7 @@
<td class="actions-col" permission="@Policies.CanModifyStoreSettings"> <td class="actions-col" permission="@Policies.CanModifyStoreSettings">
<div class="d-inline-flex align-items-center gap-3"> <div class="d-inline-flex align-items-center gap-3">
<a asp-action="StoreEmailRulesEdit" asp-route-storeId="@storeId" asp-route-ruleId="@rule.Data.Id">Edit</a> <a asp-action="StoreEmailRulesEdit" asp-route-storeId="@storeId" asp-route-ruleId="@rule.Data.Id">Edit</a>
<a asp-action="StoreEmailRulesDelete" asp-route-storeId="@storeId" asp-route-ruleId="@rule.Data.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="@ViewLocalizer["This action will remove the rule with the trigger <b>{0}</b>.", Html.Encode(rule.Trigger)]" data-confirm-input="@StringLocalizer["REMOVE"]" text-translate="true">Remove</a> <a asp-action="StoreEmailRulesDelete" asp-route-storeId="@storeId" asp-route-ruleId="@rule.Data.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="@ViewLocalizer["This action will remove the rule with the trigger <b>{0}</b>.", Html.Encode(rule.Trigger)]" data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">Remove</a>
</div> </div>
</td> </td>
</tr> </tr>

View File

@@ -53,7 +53,7 @@
<td class="d-block text-break">@wh.Url</td> <td class="d-block text-break">@wh.Url</td>
<td class="actions-col text-md-nowrap" permission="@Policies.CanModifyStoreSettings"> <td class="actions-col text-md-nowrap" permission="@Policies.CanModifyStoreSettings">
<a asp-action="ModifyWebhook" asp-route-storeId="@Context.GetRouteValue("storeId")" asp-route-webhookId="@wh.Id" text-translate="true">Modify</a> - <a asp-action="ModifyWebhook" asp-route-storeId="@Context.GetRouteValue("storeId")" asp-route-webhookId="@wh.Id" text-translate="true">Modify</a> -
<a asp-action="DeleteWebhook" asp-route-storeId="@Context.GetRouteValue("storeId")" asp-route-webhookId="@wh.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-confirm-input="DELETE" text-translate="true">Delete</a> <a asp-action="DeleteWebhook" asp-route-storeId="@Context.GetRouteValue("storeId")" asp-route-webhookId="@wh.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">Delete</a>
</td> </td>
</tr> </tr>
} }

View File

@@ -165,7 +165,7 @@
<span asp-validation-for="StartDate" class="text-danger"></span> <span asp-validation-for="StartDate" class="text-danger"></span>
<span asp-validation-for="EndDate" class="text-danger"></span> <span asp-validation-for="EndDate" class="text-danger"></span>
</div> </div>
<div class="form-group mt-4" id="ResetRow" hidden="@(Model.StartDate == null)"> <div class="form-group mt-4" id="ResetRow" hidden="@(Model.StartDate == null)">
<div class="d-flex align-items-center mb-3"> <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" /> <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" />
@@ -175,7 +175,7 @@
<div class="text-muted" text-translate="true">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>
</div> </div>
<div class="collapse @(Model.IsRecurring ? "show" : "")" id="ResetEverySettings"> <div class="collapse @(Model.IsRecurring ? "show" : "")" id="ResetEverySettings">
<div class="form-group mb-0 pt-2 w-250px"> <div class="form-group mb-0 pt-2 w-250px">
<label asp-for="ResetEveryAmount" class="form-label"></label> <label asp-for="ResetEveryAmount" class="form-label"></label>
@@ -222,14 +222,14 @@
<label asp-for="EnforceTargetAmount" class="form-check-label"></label> <label asp-for="EnforceTargetAmount" class="form-check-label"></label>
<span asp-validation-for="EnforceTargetAmount" class="text-danger"></span> <span asp-validation-for="EnforceTargetAmount" class="text-danger"></span>
</div> </div>
<h3 class="mt-5 mb-4" text-translate="true">Crowdfund Behavior</h3> <h3 class="mt-5 mb-4" text-translate="true">Crowdfund Behavior</h3>
<div class="d-flex"> <div class="d-flex">
<input asp-for="UseAllStoreInvoices" type="checkbox" class="btcpay-toggle me-3" /> <input asp-for="UseAllStoreInvoices" type="checkbox" class="btcpay-toggle me-3" />
<label asp-for="UseAllStoreInvoices" class="form-check-label"></label> <label asp-for="UseAllStoreInvoices" class="form-check-label"></label>
<span asp-validation-for="UseAllStoreInvoices" class="text-danger"></span> <span asp-validation-for="UseAllStoreInvoices" class="text-danger"></span>
</div> </div>
<h3 class="mt-5 mb-4" text-translate="true">Checkout</h3> <h3 class="mt-5 mb-4" text-translate="true">Checkout</h3>
<div class="form-group"> <div class="form-group">
<label asp-for="FormId" class="form-label"></label> <label asp-for="FormId" class="form-label"></label>
@@ -384,8 +384,8 @@ Please insert valid HTML here. Only meta tags accepted.'>
} }
</button> </button>
</form> </form>
<a id="DeleteApp" class="btn btn-outline-danger" asp-controller="UIApps" asp-action="DeleteApp" asp-route-appId="@Model.AppId" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The app <strong>@Html.Encode(Model.AppName)</strong> and its settings will be permanently deleted." data-confirm-input="DELETE" permission="@Policies.CanModifyStoreSettings">Delete this app</a> <a id="DeleteApp" class="btn btn-outline-danger" asp-controller="UIApps" asp-action="DeleteApp" asp-route-appId="@Model.AppId" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The app <strong>@Html.Encode(Model.AppName)</strong> and its settings will be permanently deleted." data-confirm-input="@StringLocalizer["Delete"]" permission="@Policies.CanModifyStoreSettings">Delete this app</a>
</div> </div>
<partial name="_Confirm" model="@(new ConfirmModel("Delete app", "This app will be removed from this store.", "Delete"))" permission="@Policies.CanModifyStoreSettings" /> <partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["Delete app"], StringLocalizer["This app will be removed from this store."], StringLocalizer["Delete"]))" permission="@Policies.CanModifyStoreSettings" />

View File

@@ -39,15 +39,15 @@
if (confirmInput) { if (confirmInput) {
$text.removeAttribute('hidden') $text.removeAttribute('hidden')
$continue.setAttribute('disabled', 'disabled') $continue.setAttribute('disabled', 'disabled')
$inputText.textContent = confirmInput $inputText.textContent = confirmInput.toUpperCase()
$input.setAttribute("autocomplete", "off") $input.setAttribute("autocomplete", "off")
$input.addEventListener('input', event => { $input.addEventListener('input', event => {
event.target.value.trim() === confirmInput event.target.value.trim().toLowerCase() === confirmInput.toLowerCase()
? $continue.removeAttribute('disabled') ? $continue.removeAttribute('disabled')
: $continue.setAttribute('disabled', 'disabled') : $continue.setAttribute('disabled', 'disabled')
}) })
$form.addEventListener('submit', event => { $form.addEventListener('submit', event => {
if ($input.value.trim() !== confirmInput) { if ($input.value.trim().toLowerCase() !== confirmInput.toLowerCase()) {
event.preventDefault() event.preventDefault()
} }
}) })

View File

@@ -136,4 +136,4 @@
} }
</div> </div>
<partial name="_Confirm" model="@(new ConfirmModel("Delete app", "This app will be removed from this store.", "Delete"))" /> <partial name="_Confirm" model="@(new ConfirmModel("Delete app", "This app will be removed from this store.", StringLocalizer["Delete"]))" />

View File

@@ -41,7 +41,7 @@
<a asp-action="ViewPublicForm" asp-route-formId="@item.Id" id="View-@item.Name" not-permission="@Policies.CanModifyStoreSettings">@item.Name</a> <a asp-action="ViewPublicForm" asp-route-formId="@item.Id" id="View-@item.Name" not-permission="@Policies.CanModifyStoreSettings">@item.Name</a>
</td> </td>
<td class="actions-col" permission="@Policies.CanModifyStoreSettings"> <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" text-translate="true">Remove</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="@StringLocalizer["Delete"]" text-translate="true">Remove</a> -
<a asp-action="ViewPublicForm" asp-route-formId="@item.Id" id="View-@item.Name" text-translate="true">View</a> <a asp-action="ViewPublicForm" asp-route-formId="@item.Id" id="View-@item.Name" text-translate="true">View</a>
</td> </td>
</tr> </tr>
@@ -59,4 +59,4 @@
</div> </div>
</div> </div>
<partial name="_Confirm" model="@(new ConfirmModel("Delete form", "This form will be removed from this store.", "Delete"))" permission="@Policies.CanModifyStoreSettings" /> <partial name="_Confirm" model="@(new ConfirmModel("Delete form", "This form will be removed from this store.", StringLocalizer["Delete"]))" permission="@Policies.CanModifyStoreSettings" />

View File

@@ -15,8 +15,8 @@
@section PageFootContent { @section PageFootContent {
<script> <script>
delegate('click', '.remove', event => { delegate('click', '.remove', event => {
event.preventDefault() event.preventDefault()
const { name, value } = event.target const { name, value } = event.target
const confirmButton = document.getElementById('ConfirmContinue') const confirmButton = document.getElementById('ConfirmContinue')
confirmButton.setAttribute('name', name) confirmButton.setAttribute('name', name)
@@ -48,7 +48,7 @@
var showAddForm = !ViewContext.ViewData.ModelState.IsValid || !string.IsNullOrEmpty(Model.Add?.Username) || Model.Add?.Max != null || Model.Add?.Min != null || !string.IsNullOrEmpty(Model.Add?.CurrencyCode); var showAddForm = !ViewContext.ViewData.ModelState.IsValid || !string.IsNullOrEmpty(Model.Add?.Username) || Model.Add?.Max != null || Model.Add?.Min != null || !string.IsNullOrEmpty(Model.Add?.CurrencyCode);
var showAdvancedOptions = !string.IsNullOrEmpty(Model.Add?.CurrencyCode) || !string.IsNullOrEmpty(Model.Add?.InvoiceMetadata) || Model.Add?.Min != null || Model.Add?.Max != null; var showAdvancedOptions = !string.IsNullOrEmpty(Model.Add?.CurrencyCode) || !string.IsNullOrEmpty(Model.Add?.InvoiceMetadata) || Model.Add?.Min != null || Model.Add?.Max != null;
} }
<div class="collapse @(showAddForm ? "show": "")" id="AddAddress"> <div class="collapse @(showAddForm ? "show": "")" id="AddAddress">
<div class="form-group"> <div class="form-group">
<label asp-for="Add.Username" class="form-label"></label> <label asp-for="Add.Username" class="form-label"></label>
@@ -97,12 +97,12 @@
</div> </div>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<button type="submit" name="command" value="add" class="btn btn-primary">Save</button> <button type="submit" name="command" value="add" class="btn btn-primary">Save</button>
</div> </div>
</div> </div>
@if (Model.Items.Any()) @if (Model.Items.Any())
{ {
<table class="table table-hover"> <table class="table table-hover">
@@ -151,7 +151,7 @@
} }
</td> </td>
<td class="text-end"> <td class="text-end">
<button type="submit" title="Remove" name="command" value="@($"remove:{Model.Items[index].Username}")" class="btn btn-link px-0 remove" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The Lightning Address <strong>@Html.Encode(address)</strong> will be removed." data-confirm-input="REMOVE" text-translate="true"> <button type="submit" title="Remove" name="command" value="@($"remove:{Model.Items[index].Username}")" class="btn btn-link px-0 remove" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The Lightning Address <strong>@Html.Encode(address)</strong> will be removed." data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">
Remove Remove
</button> </button>
</td> </td>
@@ -168,4 +168,4 @@
} }
</form> </form>
<partial name="_Confirm" model="@(new ConfirmModel("Remove Lightning Address", "This Lightning Address will be removed.", "Remove"))" /> <partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["Remove Lightning Address"], StringLocalizer["This Lightning Address will be removed."], StringLocalizer["Delete"]))" />

View File

@@ -77,7 +77,7 @@
</td> </td>
<td> <td>
<div class="d-flex align-items-center justify-content-end gap-1"> <div class="d-flex align-items-center justify-content-end gap-1">
<a asp-action="DeleteAPIKey" asp-route-id="@keyData.Id" asp-controller="UIManage" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="Any application using the API key <strong>@Html.Encode(keyData.Label ?? keyData.Id)</strong> will immediately lose access." data-confirm-input="DELETE" text-translate="true">Delete</a> <a asp-action="DeleteAPIKey" asp-route-id="@keyData.Id" asp-controller="UIManage" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="Any application using the API key <strong>@Html.Encode(keyData.Label ?? keyData.Id)</strong> will immediately lose access." data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">Delete</a>
<span>-</span> <span>-</span>
<button type="button" class="btn btn-link only-for-js p-0" data-qr="@index" text-translate="true">Show QR</button> <button type="button" class="btn btn-link only-for-js p-0" data-qr="@index" text-translate="true">Show QR</button>
</div> </div>
@@ -91,7 +91,7 @@
</div> </div>
</div> </div>
<partial name="_Confirm" model="@(new ConfirmModel("Delete API key", "Any application using the API key will immediately lose access.", "Delete"))" /> <partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["Delete API key"], StringLocalizer["Any application using the API key will immediately lose access."], StringLocalizer["Delete"]))" />
<partial name="ShowQR" /> <partial name="ShowQR" />

View File

@@ -70,7 +70,7 @@
} }
<h3 class="mt-5 mb-4" text-translate="true">Delete Account</h3> <h3 class="mt-5 mb-4" text-translate="true">Delete Account</h3>
<div id="danger-zone"> <div id="danger-zone">
<a id="delete-user" class="btn btn-outline-danger mb-5" data-confirm-input="DELETE" data-bs-toggle="modal" data-bs-target="#ConfirmModal" asp-action="DeleteUserPost" data-description="@StringLocalizer["This action will also delete all stores, invoices, apps and data associated with the user."]" text-translate="true">Delete Account</a> <a id="delete-user" class="btn btn-outline-danger mb-5" data-confirm-input="@StringLocalizer["Delete"]" data-bs-toggle="modal" data-bs-target="#ConfirmModal" asp-action="DeleteUserPost" data-description="@StringLocalizer["This action will also delete all stores, invoices, apps and data associated with the user."]" text-translate="true">Delete Account</a>
</div> </div>
</div> </div>
</form> </form>

View File

@@ -115,11 +115,11 @@
@if (device.Type == Fido2Credential.CredentialType.FIDO2) @if (device.Type == Fido2Credential.CredentialType.FIDO2)
{ {
<a asp-controller="UIFido2" asp-action="Remove" asp-route-id="@device.Id" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-title="@StringLocalizer["Remove security device"]" data-description="@ViewLocalizer["Your account will no longer have the security device <strong>{0}</strong> as an option for two-factor authentication.", Html.Encode(name)]" data-confirm="@StringLocalizer["Remove"]" data-confirm-input="@StringLocalizer["REMOVE"]" text-translate="true">Remove</a> <a asp-controller="UIFido2" asp-action="Remove" asp-route-id="@device.Id" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-title="@StringLocalizer["Remove security device"]" data-description="@ViewLocalizer["Your account will no longer have the security device <strong>{0}</strong> as an option for two-factor authentication.", Html.Encode(name)]" data-confirm="@StringLocalizer["Delete"]" data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">Remove</a>
} }
else if (device.Type == Fido2Credential.CredentialType.LNURLAuth) else if (device.Type == Fido2Credential.CredentialType.LNURLAuth)
{ {
<a asp-controller="UILNURLAuth" asp-action="Remove" asp-route-id="@device.Id" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-title="@StringLocalizer["Remove Lightning security"]" data-description="@ViewLocalizer["Your account will no longer be linked to the lightning node <strong>{0}</strong> as an option for two-factor authentication.", Html.Encode(name)]" data-confirm="@StringLocalizer["Remove"]" data-confirm-input="@StringLocalizer["REMOVE"]" text-translate="true">Remove</a> <a asp-controller="UILNURLAuth" asp-action="Remove" asp-route-id="@device.Id" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-title="@StringLocalizer["Remove Lightning security"]" data-description="@ViewLocalizer["Your account will no longer be linked to the lightning node <strong>{0}</strong> as an option for two-factor authentication.", Html.Encode(name)]" data-confirm="@StringLocalizer["Delete"]" data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">Remove</a>
} }
</div> </div>
} }
@@ -131,7 +131,7 @@
<input type="text" class="form-control" name="Name" placeholder="@StringLocalizer["Security device name"]"/> <input type="text" class="form-control" name="Name" placeholder="@StringLocalizer["Security device name"]"/>
<select asp-items="@Html.GetEnumSelectList<Fido2Credential.CredentialType>()" class="form-select w-auto" name="type"></select> <select asp-items="@Html.GetEnumSelectList<Fido2Credential.CredentialType>()" class="form-select w-auto" name="type"></select>
<button id="btn-add" type="submit" class="btn btn-primary" text-translate="true"> <button id="btn-add" type="submit" class="btn btn-primary" text-translate="true">
Add Add
</button> </button>
</div> </div>
</form> </form>

View File

@@ -44,7 +44,7 @@
else else
{ {
<a id="Configure-@conf.Key" href="@processorsView.Factory.ConfigureLink(storeId, conf.Key, Context.Request)" text-translate="true">Modify</a> <a id="Configure-@conf.Key" href="@processorsView.Factory.ConfigureLink(storeId, conf.Key, Context.Request)" text-translate="true">Modify</a>
@if (await processorsView.Factory.CanRemove()) @if (await processorsView.Factory.CanRemove())
{ {
<span>-</span> <span>-</span>
@@ -67,7 +67,7 @@
</div> </div>
</div> </div>
<partial name="_Confirm" model="@(new ConfirmModel("Delete payout processor", "This payout processor will be removed from this store.", "Delete"))" permission="@Policies.CanModifyStoreSettings" /> <partial name="_Confirm" model="@(new ConfirmModel("Delete payout processor", "This payout processor will be removed from this store.", StringLocalizer["Delete"]))" permission="@Policies.CanModifyStoreSettings" />
@section PageFootContent { @section PageFootContent {
<partial name="_ValidationScriptsPartial"/> <partial name="_ValidationScriptsPartial"/>
} }

View File

@@ -32,11 +32,11 @@
<div class="form-group"> <div class="form-group">
<p text-translate="true">Dynamic DNS allows you to have a stable DNS name pointing to your server, even if your IP address changes regularly. This is recommended if you are hosting BTCPay Server at home and wish to have a clearnet domain to access your server.</p> <p text-translate="true">Dynamic DNS allows you to have a stable DNS name pointing to your server, even if your IP address changes regularly. This is recommended if you are hosting BTCPay Server at home and wish to have a clearnet domain to access your server.</p>
<p> <p>
Note that you need to properly configure your NAT and BTCPay Server installation to get the HTTPS certificate. Note that you need to properly configure your NAT and BTCPay Server installation to get the HTTPS certificate.
See the documentation for <a href="https://docs.btcpayserver.org/Deployment/DynamicDNS/" target="_blank" rel="noreferrer noopener">more information</a>. See the documentation for <a href="https://docs.btcpayserver.org/Deployment/DynamicDNS/" target="_blank" rel="noreferrer noopener">more information</a>.
</p> </p>
</div> </div>
@if (Model.Any()) @if (Model.Any())
{ {
<div class="table-responsive-md"> <div class="table-responsive-md">
@@ -68,7 +68,7 @@
<td class="text-end"> <td class="text-end">
<a asp-action="DynamicDnsService" asp-route-hostname="@service.Settings.Hostname" text-translate="true">Edit</a> <a asp-action="DynamicDnsService" asp-route-hostname="@service.Settings.Hostname" text-translate="true">Edit</a>
<span> - </span> <span> - </span>
<a asp-action="DeleteDynamicDnsService" asp-route-hostname="@service.Settings.Hostname" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="Deleting the dynamic DNS service for <strong>@Html.Encode(service.Settings.Hostname)</strong> means your BTCPay Server will stop updating the associated DNS record periodically." data-confirm-input="DELETE" text-translate="true">Delete</a> <a asp-action="DeleteDynamicDnsService" asp-route-hostname="@service.Settings.Hostname" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="Deleting the dynamic DNS service for <strong>@Html.Encode(service.Settings.Hostname)</strong> means your BTCPay Server will stop updating the associated DNS record periodically." data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">Delete</a>
</td> </td>
</tr> </tr>
} }
@@ -85,5 +85,5 @@
</div> </div>
</div> </div>
<partial name="_Confirm" model="@(new ConfirmModel("Delete dynamic DNS service", "Deleting the dynamic DNS service means your BTCPay Server will stop updating the associated DNS record periodically.", "Delete"))" /> <partial name="_Confirm" model="@(new ConfirmModel("Delete dynamic DNS service", "Deleting the dynamic DNS service means your BTCPay Server will stop updating the associated DNS record periodically.", StringLocalizer["Delete"]))" />

View File

@@ -55,7 +55,7 @@
} }
@if (v.Editable && !v.IsSelected) @if (v.Editable && !v.IsSelected)
{ {
<a id="Delete-@v.DictionaryName" asp-action="DeleteDictionary" asp-route-dictionary="@v.DictionaryName" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The dictionary <b>@Html.Encode(v.DictionaryName)</b> will be removed from this server." data-confirm-input="DELETE" text-translate="true">Remove</a> <a id="Delete-@v.DictionaryName" asp-action="DeleteDictionary" asp-route-dictionary="@v.DictionaryName" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The dictionary <b>@Html.Encode(v.DictionaryName)</b> will be removed from this server." data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">Remove</a>
} }
</div> </div>
</td> </td>
@@ -63,5 +63,5 @@
} }
</tbody> </tbody>
</table> </table>
<partial name="_Confirm" model="@(new ConfirmModel("Delete dictionary", "This dictionary will be removed from this server.", "Delete"))" /> <partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["Delete dictionary"], StringLocalizer["This dictionary will be removed from this server."], StringLocalizer["Delete"]))" />
</div> </div>

View File

@@ -166,5 +166,5 @@
<vc:pager view-model="Model"></vc:pager> <vc:pager view-model="Model"></vc:pager>
<partial name="_Confirm" model="@(new ConfirmModel("Send verification email", $"This will send a verification email to the user.", "Send"))" /> <partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["Send verification email"], StringLocalizer["This will send a verification email to the user."], StringLocalizer["Send"]))" />
<partial name="ShowQR" /> <partial name="ShowQR" />

View File

@@ -74,7 +74,7 @@
</div> </div>
</div> </div>
<partial name="_Confirm" model="@(new ConfirmModel("Disable modification of SSH settings", "This action is permanent and will remove the ability to change the SSH settings via the BTCPay Server user interface.", "Disable"))"/> <partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["Disable modification of SSH settings"], StringLocalizer["This action is permanent and will remove the ability to change the SSH settings via the BTCPay Server user interface."], StringLocalizer["Disable"]))"/>
@section PageFootContent { @section PageFootContent {
<script> <script>

View File

@@ -32,7 +32,7 @@
<span text-translate="true">To generate Greenfield API keys, please</span> <span text-translate="true">To generate Greenfield API keys, please</span>
<a asp-controller="UIManage" asp-action="APIKeys" text-translate="true">click here</a>. <a asp-controller="UIManage" asp-action="APIKeys" text-translate="true">click here</a>.
</p> </p>
<div class="d-flex align-items-center justify-content-between mt-5 mb-3"> <div class="d-flex align-items-center justify-content-between mt-5 mb-3">
<h3 class="mb-0">@ViewData["Title"]</h3> <h3 class="mb-0">@ViewData["Title"]</h3>
<a id="CreateNewToken" asp-action="CreateToken" class="btn btn-primary" role="button" asp-route-storeId="@Context.GetRouteValue("storeId")" permission="@Policies.CanModifyStoreSettings" text-translate="true"> <a id="CreateNewToken" asp-action="CreateToken" class="btn btn-primary" role="button" asp-route-storeId="@Context.GetRouteValue("storeId")" permission="@Policies.CanModifyStoreSettings" text-translate="true">
@@ -102,4 +102,4 @@
</div> </div>
</div> </div>
<partial name="_Confirm" model="@(new ConfirmModel("Revoke access token", "The access token will be revoked. Do you wish to continue?", "Revoke"))" permission="@Policies.CanModifyStoreSettings" /> <partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["Revoke access token"], StringLocalizer["The access token will be revoked. Do you wish to continue?"], StringLocalizer["Revoke"]))" permission="@Policies.CanModifyStoreSettings" />

View File

@@ -24,7 +24,7 @@
<div class="row"> <div class="row">
<div class="col-xxl-constrain col-xl-8"> <div class="col-xxl-constrain col-xl-8">
<p>@ViewLocalizer["Give other registered BTCPay Server users access to your store. See the {0} for granted permissions.", Html.ActionLink(StringLocalizer["roles"], "ListRoles", "UIStores", new { storeId })]</p> <p>@ViewLocalizer["Give other registered BTCPay Server users access to your store. See the {0} for granted permissions.", Html.ActionLink(StringLocalizer["roles"], "ListRoles", "UIStores", new { storeId })]</p>
@if (!ViewContext.ModelState.IsValid) @if (!ViewContext.ModelState.IsValid)
{ {
<div asp-validation-summary="All" class="@(ViewContext.ModelState.ErrorCount.Equals(1) ? "no-marker" : "")"></div> <div asp-validation-summary="All" class="@(ViewContext.ModelState.ErrorCount.Equals(1) ? "no-marker" : "")"></div>
@@ -56,7 +56,7 @@
<td class="actions-col" permission="@Policies.CanModifyStoreSettings"> <td class="actions-col" permission="@Policies.CanModifyStoreSettings">
<div class="d-inline-flex align-items-center gap-3"> <div class="d-inline-flex align-items-center gap-3">
<a asp-action="UpdateStoreUser" asp-route-storeId="@Model.StoreId" asp-route-userId="@user.Id" data-bs-toggle="modal" data-bs-target="#EditModal" data-user-email="@user.Email" data-user-role="@user.Role" text-translate="true">Change Role</a> <a asp-action="UpdateStoreUser" asp-route-storeId="@Model.StoreId" asp-route-userId="@user.Id" data-bs-toggle="modal" data-bs-target="#EditModal" data-user-email="@user.Email" data-user-role="@user.Role" text-translate="true">Change Role</a>
<a asp-action="DeleteStoreUser" asp-route-storeId="@Model.StoreId" asp-route-userId="@user.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="@StringLocalizer["This action will prevent {0} from accessing this store and its settings.", Html.Encode(user.Email)]" data-confirm-input="@StringLocalizer["REMOVE"]" text-translate="true">Remove</a> <a asp-action="DeleteStoreUser" asp-route-storeId="@Model.StoreId" asp-route-userId="@user.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="@StringLocalizer["This action will prevent {0} from accessing this store and its settings.", Html.Encode(user.Email)]" data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">Remove</a>
</div> </div>
</td> </td>
</tr> </tr>
@@ -104,7 +104,7 @@
const action = $target.dataset.action || ($target.nodeName === 'A' const action = $target.dataset.action || ($target.nodeName === 'A'
? $target.getAttribute('href') ? $target.getAttribute('href')
: $target.form.getAttribute('action')) : $target.form.getAttribute('action'))
if ($form && !$form.hasAttribute('action')) $form.setAttribute('action', action) if ($form && !$form.hasAttribute('action')) $form.setAttribute('action', action)
if (userEmail) $email.textContent = userEmail if (userEmail) $email.textContent = userEmail
if (userRole) $role.value = userRole if (userRole) $role.value = userRole

View File

@@ -67,8 +67,8 @@
data-bs-target="#ConfirmModal" data-bs-target="#ConfirmModal"
data-title="@StringLocalizer["Remove {0} wallet", Model.CryptoCode]" data-title="@StringLocalizer["Remove {0} wallet", Model.CryptoCode]"
data-description="@ViewData["RemoveDescription"]" data-description="@ViewData["RemoveDescription"]"
data-confirm="@StringLocalizer["Remove"]" data-confirm="@StringLocalizer["Delete"]"
data-confirm-input="@StringLocalizer["REMOVE"]" data-confirm-input="@StringLocalizer["Delete"]"
text-translate="true">Remove wallet</button> text-translate="true">Remove wallet</button>
</form> </form>
</div> </div>
@@ -176,7 +176,7 @@
} }
</form> </form>
<partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["{0} wallet", Model.CryptoCode], StringLocalizer["Change"], "Update"))" /> <partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["{0} wallet", Model.CryptoCode], StringLocalizer["Change"], StringLocalizer["Update"]))" />
<partial name="ShowQR"/> <partial name="ShowQR"/>
@section PageFootContent { @section PageFootContent {

View File

@@ -49,7 +49,7 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["Remove label"], StringLocalizer["This label will be removed from this wallet and its associated transactions."], StringLocalizer["Remove"]))" /> <partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["Remove label"], StringLocalizer["This label will be removed from this wallet and its associated transactions."], StringLocalizer["Delete"]))" />
} }
else else
{ {