UI: Move section navigation to sidebar (#5744)

* UI: Move section navigation to sidebar

* Scroll active nav link into view

* Move CTAs to top right

* Server Settings: Make Policies first page

* Responsive table fixes

* Spacing fixes

* Add breadcrumb samples

* store settings fixes

* payment request fixes

* updates pull payment title

* adds invoice detail fix

* updates server settings breadcrumbs + copy fix

* Don't open Server Settings on Plugins page

* Add breadcrumbs to pull payment views

* adds breadcrumbs to account

* server and store breadcrumb fixes

* fixes access tokens

* Fix payment processor breadcrumbs

* fixes webhook 404

* Final touches

* Fix test

* Add breadcrumb for email rules page

* Design system updates

---------

Co-authored-by: dstrukt <gfxdsign@gmail.com>
This commit is contained in:
d11n 2024-06-19 15:23:10 +02:00 committed by GitHub
parent aa9e1acb91
commit 0f8da123b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
149 changed files with 2200 additions and 1794 deletions

View File

@ -12,7 +12,7 @@ namespace BTCPayServer.Abstractions.Extensions
private const string ACTIVE_CATEGORY_KEY = "ActiveCategory";
private const string ACTIVE_PAGE_KEY = "ActivePage";
private const string ACTIVE_ID_KEY = "ActiveId";
private const string ActivePageClass = "active";
private const string ACTIVE_CLASS = "active";
public enum DateDisplayFormat
{
@ -55,50 +55,63 @@ namespace BTCPayServer.Abstractions.Extensions
viewData[ACTIVE_CATEGORY_KEY] = activeCategory;
}
public static string IsActiveCategory<T>(this ViewDataDictionary viewData, T category, object id = null)
public static bool IsActiveCategory(this ViewDataDictionary viewData, string category, object id = null)
{
return IsActiveCategory(viewData, category.ToString(), id);
}
public static string IsActiveCategory(this ViewDataDictionary viewData, string category, object id = null)
{
if (!viewData.ContainsKey(ACTIVE_CATEGORY_KEY))
{
return null;
}
if (!viewData.ContainsKey(ACTIVE_CATEGORY_KEY)) return false;
var activeId = viewData[ACTIVE_ID_KEY];
var activeCategory = viewData[ACTIVE_CATEGORY_KEY]?.ToString();
var categoryMatch = category.Equals(activeCategory, StringComparison.InvariantCultureIgnoreCase);
var idMatch = id == null || activeId == null || id.Equals(activeId);
return categoryMatch && idMatch ? ActivePageClass : null;
return categoryMatch && idMatch;
}
public static string IsActivePage<T>(this ViewDataDictionary viewData, T page, object id = null)
where T : IConvertible
public static bool IsActiveCategory<T>(this ViewDataDictionary viewData, T category, object id = null)
{
return IsActivePage(viewData, page.ToString(), page.GetType().ToString(), id);
return IsActiveCategory(viewData, category.ToString(), id);
}
public static string IsActivePage<T>(this ViewDataDictionary viewData, IEnumerable<T> pages, object id = null)
where T : IConvertible
public static bool IsActivePage(this ViewDataDictionary viewData, string page, string category, object id = null)
{
return pages.Any(page => IsActivePage(viewData, page.ToString(), page.GetType().ToString(), id) == ActivePageClass)
? ActivePageClass
: null;
}
public static string IsActivePage(this ViewDataDictionary viewData, string page, string category, object id = null)
{
if (!viewData.ContainsKey(ACTIVE_PAGE_KEY))
{
return null;
}
if (!viewData.ContainsKey(ACTIVE_PAGE_KEY)) return false;
var activeId = viewData[ACTIVE_ID_KEY];
var activePage = viewData[ACTIVE_PAGE_KEY]?.ToString();
var activeCategory = viewData[ACTIVE_CATEGORY_KEY]?.ToString();
var categoryAndPageMatch = (category == null || activeCategory.Equals(category, StringComparison.InvariantCultureIgnoreCase)) && page.Equals(activePage, StringComparison.InvariantCultureIgnoreCase);
var categoryAndPageMatch = page.Equals(activePage, StringComparison.InvariantCultureIgnoreCase) &&
(category == null || activeCategory != null && activeCategory.Equals(category, StringComparison.InvariantCultureIgnoreCase));
var idMatch = id == null || activeId == null || id.Equals(activeId);
return categoryAndPageMatch && idMatch ? ActivePageClass : null;
return categoryAndPageMatch && idMatch;
}
public static bool IsActivePage<T>(this ViewDataDictionary viewData, IEnumerable<T> pages, object id = null)
where T : IConvertible
{
return pages.Any(page => ActivePageClass(viewData, page.ToString(), page.GetType().ToString(), id) == ACTIVE_CLASS);
}
public static string ActiveCategoryClass<T>(this ViewDataDictionary viewData, T category, object id = null)
{
return ActiveCategoryClass(viewData, category.ToString(), id);
}
public static string ActiveCategoryClass(this ViewDataDictionary viewData, string category, object id = null)
{
return IsActiveCategory(viewData, category, id) ? ACTIVE_CLASS : null;
}
public static string ActivePageClass<T>(this ViewDataDictionary viewData, T page, object id = null)
where T : IConvertible
{
return ActivePageClass(viewData, page.ToString(), page.GetType().ToString(), id);
}
public static string ActivePageClass(this ViewDataDictionary viewData, string page, string category, object id = null)
{
return IsActivePage(viewData, page, category, id) ? ACTIVE_CLASS : null;
}
public static string ActivePageClass<T>(this ViewDataDictionary viewData, IEnumerable<T> pages, object id = null) where T : IConvertible
{
return IsActivePage(viewData, pages, id) ? ACTIVE_CLASS : null;
}
public static HtmlString ToBrowserDate(this DateTimeOffset date, string netFormat, string jsDateFormat = "short", string jsTimeFormat = "short")

View File

@ -74,14 +74,17 @@ namespace BTCPayServer.Tests
var controller = user.GetController<UIStoresController>();
var lightningVm = (LightningNodeViewModel)Assert.IsType<ViewResult>(controller.SetupLightningNode(user.StoreId, cryptoCode)).Model;
Assert.True(lightningVm.Enabled);
var response = await controller.SetLightningNodeEnabled(user.StoreId, cryptoCode, false);
Assert.IsType<RedirectToActionResult>(response);
// Get enabled state from settings
LightningSettingsViewModel lnSettingsModel;
var response = controller.LightningSettings(user.StoreId, cryptoCode);
var lnSettingsModel = (LightningSettingsViewModel)Assert.IsType<ViewResult>(response).Model;
Assert.NotNull(lnSettingsModel?.ConnectionString);
Assert.True(lnSettingsModel.Enabled);
lnSettingsModel.Enabled = false;
response = await controller.LightningSettings(lnSettingsModel);
Assert.IsType<RedirectToActionResult>(response);
response = controller.LightningSettings(user.StoreId, cryptoCode);
lnSettingsModel = (LightningSettingsViewModel)Assert.IsType<ViewResult>(response).Model;
Assert.NotNull(lnSettingsModel?.ConnectionString);
Assert.False(lnSettingsModel.Enabled);
// Setup wallet

View File

@ -199,8 +199,7 @@ retry:
Assert.Equal("Recommendation (Kraken)", rateSource.SelectedOption.Text);
rateSource.SelectByText("CoinGecko");
Driver.WaitForElement(By.Id("Create")).Click();
Driver.FindElement(By.Id("StoreNav-StoreSettings")).Click();
Driver.FindElement(By.Id($"SectionNav-{StoreNavPages.General.ToString()}")).Click();
Driver.FindElement(By.Id("StoreNav-General")).Click();
var storeId = Driver.WaitForElement(By.Id("Id")).GetAttribute("value");
if (keepId)
StoreId = storeId;
@ -330,10 +329,11 @@ retry:
Assert.Contains($"{cryptoCode} Lightning node updated.", FindAlertMessage().Text);
var enabled = Driver.FindElement(By.Id($"{cryptoCode}LightningEnabled"));
if (enabled.Text == "Enable")
if (enabled.Selected == false)
{
enabled.Click();
Assert.Contains($"{cryptoCode} Lightning payments are now enabled for this store.", FindAlertMessage().Text);
Driver.FindElement(By.Id("save")).Click();
Assert.Contains($"{cryptoCode} Lightning settings successfully updated", FindAlertMessage().Text);
}
}
@ -418,23 +418,11 @@ retry:
WalletId = new WalletId(storeId, WalletId.CryptoCode);
}
Driver.FindElement(By.Id("StoreNav-StoreSettings")).Click();
if (storeNavPage != StoreNavPages.General)
{
switch (storeNavPage)
{
case StoreNavPages.Dashboard:
case StoreNavPages.Payouts:
case StoreNavPages.PayButton:
case StoreNavPages.PullPayments:
Driver.FindElement(By.Id($"StoreNav-{storeNavPage.ToString()}")).Click();
break;
default:
Driver.FindElement(By.Id($"SectionNav-{storeNavPage.ToString()}")).Click();
break;
}
Driver.FindElement(By.Id($"StoreNav-{StoreNavPages.General}")).Click();
}
Driver.FindElement(By.Id($"StoreNav-{storeNavPage}")).Click();
}
public void GoToWalletSettings(string cryptoCode = "BTC")
@ -450,9 +438,9 @@ retry:
{
Driver.FindElement(By.Id($"StoreNav-Lightning{cryptoCode}")).Click();
// if Lightning is already set up we need to navigate to the settings
if (Driver.PageSource.Contains("id=\"SectionNav-LightningSettings\""))
if (Driver.PageSource.Contains("id=\"StoreNav-LightningSettings\""))
{
Driver.FindElement(By.Id("SectionNav-LightningSettings")).Click();
Driver.FindElement(By.Id("StoreNav-LightningSettings")).Click();
}
}
@ -614,10 +602,10 @@ retry:
Driver.Navigate().GoToUrl(new Uri(ServerUri, relativeUrl));
}
public void GoToServer(ServerNavPages navPages = ServerNavPages.Index)
public void GoToServer(ServerNavPages navPages = ServerNavPages.Policies)
{
Driver.FindElement(By.Id("Nav-ServerSettings")).Click();
if (navPages != ServerNavPages.Index)
if (navPages != ServerNavPages.Policies)
{
Driver.FindElement(By.Id($"SectionNav-{navPages}")).Click();
}

View File

@ -63,9 +63,7 @@ namespace BTCPayServer.Tests
s.GoToServer();
s.Driver.AssertNoError();
s.ClickOnAllSectionLinks();
s.GoToServer();
s.Driver.FindElement(By.LinkText("Services")).Click();
s.GoToServer(ServerNavPages.Services);
TestLogs.LogInformation("Let's check if we can access the logs");
s.Driver.FindElement(By.LinkText("Logs")).Click();
s.Driver.FindElement(By.PartialLinkText(".log")).Click();
@ -257,10 +255,8 @@ namespace BTCPayServer.Tests
await s.StartAsync();
s.RegisterNewUser(true);
s.GoToHome();
s.GoToServer();
s.GoToServer(ServerNavPages.Services);
s.Driver.AssertNoError();
s.Driver.FindElement(By.LinkText("Services")).Click();
TestLogs.LogInformation("Let's if we can access LND's seed");
Assert.Contains("server/services/lndseedbackup/BTC", s.Driver.PageSource);
s.Driver.Navigate().GoToUrl(s.Link("/server/services/lndseedbackup/BTC"));
@ -609,6 +605,12 @@ namespace BTCPayServer.Tests
// Ensure empty server settings
s.Driver.Navigate().GoToUrl(s.Link("/server/emails"));
if (s.Driver.PageSource.Contains("id=\"ResetPassword\""))
{
s.Driver.FindElement(By.Id("ResetPassword")).Click();
Assert.Contains("Email server password reset", s.FindAlertMessage().Text);
}
s.Driver.FindElement(By.Id("Settings_Login")).Clear();
s.Driver.FindElement(By.Id("Settings_Password")).Clear();
s.Driver.FindElement(By.Id("Settings_From")).Clear();
@ -3429,7 +3431,7 @@ retry:
Assert.DoesNotContain(guestBadges, element => element.Text.Equals("Default", StringComparison.InvariantCultureIgnoreCase));
Assert.Contains(guestBadges, element => element.Text.Equals("Server-wide", StringComparison.InvariantCultureIgnoreCase));
guestRow.FindElement(By.Id("SetDefault")).Click();
s.FindAlertMessage();
Assert.Contains("Role set default", s.FindAlertMessage().Text);
existingServerRoles = s.Driver.FindElement(By.CssSelector("table")).FindElements(By.CssSelector("tr"));
foreach (var roleItem in existingServerRoles)
@ -3451,6 +3453,8 @@ retry:
ownerRow.FindElement(By.Id("SetDefault")).Click();
s.FindAlertMessage();
Assert.Contains("Role set default", s.FindAlertMessage().Text);
s.CreateNewStore();
s.GoToStore(StoreNavPages.Roles);
var existingStoreRoles = s.Driver.FindElement(By.CssSelector("table")).FindElements(By.CssSelector("tr"));

View File

@ -1,9 +1,5 @@
@using BTCPayServer.Abstractions.Extensions;
@using BTCPayServer.Configuration;
@using Microsoft.AspNetCore.Hosting;
@using Microsoft.AspNetCore.Mvc.Routing;
@using Microsoft.AspNetCore.Mvc.ViewFeatures;
@using Microsoft.AspNetCore.Mvc;
@inject IFileVersionProvider FileVersionProvider
@inject BTCPayServerOptions BTCPayServerOptions

View File

@ -1,6 +1,4 @@
@using BTCPayServer.Payments
@using BTCPayServer.Services.Invoices
@using BTCPayServer.Abstractions.Extensions
@model BTCPayServer.Components.InvoiceStatus.InvoiceStatusViewModel
@inject Dictionary<PaymentMethodId, IPaymentModelExtension> Extensions

View File

@ -10,7 +10,9 @@
@using BTCPayServer.Plugins
@using BTCPayServer.Services
@using BTCPayServer.Views.Apps
@using BTCPayServer.Configuration
@inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContext;
@inject BTCPayServerOptions BtcPayServerOptions
@inject BTCPayServerEnvironment Env
@inject SignInManager<ApplicationUser> SignInManager
@inject PoliciesSettings PoliciesSettings
@ -30,17 +32,48 @@
<div class="accordion-body">
<ul class="navbar-nav">
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
<a asp-area="" asp-controller="UIStores" asp-action="Dashboard" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.Dashboard)" id="StoreNav-Dashboard">
<a id="StoreNav-@(nameof(StoreNavPages.Dashboard))" asp-area="" asp-controller="UIStores" asp-action="Dashboard" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.ActivePageClass(StoreNavPages.Dashboard)">
<vc:icon symbol="nav-dashboard"/>
<span>Dashboard</span>
</a>
</li>
<li class="nav-item" permission="@Policies.CanViewStoreSettings">
<a asp-area="" asp-controller="UIStores" asp-action="GeneralSettings" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(new [] {StoreNavPages.Rates, StoreNavPages.CheckoutAppearance, StoreNavPages.General, StoreNavPages.Tokens, StoreNavPages.Users, StoreNavPages.Webhooks, StoreNavPages.PayoutProcessors, StoreNavPages.Emails})" id="StoreNav-StoreSettings">
<a id="StoreNav-@(nameof(StoreNavPages.General))" asp-area="" asp-controller="UIStores" asp-action="GeneralSettings" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.ActivePageClass(StoreNavPages.General)">
<vc:icon symbol="nav-store-settings"/>
<span>Settings</span>
</a>
</li>
@if (ViewData.IsActivePage([StoreNavPages.General, StoreNavPages.Rates, StoreNavPages.CheckoutAppearance, StoreNavPages.Tokens, StoreNavPages.Users, StoreNavPages.Roles, StoreNavPages.Webhooks, StoreNavPages.PayoutProcessors, StoreNavPages.Emails, StoreNavPages.Forms]))
{
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
<a id="StoreNav-@(nameof(StoreNavPages.Rates))" class="nav-link @ViewData.ActivePageClass(StoreNavPages.Rates)" asp-controller="UIStores" asp-action="Rates" asp-route-storeId="@Model.Store.Id">Rates</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
<a id="StoreNav-@(nameof(StoreNavPages.CheckoutAppearance))" class="nav-link @ViewData.ActivePageClass(StoreNavPages.CheckoutAppearance)" asp-controller="UIStores" asp-action="CheckoutAppearance" asp-route-storeId="@Model.Store.Id">Checkout Appearance</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
<a id="StoreNav-@(nameof(StoreNavPages.Tokens))" class="nav-link @ViewData.ActivePageClass(StoreNavPages.Tokens)" asp-controller="UIStores" asp-action="ListTokens" asp-route-storeId="@Model.Store.Id">Access Tokens</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
<a id="StoreNav-@(nameof(StoreNavPages.Users))" class="nav-link @ViewData.ActivePageClass(StoreNavPages.Users)" asp-controller="UIStores" asp-action="StoreUsers" asp-route-storeId="@Model.Store.Id">Users</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
<a id="StoreNav-@(nameof(StoreNavPages.Roles))" class="nav-link @ViewData.ActivePageClass(StoreNavPages.Roles)" asp-controller="UIStores" asp-action="ListRoles" asp-route-storeId="@Model.Store.Id">Roles</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
<a id="StoreNav-@(nameof(StoreNavPages.Webhooks))" class="nav-link @ViewData.ActivePageClass(StoreNavPages.Webhooks)" asp-controller="UIStores" asp-action="Webhooks" asp-route-storeId="@Model.Store.Id">Webhooks</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
<a id="StoreNav-@(nameof(StoreNavPages.PayoutProcessors))" class="nav-link @ViewData.ActivePageClass(StoreNavPages.PayoutProcessors)" asp-controller="UIPayoutProcessors" asp-action="ConfigureStorePayoutProcessors" asp-route-storeId="@Model.Store.Id">Payout Processors</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
<a id="StoreNav-@(nameof(StoreNavPages.Emails))" class="nav-link @ViewData.ActivePageClass(StoreNavPages.Emails)" asp-controller="UIStores" asp-action="StoreEmailSettings" asp-route-storeId="@Model.Store.Id">Emails</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
<a id="StoreNav-@(nameof(StoreNavPages.Forms))" class="nav-link @ViewData.ActivePageClass(StoreNavPages.Forms)" asp-controller="UIForms" asp-action="FormsList" asp-route-storeId="@Model.Store.Id">Forms</a>
</li>
}
<vc:ui-extension-point location="store-nav" model="@Model"/>
</ul>
</div>
</div>
@ -56,22 +89,36 @@
@foreach (var scheme in Model.DerivationSchemes.OrderBy(scheme => scheme.Collapsed))
{
var isSetUp = !string.IsNullOrWhiteSpace(scheme.Value);
var categoryId = $"{Model.Store.Id}-{scheme.WalletId.CryptoCode}";
<li class="nav-item">
@if (isSetUp && scheme.WalletSupported)
{
<a asp-area="" asp-controller="UIWallets" asp-action="WalletTransactions" asp-route-walletId="@scheme.WalletId" class="nav-link @ViewData.IsActiveCategory(typeof(WalletsNavPages), scheme.WalletId.ToString()) @ViewData.IsActivePage(StoreNavPages.OnchainSettings)" id="@($"StoreNav-Wallet{scheme.Crypto}")">
<a asp-area="" asp-controller="UIWallets" asp-action="WalletTransactions" asp-route-walletId="@scheme.WalletId" class="nav-link @ViewData.ActivePageClass([WalletsNavPages.Transactions], scheme.WalletId.ToString())" id="@($"StoreNav-Wallet{scheme.Crypto}")">
<span class="me-2 btcpay-status btcpay-status--@(scheme.Enabled ? "enabled" : "pending")"></span>
<span>@PrettyName.PrettyName(scheme.PaymentMethodId)</span>
</a>
}
else
{
<a asp-area="" asp-controller="UIStores" asp-action="SetupWallet" asp-route-cryptoCode="@scheme.Crypto" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.OnchainSettings)" id="@($"StoreNav-Wallet{scheme.Crypto}")">
<a asp-area="" asp-controller="UIStores" asp-action="SetupWallet" asp-route-cryptoCode="@scheme.Crypto" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.ActivePageClass(StoreNavPages.OnchainSettings)" id="@($"StoreNav-Wallet{scheme.Crypto}")">
<span class="me-2 btcpay-status btcpay-status--@(scheme.Enabled ? "enabled" : "pending")"></span>
<span>@PrettyName.PrettyName(scheme.PaymentMethodId)</span>
</a>
}
</li>
@if (ViewData.IsActiveCategory(typeof(WalletsNavPages), scheme.WalletId.ToString()) || ViewData.IsActivePage([WalletsNavPages.Settings], scheme.WalletId.ToString()) || ViewData.IsActivePage([StoreNavPages.OnchainSettings], categoryId))
{
<li class="nav-item nav-item-sub">
<a id="WalletNav-Send" class="nav-link @ViewData.ActivePageClass([WalletsNavPages.Send, WalletsNavPages.PSBT], scheme.WalletId.ToString())" asp-area="" asp-controller="UIWallets" asp-action="WalletSend" asp-route-walletId="@scheme.WalletId">Send</a>
</li>
<li class="nav-item nav-item-sub">
<a id="WalletNav-Receive" class="nav-link @ViewData.ActivePageClass(WalletsNavPages.Receive, scheme.WalletId.ToString())" asp-area="" asp-controller="UIWallets" asp-action="WalletReceive" asp-route-walletId="@scheme.WalletId">Receive</a>
</li>
<li class="nav-item nav-item-sub">
<a id="WalletNav-Settings" class="nav-link @ViewData.ActivePageClass(WalletsNavPages.Settings, scheme.WalletId.ToString()) @ViewData.ActivePageClass(StoreNavPages.OnchainSettings, categoryId)" asp-area="" asp-controller="UIStores" asp-action="WalletSettings" asp-route-cryptoCode="@scheme.WalletId.CryptoCode" asp-route-storeId="@scheme.WalletId.StoreId">Settings</a>
</li>
<vc:ui-extension-point location="wallet-nav" model="@Model" />
}
}
@foreach (var scheme in Model.LightningNodes)
{
@ -82,20 +129,26 @@
var status = scheme.Enabled
? scheme.Available ? "enabled" : "disabled"
: "pending";
<a asp-area="" asp-controller="UIStores" asp-action="Lightning" asp-route-cryptoCode="@scheme.CryptoCode" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.Lightning) @ViewData.IsActivePage(StoreNavPages.LightningSettings)" id="@($"StoreNav-Lightning{scheme.CryptoCode}")">
<a asp-area="" asp-controller="UIStores" asp-action="Lightning" asp-route-cryptoCode="@scheme.CryptoCode" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.ActivePageClass(StoreNavPages.Lightning, $"{Model.Store.Id}-{scheme.CryptoCode}")" id="@($"StoreNav-Lightning{scheme.CryptoCode}")">
<span class="me-2 btcpay-status btcpay-status--@status"></span>
<span>@PrettyName.PrettyName(scheme.PaymentMethodId)</span>
</a>
}
else
{
<a asp-area="" asp-controller="UIStores" asp-action="SetupLightningNode" asp-route-cryptoCode="@scheme.CryptoCode" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.LightningSettings)" id="@($"StoreNav-Lightning{scheme.CryptoCode}")">
<a asp-area="" asp-controller="UIStores" asp-action="SetupLightningNode" asp-route-cryptoCode="@scheme.CryptoCode" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.ActivePageClass(StoreNavPages.LightningSettings, $"{Model.Store.Id}-{scheme.CryptoCode}")" id="@($"StoreNav-Lightning{scheme.CryptoCode}")">
<span class="me-2 btcpay-status btcpay-status--@(scheme.Enabled ? "enabled" : "pending")"></span>
<span>@PrettyName.PrettyName(scheme.PaymentMethodId)</span>
</a>
}
</li>
@if (ViewData.IsActivePage([StoreNavPages.Lightning, StoreNavPages.LightningSettings], $"{Model.Store.Id}-{scheme.CryptoCode}"))
{
<li class="nav-item nav-item-sub">
<a id="StoreNav-@(nameof(StoreNavPages.LightningSettings))" class="nav-link @ViewData.ActivePageClass(StoreNavPages.LightningSettings)" asp-controller="UIStores" asp-action="LightningSettings" asp-route-storeId="@Model.Store.Id" asp-route-cryptoCode="@scheme.CryptoCode">Settings</a>
</li>
<vc:ui-extension-point location="lightning-nav" model="@Model"/>
}
}
<vc:ui-extension-point location="store-wallets-nav" model="@Model"/>
</ul>
@ -112,25 +165,25 @@
<div class="accordion-body">
<ul class="navbar-nav">
<li class="nav-item" permission="@Policies.CanViewInvoices">
<a asp-area="" asp-controller="UIInvoice" asp-action="ListInvoices" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActiveCategory(typeof(InvoiceNavPages))" id="StoreNav-Invoices">
<a asp-area="" asp-controller="UIInvoice" asp-action="ListInvoices" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.ActiveCategoryClass(typeof(InvoiceNavPages))" id="StoreNav-Invoices">
<vc:icon symbol="nav-invoices"/>
<span>Invoices</span>
</a>
</li>
<li class="nav-item" permission="@Policies.CanViewReports">
<a asp-area="" asp-controller="UIReports" asp-action="StoreReports" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.Reporting)" id="SectionNav-Reporting">
<vc:icon symbol="nav-reporting" />
<span>Reporting</span>
</a>
</li>
<li class="nav-item" permission="@Policies.CanViewReports">
<a asp-area="" asp-controller="UIReports" asp-action="StoreReports" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.ActivePageClass(StoreNavPages.Reporting)" id="SectionNav-Reporting">
<vc:icon symbol="nav-reporting" />
<span>Reporting</span>
</a>
</li>
<li class="nav-item" permission="@Policies.CanViewPaymentRequests">
<a asp-area="" asp-controller="UIPaymentRequest" asp-action="GetPaymentRequests" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActiveCategory(typeof(PaymentRequestsNavPages))" id="StoreNav-PaymentRequests">
<a asp-area="" asp-controller="UIPaymentRequest" asp-action="GetPaymentRequests" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.ActiveCategoryClass(typeof(PaymentRequestsNavPages))" id="StoreNav-PaymentRequests">
<vc:icon symbol="nav-payment-requests"/>
<span>Requests</span>
</a>
</li>
<li class="nav-item" permission="@Policies.CanViewPullPayments">
<a asp-area="" asp-controller="UIStorePullPayments" asp-action="PullPayments" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.PullPayments)" id="StoreNav-PullPayments">
<a asp-area="" asp-controller="UIStorePullPayments" asp-action="PullPayments" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.ActivePageClass(StoreNavPages.PullPayments)" id="StoreNav-PullPayments">
<vc:icon symbol="nav-pull-payments"/>
<span>Pull Payments</span>
</a>
@ -139,7 +192,7 @@
<a asp-area=""
asp-controller="UIStorePullPayments" asp-action="Payouts"
asp-route-pullPaymentId=""
asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.Payouts)" id="StoreNav-Payouts">
asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.ActivePageClass(StoreNavPages.Payouts)" id="StoreNav-Payouts">
<vc:icon symbol="nav-payouts"/>
<span>Payouts</span>
</a>
@ -168,7 +221,7 @@
</ul>
<ul class="navbar-nav">
<li class="nav-item" permission="@Policies.CanModifyServerSettings">
<a asp-area="" asp-controller="UIServer" asp-action="ListPlugins" class="nav-link @ViewData.IsActivePage(ServerNavPages.Plugins)" id="Nav-ManagePlugins">
<a asp-area="" asp-controller="UIServer" asp-action="ListPlugins" class="nav-link @ViewData.ActivePageClass(ServerNavPages.Plugins)" id="Nav-ManagePlugins">
@if (PluginService.GetDisabledPlugins().Any())
{
<span class="me-2 btcpay-status btcpay-status--disabled"></span>
@ -183,7 +236,7 @@
@if (Model.Store != null && Model.ArchivedAppsCount > 0)
{
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
<a asp-area="" asp-controller="UIApps" asp-action="ListApps" asp-route-storeId="@Model.Store.Id" asp-route-archived="true" class="nav-link @ViewData.IsActivePage(AppsNavPages.Index)" id="Nav-ArchivedApps">
<a asp-area="" asp-controller="UIApps" asp-action="ListApps" asp-route-storeId="@Model.Store.Id" asp-route-archived="true" class="nav-link @ViewData.ActivePageClass(AppsNavPages.Index)" id="Nav-ArchivedApps">
@Model.ArchivedAppsCount Archived App@(Model.ArchivedAppsCount == 1 ? "" : "s")
</a>
</li>
@ -234,13 +287,44 @@
{
<ul id="mainNavSettings" class="navbar-nav border-top p-3 px-lg-4">
<li class="nav-item" permission="@Policies.CanModifyServerSettings">
<a asp-area="" asp-controller="UIServer" asp-action="ListUsers" class="nav-link @ViewData.IsActivePage(ServerNavPages.Users) @ViewData.IsActivePage(ServerNavPages.Emails) @ViewData.IsActivePage(ServerNavPages.Policies) @ViewData.IsActivePage(ServerNavPages.Services) @ViewData.IsActivePage(ServerNavPages.Branding) @ViewData.IsActivePage(ServerNavPages.Maintenance) @ViewData.IsActivePage(ServerNavPages.Logs) @ViewData.IsActivePage(ServerNavPages.Files)" id="Nav-ServerSettings">
<a asp-area="" asp-controller="UIServer" asp-action="Policies" class="nav-link @ViewData.ActivePageClass(ServerNavPages.Policies)" id="Nav-ServerSettings">
<vc:icon symbol="nav-server-settings"/>
<span>Server Settings</span>
</a>
</li>
@if (ViewData.IsActiveCategory(typeof(ServerNavPages)) && !ViewData.IsActivePage([ServerNavPages.Plugins]))
{
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyServerSettings">
<a asp-controller="UIServer" id="SectionNav-@ServerNavPages.Users" class="nav-link @ViewData.ActivePageClass(ServerNavPages.Users)" asp-action="ListUsers">Users</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyServerSettings">
<a asp-controller="UIServer" id="SectionNav-@ServerNavPages.Roles" class="nav-link @ViewData.ActivePageClass(ServerNavPages.Roles)" asp-action="ListRoles">Roles</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyServerSettings">
<a asp-controller="UIServer" id="SectionNav-@ServerNavPages.Emails" class="nav-link @ViewData.ActivePageClass(ServerNavPages.Emails)" asp-action="Emails">Email</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyServerSettings">
<a asp-controller="UIServer" id="SectionNav-@ServerNavPages.Services" class="nav-link @ViewData.ActivePageClass(ServerNavPages.Services)" asp-action="Services">Services</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyServerSettings">
<a asp-controller="UIServer" id="SectionNav-@ServerNavPages.Branding" class="nav-link @ViewData.ActivePageClass(ServerNavPages.Branding)" asp-action="Branding">Branding</a>
</li>
@if (BtcPayServerOptions.DockerDeployment)
{
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyServerSettings">
<a asp-controller="UIServer" id="SectionNav-@ServerNavPages.Maintenance" class="nav-link @ViewData.ActivePageClass(ServerNavPages.Maintenance)" asp-action="Maintenance">Maintenance</a>
</li>
}
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyServerSettings">
<a asp-controller="UIServer" id="SectionNav-@ServerNavPages.Logs" class="nav-link @ViewData.ActivePageClass(ServerNavPages.Logs)" asp-action="LogsView">Logs</a>
</li>
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyServerSettings">
<a asp-controller="UIServer" id="SectionNav-@ServerNavPages.Files" class="nav-link @ViewData.ActivePageClass(ServerNavPages.Files)" asp-action="Files">Files</a>
</li>
}
<vc:ui-extension-point location="server-nav" model="@Model"/>
<li class="nav-item dropup">
<a class="nav-link @ViewData.IsActiveCategory(typeof(ManageNavPages))" role="button" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false" id="Nav-Account">
<a class="nav-link @ViewData.ActivePageClass(ManageNavPages.Index)" role="button" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false" id="Nav-Account">
<vc:icon symbol="nav-account"/>
<span>Account</span>
</a>
@ -268,7 +352,7 @@
</script>
</li>
<li class="border-top py-1 px-3">
<a asp-area="" asp-controller="UIManage" asp-action="Index" class="nav-link @ViewData.IsActiveCategory(typeof(ManageNavPages))" id="Nav-ManageAccount">
<a asp-area="" asp-controller="UIManage" asp-action="Index" class="nav-link" id="Nav-ManageAccount">
<span>Manage Account</span>
</a>
</li>
@ -279,6 +363,25 @@
</li>
</ul>
</li>
@if (ViewData.IsActiveCategory(typeof(ManageNavPages)) || ViewData.IsActivePage([ManageNavPages.ChangePassword]))
{
<li class="nav-item nav-item-sub">
<a id="SectionNav-@ManageNavPages.ChangePassword.ToString()" class="nav-link @ViewData.ActivePageClass(ManageNavPages.ChangePassword)" asp-controller="UIManage" asp-action="ChangePassword">Password</a>
</li>
<li class="nav-item nav-item-sub">
<a id="SectionNav-@ManageNavPages.TwoFactorAuthentication.ToString()" class="nav-link @ViewData.ActivePageClass(ManageNavPages.TwoFactorAuthentication)" asp-controller="UIManage" asp-action="TwoFactorAuthentication">Two-Factor Authentication</a>
</li>
<li class="nav-item nav-item-sub">
<a id="SectionNav-@ManageNavPages.APIKeys.ToString()" class="nav-link @ViewData.ActivePageClass(ManageNavPages.APIKeys)" asp-controller="UIManage" asp-action="APIKeys">API Keys</a>
</li>
<li class="nav-item nav-item-sub">
<a id="SectionNav-@ManageNavPages.Notifications.ToString()" class="nav-link @ViewData.ActivePageClass(ManageNavPages.Notifications)" asp-controller="UIManage" asp-action="NotificationSettings">Notifications</a>
</li>
<li class="nav-item nav-item-sub">
<a id="SectionNav-@ManageNavPages.LoginCodes.ToString()" class="nav-link @ViewData.ActivePageClass(ManageNavPages.LoginCodes)" asp-controller="UIManage" asp-action="LoginCodes">Login Codes</a>
</li>
<vc:ui-extension-point location="user-nav" model="@Model" />
}
@if (!string.IsNullOrWhiteSpace(Model.ContactUrl))
{
<li class="nav-item">
@ -291,3 +394,9 @@
</ul>
}
</nav>
<script>
(function () {
const activeEl = document.querySelector('#mainNav .nav-link.active')
if (activeEl) activeEl.scrollIntoView({ block: 'center', inline: 'center' })
})()
</script>

View File

@ -1,9 +1,7 @@
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Client.Models
@using BTCPayServer.Services
@using BTCPayServer.Services.Invoices
@inject DisplayFormatter DisplayFormatter
@inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
@model BTCPayServer.Components.StoreRecentInvoices.StoreRecentInvoicesViewModel
<div class="widget store-recent-invoices" id="StoreRecentInvoices-@Model.Store.Id">

View File

@ -1,4 +1,3 @@
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Services
@inject DisplayFormatter DisplayFormatter
@model BTCPayServer.Components.StoreRecentTransactions.StoreRecentTransactionsViewModel

View File

@ -51,22 +51,22 @@ else
@foreach (var option in Model.Options)
{
<li>
<a asp-controller="UIStores" asp-action="Index" asp-route-storeId="@option.Value" class="dropdown-item@(option.Selected && ViewData.IsActivePage(ServerNavPages.Stores) != "active" ? " active" : "")" id="StoreSelectorMenuItem-@option.Value">@StoreName(option.Text)</a>
<a asp-controller="UIStores" asp-action="Index" asp-route-storeId="@option.Value" class="dropdown-item@(option.Selected && ViewData.ActivePageClass(ServerNavPages.Stores) != "active" ? " active" : "")" id="StoreSelectorMenuItem-@option.Value">@StoreName(option.Text)</a>
</li>
}
@if (Model.Options.Any())
{
<li><hr class="dropdown-divider"></li>
}
<li><a asp-controller="UIUserStores" asp-action="CreateStore" class="dropdown-item @ViewData.IsActivePage(StoreNavPages.Create)" id="StoreSelectorCreate">Create Store</a></li>
<li><a asp-controller="UIUserStores" asp-action="CreateStore" class="dropdown-item @ViewData.ActivePageClass(StoreNavPages.Create)" id="StoreSelectorCreate">Create Store</a></li>
@if (Model.ArchivedCount > 0)
{
<li><hr class="dropdown-divider"></li>
<li><a asp-controller="UIUserStores" asp-action="ListStores" asp-route-archived="true" class="dropdown-item @ViewData.IsActivePage(StoreNavPages.Index)" id="StoreSelectorArchived">@Model.ArchivedCount Archived Store@(Model.ArchivedCount == 1 ? "" : "s")</a></li>
<li><a asp-controller="UIUserStores" asp-action="ListStores" asp-route-archived="true" class="dropdown-item @ViewData.ActivePageClass(StoreNavPages.Index)" id="StoreSelectorArchived">@Model.ArchivedCount Archived Store@(Model.ArchivedCount == 1 ? "" : "s")</a></li>
}
@*
<li permission="@Policies.CanModifyServerSettings"><hr class="dropdown-divider"></li>
<li permission="@Policies.CanModifyServerSettings"><a asp-controller="UIServer" asp-action="ListStores" class="dropdown-item @ViewData.IsActivePage(ServerNavPages.Stores)" id="StoreSelectorAdminStores">Admin Store Overview</a></li>
<li permission="@Policies.CanModifyServerSettings"><a asp-controller="UIServer" asp-action="ListStores" class="dropdown-item @ViewData.ActivePageClass(ServerNavPages.Stores)" id="StoreSelectorAdminStores">Admin Store Overview</a></li>
*@
</ul>
</div>

View File

@ -1,31 +1,14 @@
@using BTCPayServer.Services;
@using BTCPayServer.Views.Stores
@using BTCPayServer.Client
@using BTCPayServer.Views.Wallets
@using BTCPayServer.Services
@inject DisplayFormatter DisplayFormatter
@model BTCPayServer.Components.WalletNav.WalletNavViewModel
<div class="d-sm-flex align-items-center justify-content-between">
<a asp-controller="UIWallets" asp-action="WalletTransactions" asp-route-walletId="@Model.WalletId" class="unobtrusive-link">
<h2 class="mb-1">@Model.Label</h2>
<div class="text-muted fw-semibold" data-sensitive>
@DisplayFormatter.Currency(Model.Balance, Model.Network.CryptoCode)
@if (!string.IsNullOrEmpty(Model.BalanceDefaultCurrency))
{
<span>(@DisplayFormatter.Currency(Model.BalanceDefaultCurrency, Model.DefaultCurrency))</span>
}
</div>
</a>
<div class="d-flex gap-3 mt-3 mt-sm-0" permission="@Policies.CanModifyStoreSettings">
@if (!Model.Network.ReadonlyWallet)
<a asp-controller="UIWallets" asp-action="WalletTransactions" asp-route-walletId="@Model.WalletId" class="unobtrusive-link">
<h2 class="my-1">@Model.Label</h2>
<div class="text-muted fw-semibold" data-sensitive>
@DisplayFormatter.Currency(Model.Balance, Model.Network.CryptoCode)
@if (!string.IsNullOrEmpty(Model.BalanceDefaultCurrency))
{
<a class="btn btn-primary" asp-controller="UIWallets" asp-action="WalletSend" asp-route-walletId="@Model.WalletId" id="WalletNav-Send">Send</a>
<span>(@DisplayFormatter.Currency(Model.BalanceDefaultCurrency, Model.DefaultCurrency))</span>
}
<a class="btn btn-primary" asp-controller="UIWallets" asp-action="WalletReceive" asp-route-walletId="@Model.WalletId" id="WalletNav-Receive">Receive</a>
<a class="btn btn-secondary @ViewData.IsActivePage(WalletsNavPages.Settings) @ViewData.IsActivePage(StoreNavPages.OnchainSettings)" asp-controller="UIStores" asp-action="WalletSettings" asp-route-cryptoCode="@Model.WalletId.CryptoCode" asp-route-storeId="@Model.WalletId.StoreId" title="Settings" id="WalletNav-Settings">
<vc:icon symbol="settings"/>
</a>
</div>
</div>
<vc:ui-extension-point location="wallet-nav" model="@Model"/>
</a>

View File

@ -5,7 +5,6 @@ using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Models;
using LNURL;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;

View File

@ -60,6 +60,7 @@ namespace BTCPayServer.Controllers
Role = roleData.Role
});
}
[HttpPost("server/roles/{role}")]
public async Task<IActionResult> CreateOrEditRole([FromRoute] string role, UpdateRoleViewModel viewModel)
{

View File

@ -246,16 +246,27 @@ public partial class UIStoresController
}
var network = _explorerProvider.GetNetwork(vm.CryptoCode);
var lnId = PaymentTypes.LN.GetPaymentMethodId(network.CryptoCode);
var lnurlId = PaymentTypes.LNURL.GetPaymentMethodId(network.CryptoCode);
var lightning = GetConfig<LightningPaymentMethodConfig>(lnId, store);
if (lightning == null)
return NotFound();
var needUpdate = false;
var blob = store.GetStoreBlob();
blob.LightningDescriptionTemplate = vm.LightningDescriptionTemplate ?? string.Empty;
blob.LightningAmountInSatoshi = vm.LightningAmountInSatoshi;
blob.LightningPrivateRouteHints = vm.LightningPrivateRouteHints;
blob.OnChainWithLnInvoiceFallback = vm.OnChainWithLnInvoiceFallback;
var lnurlId = PaymentTypes.LNURL.GetPaymentMethodId(vm.CryptoCode);
blob.SetExcluded(lnurlId, !vm.LNURLEnabled);
// Lightning
blob.SetExcluded(lnId, !vm.Enabled);
// LNURL
blob.SetExcluded(lnurlId, !vm.LNURLEnabled || !vm.Enabled);
var lnurl = GetConfig<LNURLPaymentMethodConfig>(PaymentTypes.LNURL.GetPaymentMethodId(vm.CryptoCode), store);
var lnurl = GetConfig<LNURLPaymentMethodConfig>(lnurlId, store);
if (lnurl is null || (
lnurl.UseBech32Scheme != vm.LNURLBech32Mode ||
lnurl.LUD12Enabled != vm.LUD12Enabled))
@ -284,36 +295,6 @@ public partial class UIStoresController
return RedirectToAction(nameof(LightningSettings), new { vm.StoreId, vm.CryptoCode });
}
[HttpPost("{storeId}/lightning/{cryptoCode}/status")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> SetLightningNodeEnabled(string storeId, string cryptoCode, bool enabled)
{
var store = HttpContext.GetStoreData();
if (store == null)
return NotFound();
var network = _explorerProvider.GetNetwork(cryptoCode);
if (network == null)
return NotFound();
var lightning = GetConfig<LightningPaymentMethodConfig>(PaymentTypes.LN.GetPaymentMethodId(cryptoCode), store);
if (lightning == null)
return NotFound();
var paymentMethodId = PaymentTypes.LN.GetPaymentMethodId(network.CryptoCode);
var storeBlob = store.GetStoreBlob();
storeBlob.SetExcluded(paymentMethodId, !enabled);
if (!enabled)
{
storeBlob.SetExcluded(PaymentTypes.LNURL.GetPaymentMethodId(network.CryptoCode), true);
}
store.SetStoreBlob(storeBlob);
await _storeRepo.UpdateStore(store);
TempData[WellKnownTempData.SuccessMessage] = $"{network.CryptoCode} Lightning payments are now {(enabled ? "enabled" : "disabled")} for this store.";
return RedirectToAction(nameof(LightningSettings), new { storeId, cryptoCode });
}
private bool CanUseInternalLightning(string cryptoCode)
{
return _lightningNetworkOptions.InternalLightningByCryptoCode.ContainsKey(cryptoCode.ToUpperInvariant()) && (User.IsInRole(Roles.ServerAdmin) || _policiesSettings.AllowLightningInternalNodeForAll);

View File

@ -78,7 +78,6 @@ public partial class UIStoresController
var model = new CreateTokenViewModel();
ViewBag.HidePublicKey = storeId == null;
ViewBag.ShowStores = storeId == null;
ViewBag.ShowMenu = storeId != null;
model.StoreId = storeId;
return View(model);
}
@ -143,7 +142,6 @@ public partial class UIStoresController
var model = new CreateTokenViewModel();
ViewBag.HidePublicKey = true;
ViewBag.ShowStores = true;
ViewBag.ShowMenu = false;
var stores = (await _storeRepo.GetStoresByUserId(userId)).Where(data => data.HasPermission(userId, Policies.CanModifyStoreSettings)).ToArray();
model.Stores = new SelectList(stores, nameof(CurrentStore.Id), nameof(CurrentStore.StoreName));

View File

@ -1,6 +1,8 @@
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Fido2.Models;
using BTCPayServer.Models;
@ -13,7 +15,7 @@ using Newtonsoft.Json.Linq;
namespace BTCPayServer.Fido2
{
[Route("fido2")]
[Authorize]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanViewProfile)]
public class UIFido2Controller : Controller
{
private readonly UserManager<ApplicationUser> _userManager;

View File

@ -1,13 +1,11 @@
@using BTCPayServer.Client
@using BTCPayServer.Views.Server
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Abstractions.TagHelpers
@using BTCPayServer.Controllers
@using BTCPayServer.Views.Stores
@model UpdateRoleViewModel
@{
Layout = "_NavLayout.cshtml";
var role = Context.GetRouteValue("role") as string;
if (role == "create")
@ -15,35 +13,46 @@
var storeId = Context.GetRouteValue("storeId") as string;
if (storeId is null)
ViewData.SetActivePage(ServerNavPages.Roles, role is null ? "Create role" : "Update role");
ViewData.SetActivePage(ServerNavPages.Roles, role is null ? "Create role" : "Update Role");
else
{
ViewData.SetActivePage(StoreNavPages.Roles, role is null ? "Create role" : "Update role");
ViewData.SetActivePage(StoreNavPages.Roles, role is null ? "Create role" : "Update Role");
}
var storePolicies = Policies.AllPolicies.Where(Policies.IsStorePolicy).ToArray();
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="row">
<div class="col-xxl-constrain">
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<form method="post">
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="ListRoles" asp-route-storeId="@storeId">Roles</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
<button id="Save" type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
</div>
<div class="row">
<div class="col-xxl-constrain">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly"></div>
}
<div class="form-group" style="max-width:320px">
<label asp-for="Role" class="form-label"></label>
@if (role == null)
{
<input asp-for="Role" required="required" class="form-control" />
}
else
{
<input type="hidden" asp-for="Role"/>
<input required="required" class="form-control" disabled value="@Model.Role" />
}
<span asp-validation-for="Role" class="text-danger"></span>
@if (role == null)
{
<input asp-for="Role" required="required" class="form-control" />
}
else
{
<input type="hidden" asp-for="Role" />
<input required="required" class="form-control" disabled value="@Model.Role" />
}
<span asp-validation-for="Role" class="text-danger"></span>
</div>
<h4 class="mt-4 mb-3">Permissions</h4>
@ -64,10 +73,9 @@
}
</div>
<span asp-validation-for="Policies" class="text-danger"></span>
<button id="Save" type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
</form>
</div>
</div>
</div>
</form>
@{
void RenderTree(KeyValuePair<string, HashSet<string>> policy, KeyValuePair<string, HashSet<string>>[] storePolicyMap, bool isChecked)

View File

@ -3,7 +3,7 @@
@using BTCPayServer.Views.Apps
@using BTCPayServer.Plugins.Crowdfund
@using BTCPayServer.Services.Apps
@inject AppService AppService;
@inject AppService AppService
@model BTCPayServer.Components.MainNav.MainNavViewModel
@{
var store = Context.GetStoreData();
@ -14,7 +14,7 @@
var appType = AppService.GetAppType(CrowdfundAppType.AppType)!;
var apps = Model.Apps.Where(app => app.AppType == appType.Type).ToList();
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
<a asp-area="" asp-controller="UIApps" asp-action="CreateApp" asp-route-storeId="@store.Id" asp-route-appType="@appType.Type" class="nav-link @ViewData.IsActivePage(AppsNavPages.Create, appType.Type)" id="@($"StoreNav-Create{appType.Type}")">
<a asp-area="" asp-controller="UIApps" asp-action="CreateApp" asp-route-storeId="@store.Id" asp-route-appType="@appType.Type" class="nav-link @ViewData.ActivePageClass(AppsNavPages.Create, appType.Type)" id="@($"StoreNav-Create{appType.Type}")">
<vc:icon symbol="nav-crowdfund" />
<span>@appType.Description</span>
</a>
@ -31,7 +31,7 @@
@foreach (var app in apps)
{
<li class="nav-item nav-item-sub" permission="@Policies.CanViewStoreSettings">
<a asp-area="" asp-controller="UICrowdfund" asp-action="UpdateCrowdfund" asp-route-appId="@app.Id" class="nav-link @ViewData.IsActivePage(AppsNavPages.Update, app.Id)" id="@($"StoreNav-App-{app.Id}")">
<a asp-area="" asp-controller="UICrowdfund" asp-action="UpdateCrowdfund" asp-route-appId="@app.Id" class="nav-link @ViewData.ActivePageClass(AppsNavPages.Update, app.Id)" id="@($"StoreNav-App-{app.Id}")">
<span>@app.AppName</span>
</a>
</li>

View File

@ -1,5 +1,4 @@
@using System.Globalization
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Abstractions.Models
@using BTCPayServer.Client
@using BTCPayServer.TagHelpers
@ -27,9 +26,9 @@
}
<form method="post" permissioned="@Policies.CanModifyStoreSettings">
<div class="sticky-header d-sm-flex align-items-center justify-content-between">
<h2 class="mb-0">@ViewData["Title"]</h2>
<div class="d-flex gap-3 mt-3 mt-sm-0">
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<div>
<button type="submit" class="btn btn-primary order-sm-1" id="SaveSettings">Save</button>
@if (Model.Archived)
{
@ -46,8 +45,11 @@
<input type="hidden" asp-for="StoreId" />
<input type="hidden" asp-for="Archived" />
<div asp-validation-summary="ModelOnly"></div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly"></div>
}
<div class="row">
<div class="col-sm-10 col-md-9 col-xl-7 col-xxl-6">
<div class="row">

View File

@ -76,7 +76,6 @@
</div>
</div>
</div>
<button type="submit" class="btn btn-primary mt-2" name="command" value="Save" id="Save">Save</button>
</div>
</div>

View File

@ -1,6 +1,6 @@
@model BTCPayServer.Models.EmailsViewModel
<h3 class="mt-5 mb-3">Testing</h3>
<h3 class="my-3">Testing</h3>
<div class="row">
<div class="col-xl-10 col-xxl-constrain">
<div class="form-group">

View File

@ -8,7 +8,7 @@
@if (store.IsLightningEnabled(cryptoCode) && store.IsLNUrlEnabled(cryptoCode))
{
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
<a asp-area="" asp-controller="UILNURL" asp-action="EditLightningAddress" asp-route-storeId="@store.Id" class="nav-link @ViewData.IsActivePage("LightningAddress", nameof(StoreNavPages))" id="StoreNav-LightningAddress">
<a asp-area="" asp-controller="UILNURL" asp-action="EditLightningAddress" asp-route-storeId="@store.Id" class="nav-link @ViewData.ActivePageClass("LightningAddress", nameof(StoreNavPages))" id="StoreNav-LightningAddress">
<vc:icon symbol="nav-lightning-address "/>
<span>Lightning Address</span>
</a>

View File

@ -4,7 +4,6 @@
@using BTCPayServer.Client
@model BTCPayServer.Models.ServerViewModels.RolesViewModel
@{
Layout = "_NavLayout.cshtml";
var storeId = Context.GetRouteValue("storeId") as string;
var controller = ViewContext.RouteData.Values["controller"].ToString().TrimEnd("Controller", StringComparison.InvariantCultureIgnoreCase);
if (string.IsNullOrEmpty(storeId))
@ -25,10 +24,11 @@
var showInUseColumn = !Model.Roles.Any(r => r.IsUsed is null);
}
<div class="d-flex align-items-center justify-content-between mb-3">
<h3 class="mb-0">@ViewData["Title"]</h3>
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<a class="btn btn-primary" role="button" id="CreateRole" asp-controller="@controller" asp-action="CreateOrEditRole" asp-route-role="create" asp-route-storeId="@storeId" permission="@permission">Add Role</a>
</div>
<partial name="_StatusMessage" />
<div class="table-responsive">
<table class="table table-hover">
@ -50,7 +50,7 @@
<th>Permissions</th>
@if (showInUseColumn)
{
<th class="text-center">In use</th>
<th class="text-center w-75px">In use</th>
}
<th class="actions-col" permission="@permission">Actions</th>
</tr>

View File

@ -3,8 +3,11 @@
ViewData.SetActivePage(StoreNavPages.PayButton, "Pay Button", Context.GetStoreData().Id);
}
<div class="sticky-header">
<h2 class="my-1">@ViewData["Title"]</h2>
</div>
<partial name="_StatusMessage" />
<h2 class="mt-1 mb-4">@ViewData["Title"]</h2>
<div class="row">
<div class="col-xl-8 col-xxl-constrain">

View File

@ -6,7 +6,7 @@
@if (store != null)
{
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
<a asp-area="" asp-controller="UIPayButton" asp-action="PayButton" asp-route-storeId="@store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.PayButton)" id="StoreNav-PayButton">
<a asp-area="" asp-controller="UIPayButton" asp-action="PayButton" asp-route-storeId="@store.Id" class="nav-link @ViewData.ActivePageClass(StoreNavPages.PayButton)" id="StoreNav-PayButton">
<vc:icon symbol="nav-pay-button"/>
<span>Pay Button</span>
</a>

View File

@ -174,9 +174,12 @@
</script>
}
<div class="sticky-header">
<h2 class="my-1">@ViewData["Title"]</h2>
</div>
<partial name="_StatusMessage" />
<h2 class="mt-1 mb-4">@ViewData["Title"]</h2>
<div id="payButtonCtrl">
<div class="row">
<div class="col-xl-8">

View File

@ -3,7 +3,7 @@
@using BTCPayServer.Views.Apps
@using BTCPayServer.Plugins.PointOfSale
@using BTCPayServer.Services.Apps
@inject AppService AppService;
@inject AppService AppService
@model BTCPayServer.Components.MainNav.MainNavViewModel
@{
var store = Context.GetStoreData();
@ -14,7 +14,7 @@
var appType = AppService.GetAppType(PointOfSaleAppType.AppType)!;
var apps = Model.Apps.Where(app => app.AppType == appType.Type).ToList();
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
<a asp-area="" asp-controller="UIApps" asp-action="CreateApp" asp-route-storeId="@store.Id" asp-route-appType="@appType.Type" class="nav-link @ViewData.IsActivePage(AppsNavPages.Create, appType.Type)" id="@($"StoreNav-Create{appType.Type}")">
<a asp-area="" asp-controller="UIApps" asp-action="CreateApp" asp-route-storeId="@store.Id" asp-route-appType="@appType.Type" class="nav-link @ViewData.ActivePageClass(AppsNavPages.Create, appType.Type)" id="@($"StoreNav-Create{appType.Type}")">
<vc:icon symbol="nav-pointofsale" />
<span>@appType.Description</span>
</a>
@ -31,7 +31,7 @@
@foreach (var app in apps)
{
<li class="nav-item nav-item-sub" permission="@Policies.CanViewStoreSettings">
<a asp-area="" asp-controller="UIPointOfSale" asp-action="UpdatePointOfSale" asp-route-appId="@app.Id" class="nav-link @ViewData.IsActivePage(AppsNavPages.Update, app.Id)" id="@($"StoreNav-App-{app.Id}")">
<a asp-area="" asp-controller="UIPointOfSale" asp-action="UpdatePointOfSale" asp-route-appId="@app.Id" class="nav-link @ViewData.ActivePageClass(AppsNavPages.Update, app.Id)" id="@($"StoreNav-App-{app.Id}")">
<span>@app.AppName</span>
</a>
</li>

View File

@ -34,9 +34,9 @@
}
<form method="post" permissioned="@Policies.CanModifyStoreSettings">
<div class="sticky-header d-sm-flex align-items-center justify-content-between">
<h2 class="mb-0">@ViewData["Title"]</h2>
<div class="d-flex gap-3 mt-3 mt-sm-0">
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<div>
<button type="submit" class="btn btn-primary order-sm-1" id="SaveSettings">Save</button>
@if (Model.Archived)
{
@ -58,8 +58,11 @@
<input type="hidden" asp-for="StoreId" />
<input type="hidden" asp-for="Archived" />
<div asp-validation-summary="ModelOnly"></div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly"></div>
}
<div class="row">
<div class="col-sm-10 col-md-9 col-xl-7 col-xxl-6">
<div class="row">

View File

@ -1,5 +1,4 @@
@using BTCPayServer.Client
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Views.Stores
@model BTCPayServer.Components.MainNav.MainNavViewModel
@{
@ -9,7 +8,7 @@
@if (store != null)
{
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
<a asp-area="" asp-controller="UIShopify" asp-action="EditShopify" asp-route-storeId="@store.Id" class="nav-link @ViewData.IsActivePage("shopify", nameof(StoreNavPages))" id="StoreNav-Shopify">
<a asp-area="" asp-controller="UIShopify" asp-action="EditShopify" asp-route-storeId="@store.Id" class="nav-link @ViewData.ActivePageClass("shopify", nameof(StoreNavPages))" id="StoreNav-Shopify">
<vc:icon symbol="logo-shopify" />
<span>Shopify</span>
</a>

View File

@ -1,4 +1,3 @@
@using BTCPayServer.Abstractions.Extensions
@inject BTCPayServer.Services.BTCPayServerEnvironment _env
<footer class="btcpay-footer d-print-none">

View File

@ -1,32 +0,0 @@
@using NBitcoin
@{
Layout = "/Views/Shared/_Layout.cshtml";
ViewBag.ShowMenu = ViewBag.ShowMenu ?? true;
if (!ViewData.ContainsKey("NavPartialName"))
{
ViewData["NavPartialName"] = "_Nav";
}
}
@section PageHeadContent {
@await RenderSectionAsync("PageHeadContent", false)
}
@section PageFootContent {
@await RenderSectionAsync("PageFootContent", false)
}
@if (ViewBag.ShowMenu)
{
<partial name="@ViewData["NavPartialName"].ToString()" />
<script>
(function () {
const activeEl = document.querySelector('#SectionNav .nav .active')
if (activeEl) activeEl.scrollIntoView({ block: 'end', inline: 'center' })
})()
</script>
}
<partial name="_StatusMessage" />
@RenderBody()

View File

@ -10,7 +10,10 @@
</p>
<form asp-action="ForgotPassword" method="post">
<div asp-validation-summary="All" class="@(ViewContext.ModelState.ErrorCount.Equals(1) ? "no-marker" : "")"></div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="@(ViewContext.ModelState.ErrorCount.Equals(1) ? "no-marker" : "")"></div>
}
<div class="form-group">
<label asp-for="Email" class="form-label"></label>
<input asp-for="Email" class="form-control" />

View File

@ -9,7 +9,10 @@
<form asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" id="login-form" asp-action="Login">
<fieldset disabled="@(ViewData.ContainsKey("disabled") ? "disabled" : null)">
<div asp-validation-summary="ModelOnly" class="@(ViewContext.ModelState.ErrorCount.Equals(1) ? "no-marker" : "")"></div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly" class="@(ViewContext.ModelState.ErrorCount.Equals(1) ? "no-marker" : "")"></div>
}
<div class="form-group">
<label asp-for="Email" class="form-label"></label>
<input asp-for="Email" class="form-control" required autofocus />

View File

@ -3,7 +3,10 @@
<div class="twoFaBox">
<h2 class="h3 mb-3">Two-Factor Authentication</h2>
<form method="post" asp-route-returnUrl="@ViewData["ReturnUrl"]" asp-action="LoginWith2fa">
<div asp-validation-summary="ModelOnly"></div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly"></div>
}
<input asp-for="RememberMe" type="hidden"/>
<div class="form-group">
<label asp-for="TwoFactorCode" class="form-label"></label>

View File

@ -8,7 +8,10 @@
<form asp-route-returnUrl="@ViewData["ReturnUrl"]" asp-route-logon="true" method="post">
<fieldset disabled="@(ViewData.ContainsKey("disabled") ? "disabled" : null)" >
<div asp-validation-summary="ModelOnly" class="@(ViewContext.ModelState.ErrorCount.Equals(1) ? "no-marker" : "")"></div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly" class="@(ViewContext.ModelState.ErrorCount.Equals(1) ? "no-marker" : "")"></div>
}
<div class="form-group">
<label asp-for="Email" class="form-label"></label>
<input asp-for="Email" class="form-control" required autofocus />

View File

@ -6,15 +6,18 @@
}
<form method="post" asp-action="SetPassword">
<div asp-validation-summary="All" class="@(ViewContext.ModelState.ErrorCount.Equals(1) ? "no-marker" : "")"></div>
<input asp-for="Code" type="hidden"/>
<input asp-for="EmailSetInternally" type="hidden"/>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="@(ViewContext.ModelState.ErrorCount.Equals(1) ? "no-marker" : "")"></div>
}
<input asp-for="Code" type="hidden" />
<input asp-for="EmailSetInternally" type="hidden" />
@if (Model.EmailSetInternally)
{
<input asp-for="Email" type="hidden"/>
<input asp-for="Email" type="hidden" />
<div class="form-group">
<label asp-for="Email" class="form-label"></label>
<input type="text" disabled value="@Model.Email" class="form-control"/>
<input type="text" disabled value="@Model.Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
}
@ -22,18 +25,18 @@
{
<div class="form-group">
<label asp-for="Email" class="form-label"></label>
<input asp-for="Email" value="@Model.Email" class="form-control"/>
<input asp-for="Email" value="@Model.Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
}
<div class="form-group">
<label asp-for="Password" class="form-label"></label>
<input asp-for="Password" class="form-control"/>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="form-label"></label>
<input asp-for="ConfirmPassword" class="form-control"/>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary w-100 btn-lg" id="SetPassword">@cta</button>

