Compare commits
50 Commits
Author | SHA1 | Date | |
---|---|---|---|
4f9e4116a2 | |||
82d8fda05f | |||
d4935263da | |||
e158d909fb | |||
de8147d5dd | |||
16f1791a9a | |||
8745c3f8c6 | |||
ec5b45cff6 | |||
1348197295 | |||
f2516854d8 | |||
062ca6e743 | |||
44b6997bb5 | |||
78b544f9ca | |||
81926b4450 | |||
a7ad71d492 | |||
18977f7265 | |||
8a88b44e98 | |||
c9e5fe42ba | |||
56dffbf514 | |||
0e1fac3773 | |||
e7c06880a8 | |||
39463a3202 | |||
36136f0f0f | |||
22e5b2869a | |||
fc3f32a4e0 | |||
3b0914e89e | |||
76cd9a7b25 | |||
0934bebf7b | |||
cd1a4c4749 | |||
8075273ec8 | |||
97b59be9bf | |||
b87ec4f3d9 | |||
3822358096 | |||
ba7e8cfe78 | |||
41978f1c59 | |||
e75e691404 | |||
5f940df1b4 | |||
3f85918a0c | |||
195b5fdd1a | |||
d19b78b6cc | |||
9bbc05c3a7 | |||
7df3c86649 | |||
c6e0a923bb | |||
637fe1727b | |||
fd087bbeb8 | |||
2f515e1cc0 | |||
18986faca8 | |||
b099f93c78 | |||
81afe397be | |||
f869c06aee |
@ -598,8 +598,18 @@ namespace BTCPayServer.Tests
|
||||
var search = new SearchString(filter);
|
||||
Assert.Equal("storeid:abc status:abed blabhbalh", search.ToString());
|
||||
Assert.Equal("blabhbalh", search.TextSearch);
|
||||
Assert.Equal("abc", search.Filters["storeid"]);
|
||||
Assert.Equal("abed", search.Filters["status"]);
|
||||
Assert.Single(search.Filters["storeid"]);
|
||||
Assert.Single(search.Filters["status"]);
|
||||
Assert.Equal("abc", search.Filters["storeid"].First());
|
||||
Assert.Equal("abed", search.Filters["status"].First());
|
||||
|
||||
filter = "status:abed status:abed2";
|
||||
search = new SearchString(filter);
|
||||
Assert.Equal("status:abed status:abed2", search.ToString());
|
||||
Assert.Throws<KeyNotFoundException>(() => search.Filters["test"]);
|
||||
Assert.Equal(2, search.Filters["status"].Count);
|
||||
Assert.Equal("abed", search.Filters["status"].First());
|
||||
Assert.Equal("abed2", search.Filters["status"].Skip(1).First());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -633,7 +643,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(bitflyer, bitflyer2); // Should be equal because cache
|
||||
rates.Add(bitflyer);
|
||||
|
||||
foreach(var rate in rates)
|
||||
foreach (var rate in rates)
|
||||
{
|
||||
Assert.Single(rates.Where(r => r == rate));
|
||||
}
|
||||
@ -987,7 +997,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal("orange", vmview.Items[1].Title);
|
||||
Assert.Equal(10.0m, vmview.Items[1].Price.Value);
|
||||
Assert.Equal("$5.00", vmview.Items[0].Price.Formatted);
|
||||
Assert.IsType<RedirectResult>(apps.ViewPointOfSale(appId, "orange").Result);
|
||||
Assert.IsType<RedirectResult>(apps.ViewPointOfSale(appId, 0, "orange").Result);
|
||||
var invoice = user.BitPay.GetInvoices().First();
|
||||
Assert.Equal(10.00, invoice.Price);
|
||||
Assert.Equal("CAD", invoice.Currency);
|
||||
@ -1054,13 +1064,13 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
var textSearchResult = tester.PayTester.InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
{
|
||||
StoreId = user.StoreId,
|
||||
StoreId = new[] { user.StoreId },
|
||||
TextSearch = invoice.OrderId
|
||||
}).GetAwaiter().GetResult();
|
||||
Assert.Single(textSearchResult);
|
||||
textSearchResult = tester.PayTester.InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
{
|
||||
StoreId = user.StoreId,
|
||||
StoreId = new[] { user.StoreId },
|
||||
TextSearch = invoice.Id
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
@ -1185,6 +1195,26 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckQuadrigacxRateProvider()
|
||||
{
|
||||
var quadri = new QuadrigacxRateProvider("BTC");
|
||||
var rates = quadri.GetRatesAsync().GetAwaiter().GetResult();
|
||||
Assert.NotEmpty(rates);
|
||||
Assert.NotEqual(0.0m, rates.First().Value);
|
||||
Assert.NotEqual(0.0m, quadri.GetRateAsync("CAD").GetAwaiter().GetResult());
|
||||
Assert.NotEqual(0.0m, quadri.GetRateAsync("USD").GetAwaiter().GetResult());
|
||||
Assert.Throws<RateUnavailableException>(() => quadri.GetRateAsync("IOEW").GetAwaiter().GetResult());
|
||||
|
||||
quadri = new QuadrigacxRateProvider("LTC");
|
||||
rates = quadri.GetRatesAsync().GetAwaiter().GetResult();
|
||||
Assert.NotEmpty(rates);
|
||||
Assert.NotEqual(0.0m, rates.First().Value);
|
||||
Assert.NotEqual(0.0m, quadri.GetRateAsync("CAD").GetAwaiter().GetResult());
|
||||
Assert.Throws<RateUnavailableException>(() => quadri.GetRateAsync("IOEW").GetAwaiter().GetResult());
|
||||
Assert.Throws<RateUnavailableException>(() => quadri.GetRateAsync("USD").GetAwaiter().GetResult());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckRatesProvider()
|
||||
{
|
||||
|
@ -46,7 +46,7 @@ services:
|
||||
- lightning-charged
|
||||
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:1.0.2.0
|
||||
image: nicolasdorier/nbxplorer:1.0.2.2
|
||||
ports:
|
||||
- "32838:32838"
|
||||
expose:
|
||||
@ -89,7 +89,7 @@ services:
|
||||
- "bitcoin_datadir:/data"
|
||||
|
||||
customer_lightningd:
|
||||
image: nicolasdorier/clightning:0.0.0.3
|
||||
image: nicolasdorier/clightning:0.0.0.9-dev
|
||||
environment:
|
||||
EXPOSE_TCP: "true"
|
||||
LIGHTNINGD_OPT: |
|
||||
@ -98,6 +98,7 @@ services:
|
||||
network=regtest
|
||||
ipaddr=customer_lightningd
|
||||
log-level=debug
|
||||
dev-broadcast-interval=1000
|
||||
ports:
|
||||
- "30992:9835" # api port
|
||||
expose:
|
||||
@ -129,7 +130,7 @@ services:
|
||||
- merchant_lightningd
|
||||
|
||||
merchant_lightningd:
|
||||
image: nicolasdorier/clightning:0.0.0.5-dev
|
||||
image: nicolasdorier/clightning:0.0.0.9-dev
|
||||
environment:
|
||||
EXPOSE_TCP: "true"
|
||||
LIGHTNINGD_OPT: |
|
||||
|
@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<Version>1.0.1.87</Version>
|
||||
<Version>1.0.1.93</Version>
|
||||
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
@ -34,15 +34,15 @@
|
||||
<PackageReference Include="Hangfire" Version="1.6.19" />
|
||||
<PackageReference Include="Hangfire.MemoryStorage" Version="1.5.2" />
|
||||
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.1" />
|
||||
<PackageReference Include="LedgerWallet" Version="1.0.1.35" />
|
||||
<PackageReference Include="LedgerWallet" Version="1.0.1.36" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.6.1" />
|
||||
<PackageReference Include="Meziantou.AspNetCore.BundleTagHelpers" Version="1.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.NetCore.Analyzers" Version="2.6.0" />
|
||||
<PackageReference Include="NBitcoin" Version="4.1.1" />
|
||||
<PackageReference Include="NBitcoin" Version="4.1.1.3" />
|
||||
<PackageReference Include="NBitpayClient" Version="1.0.0.18" />
|
||||
<PackageReference Include="DBreeze" Version="1.87.0" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="1.0.2" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="1.0.2.3" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.1" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.2" />
|
||||
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.13" />
|
||||
@ -66,7 +66,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="wwwroot\checkout\js\core.js" />
|
||||
<None Include="wwwroot\main\js\creative.js" />
|
||||
<None Include="wwwroot\vendor\bootstrap4-creativestart\creative.js" />
|
||||
<None Include="wwwroot\vendor\font-awesome\fonts\fontawesome-webfont.svg" />
|
||||
<None Include="wwwroot\vendor\font-awesome\fonts\fontawesome-webfont.woff2" />
|
||||
<None Include="wwwroot\vendor\font-awesome\less\animated.less" />
|
||||
|
@ -42,10 +42,12 @@ namespace BTCPayServer.Controllers
|
||||
" price: 15\n\n" +
|
||||
"tshirt:\n" +
|
||||
" price: 25";
|
||||
ShowCustomAmount = true;
|
||||
}
|
||||
public string Title { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public string Template { get; set; }
|
||||
public bool ShowCustomAmount { get; set; }
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
@ -57,7 +59,7 @@ namespace BTCPayServer.Controllers
|
||||
return NotFound();
|
||||
|
||||
var settings = app.GetSettings<PointOfSaleSettings>();
|
||||
return View(new UpdatePointOfSaleViewModel() { Title = settings.Title, Currency = settings.Currency, Template = settings.Template });
|
||||
return View(new UpdatePointOfSaleViewModel() { Title = settings.Title, ShowCustomAmount = settings.ShowCustomAmount, Currency = settings.Currency, Template = settings.Template });
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("{appId}/settings/pos")]
|
||||
@ -83,6 +85,7 @@ namespace BTCPayServer.Controllers
|
||||
app.SetSettings(new PointOfSaleSettings()
|
||||
{
|
||||
Title = vm.Title,
|
||||
ShowCustomAmount = vm.ShowCustomAmount,
|
||||
Currency = vm.Currency.ToUpperInvariant(),
|
||||
Template = vm.Template
|
||||
});
|
||||
@ -99,9 +102,13 @@ namespace BTCPayServer.Controllers
|
||||
if (app == null)
|
||||
return NotFound();
|
||||
var settings = app.GetSettings<PointOfSaleSettings>();
|
||||
var currency = _Currencies.GetCurrencyData(settings.Currency);
|
||||
double step = currency == null ? 1 : Math.Pow(10, -(currency.Divisibility));
|
||||
return View(new ViewPointOfSaleViewModel()
|
||||
{
|
||||
Title = settings.Title,
|
||||
Step = step.ToString(CultureInfo.InvariantCulture),
|
||||
ShowCustomAmount = settings.ShowCustomAmount,
|
||||
Items = Parse(settings.Template, settings.Currency)
|
||||
});
|
||||
}
|
||||
@ -155,23 +162,43 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
[HttpPost]
|
||||
[Route("{appId}/pos")]
|
||||
public async Task<IActionResult> ViewPointOfSale(string appId, string choiceKey)
|
||||
public async Task<IActionResult> ViewPointOfSale(string appId, double amount, string choiceKey)
|
||||
{
|
||||
var app = await GetApp(appId, AppType.PointOfSale);
|
||||
if (string.IsNullOrEmpty(choiceKey) && amount <= 0)
|
||||
{
|
||||
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId });
|
||||
}
|
||||
if (app == null)
|
||||
return NotFound();
|
||||
var settings = app.GetSettings<PointOfSaleSettings>();
|
||||
var choices = Parse(settings.Template, settings.Currency);
|
||||
var choice = choices.FirstOrDefault(c => c.Id == choiceKey);
|
||||
if (choice == null)
|
||||
return NotFound();
|
||||
if(string.IsNullOrEmpty(choiceKey) && !settings.ShowCustomAmount)
|
||||
{
|
||||
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId });
|
||||
}
|
||||
string title = null;
|
||||
double price = 0.0;
|
||||
if (!string.IsNullOrEmpty(choiceKey))
|
||||
{
|
||||
var choices = Parse(settings.Template, settings.Currency);
|
||||
var choice = choices.FirstOrDefault(c => c.Id == choiceKey);
|
||||
if (choice == null)
|
||||
return NotFound();
|
||||
title = choice.Title;
|
||||
price = (double)choice.Price.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
price = amount;
|
||||
title = settings.Title;
|
||||
}
|
||||
|
||||
var store = await GetStore(app);
|
||||
var invoice = await _InvoiceController.CreateInvoiceCore(new NBitpayClient.Invoice()
|
||||
{
|
||||
ItemDesc = choice.Title,
|
||||
ItemDesc = title,
|
||||
Currency = settings.Currency,
|
||||
Price = (double)choice.Price.Value,
|
||||
Price = price,
|
||||
}, store, HttpContext.Request.GetAbsoluteRoot());
|
||||
return Redirect(invoice.Data.Url);
|
||||
}
|
||||
|
@ -87,8 +87,8 @@ namespace BTCPayServer.Controllers
|
||||
StartDate = dateStart,
|
||||
OrderId = orderId,
|
||||
ItemCode = itemCode,
|
||||
Status = status,
|
||||
StoreId = store.Id
|
||||
Status = status == null ? null : new[] { status },
|
||||
StoreId = new[] { store.Id }
|
||||
};
|
||||
|
||||
|
||||
|
@ -232,6 +232,7 @@ namespace BTCPayServer.Controllers
|
||||
OrderAmount = (accounting.TotalDue - accounting.NetworkFee).ToString(),
|
||||
BtcDue = accounting.Due.ToString(),
|
||||
CustomerEmail = invoice.RefundMail,
|
||||
RequiresRefundEmail = storeBlob.RequiresRefundEmail,
|
||||
ExpirationSeconds = Math.Max(0, (int)(invoice.ExpirationTime - DateTimeOffset.UtcNow).TotalSeconds),
|
||||
MaxTimeSeconds = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalSeconds,
|
||||
MaxTimeMinutes = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalMinutes,
|
||||
@ -366,15 +367,15 @@ namespace BTCPayServer.Controllers
|
||||
Count = count,
|
||||
Skip = skip,
|
||||
UserId = GetUserId(),
|
||||
Status = filterString.Filters.TryGet("status"),
|
||||
StoreId = filterString.Filters.TryGet("storeid")
|
||||
Status = filterString.Filters.ContainsKey("status") ? filterString.Filters["status"].ToArray() : null,
|
||||
StoreId = filterString.Filters.ContainsKey("storeid") ? filterString.Filters["storeid"].ToArray() : null
|
||||
}))
|
||||
{
|
||||
model.SearchTerm = searchTerm;
|
||||
model.Invoices.Add(new InvoiceModel()
|
||||
{
|
||||
Status = invoice.Status,
|
||||
Date = Prettify(invoice.InvoiceTime),
|
||||
Date = (DateTimeOffset.UtcNow - invoice.InvoiceTime).Prettify() + " ago",
|
||||
InvoiceId = invoice.Id,
|
||||
OrderId = invoice.OrderId ?? string.Empty,
|
||||
RedirectUrl = invoice.RedirectURL ?? string.Empty,
|
||||
@ -387,30 +388,6 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
private string Prettify(DateTimeOffset invoiceTime)
|
||||
{
|
||||
var ago = DateTime.UtcNow - invoiceTime;
|
||||
|
||||
if (ago.TotalMinutes < 1)
|
||||
{
|
||||
return $"{(int)ago.TotalSeconds} second{Plural((int)ago.TotalSeconds)} ago";
|
||||
}
|
||||
if (ago.TotalHours < 1)
|
||||
{
|
||||
return $"{(int)ago.TotalMinutes} minute{Plural((int)ago.TotalMinutes)} ago";
|
||||
}
|
||||
if (ago.Days < 1)
|
||||
{
|
||||
return $"{(int)ago.TotalHours} hour{Plural((int)ago.TotalHours)} ago";
|
||||
}
|
||||
return $"{(int)ago.TotalDays} day{Plural((int)ago.TotalDays)} ago";
|
||||
}
|
||||
|
||||
private string Plural(int totalDays)
|
||||
{
|
||||
return totalDays > 1 ? "s" : string.Empty;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("invoices/create")]
|
||||
[Authorize(AuthenticationSchemes = "Identity.Application")]
|
||||
|
@ -1,4 +1,5 @@
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.ServerViewModels;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Mails;
|
||||
@ -23,13 +24,18 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
private UserManager<ApplicationUser> _UserManager;
|
||||
SettingsRepository _SettingsRepository;
|
||||
private IRateProviderFactory _RateProviderFactory;
|
||||
private CssThemeManager _CssThemeManager;
|
||||
|
||||
public ServerController(UserManager<ApplicationUser> userManager,
|
||||
IRateProviderFactory rateProviderFactory,
|
||||
SettingsRepository settingsRepository)
|
||||
SettingsRepository settingsRepository,
|
||||
CssThemeManager cssThemeManager)
|
||||
{
|
||||
_UserManager = userManager;
|
||||
_SettingsRepository = settingsRepository;
|
||||
_RateProviderFactory = rateProviderFactory;
|
||||
_CssThemeManager = cssThemeManager;
|
||||
}
|
||||
|
||||
[Route("server/rates")]
|
||||
@ -126,6 +132,7 @@ namespace BTCPayServer.Controllers
|
||||
var roles = await _UserManager.GetRolesAsync(user);
|
||||
var userVM = new UserViewModel();
|
||||
userVM.Id = user.Id;
|
||||
userVM.Email = user.Email;
|
||||
userVM.IsAdmin = IsAdmin(roles);
|
||||
return View(userVM);
|
||||
}
|
||||
@ -212,7 +219,25 @@ namespace BTCPayServer.Controllers
|
||||
public async Task<IActionResult> Policies(PoliciesSettings settings)
|
||||
{
|
||||
await _SettingsRepository.UpdateSetting(settings);
|
||||
TempData["StatusMessage"] = "Policies upadated successfully";
|
||||
TempData["StatusMessage"] = "Policies updated successfully";
|
||||
return View(settings);
|
||||
}
|
||||
|
||||
[Route("server/theme")]
|
||||
public async Task<IActionResult> Theme()
|
||||
{
|
||||
var data = (await _SettingsRepository.GetSettingAsync<ThemeSettings>()) ?? new ThemeSettings();
|
||||
return View(data);
|
||||
}
|
||||
[Route("server/theme")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Theme(ThemeSettings settings)
|
||||
{
|
||||
await _SettingsRepository.UpdateSetting(settings);
|
||||
// TODO: remove controller/class-level property and have only reference to
|
||||
// CssThemeManager here in this method
|
||||
_CssThemeManager.Update(settings);
|
||||
TempData["StatusMessage"] = "Theme settings updated successfully";
|
||||
return View(settings);
|
||||
}
|
||||
|
||||
|
@ -201,6 +201,7 @@ namespace BTCPayServer.Controllers
|
||||
vm.LightningMaxValue = storeBlob.LightningMaxValue?.ToString() ?? "";
|
||||
vm.OnChainMinValue = storeBlob.OnChainMinValue?.ToString() ?? "";
|
||||
vm.AllowCoinConversion = storeBlob.AllowCoinConversion;
|
||||
vm.RequiresRefundEmail = storeBlob.RequiresRefundEmail;
|
||||
vm.CustomCSS = storeBlob.CustomCSS?.AbsoluteUri;
|
||||
vm.CustomLogo = storeBlob.CustomLogo?.AbsoluteUri;
|
||||
return View(vm);
|
||||
@ -247,6 +248,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
blob.DefaultLang = model.DefaultLang;
|
||||
blob.AllowCoinConversion = model.AllowCoinConversion;
|
||||
blob.RequiresRefundEmail = model.RequiresRefundEmail;
|
||||
blob.LightningMaxValue = lightningMaxValue;
|
||||
blob.OnChainMinValue = onchainMinValue;
|
||||
blob.CustomLogo = string.IsNullOrWhiteSpace(model.CustomLogo) ? null : new Uri(model.CustomLogo, UriKind.Absolute);
|
||||
|
@ -214,6 +214,7 @@ namespace BTCPayServer.Data
|
||||
{
|
||||
InvoiceExpiration = 15;
|
||||
MonitoringExpiration = 60;
|
||||
RequiresRefundEmail = true;
|
||||
}
|
||||
public bool NetworkFeeDisabled
|
||||
{
|
||||
@ -223,6 +224,9 @@ namespace BTCPayServer.Data
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public bool RequiresRefundEmail { get; set; }
|
||||
|
||||
public string DefaultLang { get; set; }
|
||||
[DefaultValue(60)]
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||
|
@ -34,6 +34,28 @@ namespace BTCPayServer
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static string Prettify(this TimeSpan timeSpan)
|
||||
{
|
||||
if (timeSpan.TotalMinutes < 1)
|
||||
{
|
||||
return $"{(int)timeSpan.TotalSeconds} second{Plural((int)timeSpan.TotalSeconds)}";
|
||||
}
|
||||
if (timeSpan.TotalHours < 1)
|
||||
{
|
||||
return $"{(int)timeSpan.TotalMinutes} minute{Plural((int)timeSpan.TotalMinutes)}";
|
||||
}
|
||||
if (timeSpan.Days < 1)
|
||||
{
|
||||
return $"{(int)timeSpan.TotalHours} hour{Plural((int)timeSpan.TotalHours)}";
|
||||
}
|
||||
return $"{(int)timeSpan.TotalDays} day{Plural((int)timeSpan.TotalDays)}";
|
||||
}
|
||||
|
||||
private static string Plural(int totalDays)
|
||||
{
|
||||
return totalDays > 1 ? "s" : string.Empty;
|
||||
}
|
||||
|
||||
public static string PrettyPrint(this TimeSpan expiration)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
62
BTCPayServer/HostedServices/CssThemeManager.cs
Normal file
62
BTCPayServer/HostedServices/CssThemeManager.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Logging;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using NBXplorer;
|
||||
using NBXplorer.Models;
|
||||
using System.Collections.Concurrent;
|
||||
using BTCPayServer.Events;
|
||||
using BTCPayServer.Services;
|
||||
|
||||
namespace BTCPayServer.HostedServices
|
||||
{
|
||||
public class CssThemeManager
|
||||
{
|
||||
public CssThemeManager(SettingsRepository settingsRepository)
|
||||
{
|
||||
Update(settingsRepository);
|
||||
}
|
||||
|
||||
private async void Update(SettingsRepository settingsRepository)
|
||||
{
|
||||
var data = (await settingsRepository.GetSettingAsync<ThemeSettings>()) ?? new ThemeSettings();
|
||||
Update(data);
|
||||
}
|
||||
|
||||
public void Update(ThemeSettings data)
|
||||
{
|
||||
UpdateBootstrap(data.BootstrapCssUri);
|
||||
UpdateCreativeStart(data.CreativeStartCssUri);
|
||||
}
|
||||
|
||||
private string _bootstrapUri = "/vendor/bootstrap4/css/bootstrap.css?v=" + DateTime.Now.Ticks;
|
||||
public string BootstrapUri
|
||||
{
|
||||
get { return _bootstrapUri; }
|
||||
}
|
||||
public void UpdateBootstrap(string newUri)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(newUri))
|
||||
_bootstrapUri = "/vendor/bootstrap4/css/bootstrap.css?v="+ DateTime.Now.Ticks;
|
||||
else
|
||||
_bootstrapUri = newUri;
|
||||
}
|
||||
|
||||
private string _creativeStartUri = "/vendor/bootstrap4-creativestart/creative.css?v=" + DateTime.Now.Ticks;
|
||||
public string CreativeStartUri
|
||||
{
|
||||
get { return _creativeStartUri; }
|
||||
}
|
||||
public void UpdateCreativeStart(string newUri)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(newUri))
|
||||
_creativeStartUri = "/vendor/bootstrap4-creativestart/creative.css?v=" + DateTime.Now.Ticks;
|
||||
else
|
||||
_creativeStartUri = newUri;
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,8 @@ using BTCPayServer.Services.Rates;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using BTCPayServer.Logging;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace BTCPayServer.HostedServices
|
||||
{
|
||||
@ -67,12 +69,11 @@ namespace BTCPayServer.HostedServices
|
||||
return Timer(async () =>
|
||||
{
|
||||
await new SynchronizationContextRemover();
|
||||
var tickers = await new CoinAverageRateProvider("BTC").GetExchangeTickersAsync();
|
||||
var tickers = await new CoinAverageRateProvider("BTC") { Authenticator = _coinAverageSettings }.GetExchangeTickersAsync();
|
||||
_coinAverageSettings.AvailableExchanges = tickers
|
||||
.Exchanges
|
||||
.Select(c => (c.DisplayName, c.Name))
|
||||
.ToArray();
|
||||
|
||||
await Task.Delay(TimeSpan.FromHours(5), cancellation);
|
||||
}, cancellation);
|
||||
}
|
||||
@ -88,6 +89,10 @@ namespace BTCPayServer.HostedServices
|
||||
{
|
||||
_coinAverageSettings.KeyPair = (rates.PublicKey, rates.PrivateKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
_coinAverageSettings.KeyPair = null;
|
||||
}
|
||||
await _SettingsRepository.WaitSettingsChanged<RatesSetting>(cancellation);
|
||||
}, cancellation);
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ namespace BTCPayServer.Hosting
|
||||
services.TryAddSingleton<TokenRepository>();
|
||||
services.TryAddSingleton<EventAggregator>();
|
||||
services.TryAddSingleton<CoinAverageSettings>();
|
||||
services.TryAddSingleton<ICoinAverageAuthenticator, CoinAverageSettings>();
|
||||
services.TryAddSingleton<ICoinAverageAuthenticator, CoinAverageSettingsAuthenticator>();
|
||||
services.TryAddSingleton<ApplicationDbContextFactory>(o =>
|
||||
{
|
||||
var opts = o.GetRequiredService<BTCPayServerOptions>();
|
||||
@ -138,6 +138,7 @@ namespace BTCPayServer.Hosting
|
||||
|
||||
services.TryAddSingleton<LanguageService>();
|
||||
services.TryAddSingleton<NBXplorerDashboard>();
|
||||
services.TryAddSingleton<CssThemeManager>();
|
||||
services.TryAddSingleton<StoreRepository>();
|
||||
services.TryAddSingleton<BTCPayWalletProvider>();
|
||||
services.TryAddSingleton<CurrencyNameTable>();
|
||||
|
@ -17,5 +17,8 @@ namespace BTCPayServer.Models.AppViewModels
|
||||
[Required]
|
||||
[MaxLength(5000)]
|
||||
public string Template { get; set; }
|
||||
|
||||
[Display(Name = "User can input custom amount")]
|
||||
public bool ShowCustomAmount { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ namespace BTCPayServer.Models.AppViewModels
|
||||
public ItemPrice Price { get; set; }
|
||||
public string Title { get; set; }
|
||||
}
|
||||
public bool ShowCustomAmount { get; set; }
|
||||
public string Step { get; set; }
|
||||
public string Title { get; set; }
|
||||
public Item[] Items { get; set; }
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||
public string BtcAddress { get; set; }
|
||||
public string BtcDue { get; set; }
|
||||
public string CustomerEmail { get; set; }
|
||||
public bool RequiresRefundEmail { get; set; }
|
||||
public int ExpirationSeconds { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string MerchantRefLink { get; set; }
|
||||
|
@ -31,6 +31,12 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
[MaxLength(20)]
|
||||
public string LightningMaxValue { get; set; }
|
||||
|
||||
[Display(Name = "Requires a refund email")]
|
||||
public bool RequiresRefundEmail
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Display(Name = "Do not propose on chain payment if the value of the invoice is below...")]
|
||||
[MaxLength(20)]
|
||||
public string OnChainMinValue { get; set; }
|
||||
|
@ -1111,4 +1111,17 @@ namespace BTCPayServer
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultiValueDictionaryExtensions
|
||||
{
|
||||
public static MultiValueDictionary<TKey, TValue> ToMultiValueDictionary<TInput, TKey, TValue>(this IEnumerable<TInput> collection, Func<TInput, TKey> keySelector, Func<TInput, TValue> valueSelector)
|
||||
{
|
||||
var dictionary = new MultiValueDictionary<TKey, TValue>();
|
||||
foreach(var item in collection)
|
||||
{
|
||||
dictionary.Add(keySelector(item), valueSelector(item));
|
||||
}
|
||||
return dictionary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ namespace BTCPayServer
|
||||
.Select(t => t.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
.Where(kv => kv.Length == 2)
|
||||
.Select(kv => new KeyValuePair<string, string>(kv[0].ToLowerInvariant(), kv[1]))
|
||||
.ToDictionary(o => o.Key, o => o.Value);
|
||||
.ToMultiValueDictionary(o => o.Key, o => o.Value);
|
||||
|
||||
foreach(var filter in splitted)
|
||||
{
|
||||
@ -38,8 +38,8 @@ namespace BTCPayServer
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Dictionary<string, string> Filters { get; private set; }
|
||||
|
||||
public MultiValueDictionary<string, string> Filters { get; private set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
@ -99,10 +99,14 @@ namespace BTCPayServer.Services
|
||||
try
|
||||
{
|
||||
var pubKey = await ledger.GetWalletPubKeyAsync(account);
|
||||
if (pubKey.Address.Network != network.NBitcoinNetwork)
|
||||
try
|
||||
{
|
||||
pubKey.GetAddress(network.NBitcoinNetwork);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (network.NBitcoinNetwork.NetworkType == NetworkType.Mainnet)
|
||||
throw new Exception($"The opened ledger app should be for {network.NBitcoinNetwork.Name}, not for {pubKey.Address.Network}");
|
||||
throw new Exception($"The opened ledger app does not seems to support {network.NBitcoinNetwork.Name}.");
|
||||
}
|
||||
var fingerprint = onlyChaincode ? new byte[4] : (await ledger.GetWalletPubKeyAsync(account.Parent)).UncompressedPublicKey.Compress().Hash.ToBytes().Take(4).ToArray();
|
||||
var extpubkey = new ExtPubKey(pubKey.UncompressedPublicKey.Compress(), pubKey.ChainCode, (byte)account.Indexes.Length, fingerprint, account.Indexes.Last()).GetWif(network.NBitcoinNetwork);
|
||||
@ -195,6 +199,7 @@ namespace BTCPayServer.Services
|
||||
|
||||
TransactionBuilder builder = new TransactionBuilder();
|
||||
builder.StandardTransactionPolicy.MinRelayTxFee = minTxRelayFee;
|
||||
builder.SetConsensusFactory(network.NBitcoinNetwork);
|
||||
builder.AddCoins(coins.Select(c=>c.Coin).ToArray());
|
||||
|
||||
foreach (var element in send)
|
||||
|
@ -399,9 +399,10 @@ namespace BTCPayServer.Services.Invoices
|
||||
query = query.Where(i => i.Id == queryObject.InvoiceId);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(queryObject.StoreId))
|
||||
if (queryObject.StoreId != null && queryObject.StoreId.Length > 0)
|
||||
{
|
||||
query = query.Where(i => i.StoreDataId == queryObject.StoreId);
|
||||
var stores = queryObject.StoreId.ToHashSet();
|
||||
query = query.Where(i => stores.Contains(i.StoreDataId));
|
||||
}
|
||||
|
||||
if (queryObject.UserId != null)
|
||||
@ -429,8 +430,11 @@ namespace BTCPayServer.Services.Invoices
|
||||
if (queryObject.OrderId != null)
|
||||
query = query.Where(i => i.OrderId == queryObject.OrderId);
|
||||
|
||||
if (queryObject.Status != null)
|
||||
query = query.Where(i => i.Status == queryObject.Status);
|
||||
if (queryObject.Status != null && queryObject.Status.Length > 0)
|
||||
{
|
||||
var statusSet = queryObject.Status.ToHashSet();
|
||||
query = query.Where(i => statusSet.Contains(i.Status));
|
||||
}
|
||||
|
||||
query = query.OrderByDescending(q => q.Created);
|
||||
|
||||
@ -568,7 +572,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public class InvoiceQuery
|
||||
{
|
||||
public string StoreId
|
||||
public string[] StoreId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
@ -610,7 +614,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string Status
|
||||
public string[] Status
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
@ -65,9 +65,17 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
private IRateProvider CreateExchangeRateProvider(BTCPayNetwork network, string exchange)
|
||||
{
|
||||
List<IRateProvider> providers = new List<IRateProvider>();
|
||||
|
||||
if(exchange == "quadrigacx")
|
||||
{
|
||||
providers.Add(new QuadrigacxRateProvider(network.CryptoCode));
|
||||
}
|
||||
|
||||
var coinAverage = new CoinAverageRateProviderDescription(network.CryptoCode).CreateRateProvider(serviceProvider);
|
||||
coinAverage.Exchange = exchange;
|
||||
return coinAverage;
|
||||
providers.Add(coinAverage);
|
||||
return new FallbackRateProvider(providers.ToArray());
|
||||
}
|
||||
|
||||
private CachedRateProvider CreateCachedRateProvider(BTCPayNetwork network, IRateProvider rateProvider, string additionalScope)
|
||||
|
@ -183,13 +183,32 @@ namespace BTCPayServer.Services.Rates
|
||||
var jobj = JObject.Parse(await resp.Content.ReadAsStringAsync());
|
||||
var response = new GetRateLimitsResponse();
|
||||
response.CounterReset = TimeSpan.FromSeconds(jobj["counter_reset"].Value<int>());
|
||||
var totalPeriod = jobj["total_period"].Value<string>();
|
||||
if (totalPeriod == "24h")
|
||||
{
|
||||
response.TotalPeriod = TimeSpan.FromHours(24);
|
||||
}
|
||||
else if (totalPeriod == "30d")
|
||||
{
|
||||
response.TotalPeriod = TimeSpan.FromDays(30);
|
||||
}
|
||||
else
|
||||
{
|
||||
response.TotalPeriod = TimeSpan.FromSeconds(jobj["total_period"].Value<int>());
|
||||
}
|
||||
response.RequestsLeft = jobj["requests_left"].Value<int>();
|
||||
response.RequestsPerPeriod = jobj["requests_per_period"].Value<int>();
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<GetExchangeTickersResponse> GetExchangeTickersAsync()
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "https://apiv2.bitcoinaverage.com/symbols/exchanges/ticker");
|
||||
var auth = Authenticator;
|
||||
if (auth != null)
|
||||
{
|
||||
await auth.AddHeader(request);
|
||||
}
|
||||
var resp = await _Client.SendAsync(request);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
var jobj = JObject.Parse(await resp.Content.ReadAsStringAsync());
|
||||
@ -213,5 +232,7 @@ namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public TimeSpan CounterReset { get; set; }
|
||||
public int RequestsLeft { get; set; }
|
||||
public int RequestsPerPeriod { get; set; }
|
||||
public TimeSpan TotalPeriod { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,18 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class CoinAverageSettingsAuthenticator : ICoinAverageAuthenticator
|
||||
{
|
||||
CoinAverageSettings _Settings;
|
||||
public CoinAverageSettingsAuthenticator(CoinAverageSettings settings)
|
||||
{
|
||||
_Settings = settings;
|
||||
}
|
||||
public Task AddHeader(HttpRequestMessage message)
|
||||
{
|
||||
return _Settings.AddHeader(message);
|
||||
}
|
||||
}
|
||||
public class CoinAverageSettings : ICoinAverageAuthenticator
|
||||
{
|
||||
private static readonly DateTime _epochUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
@ -15,6 +27,71 @@ namespace BTCPayServer.Services.Rates
|
||||
public (String PublicKey, String PrivateKey)? KeyPair { get; set; }
|
||||
public (String DisplayName, String Name)[] AvailableExchanges { get; set; } = Array.Empty<(String DisplayName, String Name)>();
|
||||
|
||||
public CoinAverageSettings()
|
||||
{
|
||||
//GENERATED BY:
|
||||
//StringBuilder b = new StringBuilder();
|
||||
//b.AppendLine("_coinAverageSettings.AvailableExchanges = new[] {");
|
||||
//foreach (var availableExchange in _coinAverageSettings.AvailableExchanges)
|
||||
//{
|
||||
// b.AppendLine($"(DisplayName: \"{availableExchange.DisplayName}\", Name: \"{availableExchange.Name}\"),");
|
||||
//}
|
||||
//b.AppendLine("}.ToArray()");
|
||||
|
||||
AvailableExchanges = new[] {
|
||||
(DisplayName: "BitBargain", Name: "bitbargain"),
|
||||
(DisplayName: "Tidex", Name: "tidex"),
|
||||
(DisplayName: "LocalBitcoins", Name: "localbitcoins"),
|
||||
(DisplayName: "EtherDelta", Name: "etherdelta"),
|
||||
(DisplayName: "Kraken", Name: "kraken"),
|
||||
(DisplayName: "BitBay", Name: "bitbay"),
|
||||
(DisplayName: "Independent Reserve", Name: "independentreserve"),
|
||||
(DisplayName: "Exmoney", Name: "exmoney"),
|
||||
(DisplayName: "Bitcoin.co.id", Name: "bitcoin_co_id"),
|
||||
(DisplayName: "Huobi", Name: "huobi"),
|
||||
(DisplayName: "GDAX", Name: "gdax"),
|
||||
(DisplayName: "Coincheck", Name: "coincheck"),
|
||||
(DisplayName: "Bittylicious", Name: "bittylicious"),
|
||||
(DisplayName: "Gemini", Name: "gemini"),
|
||||
(DisplayName: "QuadrigaCX", Name: "quadrigacx"),
|
||||
(DisplayName: "Bit2C", Name: "bit2c"),
|
||||
(DisplayName: "Luno", Name: "luno"),
|
||||
(DisplayName: "Negocie Coins", Name: "negociecoins"),
|
||||
(DisplayName: "FYB-SE", Name: "fybse"),
|
||||
(DisplayName: "Hitbtc", Name: "hitbtc"),
|
||||
(DisplayName: "Bitex.la", Name: "bitex"),
|
||||
(DisplayName: "Korbit", Name: "korbit"),
|
||||
(DisplayName: "itBit", Name: "itbit"),
|
||||
(DisplayName: "Okex", Name: "okex"),
|
||||
(DisplayName: "Bitsquare", Name: "bitsquare"),
|
||||
(DisplayName: "Bitfinex", Name: "bitfinex"),
|
||||
(DisplayName: "CoinMate", Name: "coinmate"),
|
||||
(DisplayName: "Bitstamp", Name: "bitstamp"),
|
||||
(DisplayName: "Cryptonit", Name: "cryptonit"),
|
||||
(DisplayName: "Foxbit", Name: "foxbit"),
|
||||
(DisplayName: "QuickBitcoin", Name: "quickbitcoin"),
|
||||
(DisplayName: "Poloniex", Name: "poloniex"),
|
||||
(DisplayName: "Bit-Z", Name: "bitz"),
|
||||
(DisplayName: "Liqui", Name: "liqui"),
|
||||
(DisplayName: "BitKonan", Name: "bitkonan"),
|
||||
(DisplayName: "Kucoin", Name: "kucoin"),
|
||||
(DisplayName: "Binance", Name: "binance"),
|
||||
(DisplayName: "Rock Trading", Name: "rocktrading"),
|
||||
(DisplayName: "Mercado Bitcoin", Name: "mercado"),
|
||||
(DisplayName: "Coinsecure", Name: "coinsecure"),
|
||||
(DisplayName: "Coinfloor", Name: "coinfloor"),
|
||||
(DisplayName: "bitFlyer", Name: "bitflyer"),
|
||||
(DisplayName: "BTCTurk", Name: "btcturk"),
|
||||
(DisplayName: "Bittrex", Name: "bittrex"),
|
||||
(DisplayName: "CampBX", Name: "campbx"),
|
||||
(DisplayName: "Zaif", Name: "zaif"),
|
||||
(DisplayName: "FYB-SG", Name: "fybsg"),
|
||||
(DisplayName: "Quoine", Name: "quoine"),
|
||||
(DisplayName: "BTC Markets", Name: "btcmarkets"),
|
||||
(DisplayName: "Bitso", Name: "bitso"),
|
||||
}.ToArray();
|
||||
}
|
||||
|
||||
public Task AddHeader(HttpRequestMessage message)
|
||||
{
|
||||
var signature = GetCoinAverageSignature();
|
||||
|
65
BTCPayServer/Services/Rates/QuadrigacxRateProvider.cs
Normal file
65
BTCPayServer/Services/Rates/QuadrigacxRateProvider.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class QuadrigacxRateProvider : IRateProvider
|
||||
{
|
||||
public QuadrigacxRateProvider(string crypto)
|
||||
{
|
||||
CryptoCode = crypto;
|
||||
}
|
||||
public string CryptoCode { get; set; }
|
||||
static HttpClient _Client = new HttpClient();
|
||||
public async Task<decimal> GetRateAsync(string currency)
|
||||
{
|
||||
return await GetRatesAsyncCore(CryptoCode, currency);
|
||||
}
|
||||
|
||||
private async Task<decimal> GetRatesAsyncCore(string cryptoCode, string currency)
|
||||
{
|
||||
var response = await _Client.GetAsync($"https://api.quadrigacx.com/v2/ticker?book={cryptoCode.ToLowerInvariant()}_{currency.ToLowerInvariant()}");
|
||||
response.EnsureSuccessStatusCode();
|
||||
var rates = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||
if (!TryToDecimal(rates, out var result))
|
||||
throw new RateUnavailableException(currency);
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool TryToDecimal(JObject p, out decimal v)
|
||||
{
|
||||
v = 0.0m;
|
||||
JToken token = p.Property("bid")?.Value;
|
||||
if (token == null)
|
||||
return false;
|
||||
return decimal.TryParse(token.Value<string>(), System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out v);
|
||||
}
|
||||
|
||||
public async Task<ICollection<Rate>> GetRatesAsync()
|
||||
{
|
||||
var response = await _Client.GetAsync($"https://api.quadrigacx.com/v2/ticker?book=all");
|
||||
response.EnsureSuccessStatusCode();
|
||||
var rates = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||
|
||||
List<Rate> result = new List<Rate>();
|
||||
foreach (var prop in rates.Properties())
|
||||
{
|
||||
var rate = new Rate();
|
||||
var splitted = prop.Name.Split('_');
|
||||
var crypto = splitted[0].ToUpperInvariant();
|
||||
if (crypto != CryptoCode)
|
||||
continue;
|
||||
rate.Currency = splitted[1].ToUpperInvariant();
|
||||
TryToDecimal((JObject)prop.Value, out var v);
|
||||
rate.Value = v;
|
||||
result.Add(rate);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
17
BTCPayServer/Services/ThemesSettings.cs
Normal file
17
BTCPayServer/Services/ThemesSettings.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Services
|
||||
{
|
||||
public class ThemeSettings
|
||||
{
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||
public string BootstrapCssUri { get; set; }
|
||||
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||
public string CreativeStartCssUri { get; set; }
|
||||
}
|
||||
}
|
@ -29,6 +29,10 @@
|
||||
<input asp-for="Currency" class="form-control" />
|
||||
<span asp-validation-for="Currency" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ShowCustomAmount"></label>
|
||||
<input asp-for="ShowCustomAmount" type="checkbox" class="form-check" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Template" class="control-label"></label>*
|
||||
<textarea asp-for="Template" rows="20" cols="40" class="form-control"></textarea>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<h1 class="mb-4">@Model.Title</h1>
|
||||
<form method="post">
|
||||
<div class="row">
|
||||
@foreach (var item in Model.Items)
|
||||
@foreach(var item in Model.Items)
|
||||
{
|
||||
<div class="col-sm-4 mb-3">
|
||||
<h3>@item.Title</h3>
|
||||
@ -28,18 +28,20 @@
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
@*<div class="row mt-4">
|
||||
@if(Model.ShowCustomAmount)
|
||||
{
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-4 offset-md-4 col-sm-6 offset-sm-3">
|
||||
<h3>Something else</h3>
|
||||
<form data-buy>
|
||||
<form method="post" data-buy>
|
||||
<div class="input-group">
|
||||
<input class="form-control" type="number" step="0.00001" name="amount" placeholder="undefined (optional)"><div class="input-group-append">
|
||||
<input class="form-control" type="number" min="0" step="@Model.Step" name="amount" placeholder="amount"><div class="input-group-append">
|
||||
<button class="btn btn-primary" type="submit">Pay</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>*@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<script src="~/vendor/jquery/jquery.js"></script>
|
||||
|
@ -6,8 +6,9 @@
|
||||
|
||||
<header class="masthead">
|
||||
<div class="header-content">
|
||||
<div class="header-content-inner">
|
||||
<h1 id="homeHeading" style="padding-bottom:30px;">Welcome to BTCPay Server</h1>
|
||||
<div class="header-content-inner text-white">
|
||||
<h1>Welcome to BTCPay Server</h1>
|
||||
<hr />
|
||||
<p>BTCPay Server is a free and open source server for merchants wanting to accept Bitcoin for their business. The API is compatible with Bitpay service to allow seamless migration.</p>
|
||||
<a style="background-color: #fff;color: #222;display:inline-block;text-align: center;white-space: nowrap;vertical-align: middle;user-select: none;line-height: 1.25;font-size: 1rem;text-decoration:none;font-weight: 700; text-transform: uppercase;border: none;border-radius: 300px;padding: 15px 30px;" href="https://github.com/btcpayserver/btcpayserver-doc">Getting started</a>
|
||||
</div>
|
||||
@ -28,21 +29,21 @@
|
||||
<div class="col-lg-4 col-md-6 text-center">
|
||||
<div class="service-box">
|
||||
<img src="~/img/lock-logo.png" />
|
||||
<h3>Secure</h3>
|
||||
<h3 class="text-dark">Secure</h3>
|
||||
<p class="text-muted">The payment server does not need to know your private keys, so your money can't be stolen.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6 text-center">
|
||||
<div class="service-box">
|
||||
<img src="~/img/qr-logo.png" />
|
||||
<h3>Easy</h3>
|
||||
<h3 class="text-dark">Easy</h3>
|
||||
<p class="text-muted">A user-friendly Bitcoin checkout page for your customers.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6 text-center">
|
||||
<div class="service-box">
|
||||
<img src="~/img/money-logo.png" />
|
||||
<h3>Visibility</h3>
|
||||
<h3 class="text-dark">Visibility</h3>
|
||||
<p class="text-muted">Manage, generate reports, and search for your invoices easily.</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -52,7 +53,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="call-to-action bg-dark">
|
||||
<div class="call-to-action bg-dark text-white">
|
||||
<div class="container text-center">
|
||||
<h2>Video tutorials</h2>
|
||||
<div class="row">
|
||||
@ -102,12 +103,12 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="call-to-action bg-dark">
|
||||
<div class="call-to-action bg-dark text-white">
|
||||
<div class="container text-center">
|
||||
<h2>Donate</h2>
|
||||
<p>Donation to this address will be reinvested into the development of this tool</p>
|
||||
<img src="~/img/donation.jpg">
|
||||
<p style="font-size:10px">3BpfdkF93GwFRWdrAN3SNsRAsi6d158YQi</p>
|
||||
<p><img src="~/img/donation.jpg"></p>
|
||||
<p>3BpfdkF93GwFRWdrAN3SNsRAsi6d158YQi</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -117,7 +118,10 @@
|
||||
<div class="col-lg-8 mx-auto text-center">
|
||||
<h2 class="section-heading">Let's Get In Touch!</h2>
|
||||
<hr class="primary">
|
||||
<p>An open source project is nothing without its community, come and get in touch with us!</p>
|
||||
<p>
|
||||
An open source project is nothing without its community<br />
|
||||
Come and get in touch with us!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@ -4,15 +4,21 @@
|
||||
}
|
||||
|
||||
<style type="text/css">
|
||||
.overflowbox {
|
||||
max-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
.linethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.money {
|
||||
text-align: right;
|
||||
.smMaxWidth {
|
||||
max-width: 200px;
|
||||
}
|
||||
@@media (min-width: 768px) {
|
||||
.smMaxWidth {
|
||||
max-width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
.firstCol {
|
||||
width: 140px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -29,14 +35,13 @@
|
||||
<div class="col-lg-12 text-center">
|
||||
<h2 class="section-heading">@ViewData["Title"]</h2>
|
||||
<hr class="primary">
|
||||
<p>Invoice details</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h3>Information</h3>
|
||||
<table class="table table-sm">
|
||||
<table class="table table-sm table-responsive-md">
|
||||
<tr>
|
||||
<th>Store</th>
|
||||
<td><a href="@Model.StoreLink">@Model.StoreName</a></td>
|
||||
@ -94,10 +99,9 @@
|
||||
|
||||
<div class="col-md-6">
|
||||
<h3>Buyer information</h3>
|
||||
<table class="table table-sm">
|
||||
<table class="table table-sm table-responsive-md">
|
||||
<tr>
|
||||
<th>Name
|
||||
<th>
|
||||
<th>Name</th>
|
||||
<td>@Model.BuyerInformation.BuyerName</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -135,7 +139,7 @@
|
||||
</table>
|
||||
|
||||
<h3>Product information</h3>
|
||||
<table class="table table-sm">
|
||||
<table class="table table-sm table-responsive-md">
|
||||
<tr>
|
||||
<th>Item code</th>
|
||||
<td>@Model.ProductInformation.ItemCode</td>
|
||||
@ -154,54 +158,58 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3>Paid summary</h3>
|
||||
<table class="table table-sm">
|
||||
<table class="table table-sm table-responsive-md">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th style="white-space:nowrap;">Payment method</th>
|
||||
<th>Payment method</th>
|
||||
<th>Address</th>
|
||||
<th class="money">Rate</th>
|
||||
<th class="money">Paid</th>
|
||||
<th class="money">Due</th>
|
||||
<th class="text-right">Rate</th>
|
||||
<th class="text-right">Paid</th>
|
||||
<th class="text-right">Due</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(var payment in Model.CryptoPayments)
|
||||
@foreach (var payment in Model.CryptoPayments)
|
||||
{
|
||||
<tr>
|
||||
<td>@payment.PaymentMethod</td>
|
||||
<td>@payment.Address</td>
|
||||
<td class="money">@payment.Rate</td>
|
||||
<td class="money">@payment.Paid</td>
|
||||
<td class="money">@payment.Due</td>
|
||||
<td class="text-right">@payment.Rate</td>
|
||||
<td class="text-right">@payment.Paid</td>
|
||||
<td class="text-right">@payment.Due</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@if(Model.OnChainPayments.Count > 0)
|
||||
@if (Model.OnChainPayments.Count > 0)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3>On-Chain payments</h3>
|
||||
<table class="table table-sm">
|
||||
<table class="table table-sm table-responsive-lg">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th style="white-space:nowrap;">Crypto</th>
|
||||
<th>Crypto</th>
|
||||
<th>Deposit address</th>
|
||||
<th>Transaction Id</th>
|
||||
<th style="text-align:right;">Confirmations</th>
|
||||
<th class="text-right">Confirmations</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(var payment in Model.OnChainPayments)
|
||||
@foreach (var payment in Model.OnChainPayments)
|
||||
{
|
||||
var replaced = payment.Replaced ? "text-decoration: line-through;" : "";
|
||||
<tr>
|
||||
<td style="@replaced">@payment.Crypto</td>
|
||||
<td style="@replaced">@payment.DepositAddress</td>
|
||||
<td style="@replaced"><a href="@payment.TransactionLink" target="_blank">@payment.TransactionId</a></td>
|
||||
<td style="text-align:right;">@payment.Confirmations</td>
|
||||
var replaced = payment.Replaced ? "class='linethrough'" : "";
|
||||
<tr @replaced>
|
||||
<td>@payment.Crypto</td>
|
||||
<td>@payment.DepositAddress</td>
|
||||
<td class="smMaxWidth text-truncate">
|
||||
<a href="@payment.TransactionLink" target="_blank">
|
||||
@payment.TransactionId
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-right">@payment.Confirmations</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
@ -209,24 +217,24 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if(Model.OffChainPayments.Count > 0)
|
||||
@if (Model.OffChainPayments.Count > 0)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3>Off-Chain payments</h3>
|
||||
<table class="table table-sm">
|
||||
<table class="table table-sm table-responsive-md">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th style="white-space:nowrap;">Crypto</th>
|
||||
<th class="firstCol">Crypto</th>
|
||||
<th>BOLT11</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(var payment in Model.OffChainPayments)
|
||||
@foreach (var payment in Model.OffChainPayments)
|
||||
{
|
||||
<tr>
|
||||
<td style="width:50px;">@payment.Crypto</td>
|
||||
<td style="max-width:100px;overflow:hidden;">@payment.BOLT11</td>
|
||||
<td>@payment.Crypto</td>
|
||||
<td class="smMaxWidth text-truncate">@payment.BOLT11</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
@ -237,20 +245,20 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3>Addresses</h3>
|
||||
<table class="table table-sm">
|
||||
<table class="table table-sm table-responsive-md">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th style="white-space:nowrap;">Payment method</th>
|
||||
<th class="firstCol">Payment method</th>
|
||||
<th>Address</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(var address in Model.Addresses)
|
||||
@foreach (var address in Model.Addresses)
|
||||
{
|
||||
var current = address.Current ? "font-weight: bold;" : "";
|
||||
var current = address.Current ? "font-weight-bold" : "";
|
||||
<tr>
|
||||
<td style="width:100px;@current">@address.PaymentMethod</td>
|
||||
<td style="max-width:100px;overflow:hidden;@current">@address.Destination</td>
|
||||
<td>@address.PaymentMethod</td>
|
||||
<td class="smMaxWidth text-truncate @current">@address.Destination</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
@ -261,7 +269,7 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3>Events</h3>
|
||||
<table class="table table-sm">
|
||||
<table class="table table-sm table-responsive-md">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
@ -269,7 +277,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(var evt in Model.Events)
|
||||
@foreach (var evt in Model.Events)
|
||||
{
|
||||
<tr>
|
||||
<td>@evt.Timestamp</td>
|
||||
|
@ -26,6 +26,9 @@
|
||||
<li><b>storeid:id</b> for filtering a specific store</li>
|
||||
<li><b>status:(expired|invalid|complete|confirmed|paid|new)</b> for filtering a specific status</li>
|
||||
</ul>
|
||||
<p>
|
||||
If you want two confirmed and complete invoices, duplicate the filter: `status:confirmed status:complete`.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<form asp-action="SearchInvoice" method="post">
|
||||
|
@ -1,7 +1,6 @@
|
||||
@model ChangePasswordViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Change password";
|
||||
ViewData.AddActivePage(ManageNavPages.ChangePassword);
|
||||
ViewData.SetActivePageAndTitle(ManageNavPages.ChangePassword, "Change password");
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
|
@ -1,6 +1,5 @@
|
||||
@{
|
||||
ViewData["Title"] = "Disable two-factor authentication (2FA)";
|
||||
ViewData.AddActivePage(ManageNavPages.TwoFactorAuthentication);
|
||||
ViewData.SetActivePageAndTitle(ManageNavPages.TwoFactorAuthentication, "Disable two-factor authentication (2FA)");
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
|
@ -1,7 +1,6 @@
|
||||
@model EnableAuthenticatorViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Enable authenticator";
|
||||
ViewData.AddActivePage(ManageNavPages.TwoFactorAuthentication);
|
||||
ViewData.SetActivePageAndTitle(ManageNavPages.TwoFactorAuthentication, "Enable authenticator");
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
|
@ -1,7 +1,6 @@
|
||||
@model ExternalLoginsViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Manage your external logins";
|
||||
ViewData.AddActivePage(ManageNavPages.ExternalLogins);
|
||||
ViewData.SetActivePageAndTitle(ManageNavPages.ExternalLogins, "Manage your external logins");
|
||||
}
|
||||
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
|
@ -1,7 +1,6 @@
|
||||
@model GenerateRecoveryCodesViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Recovery codes";
|
||||
ViewData.AddActivePage(ManageNavPages.TwoFactorAuthentication);
|
||||
ViewData.SetActivePageAndTitle(ManageNavPages.TwoFactorAuthentication, "Recovery codes");
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
|
@ -1,7 +1,6 @@
|
||||
@model IndexViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Profile";
|
||||
ViewData.AddActivePage(ManageNavPages.Index);
|
||||
ViewData.SetActivePageAndTitle(ManageNavPages.Index, "Profile");
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
|
@ -2,41 +2,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
|
||||
namespace BTCPayServer.Views.Manage
|
||||
{
|
||||
public static class ManageNavPages
|
||||
public enum ManageNavPages
|
||||
{
|
||||
public static string ActivePageKey => "ActivePage";
|
||||
|
||||
public static string Index => "Index";
|
||||
|
||||
public static string ChangePassword => "ChangePassword";
|
||||
|
||||
public static string ExternalLogins => "ExternalLogins";
|
||||
|
||||
public static string TwoFactorAuthentication => "TwoFactorAuthentication";
|
||||
|
||||
public static string Tokens => "Tokens";
|
||||
|
||||
public static string TokensNavClass(ViewContext viewContext) => PageNavClass(viewContext, Tokens);
|
||||
|
||||
public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index);
|
||||
|
||||
public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword);
|
||||
|
||||
public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins);
|
||||
|
||||
public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication);
|
||||
|
||||
public static string PageNavClass(ViewContext viewContext, string page)
|
||||
{
|
||||
var activePage = viewContext.ViewData["ActivePage"] as string;
|
||||
return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null;
|
||||
}
|
||||
|
||||
public static void AddActivePage(this ViewDataDictionary viewData, string activePage) => viewData[ActivePageKey] = activePage;
|
||||
Index, ChangePassword, ExternalLogins, TwoFactorAuthentication, Tokens
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
@{
|
||||
ViewData["Title"] = "Reset authenticator key";
|
||||
ViewData.AddActivePage(ManageNavPages.TwoFactorAuthentication);
|
||||
ViewData.SetActivePageAndTitle(ManageNavPages.TwoFactorAuthentication, "Reset authenticator key");
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
|
@ -1,7 +1,6 @@
|
||||
@model SetPasswordViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Set password";
|
||||
ViewData.AddActivePage(ManageNavPages.ChangePassword);
|
||||
ViewData.SetActivePageAndTitle(ManageNavPages.ChangePassword, "Set password");
|
||||
}
|
||||
|
||||
<h4>Set your password</h4>
|
||||
|
@ -1,7 +1,6 @@
|
||||
@model TwoFactorAuthenticationViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Two-factor authentication";
|
||||
ViewData.AddActivePage(ManageNavPages.TwoFactorAuthentication);
|
||||
ViewData.SetActivePageAndTitle(ManageNavPages.TwoFactorAuthentication, "Two-factor authentication");
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
|
@ -1,16 +1,15 @@
|
||||
@using BTCPayServer.Views.Manage
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@{
|
||||
var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any();
|
||||
}
|
||||
|
||||
<div class="nav flex-column nav-pills">
|
||||
<a class="nav-link @ManageNavPages.IndexNavClass(ViewContext)" asp-action="Index">Profile</a>
|
||||
<a class="nav-link @ManageNavPages.ChangePasswordNavClass(ViewContext)" asp-action="ChangePassword">Password</a>
|
||||
@if(hasExternalLogins)
|
||||
<a class="nav-link @ViewData.IsActivePage(ManageNavPages.Index)" asp-action="Index">Profile</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(ManageNavPages.ChangePassword)" asp-action="ChangePassword">Password</a>
|
||||
@if (hasExternalLogins)
|
||||
{
|
||||
<a class="nav-link @ManageNavPages.ExternalLoginsNavClass(ViewContext)" asp-action="ExternalLogins">External logins</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(ManageNavPages.ExternalLogins)" asp-action="ExternalLogins">External logins</a>
|
||||
}
|
||||
<a class="nav-link @ManageNavPages.TwoFactorAuthenticationNavClass(ViewContext)" asp-action="TwoFactorAuthentication">Two-factor authentication</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(ManageNavPages.TwoFactorAuthentication)" asp-action="TwoFactorAuthentication">Two-factor authentication</a>
|
||||
</div>
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
@model EmailsViewModel
|
||||
@{
|
||||
ViewData["Title"] = ServerNavPages.Emails;
|
||||
ViewData.AddActivePage(ServerNavPages.Emails);
|
||||
ViewData.SetActivePageAndTitle(ServerNavPages.Emails);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
@model UsersViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Users";
|
||||
ViewData.AddActivePage(ServerNavPages.Users);
|
||||
ViewData.SetActivePageAndTitle(ServerNavPages.Users);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,19 +1,18 @@
|
||||
@model BTCPayServer.Services.PoliciesSettings
|
||||
@{
|
||||
ViewData["Title"] = ServerNavPages.Policies;
|
||||
ViewData.AddActivePage(ServerNavPages.Policies);
|
||||
ViewData.SetActivePageAndTitle(ServerNavPages.Policies);
|
||||
}
|
||||
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", TempData["StatusMessage"])
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="col-lg-6">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="col-lg-6">
|
||||
<form method="post">
|
||||
<div class="form-group">
|
||||
<label asp-for="RequiresConfirmedEmail"></label>
|
||||
|
@ -1,7 +1,6 @@
|
||||
@model RatesViewModel
|
||||
@{
|
||||
ViewData["Title"] = ServerNavPages.Rates;
|
||||
ViewData.AddActivePage(ServerNavPages.Rates);
|
||||
ViewData.SetActivePageAndTitle(ServerNavPages.Rates);
|
||||
}
|
||||
|
||||
|
||||
@ -40,12 +39,16 @@
|
||||
<h5>Current Bitcoin Average Quotas:</h5>
|
||||
<table class="table table-sm">
|
||||
<tr>
|
||||
<th>Requests left</th>
|
||||
<td>@Model.RateLimits.RequestsLeft</td>
|
||||
<th>Quota period</th>
|
||||
<td>@Model.RateLimits.TotalPeriod.Prettify()</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Requests quota</th>
|
||||
<td>@Model.RateLimits.RequestsLeft/@Model.RateLimits.RequestsPerPeriod</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Quota reset in</th>
|
||||
<td>@Model.RateLimits.CounterReset</td>
|
||||
<td>@Model.RateLimits.CounterReset.Prettify()</td>
|
||||
</tr>
|
||||
</table>
|
||||
}
|
||||
|
@ -2,37 +2,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
|
||||
namespace BTCPayServer.Views.Server
|
||||
{
|
||||
public static class ServerNavPages
|
||||
public enum ServerNavPages
|
||||
{
|
||||
public static string ActivePageKey => "ActivePage";
|
||||
public static string Index => "Index";
|
||||
|
||||
|
||||
public static string Users => "Users";
|
||||
public static string Rates => "Rates";
|
||||
public static string Emails => "Email server";
|
||||
public static string Policies => "Policies";
|
||||
public static string Hangfire => "Hangfire";
|
||||
|
||||
public static string UsersNavClass(ViewContext viewContext) => PageNavClass(viewContext, Users);
|
||||
public static string EmailsNavClass(ViewContext viewContext) => PageNavClass(viewContext, Emails);
|
||||
public static string RatesNavClass(ViewContext viewContext) => PageNavClass(viewContext, Rates);
|
||||
public static string PoliciesNavClass(ViewContext viewContext) => PageNavClass(viewContext, Policies);
|
||||
public static string HangfireNavClass(ViewContext viewContext) => PageNavClass(viewContext, Hangfire);
|
||||
|
||||
public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index);
|
||||
|
||||
public static string PageNavClass(ViewContext viewContext, string page)
|
||||
{
|
||||
var activePage = viewContext.ViewData["ActivePage"] as string;
|
||||
return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null;
|
||||
}
|
||||
|
||||
public static void AddActivePage(this ViewDataDictionary viewData, string activePage) => viewData[ActivePageKey] = activePage;
|
||||
Index, Users, Rates, Emails, Policies, Theme, Hangfire
|
||||
}
|
||||
}
|
||||
|
43
BTCPayServer/Views/Server/Theme.cshtml
Normal file
43
BTCPayServer/Views/Server/Theme.cshtml
Normal file
@ -0,0 +1,43 @@
|
||||
@model BTCPayServer.Services.ThemeSettings
|
||||
@{
|
||||
ViewData.SetActivePageAndTitle(ServerNavPages.Theme);
|
||||
}
|
||||
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", TempData["StatusMessage"])
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<form method="post">
|
||||
<div class="form-group">
|
||||
<label asp-for="BootstrapCssUri"></label>
|
||||
<input asp-for="BootstrapCssUri" class="form-control" />
|
||||
<span asp-validation-for="BootstrapCssUri" class="text-danger"></span>
|
||||
<p class="form-text text-muted">
|
||||
<a href="https://bootstrap.build/app/v4.0/" target="_blank">Build your own theme</a>
|
||||
or <a href="https://bootswatch.com/" target="_blank">pick one already made</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="CreativeStartCssUri"></label>
|
||||
<input asp-for="CreativeStartCssUri" class="form-control" />
|
||||
<span asp-validation-for="CreativeStartCssUri" class="text-danger"></span>
|
||||
<p class="form-text text-muted">
|
||||
<a href="https://startbootstrap.com/template-overviews/creative/" target="_blank">Creative Start theme</a>
|
||||
is used on top of Bootstrap
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@await Html.PartialAsync("_ValidationScriptsPartial")
|
||||
}
|
@ -1,11 +1,10 @@
|
||||
@model UserViewModel
|
||||
@{
|
||||
ViewData["Title"] = Model.Email;
|
||||
ViewData.AddActivePage(ServerNavPages.Users);
|
||||
ViewData.SetActivePageAndTitle(ServerNavPages.Users);
|
||||
}
|
||||
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
<h4>Modify User - @Model.Email</h4>
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
@using BTCPayServer.Views.Server
|
||||
|
||||
<div class="nav flex-column nav-pills">
|
||||
<a class="nav-link @ServerNavPages.UsersNavClass(ViewContext)" asp-action="Users">Users</a>
|
||||
<a class="nav-link @ServerNavPages.RatesNavClass(ViewContext)" asp-action="Rates">Rates</a>
|
||||
<a class="nav-link @ServerNavPages.EmailsNavClass(ViewContext)" asp-action="Emails">Email server</a>
|
||||
<a class="nav-link @ServerNavPages.PoliciesNavClass(ViewContext)" asp-action="Policies">Policies</a>
|
||||
<a class="nav-link @ServerNavPages.HangfireNavClass(ViewContext)" href="~/hangfire" target="_blank">Hangfire</a>
|
||||
<div class="nav flex-column nav-pills">
|
||||
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Users)" asp-action="Users">Users</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Rates)" asp-action="Rates">Rates</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Emails)" asp-action="Emails">Email server</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Policies)" asp-action="Policies">Policies</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Theme)" asp-action="Theme">Theme</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Hangfire)" href="~/hangfire" target="_blank">Hangfire</a>
|
||||
</div>
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
@inject RoleManager<IdentityRole> RoleManager
|
||||
@inject BTCPayServer.Services.BTCPayServerEnvironment env
|
||||
@inject BTCPayServer.HostedServices.NBXplorerDashboard dashboard
|
||||
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
|
||||
@addTagHelper *, Meziantou.AspNetCore.BundleTagHelpers
|
||||
|
||||
<!DOCTYPE html>
|
||||
@ -17,6 +18,9 @@
|
||||
<title>BTCPay Server</title>
|
||||
|
||||
@* CSS *@
|
||||
<link href="@themeManager.BootstrapUri" rel="stylesheet" />
|
||||
<link href="@themeManager.CreativeStartUri" rel="stylesheet" />
|
||||
|
||||
<bundle name="wwwroot/bundles/main-bundle.min.css" />
|
||||
|
||||
@* JS *@
|
||||
@ -48,25 +52,25 @@
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
@if(SignInManager.IsSignedIn(User))
|
||||
{
|
||||
@if(User.IsInRole(Roles.ServerAdmin))
|
||||
{
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Server" asp-action="ListUsers" class="nav-link js-scroll-trigger">Server settings</a></li>
|
||||
}
|
||||
<li class="nav-item"><a asp-area="" asp-controller="UserStores" asp-action="ListStores" class="nav-link js-scroll-trigger">Stores</a></li>
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Apps" asp-action="ListApps" class="nav-link js-scroll-trigger">Apps</a></li>
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Invoice" asp-action="ListInvoices" class="nav-link js-scroll-trigger">Invoices</a></li>
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Manage" asp-action="Index" title="Manage" class="nav-link js-scroll-trigger">My settings</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Account" asp- asp-action="Logout" title="Manage" class="nav-link js-scroll-trigger">Log out</a>
|
||||
</li>}
|
||||
else
|
||||
{
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Account" asp-action="Register" class="nav-link js-scroll-trigger">Register</a></li>
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Account" asp-action="Login" class="nav-link js-scroll-trigger">Log in</a></li>}
|
||||
@if (SignInManager.IsSignedIn(User))
|
||||
{
|
||||
@if (User.IsInRole(Roles.ServerAdmin))
|
||||
{
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Server" asp-action="ListUsers" class="nav-link js-scroll-trigger">Server settings</a></li>
|
||||
}
|
||||
<li class="nav-item"><a asp-area="" asp-controller="UserStores" asp-action="ListStores" class="nav-link js-scroll-trigger">Stores</a></li>
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Apps" asp-action="ListApps" class="nav-link js-scroll-trigger">Apps</a></li>
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Invoice" asp-action="ListInvoices" class="nav-link js-scroll-trigger">Invoices</a></li>
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Manage" asp-action="Index" title="Manage" class="nav-link js-scroll-trigger">My settings</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Account" asp- asp-action="Logout" title="Manage" class="nav-link js-scroll-trigger">Log out</a>
|
||||
</li>}
|
||||
else
|
||||
{
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Account" asp-action="Register" class="nav-link js-scroll-trigger">Register</a></li>
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Account" asp-action="Login" class="nav-link js-scroll-trigger">Log in</a></li>}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
@ -75,8 +79,8 @@ else
|
||||
|
||||
@RenderBody()
|
||||
|
||||
<footer class="bg-dark">
|
||||
<div class="container text-right"><span style="font-size:8px;">@env.ToString()</span></div>
|
||||
<footer class="siteFooter bg-dark text-white">
|
||||
<div class="container text-right">@env.ToString()</div>
|
||||
</footer>
|
||||
|
||||
@if (!dashboard.IsFullySynched())
|
||||
|
@ -38,6 +38,10 @@
|
||||
<label asp-for="AllowCoinConversion"></label>
|
||||
<input asp-for="AllowCoinConversion" type="checkbox" class="form-check" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="RequiresRefundEmail"></label>
|
||||
<input asp-for="RequiresRefundEmail" type="checkbox" class="form-check" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="LightningMaxValue"></label>
|
||||
<input asp-for="LightningMaxValue" class="form-control" />
|
||||
|
26
BTCPayServer/Views/ViewsRazor.cs
Normal file
26
BTCPayServer/Views/ViewsRazor.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
|
||||
namespace BTCPayServer.Views
|
||||
{
|
||||
public static class ViewsRazor
|
||||
{
|
||||
public const string ACTIVE_PAGE_KEY = "ActivePage";
|
||||
public static void SetActivePageAndTitle<T>(this ViewDataDictionary viewData, T activePage, string title = null)
|
||||
where T : IConvertible
|
||||
{
|
||||
viewData["Title"] = title ?? activePage.ToString();
|
||||
viewData[ACTIVE_PAGE_KEY] = activePage;
|
||||
}
|
||||
|
||||
public static string IsActivePage<T>(this ViewDataDictionary viewData, T page)
|
||||
where T : IConvertible
|
||||
{
|
||||
var activePage = (T)viewData[ACTIVE_PAGE_KEY];
|
||||
return page.Equals(activePage) ? "active" : null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@using BTCPayServer
|
||||
@using BTCPayServer.Views
|
||||
@using BTCPayServer.Models
|
||||
@using BTCPayServer.Models.AccountViewModels
|
||||
@using BTCPayServer.Models.InvoicingModels
|
||||
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
"outputFileName": "wwwroot/bundles/main-bundle.min.css",
|
||||
"inputFiles": [
|
||||
"wwwroot/vendor/bootstrap4/css/bootstrap.css",
|
||||
"wwwroot/vendor/bootstrap4-creativestart/Open-Sans.css",
|
||||
"wwwroot/vendor/magnific-popup/magnific-popup.css",
|
||||
"wwwroot/vendor/font-awesome/css/font-awesome.css",
|
||||
"wwwroot/main/**/*.css",
|
||||
@ -18,7 +18,7 @@
|
||||
"wwwroot/vendor/jquery-easing/jquery.easing.js",
|
||||
"wwwroot/vendor/scrollreveal/scrollreveal.min.js",
|
||||
"wwwroot/vendor/magnific-popup/jquery.magnific-popup.js",
|
||||
"wwwroot/main/**/js/*.js"
|
||||
"wwwroot/vendor/bootstrap4-creativestart/*.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -104,14 +104,11 @@ $(document).ready(function () {
|
||||
|
||||
*/
|
||||
|
||||
var display = $(".timer-row__time-left"); // Timer container
|
||||
|
||||
// check if the Document expired
|
||||
if (srvModel.expirationSeconds > 0) {
|
||||
progressStart(srvModel.maxTimeSeconds); // Progress bar
|
||||
startTimer(srvModel.expirationSeconds, display); // Timer
|
||||
|
||||
if (!validateEmail(srvModel.customerEmail))
|
||||
if (srvModel.requiresRefundEmail && !validateEmail(srvModel.customerEmail))
|
||||
emailForm(); // Email form Display
|
||||
else
|
||||
hideEmailForm();
|
||||
@ -246,42 +243,21 @@ $(document).ready(function () {
|
||||
$(".single-item-order__right__btc-price__chevron").toggleClass("expanded");
|
||||
});
|
||||
|
||||
// Timer Countdown
|
||||
function startTimer(duration, display) {
|
||||
var timer = duration, minutes, seconds;
|
||||
var timeout = setInterval(function () {
|
||||
minutes = parseInt(timer / 60, 10);
|
||||
seconds = parseInt(timer % 60, 10);
|
||||
|
||||
minutes = minutes < 10 ? "0" + minutes : minutes;
|
||||
seconds = seconds < 10 ? "0" + seconds : seconds;
|
||||
|
||||
display.text(minutes + ":" + seconds);
|
||||
|
||||
if (--timer < 0) {
|
||||
clearInterval(timeout);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Progress bar
|
||||
// Timer Countdown && Progress bar
|
||||
function progressStart(timerMax) {
|
||||
var end = new Date(); // Setup Time Variable, should come from server
|
||||
end.setSeconds(end.getSeconds() + srvModel.expirationSeconds);
|
||||
timerMax *= 1000; // Usually 15 minutes = 9000 second= 900000 ms
|
||||
var timeoutVal = Math.floor(timerMax / 100); // Timeout calc
|
||||
animateUpdate(); //Launch it
|
||||
|
||||
function updateProgress(percentage) {
|
||||
$('.timer-row__progress-bar').css("width", percentage + "%");
|
||||
}
|
||||
|
||||
function animateUpdate() {
|
||||
var now = new Date();
|
||||
var timeDiff = end.getTime() - now.getTime();
|
||||
var perc = 100 - Math.round(timeDiff / timerMax * 100);
|
||||
var status = checkoutCtrl.srvModel.status;
|
||||
|
||||
updateTimer(timeDiff / 1000);
|
||||
|
||||
if (perc === 75 && (status === "paidPartial" || status === "new")) {
|
||||
$(".timer-row").addClass("expiring-soon");
|
||||
checkoutCtrl.expiringSoon = true;
|
||||
@ -289,12 +265,34 @@ $(document).ready(function () {
|
||||
}
|
||||
if (perc <= 100) {
|
||||
updateProgress(perc);
|
||||
|
||||
var timeoutVal = 300; // Timeout calc
|
||||
setTimeout(animateUpdate, timeoutVal);
|
||||
}
|
||||
|
||||
//if (perc >= 100 && status === "expired") {
|
||||
// onDataCallback(status);
|
||||
//}
|
||||
}
|
||||
|
||||
function updateProgress(percentage) {
|
||||
$('.timer-row__progress-bar').css("width", percentage + "%");
|
||||
}
|
||||
|
||||
function updateTimer(timer) {
|
||||
var display = $(".timer-row__time-left");
|
||||
if (timer >= 0) {
|
||||
var minutes = parseInt(timer / 60, 10);
|
||||
minutes = minutes < 10 ? "0" + minutes : minutes;
|
||||
|
||||
var seconds = parseInt(timer % 60, 10);
|
||||
seconds = seconds < 10 ? "0" + seconds : seconds;
|
||||
|
||||
display.text(minutes + ":" + seconds);
|
||||
} else {
|
||||
display.text("00:00");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clipboard Copy
|
||||
|
@ -47,5 +47,8 @@ const locales_is = {
|
||||
"Archived_Body": "Vinsamlegast hafðu samband fyrir upplýsingar eða aðstoð.",
|
||||
// Lightning
|
||||
"BOLT 11 Invoice": "BOLT 11 Reikningur",
|
||||
"Node Info": "Nótu upplýsingar"
|
||||
"Node Info": "Nótu upplýsingar",
|
||||
//
|
||||
"txCount": "{{count}} reikningur",
|
||||
"txCount_plural": "{{count}} reikningar"
|
||||
};
|
||||
|
@ -1,44 +1,16 @@
|
||||
/* Wrapping element */
|
||||
/* Set some basic padding to keep content from hitting the edges */
|
||||
.body-content {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
/* Carousel */
|
||||
.carousel-caption p {
|
||||
font-size: 20px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Make .svg files in the carousel display properly in older browsers */
|
||||
.carousel-inner .item img[src$=".svg"] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Hide/rearrange for smaller screens */
|
||||
@media screen and (max-width: 767px) {
|
||||
/* Hide captions */
|
||||
.carousel-caption {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0 0 18px !important;
|
||||
}
|
||||
|
||||
footer {
|
||||
.siteFooter {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
height: 18px;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
vertical-align: middle;
|
||||
font-size: 8px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
560
BTCPayServer/wwwroot/vendor/bootstrap4-creativestart/Open-Sans.css
vendored
Normal file
560
BTCPayServer/wwwroot/vendor/bootstrap4-creativestart/Open-Sans.css
vendored
Normal file
@ -0,0 +1,560 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hnIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hrIqM.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0Vdc1UAw.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0Wdc1UAw.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhoIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhmIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhvIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhnIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhoIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhkIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhlIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhrIqM.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hmIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hvIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hnIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hoIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hkIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hlIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hrIqM.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OXehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OXOhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OUuhp.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFWJ0bbck.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFUZ0bbck.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFWZ0bbck.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFVp0bbck.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFWp0bbck.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFW50bbck.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFVZ0b.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOUehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOXehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOUuhp.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOX-hpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOVuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOXuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOUehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOXehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOXOhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOUuhp.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOX-hpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOVuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOXuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOUehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOXehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOXOhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOUuhp.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Start Bootstrap - Createive v4.0.0-beta (http://startbootstrap.com/template-overviews/creative)
|
||||
* Start Bootstrap - Creative v4.0.0-beta (http://startbootstrap.com/template-overviews/creative)
|
||||
* Copyright 2013-2017 Start Bootstrap
|
||||
* Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap-creative/blob/master/LICENSE)
|
||||
*/
|
||||
@ -9,7 +9,7 @@ html {
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Merriweather', 'Helvetica Neue', Arial, sans-serif;
|
||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||
}
|
||||
|
||||
hr {
|
||||
@ -22,17 +22,6 @@ hr {
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #329F80;
|
||||
-webkit-transition: all 0.2s;
|
||||
-moz-transition: all 0.2s;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
a:focus, a:hover {
|
||||
color: #0F3723;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
@ -48,19 +37,6 @@ p {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.bg-primary {
|
||||
background-color: #0F3B21 !important;
|
||||
}
|
||||
|
||||
.bg-dark {
|
||||
color: white;
|
||||
background-color: #0F3723 !important;
|
||||
}
|
||||
|
||||
.text-faded {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 100px 0;
|
||||
}
|
||||
@ -195,7 +171,6 @@ header.masthead {
|
||||
width: 100%;
|
||||
min-height: auto;
|
||||
text-align: center;
|
||||
color: white;
|
||||
background-image: url("../../img/bg.png");
|
||||
background-position: center;
|
||||
-webkit-background-size: cover;
|
||||
@ -227,7 +202,6 @@ header.masthead {
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
margin-bottom: 50px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
@ -268,10 +242,6 @@ header.masthead {
|
||||
margin: 50px auto 0;
|
||||
}
|
||||
|
||||
.service-box h3 {
|
||||
color: #0F3B21;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.service-box {
|
||||
margin: 20px auto 0;
|
||||
@ -297,7 +267,6 @@ header.masthead {
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
opacity: 0;
|
||||
color: white;
|
||||
background: rgba(240, 95, 64, 0.9);
|
||||
-webkit-transition: all 0.2s;
|
||||
-moz-transition: all 0.2s;
|
||||
@ -354,100 +323,7 @@ header.masthead {
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: #0F3B21 !important;
|
||||
}
|
||||
|
||||
.no-gutter > [class*='col-'] {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus {
|
||||
color: #fff;
|
||||
background-color: #0F3723;
|
||||
}
|
||||
|
||||
/*.btn-default {
|
||||
color: #222222;
|
||||
border-color: white;
|
||||
background-color: white; }
|
||||
.btn-default.focus, .btn-default:focus {
|
||||
color: #222222;
|
||||
border-color: #bfbfbf;
|
||||
background-color: #e6e6e6; }
|
||||
.btn-default:hover {
|
||||
color: #222222;
|
||||
border-color: #e0e0e0;
|
||||
background-color: #e6e6e6; }
|
||||
.btn-default.active, .btn-default:active,
|
||||
.open > .btn-default.dropdown-toggle {
|
||||
color: #222222;
|
||||
border-color: #e0e0e0;
|
||||
background-color: #e6e6e6; }
|
||||
.btn-default.active.focus, .btn-default.active:focus, .btn-default.active:hover, .btn-default:active.focus, .btn-default:active:focus, .btn-default:active:hover,
|
||||
.open > .btn-default.dropdown-toggle.focus,
|
||||
.open > .btn-default.dropdown-toggle:focus,
|
||||
.open > .btn-default.dropdown-toggle:hover {
|
||||
color: #222222;
|
||||
border-color: #bfbfbf;
|
||||
background-color: #d4d4d4; }
|
||||
.btn-default.active, .btn-default:active,
|
||||
.open > .btn-default.dropdown-toggle {
|
||||
background-image: none; }
|
||||
.btn-default.disabled.focus, .btn-default.disabled:focus, .btn-default.disabled:hover, .btn-default[disabled].focus, .btn-default[disabled]:focus, .btn-default[disabled]:hover,
|
||||
fieldset[disabled] .btn-default.focus,
|
||||
fieldset[disabled] .btn-default:focus,
|
||||
fieldset[disabled] .btn-default:hover {
|
||||
border-color: white;
|
||||
background-color: white; }
|
||||
.btn-default .badge {
|
||||
color: white;
|
||||
background-color: #222222; }
|
||||
|
||||
.btn-primary {
|
||||
color: white;
|
||||
border-color: #0F3B21;
|
||||
background-color: #0F3B21; }
|
||||
.btn-primary.focus, .btn-primary:focus {
|
||||
color: white;
|
||||
border-color: #a4270d;
|
||||
background-color: #eb3812; }
|
||||
.btn-primary:hover {
|
||||
color: white;
|
||||
border-color: #e13612;
|
||||
background-color: #eb3812; }
|
||||
.btn-primary.active, .btn-primary:active,
|
||||
.open > .btn-primary.dropdown-toggle {
|
||||
color: white;
|
||||
border-color: #e13612;
|
||||
background-color: #eb3812; }
|
||||
.btn-primary.active.focus, .btn-primary.active:focus, .btn-primary.active:hover, .btn-primary:active.focus, .btn-primary:active:focus, .btn-primary:active:hover,
|
||||
.open > .btn-primary.dropdown-toggle.focus,
|
||||
.open > .btn-primary.dropdown-toggle:focus,
|
||||
.open > .btn-primary.dropdown-toggle:hover {
|
||||
color: white;
|
||||
border-color: #a4270d;
|
||||
background-color: #c93110; }
|
||||
.btn-primary.active, .btn-primary:active,
|
||||
.open > .btn-primary.dropdown-toggle {
|
||||
background-image: none; }
|
||||
.btn-primary.disabled.focus, .btn-primary.disabled:focus, .btn-primary.disabled:hover, .btn-primary[disabled].focus, .btn-primary[disabled]:focus, .btn-primary[disabled]:hover,
|
||||
fieldset[disabled] .btn-primary.focus,
|
||||
fieldset[disabled] .btn-primary:focus,
|
||||
fieldset[disabled] .btn-primary:hover {
|
||||
border-color: #0F3B21;
|
||||
background-color: #0F3B21; }
|
||||
.btn-primary .badge {
|
||||
color: #0F3B21;
|
||||
background-color: white; }
|
||||
|
||||
.btn {
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
border-radius: 300px;
|
||||
font-family: 'Open Sans', 'Helvetica Neue', Arial, sans-serif; }
|
||||
|
||||
.btn-xl {
|
||||
padding: 15px 30px; }*/
|
@ -1,3 +1,4 @@
|
||||
$primary: #23735c;
|
||||
$primary: #329f80;
|
||||
$secondary: #2284A6;
|
||||
$dark: #0f3b21;
|
||||
$font-size-base: 0.9rem;
|
||||
|
@ -17,15 +17,15 @@
|
||||
--cyan: #17a2b8;
|
||||
--white: #fff;
|
||||
--gray: #6c757d;
|
||||
--gray-dark: #343a40;
|
||||
--primary: #23735c;
|
||||
--gray-dark: #0f3b21;
|
||||
--primary: #329f80;
|
||||
--secondary: #2284A6;
|
||||
--success: #28a745;
|
||||
--info: #17a2b8;
|
||||
--warning: #ffc107;
|
||||
--danger: #dc3545;
|
||||
--light: #f8f9fa;
|
||||
--dark: #343a40;
|
||||
--dark: #0f3b21;
|
||||
--breakpoint-xs: 0;
|
||||
--breakpoint-sm: 576px;
|
||||
--breakpoint-md: 768px;
|
||||
@ -137,7 +137,7 @@ sup {
|
||||
top: -.5em; }
|
||||
|
||||
a {
|
||||
color: #23735c;
|
||||
color: #329f80;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
-webkit-text-decoration-skip: objects; }
|
||||
@ -1635,8 +1635,8 @@ fieldset:disabled a.btn {
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #23735c;
|
||||
border-color: #23735c; }
|
||||
background-color: #329f80;
|
||||
border-color: #329f80; }
|
||||
.btn-primary:hover {
|
||||
color: #fff;
|
||||
background-color: #1a5645;
|
||||
@ -1645,8 +1645,8 @@ fieldset:disabled a.btn {
|
||||
box-shadow: 0 0 0 0.2rem rgba(35, 115, 92, 0.5); }
|
||||
.btn-primary.disabled, .btn-primary:disabled {
|
||||
color: #fff;
|
||||
background-color: #23735c;
|
||||
border-color: #23735c; }
|
||||
background-color: #329f80;
|
||||
border-color: #329f80; }
|
||||
.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,
|
||||
.show > .btn-primary.dropdown-toggle {
|
||||
color: #fff;
|
||||
@ -1796,8 +1796,8 @@ fieldset:disabled a.btn {
|
||||
|
||||
.btn-dark {
|
||||
color: #fff;
|
||||
background-color: #343a40;
|
||||
border-color: #343a40; }
|
||||
background-color: #0f3b21;
|
||||
border-color: #0f3b21; }
|
||||
.btn-dark:hover {
|
||||
color: #fff;
|
||||
background-color: #23272b;
|
||||
@ -1806,8 +1806,8 @@ fieldset:disabled a.btn {
|
||||
box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); }
|
||||
.btn-dark.disabled, .btn-dark:disabled {
|
||||
color: #fff;
|
||||
background-color: #343a40;
|
||||
border-color: #343a40; }
|
||||
background-color: #0f3b21;
|
||||
border-color: #0f3b21; }
|
||||
.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,
|
||||
.show > .btn-dark.dropdown-toggle {
|
||||
color: #fff;
|
||||
@ -1818,24 +1818,24 @@ fieldset:disabled a.btn {
|
||||
box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); }
|
||||
|
||||
.btn-outline-primary {
|
||||
color: #23735c;
|
||||
color: #329f80;
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
border-color: #23735c; }
|
||||
border-color: #329f80; }
|
||||
.btn-outline-primary:hover {
|
||||
color: #fff;
|
||||
background-color: #23735c;
|
||||
border-color: #23735c; }
|
||||
background-color: #329f80;
|
||||
border-color: #329f80; }
|
||||
.btn-outline-primary:focus, .btn-outline-primary.focus {
|
||||
box-shadow: 0 0 0 0.2rem rgba(35, 115, 92, 0.5); }
|
||||
.btn-outline-primary.disabled, .btn-outline-primary:disabled {
|
||||
color: #23735c;
|
||||
color: #329f80;
|
||||
background-color: transparent; }
|
||||
.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,
|
||||
.show > .btn-outline-primary.dropdown-toggle {
|
||||
color: #fff;
|
||||
background-color: #23735c;
|
||||
border-color: #23735c; }
|
||||
background-color: #329f80;
|
||||
border-color: #329f80; }
|
||||
.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,
|
||||
.show > .btn-outline-primary.dropdown-toggle:focus {
|
||||
box-shadow: 0 0 0 0.2rem rgba(35, 115, 92, 0.5); }
|
||||
@ -1979,31 +1979,31 @@ fieldset:disabled a.btn {
|
||||
box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); }
|
||||
|
||||
.btn-outline-dark {
|
||||
color: #343a40;
|
||||
color: #0f3b21;
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
border-color: #343a40; }
|
||||
border-color: #0f3b21; }
|
||||
.btn-outline-dark:hover {
|
||||
color: #fff;
|
||||
background-color: #343a40;
|
||||
border-color: #343a40; }
|
||||
background-color: #0f3b21;
|
||||
border-color: #0f3b21; }
|
||||
.btn-outline-dark:focus, .btn-outline-dark.focus {
|
||||
box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); }
|
||||
.btn-outline-dark.disabled, .btn-outline-dark:disabled {
|
||||
color: #343a40;
|
||||
color: #0f3b21;
|
||||
background-color: transparent; }
|
||||
.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,
|
||||
.show > .btn-outline-dark.dropdown-toggle {
|
||||
color: #fff;
|
||||
background-color: #343a40;
|
||||
border-color: #343a40; }
|
||||
background-color: #0f3b21;
|
||||
border-color: #0f3b21; }
|
||||
.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,
|
||||
.show > .btn-outline-dark.dropdown-toggle:focus {
|
||||
box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); }
|
||||
|
||||
.btn-link {
|
||||
font-weight: 400;
|
||||
color: #23735c;
|
||||
color: #329f80;
|
||||
background-color: transparent; }
|
||||
.btn-link:hover {
|
||||
color: #11382d;
|
||||
@ -2197,7 +2197,7 @@ tbody.collapse.show {
|
||||
.dropdown-item.active, .dropdown-item:active {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
background-color: #23735c; }
|
||||
background-color: #329f80; }
|
||||
.dropdown-item.disabled, .dropdown-item:disabled {
|
||||
color: #6c757d;
|
||||
background-color: transparent; }
|
||||
@ -2429,7 +2429,7 @@ tbody.collapse.show {
|
||||
opacity: 0; }
|
||||
.custom-control-input:checked ~ .custom-control-label::before {
|
||||
color: #fff;
|
||||
background-color: #23735c; }
|
||||
background-color: #329f80; }
|
||||
.custom-control-input:focus ~ .custom-control-label::before {
|
||||
box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(35, 115, 92, 0.25); }
|
||||
.custom-control-input:active ~ .custom-control-label::before {
|
||||
@ -2469,13 +2469,13 @@ tbody.collapse.show {
|
||||
border-radius: 0.25rem; }
|
||||
|
||||
.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before {
|
||||
background-color: #23735c; }
|
||||
background-color: #329f80; }
|
||||
|
||||
.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"); }
|
||||
|
||||
.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {
|
||||
background-color: #23735c; }
|
||||
background-color: #329f80; }
|
||||
|
||||
.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E"); }
|
||||
@ -2490,7 +2490,7 @@ tbody.collapse.show {
|
||||
border-radius: 50%; }
|
||||
|
||||
.custom-radio .custom-control-input:checked ~ .custom-control-label::before {
|
||||
background-color: #23735c; }
|
||||
background-color: #329f80; }
|
||||
|
||||
.custom-radio .custom-control-input:checked ~ .custom-control-label::after {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E"); }
|
||||
@ -2636,7 +2636,7 @@ tbody.collapse.show {
|
||||
.nav-pills .nav-link.active,
|
||||
.nav-pills .show > .nav-link {
|
||||
color: #fff;
|
||||
background-color: #23735c; }
|
||||
background-color: #329f80; }
|
||||
|
||||
.nav-fill .nav-item {
|
||||
flex: 1 1 auto;
|
||||
@ -3148,7 +3148,7 @@ tbody.collapse.show {
|
||||
padding: 0.5rem 0.75rem;
|
||||
margin-left: -1px;
|
||||
line-height: 1.25;
|
||||
color: #23735c;
|
||||
color: #329f80;
|
||||
background-color: #fff;
|
||||
border: 1px solid #dee2e6; }
|
||||
.page-link:hover {
|
||||
@ -3175,8 +3175,8 @@ tbody.collapse.show {
|
||||
.page-item.active .page-link {
|
||||
z-index: 1;
|
||||
color: #fff;
|
||||
background-color: #23735c;
|
||||
border-color: #23735c; }
|
||||
background-color: #329f80;
|
||||
border-color: #329f80; }
|
||||
|
||||
.page-item.disabled .page-link {
|
||||
color: #6c757d;
|
||||
@ -3235,7 +3235,7 @@ tbody.collapse.show {
|
||||
|
||||
.badge-primary {
|
||||
color: #fff;
|
||||
background-color: #23735c; }
|
||||
background-color: #329f80; }
|
||||
.badge-primary[href]:hover, .badge-primary[href]:focus {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
@ -3291,7 +3291,7 @@ tbody.collapse.show {
|
||||
|
||||
.badge-dark {
|
||||
color: #fff;
|
||||
background-color: #343a40; }
|
||||
background-color: #0f3b21; }
|
||||
.badge-dark[href]:hover, .badge-dark[href]:focus {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
@ -3425,7 +3425,7 @@ tbody.collapse.show {
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
background-color: #23735c;
|
||||
background-color: #329f80;
|
||||
transition: width 0.6s ease; }
|
||||
|
||||
.progress-bar-striped {
|
||||
@ -3483,8 +3483,8 @@ tbody.collapse.show {
|
||||
.list-group-item.active {
|
||||
z-index: 2;
|
||||
color: #fff;
|
||||
background-color: #23735c;
|
||||
border-color: #23735c; }
|
||||
background-color: #329f80;
|
||||
border-color: #329f80; }
|
||||
|
||||
.list-group-flush .list-group-item {
|
||||
border-right: 0;
|
||||
@ -4099,7 +4099,7 @@ button.close {
|
||||
vertical-align: text-top !important; }
|
||||
|
||||
.bg-primary {
|
||||
background-color: #23735c !important; }
|
||||
background-color: #329f80 !important; }
|
||||
|
||||
a.bg-primary:hover, a.bg-primary:focus,
|
||||
button.bg-primary:hover,
|
||||
@ -4155,7 +4155,7 @@ button.bg-light:focus {
|
||||
background-color: #dae0e5 !important; }
|
||||
|
||||
.bg-dark {
|
||||
background-color: #343a40 !important; }
|
||||
background-color: #0f3b21 !important; }
|
||||
|
||||
a.bg-dark:hover, a.bg-dark:focus,
|
||||
button.bg-dark:hover,
|
||||
@ -4199,7 +4199,7 @@ button.bg-dark:focus {
|
||||
border-left: 0 !important; }
|
||||
|
||||
.border-primary {
|
||||
border-color: #23735c !important; }
|
||||
border-color: #329f80 !important; }
|
||||
|
||||
.border-secondary {
|
||||
border-color: #2284A6 !important; }
|
||||
@ -4220,7 +4220,7 @@ button.bg-dark:focus {
|
||||
border-color: #f8f9fa !important; }
|
||||
|
||||
.border-dark {
|
||||
border-color: #343a40 !important; }
|
||||
border-color: #0f3b21 !important; }
|
||||
|
||||
.border-white {
|
||||
border-color: #fff !important; }
|
||||
@ -5929,7 +5929,7 @@ button.bg-dark:focus {
|
||||
color: #fff !important; }
|
||||
|
||||
.text-primary {
|
||||
color: #23735c !important; }
|
||||
color: #329f80 !important; }
|
||||
|
||||
a.text-primary:hover, a.text-primary:focus {
|
||||
color: #174c3d !important; }
|
||||
@ -5971,7 +5971,7 @@ a.text-light:hover, a.text-light:focus {
|
||||
color: #dae0e5 !important; }
|
||||
|
||||
.text-dark {
|
||||
color: #343a40 !important; }
|
||||
color: #0f3b21 !important; }
|
||||
|
||||
a.text-dark:hover, a.text-dark:focus {
|
||||
color: #1d2124 !important; }
|
||||
|
Reference in New Issue
Block a user