mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Store centric UI: Part 3 (#3224)
* Set store context in cookie
* Fix page id usages in view
* Move Pay Button to nav
* Move integrations to plugins nav
* Store switch links to wallet if present
* Test fixes
* Nav fixes
* Fix altcoin view
* Main nav updates
* Wallet setttings nav update
* Move storeId cookie fallback to cookie auth handler
* View fixes
* Test fixes
* Fix profile check
* Rename integrations nav extension point to store-integrations-nav-list
* Allow strings for Active page/category for plugins
* Make invoice list filter based on store context
* Do not set context if we are running authorizer through tag helper
* Fix test and unfiltered invoices
* Add permission helper for wallet links
* Add sanity checks for payment requests and invoices
* Store context in home controller
* Fix PayjoinViaUI test
* Store context for notifications
* Minor UI improvements
* Store context for userstores and vault controller
* Bring back integrations page
* Rename notifications nav pages file
* Fix user stores controller policies
* Controller policy fixes from code review
* CookieAuthHandler: Simplify CanViewInvoices case
* Revert "Controller policy fixes from code review"
This reverts commit 97e8b8379c
.
* Simplify LayoutSimple
* Fix CanViewInvoices condition
Co-authored-by: Kukks <evilkukka@gmail.com>
This commit is contained in:
parent
db1a124ffb
commit
e2d0b7c5f7
97 changed files with 625 additions and 512 deletions
|
@ -13,13 +13,18 @@ namespace BTCPayServer.Abstractions.Extensions
|
|||
|
||||
public static void SetActivePage<T>(this ViewDataDictionary viewData, T activePage, string title = null, string activeId = null)
|
||||
where T : IConvertible
|
||||
{
|
||||
SetActivePage(viewData, activePage.ToString(), activePage.GetType().Name,title, activeId );
|
||||
}
|
||||
|
||||
public static void SetActivePage(this ViewDataDictionary viewData, string activePage, string category, string title = null, string activeId = null)
|
||||
{
|
||||
// Page Title
|
||||
viewData["Title"] = title ?? activePage.ToString();
|
||||
viewData["Title"] = title ?? activePage;
|
||||
// Navigation
|
||||
viewData[ACTIVE_PAGE_KEY] = activePage;
|
||||
viewData[ACTIVE_ID_KEY] = activeId;
|
||||
SetActiveCategory(viewData, activePage.GetType());
|
||||
SetActiveCategory(viewData, category);
|
||||
}
|
||||
|
||||
public static void SetActiveCategory<T>(this ViewDataDictionary viewData, T activeCategory)
|
||||
|
@ -27,30 +32,43 @@ namespace BTCPayServer.Abstractions.Extensions
|
|||
viewData[ACTIVE_CATEGORY_KEY] = activeCategory;
|
||||
}
|
||||
|
||||
public static void SetActiveCategory(this ViewDataDictionary viewData, string activeCategory)
|
||||
{
|
||||
viewData[ACTIVE_CATEGORY_KEY] = activeCategory;
|
||||
}
|
||||
|
||||
public static string IsActiveCategory<T>(this ViewDataDictionary viewData, T 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;
|
||||
}
|
||||
var activeId = viewData[ACTIVE_ID_KEY];
|
||||
var activeCategory = (T)viewData[ACTIVE_CATEGORY_KEY];
|
||||
var categoryMatch = category.Equals(activeCategory);
|
||||
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 ? "active" : null;
|
||||
}
|
||||
|
||||
public static string IsActivePage<T>(this ViewDataDictionary viewData, T page, object id = null)
|
||||
where T : IConvertible
|
||||
{
|
||||
return IsActivePage(viewData, page.ToString(), page.GetType().Name, id );
|
||||
}
|
||||
public static string IsActivePage(this ViewDataDictionary viewData, string page,string category, object id = null)
|
||||
{
|
||||
if (!viewData.ContainsKey(ACTIVE_PAGE_KEY))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var activeId = viewData[ACTIVE_ID_KEY];
|
||||
var activePage = (T)viewData[ACTIVE_PAGE_KEY];
|
||||
var activeCategory = viewData[ACTIVE_CATEGORY_KEY];
|
||||
var categoryAndPageMatch = activeCategory.Equals(activePage.GetType()) && page.Equals(activePage);
|
||||
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 idMatch = id == null || activeId == null || id.Equals(activeId);
|
||||
return categoryAndPageMatch && idMatch ? "active" : null;
|
||||
}
|
||||
|
|
|
@ -404,8 +404,6 @@ namespace BTCPayServer.Tests
|
|||
Assert.Contains("2.20000000 ₿", s.Driver.PageSource);
|
||||
if (rateSelection == "RateThenText")
|
||||
Assert.Contains("1.10000000 ₿", s.Driver.PageSource);
|
||||
s.GoToHome();
|
||||
s.GoToInvoices();
|
||||
s.GoToInvoice(invoice.Id);
|
||||
s.Driver.FindElement(By.Id("refundlink")).Click();
|
||||
Assert.Contains("pull-payments", s.Driver.Url);
|
||||
|
@ -423,19 +421,19 @@ namespace BTCPayServer.Tests
|
|||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
var store = s.CreateNewStore();
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
s.AddDerivationScheme("BTC");
|
||||
|
||||
//check that there is no dropdown since only one payment method is set
|
||||
var invoiceId = s.CreateInvoice(store.storeName, 10, "USD", "a@g.com");
|
||||
var invoiceId = s.CreateInvoice(storeId, 10, "USD", "a@g.com");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
s.Driver.FindElement(By.ClassName("payment__currencies_noborder"));
|
||||
s.GoToHome();
|
||||
s.GoToStore(store.storeId);
|
||||
s.GoToStore(storeId);
|
||||
s.AddDerivationScheme("LTC");
|
||||
s.AddLightningNode("BTC", LightningConnectionType.CLightning);
|
||||
//there should be three now
|
||||
invoiceId = s.CreateInvoice(store.storeName, 10, "USD", "a@g.com");
|
||||
invoiceId = s.CreateInvoice(storeId, 10, "USD", "a@g.com");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
var currencyDropdownButton = s.Driver.FindElement(By.ClassName("payment__currencies"));
|
||||
Assert.Contains("BTC", currencyDropdownButton.Text);
|
||||
|
|
|
@ -28,17 +28,17 @@ namespace BTCPayServer.Tests
|
|||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
var store = s.CreateNewStore();
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
s.AddDerivationScheme("BTC");
|
||||
s.GoToStore(store.storeId, StoreNavPages.CheckoutAppearance);
|
||||
s.GoToStore(storeId, StoreNavPages.CheckoutAppearance);
|
||||
s.Driver.FindElement(By.Id("RequiresRefundEmail")).Click();
|
||||
s.Driver.FindElement(By.Name("command")).Click();
|
||||
|
||||
var emailAlreadyThereInvoiceId = s.CreateInvoice(store.storeName, 100, "USD", "a@g.com");
|
||||
var emailAlreadyThereInvoiceId = s.CreateInvoice(storeId, 100, "USD", "a@g.com");
|
||||
s.GoToInvoiceCheckout(emailAlreadyThereInvoiceId);
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.GoToHome();
|
||||
var invoiceId = s.CreateInvoice(store.storeName);
|
||||
s.CreateInvoice(storeId);
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.Navigate().Back();
|
||||
|
@ -79,11 +79,11 @@ namespace BTCPayServer.Tests
|
|||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
var store = s.CreateNewStore();
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
s.AddDerivationScheme("BTC");
|
||||
|
||||
// Now create an invoice that requires a refund email
|
||||
var invoice = s.CreateInvoice(store.storeName, 100, "USD", "", null, true);
|
||||
var invoice = s.CreateInvoice(storeId, 100, "USD", "", null, true);
|
||||
s.GoToInvoiceCheckout(invoice);
|
||||
|
||||
var emailInput = s.Driver.FindElement(By.Id("emailAddressFormInput"));
|
||||
|
@ -106,7 +106,7 @@ namespace BTCPayServer.Tests
|
|||
s.GoToHome();
|
||||
|
||||
// Now create an invoice that doesn't require a refund email
|
||||
s.CreateInvoice(store.storeName, 100, "USD", "", null, false);
|
||||
s.CreateInvoice(storeId, 100, "USD", "", null, false);
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.Navigate().Back();
|
||||
|
@ -119,7 +119,7 @@ namespace BTCPayServer.Tests
|
|||
s.GoToHome();
|
||||
|
||||
// Now create an invoice that requires refund email but already has one set, email input shouldn't show up
|
||||
s.CreateInvoice(store.storeName, 100, "USD", "a@g.com", null, true);
|
||||
s.CreateInvoice(storeId, 100, "USD", "a@g.com", null, true);
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.Navigate().Back();
|
||||
|
@ -139,10 +139,10 @@ namespace BTCPayServer.Tests
|
|||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
var store = s.CreateNewStore();
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
s.AddDerivationScheme("BTC");
|
||||
|
||||
var invoiceId = s.CreateInvoice(store.storeName);
|
||||
var invoiceId = s.CreateInvoice(storeId);
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
Assert.True(s.Driver.FindElement(By.Id("DefaultLang")).FindElements(By.TagName("option")).Count > 1);
|
||||
var payWithTextEnglish = s.Driver.FindElement(By.Id("pay-with-text")).Text;
|
||||
|
@ -171,11 +171,11 @@ namespace BTCPayServer.Tests
|
|||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser(true);
|
||||
var store = s.CreateNewStore();
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
s.AddLightningNode();
|
||||
s.AddDerivationScheme("BTC");
|
||||
|
||||
var invoiceId = s.CreateInvoice(store.storeName, defaultPaymentMethod: "BTC_LightningLike");
|
||||
var invoiceId = s.CreateInvoice(storeId, defaultPaymentMethod: "BTC_LightningLike");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
|
||||
Assert.Equal("Bitcoin (Lightning) (BTC)", s.Driver.FindElement(By.ClassName("payment__currencies")).Text);
|
||||
|
@ -193,7 +193,7 @@ namespace BTCPayServer.Tests
|
|||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser(true);
|
||||
(string storeName, string storeId) = s.CreateNewStore();
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
s.AddLightningNode();
|
||||
s.GoToStore(storeId);
|
||||
s.Driver.FindElement(By.Id("Modify-LightningBTC")).Click();
|
||||
|
@ -201,7 +201,7 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.FindElement(By.Id("save")).Click();
|
||||
Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||
|
||||
var invoiceId = s.CreateInvoice(storeName, 10, "USD", "a@g.com");
|
||||
var invoiceId = s.CreateInvoice(storeId, 10, "USD", "a@g.com");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
Assert.Contains("Sats", s.Driver.FindElement(By.ClassName("payment__currencies_noborder")).Text);
|
||||
}
|
||||
|
@ -215,10 +215,10 @@ namespace BTCPayServer.Tests
|
|||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
var store = s.CreateNewStore();
|
||||
s.GoToStore(store.storeId);
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
s.GoToStore(storeId);
|
||||
s.AddDerivationScheme();
|
||||
var invoiceId = s.CreateInvoice(store.storeId, 0.001m, "BTC", "a@x.com");
|
||||
var invoiceId = s.CreateInvoice(storeId, 0.001m, "BTC", "a@x.com");
|
||||
var invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId);
|
||||
s.Driver.Navigate()
|
||||
.GoToUrl(new Uri(s.ServerUri, $"tests/index.html?invoice={invoiceId}"));
|
||||
|
|
|
@ -246,7 +246,7 @@ namespace BTCPayServer.Tests
|
|||
await s.FundStoreWallet(senderWalletId);
|
||||
await s.FundStoreWallet(receiverWalletId);
|
||||
|
||||
var invoiceId = s.CreateInvoice(receiver.storeName, null, "BTC");
|
||||
var invoiceId = s.CreateInvoice(receiver.storeId, null, "BTC");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
var bip21 = s.Driver.FindElement(By.ClassName("payment__details__instruction__open-wallet__btn"))
|
||||
.GetAttribute("href");
|
||||
|
@ -295,7 +295,7 @@ namespace BTCPayServer.Tests
|
|||
var receiverWalletId = new WalletId(receiver.storeId, cryptoCode);
|
||||
|
||||
//payjoin is enabled by default.
|
||||
var invoiceId = s.CreateInvoice(receiver.storeName);
|
||||
var invoiceId = s.CreateInvoice(receiver.storeId);
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
var bip21 = s.Driver.FindElement(By.ClassName("payment__details__instruction__open-wallet__btn"))
|
||||
.GetAttribute("href");
|
||||
|
@ -310,7 +310,7 @@ namespace BTCPayServer.Tests
|
|||
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||
await s.FundStoreWallet(senderWalletId);
|
||||
|
||||
invoiceId = s.CreateInvoice(receiver.storeName);
|
||||
invoiceId = s.CreateInvoice(receiver.storeId);
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
bip21 = s.Driver.FindElement(By.ClassName("payment__details__instruction__open-wallet__btn"))
|
||||
.GetAttribute("href");
|
||||
|
@ -343,7 +343,7 @@ namespace BTCPayServer.Tests
|
|||
StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
//let's do it all again, except now the receiver has funds and is able to payjoin
|
||||
invoiceId = s.CreateInvoice(receiver.storeName);
|
||||
invoiceId = s.CreateInvoice(receiver.storeId);
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
bip21 = s.Driver.FindElement(By.ClassName("payment__details__instruction__open-wallet__btn"))
|
||||
.GetAttribute("href");
|
||||
|
@ -393,7 +393,7 @@ namespace BTCPayServer.Tests
|
|||
var dto = invoice.EntityToDTO();
|
||||
Assert.Equal(InvoiceStatusLegacy.Paid, invoice.Status);
|
||||
});
|
||||
s.GoToInvoices();
|
||||
s.GoToInvoices(receiver.storeId);
|
||||
paymentValueRowColumn = s.Driver.FindElement(By.Id($"invoice_details_{invoiceId}"))
|
||||
.FindElement(By.ClassName("payment-value"));
|
||||
Assert.False(paymentValueRowColumn.Text.Contains("payjoin",
|
||||
|
|
|
@ -332,17 +332,10 @@ namespace BTCPayServer.Tests
|
|||
Driver.FindElement(By.Id("Password")).SendKeys(password);
|
||||
Driver.FindElement(By.Id("LoginButton")).Click();
|
||||
}
|
||||
|
||||
public void GoToApps()
|
||||
{
|
||||
Driver.FindElement(By.Id("StoreNav-Apps")).Click();
|
||||
}
|
||||
|
||||
public void GoToStore(string storeId, StoreNavPages storeNavPage = StoreNavPages.PaymentMethods)
|
||||
{
|
||||
GoToHome();
|
||||
Driver.WaitForAndClick(By.Id("StoreSelectorToggle"));
|
||||
Driver.WaitForAndClick(By.Id($"StoreSelectorMenuItem-{storeId}"));
|
||||
GoToUrl($"/stores/{storeId}/");
|
||||
|
||||
if (storeNavPage != StoreNavPages.PaymentMethods)
|
||||
{
|
||||
|
@ -360,8 +353,15 @@ namespace BTCPayServer.Tests
|
|||
|
||||
public void GoToWalletSettings(string storeId, string cryptoCode = "BTC")
|
||||
{
|
||||
GoToStore(storeId);
|
||||
Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click();
|
||||
try
|
||||
{
|
||||
GoToStore(storeId);
|
||||
Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click();
|
||||
}
|
||||
catch (NoSuchElementException)
|
||||
{
|
||||
GoToWallet(new WalletId(storeId, cryptoCode), WalletsNavPages.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
public void GoToLightningSettings(string storeId, string cryptoCode = "BTC")
|
||||
|
@ -377,10 +377,14 @@ namespace BTCPayServer.Tests
|
|||
CheckForJSErrors();
|
||||
}
|
||||
|
||||
public void GoToInvoices()
|
||||
public void GoToInvoice(string id)
|
||||
{
|
||||
GoToHome();
|
||||
Driver.FindElement(By.Id("Nav-Invoices")).Click();
|
||||
GoToUrl($"/invoices/{id}/");
|
||||
}
|
||||
|
||||
public void GoToInvoices(string storeId = null)
|
||||
{
|
||||
GoToUrl(storeId == null ? "/invoices/" : $"/stores/{storeId}/invoices/");
|
||||
}
|
||||
|
||||
public void GoToProfile(ManageNavPages navPages = ManageNavPages.Index)
|
||||
|
@ -394,11 +398,11 @@ namespace BTCPayServer.Tests
|
|||
|
||||
public void GoToLogin()
|
||||
{
|
||||
Driver.Navigate().GoToUrl(new Uri(ServerUri, "/login"));
|
||||
GoToUrl("/login");
|
||||
}
|
||||
|
||||
public string CreateInvoice(
|
||||
string storeName,
|
||||
string storeId,
|
||||
decimal? amount = 100,
|
||||
string currency = "USD",
|
||||
string refundEmail = "",
|
||||
|
@ -407,7 +411,7 @@ namespace BTCPayServer.Tests
|
|||
StatusMessageModel.StatusSeverity expectedSeverity = StatusMessageModel.StatusSeverity.Success
|
||||
)
|
||||
{
|
||||
GoToInvoices();
|
||||
GoToInvoices(storeId);
|
||||
Driver.FindElement(By.Id("CreateNewInvoice")).Click();
|
||||
if (amount is decimal v)
|
||||
Driver.FindElement(By.Id("Amount")).SendKeys(v.ToString(CultureInfo.InvariantCulture));
|
||||
|
@ -415,7 +419,6 @@ namespace BTCPayServer.Tests
|
|||
currencyEl.Clear();
|
||||
currencyEl.SendKeys(currency);
|
||||
Driver.FindElement(By.Id("BuyerEmail")).SendKeys(refundEmail);
|
||||
Driver.FindElement(By.Name("StoreId")).SendKeys(storeName);
|
||||
if (defaultPaymentMethod is string)
|
||||
new SelectElement(Driver.FindElement(By.Name("DefaultPaymentMethod"))).SelectByValue(defaultPaymentMethod);
|
||||
if (requiresRefundEmail is bool)
|
||||
|
@ -501,18 +504,5 @@ namespace BTCPayServer.Tests
|
|||
Driver.FindElement(By.Id($"SectionNav-{navPages}")).Click();
|
||||
}
|
||||
}
|
||||
|
||||
public void GoToInvoice(string id)
|
||||
{
|
||||
GoToInvoices();
|
||||
foreach (var el in Driver.FindElements(By.ClassName("invoice-details-link")))
|
||||
{
|
||||
if (el.GetAttribute("href").Contains(id, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
el.Click();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -360,7 +360,7 @@ namespace BTCPayServer.Tests
|
|||
s.Server.ActivateLightning();
|
||||
await s.StartAsync();
|
||||
var alice = s.RegisterNewUser(true);
|
||||
var (storeName, storeId) = s.CreateNewStore();
|
||||
(string storeName, string storeId) = s.CreateNewStore();
|
||||
var onchainHint = "Set up your wallet to receive payments at your store.";
|
||||
var offchainHint = "A connection to a Lightning node is required to receive Lightning payments.";
|
||||
|
||||
|
@ -392,8 +392,8 @@ namespace BTCPayServer.Tests
|
|||
|
||||
var storeUrl = s.Driver.Url;
|
||||
s.ClickOnAllSectionLinks();
|
||||
s.GoToInvoices();
|
||||
var invoiceId = s.CreateInvoice(storeName);
|
||||
s.GoToInvoices(storeId);
|
||||
var invoiceId = s.CreateInvoice(storeId);
|
||||
s.FindAlertMessage();
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
var invoiceUrl = s.Driver.Url;
|
||||
|
@ -402,16 +402,17 @@ namespace BTCPayServer.Tests
|
|||
Assert.DoesNotContain("Archived", s.Driver.FindElement(By.Id("btn-archive-toggle")).Text);
|
||||
s.Driver.FindElement(By.Id("btn-archive-toggle")).Click();
|
||||
Assert.Contains("Unarchive", s.Driver.FindElement(By.Id("btn-archive-toggle")).Text);
|
||||
|
||||
//check that it no longer appears in list
|
||||
s.GoToInvoices();
|
||||
|
||||
s.GoToInvoices(storeId);
|
||||
Assert.DoesNotContain(invoiceId, s.Driver.PageSource);
|
||||
|
||||
//ok, let's unarchive and see that it shows again
|
||||
s.Driver.Navigate().GoToUrl(invoiceUrl);
|
||||
s.Driver.FindElement(By.Id("btn-archive-toggle")).Click();
|
||||
s.FindAlertMessage();
|
||||
Assert.DoesNotContain("Unarchive", s.Driver.FindElement(By.Id("btn-archive-toggle")).Text);
|
||||
s.GoToInvoices();
|
||||
s.GoToInvoices(storeId);
|
||||
Assert.Contains(invoiceId, s.Driver.PageSource);
|
||||
|
||||
// When logout out we should not be able to access store and invoice details
|
||||
|
@ -614,7 +615,7 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser(true);
|
||||
var (_, storeId) = s.CreateNewStore();
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
s.GenerateWallet("BTC", "", false, true);
|
||||
var walletId = new WalletId(storeId, "BTC");
|
||||
s.GoToWallet(walletId, WalletsNavPages.Receive);
|
||||
|
@ -676,7 +677,7 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser(true);
|
||||
var (storeName, storeId) = s.CreateNewStore();
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
s.GoToStore(storeId, StoreNavPages.Webhooks);
|
||||
|
||||
TestLogs.LogInformation("Let's create two webhooks");
|
||||
|
@ -733,7 +734,7 @@ namespace BTCPayServer.Tests
|
|||
TestLogs.LogInformation("Let's see if we can generate an event");
|
||||
s.GoToStore(storeId);
|
||||
s.AddDerivationScheme();
|
||||
s.CreateInvoice(storeName);
|
||||
s.CreateInvoice(storeId);
|
||||
var request = await server.GetNextRequest();
|
||||
var headers = request.Request.Headers;
|
||||
var actualSig = headers["BTCPay-Sig"].First();
|
||||
|
@ -745,14 +746,14 @@ namespace BTCPayServer.Tests
|
|||
server.Done();
|
||||
|
||||
TestLogs.LogInformation("Let's make a failed event");
|
||||
s.CreateInvoice(storeName);
|
||||
s.CreateInvoice(storeId);
|
||||
request = await server.GetNextRequest();
|
||||
request.Response.StatusCode = 404;
|
||||
server.Done();
|
||||
|
||||
// The delivery is done asynchronously, so small wait here
|
||||
await Task.Delay(500);
|
||||
s.GoToStore(storeId, Views.Stores.StoreNavPages.Webhooks);
|
||||
s.GoToStore(storeId, StoreNavPages.Webhooks);
|
||||
s.Driver.FindElement(By.LinkText("Modify")).Click();
|
||||
var elements = s.Driver.FindElements(By.ClassName("redeliver"));
|
||||
// One worked, one failed
|
||||
|
@ -768,7 +769,7 @@ namespace BTCPayServer.Tests
|
|||
TestLogs.LogInformation("Can we browse the json content?");
|
||||
CanBrowseContent(s);
|
||||
|
||||
s.GoToInvoices();
|
||||
s.GoToInvoices(storeId);
|
||||
s.Driver.FindElement(By.LinkText("Details")).Click();
|
||||
CanBrowseContent(s);
|
||||
var element = s.Driver.FindElement(By.ClassName("redeliver"));
|
||||
|
@ -798,7 +799,7 @@ namespace BTCPayServer.Tests
|
|||
foreach (var isHotwallet in new[] { false, true })
|
||||
{
|
||||
var cryptoCode = "BTC";
|
||||
var (_, storeId) = s.CreateNewStore();
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
s.GenerateWallet(cryptoCode, "melody lizard phrase voice unique car opinion merge degree evil swift cargo", privkeys: isHotwallet);
|
||||
s.GoToWalletSettings(storeId, cryptoCode);
|
||||
if (isHotwallet)
|
||||
|
@ -816,7 +817,7 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser(true);
|
||||
(string storeName, string storeId) = s.CreateNewStore();
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
const string cryptoCode = "BTC";
|
||||
|
||||
// In this test, we try to spend from a manual seed. We import the xpub 49'/0'/0',
|
||||
|
@ -865,7 +866,7 @@ namespace BTCPayServer.Tests
|
|||
|
||||
Assert.NotEqual(receiveAddr, s.Driver.FindElement(By.Id("address")).GetAttribute("value"));
|
||||
|
||||
var invoiceId = s.CreateInvoice(storeName);
|
||||
var invoiceId = s.CreateInvoice(storeId);
|
||||
var invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId);
|
||||
var address = invoice.EntityToDTO().Addresses["BTC"];
|
||||
|
||||
|
@ -878,7 +879,7 @@ namespace BTCPayServer.Tests
|
|||
|
||||
//lets import and save private keys
|
||||
var root = mnemonic.DeriveExtKey();
|
||||
invoiceId = s.CreateInvoice(storeName);
|
||||
invoiceId = s.CreateInvoice(storeId);
|
||||
invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId);
|
||||
address = invoice.EntityToDTO().Addresses["BTC"];
|
||||
result = await s.Server.ExplorerNode.GetAddressInfoAsync(
|
||||
|
@ -978,8 +979,8 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser(true);
|
||||
(string _, string storeId) = s.CreateNewStore();
|
||||
var cryptoCode = "BTC";
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
const string cryptoCode = "BTC";
|
||||
var mnemonic = s.GenerateWallet(cryptoCode, "click chunk owner kingdom faint steak safe evidence bicycle repeat bulb wheel");
|
||||
|
||||
// Make sure wallet info is correct
|
||||
|
@ -1294,7 +1295,7 @@ namespace BTCPayServer.Tests
|
|||
new[] { s.Server.MerchantLightningD },
|
||||
new[] { s.Server.MerchantLnd.Client });
|
||||
s.RegisterNewUser(true);
|
||||
(string storeName, string storeId) = s.CreateNewStore();
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
var network = s.Server.NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode).NBitcoinNetwork;
|
||||
s.GoToStore(storeId);
|
||||
s.AddLightningNode(cryptoCode, LightningConnectionType.CLightning, false);
|
||||
|
@ -1307,7 +1308,7 @@ namespace BTCPayServer.Tests
|
|||
SudoForceSaveLightningSettingsRightNowAndFast(s, cryptoCode);
|
||||
|
||||
// Topup Invoice test
|
||||
var i = s.CreateInvoice(storeName, null, cryptoCode);
|
||||
var i = s.CreateInvoice(storeId, null, cryptoCode);
|
||||
s.GoToInvoiceCheckout(i);
|
||||
s.Driver.FindElement(By.Id("copy-tab")).Click();
|
||||
var lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
|
||||
|
@ -1339,8 +1340,8 @@ namespace BTCPayServer.Tests
|
|||
});
|
||||
|
||||
// Standard invoice test
|
||||
s.GoToHome();
|
||||
i = s.CreateInvoice(storeName, 0.0000001m, cryptoCode);
|
||||
s.GoToStore(storeId);
|
||||
i = s.CreateInvoice(storeId, 0.0000001m, cryptoCode);
|
||||
s.GoToInvoiceCheckout(i);
|
||||
s.Driver.FindElement(By.ClassName("payment__currencies")).Click();
|
||||
// BOLT11 is also available for standard invoices
|
||||
|
@ -1382,12 +1383,12 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.FindElement(By.Id("save")).Click();
|
||||
Assert.Contains($"{cryptoCode} Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||
|
||||
i = s.CreateInvoice(storeName, 0.000001m, cryptoCode);
|
||||
i = s.CreateInvoice(storeId, 0.000001m, cryptoCode);
|
||||
s.GoToInvoiceCheckout(i);
|
||||
s.Driver.FindElement(By.ClassName("payment__currencies_noborder"));
|
||||
|
||||
s.GoToHome();
|
||||
i = s.CreateInvoice(storeName, null, cryptoCode);
|
||||
s.GoToStore(storeId);
|
||||
i = s.CreateInvoice(storeId, null, cryptoCode);
|
||||
s.GoToInvoiceCheckout(i);
|
||||
s.Driver.FindElement(By.ClassName("payment__currencies_noborder"));
|
||||
|
||||
|
@ -1409,9 +1410,9 @@ namespace BTCPayServer.Tests
|
|||
//even though we set DisableBolt11PaymentMethod to true, logic when saving it turns it back off as otherwise no lightning option is available at all!
|
||||
Assert.False(s.Driver.FindElement(By.Id("DisableBolt11PaymentMethod")).Selected);
|
||||
// Invoice creation should fail, because it is a standard invoice with amount, but DisableBolt11PaymentMethod = true and LNURLStandardInvoiceEnabled = false
|
||||
s.CreateInvoice(storeName, 0.0000001m, cryptoCode,"",null, expectedSeverity: StatusMessageModel.StatusSeverity.Success);
|
||||
s.CreateInvoice(storeId, 0.0000001m, cryptoCode,"",null, expectedSeverity: StatusMessageModel.StatusSeverity.Success);
|
||||
|
||||
i = s.CreateInvoice(storeName, null, cryptoCode);
|
||||
i = s.CreateInvoice(storeId, null, cryptoCode);
|
||||
s.GoToInvoiceCheckout(i);
|
||||
s.Driver.FindElement(By.ClassName("payment__currencies_noborder"));
|
||||
s.Driver.FindElement(By.Id("copy-tab")).Click();
|
||||
|
@ -1427,7 +1428,7 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.SetCheckbox(By.Id("DisableBolt11PaymentMethod"), true);
|
||||
s.Driver.FindElement(By.Id("save")).Click();
|
||||
Assert.Contains($"{cryptoCode} Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||
var invForPP = s.CreateInvoice(newStore.storeName, 0.0000001m, cryptoCode);
|
||||
var invForPP = s.CreateInvoice(newStore.storeId, 0.0000001m, cryptoCode);
|
||||
s.GoToInvoiceCheckout(invForPP);
|
||||
s.Driver.FindElement(By.Id("copy-tab")).Click();
|
||||
lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
|
||||
|
@ -1486,25 +1487,19 @@ namespace BTCPayServer.Tests
|
|||
s.RegisterNewUser(true);
|
||||
//ln address tests
|
||||
s.CreateNewStore();
|
||||
s.GoToStore(s.StoreId, StoreNavPages.Integrations);
|
||||
//ensure ln address is not available as Lightning is not enable
|
||||
s.Driver.FindElement(By.Id("lightning-address-option"))
|
||||
.FindElement(By.LinkText("You need to setup Lightning first"));
|
||||
s.Driver.AssertElementNotFound(By.Id("StoreNav-LightningAddress"));
|
||||
|
||||
s.GoToStore(s.StoreId);
|
||||
s.AddLightningNode("BTC", LightningConnectionType.LndREST, false);
|
||||
s.GoToStore(s.StoreId, StoreNavPages.Integrations);
|
||||
//ensure ln address is not available as lnurl is not configured
|
||||
s.Driver.FindElement(By.Id("lightning-address-option"))
|
||||
.FindElement(By.LinkText("You need LNURL configured first"));
|
||||
s.Driver.AssertElementNotFound(By.Id("StoreNav-LightningAddress"));
|
||||
|
||||
s.GoToLightningSettings(s.StoreId, cryptoCode);
|
||||
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), true);
|
||||
SudoForceSaveLightningSettingsRightNowAndFast(s, cryptoCode);
|
||||
|
||||
s.GoToStore(s.StoreId, StoreNavPages.Integrations);
|
||||
s.Driver.FindElement(By.Id("lightning-address-option"))
|
||||
.FindElement(By.Id("lightning-address-setup-link")).Click();
|
||||
s.Driver.FindElement(By.Id("StoreNav-LightningAddress")).Click();
|
||||
|
||||
s.Driver.ToggleCollapse("AddAddress");
|
||||
var lnaddress1 = Guid.NewGuid().ToString();
|
||||
|
|
|
@ -339,7 +339,7 @@ namespace BTCPayServer.Tests
|
|||
tester.PayTester.MockRates = false;
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
List<decimal> rates = new List<decimal>();
|
||||
rates.Add(await CreateInvoice(tester, user, "coingecko"));
|
||||
|
|
|
@ -794,11 +794,11 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await tester.StartAsync();
|
||||
var acc = tester.NewAccount();
|
||||
acc.GrantAccess();
|
||||
await acc.GrantAccessAsync();
|
||||
acc.RegisterDerivationScheme("BTC");
|
||||
// First we try payment with a merchant having only BTC
|
||||
var invoice = acc.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
var invoice = await acc.BitPay.CreateInvoiceAsync(
|
||||
new Invoice
|
||||
{
|
||||
Price = 500,
|
||||
Currency = "USD",
|
||||
|
@ -811,21 +811,22 @@ namespace BTCPayServer.Tests
|
|||
var cashCow = tester.ExplorerNode;
|
||||
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network);
|
||||
var firstPayment = invoice.CryptoInfo[0].TotalDue - Money.Satoshis(10);
|
||||
cashCow.SendToAddress(invoiceAddress, firstPayment);
|
||||
await cashCow.SendToAddressAsync(invoiceAddress, firstPayment);
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
invoice = acc.BitPay.GetInvoice(invoice.Id);
|
||||
Assert.Equal(firstPayment, invoice.CryptoInfo[0].Paid);
|
||||
});
|
||||
|
||||
|
||||
AssertSearchInvoice(acc, true, invoice.Id, null);
|
||||
AssertSearchInvoice(acc, true, invoice.Id, null, acc.StoreId);
|
||||
AssertSearchInvoice(acc, true, invoice.Id, $"storeid:{acc.StoreId}");
|
||||
AssertSearchInvoice(acc, false, invoice.Id, $"storeid:blah");
|
||||
AssertSearchInvoice(acc, false, invoice.Id, "storeid:doesnotexist");
|
||||
AssertSearchInvoice(acc, true, invoice.Id, $"{invoice.Id}");
|
||||
AssertSearchInvoice(acc, true, invoice.Id, $"exceptionstatus:paidPartial");
|
||||
AssertSearchInvoice(acc, false, invoice.Id, $"exceptionstatus:paidOver");
|
||||
AssertSearchInvoice(acc, true, invoice.Id, $"unusual:true");
|
||||
AssertSearchInvoice(acc, false, invoice.Id, $"unusual:false");
|
||||
AssertSearchInvoice(acc, true, invoice.Id, "exceptionstatus:paidPartial");
|
||||
AssertSearchInvoice(acc, false, invoice.Id, "exceptionstatus:paidOver");
|
||||
AssertSearchInvoice(acc, true, invoice.Id, "unusual:true");
|
||||
AssertSearchInvoice(acc, false, invoice.Id, "unusual:false");
|
||||
|
||||
var time = invoice.InvoiceTime;
|
||||
AssertSearchInvoice(acc, true, invoice.Id, $"startdate:{time.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
|
@ -929,11 +930,11 @@ namespace BTCPayServer.Tests
|
|||
}
|
||||
}
|
||||
|
||||
private void AssertSearchInvoice(TestAccount acc, bool expected, string invoiceId, string filter)
|
||||
private void AssertSearchInvoice(TestAccount acc, bool expected, string invoiceId, string filter, string storeId = null)
|
||||
{
|
||||
var result =
|
||||
(InvoicesModel)((ViewResult)acc.GetController<InvoiceController>()
|
||||
.ListInvoices(new InvoicesModel { SearchTerm = filter }).Result).Model;
|
||||
.ListInvoices(new InvoicesModel { SearchTerm = filter, StoreId = storeId }).Result).Model;
|
||||
Assert.Equal(expected, result.Invoices.Any(i => i.InvoiceId == invoiceId));
|
||||
}
|
||||
|
||||
|
|
|
@ -23,59 +23,7 @@
|
|||
<div class="accordion px-3 px-lg-4">
|
||||
@if (SignInManager.IsSignedIn(User))
|
||||
{
|
||||
@if (Model.Store == null)
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<header class="accordion-header" id="Nav-Payments-Header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#Nav-Payments" aria-expanded="true" aria-controls="Nav-Payments">
|
||||
Payments
|
||||
<vc:icon symbol="caret-down"/>
|
||||
</button>
|
||||
</header>
|
||||
<div id="Nav-Payments" class="accordion-collapse collapse show" aria-labelledby="Nav-Payments-Header">
|
||||
<div class="accordion-body">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Invoice" asp-action="ListInvoices" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(InvoiceNavPages))" id="Nav-Invoices">
|
||||
<vc:icon symbol="invoice"/>
|
||||
<span>Invoices</span>
|
||||
</a>
|
||||
</li>
|
||||
@* FIXME: The wallets item is in here only for the tests *@
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Wallets" asp-action="ListWallets" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(WalletsNavPages))" id="Nav-Wallets">
|
||||
<vc:icon symbol="wallet-onchain"/>
|
||||
<span>Wallets</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item" permission="@Policies.CanModifyServerSettings">
|
||||
<header class="accordion-header" id="Nav-Plugins-Header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#Nav-Plugins" aria-expanded="true" aria-controls="Nav-Plugins">
|
||||
Plugins
|
||||
<vc:icon symbol="caret-down"/>
|
||||
</button>
|
||||
</header>
|
||||
<div id="Nav-Plugins" class="accordion-collapse collapse show" aria-labelledby="Nav-Plugins-Header">
|
||||
<div class="accordion-body">
|
||||
<ul class="navbar-nav">
|
||||
<vc:ui-extension-point location="header-nav" model="@Model"/>
|
||||
@* TODO: Limit this to admins *@
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Server" asp-action="ListPlugins" class="nav-link js-scroll-trigger @ViewData.IsActivePage(ServerNavPages.Plugins)" id="Nav-AddPlugin">
|
||||
<vc:icon symbol="new"/>
|
||||
<span>Add Plugin</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
@if (Model.Store != null)
|
||||
{
|
||||
<div class="accordion-item" permission="@Policies.CanModifyStoreSettings">
|
||||
<div class="accordion-body">
|
||||
|
@ -84,16 +32,16 @@
|
|||
{
|
||||
var isSetUp = !string.IsNullOrWhiteSpace(scheme.Value);
|
||||
<li class="nav-item">
|
||||
@if (isSetUp)
|
||||
@if (isSetUp && scheme.WalletSupported)
|
||||
{
|
||||
<a asp-area="" asp-controller="Stores" asp-action="WalletSettings" asp-route-cryptoCode="@scheme.Crypto" asp-route-storeId="@Model.Store.Id" class="nav-link" id="@($"StoreNav-Modify{scheme.Crypto}")">
|
||||
<a asp-area="" asp-controller="Wallets" asp-action="WalletTransactions" asp-route-walletId="@scheme.WalletId"class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(WalletsNavPages), scheme.WalletId.ToString()) @ViewData.IsActivePage(StoreNavPages.OnchainSettings)" id="@($"StoreNav-Wallet{scheme.Crypto}")">
|
||||
<span class="me-2 btcpay-status btcpay-status--@(scheme.Enabled ? "enabled" : "disabled")"></span>
|
||||
<span>@(Model.AltcoinsBuild ? $"{scheme.Crypto} " : "")Wallet</span>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a asp-area="" asp-controller="Stores" asp-action="SetupWallet" asp-route-cryptoCode="@scheme.Crypto" asp-route-storeId="@Model.Store.Id" class="nav-link" id="@($"StoreNav-Modify{scheme.Crypto}")">
|
||||
<a asp-area="" asp-controller="Stores" asp-action="SetupWallet" asp-route-cryptoCode="@scheme.Crypto" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.OnchainSettings)" id="@($"StoreNav-Modify{scheme.Crypto}")">
|
||||
<span class="me-2 btcpay-status btcpay-status--@(scheme.Enabled ? "enabled" : "disabled")"></span>
|
||||
<span>@(Model.AltcoinsBuild ? $"{scheme.Crypto} " : "")Wallet</span>
|
||||
</a>
|
||||
|
@ -106,14 +54,14 @@
|
|||
<li class="nav-item">
|
||||
@if (isSetUp)
|
||||
{
|
||||
<a asp-area="" asp-controller="Stores" asp-action="LightningSettings" asp-route-cryptoCode="@scheme.CryptoCode" asp-route-storeId="@Model.Store.Id" class="nav-link" id="@($"StoreNav-Lightning{scheme.CryptoCode}")">
|
||||
<a asp-area="" asp-controller="Stores" asp-action="LightningSettings" asp-route-cryptoCode="@scheme.CryptoCode" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.LightningSettings)" id="@($"StoreNav-Lightning{scheme.CryptoCode}")">
|
||||
<span class="me-2 btcpay-status btcpay-status--@(scheme.Enabled ? "enabled" : "disabled")"></span>
|
||||
<span>@(Model.AltcoinsBuild ? $"{scheme.CryptoCode} " : "")Lightning</span>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a asp-area="" asp-controller="Stores" asp-action="SetupLightningNode" asp-route-cryptoCode="@scheme.CryptoCode" asp-route-storeId="@Model.Store.Id" class="nav-link" id="@($"StoreNav-Lightning{scheme.CryptoCode}")">
|
||||
<a asp-area="" asp-controller="Stores" 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}")">
|
||||
<span class="me-2 btcpay-status btcpay-status--disabled"></span>
|
||||
<span>@(Model.AltcoinsBuild ? $"{scheme.CryptoCode} " : "")Lightning</span>
|
||||
</a>
|
||||
|
@ -134,19 +82,6 @@
|
|||
<div id="Nav-Payments" class="accordion-collapse collapse show" aria-labelledby="Nav-Payments-Header">
|
||||
<div class="accordion-body">
|
||||
<ul class="navbar-nav">
|
||||
@foreach (var scheme in Model.DerivationSchemes.OrderBy(scheme => scheme.Collapsed))
|
||||
{
|
||||
var isSetUp = !string.IsNullOrWhiteSpace(scheme.Value);
|
||||
if (isSetUp && scheme.WalletSupported)
|
||||
{
|
||||
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
|
||||
<a asp-area="" asp-controller="Wallets" asp-action="WalletTransactions" asp-route-walletId="@scheme.WalletId" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(WalletsNavPages), scheme.Crypto)" id="@($"StoreNav-Wallet{scheme.Crypto}")">
|
||||
<vc:icon symbol="wallet-onchain"/>
|
||||
<span>@(Model.AltcoinsBuild ? $"{scheme.Crypto} " : "")Wallet</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
|
||||
<a asp-area="" asp-controller="Invoice" asp-action="ListInvoices" asp-route-storeId="@Model.Store.Id" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(InvoiceNavPages))" id="StoreNav-Invoices">
|
||||
<vc:icon symbol="invoice"/>
|
||||
|
@ -171,6 +106,12 @@
|
|||
<span>Payouts</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
|
||||
<a asp-area="" asp-controller="Stores" asp-action="PayButton" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.PayButton)" id="StoreNav-@(nameof(StoreNavPages.PayButton))">
|
||||
<vc:icon symbol="payment-2"/>
|
||||
<span>Pay Button</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -204,6 +145,28 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<header class="accordion-header" id="Nav-Plugins-Header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#Nav-Plugins" aria-expanded="true" aria-controls="Nav-Plugins">
|
||||
Plugins
|
||||
<vc:icon symbol="caret-down"/>
|
||||
</button>
|
||||
</header>
|
||||
<div id="Nav-Plugins" class="accordion-collapse collapse show" aria-labelledby="Nav-Plugins-Header">
|
||||
<div class="accordion-body">
|
||||
<ul class="navbar-nav">
|
||||
<vc:ui-extension-point location="header-nav" model="@Model"/>
|
||||
<vc:ui-extension-point location="store-integrations-nav" model="@Model" />
|
||||
<li class="nav-item" permission="@Policies.CanModifyServerSettings">
|
||||
<a asp-area="" asp-controller="Server" asp-action="ListPlugins" class="nav-link js-scroll-trigger @ViewData.IsActivePage(ServerNavPages.Plugins)" id="Nav-AddPlugin">
|
||||
<vc:icon symbol="new"/>
|
||||
<span>Add Plugin</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item" permission="@Policies.CanModifyStoreSettings">
|
||||
<header class="accordion-header" id="Nav-Manage-Header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#Nav-Manage" aria-expanded="true" aria-controls="Nav-Manage">
|
||||
|
@ -215,7 +178,7 @@
|
|||
<div class="accordion-body">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Stores" asp-action="PaymentMethods" asp-route-storeId="@Model.Store.Id" class="nav-link js-scroll-trigger @ViewData.IsActivePage(StoreNavPages.PaymentMethods) @ViewData.IsActivePage(StoreNavPages.Rates) @ViewData.IsActivePage(StoreNavPages.CheckoutAppearance) @ViewData.IsActivePage(StoreNavPages.GeneralSettings) @ViewData.IsActivePage(StoreNavPages.Tokens) @ViewData.IsActivePage(StoreNavPages.Users) @ViewData.IsActivePage(StoreNavPages.PayButton) @ViewData.IsActivePage(StoreNavPages.Integrations) @ViewData.IsActivePage(StoreNavPages.Webhooks)" id="StoreNav-Invoices">
|
||||
<a asp-area="" asp-controller="Stores" asp-action="PaymentMethods" asp-route-storeId="@Model.Store.Id" class="nav-link js-scroll-trigger @ViewData.IsActivePage(StoreNavPages.PaymentMethods) @ViewData.IsActivePage(StoreNavPages.Rates) @ViewData.IsActivePage(StoreNavPages.CheckoutAppearance) @ViewData.IsActivePage(StoreNavPages.GeneralSettings) @ViewData.IsActivePage(StoreNavPages.Tokens) @ViewData.IsActivePage(StoreNavPages.Users) @ViewData.IsActivePage(StoreNavPages.Webhooks)" id="StoreNav-StoreSettings">
|
||||
<vc:icon symbol="settings"/>
|
||||
<span>Store Settings</span>
|
||||
</a>
|
||||
|
@ -256,7 +219,7 @@
|
|||
</div>
|
||||
<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="Server" asp-action="ListUsers" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(ServerNavPages))" id="Nav-ServerSettings">
|
||||
<a asp-area="" asp-controller="Server" asp-action="ListUsers" class="nav-link js-scroll-trigger @ViewData.IsActivePage(ServerNavPages.Users) @ViewData.IsActivePage(ServerNavPages.Emails) @ViewData.IsActivePage(ServerNavPages.Policies) @ViewData.IsActivePage(ServerNavPages.Services) @ViewData.IsActivePage(ServerNavPages.Theme) @ViewData.IsActivePage(ServerNavPages.Maintenance) @ViewData.IsActivePage(ServerNavPages.Logs) @ViewData.IsActivePage(ServerNavPages.Files)" id="Nav-ServerSettings">
|
||||
<vc:icon symbol="server-settings"/>
|
||||
<span>Server Settings</span>
|
||||
</a>
|
||||
|
@ -264,7 +227,7 @@
|
|||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Manage" asp-action="Index" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(ManageNavPages))" id="Nav-Account">
|
||||
<vc:icon symbol="account"/>
|
||||
<span>Account</span>
|
||||
<span class="text-truncate" style="max-width:195px">@User.Identity.Name</span>
|
||||
</a>
|
||||
</li>
|
||||
@if (!theme.CustomTheme)
|
||||
|
|
|
@ -7,7 +7,16 @@
|
|||
<ul id="StoreSelectorMenu" class="dropdown-menu" aria-labelledby="StoreSelectorToggle">
|
||||
@foreach (var option in Model.Options)
|
||||
{
|
||||
<li><a asp-controller="Stores" asp-action="PaymentMethods" asp-route-storeId="@option.Value" class="dropdown-item@(option.Selected ? " active" : "")" id="StoreSelectorMenuItem-@option.Value">@option.Text</a></li>
|
||||
<li>
|
||||
@if (option.WalletId != null)
|
||||
{
|
||||
<a asp-controller="Wallets" asp-action="WalletTransactions" asp-route-walletId="@option.WalletId" class="dropdown-item@(option.Selected ? " active" : "")" id="StoreSelectorMenuItem-@option.Value">@option.Text</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a asp-controller="Stores" asp-action="PaymentMethods" asp-route-storeId="@option.Value" class="dropdown-item@(option.Selected ? " active" : "")" id="StoreSelectorMenuItem-@option.Value">@option.Text</a>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a asp-controller="UserStores" asp-action="CreateStore" class="dropdown-item" id="StoreSelectorMenuItem-Create">Create Store</a></li>
|
||||
|
|
|
@ -5,20 +5,23 @@ using BTCPayServer.Services.Stores;
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using NBitcoin.Secp256k1;
|
||||
|
||||
namespace BTCPayServer.Components.StoreSelector
|
||||
{
|
||||
public class StoreSelector : ViewComponent
|
||||
{
|
||||
private const string RootName = "Global";
|
||||
private readonly StoreRepository _storeRepo;
|
||||
private readonly BTCPayNetworkProvider _networkProvider;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public StoreSelector(StoreRepository storeRepo, UserManager<ApplicationUser> userManager)
|
||||
public StoreSelector(
|
||||
StoreRepository storeRepo,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
UserManager<ApplicationUser> userManager)
|
||||
{
|
||||
_storeRepo = storeRepo;
|
||||
_userManager = userManager;
|
||||
_networkProvider = networkProvider;
|
||||
}
|
||||
|
||||
public async Task<IViewComponentResult> InvokeAsync()
|
||||
|
@ -27,11 +30,21 @@ namespace BTCPayServer.Components.StoreSelector
|
|||
var stores = await _storeRepo.GetStoresByUserId(userId);
|
||||
var currentStore = ViewContext.HttpContext.GetStoreData();
|
||||
var options = stores
|
||||
.Select(store => new SelectListItem
|
||||
.Select(store =>
|
||||
{
|
||||
Text = store.StoreName,
|
||||
Value = store.Id,
|
||||
Selected = store.Id == currentStore?.Id
|
||||
var cryptoCode = store
|
||||
.GetSupportedPaymentMethods(_networkProvider)
|
||||
.OfType<DerivationSchemeSettings>()
|
||||
.FirstOrDefault()?
|
||||
.Network.CryptoCode;
|
||||
var walletId = cryptoCode != null ? new WalletId(store.Id, cryptoCode) : null;
|
||||
return new StoreSelectorOption
|
||||
{
|
||||
Text = store.StoreName,
|
||||
Value = store.Id,
|
||||
Selected = store.Id == currentStore?.Id,
|
||||
WalletId = walletId
|
||||
};
|
||||
})
|
||||
.ToList();
|
||||
|
||||
|
@ -39,7 +52,7 @@ namespace BTCPayServer.Components.StoreSelector
|
|||
{
|
||||
Options = options,
|
||||
CurrentStoreId = currentStore?.Id,
|
||||
CurrentDisplayName = currentStore?.StoreName ?? RootName
|
||||
CurrentDisplayName = currentStore?.StoreName
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace BTCPayServer.Components.StoreSelector
|
||||
{
|
||||
public class StoreSelectorViewModel
|
||||
{
|
||||
public List<SelectListItem> Options { get; set; }
|
||||
public List<StoreSelectorOption> Options { get; set; }
|
||||
public string CurrentStoreId { get; set; }
|
||||
public string CurrentDisplayName { get; set; }
|
||||
}
|
||||
|
||||
public class StoreSelectorOption
|
||||
{
|
||||
public bool Selected { get; set; }
|
||||
public string Text { get; set; }
|
||||
public string Value { get; set; }
|
||||
public WalletId WalletId { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ using BTCPayServer.Models.StoreViewModels;
|
|||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using ExchangeSharp;
|
||||
using Google.Apis.Auth.OAuth2;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
@ -26,6 +27,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.Extensions.FileProviders;
|
||||
using NBitcoin;
|
||||
using NBitcoin.Payment;
|
||||
using NBitpayClient;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
@ -34,44 +36,59 @@ namespace BTCPayServer.Controllers
|
|||
public class HomeController : Controller
|
||||
{
|
||||
private readonly ISettingsRepository _settingsRepository;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly IFileProvider _fileProvider;
|
||||
|
||||
public IHttpClientFactory HttpClientFactory { get; }
|
||||
private IHttpClientFactory HttpClientFactory { get; }
|
||||
private SignInManager<ApplicationUser> SignInManager { get; }
|
||||
public LanguageService LanguageService { get; }
|
||||
SignInManager<ApplicationUser> SignInManager { get; }
|
||||
|
||||
public HomeController(IHttpClientFactory httpClientFactory,
|
||||
ISettingsRepository settingsRepository,
|
||||
IWebHostEnvironment webHostEnvironment,
|
||||
LanguageService languageService,
|
||||
StoreRepository storeRepository,
|
||||
SignInManager<ApplicationUser> signInManager)
|
||||
{
|
||||
_settingsRepository = settingsRepository;
|
||||
HttpClientFactory = httpClientFactory;
|
||||
LanguageService = languageService;
|
||||
_storeRepository = storeRepository;
|
||||
_fileProvider = webHostEnvironment.WebRootFileProvider;
|
||||
SignInManager = signInManager;
|
||||
}
|
||||
|
||||
[Route("")]
|
||||
[DomainMappingConstraint()]
|
||||
[DomainMappingConstraint]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
if ((await _settingsRepository.GetTheme()).FirstRun)
|
||||
{
|
||||
return RedirectToAction(nameof(AccountController.Register), "Account");
|
||||
}
|
||||
|
||||
if (SignInManager.IsSignedIn(User))
|
||||
{
|
||||
var storeId = HttpContext.GetUserPrefsCookie()?.CurrentStoreId;
|
||||
if (storeId != null)
|
||||
{
|
||||
var userId = SignInManager.UserManager.GetUserId(HttpContext.User);
|
||||
var store = await _storeRepository.FindStore(storeId, userId);
|
||||
if (store != null)
|
||||
{
|
||||
HttpContext.SetStoreData(store);
|
||||
}
|
||||
}
|
||||
return View("Home");
|
||||
else
|
||||
return Challenge();
|
||||
}
|
||||
|
||||
return Challenge();
|
||||
}
|
||||
|
||||
[Route("misc/lang")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie + "," + AuthenticationSchemes.Greenfield)]
|
||||
public IActionResult Languages()
|
||||
{
|
||||
return Json(LanguageService.GetLanguages(), new JsonSerializerSettings() { Formatting = Formatting.Indented });
|
||||
return Json(LanguageService.GetLanguages(), new JsonSerializerSettings { Formatting = Formatting.Indented });
|
||||
}
|
||||
|
||||
|
||||
|
@ -79,7 +96,7 @@ namespace BTCPayServer.Controllers
|
|||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie + "," + AuthenticationSchemes.Greenfield)]
|
||||
public IActionResult Permissions()
|
||||
{
|
||||
return Json(Client.Models.PermissionMetadata.PermissionNodes, new JsonSerializerSettings() { Formatting = Formatting.Indented });
|
||||
return Json(Client.Models.PermissionMetadata.PermissionNodes, new JsonSerializerSettings { Formatting = Formatting.Indented });
|
||||
}
|
||||
|
||||
[Route("swagger/v1/swagger.json")]
|
||||
|
@ -108,7 +125,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
[Route("recovery-seed-backup")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanModifyStoreSettings)]
|
||||
public IActionResult RecoverySeedBackup(RecoverySeedBackupViewModel vm)
|
||||
{
|
||||
return View("RecoverySeedBackup", vm);
|
||||
|
|
|
@ -740,29 +740,22 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
[HttpGet("/stores/{storeId}/invoices")]
|
||||
[HttpGet("invoices")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanViewInvoices)]
|
||||
[BitpayAPIConstraint(false)]
|
||||
public async Task<IActionResult> ListInvoices(InvoicesModel? model = null, string? storeId = null)
|
||||
public async Task<IActionResult> ListInvoices(InvoicesModel? model = null)
|
||||
{
|
||||
model = this.ParseListQuery(model ?? new InvoicesModel());
|
||||
|
||||
|
||||
var fs = new SearchString(model.SearchTerm);
|
||||
var storeIds = storeId == null
|
||||
var store = model.StoreId == null || fs.ContainsFilter("storeid") ? null : HttpContext.GetStoreData();
|
||||
var storeIds = store == null
|
||||
? fs.GetFilterArray("storeid") != null ? fs.GetFilterArray("storeid") : new List<string>().ToArray()
|
||||
: new []{ storeId };
|
||||
: new []{ store.Id };
|
||||
|
||||
model.StoreIds = storeIds;
|
||||
|
||||
if (storeId != null)
|
||||
{
|
||||
var store = await _StoreRepository.FindStore(storeId, GetUserId());
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
HttpContext.SetStoreData(store);
|
||||
model.StoreId = store.Id;
|
||||
}
|
||||
|
||||
InvoiceQuery invoiceQuery = GetInvoiceQuery(model.SearchTerm, model.TimezoneOffset ?? 0);
|
||||
invoiceQuery.StoreId = storeIds;
|
||||
var counting = _InvoiceRepository.GetInvoicesTotal(invoiceQuery);
|
||||
invoiceQuery.Take = model.Count;
|
||||
invoiceQuery.Skip = model.Skip;
|
||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Fido2;
|
||||
using BTCPayServer.Models;
|
||||
|
@ -21,7 +22,7 @@ using Microsoft.Extensions.Logging;
|
|||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanViewProfile)]
|
||||
[Route("[controller]/[action]")]
|
||||
public partial class ManageController : Controller
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Net.WebSockets;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Filters;
|
||||
using BTCPayServer.Models.NotificationViewModels;
|
||||
|
@ -18,7 +19,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[BitpayAPIConstraint(false)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanViewNotificationsForUser)]
|
||||
[Route("[controller]/[action]")]
|
||||
public class NotificationsController : Controller
|
||||
{
|
||||
|
@ -46,8 +47,7 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
return ViewComponent("NotificationsDropdown");
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> SubscribeUpdates(CancellationToken cancellationToken)
|
||||
{
|
||||
|
@ -128,6 +128,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanManageNotificationsForUser)]
|
||||
public async Task<IActionResult> FlipRead(string id)
|
||||
{
|
||||
if (ValidUserClaim(out var userId))
|
||||
|
@ -161,9 +162,9 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpPost]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanManageNotificationsForUser)]
|
||||
public async Task<IActionResult> MassAction(string command, string[] selectedItems)
|
||||
{
|
||||
if (!ValidUserClaim(out var userId))
|
||||
|
@ -209,6 +210,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanManageNotificationsForUser)]
|
||||
public async Task<IActionResult> MarkAllAsSeen(string returnUrl)
|
||||
{
|
||||
if (!ValidUserClaim(out var userId))
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
return View("Integrations", new IntegrationsViewModel());
|
||||
}
|
||||
|
||||
|
||||
private async Task<Data.WebhookDeliveryData?> LastDeliveryForWebhook(string webhookId)
|
||||
{
|
||||
return (await _Repo.GetWebhookDeliveries(CurrentStore.Id, webhookId, 1)).ToList().FirstOrDefault();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
|
@ -14,40 +15,36 @@ using Microsoft.AspNetCore.Mvc;
|
|||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Route("stores")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[AutoValidateAntiforgeryToken]
|
||||
public partial class UserStoresController : Controller
|
||||
public class UserStoresController : Controller
|
||||
{
|
||||
private readonly StoreRepository _Repo;
|
||||
private readonly BTCPayNetworkProvider _NetworkProvider;
|
||||
private readonly UserManager<ApplicationUser> _UserManager;
|
||||
private readonly StoreRepository _repo;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public UserStoresController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
StoreRepository storeRepository)
|
||||
{
|
||||
_Repo = storeRepository;
|
||||
_NetworkProvider = networkProvider;
|
||||
_UserManager = userManager;
|
||||
_repo = storeRepository;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("create")]
|
||||
[HttpGet("create")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanModifyStoreSettingsUnscoped)]
|
||||
public IActionResult CreateStore()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("create")]
|
||||
[HttpPost("create")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanModifyStoreSettingsUnscoped)]
|
||||
public async Task<IActionResult> CreateStore(CreateStoreViewModel vm)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(vm);
|
||||
}
|
||||
var store = await _Repo.CreateStore(GetUserId(), vm.Name);
|
||||
var store = await _repo.CreateStore(GetUserId(), vm.Name);
|
||||
CreatedStoreId = store.Id;
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Store successfully created";
|
||||
return RedirectToAction(nameof(StoresController.PaymentMethods), "Stores", new
|
||||
|
@ -62,6 +59,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("{storeId}/me/delete")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanModifyStoreSettings)]
|
||||
public IActionResult DeleteStore(string storeId)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
|
@ -71,25 +69,27 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
[HttpPost("{storeId}/me/delete")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanModifyStoreSettings)]
|
||||
public async Task<IActionResult> DeleteStorePost(string storeId)
|
||||
{
|
||||
var userId = GetUserId();
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
await _Repo.RemoveStore(storeId, userId);
|
||||
await _repo.RemoveStore(storeId, userId);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Store removed successfully";
|
||||
return RedirectToAction(nameof(ListStores));
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanViewStoreSettings)]
|
||||
public async Task<IActionResult> ListStores(
|
||||
string sortOrder = null,
|
||||
string sortOrderColumn = null
|
||||
)
|
||||
{
|
||||
StoresViewModel result = new StoresViewModel();
|
||||
var stores = await _Repo.GetStoresByUserId(GetUserId());
|
||||
var stores = await _repo.GetStoresByUserId(GetUserId());
|
||||
if (sortOrder != null && sortOrderColumn != null)
|
||||
{
|
||||
stores = stores.OrderByDescending(store =>
|
||||
|
@ -134,9 +134,6 @@ namespace BTCPayServer.Controllers
|
|||
return View(result);
|
||||
}
|
||||
|
||||
private string GetUserId()
|
||||
{
|
||||
return _UserManager.GetUserId(User);
|
||||
}
|
||||
private string GetUserId() => _userManager.GetUserId(User);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Globalization;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Hwi;
|
||||
|
@ -18,6 +19,7 @@ using Newtonsoft.Json.Linq;
|
|||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Route("vault")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanModifyStoreSettings)]
|
||||
public class VaultController : Controller
|
||||
{
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
|
|
|
@ -34,6 +34,7 @@ using NBitcoin.Payment;
|
|||
using NBitpayClient;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
using NBXplorer.Models;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using InvoiceCryptoInfo = BTCPayServer.Services.Invoices.InvoiceCryptoInfo;
|
||||
|
||||
|
@ -409,6 +410,32 @@ namespace BTCPayServer
|
|||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static UserPrefsCookie GetUserPrefsCookie(this HttpContext ctx)
|
||||
{
|
||||
var prefCookie = new UserPrefsCookie();
|
||||
ctx.Request.Cookies.TryGetValue(nameof(UserPrefsCookie), out var strPrefCookie);
|
||||
if (!string.IsNullOrEmpty(strPrefCookie))
|
||||
{
|
||||
try
|
||||
{
|
||||
prefCookie = JsonConvert.DeserializeObject<UserPrefsCookie>(strPrefCookie);
|
||||
}
|
||||
catch { /* ignore cookie deserialization failures */ }
|
||||
}
|
||||
|
||||
return prefCookie;
|
||||
}
|
||||
|
||||
private static void SetCurrentStoreId(this HttpContext ctx, string storeId)
|
||||
{
|
||||
var prefCookie = ctx.GetUserPrefsCookie();
|
||||
if (prefCookie.CurrentStoreId != storeId)
|
||||
{
|
||||
prefCookie.CurrentStoreId = storeId;
|
||||
ctx.Response.Cookies.Append(nameof(UserPrefsCookie), JsonConvert.SerializeObject(prefCookie));
|
||||
}
|
||||
}
|
||||
|
||||
public static StoreData GetStoreData(this HttpContext ctx)
|
||||
{
|
||||
|
@ -418,12 +445,15 @@ namespace BTCPayServer
|
|||
public static void SetStoreData(this HttpContext ctx, StoreData storeData)
|
||||
{
|
||||
ctx.Items["BTCPAY.STOREDATA"] = storeData;
|
||||
|
||||
SetCurrentStoreId(ctx, storeData.Id);
|
||||
}
|
||||
|
||||
public static StoreData[] GetStoresData(this HttpContext ctx)
|
||||
{
|
||||
return ctx.Items.TryGet("BTCPAY.STORESDATA") as StoreData[];
|
||||
}
|
||||
|
||||
public static void SetStoresData(this HttpContext ctx, StoreData[] storeData)
|
||||
{
|
||||
ctx.Items["BTCPAY.STORESDATA"] = storeData;
|
||||
|
|
|
@ -10,7 +10,7 @@ using Newtonsoft.Json;
|
|||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
// Classes here remember users preferences on certain pages and store them in unified blob cookie "UserPreferCookie"
|
||||
// Classes here remember users preferences on certain pages and store them in unified blob cookie "UserPrefsCookie"
|
||||
public static class ControllerBaseExtension
|
||||
{
|
||||
public static T ParseListQuery<T>(this ControllerBase ctrl, T model) where T : BasePagingViewModel
|
||||
|
@ -34,7 +34,7 @@ namespace BTCPayServer
|
|||
|
||||
private static T ProcessParse<T>(ControllerBase ctrl, T model, PropertyInfo prop) where T : BasePagingViewModel
|
||||
{
|
||||
var prefCookie = parsePrefCookie(ctrl);
|
||||
var prefCookie = ctrl.HttpContext.GetUserPrefsCookie();
|
||||
|
||||
// If the user enter an empty searchTerm, then the variable will be null and not empty string
|
||||
// but we want searchTerm to be null only if the user is browsing the page via some link
|
||||
|
@ -46,7 +46,7 @@ namespace BTCPayServer
|
|||
if (searchTerm is null)
|
||||
{
|
||||
var section = prop.GetValue(prefCookie) as ListQueryDataHolder;
|
||||
if (section != null && !String.IsNullOrEmpty(section.SearchTerm))
|
||||
if (section != null && !string.IsNullOrEmpty(section.SearchTerm))
|
||||
{
|
||||
model.SearchTerm = section.SearchTerm;
|
||||
model.TimezoneOffset = section.TimezoneOffset ?? 0;
|
||||
|
@ -60,45 +60,5 @@ namespace BTCPayServer
|
|||
|
||||
return model;
|
||||
}
|
||||
|
||||
private static UserPrefsCookie parsePrefCookie(ControllerBase ctrl)
|
||||
{
|
||||
var prefCookie = new UserPrefsCookie();
|
||||
ctrl.Request.Cookies.TryGetValue(nameof(UserPrefsCookie), out var strPrefCookie);
|
||||
if (!String.IsNullOrEmpty(strPrefCookie))
|
||||
{
|
||||
try
|
||||
{
|
||||
prefCookie = JsonConvert.DeserializeObject<UserPrefsCookie>(strPrefCookie);
|
||||
}
|
||||
catch { /* ignore cookie deserialization failures */ }
|
||||
}
|
||||
|
||||
return prefCookie;
|
||||
}
|
||||
|
||||
class UserPrefsCookie
|
||||
{
|
||||
public ListQueryDataHolder InvoicesQuery { get; set; }
|
||||
|
||||
public ListQueryDataHolder PaymentRequestsQuery { get; set; }
|
||||
public ListQueryDataHolder UsersQuery { get; set; }
|
||||
public ListQueryDataHolder PayoutsQuery { get; set; }
|
||||
public ListQueryDataHolder PullPaymentsQuery { get; set; }
|
||||
}
|
||||
|
||||
class ListQueryDataHolder
|
||||
{
|
||||
public ListQueryDataHolder() { }
|
||||
|
||||
public ListQueryDataHolder(string searchTerm, int? timezoneOffset)
|
||||
{
|
||||
SearchTerm = searchTerm;
|
||||
TimezoneOffset = timezoneOffset;
|
||||
}
|
||||
|
||||
public int? TimezoneOffset { get; set; }
|
||||
public string SearchTerm { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
26
BTCPayServer/Extensions/UserPrefsCookie.cs
Normal file
26
BTCPayServer/Extensions/UserPrefsCookie.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
namespace BTCPayServer
|
||||
{
|
||||
public class UserPrefsCookie
|
||||
{
|
||||
public ListQueryDataHolder InvoicesQuery { get; set; }
|
||||
public ListQueryDataHolder PaymentRequestsQuery { get; set; }
|
||||
public ListQueryDataHolder UsersQuery { get; set; }
|
||||
public ListQueryDataHolder PayoutsQuery { get; set; }
|
||||
public ListQueryDataHolder PullPaymentsQuery { get; set; }
|
||||
public string CurrentStoreId { get; set; }
|
||||
}
|
||||
|
||||
public class ListQueryDataHolder
|
||||
{
|
||||
public ListQueryDataHolder() { }
|
||||
|
||||
public ListQueryDataHolder(string searchTerm, int? timezoneOffset)
|
||||
{
|
||||
SearchTerm = searchTerm;
|
||||
TimezoneOffset = timezoneOffset;
|
||||
}
|
||||
|
||||
public int? TimezoneOffset { get; set; }
|
||||
public string SearchTerm { get; set; }
|
||||
}
|
||||
}
|
|
@ -329,6 +329,8 @@ namespace BTCPayServer.Hosting
|
|||
services.AddSingleton<IPaymentMethodHandler>(provider => provider.GetService<LightningLikePaymentHandler>());
|
||||
services.AddSingleton<LNURLPayPaymentHandler>();
|
||||
services.AddSingleton<IPaymentMethodHandler>(provider => provider.GetService<LNURLPayPaymentHandler>());
|
||||
services.AddSingleton<IUIExtension>(new UIExtension("LNURL/LightningAddressNav",
|
||||
"store-integrations-nav"));
|
||||
services.AddSingleton<IUIExtension>(new UIExtension("LNURL/LightningAddressOption",
|
||||
"store-integrations-list"));
|
||||
services.AddSingleton<IHostedService, LightningListener>();
|
||||
|
|
|
@ -15,7 +15,9 @@ namespace BTCPayServer.Plugins.Shopify
|
|||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
{
|
||||
applicationBuilder.AddSingleton<IHostedService, ShopifyOrderMarkerHostedService>();
|
||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("Shopify/StoreIntegrationShopifyOption",
|
||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("Shopify/StoreIntegrationsNav",
|
||||
"store-integrations-nav"));
|
||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("Shopify/StoreIntegrationsList",
|
||||
"store-integrations-list"));
|
||||
base.Execute(applicationBuilder);
|
||||
}
|
||||
|
|
|
@ -77,5 +77,7 @@ namespace BTCPayServer
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal bool ContainsFilter(string key) => Filters.ContainsKey(key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.PaymentRequest;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.PaymentRequests;
|
||||
|
@ -37,28 +38,32 @@ namespace BTCPayServer.Security
|
|||
_invoiceRepository = invoiceRepository;
|
||||
_paymentRequestRepository = paymentRequestRepository;
|
||||
}
|
||||
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement)
|
||||
{
|
||||
if (context.User.Identity.AuthenticationType != AuthenticationSchemes.Cookie)
|
||||
return;
|
||||
|
||||
var isAdmin = context.User.IsInRole(Roles.ServerAdmin);
|
||||
switch (requirement.Policy)
|
||||
{
|
||||
case Policies.CanModifyServerSettings:
|
||||
if (isAdmin)
|
||||
context.Succeed(requirement);
|
||||
return;
|
||||
}
|
||||
|
||||
var userId = _userManager.GetUserId(context.User);
|
||||
if (string.IsNullOrEmpty(userId))
|
||||
return;
|
||||
|
||||
bool success = false;
|
||||
var isAdmin = context.User.IsInRole(Roles.ServerAdmin);
|
||||
|
||||
AppData app = null;
|
||||
StoreData store = null;
|
||||
InvoiceEntity invoice = null;
|
||||
PaymentRequestData paymentRequest = null;
|
||||
string storeId = context.Resource is string s ? s : _httpContext.GetImplicitStoreId();
|
||||
string storeId;
|
||||
var explicitResource = false;
|
||||
if (context.Resource is string s)
|
||||
{
|
||||
explicitResource = true;
|
||||
storeId = s;
|
||||
}
|
||||
else
|
||||
storeId = _httpContext.GetImplicitStoreId();
|
||||
var routeData = _httpContext.GetRouteData();
|
||||
if (routeData != null)
|
||||
{
|
||||
|
@ -81,28 +86,52 @@ namespace BTCPayServer.Security
|
|||
{
|
||||
string payReqId = vPayReqId as string;
|
||||
paymentRequest = await _paymentRequestRepository.FindPaymentRequest(payReqId, userId);
|
||||
storeId ??= paymentRequest?.StoreDataId;
|
||||
if (storeId == null)
|
||||
{
|
||||
storeId = paymentRequest?.StoreDataId;
|
||||
}
|
||||
else if (paymentRequest?.StoreDataId != storeId)
|
||||
{
|
||||
paymentRequest = null;
|
||||
}
|
||||
}
|
||||
// resolve from invoice
|
||||
if (routeData.Values.TryGetValue("invoiceId", out var vInvoiceId))
|
||||
{
|
||||
string invoiceId = vInvoiceId as string;
|
||||
invoice = await _invoiceRepository.GetInvoice(invoiceId);
|
||||
storeId ??= invoice?.StoreId;
|
||||
if (storeId == null)
|
||||
{
|
||||
storeId = invoice?.StoreId;
|
||||
}
|
||||
else if (invoice?.StoreId != storeId)
|
||||
{
|
||||
invoice = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// store could not be found
|
||||
// Fall back to user prefs cookie
|
||||
if (storeId == null)
|
||||
{
|
||||
return;
|
||||
storeId = _httpContext.GetUserPrefsCookie()?.CurrentStoreId;
|
||||
}
|
||||
|
||||
var store = await _storeRepository.FindStore(storeId, userId);
|
||||
if (storeId != null)
|
||||
{
|
||||
store = await _storeRepository.FindStore(storeId, userId);
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
switch (requirement.Policy)
|
||||
{
|
||||
case Policies.CanModifyServerSettings:
|
||||
if (isAdmin)
|
||||
success = true;
|
||||
break;
|
||||
case Policies.CanViewInvoices:
|
||||
if (store == null || store.Role == StoreRoles.Owner || isAdmin)
|
||||
success = true;
|
||||
break;
|
||||
case Policies.CanModifyStoreSettings:
|
||||
if (store != null && (store.Role == StoreRoles.Owner || isAdmin))
|
||||
success = true;
|
||||
|
@ -115,17 +144,31 @@ namespace BTCPayServer.Security
|
|||
if (store != null || isAdmin)
|
||||
success = true;
|
||||
break;
|
||||
case Policies.CanViewProfile:
|
||||
case Policies.CanViewNotificationsForUser:
|
||||
case Policies.CanManageNotificationsForUser:
|
||||
case Policies.CanModifyStoreSettingsUnscoped:
|
||||
if (context.User != null)
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
_httpContext.SetStoreData(store);
|
||||
|
||||
// cache associated entities if present
|
||||
if (app != null) _httpContext.SetAppData(app);
|
||||
if (invoice != null) _httpContext.SetInvoiceData(invoice);
|
||||
if (paymentRequest != null) _httpContext.SetPaymentRequestData(paymentRequest);
|
||||
if (!explicitResource)
|
||||
{
|
||||
|
||||
if (store != null)
|
||||
{
|
||||
_httpContext.SetStoreData(store);
|
||||
|
||||
// cache associated entities if present
|
||||
if (app != null) _httpContext.SetAppData(app);
|
||||
if (invoice != null) _httpContext.SetInvoiceData(invoice);
|
||||
if (paymentRequest != null) _httpContext.SetPaymentRequestData(paymentRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace BTCPayServer.Security
|
|||
{
|
||||
return scopes.All(s => context.User.HasClaim(c => c.Type.Equals("scope", StringComparison.InvariantCultureIgnoreCase) && c.Value.Split(' ').Contains(s)));
|
||||
}
|
||||
|
||||
public static string GetImplicitStoreId(this HttpContext httpContext)
|
||||
{
|
||||
// 1. Check in the routeData
|
||||
|
|
|
@ -2,6 +2,5 @@
|
|||
@using BTCPayServer.Views
|
||||
@using BTCPayServer.Views.Apps
|
||||
@{
|
||||
ViewBag.CategoryTitle = "Apps";
|
||||
ViewData.SetActiveCategory(typeof(AppsNavPages));
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
@{
|
||||
Layout = "_LayoutSimple";
|
||||
ViewBag.TopSmallMargin = true;
|
||||
}
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
@using BTCPayServer.Abstractions.Extensions
|
||||
@model LNURLController.EditLightningAddressVM
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData["NavPartialName"] = "../Stores/_Nav";
|
||||
ViewData.SetActivePage(StoreNavPages.Integrations, "Lightning Address Setup", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage("LightningAddress", nameof(StoreNavPages), "Lightning Address", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
@section PageHeadContent {
|
||||
|
@ -27,17 +25,19 @@
|
|||
</script>
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
@if (Context.Request.PathBase.ToString() != string.Empty)
|
||||
{
|
||||
<div class="alert alert-warning" role="alert">
|
||||
Your BTCPay Server installation is using the root path <span class="fw-bold">@Context.Request.PathBase</span>.<br /><br />
|
||||
This is incompatible with wallets attempting to resolve <span class="fw-bold">@Context.Request.GetAbsoluteUriNoPathBase(new Uri("/.well-known/lnurlp/{username}", UriKind.Relative))</span> rather than <span class="fw-bold">@Context.Request.GetAbsoluteUri("/.well-known/lnurlp/{username}")</span>.<br /><br />
|
||||
If the LN Address doesn't work, ask your integrator to redirect queries from <span class="fw-bold">@Context.Request.GetAbsoluteUriNoPathBase(new Uri("/.well-known/lnurlp/{username}", UriKind.Relative))</span> to <span class="fw-bold">@Context.Request.GetAbsoluteUri("/.well-known/lnurlp/{username}")</span>.
|
||||
</div>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
Your BTCPay Server installation is using the root path <span class="fw-bold">@Context.Request.PathBase</span>.<br /><br />
|
||||
This is incompatible with wallets attempting to resolve <span class="fw-bold">@Context.Request.GetAbsoluteUriNoPathBase(new Uri("/.well-known/lnurlp/{username}", UriKind.Relative))</span> rather than <span class="fw-bold">@Context.Request.GetAbsoluteUri("/.well-known/lnurlp/{username}")</span>.<br /><br />
|
||||
If the LN Address doesn't work, ask your integrator to redirect queries from <span class="fw-bold">@Context.Request.GetAbsoluteUriNoPathBase(new Uri("/.well-known/lnurlp/{username}", UriKind.Relative))</span> to <span class="fw-bold">@Context.Request.GetAbsoluteUri("/.well-known/lnurlp/{username}")</span>.
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="d-sm-flex align-items-center justify-content-between mb-2">
|
||||
<h2 class="mb-3 mb-sm-0">@ViewData["Title"]</h2>
|
||||
<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">
|
||||
<span class="fa fa-plus"></span>
|
||||
Add Address
|
||||
|
@ -50,16 +50,16 @@
|
|||
var showAdvancedOptions = !string.IsNullOrEmpty(Model.Add?.CurrencyCode) || Model.Add?.Min != null || Model.Add?.Max != null;
|
||||
}
|
||||
|
||||
<div class="row collapse @(showAddForm ? "show": "")" id="AddAddress">
|
||||
<div class="form-group pt-3">
|
||||
<div class="collapse @(showAddForm ? "show": "")" id="AddAddress">
|
||||
<div class="form-group pt-2">
|
||||
<label asp-for="Add.Username" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input asp-for="Add.Username" class="form-control"/>
|
||||
<span class="input-group-text" >@@@Context.Request.Host.ToUriComponent()@Context.Request.PathBase</span>
|
||||
<span class="input-group-text">@@@Context.Request.Host.ToUriComponent()@Context.Request.PathBase</span>
|
||||
</div>
|
||||
<span asp-validation-for="Add.Username" class="text-danger"></span>
|
||||
</div>
|
||||
<a class="mb-3" role="button" data-bs-toggle="collapse" data-bs-target="#AdvancedSettings">Advanced settings</a>
|
||||
<a class="d-inline-block mb-3" role="button" data-bs-toggle="collapse" data-bs-target="#AdvancedSettings">Advanced settings</a>
|
||||
<div id="AdvancedSettings" class="collapse @(showAdvancedOptions ? "show" : "")">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-auto">
|
||||
|
@ -93,54 +93,58 @@
|
|||
|
||||
@if (Model.Items.Any())
|
||||
{
|
||||
<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"/>
|
||||
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" data-clipboard-confirm>Copy</button>
|
||||
</div>
|
||||
<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"/>
|
||||
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" data-clipboard-confirm>Copy</button>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
<td class="settings-holder align-middle">
|
||||
@if (Model.Items[index].Min.HasValue)
|
||||
{
|
||||
<span>@Safe.Raw($"{Model.Items[index].Min} min sats")</span>
|
||||
}
|
||||
@if (Model.Items[index].Max.HasValue)
|
||||
{
|
||||
<span> @Safe.Raw($"{Model.Items[index].Max} max sats")</span>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.Items[index].CurrencyCode))
|
||||
{
|
||||
<span> @Safe.Raw($"tracked in {Model.Items[index].CurrencyCode}")</span>
|
||||
}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button type="submit" title="Remove" name="command" value="@($"remove:{index}")"
|
||||
class="btn btn-link px-0 remove" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The Lightning Address <strong>@address</strong> will be removed." data-confirm-input="REMOVE">
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td class="settings-holder align-middle">
|
||||
@if (Model.Items[index].Min.HasValue)
|
||||
{
|
||||
<span>@Safe.Raw($"{Model.Items[index].Min} min sats")</span>
|
||||
}
|
||||
@if (Model.Items[index].Max.HasValue)
|
||||
{
|
||||
<span> @Safe.Raw($"{Model.Items[index].Max} max sats")</span>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.Items[index].CurrencyCode))
|
||||
{
|
||||
<span> @Safe.Raw($"tracked in {Model.Items[index].CurrencyCode}")</span>
|
||||
}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button type="submit" title="Remove" name="command" value="@($"remove:{index}")"
|
||||
class="btn btn-link px-0 remove" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The Lightning Address <strong>@address</strong> will be removed." data-confirm-input="REMOVE">
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -16,13 +16,11 @@
|
|||
|
||||
<span class="text-capitalize badge bg-secondary">@item.Amount @cryptoCode</span>
|
||||
</li>
|
||||
|
||||
<form method="post" class="list-group-item justify-content-center" id="pay-invoices-form">
|
||||
<button type="submit" class="btn btn-primary xmx-2" style="min-width:25%;" id="Pay">Pay</button>
|
||||
<button type="button" class="btn btn-secondary mx-2" onclick="history.back(); return false;" style="min-width:25%;">Go back</button>
|
||||
</form>
|
||||
}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -30,12 +28,11 @@
|
|||
@section PageFootContent {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
$("#pay-invoices-form").on("submit", function() {
|
||||
$(this).find("input[type='submit']").prop('disabled', true);
|
||||
});
|
||||
|
||||
$("#pay-invoices-form input").on("input", function () {
|
||||
$("#pay-invoices-form input").on("input", function() {
|
||||
// Give it a timeout to make sure all form validation has completed by the time we run our callback
|
||||
setTimeout(function() {
|
||||
var validationErrors = $('.field-validation-error');
|
||||
|
@ -47,4 +44,3 @@
|
|||
});
|
||||
</script>
|
||||
}
|
||||
</section>
|
||||
|
|
|
@ -3,6 +3,5 @@
|
|||
@using BTCPayServer.Views.Manage
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewBag.CategoryTitle = "Account";
|
||||
ViewData.SetActiveCategory(typeof(ManageNavPages));
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData["NavPartialName"] = "../Stores/_Nav";
|
||||
ViewData.SetActivePage(StoreNavPages.ActivePage, $"{Model.CryptoCode} Settings");
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, $"{Model.CryptoCode} Settings");
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.ActivePage, "Monero Settings");
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Monero Settings");
|
||||
ViewData["NavPartialName"] = "../Stores/_Nav";
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
@model BTCPayServer.Models.PaymentRequestViewModels.UpdatePaymentRequestViewModel
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
ViewData.SetActivePage(PaymentRequestsNavPages.Create, (string.IsNullOrEmpty(Model.Id) ? "Create" : "Edit") + " Payment Request");
|
||||
ViewData.SetActivePage(PaymentRequestsNavPages.Create, $"{(string.IsNullOrEmpty(Model.Id) ? "Create" : "Edit")} Payment Request", Model.Id);
|
||||
}
|
||||
|
||||
@section PageHeadContent {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
@model BTCPayServer.Controllers.ServerController.ListPluginsViewModel
|
||||
@inject BTCPayServerOptions BTCPayServerOptions
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
ViewData.SetActivePage(ServerNavPages.Plugins);
|
||||
var installed = Model.Installed.ToDictionary(plugin => plugin.Identifier.ToLowerInvariant(), plugin => plugin.Version);
|
||||
var availableAndNotInstalled = Model.Available
|
||||
|
@ -95,6 +96,9 @@
|
|||
.version-switch .nav-link { display: inline; }
|
||||
.version-switch .nav-link.active { display: none; }
|
||||
</style>
|
||||
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
@if (Model.Disabled.Any())
|
||||
{
|
||||
<div class="alert alert-danger mb-5">
|
||||
|
|
|
@ -13,6 +13,6 @@
|
|||
}
|
||||
<a asp-controller="Server" id="SectionNav-@ServerNavPages.Logs" class="nav-link @ViewData.IsActivePage(ServerNavPages.Logs)" asp-action="LogsView">Logs</a>
|
||||
<a asp-controller="Server" id="SectionNav-@ServerNavPages.Files" class="nav-link @ViewData.IsActivePage(ServerNavPages.Files)" asp-action="Files">Files</a>
|
||||
<a asp-controller="Server" id="SectionNav-@ServerNavPages.Plugins" class="nav-link @ViewData.IsActivePage(ServerNavPages.Plugins)" asp-action="ListPlugins">Plugins (experimental)</a>
|
||||
<vc:ui-extension-point location="server-nav" model="@Model"/>
|
||||
</nav>
|
||||
|
||||
|
|
|
@ -3,6 +3,5 @@
|
|||
@using BTCPayServer.Views.Server
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewBag.CategoryTitle = "Server settings";
|
||||
ViewData.SetActiveCategory(typeof(ServerNavPages));
|
||||
}
|
||||
|
|
|
@ -2,21 +2,17 @@
|
|||
|
||||
@if (!string.IsNullOrEmpty(Model.ProvidedComment))
|
||||
{
|
||||
|
||||
<tr>
|
||||
<td colspan="100% bg-tile">
|
||||
|
||||
LNURL Comment: @Model.ProvidedComment
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="100% bg-tile">
|
||||
LNURL Comment: @Model.ProvidedComment
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.ConsumedLightningAddress))
|
||||
{
|
||||
|
||||
<tr>
|
||||
<td colspan="100% bg-tile">
|
||||
|
||||
Lightning address used: @Model.ConsumedLightningAddress
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="100% bg-tile">
|
||||
Lightning address used: @Model.ConsumedLightningAddress
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
|
23
BTCPayServer/Views/Shared/LNURL/LightningAddressNav.cshtml
Normal file
23
BTCPayServer/Views/Shared/LNURL/LightningAddressNav.cshtml
Normal file
|
@ -0,0 +1,23 @@
|
|||
@using BTCPayServer.Payments.Lightning
|
||||
@using BTCPayServer.Views.Stores
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@inject BTCPayNetworkProvider BTCPayNetworkProvider
|
||||
@{
|
||||
var store = Context.GetStoreData();
|
||||
var isLightningEnabled = store.IsLightningEnabled(BTCPayNetworkProvider);
|
||||
var isLNUrlEnabled = store.IsLNUrlEnabled(BTCPayNetworkProvider);
|
||||
var possible =
|
||||
isLightningEnabled &&
|
||||
isLNUrlEnabled &&
|
||||
store.GetSupportedPaymentMethods(BTCPayNetworkProvider).OfType<LNURLPaySupportedPaymentMethod>().Any(type => type.CryptoCode == "BTC");
|
||||
}
|
||||
|
||||
@if (possible)
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="LNURL" asp-action="EditLightningAddress" asp-route-storeId="@store.Id" class="nav-link js-scroll-trigger @ViewData.IsActivePage("LightningAddress", nameof(StoreNavPages))" id="StoreNav-LightningAddress">
|
||||
<vc:icon symbol="wallet-lightning"/>
|
||||
<span>Lightning Address</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
@using BTCPayServer.Views.Stores
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@{
|
||||
var store = Context.GetStoreData();
|
||||
}
|
||||
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Shopify" asp-action="EditShopifyIntegration" asp-route-storeId="@store.Id" class="nav-link js-scroll-trigger @ViewData.IsActivePage("shopify", nameof(StoreNavPages))" id="StoreNav-Shopify">
|
||||
<vc:icon symbol="shopify"/>
|
||||
<span>Shopify</span>
|
||||
</a>
|
||||
</li>
|
|
@ -28,6 +28,7 @@
|
|||
$text.removeAttribute('hidden')
|
||||
$continue.setAttribute('disabled', 'disabled')
|
||||
$inputText.textContent = confirmInput
|
||||
$input.setAttribute("autocomplete", "off");
|
||||
$input.addEventListener('input', event => {
|
||||
event.target.value.trim() === confirmInput
|
||||
? $continue.removeAttribute('disabled')
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
@await RenderSectionAsync("PageHeadContent", false)
|
||||
</head>
|
||||
<body>
|
||||
<section class="content-wrapper @(ViewBag.TopSmallMargin != null ? "pt-4" : "")">
|
||||
<section class="content-wrapper">
|
||||
<!-- Dummy navbar-brand, hackish way to keep test AssertNoError passing -->
|
||||
<div class="navbar-brand d-none"></div>
|
||||
<div class="container">
|
||||
|
|
|
@ -2,23 +2,23 @@
|
|||
@using BTCPayServer.Abstractions.Extensions
|
||||
@model BTCPayServer.Plugins.Shopify.Models.ShopifySettings
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
|
||||
ViewData["NavPartialName"] = "../Stores/_Nav";
|
||||
ViewData.SetActivePage(StoreNavPages.Integrations, "Integrations");
|
||||
|
||||
ViewData.SetActivePage("shopify", nameof(StoreNavPages), "Shopify", Context.GetStoreData().Id);
|
||||
|
||||
var shopifyCredsSet = Model?.IntegratedAt.HasValue is true;
|
||||
var shopifyUrl = Model?.ShopifyUrl;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage"/>
|
||||
|
||||
<h2 class="mt-1 mb-4">
|
||||
@ViewData["Title"]
|
||||
<small>
|
||||
<a href="https://docs.btcpayserver.org/Shopify" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</small>
|
||||
</h2>
|
||||
<form method="post" id="shopifyForm">
|
||||
<h4 class="mb-3">
|
||||
Shopify
|
||||
<small>
|
||||
<a href="https://docs.btcpayserver.org/Shopify" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</small>
|
||||
</h4>
|
||||
@if (!shopifyCredsSet)
|
||||
{
|
||||
<p class="alert alert-info">Create a Shopify Private App with the permissions "Orders - Read and write"</p>
|
||||
|
@ -39,7 +39,7 @@
|
|||
{
|
||||
<span class="input-group-text">https://</span>
|
||||
}
|
||||
<input asp-for="ShopName" class="form-control" readonly="@shopifyCredsSet" />
|
||||
<input asp-for="ShopName" class="form-control" readonly="@shopifyCredsSet"/>
|
||||
|
||||
@if (!Model?.ShopName?.Contains(".") is true)
|
||||
{
|
||||
|
@ -51,13 +51,13 @@
|
|||
|
||||
<div class="form-group">
|
||||
<label asp-for="ApiKey" class="form-label"></label>
|
||||
<input asp-for="ApiKey" class="form-control" readonly="@shopifyCredsSet" />
|
||||
<input asp-for="ApiKey" class="form-control" readonly="@shopifyCredsSet"/>
|
||||
<span asp-validation-for="ApiKey" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Password" class="form-label"></label>
|
||||
<input asp-for="Password" class="form-control" type="password" value="@Model?.Password" readonly="@shopifyCredsSet" />
|
||||
<input asp-for="Password" class="form-control" type="password" value="@Model?.Password" readonly="@shopifyCredsSet"/>
|
||||
<span asp-validation-for="Password" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="alert alert-warning">
|
||||
|
@ -65,7 +65,7 @@
|
|||
In Shopify please paste following script at <a href="@shopifyUrl/admin/settings/checkout#PolarisTextField1" target="_blank" class="fw-bold" rel="noreferrer noopener"> Settings > Checkout > Order Processing > Additional Scripts</a>
|
||||
</p>
|
||||
<kbd style="display: block; word-break: break-all;">
|
||||
@($"<script src='{Url.Action("ShopifyJavascript", "Shopify", new {storeId = Context.GetRouteValue("storeId")}, Context.Request.Scheme)}'></script>")
|
||||
@($"<script src='{Url.Action("ShopifyJavascript", "Shopify", new { storeId = Context.GetRouteValue("storeId") }, Context.Request.Scheme)}'></script>")
|
||||
</kbd>
|
||||
</div>
|
||||
<p class="alert alert-warning">
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
@using BTCPayServer.Views.Stores
|
||||
@model BTCPayServer.Models.WalletViewModels.NewPullPaymentModel
|
||||
@{
|
||||
ViewData.SetActivePage(StoreNavPages.PullPayments, "New pull payment", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.PullPayments, "New pull payment", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h4 class="mb-3">@ViewData["Title"]</h4>
|
||||
<h2 class="mb-3">@ViewData["Title"]</h2>
|
||||
|
||||
<form method="post"
|
||||
asp-route-walletId="@Context.GetRouteValue("walletId")"
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
@inject IEnumerable<IPayoutHandler> PayoutHandlers;
|
||||
@{
|
||||
ViewData.SetActivePage(StoreNavPages.Payouts, $"Payouts{(string.IsNullOrEmpty(Model.PullPaymentName) ? string.Empty : " for pull payment " + Model.PullPaymentName)}", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.Payouts, $"Payouts{(string.IsNullOrEmpty(Model.PullPaymentName) ? string.Empty : " for pull payment " + Model.PullPaymentName)}", Context.GetStoreData().Id);
|
||||
Model.PaginationQuery ??= new Dictionary<string, object>();
|
||||
Model.PaginationQuery.Add("pullPaymentId", Model.PullPaymentId);
|
||||
Model.PaginationQuery.Add("paymentMethodId", Model.PaymentMethodId);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
@using BTCPayServer.Client
|
||||
@model BTCPayServer.Models.WalletViewModels.PullPaymentsModel
|
||||
@{
|
||||
ViewData.SetActivePage(StoreNavPages.PullPayments, "Pull payments", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.PullPayments, "Pull payments", Context.GetStoreData().Id);
|
||||
var nextStartDateSortOrder = (string)ViewData["NextStartSortOrder"];
|
||||
string startDateSortOrder = null;
|
||||
switch (nextStartDateSortOrder)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@model CheckoutAppearanceViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.CheckoutAppearance, "Checkout experience", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.CheckoutAppearance, "Checkout experience", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@model CreateTokenViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.Tokens, "Create New Token", Context.GetStoreData()?.StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.Tokens, "Create New Token", Context.GetStoreData()?.Id);
|
||||
ViewBag.HidePublicKey = ViewBag.HidePublicKey ?? false;
|
||||
ViewBag.ShowStores = ViewBag.ShowStores ?? false;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@model BTCPayServer.Models.ServerViewModels.EmailsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.GeneralSettings, "Email Settings", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.GeneralSettings, "Email Settings", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<partial name="EmailsBody" model="Model" />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@model GeneralSettingsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.GeneralSettings, "General Settings", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.GeneralSettings, "General Settings", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
var isHotWallet = Model.Method == WalletSetupMethod.HotWallet;
|
||||
var type = isHotWallet ? "Hot" : "Watch-Only";
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, $"Create {Model.CryptoCode} {type} Wallet", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, $"Create {Model.CryptoCode} {type} Wallet", Context.GetStoreData().Id);
|
||||
ViewData.Add(nameof(Model.CanUseHotWallet), Model.CanUseHotWallet);
|
||||
ViewData.Add(nameof(Model.CanUseRPCImport), Model.CanUseRPCImport);
|
||||
ViewData.Add(nameof(Model.SupportSegwit), Model.SupportSegwit);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, $"Generate {Model.CryptoCode} Wallet", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, $"Generate {Model.CryptoCode} Wallet", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, "Confirm addresses", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Confirm addresses", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, "Import your wallet file", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Import your wallet file", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, "Connect your hardware wallet", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Connect your hardware wallet", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, "Scan QR code", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Scan QR code", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, "Enter the wallet seed", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Enter the wallet seed", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, "Enter your extended public key", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Enter your extended public key", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, $"Import {Model.CryptoCode} Wallet", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, $"Import {Model.CryptoCode} Wallet", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@model IntegrationsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.Integrations, "Integrations", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.Integrations, "Integrations", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
|
@ -12,7 +12,7 @@
|
|||
}
|
||||
|
||||
<ul class="list-group mb-3">
|
||||
<vc:ui-extension-point location="store-integrations-list" model="@Model"></vc:ui-extension-point>
|
||||
<vc:ui-extension-point location="store-integrations-list" model="@Model" />
|
||||
</ul>
|
||||
|
||||
<h4 class="mt-5 mb-3">Other Integrations</h4>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@model LightningSettingsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, $"{Model.CryptoCode} Lightning Settings", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.LightningSettings, $"{Model.CryptoCode} Lightning Settings", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@model TokensViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.Tokens, "Access Tokens", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.Tokens, "Access Tokens", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
@if (Model.StoreNotConfigured)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@using BTCPayServer.Client.Models;
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.Webhooks, "Webhook Settings", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.Webhooks, "Webhook Settings", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
@section PageHeadContent {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
@inject BTCPayServer.Security.ContentSecurityPolicies csp
|
||||
@model PayButtonViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.PayButton, "Pay Button", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.PayButton, "Pay Button", Context.GetStoreData().Id);
|
||||
csp.AllowUnsafeHashes("onBTCPayFormSubmit(event);return false");
|
||||
}
|
||||
|
||||
|
@ -69,10 +68,13 @@
|
|||
</script>
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<h2 class="mt-1 mb-4">@ViewData["Title"]</h2>
|
||||
|
||||
<div id="payButtonCtrl">
|
||||
<div class="row">
|
||||
<div class="col-lg-7">
|
||||
<h3 class="mb-3">@ViewData["Title"]</h3>
|
||||
<div class="row">
|
||||
<p>Configure your Pay Button, and the generated code will be displayed at the bottom of the page to copy into your project.</p>
|
||||
<h4 class="mt-3 mb-3">General Settings</h4>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.PayButton, "Pay Button", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.PayButton, "Pay Button", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<h2 class="mt-1 mb-4">@ViewData["Title"]</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<h4 class="mb-3">@ViewData["Title"]</h4>
|
||||
<p>
|
||||
To start using Pay Button, you need to enable this feature explicitly.
|
||||
Once you do so, anyone could create an invoice on your store (via API, for example).
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
@using System.Text.RegularExpressions
|
||||
@using BTCPayServer.Lightning
|
||||
@using BTCPayServer.Services
|
||||
@model PaymentMethodsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, "Wallets", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, "Wallets", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@model BTCPayServer.Models.StoreViewModels.RatesViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.Rates, "Rates", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.Rates, "Rates", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@model PairingModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.Tokens, "Pairing Permission", Context.GetStoreData()?.StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.Tokens, "Pairing Permission", Context.GetStoreData()?.Id);
|
||||
}
|
||||
|
||||
<h3 class="mb-0">@ViewData["Title"]</h3>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@model LightningNodeViewModel
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, "Connect to a Lightning node", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.LightningSettings, "Connect to a Lightning node", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<header class="text-center">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@model WalletSetupViewModel
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, $"Setup {Model.CryptoCode} Wallet", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, $"Setup {Model.CryptoCode} Wallet", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<h1 class="text-center">Let's get started</h1>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@model BTCPayServer.Security.Bitpay.BitTokenEntity
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.Tokens, "Access Tokens", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.Tokens, "Access Tokens", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<h3 class="mb-4">@ViewData["Title"]</h3>
|
||||
|
|
|
@ -2,6 +2,6 @@ namespace BTCPayServer.Views.Stores
|
|||
{
|
||||
public enum StoreNavPages
|
||||
{
|
||||
Index, Create, Rates, PaymentMethods, CheckoutAppearance, GeneralSettings, Tokens, Users, PayButton, Integrations, Webhooks, ActivePage, PullPayments, Payouts
|
||||
Index, Create, Rates, PaymentMethods, OnchainSettings, LightningSettings, CheckoutAppearance, GeneralSettings, Tokens, Users, PayButton, Integrations, Webhooks, PullPayments, Payouts
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@model StoreUsersViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.Users, "Store Users", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.Users, "Store Users", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@using BTCPayServer.Client.Models;
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.Webhooks, "Send a test event to a webhook endpoint", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.Webhooks, "Send a test event to a webhook endpoint", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
@model WalletSettingsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.PaymentMethods, $"{Model.CryptoCode} Wallet Settings", Context.GetStoreData().StoreName);
|
||||
ViewData["NavPartialName"] = "../Wallets/_Nav";
|
||||
ViewData.SetActivePage(StoreNavPages.OnchainSettings, $"{Model.CryptoCode} Wallet Settings", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
@section PageHeadContent {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@model WebhooksViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(StoreNavPages.Webhooks, "Webhooks", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(StoreNavPages.Webhooks, "Webhooks", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.GeneralSettings))" class="nav-link @ViewData.IsActivePage(StoreNavPages.GeneralSettings)" asp-controller="Stores" asp-action="GeneralSettings" asp-route-storeId="@Context.GetRouteValue("storeId")">General Settings</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.Tokens))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Tokens)" asp-controller="Stores" asp-action="ListTokens" asp-route-storeId="@Context.GetRouteValue("storeId")">Access Tokens</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.Users))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Users)" asp-controller="Stores" asp-action="StoreUsers" asp-route-storeId="@Context.GetRouteValue("storeId")">Users</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.PayButton))" class="nav-link @ViewData.IsActivePage(StoreNavPages.PayButton)" asp-controller="Stores" asp-action="PayButton" asp-route-storeId="@Context.GetRouteValue("storeId")">Pay Button</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.Integrations))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Integrations)" asp-controller="Stores" asp-action="Integrations" asp-route-storeId="@Context.GetRouteValue("storeId")">Integrations</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.Webhooks))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Webhooks)" asp-controller="Stores" asp-action="Webhooks" asp-route-storeId="@Context.GetRouteValue("storeId")">Webhooks</a>
|
||||
<vc:ui-extension-point location="store-nav" model="@Model" />
|
||||
|
|
|
@ -3,6 +3,5 @@
|
|||
@using BTCPayServer.Views.Stores
|
||||
|
||||
@{
|
||||
ViewBag.CategoryTitle = "Stores";
|
||||
ViewData.SetActiveCategory(typeof(StoreNavPages));
|
||||
}
|
||||
|
|
|
@ -69,13 +69,13 @@
|
|||
}
|
||||
</td>
|
||||
<td style="text-align:right">
|
||||
<a asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchTerm="storeid:@store.Id">Invoices</a><span> - </span>
|
||||
<a asp-action="ListInvoices" asp-controller="Invoice" asp-route-storeId="@store.Id">Invoices</a><span> - </span>
|
||||
<a asp-action="PullPayments" asp-controller="StorePullPayments" asp-route-storeId="@store.Id">Pull Payments</a>
|
||||
@if (store.IsOwner)
|
||||
{
|
||||
<span> - </span>
|
||||
<a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@store.Id" id="update-store-@store.Id">Settings</a><span> - </span>
|
||||
<a asp-action="DeleteStore" asp-controller="Stores" asp-route-storeId="@store.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The store <strong>@store.Name</strong> will be permanently deleted. This action will also delete all invoices, apps and data associated with the store." data-confirm-input="DELETE">Delete</a><span> - </span>
|
||||
<a asp-action="DeleteStore" asp-controller="Stores" asp-route-storeId="@store.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The store <strong>@store.Name</strong> will be permanently deleted. This action will also delete all invoices, apps and data associated with the store." data-confirm-input="DELETE">Delete</a>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
@model SignWithSeedViewModel
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.Send, "Sign PSBT", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(WalletsNavPages.Send, "Sign PSBT", walletId);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
<a asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")" id="GoBack">
|
||||
<a asp-action="WalletPSBT" asp-route-walletId="@walletId" id="GoBack">
|
||||
<vc:icon symbol="back" />
|
||||
</a>
|
||||
<a asp-action="WalletSend" asp-route-walletId="@Context.GetRouteValue("walletId")" class="cancel">
|
||||
<a asp-action="WalletSend" asp-route-walletId="@walletId" class="cancel">
|
||||
<vc:icon symbol="close" />
|
||||
</a>
|
||||
}
|
||||
|
@ -39,7 +40,7 @@
|
|||
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
|
||||
<form method="post" asp-action="SignWithSeed" asp-route-walletId="@Context.GetRouteValue("walletId")">
|
||||
<form method="post" asp-action="SignWithSeed" asp-route-walletId="@walletId">
|
||||
<partial name="SigningContext" for="SigningContext"/>
|
||||
<div class="form-group">
|
||||
<label asp-for="SeedOrKey" class="form-label"></label>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
@model WalletPSBTViewModel
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(WalletsNavPages.PSBT, "Decode PSBT", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(WalletsNavPages.PSBT, "Decode PSBT", walletId);
|
||||
}
|
||||
|
||||
@section PageHeadContent {
|
||||
|
@ -41,7 +42,7 @@
|
|||
}
|
||||
|
||||
<p>You can decode a PSBT by either pasting its content, uploading the file or scanning the wallet QR code.</p>
|
||||
<form class="form-group" method="post" asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")" enctype="multipart/form-data">
|
||||
<form class="form-group" method="post" asp-action="WalletPSBT" asp-route-walletId="@walletId" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label asp-for="PSBT" class="form-label"></label>
|
||||
<textarea class="form-control" rows="5" asp-for="PSBT"></textarea>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
@model WalletPSBTCombineViewModel
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.PSBT, "Combine PSBT", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(WalletsNavPages.PSBT, "Combine PSBT", walletId);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
@model WalletPSBTViewModel
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
var isReady = !Model.HasErrors;
|
||||
var isSignable = !isReady && Model.NBXSeedAvailable;
|
||||
var needsExport = !isSignable && !isReady;
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.PSBT, isReady ? "Confirm broadcasting this transaction" : "Transaction Details", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(WalletsNavPages.PSBT, isReady ? "Confirm broadcasting this transaction" : "Transaction Details", walletId);
|
||||
}
|
||||
|
||||
@section PageHeadContent {
|
||||
|
@ -49,7 +50,7 @@
|
|||
|
||||
@if (isSignable)
|
||||
{
|
||||
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")" class="my-5">
|
||||
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@walletId" class="my-5">
|
||||
<input type="hidden" asp-for="CryptoCode"/>
|
||||
<input type="hidden" asp-for="NBXSeedAvailable"/>
|
||||
<input type="hidden" asp-for="PSBT"/>
|
||||
|
@ -61,7 +62,7 @@
|
|||
}
|
||||
else if (isReady)
|
||||
{
|
||||
<form method="post" asp-action="WalletPSBTReady" asp-route-walletId="@Context.GetRouteValue("walletId")" class="my-5">
|
||||
<form method="post" asp-action="WalletPSBTReady" asp-route-walletId="@walletId" class="my-5">
|
||||
<input type="hidden" asp-for="SigningKey" />
|
||||
<input type="hidden" asp-for="SigningKeyPath" />
|
||||
<partial name="SigningContext" for="SigningContext" />
|
||||
|
@ -97,7 +98,7 @@ else
|
|||
</h2>
|
||||
<div id="PSBTOptionsExportContent" class="accordion-collapse collapse @(needsExport ? "show" : "")" aria-labelledby="PSBTOptionsExportHeader" data-bs-parent="#PSBTOptions">
|
||||
<div class="accordion-body">
|
||||
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")" class="mb-2">
|
||||
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@walletId" class="mb-2">
|
||||
<input type="hidden" asp-for="CryptoCode"/>
|
||||
<input type="hidden" asp-for="PSBT"/>
|
||||
<div class="d-flex flex-column flex-sm-row flex-wrap align-items-sm-center">
|
||||
|
@ -146,7 +147,7 @@ else
|
|||
</h2>
|
||||
<div id="PSBTOptionsImportContent" class="accordion-collapse collapse" aria-labelledby="PSBTOptionsImportHeader" data-bs-parent="#PSBTOptions">
|
||||
<div class="accordion-body">
|
||||
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")" enctype="multipart/form-data" class="mb-2">
|
||||
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@walletId" enctype="multipart/form-data" class="mb-2">
|
||||
<div class="form-group">
|
||||
<label for="ImportedPSBT" class="form-label">PSBT content</label>
|
||||
<textarea id="ImportedPSBT" name="PSBT" class="form-control" rows="5"></textarea>
|
||||
|
@ -173,7 +174,7 @@ else
|
|||
</h2>
|
||||
<div id="PSBTOptionsAdvancedContent" class="accordion-collapse collapse" aria-labelledby="PSBTOptionsAdvancedHeader" data-bs-parent="#PSBTOptions">
|
||||
<div class="accordion-body">
|
||||
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")" class="mb-2">
|
||||
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@walletId" class="mb-2">
|
||||
<input type="hidden" asp-for="CryptoCode"/>
|
||||
<input type="hidden" asp-for="NBXSeedAvailable"/>
|
||||
<input type="hidden" asp-for="PSBT"/>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@model BTCPayServer.Controllers.WalletReceiveViewModel
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(WalletsNavPages.Receive, $"Receive {Model.CryptoCode}", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(WalletsNavPages.Receive, $"Receive {Model.CryptoCode}", walletId);
|
||||
}
|
||||
|
||||
@section PageHeadContent
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
@model RescanWalletModel
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(WalletsNavPages.Rescan, "Rescan wallet", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(WalletsNavPages.Rescan, "Rescan wallet", walletId);
|
||||
}
|
||||
|
||||
<h4 class="mb-3">@ViewData["Title"]</h4>
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
@using Microsoft.AspNetCore.Mvc.ModelBinding
|
||||
@model WalletSendModel
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(WalletsNavPages.Send, $"Send {Model.CryptoCode}", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(WalletsNavPages.Send, $"Send {Model.CryptoCode}", walletId);
|
||||
csp.Add("worker-src", "blob:");
|
||||
}
|
||||
|
||||
|
@ -30,7 +31,7 @@
|
|||
<div class="row">
|
||||
<div class="col-lg-8 col-xl-7 @(!Model.InputSelection && Model.Outputs.Count == 1 ? "transaction-output-form" : "")">
|
||||
<h4 class="mb-3">@ViewData["Title"]</h4>
|
||||
<form method="post" asp-action="WalletSend" asp-route-walletId="@Context.GetRouteValue("walletId")">
|
||||
<form method="post" asp-action="WalletSend" asp-route-walletId="@walletId">
|
||||
<input type="hidden" asp-for="InputSelection" />
|
||||
<input type="hidden" asp-for="FiatDivisibility" />
|
||||
<input type="hidden" asp-for="CryptoDivisibility" />
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
@model WalletSendVaultModel
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.Send, "Sign the transaction", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(WalletsNavPages.Send, "Sign the transaction", walletId);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
<a asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")" id="GoBack">
|
||||
<a asp-action="WalletPSBT" asp-route-walletId="@walletId" id="GoBack">
|
||||
<vc:icon symbol="back" />
|
||||
</a>
|
||||
<a asp-action="WalletSend" asp-route-walletId="@Context.GetRouteValue("walletId")" class="cancel">
|
||||
<a asp-action="WalletSend" asp-route-walletId="@walletId" class="cancel">
|
||||
<vc:icon symbol="close" />
|
||||
</a>
|
||||
}
|
||||
|
@ -26,7 +27,7 @@
|
|||
</div>
|
||||
|
||||
<div id="body" class="my-4">
|
||||
<form id="broadcastForm" asp-action="WalletSendVault" asp-route-walletId="@Context.GetRouteValue("walletId")" method="post" style="display:none;">
|
||||
<form id="broadcastForm" asp-action="WalletSendVault" asp-route-walletId="@walletId" method="post" style="display:none;">
|
||||
<input type="hidden" id="WalletId" asp-for="WalletId" />
|
||||
<input type="hidden" asp-for="WebsocketPath" />
|
||||
<partial name="SigningContext" for="SigningContext" />
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
@inject BTCPayNetworkProvider BTCPayNetworkProvider
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.Send, "Sign the transaction", Context.GetStoreData().StoreName);
|
||||
var walletId = WalletId.Parse(Context.GetRouteValue("walletId").ToString());
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.Send, "Sign the transaction", walletId.ToString());
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
@model ListTransactionsViewModel
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(WalletsNavPages.Transactions, $"{Model.CryptoCode} Transactions", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePage(WalletsNavPages.Transactions, $"{Model.CryptoCode} Transactions", walletId);
|
||||
}
|
||||
|
||||
@section PageHeadContent {
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace BTCPayServer.Views.Wallets
|
|||
Transactions,
|
||||
Rescan,
|
||||
PSBT,
|
||||
Receive
|
||||
Receive,
|
||||
Settings
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@inject BTCPayNetworkProvider BtcPayNetworkProvider
|
||||
@using BTCPayServer.Views.Stores
|
||||
@using BTCPayServer.Client
|
||||
@inject BTCPayNetworkProvider _btcPayNetworkProvider
|
||||
@{
|
||||
var wallet = WalletId.Parse(Context.GetRouteValue("walletId").ToString());
|
||||
var network = BtcPayNetworkProvider.GetNetwork<BTCPayNetwork>(wallet.CryptoCode);
|
||||
var walletId = Context.GetRouteValue("walletId")?.ToString();
|
||||
var storeId = Context.GetRouteValue("storeId")?.ToString();
|
||||
var cryptoCode = Context.GetRouteValue("cryptoCode")?.ToString();
|
||||
var wallet = walletId != null ? WalletId.Parse(walletId) : new WalletId(storeId, cryptoCode);
|
||||
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(wallet.CryptoCode);
|
||||
}
|
||||
<nav id="SectionNav" class="nav">
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Transactions)" asp-action="WalletTransactions" asp-route-walletId="@Context.GetRouteValue("walletId")" id="SectionNav-Transactions">Transactions</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Transactions)" asp-controller="Wallets" asp-action="WalletTransactions" asp-route-walletId="@wallet" id="SectionNav-Transactions" permission="@Policies.CanModifyStoreSettings">Transactions</a>
|
||||
@if (!network.ReadonlyWallet)
|
||||
{
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Send)" asp-action="WalletSend" asp-route-walletId="@Context.GetRouteValue("walletId")" id="SectionNav-Send">Send</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Send)" asp-controller="Wallets" asp-action="WalletSend" asp-route-walletId="@wallet" id="SectionNav-Send" permission="@Policies.CanModifyStoreSettings">Send</a>
|
||||
}
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Receive)" asp-action="WalletReceive" asp-route-walletId="@Context.GetRouteValue("walletId")" id="SectionNav-Receive">Receive</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Rescan)" asp-action="WalletRescan" asp-route-walletId="@Context.GetRouteValue("walletId")" id="SectionNav-Rescan">Rescan</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Receive)" asp-controller="Wallets" asp-action="WalletReceive" asp-route-walletId="@wallet" id="SectionNav-Receive" permission="@Policies.CanModifyStoreSettings">Receive</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Rescan)" asp-controller="Wallets" asp-action="WalletRescan" asp-route-walletId="@wallet" id="SectionNav-Rescan" permission="@Policies.CanModifyServerSettings">Rescan</a>
|
||||
@if (!network.ReadonlyWallet)
|
||||
{
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.PSBT)" asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")" id="SectionNav-PSBT">PSBT</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.PSBT)" asp-controller="Wallets" asp-action="WalletPSBT" asp-route-walletId="@wallet" id="SectionNav-PSBT" permission="@Policies.CanModifyStoreSettings">PSBT</a>
|
||||
}
|
||||
<vc:ui-extension-point location="wallet-nav" model="@Model" />
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Settings) @ViewData.IsActivePage(StoreNavPages.OnchainSettings)" asp-controller="Stores" asp-action="WalletSettings" asp-route-cryptoCode="@wallet.CryptoCode" asp-route-storeId="@wallet.StoreId" id="SectionNav-Settings" permission="@Policies.CanModifyStoreSettings">Settings</a>
|
||||
<vc:ui-extension-point location="wallet-nav" model="@Model"/>
|
||||
</nav>
|
||||
|
|
|
@ -2,6 +2,5 @@
|
|||
@using BTCPayServer.Views
|
||||
@using BTCPayServer.Views.Wallets
|
||||
@{
|
||||
ViewBag.CategoryTitle = "Wallets";
|
||||
ViewData.SetActiveCategory(typeof(WalletsNavPages));
|
||||
}
|
||||
|
|
|
@ -29,4 +29,5 @@
|
|||
<symbol id="payment-1" viewBox="0 0 24 24"><path d="M7.2 11.2h9.6v1.6H7.2v-1.6Zm0 4.8h5.6v-1.6H7.2V16ZM20 7.02v9.96c0 1.23-1.07 2.22-2.4 2.22H6.4c-1.31 0-2.4-1-2.4-2.22V7.02C4 5.81 5.09 4.8 6.4 4.8h11.2c1.31 0 2.4 1 2.4 2.22ZM18.4 12V7.02c0-.33-.38-.62-.8-.62H6.4c-.43 0-.8.29-.8.62v9.96c0 .33.37.62.8.62h11.2c.43 0 .8-.29.8-.62V12ZM7.2 9.6h9.6V8H7.2v1.6Z" fill="currentColor"/></symbol>
|
||||
<symbol id="payment-2" viewBox="0 0 24 24"><path d="M12 20a8 8 0 1 1 0-16 8 8 0 0 1 0 16Zm0-15.19a7.2 7.2 0 0 0 0 14.38A7.2 7.2 0 0 0 12 4.8Z" fill="currentColor" stroke="currentColor" stroke-width=".6"/><path d="M9.48 14.85a.44.44 0 0 1-.3-.14c-.14-.16-.14-.43.05-.57l5.02-4.31c.16-.14.43-.14.57.05.14.17.14.44-.05.57l-5.05 4.29c-.05.08-.16.1-.24.1Z" fill="currentColor" stroke="currentColor" stroke-width=".6"/><path d="M14.39 14.28a.4.4 0 0 1-.41-.4l.1-3.42-3.08-.17a.4.4 0 0 1-.38-.43c0-.22.19-.4.43-.38l3.47.19c.22 0 .38.19.38.4l-.13 3.83c.02.19-.17.38-.38.38Z" fill="currentColor" stroke="currentColor" stroke-width=".6"/></symbol>
|
||||
<symbol id="invoice" viewBox="0 0 24 24"><path d="M17.1 20H6.9c-.83 0-1.53-.7-1.53-1.52V5.52c0-.82.7-1.52 1.52-1.52h10.22c.83 0 1.52.7 1.52 1.52v12.96c0 .82-.7 1.52-1.52 1.52ZM6.9 5.3c-.14 0-.23.1-.23.22v12.96c0 .13.1.22.22.22h10.22c.13 0 .22-.1.22-.22V5.52c0-.13-.09-.22-.22-.22H6.89Z" fill="currentColor"/><path d="M12.24 7.95H8.11c-.09 0-.13-.05-.13-.15v-1c0-.05.04-.1.09-.1h4.13c.04 0 .08.05.08.1v1c.05.1 0 .15-.04.15ZM16.2 17.6H8.1c-.08 0-.12-.08-.12-.12V9.87a.1.1 0 0 1 .09-.09h8.08a.1.1 0 0 1 .09.09v7.44c0 .11.06.3-.04.3Z" fill="currentColor"/></symbol>
|
||||
<symbol id="shopify" viewBox="0 0 32 32"><path transform="scale(.7) translate(5, 5)" d="m20.45 31.97 9.62-2.08-3.5-23.64c-.03-.16-.15-.26-.28-.26l-2.57-.18s-1.7-1.7-1.92-1.88a.41.41 0 0 0-.16-.1l-1.22 28.14zm-4.83-16.9s-1.09-.56-2.37-.56c-1.93 0-2 1.2-2 1.52 0 1.64 4.31 2.29 4.31 6.17 0 3.06-1.92 5.01-4.54 5.01-3.14 0-4.72-1.95-4.72-1.95l.86-2.78s1.66 1.42 3.04 1.42c.9 0 1.3-.72 1.3-1.24 0-2.16-3.54-2.26-3.54-5.81-.04-2.98 2.1-5.9 6.44-5.9 1.68 0 2.5.49 2.5.49l-1.26 3.62zM14.9 1.1c.17 0 .36.06.53.19-1.31.62-2.75 2.18-3.34 5.32-.88.28-1.73.54-2.52.77.69-2.38 2.36-6.26 5.33-6.26zm1.64 3.94v.18l-3.2.98c.63-2.37 1.79-3.53 2.79-3.96.26.67.41 1.57.41 2.8zm.72-2.98c.92.1 1.52 1.15 1.9 2.34-.46.15-.98.3-1.54.49v-.34c0-1-.13-1.82-.36-2.5zm3.99 1.72-.1.03c-.03 0-.39.1-.96.28-.56-1.65-1.56-3.16-3.34-3.16h-.16C16.2.28 15.56 0 15.02 0 10.88 0 8.9 5.17 8.28 7.8c-1.6.48-2.75.84-2.88.9-.9.28-.93.3-1.03 1.15-.1.62-2.44 18.75-2.44 18.75L20.01 32z" fill="currentColor"/></symbol>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 24 KiB |
|
@ -8,9 +8,7 @@
|
|||
:root {
|
||||
--mobile-header-height: 4rem;
|
||||
--desktop-header-height: 8rem;
|
||||
--sidebar-width: 15%;
|
||||
--sidebar-min-width: 250px;
|
||||
--sidebar-max-width: 350px;
|
||||
--sidebar-width: 280px;
|
||||
}
|
||||
|
||||
/* Main Menu */
|
||||
|
@ -278,8 +276,6 @@
|
|||
bottom: 0;
|
||||
left: 0;
|
||||
width: var(--sidebar-width);
|
||||
min-width: var(--sidebar-min-width);
|
||||
max-width: var(--sidebar-max-width);
|
||||
z-index: 1045;
|
||||
color: var(--btcpay-body-text);
|
||||
visibility: hidden;
|
||||
|
@ -381,8 +377,6 @@
|
|||
bottom: 0;
|
||||
left: 0;
|
||||
width: var(--sidebar-width);
|
||||
min-width: var(--sidebar-min-width);
|
||||
max-width: var(--sidebar-max-width);
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
|
@ -412,7 +406,7 @@
|
|||
}
|
||||
|
||||
#mainContent {
|
||||
margin-left: clamp(var(--sidebar-min-width), var(--sidebar-width), var(--sidebar-max-width));
|
||||
margin-left: var(--sidebar-width);
|
||||
}
|
||||
|
||||
#mainContent > section {
|
||||
|
|
Loading…
Add table
Reference in a new issue