View File

@ -7,14 +7,19 @@
<partial name="_ValidationScriptsPartial" />
}
<partial name="_StatusMessage" />
<form asp-action="CreateApp" asp-route-appType="@Model.AppType">
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<input type="submit" value="Create" class="btn btn-primary" id="Create" />
</div>
<partial name="_StatusMessage" />
<h2 class="mt-1 mb-4">@ViewData["Title"]</h2>
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<form asp-action="CreateApp" asp-route-appType="@Model.AppType">
<div asp-validation-summary="ModelOnly"></div>
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly"></div>
}
@if (string.IsNullOrEmpty(Model.AppType))
{
<div class="form-group">
@ -27,9 +32,6 @@
<input asp-for="AppName" class="form-control" required />
<span asp-validation-for="AppName" class="text-danger"></span>
</div>
<div class="form-group mt-4">
<input type="submit" value="Create" class="btn btn-primary" id="Create" />
</div>
</form>
</div>
</div>
</div>
</form>

View File

@ -32,8 +32,8 @@
<partial name="_StatusMessage" />
<div class="d-sm-flex align-items-center justify-content-between mb-2">
<h2 class="mb-0">
<div class="sticky-header">
<h2>
@ViewData["Title"]
<small>
<a href="https://docs.btcpayserver.org/Apps/" target="_blank" rel="noreferrer noopener" title="More information...">
@ -41,101 +41,99 @@
</a>
</small>
</h2>
<a asp-action="CreateApp" asp-route-storeId="@Context.GetStoreData().Id" class="btn btn-primary mt-3 mt-sm-0" role="button" id="CreateNewApp">Create a new app</a>
<a asp-action="CreateApp" asp-route-storeId="@Context.GetStoreData().Id" class="btn btn-primary" role="button" id="CreateNewApp">Create a new app</a>
</div>
<div class="row">
<div class="col-lg-12">
@if (Model.Apps.Any())
{
<table class="table table-hover table-responsive-md">
<thead>
<div class="table-responsive-md">
@if (Model.Apps.Any())
{
<table class="table table-hover">
<thead>
<tr>
<th>
<a
asp-action="ListApps"
asp-route-storeId="@Context.GetStoreData().Id"
asp-route-sortOrder="@(nextAppNameSortOrder ?? "asc")"
asp-route-sortOrderColumn="AppName"
class="text-nowrap"
title="@(appNameSortOrder == "desc" ? sortByDesc : sortByAsc)"
>
Name
<vc:icon symbol="actions-sort-alpha-@(appNameSortOrder ?? nextAppNameSortOrder ?? "desc")" />
</a>
</th>
<th>
<a
asp-action="ListApps"
asp-route-storeId="@Context.GetStoreData().Id"
asp-route-sortOrder="@(nextAppTypeSortOrder ?? "asc")"
asp-route-sortOrderColumn="AppType"
class="text-nowrap"
title="@(appTypeSortOrder == "desc" ? sortByDesc : sortByAsc)"
>
App Type
<vc:icon symbol="actions-sort-alpha-@(appTypeSortOrder ?? nextAppTypeSortOrder ?? "desc")" />
</a>
</th>
<th>
<a
asp-action="ListApps"
asp-route-storeId="@Context.GetStoreData().Id"
asp-route-sortOrder="@(nextStoreNameSortOrder ?? "asc")"
asp-route-sortOrderColumn="StoreName"
class="text-nowrap"
title="@(storeNameSortOrder == "desc" ? sortByDesc : sortByAsc)"
>
Store
<vc:icon symbol="actions-sort-alpha-@(storeNameSortOrder ?? nextStoreNameSortOrder ?? "desc")" />
</a>
</th>
</tr>
</thead>
<tbody>
@foreach (var app in Model.Apps)
{
var appType = AppService.GetAppType(app.AppType)!;
var appData = new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName, AppType = app.AppType };
var url = await appType.ConfigureLink(appData);
<tr>
<th>
<a
asp-action="ListApps"
asp-route-storeId="@Context.GetStoreData().Id"
asp-route-sortOrder="@(nextAppNameSortOrder ?? "asc")"
asp-route-sortOrderColumn="AppName"
class="text-nowrap"
title="@(appNameSortOrder == "desc" ? sortByDesc : sortByAsc)"
>
Name
<vc:icon symbol="actions-sort-alpha-@(appNameSortOrder ?? nextAppNameSortOrder ?? "desc")" />
</a>
</th>
<th>
<a
asp-action="ListApps"
asp-route-storeId="@Context.GetStoreData().Id"
asp-route-sortOrder="@(nextAppTypeSortOrder ?? "asc")"
asp-route-sortOrderColumn="AppType"
class="text-nowrap"
title="@(appTypeSortOrder == "desc" ? sortByDesc : sortByAsc)"
>
App Type
<vc:icon symbol="actions-sort-alpha-@(appTypeSortOrder ?? nextAppTypeSortOrder ?? "desc")" />
</a>
</th>
<th>
<a
asp-action="ListApps"
asp-route-storeId="@Context.GetStoreData().Id"
asp-route-sortOrder="@(nextStoreNameSortOrder ?? "asc")"
asp-route-sortOrderColumn="StoreName"
class="text-nowrap"
title="@(storeNameSortOrder == "desc" ? sortByDesc : sortByAsc)"
>
Store
<vc:icon symbol="actions-sort-alpha-@(storeNameSortOrder ?? nextStoreNameSortOrder ?? "desc")" />
</a>
</th>
<td>
<a href="@url" permission="@Policies.CanModifyStoreSettings" id="App-@app.Id">@app.AppName</a>
<span not-permission="@Policies.CanModifyStoreSettings">@app.AppName</span>
@if (app.Archived)
{
<span class="badge bg-info ms-2">archived</span>
}
</td>
<td>
@AppService.GetAvailableAppTypes()[app.AppType]
@{
var viewStyle = @app.ViewStyle;
}
@if (!string.IsNullOrEmpty(viewStyle))
{
<span>-</span>
<span>@viewStyle</span>
}
</td>
<td>
<span permission="@Policies.CanModifyStoreSettings">
<a asp-controller="UIStores" asp-action="GeneralSettings" asp-route-storeId="@app.StoreId">@app.StoreName</a>
</span>
<span not-permission="@Policies.CanModifyStoreSettings">@app.StoreName</span>
</td>
</tr>
</thead>
<tbody>
@foreach (var app in Model.Apps)
{
var appType = AppService.GetAppType(app.AppType)!;
var appData = new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName, AppType = app.AppType };
var url = await appType.ConfigureLink(appData);
<tr>
<td>
<a href="@url" permission="@Policies.CanModifyStoreSettings" id="App-@app.Id">@app.AppName</a>
<span not-permission="@Policies.CanModifyStoreSettings">@app.AppName</span>
@if (app.Archived)
{
<span class="badge bg-info ms-2">archived</span>
}
</td>
<td>
@AppService.GetAvailableAppTypes()[app.AppType]
@{
var viewStyle = @app.ViewStyle;
}
@if (!string.IsNullOrEmpty(viewStyle))
{
<span>-</span>
<span>@viewStyle</span>
}
</td>
<td>
<span permission="@Policies.CanModifyStoreSettings">
<a asp-controller="UIStores" asp-action="GeneralSettings" asp-route-storeId="@app.StoreId">@app.StoreName</a>
</span>
<span not-permission="@Policies.CanModifyStoreSettings">@app.StoreName</span>
</td>
</tr>
}
</tbody>
</table>
}
else
{
<p class="text-secondary mt-3">
There are no apps yet.
</p>
}
</div>
}
</tbody>
</table>
}
else
{
<p class="text-secondary mt-3">
There are no apps yet.
</p>
}
</div>
<partial name="_Confirm" model="@(new ConfirmModel("Delete app", "This app will be removed from this store.", "Delete"))" />

View File

@ -3,7 +3,18 @@
ViewData.SetActivePage(ManageNavPages.TwoFactorAuthentication, "Register your security device");
}
<h3 class="mb-3">@ViewData["Title"]</h3>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-controller="UIManage" asp-action="TwoFactorAuthentication">Two Factor Authentication</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Register Device</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
<p>Insert your security device and proceed.</p>

View File

@ -1,4 +0,0 @@
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData["NavPartialName"] = "../UIManage/_Nav";
}

View File

@ -3,54 +3,55 @@
@using BTCPayServer.Client
@model List<BTCPayServer.Data.FormData>
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData["NavPartialName"] = "../UIStores/_Nav";
ViewData.SetActivePage(StoreNavPages.Forms, "Forms");
var storeId = Context.GetCurrentStoreId();
}
<div class="sticky-header">
<h2>
<span>@ViewData["Title"]</span>
<a href="https://docs.btcpayserver.org/Forms" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</h2>
<a asp-action="Create" asp-route-storeId="@storeId" class="btn btn-primary mt-3 mt-sm-0" role="button" id="CreateForm" permission="@Policies.CanModifyStoreSettings">
Create Form
</a>
</div>
<div class="row">
<div class="col-xxl-constrain col-xl-10">
<div class="d-flex align-items-center justify-content-between mb-3">
<h3 class="mb-0">
<span>@ViewData["Title"]</span>
<a href="https://docs.btcpayserver.org/Forms" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</h3>
<a asp-action="Create" asp-route-storeId="@storeId" class="btn btn-primary mt-3 mt-sm-0" role="button" id="CreateForm" permission="@Policies.CanModifyStoreSettings">
Create Form
</a>
</div>
@if (Model.Any())
{
<table class="table table-hover table-responsive-md">
<thead>
<tr>
<th>Name</th>
<th class="actions-col" permission="@Policies.CanModifyStoreSettings">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<div class="table-responsive-md mt-0">
<table class="table table-hover">
<thead>
<tr>
<td>
<a asp-action="Modify" asp-route-storeId="@item.StoreId" asp-route-id="@item.Id" id="Edit-@item.Name" 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 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>
</td>
<th>Name</th>
<th class="actions-col" permission="@Policies.CanModifyStoreSettings">Actions</th>
</tr>
}
</tbody>
</table>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
<a asp-action="Modify" asp-route-storeId="@item.StoreId" asp-route-id="@item.Id" id="Edit-@item.Name" 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 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>
</td>
</tr>
}
</tbody>
</table>
</div>
}
else
{
<p class="text-secondary mt-3">
<p class="text-secondary">
There are no forms yet.
</p>
}

View File

@ -6,8 +6,6 @@
Csp.UnsafeEval();
var formId = Context.GetRouteValue("id");
var isNew = formId is null;
Layout = "../Shared/_NavLayout.cshtml";
ViewData["NavPartialName"] = "../UIStores/_Nav";
ViewData.SetActivePage(StoreNavPages.Forms, $"{(isNew ? "Create" : "Edit")} Form", Model.Name);
var storeId = Context.GetCurrentStoreId();
}
@ -194,24 +192,35 @@
}
<form method="post" asp-action="Modify" asp-route-id="@formId" asp-route-storeId="@storeId">
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-controller="UIForms" asp-action="FormsList" asp-route-storeId="@storeId">Forms</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>
@ViewData["Title"]
<a href="https://docs.btcpayserver.org/Forms" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</h2>
</nav>
<div>
<button type="submit" class="btn btn-primary order-sm-1" id="SaveButton">Save</button>
@if (!isNew)
{
<a class="btn btn-secondary" asp-action="ViewPublicForm" asp-route-formId="@formId" id="ViewForm">View</a>
}
</div>
</div>
<div class="row mb-4">
<div class="col-12">
<div class="d-flex align-items-center justify-content-between mb-3">
<h3 class="mb-0">
<span>@ViewData["Title"]</span>
<a href="https://docs.btcpayserver.org/Forms" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</h3>
<div class="d-flex gap-3 mt-3 mt-sm-0">
<button type="submit" class="btn btn-primary order-sm-1" id="SaveButton">Save</button>
@if (!isNew)
{
<a class="btn btn-secondary" asp-action="ViewPublicForm" asp-route-formId="@formId" id="ViewForm">View</a>
}
</div>
</div>
<div asp-validation-summary="All"></div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<div class="form-group" style="max-width: 27rem;">
<label asp-for="Name" class="form-label" data-required></label>
<input asp-for="Name" class="form-control" required />

View File

@ -1,5 +1,4 @@
@model BTCPayServer.Models.InvoicingModels.CreateInvoiceModel
@using BTCPayServer.Services.Apps
@using BTCPayServer.TagHelpers
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using BTCPayServer.Abstractions.TagHelpers
@ -29,8 +28,16 @@
}
<form asp-action="CreateInvoice" method="post" id="create-invoice-form">
<div class="sticky-header d-flex align-items-center justify-content-between">
<h2 class="mb-0">@ViewData["Title"]</h2>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="ListInvoices" asp-route-storeId="@Model.StoreId">Invoices</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
<input type="submit" value="Create" class="btn btn-primary" id="Create" />
</div>
@ -38,7 +45,10 @@
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<div asp-validation-summary="ModelOnly"></div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly"></div>
}
@if (Model.StoreId != null)
{
<input type="hidden" asp-for="StoreId" />

View File

@ -169,9 +169,17 @@
</div>
}
<div class="sticky-header d-flex flex-wrap gap-3 align-items-center justify-content-between">
<h2 class="mb-0 text-break">@ViewData["Title"]</h2>
<div class="d-flex flex-wrap gap-3 d-print-none">
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="ListInvoices" asp-route-storeId="@Model.StoreId">Invoices</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Invoice</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
<div>
@if (Model.ShowCheckout)
{
<a asp-action="Checkout" class="invoice-checkout-link btn btn-primary text-nowrap" asp-route-invoiceId="@Model.Id">Checkout</a>

View File

@ -100,8 +100,8 @@
@Html.HiddenFor(a => a.Count)
<div class="sticky-header d-sm-flex align-items-center justify-content-between">
<h2 class="mb-0">
<div class="sticky-header">
<h2>
@ViewData["Title"]
<a href="#descriptor" data-bs-toggle="collapse">
<vc:icon symbol="info" />

View File

@ -1,5 +1,4 @@
@using BTCPayServer.Views.Stores
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Abstractions.Models
@model UILNURLController.EditLightningAddressVM
@{
@ -26,6 +25,13 @@
</script>
}
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<a data-bs-toggle="collapse" data-bs-target="#AddAddress" class="btn btn-primary" role="button">
Add Address
</a>
</div>
<partial name="_StatusMessage" />
@if (Context.Request.PathBase.ToString() != string.Empty)
@ -37,13 +43,6 @@
</div>
}
<div class="d-flex align-items-center justify-content-between mb-2">
<h2 class="mb-0">@ViewData["Title"]</h2>
<a data-bs-toggle="collapse" data-bs-target="#AddAddress" class="btn btn-primary" role="button">
Add Address
</a>
</div>
<form asp-action="EditLightningAddress" method="post">
@{
var showAddForm = !ViewContext.ViewData.ModelState.IsValid || !string.IsNullOrEmpty(Model.Add?.Username) || Model.Add?.Max != null || Model.Add?.Min != null || !string.IsNullOrEmpty(Model.Add?.CurrencyCode);
@ -51,7 +50,7 @@
}
<div class="collapse @(showAddForm ? "show": "")" id="AddAddress">
<div class="form-group pt-2">
<div class="form-group">
<label asp-for="Add.Username" class="form-label"></label>
<div class="input-group">
<input asp-for="Add.Username" class="form-control"/>
@ -106,69 +105,64 @@
@if (Model.Items.Any())
{
<div class="row">
<div class="col">
<table class="table table-hover">
<thead>
<tr>
<th>Address</th>
<th>Settings</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
@for (var index = 0; index < Model.Items.Count; index++)
{
<input asp-for="Items[index].CurrencyCode" type="hidden"/>
<input asp-for="Items[index].Min" type="hidden"/>
<input asp-for="Items[index].Max" type="hidden"/>
<input asp-for="Items[index].Username" type="hidden"/>
<input asp-for="Items[index].InvoiceMetadata" type="hidden"/>
var address = $"{Model.Items[index].Username}@{Context.Request.Host.ToUriComponent()}";
<tr>
<td>
<div class="input-group" data-clipboard="@address">
<input type="text" class="form-control copy-cursor lightning-address-value" readonly="readonly" value="@address"/>
<button type="button" class="btn btn-outline-secondary px-3">
<vc:icon symbol="actions-copy" />
</button>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>Address</th>
<th>Settings</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
@for (var index = 0; index < Model.Items.Count; index++)
{
<input asp-for="Items[index].CurrencyCode" type="hidden"/>
<input asp-for="Items[index].Min" type="hidden"/>
<input asp-for="Items[index].Max" type="hidden"/>
<input asp-for="Items[index].Username" type="hidden"/>
<input asp-for="Items[index].InvoiceMetadata" type="hidden"/>
var address = $"{Model.Items[index].Username}@{Context.Request.Host.ToUriComponent()}";
<tr>
<td>
<div class="input-group" data-clipboard="@address">
<input type="text" class="form-control copy-cursor lightning-address-value" readonly="readonly" value="@address"/>
<button type="button" class="btn btn-outline-secondary px-3">
<vc:icon symbol="actions-copy" />
</button>
</div>
</td>
<td class="settings-holder align-middle">
@if (Model.Items[index].Min.HasValue)
{
<span>@Model.Items[index].Min min sats</span>
}
@if (Model.Items[index].Max.HasValue)
{
<span>@Model.Items[index].Max max sats</span>
}
@if (!string.IsNullOrEmpty(Model.Items[index].CurrencyCode))
{
<span>tracked in @Model.Items[index].CurrencyCode</span>
}
@if (!string.IsNullOrEmpty(Model.Items[index].InvoiceMetadata))
{
<span>with invoice metadata @Model.Items[index].InvoiceMetadata</span>
}
</td>
<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">
Remove
</button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</td>
<td class="settings-holder align-middle">
@if (Model.Items[index].Min.HasValue)
{
<span>@Model.Items[index].Min min sats</span>
}
@if (Model.Items[index].Max.HasValue)
{
<span>@Model.Items[index].Max max sats</span>
}
@if (!string.IsNullOrEmpty(Model.Items[index].CurrencyCode))
{
<span>tracked in @Model.Items[index].CurrencyCode</span>
}
@if (!string.IsNullOrEmpty(Model.Items[index].InvoiceMetadata))
{
<span>with invoice metadata @Model.Items[index].InvoiceMetadata</span>
}
</td>
<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">
Remove
</button>
</td>
</tr>
}
</tbody>
</table>
}
else
{
<p class="text-secondary mt-3">
<p class="text-secondary">
There are no Lightning Addresses yet.
</p>
}

View File

@ -2,19 +2,20 @@
@model Uri
@{
ViewData.SetActivePage(ManageNavPages.TwoFactorAuthentication, "Register your Lightning node for LNURL Auth");
Dictionary<string, string> formats = new Dictionary<string, string>()
var formats = new Dictionary<string, string>
{
{ "Bech32", LNURL.EncodeUri(Model, "login", true).ToString().ToUpperInvariant() },
{ "URI", LNURL.EncodeUri(Model, "login", false).ToString().ToUpperInvariant() }
};
}
<h3 class="mb-3">@ViewData["Title"]</h3>
<h2 class="mb-2 mb-lg-3">@ViewData["Title"]</h2>
<partial name="_StatusMessage" />
<p>Scan the QR code with your Lightning wallet to link it to your user account.</p>
<div id="info-message" class="d-inline-block">
<ul class="nav justify-content-center my-2">
<ul class="nav justify-content-center btcpay-pills align-items-center gap-2 my-2">
@for (int i = 0; i < formats.Count; i++)
{
var mode = formats.ElementAt(i);

View File

@ -1,6 +0,0 @@
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewBag.MainTitle = "Manage your account";
ViewData["NavPartialName"] = "../UIManage/_Nav";
}

View File

@ -1,10 +0,0 @@
@using BTCPayServer.Client
@using BTCPayServer.Views.Stores
@using BTCPayServer.Abstractions.Extensions
<nav id="SectionNav">
<div class="nav">
<a class="nav-link @ViewData.IsActivePage(StoreNavPages.Lightning)" asp-controller="UIStores" asp-action="Lightning" asp-route-storeId="@Context.GetRouteValue("storeId")" asp-route-cryptoCode="@Context.GetRouteValue("cryptoCode")" id="SectionNav-Lightning" permission="@Policies.CanModifyStoreSettings">Lightning</a>
<a class="nav-link @ViewData.IsActivePage(StoreNavPages.LightningSettings)" asp-controller="UIStores" asp-action="LightningSettings" asp-route-storeId="@Context.GetRouteValue("storeId")" asp-route-cryptoCode="@Context.GetRouteValue("cryptoCode")" id="SectionNav-LightningSettings" permission="@Policies.CanModifyStoreSettings">Settings</a>
<vc:ui-extension-point location="lightning-nav" model="@Model"/>
</div>
</nav>

View File

@ -3,21 +3,31 @@
@using Microsoft.AspNetCore.Mvc.TagHelpers
@model BTCPayServer.PayoutProcessors.Lightning.UILightningAutomatedPayoutProcessorsController.LightningTransferViewModel
@{
ViewData["NavPartialName"] = "../UIStores/_Nav";
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePage(StoreNavPages.PayoutProcessors, "Lightning Payout Processor", Context.GetStoreData().Id);
var storeId = Context.GetStoreData().Id;
ViewData.SetActivePage(StoreNavPages.PayoutProcessors, "Lightning Payout Processor", storeId);
}
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<div class="d-flex align-items-center justify-content-between mb-3">
<h3 class="mb-0">@ViewData["Title"]</h3>
</div>
<p>Set a schedule for automated Lightning Network Payouts.</p>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<form method="post" permissioned="@Policies.CanModifyStoreSettings">
<form method="post" permissioned="@Policies.CanModifyStoreSettings">
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-controller="UIPayoutProcessors" asp-action="ConfigureStorePayoutProcessors" asp-route-storeId="@storeId">Payout Processors</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
<button name="command" type="submit" class="btn btn-primary" value="Save" id="Save">Save</button>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<p>Set a schedule for automated Lightning Network Payouts.</p>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<div class="d-flex my-3">
<input asp-for="ProcessNewPayoutsInstantly" type="checkbox" class="btcpay-toggle me-3" />
<label asp-for="ProcessNewPayoutsInstantly" class="form-check-label">Process approved payouts instantly</label>
@ -40,11 +50,9 @@
</div>
<div class="form-text">If a payout fails this many times, it will be cancelled.</div>
</div>
<button name="command" type="submit" class="btn btn-primary mt-2" value="Save" id="Save">Save</button>
</form>
</div>
</div>
</div>
</form>
@section PageFootContent {
<partial name="_ValidationScriptsPartial" />

View File

@ -7,14 +7,15 @@
Csp.UnsafeEval();
}
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<a class="btn btn-primary" asp-action="AddApiKey" id="AddApiKey">
Generate Key
</a>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-10 col-xxl-constrain">
<div class="d-flex align-items-center justify-content-between mb-3">
<h3 class="mb-0">@ViewData["Title"]</h3>
<a class="btn btn-primary" asp-action="AddApiKey" id="AddApiKey">
Generate Key
</a>
</div>
<p>
The <a asp-controller="UIHome" asp-action="SwaggerDocs" target="_blank">BTCPay Server Greenfield API</a> offers programmatic access to your instance. You can manage your BTCPay
Server (e.g. stores, invoices, users) as well as automate workflows and integrations (see <a href="https://docs.btcpayserver.org/Development/GreenFieldExample/" rel="noreferrer noopener">use case examples</a>).

View File

@ -25,15 +25,26 @@
</script>
}
<h3 class="mb-3">@ViewData["Title"]</h3>
<div class="row">
<div class="col-xl-10 col-xxl-constrain">
<p>Generate a new api key to use BTCPay through its API.</p>
<form method="post" asp-action="AddApiKey" id="Permissions">
<div asp-validation-summary="All"></div>
<form method="post" asp-action="AddApiKey" id="Permissions">
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="APIKeys">API Keys</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
<button type="submit" class="btn btn-primary" id="Generate">Generate API Key</button>
</div>
<p>Generate a new api key to use BTCPay through its API.</p>
<div class="row">
<div class="col-xl-10 col-xxl-constrain">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<div class="form-group">
<label asp-for="Label" class="form-label"></label>
<input asp-for="Label" class="form-control"/>
@ -133,8 +144,6 @@
}
}
</div>
<button type="submit" class="btn btn-primary" id="Generate">Generate API Key</button>
</form>
</div>
</div>
</div>
</form>

View File

@ -43,8 +43,11 @@
<h1>@ViewData["Title"]</h1>
<p class="lead text-secondary mt-3">@(displayName ?? "An application") is requesting access to your BTCPay Server account.</p>
</header>
<div asp-validation-summary="All"></div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
@if (Model.NeedsStorePermission && store == null)
{
@if (!Model.Stores.Any())

View File

@ -2,14 +2,19 @@
@{
ViewData.SetActivePage(ManageNavPages.ChangePassword, "Change your password");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<form method="post">
<form method="post">
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<button type="submit" class="btn btn-primary" id="UpdatePassword">Update Password</button>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<div class="form-group">
<label asp-for="OldPassword" class="form-label" data-required></label>
<input asp-for="OldPassword" class="form-control"/>
@ -25,10 +30,9 @@
<input asp-for="ConfirmPassword" class="form-control"/>
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary mt-2" id="UpdatePassword">Update Password</button>
</form>
</div>
</div>
</div>
</form>
@section PageFootContent {
<partial name="_ValidationScriptsPartial" />

View File

@ -1,11 +1,21 @@
@model EnableAuthenticatorViewModel
@{
ViewData.SetActivePage(ManageNavPages.TwoFactorAuthentication, "Enable authenticator app");
ViewData.SetActivePage(ManageNavPages.TwoFactorAuthentication, "Enable Authenticator App");
}
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="TwoFactorAuthentication">Two Factor Authentication</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
<p class="my-3">To use an authenticator app go through the following steps:</p>
<div class="col-xl-8 col-xxl-constrain">
<h3>Enable Authenticator</h3>
<p class="my-3">To use an authenticator app go through the following steps:</p>
<ol class="ps-3">
<li class="mb-5">
<div class="mb-2">Download a two-factor authenticator app like …</div>

View File

@ -3,6 +3,9 @@
ViewData.SetActivePage(ManageNavPages.TwoFactorAuthentication, "Recovery codes");
}
<h2 class="mb-2 mb-lg-3">@ViewData["Title"]</h2>
<partial name="_StatusMessage" />
<div class="alert alert-warning" role="alert">
<h5>
<vc:icon symbol="warning" />
@ -14,7 +17,7 @@
</div>
<div class="row">
<div class="col-md-12">
@for(var row = 0; row < Model.RecoveryCodes.Count(); row += 2)
@for(var row = 0; row < Model.RecoveryCodes.Length; row += 2)
{
<code>@Model.RecoveryCodes[row]</code><text>&nbsp;</text><code>@Model.RecoveryCodes[row + 1]</code><br />
}

View File

@ -3,10 +3,15 @@
@{
ViewData.SetActivePage(ManageNavPages.Index, "Update your account");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="col-xxl-constrain col-xl-8">
<form method="post">
<form method="post">
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<button type="submit" id="save" class="btn btn-primary">Save</button>
</div>
<partial name="_StatusMessage" />
<div class="col-xxl-constrain col-xl-8">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
@ -37,14 +42,12 @@
}
</div>
</div>
<button type="submit" id="save" class="btn btn-primary mt-2">Save</button>
<h3 class="mt-5 mb-4">Delete Account</h3>
<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="This action will also delete all stores, invoices, apps and data associated with the user.">Delete Account</a>
</div>
</form>
</div>
</div>
</form>
<partial name="_Confirm"
model="@(new ConfirmModel("Delete user", "The user will be permanently deleted. This action will also delete all stores, invoices, apps and data associated with your user.", "Delete", actionName: nameof(BTCPayServer.Controllers.UIManageController.DeleteUserPost)))"/>

View File

@ -2,7 +2,12 @@
@{
ViewData.SetActivePage(ManageNavPages.LoginCodes, "Login Codes");
}
<h3 class="mb-3">@ViewData["Title"]</h3>
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<a class="btn btn-primary" id="regeneratecode" asp-action="LoginCodes">Regenerate code</a>
</div>
<partial name="_StatusMessage" />
<p>Easily log into BTCPay Server on another device using a simple login code from an already authenticated device.</p>
<div class="d-inline-flex flex-column" style="width:256px">
<div class="qr-container mb-2">
@ -13,7 +18,6 @@
<div class="progress only-for-js" data-bs-toggle="tooltip" data-bs-placement="top">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width:100%" id="progressbar"></div>
</div>
<a class="btn btn-secondary mt-3" id="regeneratecode" asp-action="LoginCodes">Regenerate code</a>
</div>
@section PageFootContent

View File

@ -2,11 +2,15 @@
@{
ViewData.SetActivePage(ManageNavPages.Notifications, "Notification Settings");
}
<h3 class="mb-3">@ViewData["Title"]</h3>
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<form method="post" asp-action="NotificationSettings">
<form method="post" asp-action="NotificationSettings">
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<button type="submit" class="btn btn-primary" name="command" value="update">Save</button>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
@if (Model.All)
{
<p>All notifications are disabled.</p>
@ -27,13 +31,12 @@
</div>
}
<div class="mt-4">
<button type="submit" class="btn btn-primary" name="command" value="update">Save</button>
<button type="submit" class="btn btn-secondary ms-3" name="command" value="disable-all">Disable all notifications</button>
<button type="submit" class="btn btn-secondary" name="command" value="disable-all">Disable all notifications</button>
</div>
}
</form>
</div>
</div>
</div>
</form>
<script>
function toggleAllCheckboxes(checkbox) {

View File

@ -1,18 +1,25 @@
@model BTCPayServer.Models.ManageViewModels.SetPasswordViewModel
@{
ViewData.SetActivePage(ManageNavPages.ChangePassword, "Set password");
ViewData.SetActivePage(ManageNavPages.ChangePassword, "Set your password");
}
<h3>Set your password</h3>
<form method="post">
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<button type="submit" class="btn btn-primary">Set Password</button>
</div>
<partial name="_StatusMessage" />
<p class="text-info">
You do not have a local username/password for this site. Add a local
account so you can log in without an external login.
</p>
<div class="row">
<div class="col-md-6">
<form method="post">
<div asp-validation-summary="All"></div>
<p class="text-info">
You do not have a local username/password for this site. Add a local
account so you can log in without an external login.
</p>
<div class="row">
<div class="col-md-6">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<div class="form-group">
<label asp-for="NewPassword" class="form-label"></label>
<input asp-for="NewPassword" class="form-control" />
@ -23,10 +30,9 @@
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Set Password</button>
</form>
</div>
</div>
</div>
</form>
@section PageFootContent {
<partial name="_ValidationScriptsPartial" />

View File

@ -1,11 +1,12 @@
@using Fido2NetLib
@using BTCPayServer.Abstractions.Models
@model TwoFactorAuthenticationViewModel
@{
ViewData.SetActivePage(ManageNavPages.TwoFactorAuthentication, "Two-Factor Authentication");
}
<h3 class="mb-3">@ViewData["Title"]</h3>
<div class="sticky-header">
<h2 class="my-1">@ViewData["Title"]</h2>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<p>

View File

@ -1,16 +0,0 @@
@inject SignInManager<ApplicationUser> SignInManager
<div class="sticky-header mb-l">
<h2 class="mt-1 mb-2 mb-lg-4">Account Settings</h2>
<nav id="SectionNav">
<div class="nav">
<a id="SectionNav-@ManageNavPages.Index.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.Index)" asp-controller="UIManage" asp-action="Index">Account</a>
<a id="SectionNav-@ManageNavPages.ChangePassword.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.ChangePassword)" asp-controller="UIManage" asp-action="ChangePassword">Password</a>
<a id="SectionNav-@ManageNavPages.TwoFactorAuthentication.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.TwoFactorAuthentication)" asp-controller="UIManage" asp-action="TwoFactorAuthentication">Two-Factor Authentication</a>
<a id="SectionNav-@ManageNavPages.APIKeys.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.APIKeys)" asp-controller="UIManage" asp-action="APIKeys">API Keys</a>
<a id="SectionNav-@ManageNavPages.Notifications.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.Notifications)" asp-controller="UIManage" asp-action="NotificationSettings">Notifications</a>
<a id="SectionNav-@ManageNavPages.LoginCodes.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.LoginCodes)" asp-controller="UIManage" asp-action="LoginCodes">Login Codes</a>
<vc:ui-extension-point location="user-nav" model="@Model"/>
</div>
</nav>
</div>

View File

@ -2,6 +2,5 @@
@using BTCPayServer.Views
@using BTCPayServer.Views.Manage
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActiveCategory(typeof(ManageNavPages));
}

View File

@ -1,18 +1,17 @@
@using BTCPayServer.Views.Stores
@using BTCPayServer.Abstractions.Extensions
@using MoneroLikePaymentMethodViewModel = BTCPayServer.Services.Altcoins.Monero.UI.UIMoneroLikeStoreController.MoneroLikePaymentMethodViewModel
@using MoneroLikeSettlementThresholdChoice = BTCPayServer.Services.Altcoins.Monero.UI.UIMoneroLikeStoreController.MoneroLikeSettlementThresholdChoice;
@model MoneroLikePaymentMethodViewModel
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData["NavPartialName"] = "../UIStores/_Nav";
ViewData.SetActivePage(Model.CryptoCode, $"{Model.CryptoCode} Settings", Model.CryptoCode);
}
<div class="row">
<div class="col-md-8">
<div asp-validation-summary="All"></div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
@if (Model.Summary != null)
{
<div class="card">

View File

@ -1,18 +1,18 @@
@using BTCPayServer.Views.Stores
@using BTCPayServer.Abstractions.Extensions
@model BTCPayServer.Services.Altcoins.Monero.UI.UIMoneroLikeStoreController.MoneroLikePaymentMethodListViewModel
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePage("Monero Settings", "Monero Settings", "Monero Settings");
ViewData["NavPartialName"] = "../UIStores/_Nav";
}
<div class="row">
<div class="col-md-8">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<table class="table table-hover table-responsive-md">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<div class="table-responsive-md">
<table class="table table-hover">
<thead>
<tr>
<th>Crypto</th>

View File

@ -3,22 +3,32 @@
@using Microsoft.AspNetCore.Mvc.TagHelpers
@model BTCPayServer.PayoutProcessors.OnChain.UIOnChainAutomatedPayoutProcessorsController.OnChainTransferViewModel
@{
ViewData["NavPartialName"] = "../UIStores/_Nav";
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePage(StoreNavPages.PayoutProcessors, "On-Chain Payout Processor", Context.GetStoreData().Id);
var storeId = Context.GetStoreData().Id;
var cryptoCode = Context.GetRouteValue("cryptocode")?.ToString();
ViewData.SetActivePage(StoreNavPages.PayoutProcessors, "On-Chain Payout Processor", storeId);
}
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<div class="d-flex align-items-center justify-content-between mb-3">
<h3 class="mb-0">@ViewData["Title"]</h3>
</div>
<p>Set a schedule for automated On-Chain Bitcoin Payouts. </p>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<form method="post" permissioned="@Policies.CanModifyStoreSettings">
<form method="post" permissioned="@Policies.CanModifyStoreSettings">
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-controller="UIPayoutProcessors" asp-action="ConfigureStorePayoutProcessors" asp-route-storeId="@storeId">Payout Processors</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
<button name="command" type="submit" class="btn btn-primary" value="Save" id="Save">Save</button>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<p>Set a schedule for automated On-Chain Bitcoin Payouts. </p>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<div class="d-flex my-3">
<input asp-for="ProcessNewPayoutsInstantly" type="checkbox" class="btcpay-toggle me-3" />
<label asp-for="ProcessNewPayoutsInstantly" class="form-check-label">Process approved payouts instantly</label>
@ -49,10 +59,9 @@
</div>
<div class="form-text">Only process payouts when this payout sum is reached.</div>
</div>
<button name="command" type="submit" class="btn btn-primary mt-2" value="Save" id="Save">Save</button>
</form>
</div>
</div>
</div>
</form>
@section PageFootContent {
<partial name="_ValidationScriptsPartial" />

View File

@ -22,9 +22,17 @@
}
<form method="post" action="@Url.Action("EditPaymentRequest", "UIPaymentRequest", new { storeId = Model.StoreId, payReqId = Model.Id }, Context.Request.Scheme)">
<div class="sticky-header d-sm-flex align-items-center justify-content-between">
<h2 class="mb-0">@ViewData["Title"]</h2>
<div class="d-flex gap-3 mt-3 mt-sm-0">
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="GetPaymentRequests" asp-route-storeId="@Model.StoreId">Requests</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
<div>
@if (string.IsNullOrEmpty(Model.Id))
{
<button type="submit" class="btn btn-primary" id="SaveButton" permission="@Policies.CanModifyPaymentRequests">Create</button>
@ -36,12 +44,15 @@
}
</div>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<div asp-validation-summary="ModelOnly"></div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly"></div>
}
<div class="form-group">
<label asp-for="Title" class="form-label" data-required></label>
<input asp-for="Title" class="form-control" required />

View File

@ -23,8 +23,8 @@
Model.Search.ContainsFilter(key) && Model.Search.GetFilterBool(key) is true;
}
<div class="sticky-header d-sm-flex align-items-center justify-content-between">
<h2 class="mb-0">
<div class="sticky-header">
<h2>
@ViewData["Title"]
<a href="#descriptor" data-bs-toggle="collapse">
<vc:icon symbol="info" />

View File

@ -1,22 +1,19 @@
@using BTCPayServer.Views.Stores
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Abstractions.Models
@using BTCPayServer.Client
@model List<BTCPayServer.PayoutProcessors.UIPayoutProcessorsController.StorePayoutProcessorsView>
@{
ViewData["NavPartialName"] = "../UIStores/_Nav";
Layout = "../Shared/_NavLayout.cshtml";
var storeId = Context.GetStoreData().Id;
ViewData.SetActivePage(StoreNavPages.PayoutProcessors, "Payout Processors", storeId);
}
<div class="sticky-header">
<h2 class="my-1">@ViewData["Title"]</h2>
</div>
<p>Payout Processors allow BTCPay Server to handle payouts in an automated way.</p>
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<div class="d-flex align-items-center justify-content-between mb-3">
<h3 class="mb-0">@ViewData["Title"]</h3>
</div>
<p>Payout Processors allow BTCPay Server to handle payouts in an automated way.</p>
@if (Model.Any())
{
foreach (var processorsView in Model)

View File

@ -1,7 +1,7 @@
@using BTCPayServer.Views.Stores
@model BTCPayServer.Models.WalletViewModels.UpdatePullPaymentModel
@{
var storeId = Context.GetStoreData().Id;
ViewData.SetActivePage(StoreNavPages.Create, "Edit Pull Payment", Model.Id);
}
@ -14,10 +14,18 @@
<script src="~/vendor/summernote/summernote-bs5.js" asp-append-version="true"></script>
}
<form method="post" asp-action="EditPullPayment" asp-route-storeId="@Context.GetRouteValue("storeId")" asp-route-pullPaymentId="@Model.Id">
<div class="sticky-header d-sm-flex align-items-center justify-content-between">
<h2 class="mb-0">@ViewData["Title"]</h2>
<div class="d-flex gap-3 mt-3 mt-sm-0">
<form method="post" asp-action="EditPullPayment" asp-route-storeId="@storeId" asp-route-pullPaymentId="@Model.Id">
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-controller="UIStorePullPayments" asp-action="PullPayments" asp-route-storeId="@storeId">Pull Payments</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
<div>
@if (string.IsNullOrEmpty(Model.Id))
{
<button type="submit" class="btn btn-primary" id="SaveButton">Create</button>
@ -34,7 +42,10 @@
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<div asp-validation-summary="ModelOnly"></div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly"></div>
}
<div class="form-group">
<label asp-for="Name" class="form-label" data-required></label>
<input asp-for="Name" class="form-control" required />

View File

@ -19,17 +19,15 @@
}
<div class="sticky-header">
<div class="d-flex flex-wrap align-items-center justify-content-between gap-3">
<h2 class="mb-0">
@ViewData["Title"]
<a href="https://docs.btcpayserver.org/Accounting/" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</h2>
<div class="d-flex flex-wrap gap-3">
<a cheat-mode="true" class="btn btn-outline-info text-nowrap" asp-action="StoreReports" asp-route-fakeData="true" asp-route-viewName="@Model.Request?.ViewName">Create fake data</a>
<button id="exportCSV" class="btn btn-primary text-nowrap" type="button">Export</button>
</div>
<h2>
@ViewData["Title"]
<a href="https://docs.btcpayserver.org/Accounting/" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</h2>
<div>
<a cheat-mode="true" class="btn btn-outline-info text-nowrap" asp-action="StoreReports" asp-route-fakeData="true" asp-route-viewName="@Model.Request?.ViewName">Create fake data</a>
<button id="exportCSV" class="btn btn-primary text-nowrap" type="button">Export</button>
</div>
</div>
<div class="d-flex flex-column flex-sm-row align-items-center gap-3 mb-l">

View File

@ -13,11 +13,15 @@
<partial name="_ValidationScriptsPartial" />
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<form method="post" enctype="multipart/form-data">
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<button type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<form method="post" enctype="multipart/form-data">
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<div class="form-group">
<label asp-for="ServerName" class="form-label"></label>
<input asp-for="ServerName" class="form-control" />
@ -107,8 +111,6 @@
}
</div>
</div>
<button type="submit" class="btn btn-primary mt-2" name="command" value="Save">Save</button>
</form>
</div>
</div>
</div>
</form>

View File

@ -3,7 +3,18 @@
ViewData.SetActivePage(ServerNavPages.Services, $"C-Lightning {Model.ConnectionType}");
}
<h2 class="mt-1 mb-4">@ViewData["Title"]</h2>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Services">Services</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
@if (!ViewContext.ModelState.IsValid)
{

View File

@ -3,13 +3,25 @@
ViewData.SetActivePage(ServerNavPages.Services, "BTCPay Server Configurator");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All"></div>
</div>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Services">Services</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<div class="row">
<div class="col-md-8">

View File

@ -4,15 +4,19 @@
ViewData.SetActivePage(ServerNavPages.Services, $"Create temporary file link");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<form method="post">
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<button type="submit" class="btn btn-primary" name="command" value="Generate">Generate</button>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<form method="post">
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" asp-for="IsDownload"/>
<label asp-for="IsDownload" class="form-check-label"></label>
@ -28,10 +32,9 @@
<span asp-validation-for="TimeAmount" class="text-danger"></span>
<span asp-validation-for="TimeType" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary mt-2" name="command" value="Generate">Generate</button>
</form>
</div>
</div>
</div>
</form>
@section PageFootContent {
<partial name="_ValidationScriptsPartial" />

View File

@ -3,31 +3,42 @@
ViewData.SetActivePage(ServerNavPages.Users, "Create account");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="row">
<div class="col-xl-6 col-xxl-constrain">
<form method="post" asp-action="CreateUser">
<div asp-validation-summary="ModelOnly"></div>
<form method="post" asp-action="CreateUser">
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="ListUsers">Users</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
<button id="Save" type="submit" class="btn btn-primary" name="command" value="Save">Create Account</button>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-6 col-xxl-constrain">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly"></div>
}
<div class="form-group">
<label asp-for="Email" class="form-label"></label>
<input asp-for="Email" required="required" class="form-control"/>
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="form-label"></label>
<input asp-for="Password" class="form-control"/>
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="form-label"></label>
<input asp-for="ConfirmPassword" class="form-control"/>
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
@if (ViewData["AllowIsAdmin"] is true)
{
<div class="form-group form-check">
@ -44,9 +55,10 @@
<span asp-validation-for="EmailConfirmed" class="text-danger"></span>
</div>
}
<button id="Save" type="submit" class="btn btn-primary mt-2" name="command" value="Save">Create account</button>
</form>
</div>
</div>
</div>
</form>
@section PageFootContent {
<partial name="_ValidationScriptsPartial" />
}

View File

@ -10,16 +10,37 @@
});
</script>
}
<form method="post">
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Services">Services</a>
</li>
<li class="breadcrumb-item">
<a asp-action="DynamicDnsService">Dynamic DNS Settings</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>
<span>@ViewData["Title"]</span>
<small>
<a href="https://docs.btcpayserver.org/Apps/" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</small>
</h2>
</nav>
<button name="command" class="btn btn-primary" type="submit" value="Save">Save</button>
</div>
<partial name="_StatusMessage" />
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="row">
<div class="col-md-8">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<form method="post">
<div class="row">
<div class="col-md-8">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<div class="form-group">
<input type="hidden" asp-for="Modify"/>
<div class="form-group">
@ -68,8 +89,7 @@
<input asp-for="Settings.Enabled" disabled type="checkbox" class="btcpay-toggle ms-2" />
</div>
}
<button name="command" class="btn btn-primary mt-2" type="submit" value="Save">Save</button>
</div>
</form>
</div>
</div>
</div>
</form>

View File

@ -3,23 +3,32 @@
@{
ViewData.SetActivePage(ServerNavPages.Services, "Dynamic DNS Settings");
}
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Services">Services</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>
<span>@ViewData["Title"]</span>
<small>
<a href="https://docs.btcpayserver.org/Apps/" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</small>
</h2>
</nav>
<div>
<form method="post" asp-action="DynamicDnsService">
<button id="AddDynamicDNS" class="btn btn-primary mt-2" type="submit">Add Service</button>
</form>
</div>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-md-8">
<div class="d-sm-flex align-items-center justify-content-between mb-3">
<h3 class="mb-0">
@ViewData["Title"]
<small>
<a href="https://docs.btcpayserver.org/Apps/" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</small>
</h3>
<form method="post" asp-action="DynamicDnsService">
<button id="AddDynamicDNS" class="btn btn-primary mt-2" type="submit">Add service</button>
</form>
</div>
<div class="form-group">
<p>
Dynamic DNS allows you to have a stable DNS name pointing to your server, even if your IP address changes regulary.
@ -33,40 +42,42 @@
@if (Model.Any())
{
<table class="table table-sm table-responsive-md">
<thead>
<tr>
<th>Hostname</th>
<th>Last updated</th>
<th class="text-center">Enabled</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var service in Model)
{
<div class="table-responsive-md">
<table class="table table-sm">
<thead>
<tr>
<td>@service.Settings.Hostname</td>
<td>@service.LastUpdated</td>
<td class="text-center">
@if (service.Settings.Enabled)
{
<vc:icon symbol="checkmark" css-class="text-success" />
}
else
{
<vc:icon symbol="cross" css-class="text-danger" />
}
</td>
<td class="text-end">
<a asp-action="DynamicDnsService" asp-route-hostname="@service.Settings.Hostname">Edit</a>
<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">Delete</a>
</td>
<th>Hostname</th>
<th>Last updated</th>
<th class="text-center">Enabled</th>
<th class="text-end">Actions</th>
</tr>
}
</tbody>
</table>
</thead>
<tbody>
@foreach (var service in Model)
{
<tr>
<td>@service.Settings.Hostname</td>
<td>@service.LastUpdated</td>
<td class="text-center">
@if (service.Settings.Enabled)
{
<vc:icon symbol="checkmark" css-class="text-success" />
}
else
{
<vc:icon symbol="cross" css-class="text-danger" />
}
</td>
<td class="text-end">
<a asp-action="DynamicDnsService" asp-route-hostname="@service.Settings.Hostname">Edit</a>
<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">Delete</a>
</td>
</tr>
}
</tbody>
</table>
</div>
}
else
{

View File

@ -3,7 +3,21 @@
ViewData.SetActivePage(ServerNavPages.Files, "Amazon S3 Storage");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Files">Files</a>
</li>
<li class="breadcrumb-item">
<a asp-action="Storage">Storage Provider</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Provider</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-10 col-xxl-constrain">

View File

@ -3,7 +3,21 @@
ViewData.SetActivePage(ServerNavPages.Files, "Azure Blob Storage");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Files">Files</a>
</li>
<li class="breadcrumb-item">
<a asp-action="Storage">Storage Provider</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Provider</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-10 col-xxl-constrain">

View File

@ -3,7 +3,21 @@
ViewData.SetActivePage(ServerNavPages.Files, $"Local Filesystem Storage");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Files">Files</a>
</li>
<li class="breadcrumb-item">
<a asp-action="Storage">Storage Provider</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Provider</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
<p>Any uploaded files are being saved on the same machine that hosts BTCPay; please pay attention to your storage space.</p>
<div class="row">

View File

@ -3,7 +3,21 @@
ViewData.SetActivePage(ServerNavPages.Files, "Google Cloud Storage");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Files">Files</a>
</li>
<li class="breadcrumb-item">
<a asp-action="Storage">Storage Provider</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Provider</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-10 col-xxl-constrain">

View File

@ -3,9 +3,13 @@
ViewData.SetActivePage(ServerNavPages.Emails, "Emails");
}
<h3 class="mb-4">Email Server</h3>
<form method="post" autocomplete="off">
<div class="form-group" style="margin:1rem 0 2rem">
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<button type="submit" class="btn btn-primary" name="command" value="Save" id="Save">Save</button>
</div>
<partial name="_StatusMessage" />
<div class="form-group mb-4">
<div class="d-flex align-items-center">
<input asp-for="EnableStoresToUseServerEmailSettings" type="checkbox" class="btcpay-toggle me-3"/>
<div>

View File

@ -3,127 +3,116 @@
ViewData.SetActivePage(ServerNavPages.Files, "File Storage");
}
<div class="row">
<div class="col">
<div class="d-flex align-items-center justify-content-between mt-n1 mb-4">
<h3 class="mb-0">@ViewData["Title"]</h3>
<a asp-action="storage" asp-route-forceChoice="true" asp-route-returnurl="@ViewData["ReturnUrl"]" class="btn btn-secondary d-flex align-items-center">
<vc:icon symbol="settings" />
<span class="ms-1">Settings</span>
</a>
</div>
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<a asp-action="storage" asp-route-forceChoice="true" asp-route-returnurl="@ViewData["ReturnUrl"]" class="btn btn-secondary d-flex align-items-center">
<vc:icon symbol="settings" />
<span class="ms-1">Settings</span>
</a>
</div>
<partial name="_StatusMessage" />
@if (!Model.StorageConfigured)
@if (!Model.StorageConfigured)
{
<p>
Before being able to upload you first need to
<a asp-action="storage" asp-route-forceChoice="true" asp-route-returnurl="@ViewData["ReturnUrl"]">choose your file storage service provider</a>.
<a href="https://docs.btcpayserver.org/FAQ/ServerSettings/#how-to-upload-files-to-btcpay" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</p>
}
else
{
<p>
Change your <a asp-action="storage" asp-route-forceChoice="true" asp-route-returnurl="@ViewData["ReturnUrl"]">file storage service</a> provider.
<a href="https://docs.btcpayserver.org/FAQ/ServerSettings/#how-to-upload-files-to-btcpay" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</p>
@if (Model.DirectUrlByFiles is { Count: > 0 })
{
foreach (var fileUrlPair in Model.DirectUrlByFiles)
{
<p>
Before being able to upload you first need to
<a asp-action="storage" asp-route-forceChoice="true" asp-route-returnurl="@ViewData["ReturnUrl"]">choose your file storage service provider</a>.
<a href="https://docs.btcpayserver.org/FAQ/ServerSettings/#how-to-upload-files-to-btcpay" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</p>
}
else
{
<p>
Change your <a asp-action="storage" asp-route-forceChoice="true" asp-route-returnurl="@ViewData["ReturnUrl"]">file storage service</a> provider.
<a href="https://docs.btcpayserver.org/FAQ/ServerSettings/#how-to-upload-files-to-btcpay" target="_blank" rel="noreferrer noopener" title="More information...">
<vc:icon symbol="info" />
</a>
</p>
@if (Model.StorageConfigured)
{
<form asp-action="CreateFiles" method="post" enctype="multipart/form-data">
<div class="d-flex">
<input multiple type="file" class="form-control mb-3" name="files" id="files" required>
<button class="btn btn-primary mb-3 ms-3" role="button">Upload</button>
</div>
</form>
}
@if (Model.DirectUrlByFiles is { Count: > 0 })
{
foreach (var fileUrlPair in Model.DirectUrlByFiles)
{
var fileId = fileUrlPair.Key;
var file = Model.Files.Single(storedFile => storedFile.Id.Equals(fileId, StringComparison.InvariantCultureIgnoreCase));
var url = Url.Action("GetFile", "UIStorage", new { fileId }, Context.Request.Scheme, Context.Request.Host.ToString());
<div class="border border-light rounded bg-tile mt-3">
<div class="row">
<div class="col-sm-12 col-md-4">
<div class="input-group">
<div class="form-floating">
<input id="@fileId-name" class="form-control-plaintext" readonly="readonly" value="@file.FileName">
<label>File name</label>
</div>
<button type="button" class="btn btn-link" data-clipboard="@file.FileName">
<vc:icon symbol="actions-copy" />
</button>
</div>
</div>
<div class="col-sm-12 col-md-4 ">
<div class="input-group ">
<div class="form-floating">
<input id="@fileId" class="form-control-plaintext" readonly="readonly" value="@fileId">
<label>File Id</label>
</div>
<button type="button" class="btn btn-link" data-clipboard="@fileId">
<vc:icon symbol="actions-copy" />
</button>
</div>
</div>
<div class=" col-sm-12 col-md-4">
<div class="input-group">
<div class="form-floating">
<input id="@fileId-url" class="form-control-plaintext" readonly="readonly" value="@url">
<label>Permanent Url</label>
</div>
<button type="button" class="btn btn-link" data-clipboard="@url">
<vc:icon symbol="actions-copy" />
</button>
</div>
var fileId = fileUrlPair.Key;
var file = Model.Files.Single(storedFile => storedFile.Id.Equals(fileId, StringComparison.InvariantCultureIgnoreCase));
var url = Url.Action("GetFile", "UIStorage", new { fileId }, Context.Request.Scheme, Context.Request.Host.ToString());
<div class="border border-light rounded bg-tile mt-3">
<div class="row">
<div class="col-sm-12 col-md-4">
<div class="input-group">
<div class="form-floating">
<input id="@fileId-name" class="form-control-plaintext" readonly="readonly" value="@file.FileName">
<label>File name</label>
</div>
<button type="button" class="btn btn-link" data-clipboard="@file.FileName">
<vc:icon symbol="actions-copy" />
</button>
</div>
</div>
}
}
@if (Model.Files.Any())
{
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Timestamp</th>
<th>User</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var file in Model.Files)
{
<tr>
<td>
<a asp-action="Files" asp-route-fileIds="@file.Id">@file.FileName</a>
</td>
<td>@file.Timestamp.ToBrowserDate()</td>
<td>@file.ApplicationUser.UserName</td>
<td class="text-end">
<a href="@Url.Action("Files", "UIServer", new {fileIds = new [] { file.Id }})" class="text-nowrap">Get Link</a>
- <a asp-action="DeleteFile" asp-route-fileId="@file.Id">Remove</a>
</td>
</tr>
}
</tbody>
</table>
<div class="col-sm-12 col-md-4 ">
<div class="input-group ">
<div class="form-floating">
<input id="@fileId" class="form-control-plaintext" readonly="readonly" value="@fileId">
<label>File Id</label>
</div>
<button type="button" class="btn btn-link" data-clipboard="@fileId">
<vc:icon symbol="actions-copy" />
</button>
</div>
</div>
<div class=" col-sm-12 col-md-4">
<div class="input-group">
<div class="form-floating">
<input id="@fileId-url" class="form-control-plaintext" readonly="readonly" value="@url">
<label>Permanent Url</label>
</div>
<button type="button" class="btn btn-link" data-clipboard="@url">
<vc:icon symbol="actions-copy" />
</button>
</div>
</div>
<button type="button" class="btn btn-link" data-clipboard="@url">
<vc:icon symbol="copy" />
</button>
</div>
}
else
{
<p class="text-secondary mt-3">There are no files yet.</p>
}
</div>
}
</div>
</div>
}
@if (Model.Files.Any())
{
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Timestamp</th>
<th>Uploaded By</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var file in Model.Files)
{
<tr>
<td>
<a asp-action="Files" asp-route-fileIds="@file.Id">@file.FileName</a>
</td>
<td>@file.Timestamp.ToBrowserDate()</td>
<td>@file.ApplicationUser.UserName</td>
<td class="text-end">
<a href="@Url.Action("Files", "UIServer", new {fileIds = new [] { file.Id }})" class="text-nowrap">Get Link</a>
- <a asp-action="DeleteFile" asp-route-fileId="@file.Id">Remove</a>
</td>
</tr>
}
</tbody>
</table>
</div>
}
else
{
<p class="text-secondary mt-3">There are no files yet.</p>
}
}

View File

@ -1,9 +1,20 @@
@model ChargeServiceViewModel
@{
ViewData.SetActivePage(ServerNavPages.Services, "Lightning charge service");
ViewData.SetActivePage(ServerNavPages.Services, "Lightning Charge Service");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Services">Services</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-md-8">

View File

@ -3,7 +3,18 @@
ViewData.SetActivePage(ServerNavPages.Services, Model.WalletName);
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Services">Services</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
@if (Model.ShowQR)
{

View File

@ -3,14 +3,11 @@
Layout = "_Layout";
ViewData.SetActivePage(ServerNavPages.Stores, "Store Overview");
}
<div class="sticky-header">
<h2 class="my-1">@ViewData["Title"]</h2>
</div>
<partial name="_StatusMessage" />
<div class="d-sm-flex justify-content-between mb-2">
<h2 class="mb-0">
@ViewData["Title"]
</h2>
</div>
@if (Model.Stores.Any())
{
<table class="table table-hover">

View File

@ -13,12 +13,13 @@
const string sortByDesc = "Sort by email descending...";
const string sortByAsc = "Sort by email ascending...";
}
<div class="d-flex align-items-center justify-content-between mb-3">
<h3 class="mb-0">@ViewData["Title"]</h3>
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<a asp-action="CreateUser" class="btn btn-primary" role="button" id="CreateUser">
Add User
</a>
</div>
<partial name="_StatusMessage" />
<form asp-action="ListUsers" asp-route-sortOrder="@(userEmailSortOrder)" style="max-width:640px" method="get">
<input asp-for="SearchTerm" class="form-control" placeholder="Search by email..." />

View File

@ -4,7 +4,18 @@
ViewData.SetActivePage(ServerNavPages.Services, "LND Seed Backup");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Services">Services</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
@if (Model.IsWalletUnlockPresent)
{

View File

@ -3,7 +3,18 @@
ViewData.SetActivePage(ServerNavPages.Services, $"LND {Model.ConnectionType}");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Services">Services</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
<p>
BTCPay exposes LND's @Model.ConnectionType service for outside consumption, you will find connection information here.
</p>
@ -36,7 +47,7 @@
<h4 class="mt-4 mb-3">QR Code connection</h4>
<p>
You can use this QR Code to connect external software to your LND instance.<br/>
You can use this QR Code to connect external software to your LND instance.<br />
This QR Code is only valid for 10 minutes.
</p>
@ -74,7 +85,7 @@ else
{
<div class="form-group">
<label asp-for="Host" class="form-label"></label>
<input asp-for="Host" readonly class="form-control"/>
<input asp-for="Host" readonly class="form-control" />
</div>
<div class="form-group">
<label asp-for="SSL" class="form-label"></label>
@ -85,49 +96,49 @@ else
{
<div class="form-group">
<label asp-for="Uri" class="form-label"></label>
<input asp-for="Uri" readonly class="form-control"/>
<input asp-for="Uri" readonly class="form-control" />
</div>
}
@if (Model.Macaroon != null)
{
<div class="form-group">
<label asp-for="Macaroon" class="form-label"></label>
<input asp-for="Macaroon" readonly class="form-control"/>
<input asp-for="Macaroon" readonly class="form-control" />
</div>
}
@if (Model.AdminMacaroon != null)
{
<div class="form-group">
<label asp-for="AdminMacaroon" class="form-label"></label>
<input asp-for="AdminMacaroon" readonly class="form-control"/>
<input asp-for="AdminMacaroon" readonly class="form-control" />
</div>
}
@if (Model.InvoiceMacaroon != null)
{
<div class="form-group">
<label asp-for="InvoiceMacaroon" class="form-label"></label>
<input asp-for="InvoiceMacaroon" readonly class="form-control"/>
<input asp-for="InvoiceMacaroon" readonly class="form-control" />
</div>
}
@if (Model.ReadonlyMacaroon != null)
{
<div class="form-group">
<label asp-for="ReadonlyMacaroon" class="form-label"></label>
<input asp-for="ReadonlyMacaroon" readonly class="form-control"/>
<input asp-for="ReadonlyMacaroon" readonly class="form-control" />
</div>
}
@if (Model.GRPCSSLCipherSuites != null)
{
<div class="form-group">
<label asp-for="GRPCSSLCipherSuites" class="form-label"></label>
<input asp-for="GRPCSSLCipherSuites" readonly class="form-control"/>
<input asp-for="GRPCSSLCipherSuites" readonly class="form-control" />
</div>
}
@if (Model.CertificateThumbprint != null)
{
<div class="form-group">
<label asp-for="CertificateThumbprint" class="form-label"></label>
<input asp-for="CertificateThumbprint" readonly class="form-control"/>
<input asp-for="CertificateThumbprint" readonly class="form-control" />
</div>
}
</div>
@ -135,7 +146,7 @@ else
@section PageFootContent {
<partial name="_ValidationScriptsPartial" />
@if(Model.QRCode != null)
@if (Model.QRCode != null)
{
<script src="~/js/qrcode.js" asp-append-version="true"></script>
<script>

View File

@ -3,7 +3,10 @@
ViewData.SetActivePage(ServerNavPages.Logs, "Logs");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<h2 class="my-1">@ViewData["Title"]</h2>
</div>
<partial name="_StatusMessage" />
<ul class="list-unstyled">
@foreach (var file in Model.LogFiles)

View File

@ -3,7 +3,10 @@
ViewData.SetActivePage(ServerNavPages.Maintenance, "Maintenance");
}
<h3 class="mb-3">@ViewData["Title"]</h3>
<div class="sticky-header">
<h2 class="my-1">@ViewData["Title"]</h2>
</div>
<partial name="_StatusMessage" />
<form method="post">
<div class="row mb-5">

View File

@ -3,7 +3,18 @@
ViewData.SetActivePage(ServerNavPages.Services, Model.WalletName);
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Services">Services</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
@if (Model.ShowQR)
{

View File

@ -1,11 +1,8 @@
@using BTCPayServer.Payments;
@using BTCPayServer.Services
@using BTCPayServer.Services.Invoices
@using BTCPayServer.Services.Mails;
@model BTCPayServer.Services.PoliciesSettings
@inject SettingsRepository _SettingsRepository
@inject TransactionLinkProviders TransactionLinkProviders
@inject PaymentMethodHandlerDictionary _handlers;
@{
ViewData.SetActivePage(ServerNavPages.Policies);
var linkProviders = TransactionLinkProviders.ToArray();
@ -21,247 +18,247 @@
</style>
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<form method="post">
<div class="sticky-header">
<h2>@ViewData["Title"]</h2>
<button id="SaveButton" type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
</div>
<partial name="_StatusMessage" />
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
}
<div class="row">
<div class="col-xl-10 col-xxl-constrain">
<form method="post" class="d-flex flex-column">
<div class="form-group mb-5">
<h4 class="mb-3">Registration</h4>
<div class="d-flex gap-3">
<input asp-for="EnableRegistration" type="checkbox" class="btcpay-toggle" />
<div>
<label asp-for="EnableRegistration" class="form-label mb-0"></label>
<span asp-validation-for="EnableRegistration" class="text-danger"></span>
<div class="info-note mt-2 text-warning" role="alert">
<vc:icon symbol="warning" />
Caution: Enabling public user registration means anyone can register to your server and may expose your BTCPay Server instance to potential security risks from unknown users.
</div>
<div class="subsettings">
<div class="d-flex my-3">
@{
var emailSettings = (await _SettingsRepository.GetSettingAsync<EmailSettings>()) ?? new EmailSettings();
/* The "|| Model.RequiresConfirmedEmail" check is for the case when a user had checked
the checkbox without first configuring the e-mail settings so that they can uncheck it. */
var isEmailConfigured = emailSettings.IsComplete() || Model.RequiresConfirmedEmail;
}
<input asp-for="RequiresConfirmedEmail" type="checkbox" class="btcpay-toggle me-3" disabled="@(isEmailConfigured ? null : "disabled")" />
<div>
<label asp-for="RequiresConfirmedEmail" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/FAQ/ServerSettings/#how-to-allow-registration-on-my-btcpay-server" target="_blank" rel="noreferrer noopener">
<vc:icon symbol="info" />
</a>
<span asp-validation-for="RequiresConfirmedEmail" class="text-danger"></span>
@if (!isEmailConfigured)
{
<div class="text-secondary">Your email server has not been configured. <a asp-controller="UIServer" asp-action="Emails">Please configure it first.</a></div>
<div class="row">
<div class="col-xl-10 col-xxl-constrain">
<div class="d-flex flex-column">
<div class="form-group mb-5">
<h4 class="mb-3">Registration</h4>
<div class="d-flex gap-3">
<input asp-for="EnableRegistration" type="checkbox" class="btcpay-toggle" />
<div>
<label asp-for="EnableRegistration" class="form-label mb-0"></label>
<span asp-validation-for="EnableRegistration" class="text-danger"></span>
<div class="info-note mt-2 text-warning" role="alert">
<vc:icon symbol="warning" />
Caution: Enabling public user registration means anyone can register to your server and may expose your BTCPay Server instance to potential security risks from unknown users.
</div>
<div class="subsettings">
<div class="d-flex my-3">
@{
var emailSettings = (await _SettingsRepository.GetSettingAsync<EmailSettings>()) ?? new EmailSettings();
/* The "|| Model.RequiresConfirmedEmail" check is for the case when a user had checked
the checkbox without first configuring the e-mail settings so that they can uncheck it. */
var isEmailConfigured = emailSettings.IsComplete() || Model.RequiresConfirmedEmail;
}
<input asp-for="RequiresConfirmedEmail" type="checkbox" class="btcpay-toggle me-3" disabled="@(isEmailConfigured ? null : "disabled")" />
<div>
<label asp-for="RequiresConfirmedEmail" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/FAQ/ServerSettings/#how-to-allow-registration-on-my-btcpay-server" target="_blank" rel="noreferrer noopener">
<vc:icon symbol="info" />
</a>
<span asp-validation-for="RequiresConfirmedEmail" class="text-danger"></span>
@if (!isEmailConfigured)
{
<div class="text-secondary">Your email server has not been configured. <a asp-controller="UIServer" asp-action="Emails">Please configure it first.</a></div>
}
</div>
</div>
</div>
<div class="d-flex my-3">
<input asp-for="RequiresUserApproval" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="RequiresUserApproval" class="form-check-label"></label>
<span asp-validation-for="RequiresUserApproval" class="text-danger"></span>
<div class="d-flex my-3">
<input asp-for="RequiresUserApproval" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="RequiresUserApproval" class="form-check-label"></label>
<span asp-validation-for="RequiresUserApproval" class="text-danger"></span>
</div>
</div>
</div>
<div class="d-flex my-3">
<input asp-for="EnableNonAdminCreateUserApi" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="EnableNonAdminCreateUserApi" class="form-check-label"></label>
<span asp-validation-for="EnableNonAdminCreateUserApi" class="text-danger"></span>
<div class="info-note mt-2 text-warning" role="alert">
<vc:icon symbol="warning" />
Caution: Allowing non-admins to have access to API endpoints may expose your BTCPay Server instance to potential security risks from unknown users.
<div class="d-flex my-3">
<input asp-for="EnableNonAdminCreateUserApi" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="EnableNonAdminCreateUserApi" class="form-check-label"></label>
<span asp-validation-for="EnableNonAdminCreateUserApi" class="text-danger"></span>
<div class="info-note mt-2 text-warning" role="alert">
<vc:icon symbol="warning" />
Caution: Allowing non-admins to have access to API endpoints may expose your BTCPay Server instance to potential security risks from unknown users.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="mb-5">
<h4 class="mb-3">Users</h4>
<div class="d-flex my-3">
<input asp-for="AllowLightningInternalNodeForAll" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="AllowLightningInternalNodeForAll" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/FAQ/LightningNetwork/#how-many-users-can-use-lightning-network-in-btcpay" target="_blank" rel="noreferrer noopener">
<vc:icon symbol="info" />
</a>
<span asp-validation-for="AllowLightningInternalNodeForAll" class="text-danger"></span>
<div class="info-note mt-2 text-warning" role="alert">
<vc:icon symbol="warning"/>
Caution: Enabling this option, may simplify the onboarding for third-parties but carries liabilities and security risks associated with sharing the lightning node with other users.
</div>
</div>
</div>
<div class="d-flex my-3">
<input asp-for="AllowHotWalletForAll" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="AllowHotWalletForAll" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/CreateWallet/#requirements-to-create-wallets" target="_blank" rel="noreferrer noopener">
<vc:icon symbol="info" />
</a>
<span asp-validation-for="AllowHotWalletForAll" class="text-danger"></span>
<div class="info-note mt-2 text-warning" role="alert">
<vc:icon symbol="warning"/>
Caution: Enabling this option, may simplify the onboarding and spending for third-parties but carries liabilities and security risks associated to storing private keys of third parties on a server.
</div>
</div>
</div>
<div class="d-flex my-3">
<input asp-for="AllowHotWalletRPCImportForAll" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="AllowHotWalletRPCImportForAll" class="form-check-label"></label>
<span asp-validation-for="AllowHotWalletRPCImportForAll" class="text-danger"></span>
<div class="info-note mt-2 text-warning" role="alert">
<vc:icon symbol="warning"/>
Caution: Enabling this option, may simplify the onboarding and spending for third-parties but carries liabilities and security risks associated to storing private keys of third parties on a server.
</div>
</div>
</div>
</div>
<div class="mb-5">
<h4 class="mb-3">Server</h4>
<div class="d-flex my-3">
<input asp-for="AllowSearchEngines" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="AllowSearchEngines" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/FAQ/ServerSettings/#how-to-hide-my-btcpay-server-from-search-engines" target="_blank" rel="noreferrer noopener">
<vc:icon symbol="info" />
</a>
<span asp-validation-for="AllowSearchEngines" class="text-danger"></span>
</div>
</div>
@if (ViewBag.UpdateUrlPresent)
{
<div class="mb-5">
<h4 class="mb-3">Users</h4>
<div class="d-flex my-3">
<input asp-for="CheckForNewVersions" type="checkbox" class="btcpay-toggle me-3"/>
<input asp-for="AllowLightningInternalNodeForAll" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="CheckForNewVersions" class="form-check-label"></label>
<span asp-validation-for="CheckForNewVersions" class="text-danger"></span>
</div>
</div>
}
@*
Let's uncomment this when we have new experimental features.
<div class="d-flex align-items-center my-3">
<input asp-for="Experimental" type="checkbox" class="btcpay-toggle me-3" />
<div>
<label asp-for="Experimental" class="form-check-label"></label>
<div class="text-muted">
Will allow you to use the custodian account feature.
<a href="https://blog.btcpayserver.org/btcpay-server-1-9-0/#-exchange-integration-via-plugins-" target="_blank" rel="noreferrer noopener">
<label asp-for="AllowLightningInternalNodeForAll" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/FAQ/LightningNetwork/#how-many-users-can-use-lightning-network-in-btcpay" target="_blank" rel="noreferrer noopener">
<vc:icon symbol="info" />
</a>
<span asp-validation-for="AllowLightningInternalNodeForAll" class="text-danger"></span>
<div class="info-note mt-2 text-warning" role="alert">
<vc:icon symbol="warning"/>
Caution: Enabling this option, may simplify the onboarding for third-parties but carries liabilities and security risks associated with sharing the lightning node with other users.
</div>
</div>
</div>
</div> *@
</div>
<h4 class="mb-3">Plugins</h4>
<div class="form-group">
<label asp-for="PluginSource" class="form-label"></label>
<input asp-for="PluginSource" placeholder="@PoliciesSettings.DefaultPluginSource" class="form-control"/>
<span asp-validation-for="PluginSource" class="text-danger"></span>
</div>
<div class="d-flex mt-n2">
<input asp-for="PluginPreReleases" type="checkbox" class="btcpay-toggle me-3"/>
<label asp-for="PluginPreReleases" class="form-check-label"></label>
</div>
@* To handle the multiple submit buttons on this page: Give this button
a specific order to put it last, but keep it first in the markup *@
<div class="order-1">
<button id="SaveButton" type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
</div>
<h4 class="mt-5">Customization</h4>
<div class="form-group mb-3">
<label asp-for="DefaultCurrency" class="form-label"></label>
<input asp-for="DefaultCurrency" placeholder="@StoreBlob.StandardDefaultCurrency" class="form-control" currency-selection />
</div>
<div class="form-group mb-5">
<label asp-for="RootAppId" class="form-label"></label>
<select asp-for="RootAppId" asp-items="@(new SelectList(ViewBag.AppsList, nameof(SelectListItem.Value), nameof(SelectListItem.Text), Model.RootAppId))" class="form-select"></select>
@if (!Model.DomainToAppMapping.Any())
{
<button id="AddDomainButton" type="submit" name="command" value="add-domain" class="btn btn-link px-0">Map specific domains to specific apps</button>
}
</div>
@if (Model.DomainToAppMapping.Any())
{
<h5 class="d-flex align-items-center justify-content-between mt-5 gap-3">
Domain to app mapping
<button id="AddDomainButton" type="submit" name="command" value="add-domain" class="d-inline-block btn text-primary btn-link p-0">
Add domain mapping
</button>
</h5>
<div class="list-group list-group-flush mb-2">
@for (var index = 0; index < Model.DomainToAppMapping.Count; index++)
{
<div class="list-group-item px-0 pt-3">
<div class="form-group">
<div class="d-flex align-items-center justify-content-between">
<label asp-for="DomainToAppMapping[index].Domain" class="form-label"></label>
<button type="submit" title="Remove domain mapping" name="command" value="@($"remove-domain:{index}")" class="d-inline-block ms-2 btn text-danger btn-link p-0 mb-2">
<vc:icon symbol="cross" /> Remove Mapping
</button>
</div>
<input asp-for="DomainToAppMapping[index].Domain" class="form-control"/>
<span asp-validation-for="DomainToAppMapping[index].Domain" class="text-danger"></span>
<div class="d-flex my-3">
<input asp-for="AllowHotWalletForAll" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="AllowHotWalletForAll" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/CreateWallet/#requirements-to-create-wallets" target="_blank" rel="noreferrer noopener">
<vc:icon symbol="info" />
</a>
<span asp-validation-for="AllowHotWalletForAll" class="text-danger"></span>
<div class="info-note mt-2 text-warning" role="alert">
<vc:icon symbol="warning"/>
Caution: Enabling this option, may simplify the onboarding and spending for third-parties but carries liabilities and security risks associated to storing private keys of third parties on a server.
</div>
<div class="form-group">
<label asp-for="DomainToAppMapping[index].AppId" class="form-label"></label>
<select asp-for="DomainToAppMapping[index].AppId"
asp-items="@(new SelectList(ViewBag.AppsList, nameof(SelectListItem.Value), nameof(SelectListItem.Text), Model.DomainToAppMapping[index].AppId))"
class="form-select">
</select>
<span asp-validation-for="DomainToAppMapping[index].AppId" class="text-danger"></span>
</div>
</div>
<div class="d-flex my-3">
<input asp-for="AllowHotWalletRPCImportForAll" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="AllowHotWalletRPCImportForAll" class="form-check-label"></label>
<span asp-validation-for="AllowHotWalletRPCImportForAll" class="text-danger"></span>
<div class="info-note mt-2 text-warning" role="alert">
<vc:icon symbol="warning"/>
Caution: Enabling this option, may simplify the onboarding and spending for third-parties but carries liabilities and security risks associated to storing private keys of third parties on a server.
</div>
</div>
</div>
</div>
<div class="mb-5">
<h4 class="mb-3">Server</h4>
<div class="d-flex my-3">
<input asp-for="AllowSearchEngines" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="AllowSearchEngines" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/FAQ/ServerSettings/#how-to-hide-my-btcpay-server-from-search-engines" target="_blank" rel="noreferrer noopener">
<vc:icon symbol="info" />
</a>
<span asp-validation-for="AllowSearchEngines" class="text-danger"></span>
</div>
</div>
@if (ViewBag.UpdateUrlPresent)
{
<div class="d-flex my-3">
<input asp-for="CheckForNewVersions" type="checkbox" class="btcpay-toggle me-3"/>
<div>
<label asp-for="CheckForNewVersions" class="form-check-label"></label>
<span asp-validation-for="CheckForNewVersions" class="text-danger"></span>
</div>
</div>
}
@*
Let's uncomment this when we have new experimental features.
<div class="d-flex align-items-center my-3">
<input asp-for="Experimental" type="checkbox" class="btcpay-toggle me-3" />
<div>
<label asp-for="Experimental" class="form-check-label"></label>
<div class="text-muted">
Will allow you to use the custodian account feature.
<a href="https://blog.btcpayserver.org/btcpay-server-1-9-0/#-exchange-integration-via-plugins-" target="_blank" rel="noreferrer noopener">
<vc:icon symbol="info" />
</a>
</div>
</div>
</div> *@
</div>
}
<h4>Block Explorers</h4>
<h4 class="mb-3">Plugins</h4>
<div class="form-group">
<label asp-for="PluginSource" class="form-label"></label>
<input asp-for="PluginSource" placeholder="@PoliciesSettings.DefaultPluginSource" class="form-control"/>
<span asp-validation-for="PluginSource" class="text-danger"></span>
</div>
<div class="d-flex mt-n2">
<input asp-for="PluginPreReleases" type="checkbox" class="btcpay-toggle me-3"/>
<label asp-for="PluginPreReleases" class="form-check-label"></label>
</div>
@for (var lpi = 0; lpi < linkProviders.Length; lpi++)
{
var cryptoCode = linkProviders[lpi].Key;
var defaultLink = linkProviders[lpi].Value.BlockExplorerLinkDefault;
var existingOverride = Model.BlockExplorerLinks?.SingleOrDefault(tuple => tuple.CryptoCode == cryptoCode);
if (existingOverride is null)
<h4 class="mt-5">Customization</h4>
<div class="form-group mb-3">
<label asp-for="DefaultCurrency" class="form-label"></label>
<input asp-for="DefaultCurrency" placeholder="@StoreBlob.StandardDefaultCurrency" class="form-control" currency-selection />
</div>
<div class="form-group mb-5">
<label asp-for="RootAppId" class="form-label"></label>
<select asp-for="RootAppId" asp-items="@(new SelectList(ViewBag.AppsList, nameof(SelectListItem.Value), nameof(SelectListItem.Text), Model.RootAppId))" class="form-select"></select>
@if (!Model.DomainToAppMapping.Any())
{
<button id="AddDomainButton" type="submit" name="command" value="add-domain" class="btn btn-link px-0">Map specific domains to specific apps</button>
}
</div>
@if (Model.DomainToAppMapping.Any())
{
existingOverride = new PoliciesSettings.BlockExplorerOverrideItem { CryptoCode = cryptoCode, Link = null };
Model.BlockExplorerLinks ??= new ();
Model.BlockExplorerLinks.Add(existingOverride);
}
var i = Model.BlockExplorerLinks.IndexOf(existingOverride);
var linkValue = existingOverride.Link ?? defaultLink;
<div class="form-group" data-default="@defaultLink">
<div class="d-flex flex-wrap align-items-center justify-content-between gap-3">
<label asp-for="BlockExplorerLinks[i].Link" class="form-label">@cryptoCode</label>
<button type="button" class="btn btn-link p-0 only-for-js revert-default" title="Revert to default">
Set to default
<h5 class="d-flex align-items-center justify-content-between mt-5 gap-3">
Domain to app mapping
<button id="AddDomainButton" type="submit" name="command" value="add-domain" class="d-inline-block btn text-primary btn-link p-0">
Add domain mapping
</button>
</h5>
<div class="list-group list-group-flush mb-2">
@for (var index = 0; index < Model.DomainToAppMapping.Count; index++)
{
<div class="list-group-item px-0 pt-3">
<div class="form-group">
<div class="d-flex align-items-center justify-content-between">
<label asp-for="DomainToAppMapping[index].Domain" class="form-label"></label>
<button type="submit" title="Remove domain mapping" name="command" value="@($"remove-domain:{index}")" class="d-inline-block ms-2 btn text-danger btn-link p-0 mb-2">
<span class="fa fa-times"></span> Remove Mapping
</button>
</div>
<input asp-for="DomainToAppMapping[index].Domain" class="form-control"/>
<span asp-validation-for="DomainToAppMapping[index].Domain" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DomainToAppMapping[index].AppId" class="form-label"></label>
<select asp-for="DomainToAppMapping[index].AppId"
asp-items="@(new SelectList(ViewBag.AppsList, nameof(SelectListItem.Value), nameof(SelectListItem.Text), Model.DomainToAppMapping[index].AppId))"
class="form-select">
</select>
<span asp-validation-for="DomainToAppMapping[index].AppId" class="text-danger"></span>
</div>
</div>
}
</div>
<input type="hidden" asp-for="BlockExplorerLinks[i].CryptoCode" value="@cryptoCode" />
<input type="text" class="form-control block-explorer-link" asp-for="BlockExplorerLinks[i].Link" value="@linkValue" rel="noreferrer noopener" />
</div>
}
</form>
}
<h4>Block Explorers</h4>
@for (var lpi = 0; lpi < linkProviders.Length; lpi++)
{
var cryptoCode = linkProviders[lpi].Key;
var defaultLink = linkProviders[lpi].Value.BlockExplorerLinkDefault;
var existingOverride = Model.BlockExplorerLinks?.SingleOrDefault(tuple => tuple.CryptoCode == cryptoCode);
if (existingOverride is null)
{
existingOverride = new PoliciesSettings.BlockExplorerOverrideItem { CryptoCode = cryptoCode, Link = null };
Model.BlockExplorerLinks ??= new ();
Model.BlockExplorerLinks.Add(existingOverride);
}
var i = Model.BlockExplorerLinks.IndexOf(existingOverride);
var linkValue = existingOverride.Link ?? defaultLink;
<div class="form-group" data-default="@defaultLink">
<div class="d-flex flex-wrap align-items-center justify-content-between gap-3">
<label asp-for="BlockExplorerLinks[i].Link" class="form-label">@cryptoCode</label>
<button type="button" class="btn btn-link p-0 only-for-js revert-default" title="Revert to default">
Set to default
</button>
</div>
<input type="hidden" asp-for="BlockExplorerLinks[i].CryptoCode" value="@cryptoCode" />
<input type="text" class="form-control block-explorer-link" asp-for="BlockExplorerLinks[i].Link" value="@linkValue" rel="noreferrer noopener" />
</div>
}
</div>
</div>
</div>
</div>
</form>
@section PageFootContent {
<script>

View File

@ -3,7 +3,18 @@
ViewData.SetActivePage(ServerNavPages.Services, Model.WalletName);
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Services">Services</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
@if (Model.ShowQR)
{

View File

@ -4,15 +4,21 @@
ViewData.SetActivePage(ServerNavPages.Services, "SSH settings");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Services">Services</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
</div>
<partial name="_StatusMessage" />
<p>SSH services are used by the maintenance operations.</p>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<p>
<span>SSH services are used by the maintenance operations<br /></span>
</p>
</div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
@ -44,15 +50,9 @@
@if (Model.SSHKeyFileContent != null)
{
<h4>Authorized keys</h4>
<p>You can enter here SSH public keys authorized to connect to your server.</p>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<p>
<span>You can enter here SSH public keys authorized to connect to your server.<br /></span>
</p>
</div>
<form method="post">
<div class="form-group">
<textarea asp-for="SSHKeyFileContent" rows="20" cols="80" class="form-control"></textarea>
@ -65,18 +65,12 @@
}
<h4 class="mt-5 mb-3">Other actions</h4>
<p>Increase the security of your instance by disabling the ability to change the SSH settings in this BTCPay Server instance's user interface.</p>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<p>
<span>Increase the security of your instance by disabling the ability to change the SSH settings in this BTCPay Server instance's user interface.<br /></span>
</p>
</div>
<div>
<form method="post">
<button name="command" id="disable" type="submit" class="btn btn-outline-danger mb-5" value="disable" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-confirm-input="DISABLE">Disable</button>
</form>
</div>
<form method="post">
<button name="command" id="disable" type="submit" class="btn btn-outline-danger mb-5" value="disable" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-confirm-input="DISABLE">Disable</button>
</form>
</div>
</div>

View File

@ -2,7 +2,7 @@ namespace BTCPayServer.Views.Server
{
public enum ServerNavPages
{
Index, Users, Emails, Policies, Branding, Services, Maintenance, Logs, Files, Plugins,
Users, Emails, Policies, Branding, Services, Maintenance, Logs, Files, Plugins,
Roles,
Stores
}

View File

@ -3,7 +3,10 @@
ViewData.SetActivePage(ServerNavPages.Services, "Services");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<div class="sticky-header">
<h2 class="my-1">@ViewData["Title"]</h2>
</div>
<partial name="_StatusMessage" />
<div class="mb-5">
<h4 class="mb-3">Crypto services exposed by your server</h4>
@ -64,7 +67,7 @@
<tr>
<td>@s.Name</td>
<td style="text-align: right">
<a href="@s.Link" target="_blank" rel="noreferrer noopener">See information</a>
<a href="@s.Link" rel="noreferrer noopener">See information</a>
</td>
</tr>
}

View File

@ -1,19 +1,31 @@
@model BTCPayServer.Storage.ViewModels.ChooseStorageViewModel
@{
ViewData.SetActivePage(ServerNavPages.Files, "Storage");
ViewData.SetActivePage(ServerNavPages.Files, "Storage Provider");
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<form method="post">
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="Files">Files</a>
</li>
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
<button type="submit" class="btn btn-primary" name="command" value="Save">Next</button>
</div>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-xl-10 col-xxl-constrain">
@if (Model.ShowChangeWarning)
{
<div class="alert alert-danger mb-4">
If you change your configured storage provider, your current files will become inaccessible.
</div>
}
<form method="post">
<div class="row">
<div class="col-xl-10 col-xxl-constrain">
@if (Model.ShowChangeWarning)
{
<div class="alert alert-danger mb-4">
If you change your configured storage provider, your current files will become inaccessible.
</div>
}
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All"></div>
@ -22,10 +34,9 @@
<label asp-for="Provider" class="form-label"></label>
<select asp-for="Provider" asp-items="@Model.ProvidersList" class="form-select w-auto"></select>
</div>
<button type="submit" class="btn btn-primary mt-2" name="command" value="Save">Next</button>
</form>
</div>
</div>
</div>
</form>
@section PageFootContent {
<partial name="_ValidationScriptsPartial" />

View File

@ -3,16 +3,27 @@
ViewData.SetActivePage(ServerNavPages.Users, Model.Email);
}
<h3 class="mb-4">@ViewData["Title"]</h3>
<form method="post">
<div class="form-check my-3">
<div class="sticky-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a asp-action="ListUsers">Users</a>
</li>
<li class="breadcrumb-item active" aria-current="page">User</li>
</ol>
<h2>@ViewData["Title"]</h2>
</nav>
<button name="command" type="submit" class="btn btn-primary" value="Save" id="SaveUser">Save</button>
</div>
<partial name="_StatusMessage" />
<div class="form-check">
<input asp-for="IsAdmin" type="checkbox" class="form-check-input" />
<label asp-for="IsAdmin" class="form-check-label">User is admin</label>
</div>
@if (Model.Approved.HasValue)
{
<div class="form-check my-3">
<div class="form-check">
<input id="Approved" name="Approved" type="checkbox" value="true" class="form-check-input" @(Model.Approved.Value ? "checked" : "") />
<label for="Approved" class="form-check-label">User is approved</label>
</div>
@ -20,11 +31,10 @@
}
@if (Model.EmailConfirmed.HasValue)
{
<div class="form-check my-3">
<div class="form-check">
<input id="EmailConfirmed" name="EmailConfirmed" value="true" type="checkbox" class="form-check-input" @(Model.EmailConfirmed.Value ? "checked" : "") />
<label for="EmailConfirmed" class="form-check-label">Email address is confirmed</label>
</div>
<input name="EmailConfirmed" type="hidden" value="false">
}
<button name="command" type="submit" class="btn btn-primary mt-3" value="Save" id="SaveUser">Save</button>
</form>

Some files were not shown because too many files have changed in this diff Show More