Compare commits
551 Commits
Author | SHA1 | Date | |
---|---|---|---|
1e0c7cf43d | |||
7978c89731 | |||
2e23e6e4d5 | |||
82221f54c5 | |||
3a79fa074a | |||
696c63c6d2 | |||
8f6dc52430 | |||
c8a9ad9807 | |||
a0933d07d8 | |||
6bf61e2008 | |||
36ecc55578 | |||
47065299ca | |||
0f707a7607 | |||
7590c5550f | |||
3d23a5c79a | |||
9aa0374dc2 | |||
058a191673 | |||
5fbb9160f1 | |||
2c3dad0592 | |||
00f039d31e | |||
497ef1750b | |||
7d4cd04ce9 | |||
04c0b9d37d | |||
b9c16c0727 | |||
0a30f2b7e8 | |||
dbb1d53202 | |||
2c88f36e3d | |||
d91dcad774 | |||
6eb4ea2a2d | |||
ff6187c336 | |||
1253dfe0c8 | |||
c1f2f9a970 | |||
8d5e7ed69f | |||
3d3540f090 | |||
61f1fcb99c | |||
c7b576d6d3 | |||
ab3b1d5e46 | |||
ddff3faba9 | |||
f7bb8444da | |||
92128974bb | |||
4eb15d4806 | |||
39cb3780c8 | |||
81030f468b | |||
2032cb1ace | |||
5e4c91440e | |||
13bbad863f | |||
3065ed5094 | |||
9eb75cfff0 | |||
9305c99577 | |||
9078da6937 | |||
4c579639b9 | |||
b86472ab52 | |||
5e1ac485a0 | |||
527c1a12f4 | |||
ef4e99b3a7 | |||
12a9635309 | |||
78adb24a75 | |||
49955cf642 | |||
af3b289331 | |||
d11591e5aa | |||
9c6174e3f2 | |||
09c2f74d52 | |||
7d7e0254cb | |||
fe767afe9c | |||
3378f0e4ee | |||
c0341798ea | |||
fc114dc5db | |||
7107d844e1 | |||
59af0a817e | |||
9615eded85 | |||
c51fa23fcb | |||
8737fd6f1e | |||
0f109ab93a | |||
63df2742db | |||
7b7811f8ad | |||
80f74b9939 | |||
1f29f77f66 | |||
dd7da5f354 | |||
49b30da697 | |||
90ed767d75 | |||
7871f0f3ce | |||
a60cf40a3c | |||
f4eb2f6726 | |||
cfe87a802e | |||
6c90ec812f | |||
196d611c1c | |||
ff41cb2735 | |||
fb11674301 | |||
b9e7f4530c | |||
27049687bf | |||
13503edc63 | |||
d33f82d969 | |||
177632eee0 | |||
ca0de5258e | |||
1de788bc26 | |||
2b41909d47 | |||
e23a9f22dd | |||
465b7850b7 | |||
a0f2eedd7f | |||
8605e3ca5a | |||
dd893e6d48 | |||
c4cd1a5a54 | |||
94152651fc | |||
563ea76192 | |||
4913fab0b3 | |||
b49d011edf | |||
6e04281b03 | |||
f2df8e96db | |||
c6dd7605b2 | |||
71dd00da0f | |||
da48ca884d | |||
8c6c66fb11 | |||
f333b57310 | |||
d1d00e6c98 | |||
52300e680a | |||
b3f7353582 | |||
7db6b82a1a | |||
a50a13374f | |||
3952fe5a72 | |||
2e61a43e4f | |||
ebe03e9310 | |||
11dd3ce110 | |||
1919c24959 | |||
aa80f31e52 | |||
6d8400e72f | |||
fa8d0c91fc | |||
e91ff95712 | |||
0883a8dbff | |||
0db297d1cd | |||
db73052ee5 | |||
8b95069610 | |||
2a12744cd5 | |||
1df4059284 | |||
475894b680 | |||
1663bf8e52 | |||
ffca1d2486 | |||
eb876845ff | |||
02c134bf4b | |||
af55c11aa0 | |||
33bc6adcb5 | |||
56e4dcc11e | |||
467cf7620e | |||
85ac8dd701 | |||
1f2ad4e884 | |||
cf2d9af664 | |||
7a105047e9 | |||
bc8bdef37d | |||
fd0519b955 | |||
d5ffb56fa8 | |||
d6cce9e2d8 | |||
08ec46637f | |||
f596795792 | |||
2c56bfd4aa | |||
755615da30 | |||
4cc0060c67 | |||
52af015c2f | |||
eae6d13284 | |||
2a20a7cf21 | |||
27251005ec | |||
90a9b2e5a1 | |||
cd1f12e9b4 | |||
734b9b6458 | |||
a120449c8d | |||
b005a1da56 | |||
afc75a09d9 | |||
c6e6c98875 | |||
fa1a5ab913 | |||
b671b590ad | |||
a46c98eed3 | |||
7ab5ed5bcb | |||
5e609edc31 | |||
ac466429f8 | |||
2804c50c8a | |||
c4315c25bc | |||
e2d5fa48cf | |||
c2375c897d | |||
49f181583b | |||
ea463a6548 | |||
1a567dbdc1 | |||
e4ec10ef49 | |||
3db4fc687a | |||
e136972cd7 | |||
7b1b32e16f | |||
1616f94b86 | |||
7043d1f3bf | |||
7bebfe1919 | |||
d33ded0426 | |||
66aa67581f | |||
3a95c05db9 | |||
67046e9d36 | |||
6f2965a5b0 | |||
197db449a1 | |||
f4800bb7f0 | |||
6a213561f6 | |||
8f8d31f0c9 | |||
f8cfdacc26 | |||
07223e27a4 | |||
4c6f46ad17 | |||
39dc288f9c | |||
467e88ef55 | |||
0965db5d57 | |||
c30339d9b8 | |||
369adcb173 | |||
d837b981d4 | |||
f455461c1e | |||
0087b188a1 | |||
98602f49d8 | |||
70466e5626 | |||
cc7f98a6fe | |||
fd13ad1fca | |||
5077f6fbca | |||
9b15ce6d29 | |||
5a8ca24566 | |||
d3f982cae1 | |||
28b58b9048 | |||
cb10dde97d | |||
2c179867e8 | |||
44fc6de82b | |||
f671af43cd | |||
8c0dc6422e | |||
c91e285475 | |||
642e41530e | |||
b09a3ccdae | |||
a1aab62cea | |||
e8b1aca45c | |||
3de98873d6 | |||
c030ede12c | |||
67f740c264 | |||
9b68337047 | |||
2bae971b92 | |||
c5c5fd859f | |||
15c46fd157 | |||
424950bd3e | |||
39a9968971 | |||
075a09f0df | |||
26e628e189 | |||
7489d9d186 | |||
7db5b6c7f3 | |||
9b7fa8cac2 | |||
5028051a34 | |||
83b3767df6 | |||
6182b96d16 | |||
a719382563 | |||
2aa6eb90e2 | |||
d2495455cd | |||
23d1dd23d1 | |||
573acd6a5d | |||
40ddbbfbb7 | |||
1b488b298b | |||
54b45943db | |||
89b3ae4c74 | |||
fe97a76d00 | |||
78094f69d7 | |||
4499d55464 | |||
4c12ca0607 | |||
1daa9575db | |||
f7c9961f8c | |||
e27a625069 | |||
42b1669cce | |||
74571afc9e | |||
f678f11c59 | |||
e685252b1d | |||
bb75fcb5a3 | |||
7653f36f31 | |||
0670148064 | |||
368b900a6e | |||
6378a78cbd | |||
d3bcdec0f2 | |||
82c074ce2e | |||
a313e2d386 | |||
578cb2f7e3 | |||
c8f56e1659 | |||
56631a3e6b | |||
84ee9de18c | |||
0aeb4e9173 | |||
bb65e5c373 | |||
45e2027c56 | |||
6dc5ef44b7 | |||
e88d3cca07 | |||
a4b7381141 | |||
2ea054dc72 | |||
13ec726ab2 | |||
2e32b65421 | |||
f48ca53cdb | |||
c5b632cb24 | |||
422e9ae99e | |||
68ada8fbe4 | |||
e40bf08691 | |||
a04c7222b2 | |||
172faec5a0 | |||
e01c3e7e4a | |||
7a3d5d0429 | |||
ddf1caaaaa | |||
021293cbb0 | |||
1438e61f1b | |||
182f4dbae7 | |||
26ec3fc7cf | |||
225c758795 | |||
5e653250f3 | |||
b7a3713946 | |||
44242fdb4a | |||
45515b2c06 | |||
2d95fe294c | |||
72cc44641b | |||
9ac3fa5269 | |||
d1ea141165 | |||
dca21fbb8c | |||
06812d5df8 | |||
564410d3cd | |||
4b1cec979a | |||
a5f1bc3895 | |||
b097d031b5 | |||
45df729711 | |||
802ee8a1ff | |||
e312970212 | |||
c0f4069a9b | |||
654352827e | |||
7dd210976d | |||
5302be8bc1 | |||
9ed7181e28 | |||
23ae4b01cb | |||
59764d3378 | |||
b8e2c729c1 | |||
530d80a011 | |||
2d306e8fda | |||
b3d9a70fd1 | |||
b880207f61 | |||
ee76d02999 | |||
804c33a375 | |||
de784714d9 | |||
2404e26b61 | |||
b191fdda2c | |||
e8adfd45d2 | |||
7158595801 | |||
ba97f63338 | |||
62eca2aedc | |||
b15f6b1fa7 | |||
5b22de589c | |||
d1f50f12af | |||
d40c1d9b31 | |||
b69041d4af | |||
64c5d9a09f | |||
dd170bb41a | |||
1e6e4033f8 | |||
01fabc8d9e | |||
2ca2539b53 | |||
51e2e2966f | |||
55d02d2db5 | |||
282a0b0c44 | |||
8432779b23 | |||
13b9982461 | |||
d76b8cebdc | |||
80315ae6d4 | |||
95e7344286 | |||
28f73727b5 | |||
68f5bf5759 | |||
075748d697 | |||
e8d86f94f2 | |||
d6bb802892 | |||
52680e9002 | |||
d6385d82ae | |||
d058de067c | |||
32d6d143dd | |||
b49432802b | |||
99d4d75d8e | |||
1f584d57ac | |||
2c1543aa82 | |||
4390cbbfae | |||
bbf9e5717e | |||
c7edc28bd9 | |||
6e0de6f7bf | |||
3659422165 | |||
1af30da44e | |||
56c082cb26 | |||
e8eca582de | |||
4084b352de | |||
633e4acf0e | |||
468df15d80 | |||
f4537b4fcb | |||
8bca345b45 | |||
ee80712c77 | |||
3cf7153f44 | |||
8e2fc75e48 | |||
4aa51c8583 | |||
3c6ebd7742 | |||
b85539dc17 | |||
4cae3f02ed | |||
469b436f10 | |||
66e3e6729b | |||
fc6a794714 | |||
d75ed3d5ac | |||
bd0a218214 | |||
f96129fa37 | |||
920418618a | |||
29247481a6 | |||
773710aeef | |||
d0c8ee57e6 | |||
cf2adc7f6a | |||
b621f24540 | |||
99be638525 | |||
d35c204e07 | |||
d8b4267668 | |||
3c2f3be451 | |||
e846cf8672 | |||
8804bce6c0 | |||
83acda6d05 | |||
063719532f | |||
7b1b061355 | |||
ed4540887e | |||
6968476ed0 | |||
5d2c7c3058 | |||
e6cb90e545 | |||
ec73f4dbea | |||
4f41a52ee7 | |||
c097956fcb | |||
8cbc17ed98 | |||
50d89d0f13 | |||
7b4d13b73e | |||
2909aa1656 | |||
64131b6764 | |||
0b78d75a21 | |||
b35c342960 | |||
24c858d379 | |||
b8a31a8be9 | |||
02c30d6454 | |||
985f003e6d | |||
98045e1e2e | |||
5762ce58a4 | |||
2787ee71fc | |||
e61a6df4d7 | |||
0a5c2ecbf5 | |||
6bfab696ad | |||
928f2dd496 | |||
bcf75892f7 | |||
594761385f | |||
d05fba06ec | |||
25155b1b38 | |||
ded6c9c199 | |||
c62e6c0045 | |||
b3feda9fd1 | |||
7ef8e2c8b8 | |||
d5ff211871 | |||
51c23e3842 | |||
557b30815e | |||
145459bfc3 | |||
f97a6a2bee | |||
1134422891 | |||
6012275c7b | |||
2f07063375 | |||
310d1ed485 | |||
a48edbb16e | |||
d6258409fc | |||
1b94b1247b | |||
9ef63ae60e | |||
f99de4be48 | |||
80fd1820c2 | |||
0a4a983d20 | |||
c2cc830691 | |||
4d0490d1c6 | |||
02d1838547 | |||
5c6edff778 | |||
7cbca32ddd | |||
8950e315f8 | |||
a703df40c0 | |||
225fce8810 | |||
bdc0f0fcdd | |||
5bbb8c4858 | |||
35b9551bfb | |||
c8c5a05b39 | |||
2771f0301a | |||
c4f04edc59 | |||
5530422846 | |||
ce77c81fb5 | |||
bc488d4ac2 | |||
fe72a10346 | |||
0da88398b4 | |||
c42de3da20 | |||
4bf9f36baa | |||
380cb192c7 | |||
8882e19ec5 | |||
7f52059b98 | |||
1ce3cc4d7c | |||
657c71e94d | |||
c8cfb3c7b7 | |||
6e7e90acf4 | |||
6d3a556d34 | |||
7d9188b659 | |||
4f0a805c79 | |||
53f3320492 | |||
f9ce51b4a5 | |||
f8bf432c0d | |||
a822482172 | |||
b22f8a0b02 | |||
227331bf24 | |||
744688cbe1 | |||
79c8126c4a | |||
0b7c8e4ef7 | |||
35e00f61d8 | |||
aba3d58df8 | |||
45984a8166 | |||
ea5655ae42 | |||
f06cb0dfbb | |||
1abae55976 | |||
a83ed40ec4 | |||
75ecae4672 | |||
16a6f942c5 | |||
aa98508e57 | |||
583383aee1 | |||
13f69f81d7 | |||
43c34fcd64 | |||
c272238539 | |||
6e0d2706a8 | |||
25a7289ce8 | |||
1ba7d045e4 | |||
a4d75befe7 | |||
88377529bc | |||
91b9a0280f | |||
25173ae85c | |||
ad3350705e | |||
a16bc5db28 | |||
51657338f5 | |||
0fe3ea25af | |||
806daaf7c9 | |||
21ff4a83b5 | |||
ecc9aa40d7 | |||
c34ca2a59b | |||
dde7094fe3 | |||
105afdfefc | |||
4c254a8686 | |||
33ca203e57 | |||
2ff4133cd4 | |||
49ad85713e | |||
1978bf151f | |||
506378de82 | |||
53ead7a03f | |||
ab979fd63c | |||
1e84a2238b | |||
5618adf86c | |||
345b0bc95f | |||
b1d6c35e99 | |||
d767f1a101 | |||
c15f2b9a12 | |||
a21a53662b | |||
ebb5340019 | |||
6108bd214e | |||
eed27e101a | |||
2767680bed | |||
4080e30b6f | |||
e89257be62 | |||
d3c40a7e8b | |||
60657d5d25 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -10,6 +10,8 @@ msbuild.binlog
|
|||||||
*.zip
|
*.zip
|
||||||
|
|
||||||
*.idea
|
*.idea
|
||||||
|
_ReSharper.Caches
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
Oqtane.Server/appsettings.json
|
Oqtane.Server/appsettings.json
|
||||||
Oqtane.Server/Data
|
Oqtane.Server/Data
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2018-2022 .NET Foundation
|
Copyright (c) 2018-2023 .NET Foundation
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.Localization
|
namespace Microsoft.Extensions.Localization
|
||||||
{
|
{
|
||||||
public static class OqtaneLocalizationExtensions
|
public static class OqtaneLocalizationExtensions
|
||||||
@ -18,5 +20,42 @@ namespace Microsoft.Extensions.Localization
|
|||||||
}
|
}
|
||||||
return localizedValue;
|
return localizedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an IStringLocalizer based on a type name. This extension method is useful in scenarios where the default IStringLocalizer is unable to locate the resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localizerFactory"></param>
|
||||||
|
/// <param name="fullTypeName">the full type name ie. GetType().FullName</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IStringLocalizer Create(this IStringLocalizerFactory localizerFactory, string fullTypeName)
|
||||||
|
{
|
||||||
|
var typename = fullTypeName;
|
||||||
|
|
||||||
|
// handle generic types
|
||||||
|
var type = Type.GetType(fullTypeName);
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
typename = type.GetGenericTypeDefinition().FullName;
|
||||||
|
typename = typename.Substring(0, typename.IndexOf("`")); // remove generic type info
|
||||||
|
}
|
||||||
|
|
||||||
|
// format typename
|
||||||
|
if (typename.Contains(","))
|
||||||
|
{
|
||||||
|
typename = typename.Substring(0, typename.IndexOf(",")); // remove assembly info
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove rootnamespace
|
||||||
|
var rootnamespace = "";
|
||||||
|
var attributes = type.Assembly.GetCustomAttributes(typeof(RootNamespaceAttribute), false);
|
||||||
|
if (attributes.Length > 0)
|
||||||
|
{
|
||||||
|
rootnamespace = ((RootNamespaceAttribute)attributes[0]).RootNamespace;
|
||||||
|
}
|
||||||
|
typename = typename.Replace(rootnamespace + ".", "");
|
||||||
|
|
||||||
|
// create IStringLocalizer using factory
|
||||||
|
return localizerFactory.Create(typename, type.Assembly.GetName().Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static IServiceCollection AddOqtaneScopedServices(this IServiceCollection services)
|
public static IServiceCollection AddOqtaneScopedServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddScoped<SiteState>();
|
services.AddScoped<SiteState>();
|
||||||
services.AddScoped<IInstallationService, InstallationService>();
|
services.AddScoped<IInstallationService, InstallationService>();
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label>
|
<Label Class="col-sm-3" For="server" HelpText="Enter the database server name. This might include a port number as well if you are using a cloud service (ie. servername.database.windows.net,1433) " ResourceKey="Server">Server:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="server" type="text" class="form-control" @bind="@_server" />
|
<input id="server" type="text" class="form-control" @bind="@_server" />
|
||||||
</div>
|
</div>
|
||||||
@ -51,7 +51,7 @@
|
|||||||
@if (_encryption == "true")
|
@if (_encryption == "true")
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="trustservercertificate" HelpText="Specify the type of certificate you are using for encryption" ResourceKey="TrustServerCertificate">Certificate:</Label>
|
<Label Class="col-sm-3" For="trustservercertificate" HelpText="Specify the type of certificate you are using for encryption. Verifiable is equivalent to False. Self Signed is equivalent to True." ResourceKey="TrustServerCertificate">Certificate:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="encryption" class="form-select custom-select" @bind="@_trustservercertificate">
|
<select id="encryption" class="form-select custom-select" @bind="@_trustservercertificate">
|
||||||
<option value="true">@Localizer["Self Signed"]</option>
|
<option value="true">@Localizer["Self Signed"]</option>
|
||||||
|
@ -26,28 +26,41 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@if (_databases != null)
|
@if (_databases != null)
|
||||||
{
|
{
|
||||||
<select id="databasetype" class="form-select custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))">
|
<div class="input-group">
|
||||||
|
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
||||||
@foreach (var database in _databases)
|
@foreach (var database in _databases)
|
||||||
{
|
|
||||||
if (database.IsDefault)
|
|
||||||
{
|
|
||||||
<option value="@database.Name" selected>@Localizer[@database.Name]</option>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
|
@if (!_showConnectionString)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionString"]</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionParameters"]</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@{
|
@if (!_showConnectionString)
|
||||||
|
{
|
||||||
if (_databaseConfigType != null)
|
if (_databaseConfigType != null)
|
||||||
{
|
{
|
||||||
@DatabaseConfigComponent;
|
@DatabaseConfigComponent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">Settings:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col text-center">
|
<div class="col text-center">
|
||||||
@ -63,8 +76,8 @@
|
|||||||
<Label Class="col-sm-3" For="password" HelpText="Provide a password for the primary user account" ResourceKey="Password">Password:</Label>
|
<Label Class="col-sm-3" For="password" HelpText="Provide a password for the primary user account" ResourceKey="Password">Password:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_hostPassword" autocomplete="new-password" />
|
<input id="password" type="@_passwordType" class="form-control" @bind="@_hostPassword" autocomplete="new-password" />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglePassword</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -72,8 +85,8 @@
|
|||||||
<Label Class="col-sm-3" For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirmPassword" autocomplete="new-password" />
|
<input id="confirm" type="@_confirmPasswordType" class="form-control" @bind="@_confirmPassword" autocomplete="new-password" />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleConfirmPassword">@_toggleConfirmPassword</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -107,11 +120,15 @@
|
|||||||
private Type _databaseConfigType;
|
private Type _databaseConfigType;
|
||||||
private object _databaseConfig;
|
private object _databaseConfig;
|
||||||
private RenderFragment DatabaseConfigComponent { get; set; }
|
private RenderFragment DatabaseConfigComponent { get; set; }
|
||||||
|
private bool _showConnectionString = false;
|
||||||
|
private string _connectionString = string.Empty;
|
||||||
|
|
||||||
private string _hostUsername = string.Empty;
|
private string _hostUsername = string.Empty;
|
||||||
private string _hostPassword = string.Empty;
|
private string _hostPassword = string.Empty;
|
||||||
private string _passwordtype = "password";
|
private string _passwordType = "password";
|
||||||
private string _togglepassword = string.Empty;
|
private string _confirmPasswordType = "password";
|
||||||
|
private string _togglePassword = string.Empty;
|
||||||
|
private string _toggleConfirmPassword = string.Empty;
|
||||||
private string _confirmPassword = string.Empty;
|
private string _confirmPassword = string.Empty;
|
||||||
private string _hostEmail = string.Empty;
|
private string _hostEmail = string.Empty;
|
||||||
private bool _register = true;
|
private bool _register = true;
|
||||||
@ -120,7 +137,9 @@
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||||
|
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
_databases = await DatabaseService.GetDatabasesAsync();
|
_databases = await DatabaseService.GetDatabasesAsync();
|
||||||
if (_databases.Exists(item => item.IsDefault))
|
if (_databases.Exists(item => item.IsDefault))
|
||||||
{
|
{
|
||||||
@ -138,7 +157,7 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_databaseName = (string)eventArgs.Value;
|
_databaseName = (string)eventArgs.Value;
|
||||||
|
_showConnectionString = false;
|
||||||
LoadDatabaseConfigComponent();
|
LoadDatabaseConfigComponent();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -167,18 +186,25 @@
|
|||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
await interop.IncludeLink("", "stylesheet", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css", "text/css", "sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==", "anonymous", "");
|
await interop.IncludeLink("", "stylesheet", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/css/bootstrap.min.css", "text/css", "sha512-XWTTruHZEYJsxV3W/lSXG1n3Q39YIWOstqvmFsdNEEQfHoZ6vm6E9GK2OrF6DSJSpIbRbi+Nn0WDPID9O7xB2Q==", "anonymous", "");
|
||||||
await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", "anonymous", "", "head");
|
await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.bundle.min.js", "sha512-9GacT4119eY3AcosfWtHMsT5JyZudrexyEVzTBWV3viP/YfB9e2pEy3N7WXL3SV6ASXpTU0vzzSxsbfsuUH4sQ==", "anonymous", "", "head");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Install()
|
private async Task Install()
|
||||||
{
|
{
|
||||||
var connectionString = String.Empty;
|
var connectionString = String.Empty;
|
||||||
|
if (_showConnectionString)
|
||||||
|
{
|
||||||
|
connectionString = _connectionString;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
{
|
{
|
||||||
connectionString = databaseConfigControl.GetConnectionString();
|
connectionString = databaseConfigControl.GetConnectionString();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
|
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
|
||||||
{
|
{
|
||||||
@ -230,15 +256,39 @@
|
|||||||
|
|
||||||
private void TogglePassword()
|
private void TogglePassword()
|
||||||
{
|
{
|
||||||
if (_passwordtype == "password")
|
if (_passwordType == "password")
|
||||||
{
|
{
|
||||||
_passwordtype = "text";
|
_passwordType = "text";
|
||||||
_togglepassword = SharedLocalizer["HidePassword"];
|
_togglePassword = SharedLocalizer["HidePassword"];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_passwordtype = "password";
|
_passwordType = "password";
|
||||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ToggleConfirmPassword()
|
||||||
|
{
|
||||||
|
if (_confirmPasswordType == "password")
|
||||||
|
{
|
||||||
|
_confirmPasswordType = "text";
|
||||||
|
_toggleConfirmPassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_confirmPasswordType = "password";
|
||||||
|
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleConnectionString()
|
||||||
|
{
|
||||||
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
|
{
|
||||||
|
_connectionString = databaseConfigControl.GetConnectionString();
|
||||||
|
}
|
||||||
|
_showConnectionString = !_showConnectionString;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,11 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
@foreach (var p in _pages)
|
@foreach (var p in _pages)
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||||
{
|
{
|
||||||
string url = NavigateUrl(p.Path);
|
string url = NavigateUrl(p.Path);
|
||||||
<div class="col-md-2 mx-auto text-center">
|
<div class="col-md-2 mx-auto text-center mb-3">
|
||||||
<NavLink class="nav-link" href="@url" Match="NavLinkMatch.All">
|
<NavLink class="nav-link text-primary" href="@url" Match="NavLinkMatch.All">
|
||||||
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>@SharedLocalizer[p.Name]
|
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>@SharedLocalizer[p.Name]
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
@ -22,11 +22,14 @@
|
|||||||
@code {
|
@code {
|
||||||
private List<Page> _pages;
|
private List<Page> _pages;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
|
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
|
||||||
|
if (admin != null)
|
||||||
|
{
|
||||||
_pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId).ToList();
|
_pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId).ToList();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,7 @@
|
|||||||
Module module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
|
Module module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
string message = string.Format(Localizer["Error.Module.Load"], module.ModuleDefinitionName);
|
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], module.ModuleDefinitionName), MessageType.Error);
|
||||||
AddModuleMessage(message, MessageType.Error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await logger.LogCritical("Error Loading Module {Module}", module);
|
await logger.LogCritical("Error Loading Module {Module}", module);
|
||||||
|
@ -62,8 +62,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<Label Class="col-sm-3" For="permissions" HelpText="Select the permissions you want for the folder" ResourceKey="Permissions">Permissions: </Label>
|
<Label Class="col-sm-3" For="permissions" HelpText="Select the permissions you want for the folder" ResourceKey="Permissions">Permissions: </Label>
|
||||||
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" Permissions="@_permissions" @ref="_permissionGrid" />
|
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" PermissionList="@_permissions" @ref="_permissionGrid" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -99,7 +98,7 @@
|
|||||||
private string _imagesizes = string.Empty;
|
private string _imagesizes = string.Empty;
|
||||||
private string _capacity = "0";
|
private string _capacity = "0";
|
||||||
private bool _isSystem;
|
private bool _isSystem;
|
||||||
private string _permissions = string.Empty;
|
private List<Permission> _permissions = null;
|
||||||
private string _createdBy;
|
private string _createdBy;
|
||||||
private DateTime _createdOn;
|
private DateTime _createdOn;
|
||||||
private string _modifiedBy;
|
private string _modifiedBy;
|
||||||
@ -131,7 +130,7 @@
|
|||||||
_imagesizes = folder.ImageSizes;
|
_imagesizes = folder.ImageSizes;
|
||||||
_capacity = folder.Capacity.ToString();
|
_capacity = folder.Capacity.ToString();
|
||||||
_isSystem = folder.IsSystem;
|
_isSystem = folder.IsSystem;
|
||||||
_permissions = folder.Permissions;
|
_permissions = folder.PermissionList;
|
||||||
_createdBy = folder.CreatedBy;
|
_createdBy = folder.CreatedBy;
|
||||||
_createdOn = folder.CreatedOn;
|
_createdOn = folder.CreatedOn;
|
||||||
_modifiedBy = folder.ModifiedBy;
|
_modifiedBy = folder.ModifiedBy;
|
||||||
@ -196,7 +195,7 @@
|
|||||||
folder.ImageSizes = _imagesizes;
|
folder.ImageSizes = _imagesizes;
|
||||||
folder.Capacity = int.Parse(_capacity);
|
folder.Capacity = int.Parse(_capacity);
|
||||||
folder.IsSystem = _isSystem;
|
folder.IsSystem = _isSystem;
|
||||||
folder.Permissions = _permissionGrid.GetPermissions();
|
folder.PermissionList = _permissionGrid.GetPermissionList();
|
||||||
|
|
||||||
if (_folderId != -1)
|
if (_folderId != -1)
|
||||||
{
|
{
|
||||||
|
@ -132,22 +132,10 @@
|
|||||||
_isEnabled = job.IsEnabled.ToString();
|
_isEnabled = job.IsEnabled.ToString();
|
||||||
_interval = job.Interval.ToString();
|
_interval = job.Interval.ToString();
|
||||||
_frequency = job.Frequency;
|
_frequency = job.Frequency;
|
||||||
_startDate = job.StartDate;
|
(_startDate, _startTime) = Utilities.UtcAsLocalDateAndTime(job.StartDate);
|
||||||
if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
|
(_endDate, _endTime) = Utilities.UtcAsLocalDateAndTime(job.EndDate);
|
||||||
{
|
|
||||||
_startTime = job.StartDate.Value.ToString("HH:mm");
|
|
||||||
}
|
|
||||||
_endDate = job.EndDate;
|
|
||||||
if (job.EndDate != null && job.EndDate.Value.TimeOfDay.TotalSeconds != 0)
|
|
||||||
{
|
|
||||||
_endTime = job.EndDate.Value.ToString("HH:mm");
|
|
||||||
}
|
|
||||||
_retentionHistory = job.RetentionHistory.ToString();
|
_retentionHistory = job.RetentionHistory.ToString();
|
||||||
_nextDate = job.NextExecution;
|
(_nextDate, _nextTime) = Utilities.UtcAsLocalDateAndTime(job.NextExecution);
|
||||||
if (job.NextExecution != null && job.NextExecution.Value.TimeOfDay.TotalSeconds != 0)
|
|
||||||
{
|
|
||||||
_nextTime = job.NextExecution.Value.ToString("HH:mm");
|
|
||||||
}
|
|
||||||
createdby = job.CreatedBy;
|
createdby = job.CreatedBy;
|
||||||
createdon = job.CreatedOn;
|
createdon = job.CreatedOn;
|
||||||
modifiedby = job.ModifiedBy;
|
modifiedby = job.ModifiedBy;
|
||||||
@ -180,34 +168,10 @@
|
|||||||
{
|
{
|
||||||
job.Interval = int.Parse(_interval);
|
job.Interval = int.Parse(_interval);
|
||||||
}
|
}
|
||||||
job.StartDate = _startDate;
|
job.StartDate = Utilities.LocalDateAndTimeAsUtc(_startDate, _startTime);
|
||||||
if (job.StartDate != null)
|
job.EndDate = Utilities.LocalDateAndTimeAsUtc(_endDate, _endTime);
|
||||||
{
|
|
||||||
job.StartDate = job.StartDate.Value.Date;
|
|
||||||
if (!string.IsNullOrEmpty(_startTime))
|
|
||||||
{
|
|
||||||
job.StartDate = DateTime.Parse(job.StartDate.Value.ToShortDateString() + " " + _startTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
job.EndDate = _endDate;
|
|
||||||
if (job.EndDate != null)
|
|
||||||
{
|
|
||||||
job.EndDate = job.EndDate.Value.Date;
|
|
||||||
if (!string.IsNullOrEmpty(_endTime))
|
|
||||||
{
|
|
||||||
job.EndDate = DateTime.Parse(job.EndDate.Value.ToShortDateString() + " " + _endTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
job.RetentionHistory = int.Parse(_retentionHistory);
|
job.RetentionHistory = int.Parse(_retentionHistory);
|
||||||
job.NextExecution = _nextDate;
|
job.NextExecution = Utilities.LocalDateAndTimeAsUtc(_nextDate, _nextTime);
|
||||||
if (job.NextExecution != null)
|
|
||||||
{
|
|
||||||
job.NextExecution = job.NextExecution.Value.Date;
|
|
||||||
if (!string.IsNullOrEmpty(_nextTime))
|
|
||||||
{
|
|
||||||
job.NextExecution = DateTime.Parse(job.NextExecution.Value.ToShortDateString() + " " + _nextTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -226,4 +190,5 @@
|
|||||||
AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ else
|
|||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
|
<td>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
|
||||||
<td>@DisplayFrequency(context.Interval, context.Frequency)</td>
|
<td>@DisplayFrequency(context.Interval, context.Frequency)</td>
|
||||||
<td>@context.NextExecution</td>
|
<td>@context.NextExecution?.ToLocalTime()</td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.IsStarted)
|
@if (context.IsStarted)
|
||||||
{
|
{
|
||||||
@ -56,6 +56,10 @@ else
|
|||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
_jobs = await JobService.GetJobsAsync();
|
_jobs = await JobService.GetJobsAsync();
|
||||||
|
if (_jobs.Count == 0)
|
||||||
|
{
|
||||||
|
AddModuleMessage(string.Format(Localizer["Message.NoJobs"], NavigateUrl("admin/system")), MessageType.Warning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string DisplayStatus(bool isEnabled, bool isExecuting)
|
private string DisplayStatus(bool isEnabled, bool isExecuting)
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
@inject IStringLocalizer<Add> Localizer
|
@inject IStringLocalizer<Add> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@if (_supportedCultures == null)
|
@if (_cultures == null)
|
||||||
{
|
{
|
||||||
<p><em>@SharedLocalizer["Loading"]</em></p>
|
<p><em>@SharedLocalizer["Loading"]</em></p>
|
||||||
}
|
}
|
||||||
@ -17,21 +17,16 @@ else
|
|||||||
{
|
{
|
||||||
<TabStrip>
|
<TabStrip>
|
||||||
<TabPanel Name="Manage" ResourceKey="Manage">
|
<TabPanel Name="Manage" ResourceKey="Manage">
|
||||||
@if (_availableCultures.Count() == 0)
|
|
||||||
{
|
|
||||||
<ModuleMessage Type="MessageType.Info" Message="@_message"></ModuleMessage>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="name" HelpText="Name Of The Language" ResourceKey="Name">Name:</Label>
|
<Label Class="col-sm-3" For="name" HelpText="Name Of The Language" ResourceKey="Name">Name:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="_code" class="form-select" @bind="@_code" required>
|
<select id="_code" class="form-select" @bind="@_code" required>
|
||||||
@foreach (var culture in _availableCultures)
|
<option value="-"><@SharedLocalizer["Not Specified"]></option>
|
||||||
|
@foreach (var culture in _cultures)
|
||||||
{
|
{
|
||||||
<option value="@culture.Name">@culture.DisplayName</option>
|
<option value="@culture.Name">@(culture.DisplayName + " (" + culture.Name + ")")</option>
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -39,7 +34,7 @@ else
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label>
|
<Label Class="col-sm-3" For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="default" class="form-select" @bind="@_isDefault" required>
|
<select id="default" class="form-select" @bind="@_default" required>
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
<option value="False">@SharedLocalizer["No"]</option>
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
</select>
|
</select>
|
||||||
@ -47,213 +42,62 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
|
||||||
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
</form>
|
</form>
|
||||||
}
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel Name="Download" ResourceKey="Download" Security="SecurityAccessLevel.Host">
|
|
||||||
<div class="row justify-content-center mb-3">
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<div class="input-group">
|
|
||||||
<select id="price" class="form-select custom-select" @onchange="(e => PriceChanged(e))">
|
|
||||||
<option value="free">@SharedLocalizer["Free"]</option>
|
|
||||||
<option value="paid">@SharedLocalizer["Paid"]</option>
|
|
||||||
</select>
|
|
||||||
<input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" />
|
|
||||||
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
|
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (_packages != null)
|
|
||||||
{
|
|
||||||
@if (_packages.Count > 0)
|
|
||||||
{
|
|
||||||
<Pager Items="@_packages">
|
|
||||||
<Row>
|
|
||||||
<td>
|
|
||||||
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_new">@context.Name</a></h3> by: <strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
|
|
||||||
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
|
|
||||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
|
||||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
|
||||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
|
||||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
|
||||||
</td>
|
|
||||||
<td style="width: 1px; vertical-align: middle;">
|
|
||||||
@if (context.Price != null && !string.IsNullOrEmpty(context.PackageUrl))
|
|
||||||
{
|
|
||||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td style="width: 1px; vertical-align: middle;">
|
|
||||||
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
|
|
||||||
{
|
|
||||||
<a class="btn btn-primary" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@context.Price.Value.ToString("$#,##0.00")</a>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
</Row>
|
|
||||||
</Pager>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<br />
|
|
||||||
<div class="mx-auto text-center">
|
|
||||||
@Localizer["Search.NoResults"]
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<button type="button" class="btn btn-success" @onclick="InstallLanguages">@SharedLocalizer["Install"]</button>
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
|
||||||
}
|
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host">
|
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" HelpText="Upload one or more translations. Once they are uploaded click Install to complete the installation." ResourceKey="LanguageUpload">Language: </Label>
|
<Label Class="col-sm-3" HelpText="Upload one or more translations. Once they are uploaded click Install to complete the installation." ResourceKey="LanguageUpload">Translation: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" />
|
<FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success" @onclick="InstallLanguages">@SharedLocalizer["Install"]</button>
|
<button type="button" class="btn btn-success" @onclick="InstallTranslations">@SharedLocalizer["Install"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (_productname != "")
|
|
||||||
{
|
|
||||||
<div class="app-actiondialog">
|
|
||||||
<div class="modal" tabindex="-1" role="dialog">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title">@SharedLocalizer["Review License Terms"]</h5>
|
|
||||||
<button type="button" class="btn-close" aria-label="Close" @onclick="HideModal"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<p style="height: 200px; overflow-y: scroll;">
|
|
||||||
<h3>@_productname</h3>
|
|
||||||
@if (!string.IsNullOrEmpty(_license))
|
|
||||||
{
|
|
||||||
@((MarkupString)_license)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@SharedLocalizer["License Not Specified"]
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-success" @onclick="DownloadPackage">@SharedLocalizer["Accept"]</button>
|
|
||||||
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
|
|
||||||
private string _code = string.Empty;
|
private string _code = "-";
|
||||||
private string _isDefault = "False";
|
private string _default = "False";
|
||||||
private string _message;
|
private List<string> _languages;
|
||||||
private IEnumerable<Culture> _supportedCultures;
|
private IEnumerable<Culture> _cultures;
|
||||||
private IEnumerable<Culture> _availableCultures;
|
|
||||||
private List<Package> _packages;
|
|
||||||
private string _price = "free";
|
|
||||||
private string _search = "";
|
|
||||||
private string _productname = "";
|
|
||||||
private string _license = "";
|
|
||||||
private string _packageid = "";
|
|
||||||
private string _version = "";
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
||||||
var languagesCodes = languages.Select(l => l.Code).ToList();
|
_languages = languages.Select(l => l.Code).ToList();
|
||||||
|
|
||||||
_supportedCultures = await LocalizationService.GetCulturesAsync();
|
await LoadCultures();
|
||||||
_availableCultures = _supportedCultures
|
|
||||||
.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !languagesCodes.Contains(c.Name));
|
|
||||||
await LoadTranslations();
|
|
||||||
|
|
||||||
if (_supportedCultures.Count() == 1)
|
|
||||||
{
|
|
||||||
_message = Localizer["OnlyEnglish"];
|
|
||||||
}
|
|
||||||
else if (_availableCultures.Count() == 0)
|
|
||||||
{
|
|
||||||
_message = Localizer["AllLanguages"];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadTranslations()
|
private async Task LoadCultures()
|
||||||
{
|
{
|
||||||
_packages = await PackageService.GetPackagesAsync("translation", _search, _price, "");
|
_cultures = await LocalizationService.GetCulturesAsync(false);
|
||||||
}
|
_cultures = _cultures.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !_languages.Contains(c.Name));
|
||||||
|
_code = "-";
|
||||||
private async void PriceChanged(ChangeEventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_price = (string)e.Value;
|
|
||||||
_search = "";
|
|
||||||
await LoadTranslations();
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error On PriceChanged");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Search()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await LoadTranslations();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error On Search");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Reset()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_search = "";
|
|
||||||
await LoadTranslations();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error On Reset");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveLanguage()
|
private async Task SaveLanguage()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form) && _code != "-")
|
||||||
{
|
{
|
||||||
var language = new Language
|
var language = new Language
|
||||||
{
|
{
|
||||||
SiteId = PageState.Page.SiteId,
|
SiteId = PageState.Page.SiteId,
|
||||||
Name = CultureInfo.GetCultureInfo(_code).DisplayName,
|
Name = CultureInfo.GetCultureInfo(_code).DisplayName,
|
||||||
Code = _code,
|
Code = _code,
|
||||||
IsDefault = (_isDefault == null ? false : Boolean.Parse(_isDefault))
|
IsDefault = (_default == null ? false : Boolean.Parse(_default))
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -281,55 +125,7 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task InstallTranslations()
|
||||||
private void HideModal()
|
|
||||||
{
|
|
||||||
_productname = "";
|
|
||||||
_license = "";
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task GetPackage(string packageid, string version)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var package = await PackageService.GetPackageAsync(packageid, version);
|
|
||||||
if (package != null)
|
|
||||||
{
|
|
||||||
_productname = package.Name;
|
|
||||||
if (!string.IsNullOrEmpty(package.License))
|
|
||||||
{
|
|
||||||
_license = package.License.Replace("\n", "<br />");
|
|
||||||
}
|
|
||||||
_packageid = package.PackageId;
|
|
||||||
_version = package.Version;
|
|
||||||
}
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version);
|
|
||||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DownloadPackage()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder);
|
|
||||||
await logger.LogInformation("Language Package {Name} {Version} Downloaded Successfully", _packageid, _version);
|
|
||||||
AddModuleMessage(Localizer["Success.Language.Download"], MessageType.Success);
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Downloading Translation {Name} {Version}", _packageid, _version);
|
|
||||||
AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InstallLanguages()
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -20,38 +20,99 @@ else
|
|||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
<th>@Localizer["Code"]</th>
|
<th>@Localizer["Code"]</th>
|
||||||
<th>@Localizer["Default"]</th>
|
<th>@Localizer["Default"]</th>
|
||||||
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
<th style="width: 1px;">@Localizer["Translation"]</th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
|
}
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionDialog Header="Delete Language" Message="@string.Format(Localizer["Confirm.Language.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@((context.IsDefault && _languages.Count > 2) || context.Code == Constants.DefaultCulture)" ResourceKey="DeleteLanguage" /></td>
|
<td><ActionDialog Header="Delete Language" Message="@string.Format(Localizer["Confirm.Language.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@((context.IsDefault && _languages.Count > 2) || context.Code == Constants.DefaultCulture)" ResourceKey="DeleteLanguage" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.Code</td>
|
<td>@context.Code</td>
|
||||||
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
||||||
<td>
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
@if (UpgradeAvailable(context.Code))
|
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button>
|
<td>@((string.IsNullOrEmpty(context.Version)) ? "---" : context.Version)</td>
|
||||||
|
<td>
|
||||||
|
@{
|
||||||
|
var translation = TranslationAvailable(context.Code, context.Version);
|
||||||
|
}
|
||||||
|
@if (translation != null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(context.Version))
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(context.Code, translation.Version))>@SharedLocalizer["Download"]</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Version.Parse(translation.Version).CompareTo(Version.Parse(context.Version)) > 0)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(context.Code, translation.Version))>@SharedLocalizer["Upgrade"]</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
|
}
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && _install)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-success" @onclick="InstallTranslations">@SharedLocalizer["Install"]</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (_package != null)
|
||||||
|
{
|
||||||
|
<div class="app-actiondialog">
|
||||||
|
<div class="modal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">@SharedLocalizer["Review License Terms"]</h5>
|
||||||
|
<button type="button" class="btn-close" aria-label="Close" @onclick="HideModal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p style="height: 200px; overflow-y: scroll;">
|
||||||
|
<h4 style="display: inline;"><a href="@_package.ProductUrl" target="_new">@_package.Name</a></h4><br />
|
||||||
|
@SharedLocalizer["Search.By"]: <strong><a href="@_package.OwnerUrl" target="new">@_package.Owner</a></strong><br />
|
||||||
|
@(_package.Description.Length > 400 ? (_package.Description.Substring(0, 400) + "...") : _package.Description)<br />
|
||||||
|
<strong>@(String.Format("{0:n0}", _package.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||||
|
@SharedLocalizer["Search.Released"]: <strong>@_package.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||||
|
@SharedLocalizer["Search.Version"]: <strong>@_package.Version</strong>
|
||||||
|
@((MarkupString)(!string.IsNullOrEmpty(_package.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(_package.PackageUrl).Host + "</strong>" : ""))
|
||||||
|
<br /><br />
|
||||||
|
@if (!string.IsNullOrEmpty(_package.License))
|
||||||
|
{
|
||||||
|
@((MarkupString)_package.License.Replace("\n", "<br />"))
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@SharedLocalizer["License Not Specified"]
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-success" @onclick="DownloadPackage">@SharedLocalizer["Accept"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Language> _languages;
|
private List<Language> _languages;
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
|
private Package _package;
|
||||||
|
private bool _install = false;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
await GetLanguages();
|
||||||
|
|
||||||
var cultures = await LocalizationService.GetCulturesAsync();
|
|
||||||
var culture = cultures.First(c => c.Name.Equals(Constants.DefaultCulture));
|
|
||||||
|
|
||||||
// Adds English as default language
|
|
||||||
_languages.Insert(0, new Language { Name = culture.DisplayName, Code = culture.Name, IsDefault = !_languages.Any(l => l.IsDefault) });
|
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
@ -59,13 +120,18 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task GetLanguages()
|
||||||
|
{
|
||||||
|
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId, Constants.ClientId);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task DeleteLanguage(Language language)
|
private async Task DeleteLanguage(Language language)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await LanguageService.DeleteLanguageAsync(language.LanguageId);
|
await LanguageService.DeleteLanguageAsync(language.LanguageId);
|
||||||
await logger.LogInformation("Language Deleted {Language}", language);
|
await logger.LogInformation("Language Deleted {Language}", language);
|
||||||
|
await GetLanguages();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -76,37 +142,61 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UpgradeAvailable(string code)
|
private Package TranslationAvailable(string code, string version)
|
||||||
{
|
{
|
||||||
var upgradeavailable = false;
|
return _packages?.FirstOrDefault(item => item.PackageId == (Constants.PackageId + "." + code));
|
||||||
if (_packages != null)
|
|
||||||
{
|
|
||||||
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + ".Client." + code)).FirstOrDefault();
|
|
||||||
if (package != null)
|
|
||||||
{
|
|
||||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private async Task GetPackage(string code, string version)
|
||||||
return upgradeavailable;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DownloadLanguage(string code)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
_package = await PackageService.GetPackageAsync(Constants.PackageId + "." + code, version);
|
||||||
{
|
StateHasChanged();
|
||||||
await PackageService.DownloadPackageAsync(Constants.PackageId + ".Client." + code, Constants.Version, Constants.PackagesFolder);
|
|
||||||
await logger.LogInformation("Translation Downloaded {Code} {Version}", code, Constants.Version);
|
|
||||||
await PackageService.InstallPackagesAsync();
|
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Language.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Downloading Translation {Code} {Version} {Error}", code, Constants.Version, ex.Message);
|
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", Constants.PackageId + "." + code, Constants.Version);
|
||||||
|
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DownloadPackage()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await PackageService.DownloadPackageAsync(_package.PackageId, _package.Version, Constants.PackagesFolder);
|
||||||
|
await logger.LogInformation("Language Package {Name} {Version} Downloaded Successfully", _package.PackageId, _package.Version);
|
||||||
|
AddModuleMessage(Localizer["Success.Language.Download"], MessageType.Success);
|
||||||
|
_package = null;
|
||||||
|
_install = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Downloading Translation {Name} {Version}", _package.PackageId, _package.Version);
|
||||||
AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HideModal()
|
||||||
|
{
|
||||||
|
_package = null;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InstallTranslations()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await PackageService.InstallPackagesAsync();
|
||||||
|
AddModuleMessage(string.Format(Localizer["Success.Language.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
|
_install = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Installing Translations");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
@using System.Net
|
||||||
@namespace Oqtane.Modules.Admin.Login
|
@namespace Oqtane.Modules.Admin.Login
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
@inject IServiceProvider ServiceProvider
|
@inject IServiceProvider ServiceProvider
|
||||||
@inject SiteState SiteState
|
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@ -184,11 +184,12 @@
|
|||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(login))
|
if (await interop.FormValid(login))
|
||||||
{
|
{
|
||||||
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password};
|
var hybrid = (PageState.Runtime == Shared.Runtime.Hybrid);
|
||||||
|
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password, LastIPAddress = SiteState.RemoteIPAddress};
|
||||||
|
|
||||||
if (!twofactor)
|
if (!twofactor)
|
||||||
{
|
{
|
||||||
user = await UserService.LoginUserAsync(user);
|
user = await UserService.LoginUserAsync(user, hybrid, _remember);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -199,14 +200,25 @@
|
|||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
||||||
|
|
||||||
|
if (hybrid)
|
||||||
|
{
|
||||||
|
// hybrid apps utilize an interactive login
|
||||||
|
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider
|
||||||
|
.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||||
|
authstateprovider.NotifyAuthenticationChanged();
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl(WebUtility.UrlDecode(_returnUrl), true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// post back to the Login page so that the cookies are set correctly
|
// post back to the Login page so that the cookies are set correctly
|
||||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
|
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
|
||||||
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
||||||
await interop.SubmitForm(url, fields);
|
await interop.SubmitForm(url, fields);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (PageState.Site.Settings["LoginOptions:TwoFactor"] == "required" || user.TwoFactorRequired)
|
if ((PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && PageState.Site.Settings["LoginOptions:TwoFactor"] == "required") || user.TwoFactorRequired)
|
||||||
{
|
{
|
||||||
twofactor = true;
|
twofactor = true;
|
||||||
validated = false;
|
validated = false;
|
||||||
|
@ -130,13 +130,14 @@
|
|||||||
private string _properties = string.Empty;
|
private string _properties = string.Empty;
|
||||||
private string _server = string.Empty;
|
private string _server = string.Empty;
|
||||||
|
|
||||||
|
public override string UrlParametersTemplate => "/{id}";
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logId = Int32.Parse(PageState.QueryString["id"]);
|
_logId = Int32.Parse(UrlParameters["id"]);
|
||||||
var log = await LogService.GetLogAsync(_logId);
|
var log = await LogService.GetLogAsync(_logId);
|
||||||
if (log != null)
|
if (log != null)
|
||||||
{
|
{
|
||||||
@ -191,13 +192,6 @@
|
|||||||
|
|
||||||
private string CloseUrl()
|
private string CloseUrl()
|
||||||
{
|
{
|
||||||
if (!PageState.QueryString.ContainsKey("level"))
|
return (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : NavigateUrl();
|
||||||
{
|
|
||||||
return NavigateUrl();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return NavigateUrl(PageState.Page.Path, "level=" + PageState.QueryString["level"] + "&function=" + PageState.QueryString["function"] + "&rows=" + PageState.QueryString["rows"] + "&page=" + PageState.QueryString["page"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Admin.Logs
|
@namespace Oqtane.Modules.Admin.Logs
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
@inject ILogService LogService
|
@inject ILogService LogService
|
||||||
@inject ISettingService SettingService
|
@inject ISettingService SettingService
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@ -62,7 +63,7 @@ else
|
|||||||
<th>@Localizer["Function"]</th>
|
<th>@Localizer["Function"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString() + "&level=" + _level + "&function=" + _function + "&rows=" + _rows + "&page=" + _page.ToString())" ResourceKey="LogDetails" /></td>
|
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"/{context.LogId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, AddUrlParameters(_level, _function, _rows, _page)))" ResourceKey="LogDetails" /></td>
|
||||||
<td class="@GetClass(context.Function)">@context.LogDate</td>
|
<td class="@GetClass(context.Function)">@context.LogDate</td>
|
||||||
<td class="@GetClass(context.Function)">@context.Level</td>
|
<td class="@GetClass(context.Function)">@context.Level</td>
|
||||||
<td class="@GetClass(context.Function)">@context.Feature</td>
|
<td class="@GetClass(context.Function)">@context.Feature</td>
|
||||||
@ -98,25 +99,26 @@ else
|
|||||||
private List<Log> _logs;
|
private List<Log> _logs;
|
||||||
private string _retention = "";
|
private string _retention = "";
|
||||||
|
|
||||||
|
public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}";
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (PageState.QueryString.ContainsKey("level"))
|
if (UrlParameters.ContainsKey("level"))
|
||||||
{
|
{
|
||||||
_level = PageState.QueryString["level"];
|
_level = UrlParameters["level"];
|
||||||
}
|
}
|
||||||
if (PageState.QueryString.ContainsKey("function"))
|
if (UrlParameters.ContainsKey("function"))
|
||||||
{
|
{
|
||||||
_function = PageState.QueryString["function"];
|
_function = UrlParameters["function"];
|
||||||
}
|
}
|
||||||
if (PageState.QueryString.ContainsKey("rows"))
|
if (UrlParameters.ContainsKey("rows"))
|
||||||
{
|
{
|
||||||
_rows = PageState.QueryString["rows"];
|
_rows = UrlParameters["rows"];
|
||||||
}
|
}
|
||||||
if (PageState.QueryString.ContainsKey("page") && int.TryParse(PageState.QueryString["page"], out int page))
|
if (UrlParameters.ContainsKey("page") && int.TryParse(UrlParameters["page"], out int page))
|
||||||
{
|
{
|
||||||
_page = page;
|
_page = page;
|
||||||
}
|
}
|
||||||
@ -233,4 +235,15 @@ else
|
|||||||
_page = page;
|
_page = page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnAfterRender(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
// external link to log item will display Details component
|
||||||
|
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"/{id}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ else
|
|||||||
private List<Template> _templates;
|
private List<Template> _templates;
|
||||||
private string _template = "-";
|
private string _template = "-";
|
||||||
private string[] _versions;
|
private string[] _versions;
|
||||||
private string _reference = Constants.Version;
|
private string _reference = "local";
|
||||||
private string _minversion = "2.0.0";
|
private string _minversion = "2.0.0";
|
||||||
private string _location = string.Empty;
|
private string _location = string.Empty;
|
||||||
|
|
||||||
@ -124,17 +124,24 @@ else
|
|||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
|
||||||
{
|
{
|
||||||
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
|
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
|
||||||
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
||||||
|
|
||||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||||
SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
|
settings = SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
|
||||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||||
|
|
||||||
GetLocation();
|
GetLocation();
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
|
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Creating Module");
|
await logger.LogError(ex, "Error Creating Module");
|
||||||
@ -167,7 +174,7 @@ else
|
|||||||
private bool IsValid(string name)
|
private bool IsValid(string name)
|
||||||
{
|
{
|
||||||
// must contain letters, underscores and digits and first character must be letter or underscore
|
// must contain letters, underscores and digits and first character must be letter or underscore
|
||||||
return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
|
return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TemplateChanged(ChangeEventArgs e)
|
private void TemplateChanged(ChangeEventArgs e)
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||||
|
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||||
</td>
|
</td>
|
||||||
<td style="width: 1px; vertical-align: middle;">
|
<td style="width: 1px; vertical-align: middle;">
|
||||||
@ -90,9 +91,9 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p style="height: 200px; overflow-y: scroll;">
|
<p style="height: 200px; overflow-y: scroll;">
|
||||||
<h3>@_productname</h3>
|
<h3>@_productname</h3>
|
||||||
@if (!string.IsNullOrEmpty(_license))
|
@if (!string.IsNullOrEmpty(_packagelicense))
|
||||||
{
|
{
|
||||||
@((MarkupString)_license)
|
@((MarkupString)_packagelicense)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -113,14 +114,18 @@
|
|||||||
<button type="button" class="btn btn-success" @onclick="InstallModules">@SharedLocalizer["Install"]</button>
|
<button type="button" class="btn btn-success" @onclick="InstallModules">@SharedLocalizer["Install"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<ModuleMessage Type="MessageType.Info" Message="@SharedLocalizer["Oqtane.Marketplace"]" />
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
private string _price = "free";
|
private string _price = "free";
|
||||||
private string _search = "";
|
private string _search = "";
|
||||||
private string _productname = "";
|
private string _productname = "";
|
||||||
private string _license = "";
|
|
||||||
private string _packageid = "";
|
private string _packageid = "";
|
||||||
private string _version = "";
|
private string _packagelicense = "";
|
||||||
|
private string _packageversion = "";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
@ -197,7 +202,7 @@
|
|||||||
private void HideModal()
|
private void HideModal()
|
||||||
{
|
{
|
||||||
_productname = "";
|
_productname = "";
|
||||||
_license = "";
|
_packagelicense = "";
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,12 +214,12 @@
|
|||||||
if (package != null)
|
if (package != null)
|
||||||
{
|
{
|
||||||
_productname = package.Name;
|
_productname = package.Name;
|
||||||
|
_packageid = package.PackageId;
|
||||||
if (!string.IsNullOrEmpty(package.License))
|
if (!string.IsNullOrEmpty(package.License))
|
||||||
{
|
{
|
||||||
_license = package.License.Replace("\n", "<br />");
|
_packagelicense = package.License.Replace("\n", "<br />");
|
||||||
}
|
}
|
||||||
_packageid = package.PackageId;
|
_packageversion = package.Version;
|
||||||
_version = package.Version;
|
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
@ -229,16 +234,16 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder);
|
await PackageService.DownloadPackageAsync(_packageid, _packageversion, Constants.PackagesFolder);
|
||||||
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version);
|
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _packageversion);
|
||||||
AddModuleMessage(Localizer["Success.Module.Download"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.Module.Download"], MessageType.Success);
|
||||||
_productname = "";
|
_productname = "";
|
||||||
_license = "";
|
_packagelicense = "";
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packageid, _version);
|
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packageid, _packageversion);
|
||||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -252,7 +257,7 @@
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Installing Module");
|
await logger.LogError(ex, "Error Installing Modules");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@
|
|||||||
private bool IsValid(string name)
|
private bool IsValid(string name)
|
||||||
{
|
{
|
||||||
// must contain letters, underscores and digits and first character must be letter or underscore
|
// must contain letters, underscores and digits and first character must be letter or underscore
|
||||||
return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
|
return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TemplateChanged(ChangeEventArgs e)
|
private void TemplateChanged(ChangeEventArgs e)
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
|
@using System.Globalization
|
||||||
|
@using Microsoft.AspNetCore.Localization
|
||||||
@inject IModuleDefinitionService ModuleDefinitionService
|
@inject IModuleDefinitionService ModuleDefinitionService
|
||||||
|
@inject IPackageService PackageService
|
||||||
|
@inject ILanguageService LanguageService
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IStringLocalizer<Edit> Localizer
|
@inject IStringLocalizer<Edit> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<TabStrip>
|
@if (_initialized)
|
||||||
|
{
|
||||||
|
<TabStrip>
|
||||||
<TabPanel Name="Definition" ResourceKey="Definition">
|
<TabPanel Name="Definition" ResourceKey="Definition">
|
||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -43,6 +49,12 @@
|
|||||||
<input id="version" class="form-control" @bind="@_version" disabled />
|
<input id="version" class="form-control" @bind="@_version" disabled />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this module was installed" ResourceKey="PackageName">Package Name: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="owner" HelpText="The owner or creator of the module" ResourceKey="Owner">Owner: </Label>
|
<Label Class="col-sm-3" For="owner" HelpText="The owner or creator of the module" ResourceKey="Owner">Owner: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -75,36 +87,131 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
|
||||||
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Permissions" ResourceKey="Permissions">
|
<TabPanel Name="Permissions" ResourceKey="Permissions">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" Permissions="@_permissions" @ref="_permissionGrid" />
|
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" PermissionList="@_permissions" @ref="_permissionGrid" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
|
||||||
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabStrip>
|
<TabPanel Name="Translations" ResourceKey="Translations">
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
|
@if (_languages != null && _languages.Count > 0)
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
{
|
||||||
<br />
|
<Pager Items="@_languages">
|
||||||
<br />
|
<Header>
|
||||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
|
<th>@Localizer["Code"]</th>
|
||||||
|
<th style="width: 1px;">@Localizer["Version"]</th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
</Header>
|
||||||
|
<Row>
|
||||||
|
<td>@context.Name</td>
|
||||||
|
<td>@context.Code</td>
|
||||||
|
<td>@((string.IsNullOrEmpty(context.Version)) ? "---" : context.Version)</td>
|
||||||
|
<td>
|
||||||
|
@switch (TranslationAvailable(_packagename + "." + context.Code, context.Version))
|
||||||
|
{
|
||||||
|
case "install":
|
||||||
|
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(_packagename + "." + context.Code))>@SharedLocalizer["Download"]</button>
|
||||||
|
break;
|
||||||
|
case "upgrade":
|
||||||
|
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(_packagename + "." + context.Code))>@SharedLocalizer["Upgrade"]</button>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
|
@if (_install)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-success" @onclick="InstallTranslations">@SharedLocalizer["Install"]</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<br />
|
||||||
|
<div class="mx-auto text-center">
|
||||||
|
@if (string.IsNullOrEmpty(_packagename))
|
||||||
|
{
|
||||||
|
@Localizer["Search.PackageNameMissing"]
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@Localizer["Search.NoResults"]
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
}
|
||||||
|
</TabPanel>
|
||||||
|
</TabStrip>
|
||||||
|
|
||||||
|
@if (_package != null)
|
||||||
|
{
|
||||||
|
<div class="app-actiondialog">
|
||||||
|
<div class="modal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">@SharedLocalizer["Review License Terms"]</h5>
|
||||||
|
<button type="button" class="btn-close" aria-label="Close" @onclick="HideModal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p style="height: 200px; overflow-y: scroll;">
|
||||||
|
<h4 style="display: inline;"><a href="@_package.ProductUrl" target="_new">@_package.Name</a></h4><br />
|
||||||
|
@SharedLocalizer["Search.By"]: <strong><a href="@_package.OwnerUrl" target="new">@_package.Owner</a></strong><br />
|
||||||
|
@(_package.Description.Length > 400 ? (_package.Description.Substring(0, 400) + "...") : _package.Description)<br />
|
||||||
|
<strong>@(String.Format("{0:n0}", _package.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||||
|
@SharedLocalizer["Search.Released"]: <strong>@_package.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||||
|
@SharedLocalizer["Search.Version"]: <strong>@_package.Version</strong>
|
||||||
|
@((MarkupString)(!string.IsNullOrEmpty(_package.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(_package.PackageUrl).Host + "</strong>" : ""))
|
||||||
|
<br /><br />
|
||||||
|
@if (!string.IsNullOrEmpty(_package.License))
|
||||||
|
{
|
||||||
|
@((MarkupString)_package.License.Replace("\n", "<br />"))
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@SharedLocalizer["License Not Specified"]
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-success" @onclick="DownloadPackage">@SharedLocalizer["Accept"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private bool _initialized = false;
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private int _moduleDefinitionId;
|
private int _moduleDefinitionId;
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _version;
|
private string _description = "";
|
||||||
private string _categories;
|
private string _categories;
|
||||||
private string _moduledefinitionname = "";
|
private string _moduledefinitionname = "";
|
||||||
private string _description = "";
|
private string _version;
|
||||||
|
private string _packagename = "";
|
||||||
private string _owner = "";
|
private string _owner = "";
|
||||||
private string _url = "";
|
private string _url = "";
|
||||||
private string _contact = "";
|
private string _contact = "";
|
||||||
private string _license = "";
|
private string _license = "";
|
||||||
private string _runtimes = "";
|
private string _runtimes = "";
|
||||||
private string _permissions;
|
private List<Permission> _permissions = null;
|
||||||
private string _createdby;
|
private string _createdby;
|
||||||
private DateTime _createdon;
|
private DateTime _createdon;
|
||||||
private string _modifiedby;
|
private string _modifiedby;
|
||||||
@ -114,6 +221,11 @@
|
|||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
private List<Package> _packages;
|
||||||
|
private List<Language> _languages;
|
||||||
|
private Package _package;
|
||||||
|
private bool _install = false;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
@ -125,20 +237,38 @@
|
|||||||
if (moduleDefinition != null)
|
if (moduleDefinition != null)
|
||||||
{
|
{
|
||||||
_name = moduleDefinition.Name;
|
_name = moduleDefinition.Name;
|
||||||
_version = moduleDefinition.Version;
|
_description = moduleDefinition.Description;
|
||||||
_categories = moduleDefinition.Categories;
|
_categories = moduleDefinition.Categories;
|
||||||
_moduledefinitionname = moduleDefinition.ModuleDefinitionName;
|
_moduledefinitionname = moduleDefinition.ModuleDefinitionName;
|
||||||
_description = moduleDefinition.Description;
|
_version = moduleDefinition.Version;
|
||||||
|
_packagename = moduleDefinition.PackageName;
|
||||||
_owner = moduleDefinition.Owner;
|
_owner = moduleDefinition.Owner;
|
||||||
_url = moduleDefinition.Url;
|
_url = moduleDefinition.Url;
|
||||||
_contact = moduleDefinition.Contact;
|
_contact = moduleDefinition.Contact;
|
||||||
_license = moduleDefinition.License;
|
_license = moduleDefinition.License;
|
||||||
_runtimes = moduleDefinition.Runtimes;
|
_runtimes = moduleDefinition.Runtimes;
|
||||||
_permissions = moduleDefinition.Permissions;
|
_permissions = moduleDefinition.PermissionList;
|
||||||
_createdby = moduleDefinition.CreatedBy;
|
_createdby = moduleDefinition.CreatedBy;
|
||||||
_createdon = moduleDefinition.CreatedOn;
|
_createdon = moduleDefinition.CreatedOn;
|
||||||
_modifiedby = moduleDefinition.ModifiedBy;
|
_modifiedby = moduleDefinition.ModifiedBy;
|
||||||
_modifiedon = moduleDefinition.ModifiedOn;
|
_modifiedon = moduleDefinition.ModifiedOn;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_packagename))
|
||||||
|
{
|
||||||
|
_packages = await PackageService.GetPackagesAsync("translation", "", "", _packagename);
|
||||||
|
_languages = await LanguageService.GetLanguagesAsync(-1, _packagename);
|
||||||
|
foreach (var package in _packages)
|
||||||
|
{
|
||||||
|
var code = package.PackageId.Split('.').Last();
|
||||||
|
if (!_languages.Any(item => item.Code == code))
|
||||||
|
{
|
||||||
|
_languages.Add(new Language { Code = code, Name = CultureInfo.GetCultureInfo(code).DisplayName, Version = package.Version, IsDefault = true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_languages = _languages.OrderBy(item => item.Name).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -169,7 +299,7 @@
|
|||||||
{
|
{
|
||||||
moduledefinition.Categories = _categories;
|
moduledefinition.Categories = _categories;
|
||||||
}
|
}
|
||||||
moduledefinition.Permissions = _permissionGrid.GetPermissions();
|
moduledefinition.PermissionList = _permissionGrid.GetPermissionList();
|
||||||
await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition);
|
await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition);
|
||||||
await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition);
|
await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition);
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
@ -185,4 +315,81 @@
|
|||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HideModal()
|
||||||
|
{
|
||||||
|
_package = null;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string TranslationAvailable(string packagename, string version)
|
||||||
|
{
|
||||||
|
if (_packages != null)
|
||||||
|
{
|
||||||
|
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||||
|
if (package != null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(version))
|
||||||
|
{
|
||||||
|
return "install";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
|
||||||
|
{
|
||||||
|
return "upgrade";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GetPackage(string packagename)
|
||||||
|
{
|
||||||
|
var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_package = await PackageService.GetPackageAsync(packagename, version);
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packagename, version);
|
||||||
|
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DownloadPackage()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await PackageService.DownloadPackageAsync(_package.PackageId, _package.Version, Constants.PackagesFolder);
|
||||||
|
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _package.PackageId, _package.Version);
|
||||||
|
AddModuleMessage(Localizer["Success.Translation.Download"], MessageType.Success);
|
||||||
|
_package = null;
|
||||||
|
_install = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packagename, _version);
|
||||||
|
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InstallTranslations()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await PackageService.InstallPackagesAsync();
|
||||||
|
AddModuleMessage(string.Format(Localizer["Success.Translation.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
|
_install = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Installing Translations");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,32 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-3 align-items-center">
|
||||||
|
<div class="col-sm-6">
|
||||||
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
||||||
@((MarkupString)" ")
|
@((MarkupString)" ")
|
||||||
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary" />
|
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary" />
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<select class="form-select" @onchange="(e => CategoryChanged(e))">
|
||||||
|
@foreach (var category in _categories)
|
||||||
|
{
|
||||||
|
if (category == _category)
|
||||||
|
{
|
||||||
|
<option value="@category" selected>@category @Localizer["Modules"]</option>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<option value="@category">@category @Localizer["Modules"]</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Pager Items="@_moduleDefinitions">
|
<Pager Items="@_moduleDefinitions.Where(item => item.Categories.Contains(_category))">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
@ -29,7 +50,7 @@ else
|
|||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" ResourceKey="EditModule" /></td>
|
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" ResourceKey="EditModule" /></td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.AssemblyName != "Oqtane.Client")
|
@if (context.AssemblyName != Constants.ClientId)
|
||||||
{
|
{
|
||||||
<ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete", context.Name])" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" />
|
<ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete", context.Name])" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" />
|
||||||
}
|
}
|
||||||
@ -37,7 +58,7 @@ else
|
|||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.Version</td>
|
<td>@context.Version</td>
|
||||||
<td>
|
<td>
|
||||||
@if(context.AssemblyName == "Oqtane.Client" || PageState.Modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
|
@if (context.AssemblyName == Constants.ClientId || PageState.Modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
|
||||||
{
|
{
|
||||||
<span>@SharedLocalizer["Yes"]</span>
|
<span>@SharedLocalizer["Yes"]</span>
|
||||||
}
|
}
|
||||||
@ -65,6 +86,8 @@ else
|
|||||||
@code {
|
@code {
|
||||||
private List<ModuleDefinition> _moduleDefinitions;
|
private List<ModuleDefinition> _moduleDefinitions;
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
|
private List<string> _categories = new List<string>();
|
||||||
|
private string _category = "Common";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
@ -74,6 +97,7 @@ else
|
|||||||
{
|
{
|
||||||
_moduleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
_moduleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||||
_packages = await PackageService.GetPackagesAsync("module");
|
_packages = await PackageService.GetPackagesAsync("module");
|
||||||
|
_categories = _moduleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -149,4 +173,10 @@ else
|
|||||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CategoryChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
_category = (string)e.Value;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _content = string.Empty;
|
private string _content = string.Empty;
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
public override string Title => "Export Content";
|
public override string Title => "Export Content";
|
||||||
|
|
||||||
|
|
||||||
@ -27,7 +27,7 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_content = await ModuleService.ExportModuleAsync(ModuleState.ModuleId);
|
_content = await ModuleService.ExportModuleAsync(ModuleState.ModuleId, PageState.Page.PageId);
|
||||||
AddModuleMessage(Localizer["Success.Content.Export"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.Content.Export"], MessageType.Success);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
public override string Title => "Import Content";
|
public override string Title => "Import Content";
|
||||||
|
|
||||||
private async Task ImportModule()
|
private async Task ImportModule()
|
||||||
@ -38,7 +38,7 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
|
bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, PageState.Page.PageId, _content);
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Success.Content.Import"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.Content.Import"], MessageType.Success);
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
<select id="page" class="form-select" @bind="@_pageId" required>
|
<select id="page" class="form-select" @bind="@_pageId" required>
|
||||||
@foreach (Page p in PageState.Pages)
|
@foreach (Page p in PageState.Pages)
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||||
{
|
{
|
||||||
<option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option>
|
<option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option>
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@
|
|||||||
{
|
{
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<PermissionGrid EntityName="@EntityNames.Module" PermissionNames="@_permissionNames" Permissions="@_permissions" @ref="_permissionGrid" />
|
<PermissionGrid EntityName="@EntityNames.Module" PermissionNames="@_permissionNames" PermissionList="@_permissions" @ref="_permissionGrid" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -101,7 +101,7 @@
|
|||||||
private string _containerType;
|
private string _containerType;
|
||||||
private string _allPages = "false";
|
private string _allPages = "false";
|
||||||
private string _permissionNames = "";
|
private string _permissionNames = "";
|
||||||
private string _permissions = null;
|
private List<Permission> _permissions = null;
|
||||||
private string _pageId;
|
private string _pageId;
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
private Type _moduleSettingsType;
|
private Type _moduleSettingsType;
|
||||||
@ -123,14 +123,17 @@
|
|||||||
_containers = ThemeService.GetContainerControls(_themes, PageState.Page.ThemeType);
|
_containers = ThemeService.GetContainerControls(_themes, PageState.Page.ThemeType);
|
||||||
_containerType = ModuleState.ContainerType;
|
_containerType = ModuleState.ContainerType;
|
||||||
_allPages = ModuleState.AllPages.ToString();
|
_allPages = ModuleState.AllPages.ToString();
|
||||||
_permissions = ModuleState.Permissions;
|
_permissions = ModuleState.PermissionList;
|
||||||
_permissionNames = ModuleState.ModuleDefinition.PermissionNames;
|
|
||||||
_pageId = ModuleState.PageId.ToString();
|
_pageId = ModuleState.PageId.ToString();
|
||||||
createdby = ModuleState.CreatedBy;
|
createdby = ModuleState.CreatedBy;
|
||||||
createdon = ModuleState.CreatedOn;
|
createdon = ModuleState.CreatedOn;
|
||||||
modifiedby = ModuleState.ModifiedBy;
|
modifiedby = ModuleState.ModifiedBy;
|
||||||
modifiedon = ModuleState.ModifiedOn;
|
modifiedon = ModuleState.ModifiedOn;
|
||||||
|
|
||||||
|
if (ModuleState.ModuleDefinition != null)
|
||||||
|
{
|
||||||
|
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
||||||
{
|
{
|
||||||
// module settings type explicitly declared in IModule interface
|
// module settings type explicitly declared in IModule interface
|
||||||
@ -156,6 +159,11 @@
|
|||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], ModuleState.ModuleDefinitionName), MessageType.Error);
|
||||||
|
}
|
||||||
|
|
||||||
var theme = _themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
|
var theme = _themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
|
||||||
if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType))
|
if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType))
|
||||||
@ -198,7 +206,8 @@
|
|||||||
|
|
||||||
var module = ModuleState;
|
var module = ModuleState;
|
||||||
module.AllPages = bool.Parse(_allPages);
|
module.AllPages = bool.Parse(_allPages);
|
||||||
module.Permissions = _permissionGrid.GetPermissions();
|
module.PageModuleId = ModuleState.PageModuleId;
|
||||||
|
module.PermissionList = _permissionGrid.GetPermissionList();
|
||||||
await ModuleService.UpdateModuleAsync(module);
|
await ModuleService.UpdateModuleAsync(module);
|
||||||
|
|
||||||
if (_moduleSettingsType != null)
|
if (_moduleSettingsType != null)
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="parent" class="form-select" @onchange="(e => ParentChanged(e))" required>
|
<select id="parent" class="form-select" @onchange="(e => ParentChanged(e))" required>
|
||||||
<option value="-1"><@Localizer["SiteRoot"]></option>
|
<option value="-1"><@Localizer["SiteRoot"]></option>
|
||||||
@foreach (Page page in _pageList)
|
@foreach (Page page in PageState.Pages)
|
||||||
{
|
{
|
||||||
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
||||||
}
|
}
|
||||||
@ -157,6 +157,7 @@
|
|||||||
</TabPanel>
|
</TabPanel>
|
||||||
}
|
}
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
</form>
|
</form>
|
||||||
@ -167,7 +168,6 @@
|
|||||||
private List<Theme> _themeList;
|
private List<Theme> _themeList;
|
||||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
private List<Page> _pageList;
|
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _title;
|
private string _title;
|
||||||
private string _meta;
|
private string _meta;
|
||||||
@ -183,7 +183,7 @@
|
|||||||
private string _themetype = string.Empty;
|
private string _themetype = string.Empty;
|
||||||
private string _containertype = string.Empty;
|
private string _containertype = string.Empty;
|
||||||
private string _icon = string.Empty;
|
private string _icon = string.Empty;
|
||||||
private string _permissions = string.Empty;
|
private string _permissions = null;
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
private Type _themeSettingsType;
|
private Type _themeSettingsType;
|
||||||
private object _themeSettings;
|
private object _themeSettings;
|
||||||
@ -201,9 +201,7 @@
|
|||||||
_themetype = PageState.Site.DefaultThemeType;
|
_themetype = PageState.Site.DefaultThemeType;
|
||||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||||
_containertype = PageState.Site.DefaultContainerType;
|
_containertype = PageState.Site.DefaultContainerType;
|
||||||
_pageList = PageState.Pages;
|
|
||||||
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
||||||
_permissions = string.Empty;
|
|
||||||
ThemeSettings();
|
ThemeSettings();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -223,7 +221,7 @@
|
|||||||
{
|
{
|
||||||
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
|
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||||
{
|
{
|
||||||
_children.Add(p);
|
_children.Add(p);
|
||||||
}
|
}
|
||||||
@ -233,7 +231,7 @@
|
|||||||
{
|
{
|
||||||
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
|
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||||
{
|
{
|
||||||
_children.Add(p);
|
_children.Add(p);
|
||||||
}
|
}
|
||||||
@ -307,6 +305,10 @@
|
|||||||
}
|
}
|
||||||
if (_path.Contains("/"))
|
if (_path.Contains("/"))
|
||||||
{
|
{
|
||||||
|
if (_path.EndsWith("/") && _path != "/")
|
||||||
|
{
|
||||||
|
_path = _path.Substring(0, _path.Length - 1);
|
||||||
|
}
|
||||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,15 +331,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(PagePathIsDeleted(page.Path, page.SiteId, _pageList))
|
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||||
|
if (_pages.Any(item => item.Path == page.Path))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Deleted"], _path), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
|
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,7 +377,7 @@
|
|||||||
page.DefaultContainerType = string.Empty;
|
page.DefaultContainerType = string.Empty;
|
||||||
}
|
}
|
||||||
page.Icon = (_icon == null ? string.Empty : _icon);
|
page.Icon = (_icon == null ? string.Empty : _icon);
|
||||||
page.Permissions = _permissionGrid.GetPermissions();
|
page.PermissionList = _permissionGrid.GetPermissionList();
|
||||||
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
|
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
|
||||||
page.UserId = null;
|
page.UserId = null;
|
||||||
page.Meta = _meta;
|
page.Meta = _meta;
|
||||||
@ -421,14 +424,4 @@
|
|||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool PagePathIsUnique(string pagePath, int siteId, List<Page> existingPages)
|
|
||||||
{
|
|
||||||
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool PagePathIsDeleted(string pagePath, int siteId, List<Page> existingPages)
|
|
||||||
{
|
|
||||||
return existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath && page.IsDeleted == true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
||||||
<option value="-1"><@Localizer["SiteRoot"]></option>
|
<option value="-1"><@Localizer["SiteRoot"]></option>
|
||||||
@foreach (Page page in _pageList)
|
@foreach (Page page in PageState.Pages)
|
||||||
{
|
{
|
||||||
if (page.PageId != _pageId)
|
if (page.PageId != _pageId)
|
||||||
{
|
{
|
||||||
@ -148,7 +148,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
<br /><br />
|
<br />
|
||||||
|
<br />
|
||||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
|
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
@ -157,7 +158,7 @@
|
|||||||
{
|
{
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" />
|
<PermissionGrid EntityName="@EntityNames.Page" PermissionList="@_permissions" @ref="_permissionGrid" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@ -173,8 +174,8 @@
|
|||||||
<th>@Localizer["ModuleDefinition"]</th>
|
<th>@Localizer["ModuleDefinition"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Settings" Text="Edit" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" Permissions="@context.Permissions" ResourceKey="ModuleSettings" /></td>
|
<td><ActionLink Action="Settings" Text="Edit" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" ResourceKey="ModuleSettings" /></td>
|
||||||
<td><ActionDialog Header="Delete Module" Message="Are You Sure You Wish To Delete This Module?" Action="Delete" Security="SecurityAccessLevel.Edit" Permissions="@context.Permissions" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
|
<td><ActionDialog Header="Delete Module" Message="Are You Sure You Wish To Delete This Module?" Action="Delete" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
|
||||||
<td>@context.Title</td>
|
<td>@context.Title</td>
|
||||||
<td>@context.ModuleDefinition?.Name</td>
|
<td>@context.ModuleDefinition?.Name</td>
|
||||||
</Row>
|
</Row>
|
||||||
@ -189,6 +190,7 @@
|
|||||||
<br />
|
<br />
|
||||||
}
|
}
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
</form>
|
</form>
|
||||||
@ -201,7 +203,6 @@
|
|||||||
private List<Theme> _themeList;
|
private List<Theme> _themeList;
|
||||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
private List<Page> _pageList;
|
|
||||||
private List<Module> _pageModules;
|
private List<Module> _pageModules;
|
||||||
private int _pageId;
|
private int _pageId;
|
||||||
private string _name;
|
private string _name;
|
||||||
@ -220,7 +221,7 @@
|
|||||||
private string _themetype;
|
private string _themetype;
|
||||||
private string _containertype = "-";
|
private string _containertype = "-";
|
||||||
private string _icon;
|
private string _icon;
|
||||||
private string _permissions = null;
|
private List<Permission> _permissions = null;
|
||||||
private string _createdby;
|
private string _createdby;
|
||||||
private DateTime _createdon;
|
private DateTime _createdon;
|
||||||
private string _modifiedby;
|
private string _modifiedby;
|
||||||
@ -238,7 +239,6 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_pageList = PageState.Pages;
|
|
||||||
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
||||||
_themeList = await ThemeService.GetThemesAsync();
|
_themeList = await ThemeService.GetThemesAsync();
|
||||||
_themes = ThemeService.GetThemeControls(_themeList);
|
_themes = ThemeService.GetThemeControls(_themeList);
|
||||||
@ -292,7 +292,7 @@
|
|||||||
_containertype = PageState.Site.DefaultContainerType;
|
_containertype = PageState.Site.DefaultContainerType;
|
||||||
}
|
}
|
||||||
_icon = page.Icon;
|
_icon = page.Icon;
|
||||||
_permissions = page.Permissions;
|
_permissions = page.PermissionList;
|
||||||
_createdby = page.CreatedBy;
|
_createdby = page.CreatedBy;
|
||||||
_createdon = page.CreatedOn;
|
_createdon = page.CreatedOn;
|
||||||
_modifiedby = page.ModifiedBy;
|
_modifiedby = page.ModifiedBy;
|
||||||
@ -339,7 +339,7 @@
|
|||||||
{
|
{
|
||||||
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
|
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||||
{
|
{
|
||||||
_children.Add(p);
|
_children.Add(p);
|
||||||
}
|
}
|
||||||
@ -349,7 +349,7 @@
|
|||||||
{
|
{
|
||||||
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
|
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||||
{
|
{
|
||||||
_children.Add(p);
|
_children.Add(p);
|
||||||
}
|
}
|
||||||
@ -435,6 +435,10 @@
|
|||||||
}
|
}
|
||||||
if (_path.Contains("/"))
|
if (_path.Contains("/"))
|
||||||
{
|
{
|
||||||
|
if (_path.EndsWith("/") && _path != "/")
|
||||||
|
{
|
||||||
|
_path = _path.Substring(0, _path.Length - 1);
|
||||||
|
}
|
||||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,12 +461,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList))
|
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||||
|
if (_pages.Any(item => item.Path == page.Path && item.PageId != page.PageId))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
||||||
|
{
|
||||||
|
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_insert != "=")
|
if (_insert != "=")
|
||||||
{
|
{
|
||||||
Page child;
|
Page child;
|
||||||
@ -498,7 +509,7 @@
|
|||||||
page.DefaultContainerType = string.Empty;
|
page.DefaultContainerType = string.Empty;
|
||||||
}
|
}
|
||||||
page.Icon = _icon ?? string.Empty;
|
page.Icon = _icon ?? string.Empty;
|
||||||
page.Permissions = _permissionGrid.GetPermissions();
|
page.PermissionList = _permissionGrid.GetPermissionList();
|
||||||
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
|
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
|
||||||
page.UserId = null;
|
page.UserId = null;
|
||||||
page.Meta = _meta;
|
page.Meta = _meta;
|
||||||
@ -567,9 +578,4 @@
|
|||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool PagePathIsUnique(string pagePath, int siteId, int pageId, List<Page> existingPages)
|
|
||||||
{
|
|
||||||
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath && page.PageId != pageId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@
|
|||||||
private string modifiedby;
|
private string modifiedby;
|
||||||
private DateTime modifiedon;
|
private DateTime modifiedon;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
public override string Actions => "Add,Edit";
|
public override string Actions => "Add,Edit";
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ActionLink Action="Add" Security="SecurityAccessLevel.Admin" Text="Add Profile" ResourceKey="AddProfile" />
|
<ActionLink Action="Add" Text="Add Profile" Security="SecurityAccessLevel.Edit" ResourceKey="AddProfile" />
|
||||||
|
|
||||||
<Pager Items="@_profiles">
|
<Pager Items="@_profiles">
|
||||||
<Header>
|
<Header>
|
||||||
@ -19,8 +19,8 @@ else
|
|||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" ResourceKey="EditProfile" /></td>
|
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditProfile" /></td>
|
||||||
<td><ActionDialog Header="Delete Profile" Message="@string.Format(Localizer["Confirm.Profile.Delete"], context.Name)" Action="Delete" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td>
|
<td><ActionDialog Header="Delete Profile" Message="@string.Format(Localizer["Confirm.Profile.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
@ -29,7 +29,7 @@ else
|
|||||||
@code {
|
@code {
|
||||||
private List<Profile> _profiles;
|
private List<Profile> _profiles;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
|
20
Oqtane.Client/Modules/Admin/Profiles/ModuleInfo.cs
Normal file
20
Oqtane.Client/Modules/Admin/Profiles/ModuleInfo.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Modules.Admin.Profiles
|
||||||
|
{
|
||||||
|
[PrivateApi("Mark this as private, since it's not very useful in the public docs")]
|
||||||
|
public class ModuleInfo : IModule
|
||||||
|
{
|
||||||
|
public ModuleDefinition ModuleDefinition => new ModuleDefinition
|
||||||
|
{
|
||||||
|
Name = "Profiles",
|
||||||
|
Description = "Manage Profiles",
|
||||||
|
Categories = "Admin",
|
||||||
|
Version = Constants.Version,
|
||||||
|
PermissionNames = $"{PermissionNames.View},{PermissionNames.Edit}," +
|
||||||
|
$"{EntityNames.Profile}:{PermissionNames.Write}:{RoleNames.Admin}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -7,16 +7,22 @@
|
|||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<TabStrip>
|
@if (_pages == null || _modules == null)
|
||||||
|
{
|
||||||
|
<p><em>@SharedLocalizer["Loading"]</em></p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<TabStrip>
|
||||||
<TabPanel Name="Pages" ResourceKey="Pages">
|
<TabPanel Name="Pages" ResourceKey="Pages">
|
||||||
@if (_pages == null)
|
@if (!_pages.Where(item => item.IsDeleted).Any())
|
||||||
{
|
{
|
||||||
<br />
|
<br />
|
||||||
<p>@Localizer["NoPage.Deleted"]</p>
|
<p>@Localizer["NoPage.Deleted"]</p>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<Pager Items="@_pages">
|
<Pager Items="@_pages.Where(item => item.IsDeleted)" CurrentPage="@_pagePage.ToString()" OnPageChange="OnPageChangePage">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
@ -32,21 +38,19 @@
|
|||||||
<td>@context.DeletedOn</td>
|
<td>@context.DeletedOn</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
@if (_pages.Any())
|
<br />
|
||||||
{
|
<ActionDialog Header="Remove All Deleted Pages" Message="Are You Sure You Wish To Permanently Remove All Deleted Pages?" Action="Remove All Deleted Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
|
||||||
<br /><ActionDialog Header="Delete All Pages" Message="Are You Sure You Wish To Permanently Delete All Pages?" Action="Delete All Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Modules" ResourceKey="Modules">
|
<TabPanel Name="Modules" ResourceKey="Modules">
|
||||||
@if (_modules == null)
|
@if (!_modules.Where(item => item.IsDeleted).Any())
|
||||||
{
|
{
|
||||||
<br />
|
<br />
|
||||||
<p>@Localizer["NoModule.Deleted"]</p>
|
<p>@Localizer["NoModule.Deleted"]</p>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<Pager Items="@_modules">
|
<Pager Items="@_modules.Where(item => item.IsDeleted)" CurrentPage="@_pageModule.ToString()" OnPageChange="OnPageChangeModule">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
@ -58,25 +62,24 @@
|
|||||||
<Row>
|
<Row>
|
||||||
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
||||||
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
|
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
|
||||||
<td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td>
|
<td>@_pages.Find(item => item.PageId == context.PageId).Name</td>
|
||||||
<td>@context.Title</td>
|
<td>@context.Title</td>
|
||||||
<td>@context.DeletedBy</td>
|
<td>@context.DeletedBy</td>
|
||||||
<td>@context.DeletedOn</td>
|
<td>@context.DeletedOn</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
@if (_modules.Any())
|
<br />
|
||||||
{
|
<ActionDialog Header="Remove All Deleted Modules" Message="Are You Sure You Wish To Permanently Remove All Deleted Modules?" Action="Remove All Deleted Modules" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllModules())" ResourceKey="DeleteAllModules" />
|
||||||
<br /><ActionDialog Header="Delete All Modules" Message="Are You Sure You Wish To Permanently Delete All Modules?" Action="Delete All Modules" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllModules())" ResourceKey="DeleteAllModules" />
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Page> _pages;
|
private List<Page> _pages;
|
||||||
private List<Module> _modules;
|
private List<Module> _modules;
|
||||||
|
private int _pagePage = 1;
|
||||||
|
private int _pageModule = 1;
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
@ -95,10 +98,7 @@
|
|||||||
private async Task Load()
|
private async Task Load()
|
||||||
{
|
{
|
||||||
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||||
_pages = _pages.Where(item => item.IsDeleted).ToList();
|
|
||||||
|
|
||||||
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
||||||
_modules = _modules.Where(item => item.IsDeleted).ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RestorePage(Page page)
|
private async Task RestorePage(Page page)
|
||||||
@ -140,7 +140,8 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (Page page in _pages)
|
ModuleInstance.ShowProgressIndicator();
|
||||||
|
foreach (Page page in _pages.Where(item => item.IsDeleted))
|
||||||
{
|
{
|
||||||
await PageService.DeletePageAsync(page.PageId);
|
await PageService.DeletePageAsync(page.PageId);
|
||||||
await logger.LogInformation("Page Permanently Deleted {Page}", page);
|
await logger.LogInformation("Page Permanently Deleted {Page}", page);
|
||||||
@ -148,6 +149,7 @@
|
|||||||
|
|
||||||
await logger.LogInformation("Pages Permanently Deleted");
|
await logger.LogInformation("Pages Permanently Deleted");
|
||||||
await Load();
|
await Load();
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
@ -155,6 +157,7 @@
|
|||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Permanently Deleting Pages {Error}", ex.Message);
|
await logger.LogError(ex, "Error Permanently Deleting Pages {Error}", ex.Message);
|
||||||
AddModuleMessage(ex.Message, MessageType.Error);
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,10 +184,9 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
||||||
// check if there are any remaining module instances in the site
|
|
||||||
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
|
||||||
|
|
||||||
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
|
// check if there are any remaining module instances in the site
|
||||||
|
if (!_modules.Exists (item => item.ModuleId == module.ModuleId && item.PageModuleId != module.PageModuleId))
|
||||||
{
|
{
|
||||||
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
||||||
}
|
}
|
||||||
@ -204,13 +206,15 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (Module module in _modules)
|
ModuleInstance.ShowProgressIndicator();
|
||||||
|
foreach (Module module in _modules.Where(item => item.IsDeleted).ToList())
|
||||||
{
|
{
|
||||||
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
||||||
// check if there are any remaining module instances in the site
|
|
||||||
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
|
||||||
|
|
||||||
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
|
// DeletePageModuleAsync does not update _modules so remove it.
|
||||||
|
_modules.Remove(module);
|
||||||
|
// check if there are any remaining module instances in the site
|
||||||
|
if (!_modules.Exists(item => item.ModuleId == module.ModuleId && item.PageModuleId != module.PageModuleId))
|
||||||
{
|
{
|
||||||
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
||||||
}
|
}
|
||||||
@ -218,12 +222,22 @@
|
|||||||
|
|
||||||
await logger.LogInformation("Modules Permanently Deleted");
|
await logger.LogInformation("Modules Permanently Deleted");
|
||||||
await Load();
|
await Load();
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Permanently Deleting Modules {Error}", ex.Message);
|
await logger.LogError(ex, "Error Permanently Deleting Modules {Error}", ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Modules.Delete"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Modules.Delete"], MessageType.Error);
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private void OnPageChangePage(int page)
|
||||||
|
{
|
||||||
|
_pagePage = page;
|
||||||
|
}
|
||||||
|
private void OnPageChangeModule(int page)
|
||||||
|
{
|
||||||
|
_pageModule = page;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
|
||||||
@if (PageState.Site.AllowRegistration)
|
@if (PageState.Site.AllowRegistration)
|
||||||
{
|
{
|
||||||
@ -15,7 +16,7 @@
|
|||||||
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
|
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
|
||||||
</Authorized>
|
</Authorized>
|
||||||
<NotAuthorized>
|
<NotAuthorized>
|
||||||
<ModuleMessage Message="@Localizer["Info.Registration.InvalidEmail"]" Type="MessageType.Info" />
|
<ModuleMessage Message="@_passwordconstruction" Type="MessageType.Info" />
|
||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -78,11 +79,44 @@ else
|
|||||||
private string _email = string.Empty;
|
private string _email = string.Empty;
|
||||||
private string _displayname = string.Empty;
|
private string _displayname = string.Empty;
|
||||||
|
|
||||||
|
//Password construction
|
||||||
|
private string _minimumlength;
|
||||||
|
private string _uniquecharacters;
|
||||||
|
private bool _requiredigit;
|
||||||
|
private bool _requireupper;
|
||||||
|
private bool _requirelower;
|
||||||
|
private bool _requirepunctuation;
|
||||||
|
private string _passwordconstruction;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
|
|
||||||
|
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
|
||||||
|
_uniquecharacters = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", "1");
|
||||||
|
_requiredigit = bool.Parse(SettingService.GetSetting(settings, "IdentityOptions:Password:RequireDigit", "true"));
|
||||||
|
_requireupper = bool.Parse(SettingService.GetSetting(settings, "IdentityOptions:Password:RequireUppercase", "true"));
|
||||||
|
_requirelower = bool.Parse(SettingService.GetSetting(settings, "IdentityOptions:Password:RequireLowercase", "true"));
|
||||||
|
_requirepunctuation = bool.Parse(SettingService.GetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", "true"));
|
||||||
|
|
||||||
|
// Replace the placeholders with the actual values of the variables
|
||||||
|
string digitRequirement = _requiredigit ? Localizer["Password.DigitRequirement"] + ", " : "";
|
||||||
|
string uppercaseRequirement = _requireupper ? Localizer["Password.UppercaseRequirement"] + ", " : "";
|
||||||
|
string lowercaseRequirement = _requirelower ? Localizer["Password.LowercaseRequirement"] + ", " : "";
|
||||||
|
string punctuationRequirement = _requirepunctuation ? Localizer["Password.PunctuationRequirement"] + ", " : "";
|
||||||
|
|
||||||
|
// Replace the placeholders with the actual values of the variables
|
||||||
|
string passwordValidationCriteriaTemplate = Localizer["Password.ValidationCriteria"];
|
||||||
|
_passwordconstruction = Localizer["Info.Registration.InvalidEmail"] + ". " + string.Format(passwordValidationCriteriaTemplate,
|
||||||
|
_minimumlength, _uniquecharacters, digitRequirement, uppercaseRequirement, lowercaseRequirement, punctuationRequirement);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Register()
|
private async Task Register()
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
private string _description = string.Empty;
|
private string _description = string.Empty;
|
||||||
private string _isautoassigned = "False";
|
private string _isautoassigned = "False";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
private async Task SaveRole()
|
private async Task SaveRole()
|
||||||
{
|
{
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
private string _modifiedby;
|
private string _modifiedby;
|
||||||
private DateTime _modifiedon;
|
private DateTime _modifiedon;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ActionLink Action="Add" Text="Add Role" ResourceKey="AddRole" />
|
<ActionLink Action="Add" Text="Add Role" Security="SecurityAccessLevel.Edit" ResourceKey="AddRole" />
|
||||||
|
|
||||||
<Pager Items="@_roles">
|
<Pager Items="@_roles">
|
||||||
<Header>
|
<Header>
|
||||||
@ -20,9 +20,9 @@ else
|
|||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.RoleId.ToString())" Disabled="@(context.IsSystem)" ResourceKey="Edit" /></td>
|
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.RoleId.ToString())" Security="SecurityAccessLevel.Edit" Disabled="@(context.IsSystem)" ResourceKey="Edit" /></td>
|
||||||
<td><ActionDialog Header="Delete Role" Message="@string.Format(Localizer["Confirm.DeleteUser"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteRole(context))" Disabled="@(context.IsSystem)" ResourceKey="DeleteRole" /></td>
|
<td><ActionDialog Header="Delete Role" Message="@string.Format(Localizer["Confirm.DeleteUser"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteRole(context))" Disabled="@(context.IsSystem)" ResourceKey="DeleteRole" /></td>
|
||||||
<td><ActionLink Action="Users" Parameters="@($"id=" + context.RoleId.ToString())" ResourceKey="Users" /></td>
|
<td><ActionLink Action="Users" Parameters="@($"id=" + context.RoleId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Users" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
@ -31,7 +31,7 @@ else
|
|||||||
@code {
|
@code {
|
||||||
private List<Role> _roles;
|
private List<Role> _roles;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
@ -59,7 +59,7 @@ else
|
|||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
_roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true);
|
_roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true);
|
||||||
_roles = _roles.Where(item => item.Name != RoleNames.Everyone).ToList();
|
_roles.RemoveAll(item => item.Name == RoleNames.Everyone || item.Name == RoleNames.Unauthenticated);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
21
Oqtane.Client/Modules/Admin/Roles/ModuleInfo.cs
Normal file
21
Oqtane.Client/Modules/Admin/Roles/ModuleInfo.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Modules.Admin.Roles
|
||||||
|
{
|
||||||
|
[PrivateApi("Mark this as private, since it's not very useful in the public docs")]
|
||||||
|
public class ModuleInfo : IModule
|
||||||
|
{
|
||||||
|
public ModuleDefinition ModuleDefinition => new ModuleDefinition
|
||||||
|
{
|
||||||
|
Name = "Roles",
|
||||||
|
Description = "Manage Roles",
|
||||||
|
Categories = "Admin",
|
||||||
|
Version = Constants.Version,
|
||||||
|
PermissionNames = $"{PermissionNames.View},{PermissionNames.Edit}," +
|
||||||
|
$"{EntityNames.Role}:{PermissionNames.Write}:{RoleNames.Admin}," +
|
||||||
|
$"{EntityNames.UserRole}:{PermissionNames.Write}:{RoleNames.Admin}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -23,13 +23,7 @@ else
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="user" HelpText="Select a user" ResourceKey="User">User: </Label>
|
<Label Class="col-sm-3" For="user" HelpText="Select a user" ResourceKey="User">User: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="user" class="form-select" @bind="@userid" required>
|
<AutoComplete OnSearch="GetUsers" Placeholder="@Localizer["User.Select"]" @ref="user" />
|
||||||
<option value="-1"><@Localizer["User.Select"]></option>
|
|
||||||
@foreach (UserRole userrole in users)
|
|
||||||
{
|
|
||||||
<option value="@(userrole.UserId)">@userrole.User.DisplayName</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -64,7 +58,7 @@ else
|
|||||||
<td>@context.EffectiveDate</td>
|
<td>@context.EffectiveDate</td>
|
||||||
<td>@context.ExpiryDate</td>
|
<td>@context.ExpiryDate</td>
|
||||||
<td>
|
<td>
|
||||||
<ActionDialog Header="Remove User" Message="@string.Format(Localizer["Confirm.User.DeleteRole"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || PageState.User.Username == UserNames.Host)" ResourceKey="DeleteUserRole" />
|
<ActionDialog Header="Remove User" Message="@string.Format(Localizer["Confirm.User.DeleteRole"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || context.User.Username == UserNames.Host || context.User.UserId == PageState.User.UserId)" ResourceKey="DeleteUserRole" />
|
||||||
</td>
|
</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
@ -80,13 +74,12 @@ else
|
|||||||
|
|
||||||
private int roleid;
|
private int roleid;
|
||||||
private string name = string.Empty;
|
private string name = string.Empty;
|
||||||
private List<UserRole> users;
|
private AutoComplete user;
|
||||||
private int userid = -1;
|
|
||||||
private DateTime? effectivedate = null;
|
private DateTime? effectivedate = null;
|
||||||
private DateTime? expirydate = null;
|
private DateTime? expirydate = null;
|
||||||
private List<UserRole> userroles;
|
private List<UserRole> userroles;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
@ -95,7 +88,6 @@ else
|
|||||||
roleid = Int32.Parse(PageState.QueryString["id"]);
|
roleid = Int32.Parse(PageState.QueryString["id"]);
|
||||||
Role role = await RoleService.GetRoleAsync(roleid);
|
Role role = await RoleService.GetRoleAsync(roleid);
|
||||||
name = role.Name;
|
name = role.Name;
|
||||||
users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
|
||||||
await GetUserRoles();
|
await GetUserRoles();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -105,6 +97,22 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<Dictionary<string, string>> GetUsers(string filter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
||||||
|
return users.Where(item => item.User.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ToDictionary(item => item.UserId.ToString(), item => item.User.DisplayName);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Users {filter} {Error}", filter, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error);
|
||||||
|
}
|
||||||
|
return new Dictionary<string, string>();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task GetUserRoles()
|
private async Task GetUserRoles()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -126,7 +134,7 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (userid != -1)
|
if (!string.IsNullOrEmpty(user.Key) && int.TryParse(user.Key, out int userid))
|
||||||
{
|
{
|
||||||
var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault();
|
var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault();
|
||||||
if (userrole != null)
|
if (userrole != null)
|
||||||
@ -149,6 +157,7 @@ else
|
|||||||
await logger.LogInformation("User Assigned To Role {UserRole}", userrole);
|
await logger.LogInformation("User Assigned To Role {UserRole}", userrole);
|
||||||
AddModuleMessage(Localizer["Success.User.AssignedRole"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.User.AssignedRole"], MessageType.Success);
|
||||||
await GetUserRoles();
|
await GetUserRoles();
|
||||||
|
user.Clear();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -70,6 +70,21 @@
|
|||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="homepage" HelpText="Select the home page for the site (to be used if there is no page with a path of '/')" ResourceKey="HomePage">Home Page: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="homepage" class="form-select" @bind="@_homepageid" required>
|
||||||
|
<option value="-"><@Localizer["Not Specified"]></option>
|
||||||
|
@foreach (Page page in PageState.Pages)
|
||||||
|
{
|
||||||
|
if (UserSecurity.ContainsRole(page.PermissionList, PermissionNames.View, RoleNames.Everyone))
|
||||||
|
{
|
||||||
|
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Deleted? </Label>
|
<Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Deleted? </Label>
|
||||||
@ -80,6 +95,12 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="sitemap" HelpText="The site map url for this site which can be submitted to search engines for indexing" ResourceKey="SiteMap">Site Map: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="sitemap" class="form-control" @bind="@_sitemap" required disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
|
<Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -112,7 +133,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmptUsername">Username: </Label>
|
<Label Class="col-sm-3" For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmtpUsername">Username: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="username" class="form-control" @bind="@_smtpusername" />
|
<input id="username" class="form-control" @bind="@_smtpusername" />
|
||||||
</div>
|
</div>
|
||||||
@ -127,10 +148,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="sender" HelpText="Enter the email which emails will be sent from. Please note that this email address may need to be authorized with the SMTP server." ResourceKey="SmptSender">Email Sender: </Label>
|
<Label Class="col-sm-3" For="sender" HelpText="Enter the email which emails will be sent from. Please note that this email address may need to be authorized with the SMTP server." ResourceKey="SmtpSender">Email Sender: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="sender" class="form-control" @bind="@_smtpsender" />
|
<input id="sender" class="form-control" @bind="@_smtpsender" />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="relay" HelpText="Only specify this option if you have properly configured an SMTP Relay Service to route your outgoing mail. This option will send notifications from the user's email rather than from the Email Sender specified above." ResourceKey="SmtpRelay">Relay Configured? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="relay" class="form-select" @bind="@_smtprelay" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label>
|
<Label Class="col-sm-3" For="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||||
@ -172,20 +202,50 @@
|
|||||||
<Section Name="Aliases" Heading="Aliases" ResourceKey="Aliases">
|
<Section Name="Aliases" Heading="Aliases" ResourceKey="Aliases">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="alias" HelpText="The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they should be separated by commas." ResourceKey="Aliases">Aliases: </Label>
|
<Label Class="col-sm-3" For="aliases" HelpText="The list of aliases for this site" ResourceKey="Aliases">Aliases: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
<button type="button" class="btn btn-primary" @onclick="AddAlias">@SharedLocalizer["Add"]</button>
|
||||||
</div>
|
<Pager Items="@_aliases">
|
||||||
</div>
|
<Header>
|
||||||
<div class="row mb-1 align-items-center">
|
<th style="width: 1px;"> </th>
|
||||||
<Label Class="col-sm-3" For="defaultalias" HelpText="The default alias for the site. Requests for non-default aliases will be redirected to the default alias." ResourceKey="DefaultAlias">Default Alias: </Label>
|
<th style="width: 1px;"> </th>
|
||||||
<div class="col-sm-9">
|
<th>@Localizer["AliasName"]</th>
|
||||||
<select id="defaultalias" class="form-select" @bind="@_defaultalias" required>
|
<th>@Localizer["AliasDefault"]</th>
|
||||||
@foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
</Header>
|
||||||
|
<Row>
|
||||||
|
@if (context.AliasId != _aliasid)
|
||||||
{
|
{
|
||||||
<option value="@name">@name</option>
|
<td>
|
||||||
|
@if (_aliasid == -1)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="@(() => EditAlias(context))">@SharedLocalizer["Edit"]</button>
|
||||||
}
|
}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@if (_aliasid == -1)
|
||||||
|
{
|
||||||
|
<ActionDialog Action="Delete" OnClick="@(async () => await DeleteAlias(context))" ResourceKey="DeleteModule" Class="btn btn-danger" Header="Delete Alias" Message="@string.Format(Localizer["Confirm.Alias.Delete", context.Name])" />
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td>@context.Name</td>
|
||||||
|
<td>@context.IsDefault</td>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<td><button type="button" class="btn btn-success" @onclick="@(async () => await SaveAlias())">@SharedLocalizer["Save"]</button></td>
|
||||||
|
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => await CancelAlias())">@SharedLocalizer["Cancel"]</button></td>
|
||||||
|
<td>
|
||||||
|
<input id="aliasname" class="form-control" @bind="@_aliasname" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select id="defaultaias" class="form-select" @bind="@_defaultalias" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
</select>
|
</select>
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -198,6 +258,7 @@
|
|||||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||||
|
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -212,16 +273,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
<Section Name="TenantInformation" Heading="Tenant Information" ResourceKey="TenantInformation">
|
<Section Name="TenantInformation" Heading="Database" ResourceKey="TenantInformation">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="tenant" HelpText="The tenant for the site" ResourceKey="Tenant">Tenant: </Label>
|
<Label Class="col-sm-3" For="tenant" HelpText="The name of the database used for the site" ResourceKey="Tenant">Database: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
|
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="database" HelpText="The database for the tenant" ResourceKey="Database">Database: </Label>
|
<Label Class="col-sm-3" For="database" HelpText="The type of database" ResourceKey="Database">Type: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="database" class="form-control" @bind="@_database" readonly />
|
<input id="database" class="form-control" @bind="@_database" readonly />
|
||||||
</div>
|
</div>
|
||||||
@ -229,7 +290,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
|
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="2" readonly></textarea>
|
<input id="connectionstring" class="form-control" @bind="@_connectionstring" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -253,8 +314,9 @@
|
|||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
private string _name = string.Empty;
|
private string _name = string.Empty;
|
||||||
private List<Alias> _aliases;
|
private List<Alias> _aliases;
|
||||||
private string _defaultalias = string.Empty;
|
private int _aliasid = -1;
|
||||||
private string _urls = string.Empty;
|
private string _aliasname;
|
||||||
|
private string _defaultalias;
|
||||||
private string _runtime = "";
|
private string _runtime = "";
|
||||||
private string _prerender = "";
|
private string _prerender = "";
|
||||||
private int _logofileid = -1;
|
private int _logofileid = -1;
|
||||||
@ -264,6 +326,8 @@
|
|||||||
private string _themetype = "-";
|
private string _themetype = "-";
|
||||||
private string _containertype = "-";
|
private string _containertype = "-";
|
||||||
private string _admincontainertype = "-";
|
private string _admincontainertype = "-";
|
||||||
|
private string _homepageid = "-";
|
||||||
|
private string _sitemap = "";
|
||||||
private string _smtphost = string.Empty;
|
private string _smtphost = string.Empty;
|
||||||
private string _smtpport = string.Empty;
|
private string _smtpport = string.Empty;
|
||||||
private string _smtpssl = "False";
|
private string _smtpssl = "False";
|
||||||
@ -272,6 +336,7 @@
|
|||||||
private string _smtppasswordtype = "password";
|
private string _smtppasswordtype = "password";
|
||||||
private string _togglesmtppassword = string.Empty;
|
private string _togglesmtppassword = string.Empty;
|
||||||
private string _smtpsender = string.Empty;
|
private string _smtpsender = string.Empty;
|
||||||
|
private string _smtprelay = "False";
|
||||||
private string _retention = string.Empty;
|
private string _retention = string.Empty;
|
||||||
private string _pwaisenabled;
|
private string _pwaisenabled;
|
||||||
private int _pwaappiconfileid = -1;
|
private int _pwaappiconfileid = -1;
|
||||||
@ -303,11 +368,9 @@
|
|||||||
_runtime = site.Runtime;
|
_runtime = site.Runtime;
|
||||||
_prerender = site.RenderMode.Replace(_runtime, "");
|
_prerender = site.RenderMode.Replace(_runtime, "");
|
||||||
_isdeleted = site.IsDeleted.ToString();
|
_isdeleted = site.IsDeleted.ToString();
|
||||||
|
_sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/pages/sitemap.xml";
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
|
||||||
{
|
|
||||||
await GetAliases();
|
await GetAliases();
|
||||||
}
|
|
||||||
|
|
||||||
if (site.LogoFileId != null)
|
if (site.LogoFileId != null)
|
||||||
{
|
{
|
||||||
@ -325,6 +388,11 @@
|
|||||||
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
|
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
|
||||||
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
|
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
|
||||||
|
|
||||||
|
if (site.HomePageId != null)
|
||||||
|
{
|
||||||
|
_homepageid = site.HomePageId.Value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
_pwaisenabled = site.PwaIsEnabled.ToString();
|
_pwaisenabled = site.PwaIsEnabled.ToString();
|
||||||
if (site.PwaAppIconFileId != null)
|
if (site.PwaAppIconFileId != null)
|
||||||
{
|
{
|
||||||
@ -343,6 +411,7 @@
|
|||||||
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
|
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
|
||||||
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||||
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
||||||
|
_smtprelay = SettingService.GetSetting(settings, "SMTPRelay", "False");
|
||||||
_retention = SettingService.GetSetting(settings, "NotificationRetention", "30");
|
_retention = SettingService.GetSetting(settings, "NotificationRetention", "30");
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
@ -394,7 +463,7 @@
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Theme.LoadPane"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Theme.LoadPane"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,24 +477,6 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_name != string.Empty && _themetype != "-" && _containertype != "-")
|
if (_name != string.Empty && _themetype != "-" && _containertype != "-")
|
||||||
{
|
|
||||||
var unique = true;
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
|
||||||
{
|
|
||||||
_urls = Regex.Replace(_urls, @"\r\n?|\n", ","); // convert line breaks to commas
|
|
||||||
var aliases = await AliasService.GetAliasesAsync();
|
|
||||||
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
|
||||||
{
|
|
||||||
var alias = aliases.Where(item => item.Name == name).FirstOrDefault();
|
|
||||||
if (alias != null && unique)
|
|
||||||
{
|
|
||||||
unique = (alias.TenantId == PageState.Site.TenantId && alias.SiteId == PageState.Site.SiteId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (unique && string.IsNullOrEmpty(_defaultalias)) unique = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unique)
|
|
||||||
{
|
{
|
||||||
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
||||||
if (site != null)
|
if (site != null)
|
||||||
@ -469,6 +520,7 @@
|
|||||||
refresh = true; // needs to be refreshed on client
|
refresh = true; // needs to be refreshed on client
|
||||||
}
|
}
|
||||||
site.AdminContainerType = _admincontainertype;
|
site.AdminContainerType = _admincontainertype;
|
||||||
|
site.HomePageId = (_homepageid != "-" ? int.Parse(_homepageid) : null);
|
||||||
|
|
||||||
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
||||||
{
|
{
|
||||||
@ -499,46 +551,10 @@
|
|||||||
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||||
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||||
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
|
||||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
|
||||||
{
|
|
||||||
var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
|
||||||
.Select(sValue => sValue.Trim()).ToArray();
|
|
||||||
foreach (Alias alias in _aliases)
|
|
||||||
{
|
|
||||||
if (!names.Contains(alias.Name.Trim()))
|
|
||||||
{
|
|
||||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string name in names)
|
|
||||||
{
|
|
||||||
var alias = _aliases.Find(item => item.Name.Trim() == name);
|
|
||||||
if (alias == null)
|
|
||||||
{
|
|
||||||
alias = new Alias();
|
|
||||||
alias.Name = name;
|
|
||||||
alias.TenantId = site.TenantId;
|
|
||||||
alias.SiteId = site.SiteId;
|
|
||||||
alias.IsDefault = (name == _defaultalias);
|
|
||||||
await AliasService.AddAliasAsync(alias);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (alias.Name != name || alias.IsDefault != (alias.Name.Trim() == _defaultalias))
|
|
||||||
{
|
|
||||||
alias.Name = name;
|
|
||||||
alias.IsDefault = (name == _defaultalias);
|
|
||||||
await AliasService.UpdateAliasAsync(alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await GetAliases();
|
|
||||||
}
|
|
||||||
|
|
||||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||||
|
|
||||||
if (refresh || reload)
|
if (refresh || reload)
|
||||||
@ -552,11 +568,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // deuplicate alias or default alias not specified
|
|
||||||
{
|
|
||||||
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
|
||||||
@ -578,19 +589,19 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var sites = await SiteService.GetSitesAsync();
|
var aliases = await AliasService.GetAliasesAsync();
|
||||||
if (sites.Count > 1)
|
if (aliases.Any(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId))
|
||||||
{
|
{
|
||||||
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
|
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
|
||||||
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
|
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
|
||||||
|
|
||||||
var aliases = await AliasService.GetAliasesAsync();
|
foreach (Alias alias in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
|
||||||
foreach (Alias a in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
|
|
||||||
{
|
{
|
||||||
await AliasService.DeleteAliasAsync(a.AliasId);
|
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl("admin/sites"));
|
var redirect = aliases.First(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId);
|
||||||
|
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + redirect.Name, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -611,12 +622,12 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
||||||
SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||||
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||||
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||||
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||||
SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
await logger.LogInformation("Site SMTP Settings Saved");
|
await logger.LogInformation("Site SMTP Settings Saved");
|
||||||
|
|
||||||
@ -637,20 +648,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GetAliases()
|
|
||||||
{
|
|
||||||
_urls = string.Empty;
|
|
||||||
_defaultalias = string.Empty;
|
|
||||||
_aliases = await AliasService.GetAliasesAsync();
|
|
||||||
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
|
|
||||||
foreach (Alias alias in _aliases)
|
|
||||||
{
|
|
||||||
_urls += (_urls == string.Empty) ? alias.Name.Trim() : ", " + alias.Name.Trim();
|
|
||||||
if (alias.IsDefault && string.IsNullOrEmpty(_defaultalias)) _defaultalias = alias.Name.Trim();
|
|
||||||
}
|
|
||||||
if (string.IsNullOrEmpty(_defaultalias)) _defaultalias = _aliases.First().Name.Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ToggleSMTPPassword()
|
private void ToggleSMTPPassword()
|
||||||
{
|
{
|
||||||
if (_smtppasswordtype == "password")
|
if (_smtppasswordtype == "password")
|
||||||
@ -664,4 +661,84 @@
|
|||||||
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task GetAliases()
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
_aliases = await AliasService.GetAliasesAsync();
|
||||||
|
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddAlias()
|
||||||
|
{
|
||||||
|
_aliases.Add(new Alias { AliasId = 0, Name = "", IsDefault = false });
|
||||||
|
_aliasid = 0;
|
||||||
|
_aliasname = "";
|
||||||
|
_defaultalias = "False";
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditAlias(Alias alias)
|
||||||
|
{
|
||||||
|
_aliasid = alias.AliasId;
|
||||||
|
_aliasname = alias.Name;
|
||||||
|
_defaultalias = alias.IsDefault.ToString();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAlias(Alias alias)
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||||
|
await GetAliases();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveAlias()
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_aliasname))
|
||||||
|
{
|
||||||
|
var aliases = await AliasService.GetAliasesAsync();
|
||||||
|
var alias = aliases.Where(item => item.Name == _aliasname).FirstOrDefault();
|
||||||
|
bool unique = (alias == null || alias.AliasId == _aliasid);
|
||||||
|
if (unique)
|
||||||
|
{
|
||||||
|
if (_aliasid == 0)
|
||||||
|
{
|
||||||
|
alias = new Alias { SiteId = PageState.Site.SiteId, TenantId = PageState.Site.TenantId, Name = _aliasname, IsDefault = bool.Parse(_defaultalias) };
|
||||||
|
await AliasService.AddAliasAsync(alias);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alias = _aliases.Single(item => item.AliasId == _aliasid);
|
||||||
|
alias.Name = _aliasname;
|
||||||
|
alias.IsDefault = bool.Parse(_defaultalias);
|
||||||
|
await AliasService.UpdateAliasAsync(alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // duplicate alias
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await GetAliases();
|
||||||
|
_aliasid = -1;
|
||||||
|
_aliasname = "";
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CancelAlias()
|
||||||
|
{
|
||||||
|
await GetAliases();
|
||||||
|
_aliasid = -1;
|
||||||
|
_aliasname = "";
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="alias" HelpText="Enter the aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they can be separated by commas." ResourceKey="Aliases">Aliases: </Label>
|
<Label Class="col-sm-3" For="alias" HelpText="Enter the aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder)." ResourceKey="Aliases">Aliases: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
||||||
</div>
|
</div>
|
||||||
@ -89,6 +89,7 @@ else
|
|||||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||||
|
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -102,7 +103,7 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="tenant" HelpText="Select the tenant for the site" ResourceKey="Tenant">Tenant: </Label>
|
<Label Class="col-sm-3" For="tenant" HelpText="Select the database for the site" ResourceKey="Tenant">Database: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="tenant" class="form-select" @onchange="(e => TenantChanged(e))" required>
|
<select id="tenant" class="form-select" @onchange="(e => TenantChanged(e))" required>
|
||||||
<option value="-"><@Localizer["Tenant.Select"]></option>
|
<option value="-"><@Localizer["Tenant.Select"]></option>
|
||||||
@ -120,32 +121,50 @@ else
|
|||||||
<hr class="app-rule" />
|
<hr class="app-rule" />
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="name" HelpText="Enter the name for the tenant" ResourceKey="TenantName">Tenant Name: </Label>
|
<Label Class="col-sm-3" For="name" HelpText="Enter the name for the database" ResourceKey="TenantName">Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required />
|
<input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="databaseType" HelpText="Select the database type for the tenant" ResourceKey="DatabaseType">Database Type: </Label>
|
<Label Class="col-sm-3" For="databaseType" HelpText="Select the database type" ResourceKey="DatabaseType">Type: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
|
@if (_databases != null)
|
||||||
|
{
|
||||||
|
<div class="input-group">
|
||||||
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
||||||
@foreach (var database in _databases)
|
@foreach (var database in _databases)
|
||||||
{
|
{
|
||||||
if (database.IsDefault)
|
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
@if (!_showConnectionString)
|
||||||
{
|
{
|
||||||
<option value="@database.Name" selected>@Localizer[@database.Name]</option>
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionString"]</button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionParameters"]</button>
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@if (!_showConnectionString)
|
||||||
|
{
|
||||||
if (_databaseConfigType != null)
|
if (_databaseConfigType != null)
|
||||||
{
|
{
|
||||||
@DatabaseConfigComponent;
|
@DatabaseConfigComponent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">Settings:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="hostUsername" HelpText="Enter the username of an existing host user" ResourceKey="HostUsername">Host Username:</Label>
|
<Label Class="col-sm-3" For="hostUsername" HelpText="Enter the username of an existing host user" ResourceKey="HostUsername">Host Username:</Label>
|
||||||
@ -172,11 +191,12 @@ else
|
|||||||
private List<Database> _databases;
|
private List<Database> _databases;
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private string _databaseName = "LocalDB";
|
private string _databaseName;
|
||||||
private Type _databaseConfigType;
|
private Type _databaseConfigType;
|
||||||
private object _databaseConfig;
|
private object _databaseConfig;
|
||||||
private RenderFragment DatabaseConfigComponent { get; set; }
|
private RenderFragment DatabaseConfigComponent { get; set; }
|
||||||
|
private bool _showConnectionString = false;
|
||||||
|
private string _connectionString = string.Empty;
|
||||||
|
|
||||||
private List<Theme> _themeList;
|
private List<Theme> _themeList;
|
||||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||||
@ -208,7 +228,16 @@ else
|
|||||||
_themeList = await ThemeService.GetThemesAsync();
|
_themeList = await ThemeService.GetThemesAsync();
|
||||||
_themes = ThemeService.GetThemeControls(_themeList);
|
_themes = ThemeService.GetThemeControls(_themeList);
|
||||||
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
||||||
|
|
||||||
_databases = await DatabaseService.GetDatabasesAsync();
|
_databases = await DatabaseService.GetDatabasesAsync();
|
||||||
|
if (_databases.Exists(item => item.IsDefault))
|
||||||
|
{
|
||||||
|
_databaseName = _databases.Find(item => item.IsDefault).Name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_databaseName = "LocalDB";
|
||||||
|
}
|
||||||
LoadDatabaseConfigComponent();
|
LoadDatabaseConfigComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +246,7 @@ else
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_databaseName = (string)eventArgs.Value;
|
_databaseName = (string)eventArgs.Value;
|
||||||
|
_showConnectionString = false;
|
||||||
LoadDatabaseConfigComponent();
|
LoadDatabaseConfigComponent();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -286,7 +315,7 @@ else
|
|||||||
_urls = Regex.Replace(_urls, @"\r\n?|\n", ",");
|
_urls = Regex.Replace(_urls, @"\r\n?|\n", ",");
|
||||||
var duplicates = new List<string>();
|
var duplicates = new List<string>();
|
||||||
var aliases = await AliasService.GetAliasesAsync();
|
var aliases = await AliasService.GetAliasesAsync();
|
||||||
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
foreach (string name in _urls.Split(',', StringSplitOptions.RemoveEmptyEntries))
|
||||||
{
|
{
|
||||||
if (aliases.Exists(item => item.Name == name))
|
if (aliases.Exists(item => item.Name == name))
|
||||||
{
|
{
|
||||||
@ -300,22 +329,30 @@ else
|
|||||||
|
|
||||||
if (_tenantid == "+")
|
if (_tenantid == "+")
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(_tenantName) && _tenants.FirstOrDefault(item => item.Name == _tenantName) == null)
|
if (!string.IsNullOrEmpty(_tenantName) && !_tenants.Exists(item => item.Name == _tenantName))
|
||||||
{
|
{
|
||||||
// validate host credentials
|
// validate host credentials
|
||||||
var user = new User();
|
var user = new User();
|
||||||
user.SiteId = PageState.Site.SiteId;
|
user.SiteId = PageState.Site.SiteId;
|
||||||
user.Username = _hostusername;
|
user.Username = _hostusername;
|
||||||
user.Password = _hostpassword;
|
user.Password = _hostpassword;
|
||||||
user = await UserService.LoginUserAsync(user);
|
user.LastIPAddress = PageState.RemoteIPAddress;
|
||||||
|
user = await UserService.LoginUserAsync(user, false, false);
|
||||||
if (user.IsAuthenticated)
|
if (user.IsAuthenticated)
|
||||||
{
|
{
|
||||||
|
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||||
var connectionString = String.Empty;
|
var connectionString = String.Empty;
|
||||||
|
if (_showConnectionString)
|
||||||
|
{
|
||||||
|
connectionString = _connectionString;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
{
|
{
|
||||||
connectionString = databaseConfigControl.GetConnectionString();
|
connectionString = databaseConfigControl.GetConnectionString();
|
||||||
}
|
}
|
||||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
}
|
||||||
|
|
||||||
if (connectionString != "")
|
if (connectionString != "")
|
||||||
{
|
{
|
||||||
@ -396,4 +433,13 @@ else
|
|||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ToggleConnectionString()
|
||||||
|
{
|
||||||
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
|
{
|
||||||
|
_connectionString = databaseConfigControl.GetConnectionString();
|
||||||
|
}
|
||||||
|
_showConnectionString = !_showConnectionString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
@namespace Oqtane.Modules.Admin.Sql
|
@namespace Oqtane.Modules.Admin.Sql
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject ISystemService SystemService
|
||||||
@inject ITenantService TenantService
|
@inject ITenantService TenantService
|
||||||
@inject IDatabaseService DatabaseService
|
@inject IDatabaseService DatabaseService
|
||||||
@inject ISqlService SqlService
|
@inject ISqlService SqlService
|
||||||
@ -14,40 +15,112 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="tenant" HelpText="Select the tenant for the SQL server" ResourceKey="Tenant">Tenant: </Label>
|
<Label Class="col-sm-3" For="connection" HelpText="Select a database connection (from appsettings.json)" ResourceKey="Connection">Connection: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="tenant" class="form-select" value="@_tenantid" @onchange="(e => TenantChanged(e))">
|
<select id="tenant" class="form-select" value="@_connection" @onchange="(e => ConnectionChanged(e))">
|
||||||
<option value="-1"><@Localizer["Tenant.Select"]></option>
|
<option value="-"><@Localizer["Connection.Select"]></option>
|
||||||
@foreach (Tenant tenant in _tenants)
|
<option value="+"><@Localizer["Connection.Add"]></option>
|
||||||
|
@foreach (var connection in _connections)
|
||||||
{
|
{
|
||||||
<option value="@tenant.TenantId">@tenant.Name</option>
|
<option value="@connection.Key">@connection.Key</option>
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (_tenantid != "-1")
|
@if (_connection == "+")
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="database" HelpText="The database for the tenant" ResourceKey="Database">Database: </Label>
|
<Label Class="col-sm-3" For="name" HelpText="Enter the name of the connection" ResourceKey="Name">Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="database" class="form-control" @bind="@_database" readonly />
|
<input id="name" class="form-control" @bind="@_name" maxlength="100" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
|
<Label Class="col-sm-3" For="databasetype" HelpText="Select the database type" ResourceKey="DatabaseType">Type: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="2" readonly></textarea>
|
@if (_databases != null)
|
||||||
|
{
|
||||||
|
<div class="input-group">
|
||||||
|
<select id="databasetype" class="form-select" value="@_databasetype" @onchange="(e => DatabaseTypeChanged(e))" required>
|
||||||
|
@foreach (var database in _databases)
|
||||||
|
{
|
||||||
|
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
@if (!_showConnectionString)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="ShowConnectionString">@Localizer["EnterConnectionString"]</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="ShowConnectionString">@Localizer["EnterConnectionParameters"]</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@if (!_showConnectionString)
|
||||||
|
{
|
||||||
|
if (_databaseConfigType != null)
|
||||||
|
{
|
||||||
|
@DatabaseConfigComponent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="sqlQeury" HelpText="Enter the query for the SQL server" ResourceKey="SqlQuery">SQL Query: </Label>
|
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">Settings:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="sqlQeury" class="form-control" @bind="@_sql" rows="3"></textarea>
|
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="3"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="Add">@Localizer["Add"]</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@if (_connection != "-")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="databasetype" HelpText="The database type" ResourceKey="DatabaseType">Type: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
@if (_databases != null)
|
||||||
|
{
|
||||||
|
<select id="databasetype" class="form-select" @bind="@_databasetype" required>
|
||||||
|
<option value="-"><@Localizer["Type.Select"]></option>
|
||||||
|
@foreach (var database in _databases)
|
||||||
|
{
|
||||||
|
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (!string.IsNullOrEmpty(_tenant))
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="tenant" HelpText="The database using this connection" ResourceKey="Tenant">Database: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection string" ResourceKey="ConnectionString">Settings: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="connectionstring" type="@_connectionstringtype" class="form-control" @bind="@_connectionstring" readonly />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleConnectionString">@_connectionstringtoggle</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="sqlQuery" HelpText="Enter a valid SQL query for the database" ResourceKey="SqlQuery">SQL Query: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="sqlQuery" class="form-control" @bind="@_sql" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="Execute">@Localizer["Execute"]</button>
|
<button type="button" class="btn btn-success" @onclick="Execute">@Localizer["Execute"]</button>
|
||||||
@ -77,15 +150,30 @@ else
|
|||||||
@Localizer["Return.NoResult"]
|
@Localizer["Return.NoResult"]
|
||||||
}
|
}
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private string _connection = "-";
|
||||||
|
private Dictionary<string, object> _connections;
|
||||||
private List<Tenant> _tenants;
|
private List<Tenant> _tenants;
|
||||||
private string _tenantid = "-1";
|
private List<Database> _databases;
|
||||||
private string _database = string.Empty;
|
|
||||||
|
private string _name = string.Empty;
|
||||||
|
private string _databasetype = string.Empty;
|
||||||
|
private Type _databaseConfigType;
|
||||||
|
private object _databaseConfig;
|
||||||
|
private RenderFragment DatabaseConfigComponent { get; set; }
|
||||||
|
private bool _showConnectionString = false;
|
||||||
|
private string _tenant = string.Empty;
|
||||||
private string _connectionstring = string.Empty;
|
private string _connectionstring = string.Empty;
|
||||||
|
private string _connectionstringtype = "password";
|
||||||
|
private string _connectionstringtoggle = string.Empty;
|
||||||
private string _sql = string.Empty;
|
private string _sql = string.Empty;
|
||||||
private List<Dictionary<string, string>> _results;
|
private List<Dictionary<string, string>> _results;
|
||||||
|
|
||||||
@ -95,7 +183,10 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_connections = await SystemService.GetSystemInfoAsync("connectionstrings");
|
||||||
_tenants = await TenantService.GetTenantsAsync();
|
_tenants = await TenantService.GetTenantsAsync();
|
||||||
|
_databases = await DatabaseService.GetDatabasesAsync();
|
||||||
|
_connectionstringtoggle = SharedLocalizer["ShowPassword"];
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -104,35 +195,127 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void TenantChanged(ChangeEventArgs e)
|
private async void ConnectionChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_tenantid = (string)e.Value;
|
_connection = (string)e.Value;
|
||||||
var tenants = await TenantService.GetTenantsAsync();
|
if (_connection != "-" && _connection != "+")
|
||||||
var _databases = await DatabaseService.GetDatabasesAsync();
|
{
|
||||||
var tenant = tenants.Find(item => item.TenantId == int.Parse(_tenantid));
|
_connectionstring = _connections[_connection].ToString();
|
||||||
|
_tenant = "";
|
||||||
|
_databasetype = "-";
|
||||||
|
var tenant = _tenants.FirstOrDefault(item => item.DBConnectionString == _connection);
|
||||||
if (tenant != null)
|
if (tenant != null)
|
||||||
{
|
{
|
||||||
_database = _databases.Find(item => item.DBType == tenant.DBType)?.Name;
|
_tenant = tenant.Name;
|
||||||
_connectionstring = tenant.DBConnectionString;
|
_databasetype = _databases.FirstOrDefault(item => item.DBType == tenant.DBType).Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_databases.Exists(item => item.IsDefault))
|
||||||
|
{
|
||||||
|
_databasetype = _databases.Find(item => item.IsDefault).Name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_databasetype = "LocalDB";
|
||||||
|
}
|
||||||
|
_showConnectionString = false;
|
||||||
|
LoadDatabaseConfigComponent();
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Tenant {TenantId} {Error}", _tenantid, ex.Message);
|
await logger.LogError(ex, "Error Loading Connection {Connection} {Error}", _connection, ex.Message);
|
||||||
AddModuleMessage(ex.Message, MessageType.Error);
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DatabaseTypeChanged(ChangeEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_databasetype = (string)eventArgs.Value;
|
||||||
|
_showConnectionString = false;
|
||||||
|
LoadDatabaseConfigComponent();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadDatabaseConfigComponent()
|
||||||
|
{
|
||||||
|
var database = _databases.SingleOrDefault(d => d.Name == _databasetype);
|
||||||
|
if (database != null)
|
||||||
|
{
|
||||||
|
_databaseConfigType = Type.GetType(database.ControlType);
|
||||||
|
DatabaseConfigComponent = builder =>
|
||||||
|
{
|
||||||
|
builder.OpenComponent(0, _databaseConfigType);
|
||||||
|
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
||||||
|
builder.CloseComponent();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowConnectionString()
|
||||||
|
{
|
||||||
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
|
{
|
||||||
|
_connectionstring = databaseConfigControl.GetConnectionString();
|
||||||
|
}
|
||||||
|
_showConnectionString = !_showConnectionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Add()
|
||||||
|
{
|
||||||
|
var connectionstring = _connectionstring;
|
||||||
|
if (!_showConnectionString && _databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
|
{
|
||||||
|
connectionstring = databaseConfigControl.GetConnectionString();
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(_name) && !string.IsNullOrEmpty(connectionstring))
|
||||||
|
{
|
||||||
|
var settings = new Dictionary<string, object>();
|
||||||
|
settings.Add($"{SettingKeys.ConnectionStringsSection}:{_name}", connectionstring);
|
||||||
|
await SystemService.UpdateSystemInfoAsync(settings);
|
||||||
|
_connections = await SystemService.GetSystemInfoAsync("connectionstrings");
|
||||||
|
_connection = "-";
|
||||||
|
AddModuleMessage(Localizer["Message.Connection.Added"], MessageType.Success);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Required.Connection"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleConnectionString()
|
||||||
|
{
|
||||||
|
if (_connectionstringtype == "password")
|
||||||
|
{
|
||||||
|
_connectionstringtype = "text";
|
||||||
|
_connectionstringtoggle = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_connectionstringtype = "password";
|
||||||
|
_connectionstringtoggle = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Execute()
|
private async Task Execute()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_tenantid != "-1" && !string.IsNullOrEmpty(_sql))
|
if (_databasetype != "-" && !string.IsNullOrEmpty(_sql))
|
||||||
{
|
{
|
||||||
var sqlquery = new SqlQuery { TenantId = int.Parse(_tenantid), Query = _sql };
|
var dbtype = _databases.FirstOrDefault(item => item.Name == _databasetype).DBType;
|
||||||
|
var sqlquery = new SqlQuery { DBConnectionString = _connection, DBType = dbtype, Query = _sql };
|
||||||
sqlquery = await SqlService.ExecuteQueryAsync(sqlquery);
|
sqlquery = await SqlService.ExecuteQueryAsync(sqlquery);
|
||||||
_results = sqlquery.Results;
|
_results = sqlquery.Results;
|
||||||
AddModuleMessage(Localizer["Success.QueryExecuted"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.QueryExecuted"], MessageType.Success);
|
||||||
|
@ -37,6 +37,12 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="ipaddress" class="form-control" @bind="@_ipaddress" readonly />
|
<input id="ipaddress" class="form-control" @bind="@_ipaddress" readonly />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="environment" HelpText="Environment name" ResourceKey="Environment">Environment: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="environment" class="form-control" @bind="@_environment" readonly />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="contentrootpath" HelpText="Root Path" ResourceKey="ContentRootPath">Root Path: </Label>
|
<Label Class="col-sm-3" For="contentrootpath" HelpText="Root Path" ResourceKey="ContentRootPath">Root Path: </Label>
|
||||||
@ -141,7 +147,20 @@
|
|||||||
<a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Access.ApiFramework"]</a>
|
<a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Access.ApiFramework"]</a>
|
||||||
<ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" />
|
<ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
<TabPanel Name="Log" Heading="Log" ResourceKey="Log">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="log" HelpText="System log information for current day" ResourceKey="Log">Log: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="log" class="form-control" rows="10" @bind="@_log" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br /><br />
|
||||||
|
<button type="button" class="btn btn-danger" @onclick="ClearLog">@Localizer["Clear"]</button>
|
||||||
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
@ -151,6 +170,7 @@
|
|||||||
private string _osversion = string.Empty;
|
private string _osversion = string.Empty;
|
||||||
private string _machinename = string.Empty;
|
private string _machinename = string.Empty;
|
||||||
private string _ipaddress = string.Empty;
|
private string _ipaddress = string.Empty;
|
||||||
|
private string _environment = string.Empty;
|
||||||
private string _contentrootpath = string.Empty;
|
private string _contentrootpath = string.Empty;
|
||||||
private string _webrootpath = string.Empty;
|
private string _webrootpath = string.Empty;
|
||||||
private string _servertime = string.Empty;
|
private string _servertime = string.Empty;
|
||||||
@ -164,17 +184,20 @@
|
|||||||
private string _swagger = string.Empty;
|
private string _swagger = string.Empty;
|
||||||
private string _packageservice = string.Empty;
|
private string _packageservice = string.Empty;
|
||||||
|
|
||||||
|
private string _log = string.Empty;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
_version = Constants.Version;
|
_version = Constants.Version;
|
||||||
|
|
||||||
Dictionary<string, object> systeminfo = await SystemService.GetSystemInfoAsync("environment");
|
var systeminfo = await SystemService.GetSystemInfoAsync("environment");
|
||||||
if (systeminfo != null)
|
if (systeminfo != null)
|
||||||
{
|
{
|
||||||
_clrversion = systeminfo["CLRVersion"].ToString();
|
_clrversion = systeminfo["CLRVersion"].ToString();
|
||||||
_osversion = systeminfo["OSVersion"].ToString();
|
_osversion = systeminfo["OSVersion"].ToString();
|
||||||
_machinename = systeminfo["MachineName"].ToString();
|
_machinename = systeminfo["MachineName"].ToString();
|
||||||
_ipaddress = systeminfo["IPAddress"].ToString();
|
_ipaddress = systeminfo["IPAddress"].ToString();
|
||||||
|
_environment = systeminfo["Environment"].ToString();
|
||||||
_contentrootpath = systeminfo["ContentRootPath"].ToString();
|
_contentrootpath = systeminfo["ContentRootPath"].ToString();
|
||||||
_webrootpath = systeminfo["WebRootPath"].ToString();
|
_webrootpath = systeminfo["WebRootPath"].ToString();
|
||||||
_servertime = systeminfo["ServerTime"].ToString() + " UTC";
|
_servertime = systeminfo["ServerTime"].ToString() + " UTC";
|
||||||
@ -182,7 +205,7 @@
|
|||||||
_workingset = (Convert.ToInt64(systeminfo["WorkingSet"].ToString()) / 1000000).ToString() + " MB";
|
_workingset = (Convert.ToInt64(systeminfo["WorkingSet"].ToString()) / 1000000).ToString() + " MB";
|
||||||
}
|
}
|
||||||
|
|
||||||
systeminfo = await SystemService.GetSystemInfoAsync();
|
systeminfo = await SystemService.GetSystemInfoAsync("configuration");
|
||||||
if (systeminfo != null)
|
if (systeminfo != null)
|
||||||
{
|
{
|
||||||
_installationid = systeminfo["InstallationId"].ToString();
|
_installationid = systeminfo["InstallationId"].ToString();
|
||||||
@ -192,6 +215,12 @@
|
|||||||
_swagger = systeminfo["UseSwagger"].ToString();
|
_swagger = systeminfo["UseSwagger"].ToString();
|
||||||
_packageservice = systeminfo["PackageService"].ToString();
|
_packageservice = systeminfo["PackageService"].ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
systeminfo = await SystemService.GetSystemInfoAsync("log");
|
||||||
|
if (systeminfo != null)
|
||||||
|
{
|
||||||
|
_log = systeminfo["Log"].ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveConfig()
|
private async Task SaveConfig()
|
||||||
@ -214,6 +243,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ClearLog()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = new Dictionary<string, object>();
|
||||||
|
settings.Add("clearlog", "true");
|
||||||
|
await SystemService.UpdateSystemInfoAsync(settings);
|
||||||
|
_log = string.Empty;
|
||||||
|
AddModuleMessage(Localizer["Success.ClearLog"], MessageType.Success);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Clearing Log");
|
||||||
|
AddModuleMessage(Localizer["Error.ClearLog"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task RestartApplication()
|
private async Task RestartApplication()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||||
|
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||||
</td>
|
</td>
|
||||||
<td style="width: 1px; vertical-align: middle;">
|
<td style="width: 1px; vertical-align: middle;">
|
||||||
@ -113,6 +114,10 @@
|
|||||||
<button type="button" class="btn btn-success" @onclick="InstallThemes">@SharedLocalizer["Install"]</button>
|
<button type="button" class="btn btn-success" @onclick="InstallThemes">@SharedLocalizer["Install"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<ModuleMessage Type="MessageType.Info" Message="@SharedLocalizer["Oqtane.Marketplace"]" />
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
private string _price = "free";
|
private string _price = "free";
|
||||||
|
@ -121,7 +121,7 @@
|
|||||||
private bool IsValid(string name)
|
private bool IsValid(string name)
|
||||||
{
|
{
|
||||||
// must contain letters, underscores and digits and first character must be letter or underscore
|
// must contain letters, underscores and digits and first character must be letter or underscore
|
||||||
return !string.IsNullOrEmpty(name) && name.ToLower() != "theme" && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
|
return !string.IsNullOrEmpty(name) && name.ToLower() != "theme" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TemplateChanged(ChangeEventArgs e)
|
private void TemplateChanged(ChangeEventArgs e)
|
||||||
|
@ -29,7 +29,7 @@ else
|
|||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="View" Parameters="@($"name=" + WebUtility.UrlEncode(context.ThemeName))" ResourceKey="ViewTheme" /></td>
|
<td><ActionLink Action="View" Parameters="@($"name=" + WebUtility.UrlEncode(context.ThemeName))" ResourceKey="ViewTheme" /></td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.AssemblyName != "Oqtane.Client")
|
@if (context.AssemblyName != Constants.ClientId)
|
||||||
{
|
{
|
||||||
<ActionDialog Header="Delete Theme" Message="@string.Format(Localizer["Confirm.Theme.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" ResourceKey="DeleteTheme" />
|
<ActionDialog Header="Delete Theme" Message="@string.Format(Localizer["Confirm.Theme.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" ResourceKey="DeleteTheme" />
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,12 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="version" class="form-control" @bind="@_version" disabled />
|
<input id="version" class="form-control" @bind="@_version" disabled />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this module was installed" ResourceKey="PackageName">Package Name: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="owner" HelpText="The owner or creator of the theme" ResourceKey="Owner">Owner: </Label>
|
<Label Class="col-sm-3" For="owner" HelpText="The owner or creator of the theme" ResourceKey="Owner">Owner: </Label>
|
||||||
@ -56,6 +62,7 @@
|
|||||||
private string _themeName = "";
|
private string _themeName = "";
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _version;
|
private string _version;
|
||||||
|
private string _packagename;
|
||||||
private string _owner = "";
|
private string _owner = "";
|
||||||
private string _url = "";
|
private string _url = "";
|
||||||
private string _contact = "";
|
private string _contact = "";
|
||||||
@ -74,6 +81,7 @@
|
|||||||
{
|
{
|
||||||
_name = theme.Name;
|
_name = theme.Name;
|
||||||
_version = theme.Version;
|
_version = theme.Version;
|
||||||
|
_packagename = theme.PackageName;
|
||||||
_owner = theme.Owner;
|
_owner = theme.Owner;
|
||||||
_url = theme.Url;
|
_url = theme.Url;
|
||||||
_contact = theme.Contact;
|
_contact = theme.Contact;
|
||||||
|
@ -160,7 +160,7 @@ else
|
|||||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||||
<td>@context.FromDisplayName</td>
|
<td>@context.FromDisplayName</td>
|
||||||
<td>@context.Subject</td>
|
<td>@context.Subject</td>
|
||||||
<td>@context.CreatedOn</td>
|
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||||
</Row>
|
</Row>
|
||||||
<Detail>
|
<Detail>
|
||||||
<td colspan="2"></td>
|
<td colspan="2"></td>
|
||||||
@ -193,7 +193,7 @@ else
|
|||||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||||
<td>@context.ToDisplayName</td>
|
<td>@context.ToDisplayName</td>
|
||||||
<td>@context.Subject</td>
|
<td>@context.Subject</td>
|
||||||
<td>@context.CreatedOn</td>
|
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||||
</Row>
|
</Row>
|
||||||
<Detail>
|
<Detail>
|
||||||
<td colspan="2"></td>
|
<td colspan="2"></td>
|
||||||
@ -211,8 +211,11 @@ else
|
|||||||
</Detail>
|
</Detail>
|
||||||
</Pager>
|
</Pager>
|
||||||
}
|
}
|
||||||
|
@if (notifications.Any())
|
||||||
|
{
|
||||||
<br />
|
<br />
|
||||||
<ActionDialog Header="Clear Notifications" Message="Are You Sure You Wish To Permanently Delete All Notifications ?" Action="Delete All Notifications" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllNotifications())" ResourceKey="DeleteAllNotifications" />
|
<ActionDialog Header="Clear Notifications" Message="Are You Sure You Wish To Permanently Delete All Notifications ?" Action="Delete All Notifications" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllNotifications())" ResourceKey="DeleteAllNotifications" />
|
||||||
|
}
|
||||||
<br /><hr />
|
<br /><hr />
|
||||||
<select class="form-select" @onchange="(e => FilterChanged(e))">
|
<select class="form-select" @onchange="(e => FilterChanged(e))">
|
||||||
<option value="to">@Localizer["Inbox"]</option>
|
<option value="to">@Localizer["Inbox"]</option>
|
||||||
@ -305,7 +308,14 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||||
=> SettingService.GetSetting(settings, SettingName, DefaultValue);
|
{
|
||||||
|
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||||
|
if (value.Contains("]"))
|
||||||
|
{
|
||||||
|
value = value.Substring(value.IndexOf("]") + 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Save()
|
private async Task Save()
|
||||||
{
|
{
|
||||||
@ -337,7 +347,9 @@ else
|
|||||||
photo = null;
|
photo = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
await UserService.UpdateUserAsync(user);
|
user = await UserService.UpdateUserAsync(user);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||||
await logger.LogInformation("User Profile Saved");
|
await logger.LogInformation("User Profile Saved");
|
||||||
|
|
||||||
@ -345,6 +357,11 @@ else
|
|||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Password.Invalid"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Password.Invalid"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
@ -429,6 +446,7 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
ModuleInstance.ShowProgressIndicator();
|
||||||
foreach(var Notification in notifications)
|
foreach(var Notification in notifications)
|
||||||
{
|
{
|
||||||
if (!Notification.IsDeleted)
|
if (!Notification.IsDeleted)
|
||||||
@ -444,12 +462,15 @@ else
|
|||||||
}
|
}
|
||||||
await logger.LogInformation("Notifications Permanently Deleted");
|
await logger.LogInformation("Notifications Permanently Deleted");
|
||||||
await LoadNotificationsAsync();
|
await LoadNotificationsAsync();
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Deleting Notifications {Error}", ex.Message);
|
await logger.LogError(ex, "Error Deleting Notifications {Error}", ex.Message);
|
||||||
AddModuleMessage(ex.Message, MessageType.Error);
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@
|
|||||||
private Dictionary<string, string> settings;
|
private Dictionary<string, string> settings;
|
||||||
private string category = string.Empty;
|
private string category = string.Empty;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
@ -122,7 +122,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||||
=> SettingService.GetSetting(settings, SettingName, DefaultValue);
|
{
|
||||||
|
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||||
|
if (value.Contains("]"))
|
||||||
|
{
|
||||||
|
value = value.Substring(value.IndexOf("]") + 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveUser()
|
private async Task SaveUser()
|
||||||
{
|
{
|
||||||
|
@ -72,8 +72,19 @@ else
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="lastlogin" HelpText="The date and time when the user last signed in" ResourceKey="LastLogin"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="lastlogin" class="form-control" @bind="@lastlogin" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="lastipaddress" HelpText="The IP Address of the user recorded during their last login" ResourceKey="LastIPAddress"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="lastipaddress" class="form-control" @bind="@lastipaddress" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||||
@ -148,18 +159,22 @@ else
|
|||||||
private FileManager filemanager;
|
private FileManager filemanager;
|
||||||
private int photofileid = -1;
|
private int photofileid = -1;
|
||||||
private File photo = null;
|
private File photo = null;
|
||||||
|
private string isdeleted;
|
||||||
|
private string lastlogin;
|
||||||
|
private string lastipaddress;
|
||||||
|
|
||||||
private List<Profile> profiles;
|
private List<Profile> profiles;
|
||||||
private Dictionary<string, string> settings;
|
private Dictionary<string, string> settings;
|
||||||
private string category = string.Empty;
|
private string category = string.Empty;
|
||||||
|
|
||||||
private string createdby;
|
private string createdby;
|
||||||
private DateTime createdon;
|
private DateTime createdon;
|
||||||
private string modifiedby;
|
private string modifiedby;
|
||||||
private DateTime modifiedon;
|
private DateTime modifiedon;
|
||||||
private string deletedby;
|
private string deletedby;
|
||||||
private DateTime? deletedon;
|
private DateTime? deletedon;
|
||||||
private string isdeleted;
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
@ -186,6 +201,10 @@ else
|
|||||||
photofileid = -1;
|
photofileid = -1;
|
||||||
photo = null;
|
photo = null;
|
||||||
}
|
}
|
||||||
|
isdeleted = user.IsDeleted.ToString();
|
||||||
|
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||||
|
lastipaddress = user.LastIPAddress;
|
||||||
|
|
||||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
||||||
createdby = user.CreatedBy;
|
createdby = user.CreatedBy;
|
||||||
createdon = user.CreatedOn;
|
createdon = user.CreatedOn;
|
||||||
@ -193,7 +212,6 @@ else
|
|||||||
modifiedon = user.ModifiedOn;
|
modifiedon = user.ModifiedOn;
|
||||||
deletedby = user.DeletedBy;
|
deletedby = user.DeletedBy;
|
||||||
deletedon = user.DeletedOn;
|
deletedon = user.DeletedOn;
|
||||||
isdeleted = user.IsDeleted.ToString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,7 +223,14 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||||
=> SettingService.GetSetting(settings, SettingName, DefaultValue);
|
{
|
||||||
|
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||||
|
if (value.Contains("]"))
|
||||||
|
{
|
||||||
|
value = value.Substring(value.IndexOf("]") + 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveUser()
|
private async Task SaveUser()
|
||||||
{
|
{
|
||||||
@ -231,12 +256,18 @@ else
|
|||||||
user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
|
user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
|
||||||
|
|
||||||
user = await UserService.UpdateUserAsync(user);
|
user = await UserService.UpdateUserAsync(user);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
||||||
await logger.LogInformation("User Saved {User}", user);
|
await logger.LogInformation("User Saved {User}", user);
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Password.NoMatch"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Password.NoMatch"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
@inject ISiteService SiteService
|
@inject ISiteService SiteService
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
@inject SiteState SiteState
|
|
||||||
|
|
||||||
@if (users == null)
|
@if (users == null)
|
||||||
{
|
{
|
||||||
@ -21,7 +20,7 @@ else
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<ActionLink Action="Add" Text="Add User" ResourceKey="AddUser" />
|
<ActionLink Action="Add" Text="Add User" Security="SecurityAccessLevel.Edit" ResourceKey="AddUser" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<input class="form-control" @bind="@_search" />
|
<input class="form-control" @bind="@_search" />
|
||||||
@ -36,32 +35,27 @@ else
|
|||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
|
||||||
<th>@SharedLocalizer["Username"]</th>
|
<th>@SharedLocalizer["Username"]</th>
|
||||||
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
<th>@Localizer["LastLoginOn"]</th>
|
<th>@Localizer["LastLoginOn"]</th>
|
||||||
<th>@Localizer["LastIPAddress"]</th>
|
|
||||||
<th>@Localizer["CreatedOn"]</th>
|
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td>
|
<td>
|
||||||
<ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="EditUser" />
|
<ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditUser" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.UserId == PageState.User.UserId)" ResourceKey="DeleteUser" />
|
<ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.UserId == PageState.User.UserId)" ResourceKey="DeleteUser" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
|
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Roles" />
|
||||||
</td>
|
</td>
|
||||||
<td>@context.User.DisplayName</td>
|
|
||||||
<td>@context.User.Username</td>
|
<td>@context.User.Username</td>
|
||||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}",context.User.LastLoginOn)</td>
|
<td>@((MarkupString)string.Format("<a href=\"mailto:{0}\">{1}</a>", @context.User.Email, @context.User.DisplayName))</td>
|
||||||
<td>@context.User.LastIPAddress</td>
|
<td>@((context.User.LastLoginOn != DateTime.MinValue) ? string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn) : "")</td>
|
||||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}",context.User.CreatedOn)</td>
|
|
||||||
|
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings" Security="SecurityAccessLevel.Admin">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Section Name="User" Heading="User Settings" ResourceKey="UserSettings">
|
<Section Name="User" Heading="User Settings" ResourceKey="UserSettings">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -259,6 +253,12 @@ else
|
|||||||
<input id="scopes" class="form-control" @bind="@_scopes" />
|
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="parameters" HelpText="Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple)." ResourceKey="Parameters">Parameters:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="parameters" class="form-control" @bind="@_parameters" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -286,6 +286,21 @@ else
|
|||||||
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the role claim provided by the provider" ResourceKey="RoleClaimType">Role Claim:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -380,10 +395,13 @@ else
|
|||||||
private string _clientsecrettype = "password";
|
private string _clientsecrettype = "password";
|
||||||
private string _toggleclientsecret = string.Empty;
|
private string _toggleclientsecret = string.Empty;
|
||||||
private string _scopes;
|
private string _scopes;
|
||||||
|
private string _parameters;
|
||||||
private string _pkce;
|
private string _pkce;
|
||||||
private string _redirecturl;
|
private string _redirecturl;
|
||||||
private string _identifierclaimtype;
|
private string _identifierclaimtype;
|
||||||
private string _emailclaimtype;
|
private string _emailclaimtype;
|
||||||
|
private string _roleclaimtype;
|
||||||
|
private string _profileclaimtypes;
|
||||||
private string _domainfilter;
|
private string _domainfilter;
|
||||||
private string _createusers;
|
private string _createusers;
|
||||||
|
|
||||||
@ -395,7 +413,7 @@ else
|
|||||||
private string _lifetime;
|
private string _lifetime;
|
||||||
private string _token;
|
private string _token;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
@ -432,10 +450,13 @@ else
|
|||||||
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
||||||
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
||||||
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
||||||
|
_parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", "");
|
||||||
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
||||||
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||||
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
|
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "sub");
|
||||||
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
|
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email");
|
||||||
|
_roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", "");
|
||||||
|
_profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", "");
|
||||||
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
|
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
|
||||||
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
|
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
|
||||||
|
|
||||||
@ -443,7 +464,8 @@ else
|
|||||||
_togglesecret = SharedLocalizer["ShowPassword"];
|
_togglesecret = SharedLocalizer["ShowPassword"];
|
||||||
_issuer = SettingService.GetSetting(settings, "JwtOptions:Issuer", PageState.Uri.Scheme + "://" + PageState.Alias.Name);
|
_issuer = SettingService.GetSetting(settings, "JwtOptions:Issuer", PageState.Uri.Scheme + "://" + PageState.Alias.Name);
|
||||||
_audience = SettingService.GetSetting(settings, "JwtOptions:Audience", "");
|
_audience = SettingService.GetSetting(settings, "JwtOptions:Audience", "");
|
||||||
_lifetime = SettingService.GetSetting(settings, "JwtOptions:Lifetime", "20"); }
|
_lifetime = SettingService.GetSetting(settings, "JwtOptions:Lifetime", "20");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadUsersAsync(bool load)
|
private async Task LoadUsersAsync(bool load)
|
||||||
@ -509,7 +531,7 @@ else
|
|||||||
private async Task UpdateUserSettingsAsync()
|
private async Task UpdateUserSettingsAsync()
|
||||||
{
|
{
|
||||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||||
SettingService.SetSetting(settings, settingSearch, _search);
|
settings = SettingService.SetSetting(settings, settingSearch, _search);
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -549,9 +571,12 @@ else
|
|||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Parameters", _parameters, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
||||||
|
|
||||||
@ -587,14 +612,10 @@ else
|
|||||||
if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||||
{
|
{
|
||||||
_scopes = "openid,profile,email";
|
_scopes = "openid,profile,email";
|
||||||
_identifierclaimtype = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
|
|
||||||
_emailclaimtype = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_scopes = "";
|
_scopes = "";
|
||||||
_identifierclaimtype = "sub";
|
|
||||||
_emailclaimtype = "email";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||||
|
21
Oqtane.Client/Modules/Admin/Users/ModuleInfo.cs
Normal file
21
Oqtane.Client/Modules/Admin/Users/ModuleInfo.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Modules.Admin.Users
|
||||||
|
{
|
||||||
|
[PrivateApi("Mark this as private, since it's not very useful in the public docs")]
|
||||||
|
public class ModuleInfo : IModule
|
||||||
|
{
|
||||||
|
public ModuleDefinition ModuleDefinition => new ModuleDefinition
|
||||||
|
{
|
||||||
|
Name = "Users",
|
||||||
|
Description = "Manage Users",
|
||||||
|
Categories = "Admin",
|
||||||
|
Version = Constants.Version,
|
||||||
|
PermissionNames = $"{PermissionNames.View},{PermissionNames.Edit}," +
|
||||||
|
$"{EntityNames.User}:{PermissionNames.Write}:{RoleNames.Admin}," +
|
||||||
|
$"{EntityNames.UserRole}:{PermissionNames.Write}:{RoleNames.Admin}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -63,7 +63,7 @@ else
|
|||||||
<td>@context.EffectiveDate</td>
|
<td>@context.EffectiveDate</td>
|
||||||
<td>@context.ExpiryDate</td>
|
<td>@context.ExpiryDate</td>
|
||||||
<td>
|
<td>
|
||||||
<ActionDialog Header="Remove Role" Message="@string.Format(Localizer["Confirm.User.RemoveRole"], context.Role.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || (context.Role.Name == RoleNames.Host && userid == PageState.User.UserId))" ResourceKey="DeleteUserRole" />
|
<ActionDialog Header="Remove Role" Message="@string.Format(Localizer["Confirm.User.RemoveRole"], context.Role.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || (context.Role.Name == RoleNames.Host && userid == PageState.User.UserId))" ResourceKey="DeleteUserRole" />
|
||||||
</td>
|
</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
@ -79,7 +79,7 @@ else
|
|||||||
private string expirydate = string.Empty;
|
private string expirydate = string.Empty;
|
||||||
private List<UserRole> userroles;
|
private List<UserRole> userroles;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
@ -88,15 +88,17 @@ else
|
|||||||
userid = Int32.Parse(PageState.QueryString["id"]);
|
userid = Int32.Parse(PageState.QueryString["id"]);
|
||||||
User user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
User user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||||
name = user.DisplayName;
|
name = user.DisplayName;
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true);
|
roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true);
|
||||||
roles = roles.Where(item => item.Name != RoleNames.Everyone).ToList();
|
roles.RemoveAll(item => item.Name == RoleNames.Everyone || item.Name == RoleNames.Unauthenticated);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
roles = await RoleService.GetRolesAsync(PageState.Site.SiteId);
|
roles = await RoleService.GetRolesAsync(PageState.Site.SiteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
await GetUserRoles();
|
await GetUserRoles();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -128,13 +128,6 @@
|
|||||||
|
|
||||||
private string CloseUrl()
|
private string CloseUrl()
|
||||||
{
|
{
|
||||||
if (!PageState.QueryString.ContainsKey("type"))
|
return (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : NavigateUrl();
|
||||||
{
|
|
||||||
return NavigateUrl();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return NavigateUrl(PageState.Page.Path, "type=" + PageState.QueryString["type"] + "&days=" + PageState.QueryString["days"] + "&page=" + PageState.QueryString["page"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ else
|
|||||||
<th>@Localizer["Created"]</th>
|
<th>@Localizer["Created"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Detail" Parameters="@($"id=" + context.VisitorId.ToString() + "&type=" + _type.ToString() + "&days=" + _days.ToString() + "&page=" + _page.ToString())" ResourceKey="Details" /></td>
|
<td><ActionLink Action="Detail" Parameters="@($"id={context.VisitorId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, $"type={_type}&days={_days}&page={_page}"))" ResourceKey="Details" /></td>
|
||||||
<td>@context.IPAddress</td>
|
<td>@context.IPAddress</td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.UserId != null)
|
@if (context.UserId != null)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@namespace Oqtane.Modules.Controls
|
@namespace Oqtane.Modules.Controls
|
||||||
|
@using System.Text.Json
|
||||||
@inherits LocalizableComponent
|
@inherits LocalizableComponent
|
||||||
|
|
||||||
@if (_visible)
|
@if (_visible)
|
||||||
@ -40,7 +41,7 @@
|
|||||||
|
|
||||||
@code {
|
@code {
|
||||||
private bool _visible = false;
|
private bool _visible = false;
|
||||||
private string _permissions = string.Empty;
|
private List<Permission> _permissions;
|
||||||
private bool _editmode = false;
|
private bool _editmode = false;
|
||||||
private bool _authorized = false;
|
private bool _authorized = false;
|
||||||
private string _iconSpan = string.Empty;
|
private string _iconSpan = string.Empty;
|
||||||
@ -61,7 +62,10 @@
|
|||||||
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
|
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Permissions { get; set; } // optional - can be used to specify a permission string
|
public string Permissions { get; set; } // deprecated - use PermissionList instead
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public List<Permission> PermissionList { get; set; } // optional - can be used to specify permissions
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Class { get; set; } // optional
|
public string Class { get; set; } // optional
|
||||||
@ -78,6 +82,14 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
|
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Permissions))
|
||||||
|
{
|
||||||
|
PermissionList = JsonSerializer.Deserialize<List<Permission>>(Permissions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
base.OnParametersSet();
|
base.OnParametersSet();
|
||||||
@ -109,7 +121,7 @@
|
|||||||
Header = Localize(nameof(Header), Header);
|
Header = Localize(nameof(Header), Header);
|
||||||
Message = Localize(nameof(Message), Message);
|
Message = Localize(nameof(Message), Message);
|
||||||
|
|
||||||
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
|
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
|
||||||
_authorized = IsAuthorized();
|
_authorized = IsAuthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Controls
|
@namespace Oqtane.Modules.Controls
|
||||||
|
@using System.Net
|
||||||
|
@using System.Text.Json
|
||||||
@inherits LocalizableComponent
|
@inherits LocalizableComponent
|
||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
|
|
||||||
@ -25,7 +27,7 @@
|
|||||||
private string _text = string.Empty;
|
private string _text = string.Empty;
|
||||||
private string _parameters = string.Empty;
|
private string _parameters = string.Empty;
|
||||||
private string _url = string.Empty;
|
private string _url = string.Empty;
|
||||||
private string _permissions = string.Empty;
|
private List<Permission> _permissions;
|
||||||
private bool _editmode = false;
|
private bool _editmode = false;
|
||||||
private bool _authorized = false;
|
private bool _authorized = false;
|
||||||
private string _classname = "btn btn-primary";
|
private string _classname = "btn btn-primary";
|
||||||
@ -51,7 +53,10 @@
|
|||||||
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
|
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Permissions { get; set; } // optional - can be used to specify a permission string
|
public string Permissions { get; set; } // deprecated - use PermissionList instead
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public List<Permission> PermissionList { get; set; } // optional - can be used to specify permissions
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool Disabled { get; set; } // optional
|
public bool Disabled { get; set; } // optional
|
||||||
@ -71,6 +76,17 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public bool IconOnly { get; set; } // optional - specifies only icon in link
|
public bool IconOnly { get; set; } // optional - specifies only icon in link
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string ReturnUrl { get; set; } // optional - used to set a url to redirect to
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Permissions))
|
||||||
|
{
|
||||||
|
PermissionList = JsonSerializer.Deserialize<List<Permission>>(Permissions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
base.OnParametersSet();
|
base.OnParametersSet();
|
||||||
@ -115,9 +131,13 @@
|
|||||||
_iconSpan = $"<span class=\"{IconName}\"></span>{(IconOnly ? "" : " ")}";
|
_iconSpan = $"<span class=\"{IconName}\"></span>{(IconOnly ? "" : " ")}";
|
||||||
}
|
}
|
||||||
|
|
||||||
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
|
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
|
||||||
_text = Localize(nameof(Text), _text);
|
_text = Localize(nameof(Text), _text);
|
||||||
_url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters);
|
_url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters);
|
||||||
|
if (!string.IsNullOrEmpty(ReturnUrl))
|
||||||
|
{
|
||||||
|
_url += ((_url.Contains("?")) ? "&" : "?") + $"returnurl={WebUtility.UrlEncode(ReturnUrl)}";
|
||||||
|
}
|
||||||
_authorized = IsAuthorized();
|
_authorized = IsAuthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
153
Oqtane.Client/Modules/Controls/AutoComplete.razor
Normal file
153
Oqtane.Client/Modules/Controls/AutoComplete.razor
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
@namespace Oqtane.Modules.Controls
|
||||||
|
@inherits LocalizableComponent
|
||||||
|
|
||||||
|
<div class="app-autocomplete">
|
||||||
|
<input class="form-control" value="@Value" @oninput="OnInput" @onkeyup="OnKeyUp" placeholder="@Placeholder" autocomplete="off" />
|
||||||
|
@if (_results != null)
|
||||||
|
{
|
||||||
|
<select class="form-select" style="position: relative;" value="@Value" size="@Rows" @onkeyup="OnKeyUp" @onchange="(e => OnChange(e))">
|
||||||
|
@if (_results.Any())
|
||||||
|
{
|
||||||
|
@foreach (var result in _results)
|
||||||
|
{
|
||||||
|
if (result.Value == Value)
|
||||||
|
{
|
||||||
|
<option selected>@result.Value</option>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<option>@result.Value</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<option disabled>No Results</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
Dictionary<string, string> _results;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Func<string, Task<Dictionary<string, string>>> OnSearch { get; set; } // required - an async delegate method which accepts a filter string parameter and returns a dictionary
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public int Characters { get; set; } = 3; // optional - number of characters before search is initiated
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public int Rows { get; set; } = 3; // optional - number of result rows to display
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Placeholder { get; set; } // optional - placeholder input text
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Value { get; set; } // value of item selected
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Key { get; set; } // key of item selected
|
||||||
|
|
||||||
|
private async Task OnInput(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
Value = e.Value?.ToString();
|
||||||
|
if (Value?.Length >= Characters)
|
||||||
|
{
|
||||||
|
_results = await OnSearch?.Invoke(Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_results = null;
|
||||||
|
}
|
||||||
|
SetKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnKeyUp(KeyboardEventArgs e)
|
||||||
|
{
|
||||||
|
var index = -1;
|
||||||
|
switch (e.Key)
|
||||||
|
{
|
||||||
|
case "ArrowDown":
|
||||||
|
if (_results == null)
|
||||||
|
{
|
||||||
|
if (Value?.Length >= Characters)
|
||||||
|
{
|
||||||
|
_results = await OnSearch?.Invoke(Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = GetIndex();
|
||||||
|
if (index < _results.Count - 1)
|
||||||
|
{
|
||||||
|
Value = _results.ElementAt(index + 1).Value;
|
||||||
|
Key = _results.ElementAt(index + 1).Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ArrowUp":
|
||||||
|
index = GetIndex();
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
Value = _results.ElementAt(index - 1).Value;
|
||||||
|
Key = _results.ElementAt(index - 1).Key;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ArrowRight":
|
||||||
|
case "Tab":
|
||||||
|
_results = null;
|
||||||
|
break;
|
||||||
|
case "Enter": // note within a form the enter key submits the entire form
|
||||||
|
case "NumpadEnter":
|
||||||
|
_results = null;
|
||||||
|
break;
|
||||||
|
case "Escape":
|
||||||
|
Value = "";
|
||||||
|
_results = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnChange(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
Value = (string)e.Value;
|
||||||
|
SetKey();
|
||||||
|
_results = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetIndex()
|
||||||
|
{
|
||||||
|
if (_results != null)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < _results.Count; index++)
|
||||||
|
{
|
||||||
|
if (_results.ElementAt(index).Value == Value)
|
||||||
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetKey()
|
||||||
|
{
|
||||||
|
var index = GetIndex();
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
Key = _results.ElementAt(index).Key;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Key = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
Value = "";
|
||||||
|
Key = "";
|
||||||
|
_results = null;
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
@namespace Oqtane.Modules.Controls
|
@namespace Oqtane.Modules.Controls
|
||||||
|
@using System.Threading
|
||||||
@inherits ModuleControlBase
|
@inherits ModuleControlBase
|
||||||
@inject IFolderService FolderService
|
@inject IFolderService FolderService
|
||||||
@inject IFileService FileService
|
@inject IFileService FileService
|
||||||
@inject IStringLocalizer<FileManager> Localizer
|
@inject IStringLocalizer<FileManager> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@if (_folders != null)
|
@if (_initialized)
|
||||||
{
|
{
|
||||||
<div id="@Id" class="container-fluid px-0">
|
<div id="@Id" class="container-fluid px-0">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -53,7 +54,7 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="col mt-2 text-end">
|
<div class="col mt-2 text-end">
|
||||||
<button type="button" class="btn btn-success" @onclick="UploadFile">@SharedLocalizer["Upload"]</button>
|
<button type="button" class="btn btn-success" @onclick="UploadFiles">@SharedLocalizer["Upload"]</button>
|
||||||
@if (ShowFiles && GetFileId() != -1)
|
@if (ShowFiles && GetFileId() != -1)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-danger mx-1" @onclick="DeleteFile">@SharedLocalizer["Delete"]</button>
|
<button type="button" class="btn btn-danger mx-1" @onclick="DeleteFile">@SharedLocalizer["Delete"]</button>
|
||||||
@ -86,7 +87,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _id;
|
private bool _initialized = false;
|
||||||
private List<Folder> _folders;
|
private List<Folder> _folders;
|
||||||
private List<File> _files = new List<File>();
|
private List<File> _files = new List<File>();
|
||||||
private string _fileinputid = string.Empty;
|
private string _fileinputid = string.Empty;
|
||||||
@ -144,11 +145,6 @@
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(Id))
|
|
||||||
{
|
|
||||||
_id = Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// packages folder is a framework folder for uploading installable nuget packages
|
// packages folder is a framework folder for uploading installable nuget packages
|
||||||
if (Folder == Constants.PackagesFolder)
|
if (Folder == Constants.PackagesFolder)
|
||||||
{
|
{
|
||||||
@ -207,9 +203,11 @@
|
|||||||
|
|
||||||
// create unique id for component
|
// create unique id for component
|
||||||
_guid = Guid.NewGuid().ToString("N");
|
_guid = Guid.NewGuid().ToString("N");
|
||||||
_fileinputid = _guid + "FileInput";
|
_fileinputid = "FileInput_" + _guid;
|
||||||
_progressinfoid = _guid + "ProgressInfo";
|
_progressinfoid = "ProgressInfo_" + _guid;
|
||||||
_progressbarid = _guid + "ProgressBar";
|
_progressbarid = "ProgressBar_" + _guid;
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GetFiles()
|
private async Task GetFiles()
|
||||||
@ -225,7 +223,7 @@
|
|||||||
Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId);
|
Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId);
|
||||||
if (folder != null)
|
if (folder != null)
|
||||||
{
|
{
|
||||||
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.Permissions);
|
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.PermissionList);
|
||||||
_files = await FileService.GetFilesAsync(FolderId);
|
_files = await FileService.GetFilesAsync(FolderId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -304,37 +302,77 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UploadFile()
|
private async Task UploadFiles()
|
||||||
{
|
{
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
var upload = await interop.GetFiles(_fileinputid);
|
var uploads = await interop.GetFiles(_fileinputid);
|
||||||
if (upload.Length > 0)
|
if (uploads.Length > 0)
|
||||||
|
{
|
||||||
|
string restricted = "";
|
||||||
|
foreach (var upload in uploads)
|
||||||
|
{
|
||||||
|
var extension = (upload.LastIndexOf(".") != -1) ? upload.Substring(upload.LastIndexOf(".") + 1) : "";
|
||||||
|
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||||
|
{
|
||||||
|
restricted += (restricted == "" ? "" : ",") + extension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (restricted == "")
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string result;
|
// upload the files
|
||||||
if (Folder == Constants.PackagesFolder)
|
var posturl = Utilities.TenantUrl(PageState.Alias, "/api/file/upload");
|
||||||
|
var folder = (Folder == Constants.PackagesFolder) ? Folder : FolderId.ToString();
|
||||||
|
await interop.UploadFiles(posturl, folder, _guid, SiteState.AntiForgeryToken);
|
||||||
|
|
||||||
|
// uploading is asynchronous so we need to wait for the uploads to complete
|
||||||
|
// note that this will only wait a maximum of 15 seconds which may not be long enough for very large file uploads
|
||||||
|
bool success = false;
|
||||||
|
int attempts = 0;
|
||||||
|
while (attempts < 5 && !success)
|
||||||
{
|
{
|
||||||
result = await FileService.UploadFilesAsync(Folder, upload, _guid);
|
attempts += 1;
|
||||||
|
Thread.Sleep(1000 * attempts); // progressive retry
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
List<File> files = await FileService.GetFilesAsync(folder);
|
||||||
|
if (files.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (string upload in uploads)
|
||||||
|
{
|
||||||
|
if (!files.Exists(item => item.Name == upload))
|
||||||
|
{
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
result = await FileService.UploadFilesAsync(FolderId, upload, _guid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == string.Empty)
|
// reset progress indicators
|
||||||
|
await interop.SetElementAttribute(_guid + "ProgressInfo", "style", "display: none;");
|
||||||
|
await interop.SetElementAttribute(_guid + "ProgressBar", "style", "display: none;");
|
||||||
|
|
||||||
|
if (success)
|
||||||
{
|
{
|
||||||
await logger.LogInformation("File Upload Succeeded {Files}", upload);
|
await logger.LogInformation("File Upload Succeeded {Files}", uploads);
|
||||||
if (ShowSuccess)
|
if (ShowSuccess)
|
||||||
{
|
{
|
||||||
_message = Localizer["Success.File.Upload"];
|
_message = Localizer["Success.File.Upload"];
|
||||||
_messagetype = MessageType.Success;
|
_messagetype = MessageType.Success;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await logger.LogInformation("File Upload Failed Or Is Still In Progress {Files}", uploads);
|
||||||
|
_message = Localizer["Error.File.Upload"];
|
||||||
|
_messagetype = MessageType.Error;
|
||||||
|
}
|
||||||
|
|
||||||
// set FileId to first file in upload collection
|
// set FileId to first file in upload collection
|
||||||
await GetFiles();
|
await GetFiles();
|
||||||
var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault();
|
var file = _files.Where(item => item.Name == uploads[0]).FirstOrDefault();
|
||||||
if (file != null)
|
if (file != null)
|
||||||
{
|
{
|
||||||
FileId = file.FileId;
|
FileId = file.FileId;
|
||||||
@ -343,23 +381,20 @@
|
|||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
await logger.LogError("File Upload Failed For {Files}", result.Replace(",", ", "));
|
|
||||||
|
|
||||||
_message = Localizer["Error.File.Upload"];
|
|
||||||
_messagetype = MessageType.Error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "File Upload Failed {Error}", ex.Message);
|
await logger.LogError(ex, "File Upload Failed {Error}", ex.Message);
|
||||||
|
|
||||||
_message = Localizer["Error.File.Upload"];
|
_message = Localizer["Error.File.Upload"];
|
||||||
_messagetype = MessageType.Error;
|
_messagetype = MessageType.Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
_message = string.Format(Localizer["Message.File.Restricted"], restricted);
|
||||||
|
_messagetype = MessageType.Warning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
_message = Localizer["Message.File.NotSelected"];
|
_message = Localizer["Message.File.NotSelected"];
|
||||||
_messagetype = MessageType.Warning;
|
_messagetype = MessageType.Warning;
|
||||||
@ -397,4 +432,24 @@
|
|||||||
public int GetFolderId() => FolderId;
|
public int GetFolderId() => FolderId;
|
||||||
|
|
||||||
public File GetFile() => _file;
|
public File GetFile() => _file;
|
||||||
|
|
||||||
|
public async Task Refresh()
|
||||||
|
{
|
||||||
|
await Refresh(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Refresh(int fileId)
|
||||||
|
{
|
||||||
|
await GetFiles();
|
||||||
|
if (fileId != -1)
|
||||||
|
{
|
||||||
|
var file = _files.Where(item => item.FileId == fileId).FirstOrDefault();
|
||||||
|
if (file != null)
|
||||||
|
{
|
||||||
|
FileId = file.FileId;
|
||||||
|
await SetImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Oqtane.Shared;
|
|
||||||
|
|
||||||
namespace Oqtane.Modules.Controls
|
namespace Oqtane.Modules.Controls
|
||||||
{
|
{
|
||||||
public class LocalizableComponent : ModuleControlBase
|
public class LocalizableComponent : ModuleControlBase
|
||||||
{
|
{
|
||||||
|
[Inject] public IStringLocalizerFactory LocalizerFactory { get; set; }
|
||||||
|
|
||||||
private IStringLocalizer _localizer;
|
private IStringLocalizer _localizer;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
@ -30,48 +30,32 @@ namespace Oqtane.Modules.Controls
|
|||||||
var key = $"{ResourceKey}.{propertyName}";
|
var key = $"{ResourceKey}.{propertyName}";
|
||||||
var value = Localize(key);
|
var value = Localize(key);
|
||||||
|
|
||||||
if (value == key)
|
if (value == key || value == String.Empty)
|
||||||
{
|
{
|
||||||
// Returns default property value (English version) instead of ResourceKey.PropertyName
|
// return default property value if key does not exist in resource file or value is empty
|
||||||
return propertyValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (value == String.Empty)
|
|
||||||
{
|
|
||||||
// Returns default property value (English version)
|
|
||||||
return propertyValue;
|
return propertyValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// return localized value
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
IsLocalizable = false;
|
IsLocalizable = false;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(ResourceType))
|
if (String.IsNullOrEmpty(ResourceType))
|
||||||
{
|
{
|
||||||
ResourceType = ModuleState?.ModuleType;
|
ResourceType = ModuleState?.ModuleType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!String.IsNullOrEmpty(ResourceKey) && !string.IsNullOrEmpty(ResourceType))
|
if (!String.IsNullOrEmpty(ResourceKey) && !String.IsNullOrEmpty(ResourceType))
|
||||||
{
|
{
|
||||||
var moduleType = Type.GetType(ResourceType);
|
_localizer = LocalizerFactory.Create(ResourceType);
|
||||||
if (moduleType != null)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceActivator.GetScope())
|
|
||||||
{
|
|
||||||
var localizerFactory = scope.ServiceProvider.GetService<IStringLocalizerFactory>();
|
|
||||||
_localizer = localizerFactory.Create(moduleType);
|
|
||||||
|
|
||||||
IsLocalizable = true;
|
IsLocalizable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Controls
|
@namespace Oqtane.Modules.Controls
|
||||||
@inherits ModuleControlBase
|
@inherits ModuleControlBase
|
||||||
|
@inject IStringLocalizerFactory LocalizerFactory
|
||||||
@typeparam TableItem
|
@typeparam TableItem
|
||||||
|
|
||||||
@if (ItemList != null)
|
@if (ItemList != null)
|
||||||
@ -7,16 +8,16 @@
|
|||||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-center my-2">
|
||||||
<li class="page-item@((_page > 1) ? "" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_page > _displayPages) ? "" : " disabled")">
|
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page > 1) ? "" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@for (int i = _startPage; i <= _endPage; i++)
|
@for (int i = _startPage; i <= _endPage; i++)
|
||||||
@ -24,31 +25,31 @@
|
|||||||
var pager = i;
|
var pager = i;
|
||||||
if (pager == _page)
|
if (pager == _page)
|
||||||
{
|
{
|
||||||
<li class="page-item active">
|
<li class="page-item app-pager-pointer active">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<li class="page-item">
|
<li class="page-item app-pager-pointer">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? "" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_endPage < _pages) ? "" : " disabled")">
|
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? "" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item disabled">
|
<li class="page-item disabled">
|
||||||
<a class="page-link" style="white-space: nowrap;">Page @_page of @_pages</a>
|
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
@ -69,6 +70,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="@RowClass">@Footer</tr>
|
||||||
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@ -115,16 +119,16 @@
|
|||||||
@if ((Toolbar == "Bottom" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
@if ((Toolbar == "Bottom" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-center my-2">
|
||||||
<li class="page-item@((_page > 1) ? "" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_page > _displayPages) ? "" : " disabled")">
|
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page > 1) ? "" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@for (int i = _startPage; i <= _endPage; i++)
|
@for (int i = _startPage; i <= _endPage; i++)
|
||||||
@ -132,37 +136,38 @@
|
|||||||
var pager = i;
|
var pager = i;
|
||||||
if (pager == _page)
|
if (pager == _page)
|
||||||
{
|
{
|
||||||
<li class="page-item active">
|
<li class="page-item app-pager-pointer active">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<li class="page-item">
|
<li class="page-item app-pager-pointer">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? "" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_endPage < _pages) ? "" : " disabled")">
|
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? "" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item disabled">
|
<li class="page-item disabled">
|
||||||
<a class="page-link" style="white-space: nowrap;">Page @_page of @_pages</a>
|
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private IStringLocalizer Localizer;
|
||||||
private int _pages = 0;
|
private int _pages = 0;
|
||||||
private int _page = 1;
|
private int _page = 1;
|
||||||
private int _maxItems = 10;
|
private int _maxItems = 10;
|
||||||
@ -183,6 +188,9 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment<TableItem> Row { get; set; } = null; // required
|
public RenderFragment<TableItem> Row { get; set; } = null; // required
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment Footer { get; set; } = null; // only applicable to Table layouts
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
|
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
|
||||||
|
|
||||||
@ -215,6 +223,11 @@
|
|||||||
|
|
||||||
private IEnumerable<TableItem> ItemList { get; set; }
|
private IEnumerable<TableItem> ItemList { get; set; }
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
Localizer = LocalizerFactory.Create(GetType().FullName);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(Format))
|
if (string.IsNullOrEmpty(Format))
|
||||||
@ -286,6 +299,7 @@
|
|||||||
{
|
{
|
||||||
_page = 1;
|
_page = 1;
|
||||||
}
|
}
|
||||||
|
if (_page < 1) _page = 1;
|
||||||
|
|
||||||
_startPage = 0;
|
_startPage = 0;
|
||||||
_endPage = 0;
|
_endPage = 0;
|
||||||
@ -297,7 +311,6 @@
|
|||||||
{
|
{
|
||||||
_page = _pages;
|
_page = _pages;
|
||||||
}
|
}
|
||||||
ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems);
|
|
||||||
SetPagerSize();
|
SetPagerSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,13 +323,13 @@
|
|||||||
{
|
{
|
||||||
_endPage = _pages;
|
_endPage = _pages;
|
||||||
}
|
}
|
||||||
OnPageChange?.Invoke(_page);
|
ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
OnPageChange?.Invoke(_page);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateList(int page)
|
public void UpdateList(int page)
|
||||||
{
|
{
|
||||||
ItemList = Items.Skip((page - 1) * _maxItems).Take(_maxItems);
|
|
||||||
_page = page;
|
_page = page;
|
||||||
SetPagerSize();
|
SetPagerSize();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
@namespace Oqtane.Modules.Controls
|
@namespace Oqtane.Modules.Controls
|
||||||
|
@using System.Text.Json
|
||||||
@inherits ModuleControlBase
|
@inherits ModuleControlBase
|
||||||
@inject IRoleService RoleService
|
@inject IRoleService RoleService
|
||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
|
@inject IUserRoleService UserRoleService
|
||||||
@inject IStringLocalizer<PermissionGrid> Localizer
|
@inject IStringLocalizer<PermissionGrid> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@ -14,20 +16,19 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">@Localizer["Role"]</th>
|
<th scope="col">@Localizer["Role"]</th>
|
||||||
@foreach (PermissionString permission in _permissions)
|
@foreach (var permissionname in _permissionnames)
|
||||||
{
|
{
|
||||||
<th style="text-align: center; width: 1px;">@Localizer[permission.PermissionName]</th>
|
<th style="text-align: center; width: 1px;">@((MarkupString)DisplayPermissionName(permissionname).Replace(" ", "<br />"))</th>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
@foreach (Role role in _roles)
|
@foreach (Role role in _roles)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>@role.Name</td>
|
<td>@role.Name</td>
|
||||||
@foreach (PermissionString permission in _permissions)
|
@foreach (var permissionname in _permissionnames)
|
||||||
{
|
{
|
||||||
var p = permission;
|
|
||||||
<td style="text-align: center;">
|
<td style="text-align: center;">
|
||||||
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, role.Name) Disabled=@GetPermissionDisabled(role.Name) OnChange="@(e => PermissionChanged(e, p.PermissionName, role.Name))" />
|
<TriStateCheckBox Value=@GetPermissionValue(permissionname, role.Name, -1) Disabled="@GetPermissionDisabled(permissionname, role.Name)" OnChange="@(e => PermissionChanged(e, permissionname, role.Name, -1))" />
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
@ -49,23 +50,21 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">@Localizer["User"]</th>
|
<th scope="col">@Localizer["User"]</th>
|
||||||
@foreach (PermissionString permission in _permissions)
|
@foreach (var permissionname in _permissionnames)
|
||||||
{
|
{
|
||||||
<th style="text-align: center; width: 1px;">@Localizer[permission.PermissionName]</th>
|
<th style="text-align: center; width: 1px;">@((MarkupString)DisplayPermissionName(permissionname).Replace(" ", "<br />"))</th>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach (User user in _users)
|
@foreach (User user in _users)
|
||||||
{
|
{
|
||||||
string userid = "[" + user.UserId.ToString() + "]";
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>@user.DisplayName</td>
|
<td>@user.DisplayName</td>
|
||||||
@foreach (PermissionString permission in _permissions)
|
@foreach (var permissionname in _permissionnames)
|
||||||
{
|
{
|
||||||
var p = permission;
|
|
||||||
<td style="text-align: center; width: 1px;">
|
<td style="text-align: center; width: 1px;">
|
||||||
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, userid) Disabled=false OnChange="@(e => PermissionChanged(e, p.PermissionName, userid))" />
|
<TriStateCheckBox Value=@GetPermissionValue(permissionname, "", user.UserId) Disabled="@GetPermissionDisabled(permissionname, "")" OnChange="@(e => PermissionChanged(e, permissionname, "", user.UserId))" />
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
@ -77,34 +76,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col-11">
|
||||||
<table class="table table-borderless">
|
<AutoComplete OnSearch="GetUsers" Placeholder="@Localizer["Username.Enter"]" @ref="_user" />
|
||||||
<tbody>
|
</div>
|
||||||
<tr>
|
<div class="col-1">
|
||||||
<td class="input-group">
|
|
||||||
<input type="text" name="Username" class="form-control" placeholder="@Localizer["Username.Enter"]" @bind="@_username" />
|
|
||||||
<button type="button" class="btn btn-primary" @onclick="AddUser">@SharedLocalizer["Add"]</button>
|
<button type="button" class="btn btn-primary" @onclick="AddUser">@SharedLocalizer["Add"]</button>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<br />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<ModuleMessage Type="MessageType.Error" Message="@_message" />
|
<ModuleMessage Type="MessageType.Warning" Message="@_message" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _permissionnames = string.Empty;
|
private List<string> _permissionnames;
|
||||||
|
private List<Permission> _permissions;
|
||||||
private List<Role> _roles;
|
private List<Role> _roles;
|
||||||
private List<PermissionString> _permissions;
|
|
||||||
private List<User> _users = new List<User>();
|
private List<User> _users = new List<User>();
|
||||||
private string _username = string.Empty;
|
private AutoComplete _user;
|
||||||
private string _message = string.Empty;
|
private string _message = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
@ -114,159 +106,230 @@
|
|||||||
public string PermissionNames { get; set; }
|
public string PermissionNames { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Permissions { get; set; }
|
public string Permissions { get; set; } // deprecated - use PermissionList instead
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public List<Permission> PermissionList { get; set; }
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(PermissionNames))
|
|
||||||
{
|
|
||||||
_permissionnames = Shared.PermissionNames.View + "," + Shared.PermissionNames.Edit;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_permissionnames = PermissionNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId);
|
|
||||||
_roles.Insert(0, new Role { Name = RoleNames.Everyone });
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
|
||||||
{
|
|
||||||
_roles.Add(new Role { Name = RoleNames.Host });
|
|
||||||
}
|
|
||||||
|
|
||||||
_permissions = new List<PermissionString>();
|
|
||||||
|
|
||||||
foreach (string permissionname in _permissionnames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
{
|
|
||||||
// initialize with admin role
|
|
||||||
_permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = RoleNames.Admin });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Permissions))
|
if (!string.IsNullOrEmpty(Permissions))
|
||||||
{
|
{
|
||||||
// populate permissions
|
PermissionList = JsonSerializer.Deserialize<List<Permission>>(Permissions);
|
||||||
foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions))
|
|
||||||
{
|
|
||||||
if (_permissions.Find(item => item.PermissionName == permissionstring.PermissionName) != null)
|
|
||||||
{
|
|
||||||
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permissionstring.Permissions.Contains("["))
|
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId, true);
|
||||||
|
if (!UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries))
|
_roles.RemoveAll(item => item.Name == RoleNames.Host);
|
||||||
{
|
|
||||||
if (user.Contains("]"))
|
|
||||||
{
|
|
||||||
var userid = int.Parse(user.Substring(0, user.IndexOf("]")));
|
|
||||||
if (_users.Where(item => item.UserId == userid).FirstOrDefault() == null)
|
|
||||||
{
|
|
||||||
_users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool? GetPermissionValue(string permissions, string securityKey)
|
// get permission names
|
||||||
|
if (string.IsNullOrEmpty(PermissionNames))
|
||||||
{
|
{
|
||||||
if ((";" + permissions + ";").Contains(";" + "!" + securityKey + ";"))
|
_permissionnames = new List<string>();
|
||||||
{
|
_permissionnames.Add(Shared.PermissionNames.View);
|
||||||
return false; // deny permission
|
_permissionnames.Add(Shared.PermissionNames.Edit);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((";" + permissions + ";").Contains(";" + securityKey + ";"))
|
_permissionnames = PermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize permissions
|
||||||
|
_permissions = new List<Permission>();
|
||||||
|
if (PermissionList != null && PermissionList.Any())
|
||||||
{
|
{
|
||||||
return true; // grant permission
|
foreach (var permission in PermissionList)
|
||||||
|
{
|
||||||
|
_permissions.Add(permission);
|
||||||
|
if (permission.UserId != null)
|
||||||
|
{
|
||||||
|
if (!_users.Any(item => item.UserId == permission.UserId.Value))
|
||||||
|
{
|
||||||
|
_users.Add(await UserService.GetUserAsync(permission.UserId.Value, ModuleState.SiteId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return null; // not specified
|
foreach (string permissionname in _permissionnames)
|
||||||
|
{
|
||||||
|
// permission names can be in the form of "EntityName:PermissionName:Roles"
|
||||||
|
if (permissionname.Contains(":"))
|
||||||
|
{
|
||||||
|
var segments = permissionname.Split(':');
|
||||||
|
if (segments.Length == 3)
|
||||||
|
{
|
||||||
|
foreach (var role in segments[2].Split(';'))
|
||||||
|
{
|
||||||
|
_permissions.Add(new Permission(ModuleState.SiteId, segments[0], segments[1], role, null, true));
|
||||||
|
}
|
||||||
|
// ensure admin access
|
||||||
|
if (!_permissions.Any(item => item.EntityName == segments[0] && item.PermissionName == segments[1] && item.RoleName == RoleNames.Admin))
|
||||||
|
{
|
||||||
|
_permissions.Add(new Permission(ModuleState.SiteId, segments[0], segments[1], RoleNames.Admin, null, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_permissions.Add(new Permission(ModuleState.SiteId, EntityName, permissionname, RoleNames.Admin, null, true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool GetPermissionDisabled(string roleName)
|
private string GetPermissionName(string permissionName)
|
||||||
=> (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) ? true : false;
|
{
|
||||||
|
return (permissionName.Contains(":")) ? permissionName.Split(':')[1] : permissionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetEntityName(string permissionName)
|
||||||
|
{
|
||||||
|
return (permissionName.Contains(":")) ? permissionName.Split(':')[0] : EntityName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string DisplayPermissionName(string permissionName)
|
||||||
|
{
|
||||||
|
var name = Localizer[GetPermissionName(permissionName)].ToString();
|
||||||
|
name += " " + Localizer[GetEntityName(permissionName)].ToString();
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool? GetPermissionValue(string permissionName, string roleName, int userId)
|
||||||
|
{
|
||||||
|
bool? isauthorized = null;
|
||||||
|
if (roleName != "")
|
||||||
|
{
|
||||||
|
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.RoleName == roleName);
|
||||||
|
if (permission != null)
|
||||||
|
{
|
||||||
|
isauthorized = permission.IsAuthorized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.UserId == userId);
|
||||||
|
if (permission != null)
|
||||||
|
{
|
||||||
|
isauthorized = permission.IsAuthorized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool GetPermissionDisabled(string permissionName, string roleName)
|
||||||
|
{
|
||||||
|
if (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GetEntityName(permissionName) != EntityName && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PermissionChanged(bool? value, string permissionName, string roleName, int userId)
|
||||||
|
{
|
||||||
|
if (roleName != "")
|
||||||
|
{
|
||||||
|
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.RoleName == roleName);
|
||||||
|
if (permission != null)
|
||||||
|
{
|
||||||
|
_permissions.Remove(permission);
|
||||||
|
}
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
_permissions.Add(new Permission(ModuleState.SiteId, GetEntityName(permissionName), GetPermissionName(permissionName), roleName, null, value.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.UserId == userId);
|
||||||
|
if (permission != null)
|
||||||
|
{
|
||||||
|
_permissions.Remove(permission);
|
||||||
|
}
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
_permissions.Add(new Permission(ModuleState.SiteId, GetEntityName(permissionName), GetPermissionName(permissionName), null, userId, value.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Dictionary<string, string>> GetUsers(string filter)
|
||||||
|
{
|
||||||
|
var users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
||||||
|
return users.Where(item => item.User.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ToDictionary(item => item.UserId.ToString(), item => item.User.DisplayName);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task AddUser()
|
private async Task AddUser()
|
||||||
{
|
{
|
||||||
if (_users.Where(item => item.Username == _username).FirstOrDefault() == null)
|
if (!string.IsNullOrEmpty(_user.Key))
|
||||||
{
|
{
|
||||||
try
|
var user = await UserService.GetUserAsync(int.Parse(_user.Key), ModuleState.SiteId);
|
||||||
{
|
if (user != null && !_users.Any(item => item.UserId == user.UserId))
|
||||||
var user = await UserService.GetUserAsync(_username, ModuleState.SiteId);
|
|
||||||
if (user != null)
|
|
||||||
{
|
{
|
||||||
_users.Add(user);
|
_users.Add(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
else
|
||||||
{
|
{
|
||||||
_message = Localizer["Message.Username.DontExist"];
|
_message = Localizer["Message.Username.DontExist"];
|
||||||
}
|
}
|
||||||
}
|
_user.Clear();
|
||||||
|
|
||||||
_username = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PermissionChanged(bool? value, string permissionName, string securityId)
|
|
||||||
{
|
|
||||||
var selected = value;
|
|
||||||
var permission = _permissions.Find(item => item.PermissionName == permissionName);
|
|
||||||
if (permission != null)
|
|
||||||
{
|
|
||||||
var ids = permission.Permissions.Split(';').ToList();
|
|
||||||
|
|
||||||
ids.Remove(securityId); // remove grant permission
|
|
||||||
ids.Remove("!" + securityId); // remove deny permission
|
|
||||||
|
|
||||||
switch (selected)
|
|
||||||
{
|
|
||||||
case true:
|
|
||||||
ids.Add(securityId); // add grant permission
|
|
||||||
break;
|
|
||||||
case false:
|
|
||||||
ids.Add("!" + securityId); // add deny permission
|
|
||||||
break;
|
|
||||||
case null:
|
|
||||||
break; // permission not specified
|
|
||||||
}
|
|
||||||
|
|
||||||
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionName)].Permissions = string.Join(";", ids.ToArray());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetPermissions()
|
public string GetPermissions()
|
||||||
{
|
{
|
||||||
ValidatePermissions();
|
ValidatePermissions();
|
||||||
return UserSecurity.SetPermissionStrings(_permissions);
|
return JsonSerializer.Serialize(_permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Permission> GetPermissionList()
|
||||||
|
{
|
||||||
|
ValidatePermissions();
|
||||||
|
return _permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidatePermissions()
|
private void ValidatePermissions()
|
||||||
{
|
{
|
||||||
PermissionString permission;
|
// remove deny all users, unauthenticated, and registered users
|
||||||
for (int i = 0; i < _permissions.Count; i++)
|
var permissions = _permissions.Where(item => !item.IsAuthorized &&
|
||||||
|
(item.RoleName == RoleNames.Everyone || item.RoleName == RoleNames.Unauthenticated || item.RoleName == RoleNames.Registered)).ToList();
|
||||||
|
foreach (var permission in permissions)
|
||||||
{
|
{
|
||||||
permission = _permissions[i];
|
_permissions.Remove(permission);
|
||||||
List<string> ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
|
}
|
||||||
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
|
||||||
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
ids.Remove("!" + RoleNames.Admin); // remove deny administrators
|
// remove deny administrators and host users
|
||||||
ids.Remove("!" + RoleNames.Host); // remove deny host users
|
permissions = _permissions.Where(item => !item.IsAuthorized &&
|
||||||
if (!ids.Contains(RoleNames.Host) && !ids.Contains(RoleNames.Admin))
|
(item.RoleName == RoleNames.Admin || item.RoleName == RoleNames.Host)).ToList();
|
||||||
|
foreach (var permission in permissions)
|
||||||
{
|
{
|
||||||
// add administrators role if host user role is not assigned
|
_permissions.Remove(permission);
|
||||||
ids.Add(RoleNames.Admin);
|
}
|
||||||
|
foreach (var permissionname in _permissionnames)
|
||||||
|
{
|
||||||
|
// add administrators role if neither host or administrator is assigned
|
||||||
|
if (!_permissions.Any(item => item.EntityName == GetEntityName(permissionname) && item.PermissionName == GetPermissionName(permissionname) &&
|
||||||
|
(item.RoleName == RoleNames.Admin || item.RoleName == RoleNames.Host)))
|
||||||
|
{
|
||||||
|
_permissions.Add(new Permission(ModuleState.SiteId, GetEntityName(permissionname), GetPermissionName(permissionname), RoleNames.Admin, null, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
permission.Permissions = string.Join(";", ids.ToArray());
|
|
||||||
_permissions[i] = permission;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,24 +6,27 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<TabStrip>
|
<TabStrip>
|
||||||
<TabPanel Name="Rich" Heading="Rich Text Editor">
|
<TabPanel Name="Rich" Heading="Rich Text Editor">
|
||||||
@if (AllowFileManagement)
|
@if (_richfilemanager)
|
||||||
{
|
|
||||||
@if (_filemanagervisible)
|
|
||||||
{
|
{
|
||||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||||
<br />
|
<br />
|
||||||
}
|
}
|
||||||
<div class="d-flex justify-content-center mb-2">
|
<div class="d-flex justify-content-center mb-2">
|
||||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>
|
@if (AllowRawHtml)
|
||||||
<button type="button" class="btn btn-primary" @onclick="InsertImage">@Localizer["InsertImage"]</button>
|
{
|
||||||
@if (_filemanagervisible)
|
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>@((MarkupString)" ")
|
||||||
|
}
|
||||||
|
@if (AllowFileManagement)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
|
||||||
|
}
|
||||||
|
@if (_richfilemanager)
|
||||||
{
|
{
|
||||||
@((MarkupString)" ")
|
@((MarkupString)" ")
|
||||||
<button type="button" class="btn btn-secondary" @onclick="CloseFileManager">@Localizer["Close"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="CloseRichFileManager">@Localizer["Close"]</button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div @ref="@_toolBar">
|
<div @ref="@_toolBar">
|
||||||
@ -65,19 +68,37 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
@if (AllowRawHtml)
|
||||||
|
{
|
||||||
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
||||||
|
@if (_rawfilemanager)
|
||||||
|
{
|
||||||
|
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||||
|
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||||
|
<br />
|
||||||
|
}
|
||||||
<div class="d-flex justify-content-center mb-2">
|
<div class="d-flex justify-content-center mb-2">
|
||||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRawHtml">@Localizer["SynchronizeContent"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="RefreshRawHtml">@Localizer["SynchronizeContent"]</button>
|
||||||
|
@if (AllowFileManagement)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="InsertRawImage">@Localizer["InsertImage"]</button>
|
||||||
|
}
|
||||||
|
@if (_rawfilemanager)
|
||||||
|
{
|
||||||
|
@((MarkupString)" ")
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="CloseRawFileManager">@Localizer["Close"]</button>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
@if (ReadOnly)
|
@if (ReadOnly)
|
||||||
{
|
{
|
||||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10" readonly></textarea>
|
<textarea id="rawhtmleditor" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10" readonly></textarea>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
|
<textarea id="rawhtmleditor" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
}
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -85,10 +106,11 @@
|
|||||||
@code {
|
@code {
|
||||||
private ElementReference _editorElement;
|
private ElementReference _editorElement;
|
||||||
private ElementReference _toolBar;
|
private ElementReference _toolBar;
|
||||||
private bool _filemanagervisible = false;
|
private bool _richfilemanager = false;
|
||||||
private FileManager _fileManager;
|
private FileManager _fileManager;
|
||||||
private string _richhtml = string.Empty;
|
private string _richhtml = string.Empty;
|
||||||
private string _originalrichhtml = string.Empty;
|
private string _originalrichhtml = string.Empty;
|
||||||
|
private bool _rawfilemanager = false;
|
||||||
private string _rawhtml = string.Empty;
|
private string _rawhtml = string.Empty;
|
||||||
private string _originalrawhtml = string.Empty;
|
private string _originalrawhtml = string.Empty;
|
||||||
private string _message = string.Empty;
|
private string _message = string.Empty;
|
||||||
@ -102,6 +124,12 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string Placeholder { get; set; } = "Enter Your Content...";
|
public string Placeholder { get; set; } = "Enter Your Content...";
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool AllowFileManagement { get; set; } = true;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool AllowRawHtml { get; set; } = true;
|
||||||
|
|
||||||
// parameters only applicable to rich text editor
|
// parameters only applicable to rich text editor
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment ToolbarContent { get; set; }
|
public RenderFragment ToolbarContent { get; set; }
|
||||||
@ -112,9 +140,6 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string DebugLevel { get; set; } = "info";
|
public string DebugLevel { get; set; } = "info";
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public bool AllowFileManagement { get; set; } = true;
|
|
||||||
|
|
||||||
public override List<Resource> Resources => new List<Resource>()
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
{
|
{
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" },
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" },
|
||||||
@ -150,15 +175,18 @@
|
|||||||
// preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor)
|
// preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor)
|
||||||
_originalrichhtml = await interop.GetHtml(_editorElement);
|
_originalrichhtml = await interop.GetHtml(_editorElement);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
await interop.LoadEditorContent(_editorElement, _richhtml);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CloseFileManager()
|
public void CloseRichFileManager()
|
||||||
{
|
{
|
||||||
_filemanagervisible = false;
|
_richfilemanager = false;
|
||||||
|
_message = string.Empty;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseRawFileManager()
|
||||||
|
{
|
||||||
|
_rawfilemanager = false;
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
@ -200,17 +228,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InsertImage()
|
public async Task InsertRichImage()
|
||||||
{
|
{
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
if (_filemanagervisible)
|
if (_richfilemanager)
|
||||||
{
|
{
|
||||||
var file = _fileManager.GetFile();
|
var file = _fileManager.GetFile();
|
||||||
if (file != null)
|
if (file != null)
|
||||||
{
|
{
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
var interop = new RichTextEditorInterop(JSRuntime);
|
||||||
await interop.InsertImage(_editorElement, file.Url, file.Name);
|
await interop.InsertImage(_editorElement, file.Url, ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name));
|
||||||
_filemanagervisible = false;
|
_richfilemanager = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -219,7 +247,33 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_filemanagervisible = true;
|
_richfilemanager = true;
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InsertRawImage()
|
||||||
|
{
|
||||||
|
_message = string.Empty;
|
||||||
|
if (_rawfilemanager)
|
||||||
|
{
|
||||||
|
var file = _fileManager.GetFile();
|
||||||
|
if (file != null)
|
||||||
|
{
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
int pos = await interop.GetCaretPosition("rawhtmleditor");
|
||||||
|
var image = "<img src=\"" + file.Url + "\" alt=\"" + ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name) + "\" class=\"img-fluid\">";
|
||||||
|
_rawhtml = _rawhtml.Substring(0, pos) + image + _rawhtml.Substring(pos);
|
||||||
|
_rawfilemanager = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_message = Localizer["Message.Require.Image"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_rawfilemanager = true;
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
@if (Name == Parent.ActiveTab)
|
@if (Name == Parent.ActiveTab)
|
||||||
{
|
{
|
||||||
<div id="@Name" class="tab-pane fade show active" role="tabpanel">
|
<div id="@(Parent.Id + Name)" class="tab-pane fade show active" role="tabpanel">
|
||||||
@ChildContent
|
@ChildContent
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div id="@Name" class="tab-pane fade" role="tabpanel">
|
<div id="@(Parent.Id + Name)" class="tab-pane fade" role="tabpanel">
|
||||||
@ChildContent
|
@ChildContent
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -10,13 +10,13 @@
|
|||||||
<li class="nav-item" @key="tabPanel.Name">
|
<li class="nav-item" @key="tabPanel.Name">
|
||||||
@if (tabPanel.Name == ActiveTab)
|
@if (tabPanel.Name == ActiveTab)
|
||||||
{
|
{
|
||||||
<a class="nav-link active" data-bs-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
|
<a class="nav-link active" data-bs-toggle="tab" href="#@(Id + tabPanel.Name)" role="tab" @onclick:preventDefault="true">
|
||||||
@tabPanel.DisplayHeading()
|
@tabPanel.DisplayHeading()
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<a class="nav-link" data-bs-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
|
<a class="nav-link" data-bs-toggle="tab" href="#@(Id + tabPanel.Name)" role="tab" @onclick:preventDefault="true">
|
||||||
@tabPanel.DisplayHeading()
|
@tabPanel.DisplayHeading()
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<TabPanel> _tabPanels;
|
private List<TabPanel> _tabPanels;
|
||||||
|
private string _tabpanelid = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment ChildContent { get; set; } // contains the TabPanels
|
public RenderFragment ChildContent { get; set; } // contains the TabPanels
|
||||||
@ -43,6 +44,18 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public bool Refresh { get; set; } // optional - used in scenarios where TabPanels are added/removed dynamically within a parent form. ActiveTab may need to be reset as well when this property is used.
|
public bool Refresh { get; set; } // optional - used in scenarios where TabPanels are added/removed dynamically within a parent form. ActiveTab may need to be reset as well when this property is used.
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Id { get; set; } // optional - used to uniquely identify an instance of a tab strip component (will be set automatically if no value provided)
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Id))
|
||||||
|
{
|
||||||
|
// create unique id for component
|
||||||
|
Id = "TabStrip_" + Guid.NewGuid().ToString("N") + "_" ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
if (PageState.QueryString.ContainsKey("tab"))
|
if (PageState.QueryString.ContainsKey("tab"))
|
||||||
@ -80,10 +93,10 @@
|
|||||||
authorized = true;
|
authorized = true;
|
||||||
break;
|
break;
|
||||||
case SecurityAccessLevel.View:
|
case SecurityAccessLevel.View:
|
||||||
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, ModuleState.Permissions);
|
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, ModuleState.PermissionList);
|
||||||
break;
|
break;
|
||||||
case SecurityAccessLevel.Edit:
|
case SecurityAccessLevel.Edit:
|
||||||
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions);
|
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.PermissionList);
|
||||||
break;
|
break;
|
||||||
case SecurityAccessLevel.Admin:
|
case SecurityAccessLevel.Admin:
|
||||||
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);
|
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<TabPanel Name="Edit" Heading="Edit" ResourceKey="Edit">
|
<TabPanel Name="Edit" Heading="Edit" ResourceKey="Edit">
|
||||||
@if (_content != null)
|
@if (_content != null)
|
||||||
{
|
{
|
||||||
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" @ref="@RichTextEditorHtml"></RichTextEditor>
|
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" AllowRawHtml="@_allowrawhtml" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||||
<br />
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
@ -60,6 +60,7 @@
|
|||||||
|
|
||||||
private RichTextEditor RichTextEditorHtml;
|
private RichTextEditor RichTextEditorHtml;
|
||||||
private bool _allowfilemanagement;
|
private bool _allowfilemanagement;
|
||||||
|
private bool _allowrawhtml;
|
||||||
private string _content = null;
|
private string _content = null;
|
||||||
private string _createdby;
|
private string _createdby;
|
||||||
private DateTime _createdon;
|
private DateTime _createdon;
|
||||||
@ -73,6 +74,7 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
|
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
|
||||||
|
_allowrawhtml = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowRawHtml", "true"));
|
||||||
await LoadContent();
|
await LoadContent();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -15,17 +15,28 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="files" ResourceKey="AllowRawHtml" ResourceType="@resourceType" HelpText="Specify If Editors Can Enter Raw HTML">Allow Raw HTML: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="files" class="form-select" @bind="@_allowrawhtml">
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
|
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
|
||||||
private string _allowfilemanagement;
|
private string _allowfilemanagement;
|
||||||
|
private string _allowrawhtml;
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true");
|
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true");
|
||||||
|
_allowrawhtml = SettingService.GetSetting(ModuleState.Settings, "AllowRawHtml", "true");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -39,6 +50,7 @@
|
|||||||
{
|
{
|
||||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||||
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
|
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
|
||||||
|
settings = SettingService.SetSetting(settings, "AllowRawHtml", _allowrawhtml);
|
||||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -15,6 +15,8 @@ namespace Oqtane.Modules
|
|||||||
public abstract class ModuleBase : ComponentBase, IModuleControl
|
public abstract class ModuleBase : ComponentBase, IModuleControl
|
||||||
{
|
{
|
||||||
private Logger _logger;
|
private Logger _logger;
|
||||||
|
private string _urlparametersstate;
|
||||||
|
private Dictionary<string, string> _urlparameters;
|
||||||
|
|
||||||
protected Logger logger => _logger ?? (_logger = new Logger(this));
|
protected Logger logger => _logger ?? (_logger = new Logger(this));
|
||||||
|
|
||||||
@ -24,6 +26,9 @@ namespace Oqtane.Modules
|
|||||||
[Inject]
|
[Inject]
|
||||||
protected IJSRuntime JSRuntime { get; set; }
|
protected IJSRuntime JSRuntime { get; set; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
protected SiteState SiteState { get; set; }
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
protected PageState PageState { get; set; }
|
protected PageState PageState { get; set; }
|
||||||
|
|
||||||
@ -44,6 +49,21 @@ namespace Oqtane.Modules
|
|||||||
|
|
||||||
public virtual List<Resource> Resources { get; set; }
|
public virtual List<Resource> Resources { get; set; }
|
||||||
|
|
||||||
|
// url parameters
|
||||||
|
public virtual string UrlParametersTemplate { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, string> UrlParameters {
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_urlparametersstate == null || _urlparametersstate != PageState.UrlParameters)
|
||||||
|
{
|
||||||
|
_urlparametersstate = PageState.UrlParameters;
|
||||||
|
_urlparameters = GetUrlParameters(UrlParametersTemplate);
|
||||||
|
}
|
||||||
|
return _urlparameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// base lifecycle method for handling JSInterop script registration
|
// base lifecycle method for handling JSInterop script registration
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
@ -52,14 +72,24 @@ namespace Oqtane.Modules
|
|||||||
{
|
{
|
||||||
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
||||||
{
|
{
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
var scripts = new List<object>();
|
var scripts = new List<object>();
|
||||||
|
var inline = 0;
|
||||||
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
|
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
|
||||||
{
|
{
|
||||||
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module });
|
if (!string.IsNullOrEmpty(resource.Url))
|
||||||
|
{
|
||||||
|
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||||
|
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inline += 1;
|
||||||
|
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (scripts.Any())
|
if (scripts.Any())
|
||||||
{
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
|
||||||
await interop.IncludeScripts(scripts.ToArray());
|
await interop.IncludeScripts(scripts.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,7 +100,7 @@ namespace Oqtane.Modules
|
|||||||
|
|
||||||
public string ModulePath()
|
public string ModulePath()
|
||||||
{
|
{
|
||||||
return "Modules/" + GetType().Namespace + "/";
|
return PageState?.Alias.BaseUrl + "/Modules/" + GetType().Namespace + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
// url methods
|
// url methods
|
||||||
@ -124,14 +154,23 @@ namespace Oqtane.Modules
|
|||||||
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
|
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ContentUrl(int fileid)
|
public string FileUrl(string folderpath, string filename)
|
||||||
{
|
{
|
||||||
return ContentUrl(fileid, false);
|
return FileUrl(folderpath, filename, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ContentUrl(int fileid, bool asAttachment)
|
public string FileUrl(string folderpath, string filename, bool download)
|
||||||
{
|
{
|
||||||
return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment);
|
return Utilities.FileUrl(PageState.Alias, folderpath, filename, download);
|
||||||
|
}
|
||||||
|
public string FileUrl(int fileid)
|
||||||
|
{
|
||||||
|
return FileUrl(fileid, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FileUrl(int fileid, bool download)
|
||||||
|
{
|
||||||
|
return Utilities.FileUrl(PageState.Alias, fileid, download);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ImageUrl(int fileid, int width, int height)
|
public string ImageUrl(int fileid, int width, int height)
|
||||||
@ -149,15 +188,21 @@ namespace Oqtane.Modules
|
|||||||
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, position, background, rotate, recreate);
|
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, position, background, rotate, recreate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Dictionary<string, string> GetUrlParameters(string parametersTemplate = "")
|
public string AddUrlParameters(params object[] parameters)
|
||||||
|
{
|
||||||
|
return Utilities.AddUrlParameters(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// template is in the form of a standard route template ie. "/{id}/{name}" and produces dictionary of key/value pairs
|
||||||
|
// if url parameters belong to a specific module you should embed a unique key into the route (ie. /!/blog/1) and validate the url parameter key in the module
|
||||||
|
public virtual Dictionary<string, string> GetUrlParameters(string template = "")
|
||||||
{
|
{
|
||||||
var urlParameters = new Dictionary<string, string>();
|
var urlParameters = new Dictionary<string, string>();
|
||||||
string[] templateSegments;
|
var parameters = _urlparametersstate.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
var parameters = PageState.UrlParameters.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
var parameterId = 0;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(parametersTemplate))
|
if (string.IsNullOrEmpty(template))
|
||||||
{
|
{
|
||||||
|
// no template will populate dictionary with generic "parameter#" keys
|
||||||
for (int i = 0; i < parameters.Length; i++)
|
for (int i = 0; i < parameters.Length; i++)
|
||||||
{
|
{
|
||||||
urlParameters.TryAdd("parameter" + i, parameters[i]);
|
urlParameters.TryAdd("parameter" + i, parameters[i]);
|
||||||
@ -165,39 +210,37 @@ namespace Oqtane.Modules
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
templateSegments = parametersTemplate.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
var segments = template.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
string key;
|
||||||
|
|
||||||
if (parameters.Length == templateSegments.Length)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < parameters.Length; i++)
|
for (int i = 0; i < parameters.Length; i++)
|
||||||
{
|
{
|
||||||
if (parameters.Length > i)
|
if (i < segments.Length)
|
||||||
{
|
{
|
||||||
if (templateSegments[i] == parameters[i])
|
key = segments[i];
|
||||||
|
if (key.StartsWith("{") && key.EndsWith("}"))
|
||||||
{
|
{
|
||||||
urlParameters.TryAdd("parameter" + parameterId, parameters[i]);
|
// dynamic segment
|
||||||
parameterId++;
|
key = key.Substring(1, key.Length - 2);
|
||||||
}
|
|
||||||
else if (templateSegments[i].StartsWith("{") && templateSegments[i].EndsWith("}"))
|
|
||||||
{
|
|
||||||
var key = templateSegments[i].Replace("{", "");
|
|
||||||
key = key.Replace("}", "");
|
|
||||||
urlParameters.TryAdd(key, parameters[i]);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
i = parameters.Length;
|
// static segments use generic "parameter#" keys
|
||||||
urlParameters.Clear();
|
key = "parameter" + i.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else // unspecified segments use generic "parameter#" keys
|
||||||
|
{
|
||||||
|
key = "parameter" + i.ToString();
|
||||||
}
|
}
|
||||||
|
urlParameters.TryAdd(key, parameters[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return urlParameters;
|
return urlParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
// user feedback methods
|
// UI methods
|
||||||
public void AddModuleMessage(string message, MessageType type)
|
public void AddModuleMessage(string message, MessageType type)
|
||||||
{
|
{
|
||||||
ModuleInstance.AddModuleMessage(message, type);
|
ModuleInstance.AddModuleMessage(message, type);
|
||||||
@ -218,6 +261,18 @@ namespace Oqtane.Modules
|
|||||||
ModuleInstance.HideProgressIndicator();
|
ModuleInstance.HideProgressIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetModuleTitle(string title)
|
||||||
|
{
|
||||||
|
var obj = new { PageModuleId = ModuleState.PageModuleId, Title = title };
|
||||||
|
SiteState.Properties.ModuleTitle = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetModuleVisibility(bool visible)
|
||||||
|
{
|
||||||
|
var obj = new { PageModuleId = ModuleState.PageModuleId, Visible = visible };
|
||||||
|
SiteState.Properties.ModuleVisibility = obj;
|
||||||
|
}
|
||||||
|
|
||||||
// logging methods
|
// logging methods
|
||||||
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
|
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
|
||||||
{
|
{
|
||||||
@ -255,15 +310,10 @@ namespace Oqtane.Modules
|
|||||||
{
|
{
|
||||||
int pageId = ModuleState.PageId;
|
int pageId = ModuleState.PageId;
|
||||||
int moduleId = ModuleState.ModuleId;
|
int moduleId = ModuleState.ModuleId;
|
||||||
int? userId = null;
|
|
||||||
if (PageState.User != null)
|
|
||||||
{
|
|
||||||
userId = PageState.User.UserId;
|
|
||||||
}
|
|
||||||
string category = GetType().AssemblyQualifiedName;
|
string category = GetType().AssemblyQualifiedName;
|
||||||
string feature = Utilities.GetTypeNameLastSegment(category, 1);
|
string feature = Utilities.GetTypeNameLastSegment(category, 1);
|
||||||
|
|
||||||
await LoggingService.Log(alias, pageId, moduleId, userId, category, feature, function, level, exception, message, args);
|
await LoggingService.Log(alias, pageId, moduleId, PageState.User?.UserId, category, feature, function, level, exception, message, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Logger
|
public class Logger
|
||||||
@ -365,5 +415,17 @@ namespace Oqtane.Modules
|
|||||||
await _moduleBase.Log(null, LogLevel.Critical, "", exception, message, args);
|
await _moduleBase.Log(null, LogLevel.Critical, "", exception, message, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("ContentUrl(int fileId) is deprecated. Use FileUrl(int fileId) instead.", false)]
|
||||||
|
public string ContentUrl(int fileid)
|
||||||
|
{
|
||||||
|
return ContentUrl(fileid, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("ContentUrl(int fileId, bool asAttachment) is deprecated. Use FileUrl(int fileId, bool download) instead.", false)]
|
||||||
|
public string ContentUrl(int fileid, bool asAttachment)
|
||||||
|
{
|
||||||
|
return Utilities.FileUrl(PageState.Alias, fileid, asAttachment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,15 +5,15 @@
|
|||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RazorLangVersion>3.0</RazorLangVersion>
|
<RazorLangVersion>3.0</RazorLangVersion>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>3.1.1</Version>
|
<Version>3.4.2</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
<Description>Modular Application Framework for Blazor</Description>
|
<Description>Modular Application Framework for Blazor and MAUI</Description>
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.2</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
@ -35,13 +35,8 @@
|
|||||||
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
|
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<TrimmerRootAssembly Include="System.Runtime" />
|
|
||||||
<TrimmerRootAssembly Include="System.Linq.Parallel" />
|
|
||||||
<TrimmerRootAssembly Include="System.Runtime.CompilerServices.VisualC" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<PublishTrimmed>false</PublishTrimmed>
|
||||||
<BlazorEnableCompression>false</BlazorEnableCompression>
|
<BlazorEnableCompression>false</BlazorEnableCompression>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Loader;
|
using System.Runtime.Loader;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
using Microsoft.AspNetCore.Localization;
|
using Microsoft.AspNetCore.Localization;
|
||||||
@ -33,7 +34,7 @@ namespace Oqtane.Client
|
|||||||
|
|
||||||
builder.Services.AddOptions();
|
builder.Services.AddOptions();
|
||||||
|
|
||||||
// Register localization services
|
// register localization services
|
||||||
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||||
|
|
||||||
// register auth services
|
// register auth services
|
||||||
@ -42,7 +43,9 @@ namespace Oqtane.Client
|
|||||||
// register scoped core services
|
// register scoped core services
|
||||||
builder.Services.AddOqtaneScopedServices();
|
builder.Services.AddOqtaneScopedServices();
|
||||||
|
|
||||||
await LoadClientAssemblies(httpClient);
|
var serviceProvider = builder.Services.BuildServiceProvider();
|
||||||
|
|
||||||
|
await LoadClientAssemblies(httpClient, serviceProvider);
|
||||||
|
|
||||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||||
foreach (var assembly in assemblies)
|
foreach (var assembly in assemblies)
|
||||||
@ -58,33 +61,106 @@ namespace Oqtane.Client
|
|||||||
|
|
||||||
await SetCultureFromLocalizationCookie(host.Services);
|
await SetCultureFromLocalizationCookie(host.Services);
|
||||||
|
|
||||||
ServiceActivator.Configure(host.Services);
|
|
||||||
|
|
||||||
await host.RunAsync();
|
await host.RunAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task LoadClientAssemblies(HttpClient http)
|
private static async Task LoadClientAssemblies(HttpClient http, IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
// get list of loaded assemblies on the client
|
var dlls = new Dictionary<string, byte[]>();
|
||||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToList();
|
var pdbs = new Dictionary<string, byte[]>();
|
||||||
|
var list = new List<string>();
|
||||||
|
|
||||||
|
var jsRuntime = serviceProvider.GetRequiredService<IJSRuntime>();
|
||||||
|
var interop = new Interop(jsRuntime);
|
||||||
|
var files = await interop.GetIndexedDBKeys(".dll");
|
||||||
|
|
||||||
|
if (files.Count() != 0)
|
||||||
|
{
|
||||||
|
// get list of assemblies from server
|
||||||
|
var json = await http.GetStringAsync("/api/Installation/list");
|
||||||
|
var assemblies = JsonSerializer.Deserialize<List<string>>(json);
|
||||||
|
|
||||||
|
// determine which assemblies need to be downloaded
|
||||||
|
foreach (var assembly in assemblies)
|
||||||
|
{
|
||||||
|
var file = files.FirstOrDefault(item => item.Contains(assembly));
|
||||||
|
if (file == null)
|
||||||
|
{
|
||||||
|
list.Add(assembly);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// check if newer version available
|
||||||
|
if (GetFileDate(assembly) > GetFileDate(file))
|
||||||
|
{
|
||||||
|
list.Add(assembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get assemblies already downloaded
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
if (assemblies.Contains(file) && !list.Contains(file))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dlls.Add(file, await interop.GetIndexedDBItem<byte[]>(file));
|
||||||
|
var pdb = file.Replace(".dll", ".pdb");
|
||||||
|
if (files.Contains(pdb))
|
||||||
|
{
|
||||||
|
pdbs.Add(pdb, await interop.GetIndexedDBItem<byte[]>(pdb));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // file is deprecated
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await interop.RemoveIndexedDBItem(file);
|
||||||
|
await interop.RemoveIndexedDBItem(file.Replace(".dll", ".pdb"));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list.Add("*");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.Count != 0)
|
||||||
|
{
|
||||||
// get assemblies from server and load into client app domain
|
// get assemblies from server and load into client app domain
|
||||||
var zip = await http.GetByteArrayAsync($"/api/Installation/load");
|
var zip = await http.GetByteArrayAsync($"/api/Installation/load?list=" + string.Join(",", list));
|
||||||
|
|
||||||
// asemblies and debug symbols are packaged in a zip file
|
// asemblies and debug symbols are packaged in a zip file
|
||||||
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
||||||
{
|
{
|
||||||
var dlls = new Dictionary<string, byte[]>();
|
|
||||||
var pdbs = new Dictionary<string, byte[]>();
|
|
||||||
|
|
||||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||||
{
|
|
||||||
if (!assemblies.Contains(Path.GetFileNameWithoutExtension(entry.FullName)))
|
|
||||||
{
|
{
|
||||||
using (var memoryStream = new MemoryStream())
|
using (var memoryStream = new MemoryStream())
|
||||||
{
|
{
|
||||||
entry.Open().CopyTo(memoryStream);
|
entry.Open().CopyTo(memoryStream);
|
||||||
byte[] file = memoryStream.ToArray();
|
byte[] file = memoryStream.ToArray();
|
||||||
|
|
||||||
|
// save assembly to indexeddb
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await interop.SetIndexedDBItem(entry.FullName, file);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
switch (Path.GetExtension(entry.FullName))
|
switch (Path.GetExtension(entry.FullName))
|
||||||
{
|
{
|
||||||
case ".dll":
|
case ".dll":
|
||||||
@ -97,12 +173,14 @@ namespace Oqtane.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// load assemblies into app domain
|
||||||
foreach (var item in dlls)
|
foreach (var item in dlls)
|
||||||
{
|
{
|
||||||
if (pdbs.ContainsKey(item.Key))
|
if (pdbs.ContainsKey(item.Key.Replace(".dll", ".pdb")))
|
||||||
{
|
{
|
||||||
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value), new MemoryStream(pdbs[item.Key]));
|
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value), new MemoryStream(pdbs[item.Key.Replace(".dll", ".pdb")]));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -110,9 +188,17 @@ namespace Oqtane.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static DateTime GetFileDate(string filepath)
|
||||||
|
{
|
||||||
|
var segments = filepath.Split('.');
|
||||||
|
return DateTime.ParseExact(segments[segments.Length - 2], "yyyyMMddHHmmss", CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RegisterModuleServices(Assembly assembly, IServiceCollection services)
|
private static void RegisterModuleServices(Assembly assembly, IServiceCollection services)
|
||||||
|
{
|
||||||
|
// dynamically register module scoped services
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var implementationTypes = assembly.GetInterfaces<IService>();
|
var implementationTypes = assembly.GetInterfaces<IService>();
|
||||||
foreach (var implementationType in implementationTypes)
|
foreach (var implementationType in implementationTypes)
|
||||||
@ -124,8 +210,15 @@ namespace Oqtane.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// could not interrogate assembly - likely missing dependencies
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void RegisterClientStartups(Assembly assembly, IServiceCollection services)
|
private static void RegisterClientStartups(Assembly assembly, IServiceCollection services)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var startUps = assembly.GetInstances<IClientStartup>();
|
var startUps = assembly.GetInstances<IClientStartup>();
|
||||||
foreach (var startup in startUps)
|
foreach (var startup in startUps)
|
||||||
@ -133,6 +226,11 @@ namespace Oqtane.Client
|
|||||||
startup.ConfigureServices(services);
|
startup.ConfigureServices(services);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// could not interrogate assembly - likely missing dependencies
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task SetCultureFromLocalizationCookie(IServiceProvider serviceProvider)
|
private static async Task SetCultureFromLocalizationCookie(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
@ -141,7 +239,7 @@ namespace Oqtane.Client
|
|||||||
var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName);
|
var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName);
|
||||||
var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICultures?[0].Value;
|
var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICultures?[0].Value;
|
||||||
var localizationService = serviceProvider.GetRequiredService<ILocalizationService>();
|
var localizationService = serviceProvider.GetRequiredService<ILocalizationService>();
|
||||||
var cultures = await localizationService.GetCulturesAsync();
|
var cultures = await localizationService.GetCulturesAsync(false);
|
||||||
|
|
||||||
if (culture == null || !cultures.Any(c => c.Name.Equals(culture, StringComparison.OrdinalIgnoreCase)))
|
if (culture == null || !cultures.Any(c => c.Name.Equals(culture, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
|
@ -121,7 +121,7 @@
|
|||||||
<value>Server:</value>
|
<value>Server:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Server.HelpText" xml:space="preserve">
|
<data name="Server.HelpText" xml:space="preserve">
|
||||||
<value>Enter the database server</value>
|
<value>Enter the database server name. This might include a port number as well if you are using a cloud service (ie. servername.database.windows.net,1433) </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Database.Text" xml:space="preserve">
|
<data name="Database.Text" xml:space="preserve">
|
||||||
<value>Database:</value>
|
<value>Database:</value>
|
||||||
@ -133,7 +133,7 @@
|
|||||||
<value>Integrated Security:</value>
|
<value>Integrated Security:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IntegratedSecurity.HelpText" xml:space="preserve">
|
<data name="IntegratedSecurity.HelpText" xml:space="preserve">
|
||||||
<value>Select if you want integrated security or not</value>
|
<value>Select if you are using integrated security</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Uid.Text" xml:space="preserve">
|
<data name="Uid.Text" xml:space="preserve">
|
||||||
<value>User Id:</value>
|
<value>User Id:</value>
|
||||||
@ -163,7 +163,7 @@
|
|||||||
<value>Self Signed</value>
|
<value>Self Signed</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TrustServerCertificate.HelpText" xml:space="preserve">
|
<data name="TrustServerCertificate.HelpText" xml:space="preserve">
|
||||||
<value>Specify the type of certificate you are using for encryption</value>
|
<value>Specify the type of certificate you are using for encryption. Verifiable is equivalent to False. Self Signed is equivalent to True.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TrustServerCertificate.Text" xml:space="preserve">
|
<data name="TrustServerCertificate.Text" xml:space="preserve">
|
||||||
<value>Trust Server Certificate:</value>
|
<value>Trust Server Certificate:</value>
|
||||||
|
@ -136,7 +136,7 @@
|
|||||||
<value>Please Enter All Required Fields. Ensure Passwords Match And Email Address Provided Is Valid.</value>
|
<value>Please Enter All Required Fields. Ensure Passwords Match And Email Address Provided Is Valid.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Password.Invalid" xml:space="preserve">
|
<data name="Message.Password.Invalid" xml:space="preserve">
|
||||||
<value>The Password Provided Does Not Meet The Password Policy. Please Verify The Minimum Password Length And Complexity Requirements.</value>
|
<value>The Password Provided Does Not Meet The Complexity Policy. Passwords Must Be At Least 6 Characters In Length And Contain Uppercase, Lowercase, Numeric, And Punctuation Characters.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Register" xml:space="preserve">
|
<data name="Register" xml:space="preserve">
|
||||||
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
||||||
@ -168,4 +168,16 @@
|
|||||||
<data name="Username.Text" xml:space="preserve">
|
<data name="Username.Text" xml:space="preserve">
|
||||||
<value>Username:</value>
|
<value>Username:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ConnectionString.HelpText" xml:space="preserve">
|
||||||
|
<value>Enter a complete connection string including all parameters and delimiters</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConnectionString.Text" xml:space="preserve">
|
||||||
|
<value>Settings:</value>
|
||||||
|
</data>
|
||||||
|
<data name="EnterConnectionParameters" xml:space="preserve">
|
||||||
|
<value>Enter Connection Parameters</value>
|
||||||
|
</data>
|
||||||
|
<data name="EnterConnectionString" xml:space="preserve">
|
||||||
|
<value>Enter Connection String</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
126
Oqtane.Client/Resources/Modules/Admin/Api/Edit.resx
Normal file
126
Oqtane.Client/Resources/Modules/Admin/Api/Edit.resx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="EntityName.HelpText" xml:space="preserve">
|
||||||
|
<value>The Name Of The Entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="EntityName.Text" xml:space="preserve">
|
||||||
|
<value>Entity:</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
126
Oqtane.Client/Resources/Modules/Admin/Api/Index.resx
Normal file
126
Oqtane.Client/Resources/Modules/Admin/Api/Index.resx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="Entity" xml:space="preserve">
|
||||||
|
<value>Entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="Permissions" xml:space="preserve">
|
||||||
|
<value>Permissions</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -118,6 +118,6 @@
|
|||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="Error.Module.Load" xml:space="preserve">
|
<data name="Error.Module.Load" xml:space="preserve">
|
||||||
<value>A Problem Was Encountered Loading Module {0}</value>
|
<value>A Problem Was Encountered Loading Module {0}. The Module Is Either Invalid Or Does Not Exist.</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -192,4 +192,7 @@
|
|||||||
<data name="Once" xml:space="preserve">
|
<data name="Once" xml:space="preserve">
|
||||||
<value>Execute Once</value>
|
<value>Execute Once</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.NoJobs" xml:space="preserve">
|
||||||
|
<value>Please Note That After An Initial Installation You Must <a href={0}>Restart</a> The Application In Order To Activate The Default Scheduled Jobs.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -117,9 +117,6 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="The Only Supported Culture That Has Been Defined Is English" xml:space="preserve">
|
|
||||||
<value>The Only Supported Culture That Has Been Defined Is English</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error.Language.Add" xml:space="preserve">
|
<data name="Error.Language.Add" xml:space="preserve">
|
||||||
<value>Error Adding Language</value>
|
<value>Error Adding Language</value>
|
||||||
</data>
|
</data>
|
||||||
@ -135,32 +132,14 @@
|
|||||||
<data name="IsDefault.Text" xml:space="preserve">
|
<data name="IsDefault.Text" xml:space="preserve">
|
||||||
<value>Default?</value>
|
<value>Default?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AllLanguages" xml:space="preserve">
|
|
||||||
<value>All The Installed Languages Have Been Added.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error.Language.Download" xml:space="preserve">
|
|
||||||
<value>Error Downloading Translation</value>
|
|
||||||
</data>
|
|
||||||
<data name="OnlyEnglish" xml:space="preserve">
|
|
||||||
<value>The Only Installed Language Is English</value>
|
|
||||||
</data>
|
|
||||||
<data name="Success.Language.Download" xml:space="preserve">
|
|
||||||
<value>Translation Downloaded Successfully. Click Install To Complete Installation.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Success.Language.Install" xml:space="preserve">
|
<data name="Success.Language.Install" xml:space="preserve">
|
||||||
<value>Translations Installed Successfully. You Must <a href={0}>Restart</a> Your Application To Apply These Changes.</value>
|
<value>Translations Installed Successfully. You Must <a href={0}>Restart</a> Your Application To Apply These Changes.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Search.NoResults" xml:space="preserve">
|
|
||||||
<value>No Translations Match The Criteria Provided Or Package Service Is Disabled</value>
|
|
||||||
</data>
|
|
||||||
<data name="Download.Heading" xml:space="preserve">
|
|
||||||
<value>Download</value>
|
|
||||||
</data>
|
|
||||||
<data name="LanguageUpload.HelpText" xml:space="preserve">
|
<data name="LanguageUpload.HelpText" xml:space="preserve">
|
||||||
<value>Upload one or more translations. Once they are uploaded click Install to complete the installation.</value>
|
<value>Upload one or more translation packages. Once they are uploaded click Install to complete the installation.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LanguageUpload.Text" xml:space="preserve">
|
<data name="LanguageUpload.Text" xml:space="preserve">
|
||||||
<value>Upload Language</value>
|
<value>Translation</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Manage.Heading" xml:space="preserve">
|
<data name="Manage.Heading" xml:space="preserve">
|
||||||
<value>Manage</value>
|
<value>Manage</value>
|
||||||
|
@ -132,6 +132,9 @@
|
|||||||
<data name="DeleteLanguage.Header" xml:space="preserve">
|
<data name="DeleteLanguage.Header" xml:space="preserve">
|
||||||
<value>Delete Language</value>
|
<value>Delete Language</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Success.Language.Download" xml:space="preserve">
|
||||||
|
<value>Translation Downloaded Successfully. Click Install To Complete Installation.</value>
|
||||||
|
</data>
|
||||||
<data name="Error.Language.Download" xml:space="preserve">
|
<data name="Error.Language.Download" xml:space="preserve">
|
||||||
<value>Error Downloading Translation</value>
|
<value>Error Downloading Translation</value>
|
||||||
</data>
|
</data>
|
||||||
@ -144,4 +147,7 @@
|
|||||||
<data name="DeleteLanguage.Text" xml:space="preserve">
|
<data name="DeleteLanguage.Text" xml:space="preserve">
|
||||||
<value>Delete</value>
|
<value>Delete</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Translation" xml:space="preserve">
|
||||||
|
<value>Translation</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -195,4 +195,28 @@
|
|||||||
<data name="Information.Text" xml:space="preserve">
|
<data name="Information.Text" xml:space="preserve">
|
||||||
<value>Information</value>
|
<value>Information</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PackageName.HelpText" xml:space="preserve">
|
||||||
|
<value>The unique name of the package from which this module was installed</value>
|
||||||
|
</data>
|
||||||
|
<data name="PackageName.Text" xml:space="preserve">
|
||||||
|
<value>Package Name:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.Translation.Download" xml:space="preserve">
|
||||||
|
<value>Error Downloading Translation</value>
|
||||||
|
</data>
|
||||||
|
<data name="Search.PackageNameMissing" xml:space="preserve">
|
||||||
|
<value>A Package Name Was Not Provided For The Module</value>
|
||||||
|
</data>
|
||||||
|
<data name="Search.NoResults" xml:space="preserve">
|
||||||
|
<value>No Translations Exist For This Module Or Package Service Is Disabled</value>
|
||||||
|
</data>
|
||||||
|
<data name="Success.Translation.Download" xml:space="preserve">
|
||||||
|
<value>Translation Downloaded Successfully. Click Install To Complete Installation.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Success.Translation.Install" xml:space="preserve">
|
||||||
|
<value>Translation Installed Successfully. You Must <a href={0}>Restart</a> Your Application To Apply These Changes.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Translations.Heading" xml:space="preserve">
|
||||||
|
<value>Translations</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -150,4 +150,7 @@
|
|||||||
<data name="EditModule.Text" xml:space="preserve">
|
<data name="EditModule.Text" xml:space="preserve">
|
||||||
<value>Edit</value>
|
<value>Edit</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Modules" xml:space="preserve">
|
||||||
|
<value>Modules</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -147,4 +147,7 @@
|
|||||||
<data name="Message.Required.Title" xml:space="preserve">
|
<data name="Message.Required.Title" xml:space="preserve">
|
||||||
<value>You Must Provide A Title For The Module</value>
|
<value>You Must Provide A Title For The Module</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Error.Module.Load" xml:space="preserve">
|
||||||
|
<value>A Problem Was Encountered Loading Module {0}. The Module Is Either Invalid Or Does Not Exist.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -160,7 +160,7 @@
|
|||||||
<value>Error Loading Pane Layouts For Theme</value>
|
<value>Error Loading Pane Layouts For Theme</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Page.Exists" xml:space="preserve">
|
<data name="Message.Page.Exists" xml:space="preserve">
|
||||||
<value>A page with path {0} already exists for the selected parent page. The page path needs to be unique for the selected parent.</value>
|
<value>A page with path '{0}' already exists for this site. Page paths must be unique. You may need to check if a page with this path exists in the Recycle Bin.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Required.PageInfo" xml:space="preserve">
|
<data name="Message.Required.PageInfo" xml:space="preserve">
|
||||||
<value>You Must Provide Page Name, Theme, and Container</value>
|
<value>You Must Provide Page Name, Theme, and Container</value>
|
||||||
@ -228,13 +228,13 @@
|
|||||||
<data name="Appearance.Name" xml:space="preserve">
|
<data name="Appearance.Name" xml:space="preserve">
|
||||||
<value>Appearance</value>
|
<value>Appearance</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Page.Deleted" xml:space="preserve">
|
|
||||||
<value>A page with path {0} already exists for the selected parent page in the Recycle Bin. Either recover the page or remove from the Recycle Bin and create it again.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Meta.HelpText" xml:space="preserve">
|
<data name="Meta.HelpText" xml:space="preserve">
|
||||||
<value>Optionally enter meta tags (in exactly the form you want them to be included in the page output).</value>
|
<value>Optionally enter meta tags (in exactly the form you want them to be included in the page output).</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Meta.Text" xml:space="preserve">
|
<data name="Meta.Text" xml:space="preserve">
|
||||||
<value>Meta:</value>
|
<value>Meta:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.Page.Reserved" xml:space="preserve">
|
||||||
|
<value>The page name {0} is reserved. Please enter a different name for your page.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -151,7 +151,7 @@
|
|||||||
<value>Error Loading Pane Layouts For Theme</value>
|
<value>Error Loading Pane Layouts For Theme</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Mesage.Page.PathExists" xml:space="preserve">
|
<data name="Mesage.Page.PathExists" xml:space="preserve">
|
||||||
<value>A page with path {0} already exists for the selected parent page. The page path needs to be unique for the selected parent.</value>
|
<value>A page with path '{0}' already exists for this site. Page paths must be unique. You may need to check if a page with this path exists in the Recycle Bin.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Required.PageInfo" xml:space="preserve">
|
<data name="Message.Required.PageInfo" xml:space="preserve">
|
||||||
<value>You Must Provide Page Name, Theme, and Container</value>
|
<value>You Must Provide Page Name, Theme, and Container</value>
|
||||||
@ -270,4 +270,7 @@
|
|||||||
<data name="Meta.Text" xml:space="preserve">
|
<data name="Meta.Text" xml:space="preserve">
|
||||||
<value>Meta:</value>
|
<value>Meta:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.Page.Reserved" xml:space="preserve">
|
||||||
|
<value>The page name {0} is reserved. Please enter a different name for your page.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -166,16 +166,22 @@
|
|||||||
<value>Error Permanently Deleting Modules</value>
|
<value>Error Permanently Deleting Modules</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteAllPages.Header" xml:space="preserve">
|
<data name="DeleteAllPages.Header" xml:space="preserve">
|
||||||
<value>Delete All Pages</value>
|
<value>Remove All Deleted Pages</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteAllPages.Message" xml:space="preserve">
|
<data name="DeleteAllPages.Message" xml:space="preserve">
|
||||||
<value>Are You Sure You Wish To Permanently Delete All Pages?</value>
|
<value>Are You Sure You Wish To Permanently Remove All Deleted Pages?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAllPages.Text" xml:space="preserve">
|
||||||
|
<value>Remove All Deleted Pages</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteAllModules.Header" xml:space="preserve">
|
<data name="DeleteAllModules.Header" xml:space="preserve">
|
||||||
<value>Delete All Modules</value>
|
<value>Remove All Deleted Modules</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteAllModules.Message" xml:space="preserve">
|
<data name="DeleteAllModules.Message" xml:space="preserve">
|
||||||
<value>Are You Sure You Wish To Permanently Delete All Modules?</value>
|
<value>Are You Sure You Wish To Permanently Remove All Deleted Modules?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAllModules.Text" xml:space="preserve">
|
||||||
|
<value>Remove All Deleted Modules</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Pages.Heading" xml:space="preserve">
|
<data name="Pages.Heading" xml:space="preserve">
|
||||||
<value>Pages</value>
|
<value>Pages</value>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -166,7 +166,7 @@
|
|||||||
<value>Email:</value>
|
<value>Email:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Password.HelpText" xml:space="preserve">
|
<data name="Password.HelpText" xml:space="preserve">
|
||||||
<value>Please choose a sufficiently secure password and enter it here</value>
|
<value>Please enter a sufficiently secure password which meets the password complexity requirements</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Password.Text" xml:space="preserve">
|
<data name="Password.Text" xml:space="preserve">
|
||||||
<value>Password:</value>
|
<value>Password:</value>
|
||||||
@ -177,4 +177,19 @@
|
|||||||
<data name="Username.Text" xml:space="preserve">
|
<data name="Username.Text" xml:space="preserve">
|
||||||
<value>Username:</value>
|
<value>Username:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Password.ValidationCriteria" xml:space="preserve">
|
||||||
|
<value>Passwords Must Have A Minimum Length Of {0} Characters, Including At Least {1} Unique Character(s), {2}{3}{4}{5} To Satisfy Password Compexity Requirements For This Site.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password.DigitRequirement" xml:space="preserve">
|
||||||
|
<value>At Least One Digit</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password.LowercaseRequirement" xml:space="preserve">
|
||||||
|
<value>At Least One Lowercase Letter</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password.PunctuationRequirement" xml:space="preserve">
|
||||||
|
<value>At Least One Punctuation Mark</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password.UppercaseRequirement" xml:space="preserve">
|
||||||
|
<value>At Least One Uppercase Letter</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -121,7 +121,7 @@
|
|||||||
<value>User: </value>
|
<value>User: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="User.Select" xml:space="preserve">
|
<data name="User.Select" xml:space="preserve">
|
||||||
<value>Select User</value>
|
<value>Enter User's Name</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Users" xml:space="preserve">
|
<data name="Users" xml:space="preserve">
|
||||||
<value>Users</value>
|
<value>Users</value>
|
||||||
@ -129,9 +129,6 @@
|
|||||||
<data name="Error.User.Load" xml:space="preserve">
|
<data name="Error.User.Load" xml:space="preserve">
|
||||||
<value>Error Loading Users</value>
|
<value>Error Loading Users</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.User.LoadRole" xml:space="preserve">
|
|
||||||
<value>Error Loading User Roles</value>
|
|
||||||
</data>
|
|
||||||
<data name="Success.User.AssignedRole" xml:space="preserve">
|
<data name="Success.User.AssignedRole" xml:space="preserve">
|
||||||
<value>User Assigned To Role</value>
|
<value>User Assigned To Role</value>
|
||||||
</data>
|
</data>
|
||||||
@ -151,7 +148,7 @@
|
|||||||
<value>The role you are assigning users to</value>
|
<value>The role you are assigning users to</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="User.HelpText" xml:space="preserve">
|
<data name="User.HelpText" xml:space="preserve">
|
||||||
<value>Select a user</value>
|
<value>Enter the name of a user</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EffectiveDate.HelpText" xml:space="preserve">
|
<data name="EffectiveDate.HelpText" xml:space="preserve">
|
||||||
<value>The date that this role assignment is active</value>
|
<value>The date that this role assignment is active</value>
|
||||||
|
@ -142,7 +142,7 @@
|
|||||||
<value>Site Settings Saved</value>
|
<value>Site Settings Saved</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Aliases.Taken" xml:space="preserve">
|
<data name="Message.Aliases.Taken" xml:space="preserve">
|
||||||
<value>The Default Alias Has Not Been Specified Or An Alias Was Specified That Has Already Been Used For Another Site</value>
|
<value>An Alias Was Specified That Has Already Been Used For Another Site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Required.SiteName" xml:space="preserve">
|
<data name="Message.Required.SiteName" xml:space="preserve">
|
||||||
<value>You Must Provide A Site Name, Alias, And Default Theme/Container</value>
|
<value>You Must Provide A Site Name, Alias, And Default Theme/Container</value>
|
||||||
@ -163,10 +163,10 @@
|
|||||||
<value>Enter the site name</value>
|
<value>Enter the site name</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Tenant.HelpText" xml:space="preserve">
|
<data name="Tenant.HelpText" xml:space="preserve">
|
||||||
<value>Enter the tenant for the site</value>
|
<value>The name of the database used for the site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Aliases.HelpText" xml:space="preserve">
|
<data name="Aliases.HelpText" xml:space="preserve">
|
||||||
<value>The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they should be separated by commas.</value>
|
<value>The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder).</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IsDeleted.HelpText" xml:space="preserve">
|
<data name="IsDeleted.HelpText" xml:space="preserve">
|
||||||
<value>Is this site deleted?</value>
|
<value>Is this site deleted?</value>
|
||||||
@ -195,13 +195,13 @@
|
|||||||
<data name="UseSsl.HelpText" xml:space="preserve">
|
<data name="UseSsl.HelpText" xml:space="preserve">
|
||||||
<value>Specify if SSL is required for your SMTP server</value>
|
<value>Specify if SSL is required for your SMTP server</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SmptUsername.HelpText" xml:space="preserve">
|
<data name="SmtpUsername.HelpText" xml:space="preserve">
|
||||||
<value>Enter the username for your SMTP account</value>
|
<value>Enter the username for your SMTP account</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SmtpPassword.HelpText" xml:space="preserve">
|
<data name="SmtpPassword.HelpText" xml:space="preserve">
|
||||||
<value>Enter the password for your SMTP account</value>
|
<value>Enter the password for your SMTP account</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SmptSender.HelpText" xml:space="preserve">
|
<data name="SmtpSender.HelpText" xml:space="preserve">
|
||||||
<value>Enter the email which emails will be sent from. Please note that this email address may need to be authorized with the SMTP server.</value>
|
<value>Enter the email which emails will be sent from. Please note that this email address may need to be authorized with the SMTP server.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EnablePWA.HelpText" xml:space="preserve">
|
<data name="EnablePWA.HelpText" xml:space="preserve">
|
||||||
@ -214,7 +214,7 @@
|
|||||||
<value>Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension.</value>
|
<value>Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Tenant.Text" xml:space="preserve">
|
<data name="Tenant.Text" xml:space="preserve">
|
||||||
<value>Tenant: </value>
|
<value>Database: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Aliases.Text" xml:space="preserve">
|
<data name="Aliases.Text" xml:space="preserve">
|
||||||
<value>Aliases: </value>
|
<value>Aliases: </value>
|
||||||
@ -243,13 +243,13 @@
|
|||||||
<data name="UseSsl.Text" xml:space="preserve">
|
<data name="UseSsl.Text" xml:space="preserve">
|
||||||
<value>SSL Enabled: </value>
|
<value>SSL Enabled: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SmptUsername.Text" xml:space="preserve">
|
<data name="SmtpUsername.Text" xml:space="preserve">
|
||||||
<value>Username: </value>
|
<value>Username: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SmtpPassword.Text" xml:space="preserve">
|
<data name="SmtpPassword.Text" xml:space="preserve">
|
||||||
<value>Password: </value>
|
<value>Password: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SmptSender.Text" xml:space="preserve">
|
<data name="SmtpSender.Text" xml:space="preserve">
|
||||||
<value>Email Sender: </value>
|
<value>Email Sender: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EnablePWA.Text" xml:space="preserve">
|
<data name="EnablePWA.Text" xml:space="preserve">
|
||||||
@ -292,7 +292,7 @@
|
|||||||
<value>Browse</value>
|
<value>Browse</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TenantInformation.Heading" xml:space="preserve">
|
<data name="TenantInformation.Heading" xml:space="preserve">
|
||||||
<value>Tenant Information</value>
|
<value>Database</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PWASettings.Heading" xml:space="preserve">
|
<data name="PWASettings.Heading" xml:space="preserve">
|
||||||
<value>PWA Settings</value>
|
<value>PWA Settings</value>
|
||||||
@ -304,13 +304,13 @@
|
|||||||
<value>Connection:</value>
|
<value>Connection:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Database.Text" xml:space="preserve">
|
<data name="Database.Text" xml:space="preserve">
|
||||||
<value>Database:</value>
|
<value>Type:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ConnectionString.HelpText" xml:space="preserve">
|
<data name="ConnectionString.HelpText" xml:space="preserve">
|
||||||
<value>The connection information for the database</value>
|
<value>The connection information for the database</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Database.HelpText" xml:space="preserve">
|
<data name="Database.HelpText" xml:space="preserve">
|
||||||
<value>The database for the tenant</value>
|
<value>The type of database</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteSite.Text" xml:space="preserve">
|
<data name="DeleteSite.Text" xml:space="preserve">
|
||||||
<value>Delete Site</value>
|
<value>Delete Site</value>
|
||||||
@ -324,4 +324,31 @@
|
|||||||
<data name="Aliases.Heading" xml:space="preserve">
|
<data name="Aliases.Heading" xml:space="preserve">
|
||||||
<value>Aliases</value>
|
<value>Aliases</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AliasName" xml:space="preserve">
|
||||||
|
<value>Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="AliasDefault" xml:space="preserve">
|
||||||
|
<value>Default?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirm.Alias.Delete" xml:space="preserve">
|
||||||
|
<value>Are You Sure You Wish To Delete {0}?</value>
|
||||||
|
</data>
|
||||||
|
<data name="HomePage.HelpText" xml:space="preserve">
|
||||||
|
<value>Select the home page for the site (to be used if there is no page with a path of '/')</value>
|
||||||
|
</data>
|
||||||
|
<data name="HomePage.Text" xml:space="preserve">
|
||||||
|
<value>Home Page:</value>
|
||||||
|
</data>
|
||||||
|
<data name="SmtpRelay.HelpText" xml:space="preserve">
|
||||||
|
<value>Only specify this option if you have properly configured an SMTP Relay Service to route your outgoing mail. This option will send notifications from the user's email rather than from the Email Sender specified above.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SmtpRelay.Text" xml:space="preserve">
|
||||||
|
<value>Relay Configured?</value>
|
||||||
|
</data>
|
||||||
|
<data name="SiteMap.HelpText" xml:space="preserve">
|
||||||
|
<value>The site map url for this site which can be submitted to search engines for indexing</value>
|
||||||
|
</data>
|
||||||
|
<data name="SiteMap.Text" xml:space="preserve">
|
||||||
|
<value>Site Map:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -123,9 +123,6 @@
|
|||||||
<data name="SqlServer" xml:space="preserve">
|
<data name="SqlServer" xml:space="preserve">
|
||||||
<value>SQL Server</value>
|
<value>SQL Server</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Server.Text" xml:space="preserve">
|
|
||||||
<value>Server: </value>
|
|
||||||
</data>
|
|
||||||
<data name="Container.Select" xml:space="preserve">
|
<data name="Container.Select" xml:space="preserve">
|
||||||
<value>Select Container</value>
|
<value>Select Container</value>
|
||||||
</data>
|
</data>
|
||||||
@ -145,7 +142,7 @@
|
|||||||
<value>Select the default container for the site</value>
|
<value>Select the default container for the site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Tenant.Text" xml:space="preserve">
|
<data name="Tenant.Text" xml:space="preserve">
|
||||||
<value>Tenant: </value>
|
<value>Database: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Aliases.Text" xml:space="preserve">
|
<data name="Aliases.Text" xml:space="preserve">
|
||||||
<value>Aliases: </value>
|
<value>Aliases: </value>
|
||||||
@ -157,10 +154,10 @@
|
|||||||
<value>Select Site Template</value>
|
<value>Select Site Template</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Tenant.Select" xml:space="preserve">
|
<data name="Tenant.Select" xml:space="preserve">
|
||||||
<value>Select Tenant</value>
|
<value>Select Database</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Tenant.Add" xml:space="preserve">
|
<data name="Tenant.Add" xml:space="preserve">
|
||||||
<value>Create New Tenant</value>
|
<value>Create Database</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.Theme.LoadContainers" xml:space="preserve">
|
<data name="Error.Theme.LoadContainers" xml:space="preserve">
|
||||||
<value>Error Loading Containers For Theme</value>
|
<value>Error Loading Containers For Theme</value>
|
||||||
@ -172,19 +169,19 @@
|
|||||||
<value>Invalid Host Password</value>
|
<value>Invalid Host Password</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.TenantName.Exists" xml:space="preserve">
|
<data name="Error.TenantName.Exists" xml:space="preserve">
|
||||||
<value>Tenant Name Is Missing Or Already Exists</value>
|
<value>Database Name Is Missing Or Already Exists</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.SiteName.InUse" xml:space="preserve">
|
<data name="Message.SiteName.InUse" xml:space="preserve">
|
||||||
<value>{0} Already Used For Another Site</value>
|
<value>{0} Already Used For Another Site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Required.Tenant" xml:space="preserve">
|
<data name="Message.Required.Tenant" xml:space="preserve">
|
||||||
<value>You Must Provide A Tenant, Site Name, Alias, Default Theme/Container, And Site Template</value>
|
<value>You Must Provide A Database, Site Name, Alias, Default Theme/Container, And Site Template</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Name.HelpText" xml:space="preserve">
|
<data name="Name.HelpText" xml:space="preserve">
|
||||||
<value>Enter the name of the site</value>
|
<value>Enter the name of the site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DefaultTheme.HelpText" xml:space="preserve">
|
<data name="DefaultTheme.HelpText" xml:space="preserve">
|
||||||
<value>Select the default theme for the website</value>
|
<value>Select the default theme for the site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AdminContainer.HelpText" xml:space="preserve">
|
<data name="AdminContainer.HelpText" xml:space="preserve">
|
||||||
<value>Select the admin container for the site</value>
|
<value>Select the admin container for the site</value>
|
||||||
@ -193,28 +190,13 @@
|
|||||||
<value>Select the site template</value>
|
<value>Select the site template</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Tenant.HelpText" xml:space="preserve">
|
<data name="Tenant.HelpText" xml:space="preserve">
|
||||||
<value>Select the tenant for the site</value>
|
<value>Select the database for the site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TenantName.HelpText" xml:space="preserve">
|
<data name="TenantName.HelpText" xml:space="preserve">
|
||||||
<value>Enter the name for the tenant</value>
|
<value>Enter the name for the database</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DatabaseType.HelpText" xml:space="preserve">
|
<data name="DatabaseType.HelpText" xml:space="preserve">
|
||||||
<value>Select the database type for the tenant</value>
|
<value>Select the database type</value>
|
||||||
</data>
|
|
||||||
<data name="DatabaseServer.HelpText" xml:space="preserve">
|
|
||||||
<value>Enter the server for the tenant</value>
|
|
||||||
</data>
|
|
||||||
<data name="Database.HelpText" xml:space="preserve">
|
|
||||||
<value>Enter the database for the tenant</value>
|
|
||||||
</data>
|
|
||||||
<data name="IntegratedSecurity.HelpText" xml:space="preserve">
|
|
||||||
<value>Select if you want integrated security or not</value>
|
|
||||||
</data>
|
|
||||||
<data name="DatabaseUsername.HelpText" xml:space="preserve">
|
|
||||||
<value>Enter the username for the integrated security</value>
|
|
||||||
</data>
|
|
||||||
<data name="DatabasePassword.HelpText" xml:space="preserve">
|
|
||||||
<value>Enter the password for the integrated security</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="HostUsername.HelpText" xml:space="preserve">
|
<data name="HostUsername.HelpText" xml:space="preserve">
|
||||||
<value>Enter the username of an existing host user</value>
|
<value>Enter the username of an existing host user</value>
|
||||||
@ -232,23 +214,14 @@
|
|||||||
<value>Site Template: </value>
|
<value>Site Template: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TenantName.Text" xml:space="preserve">
|
<data name="TenantName.Text" xml:space="preserve">
|
||||||
<value>Tenant Name: </value>
|
<value>Name: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DatabaseType.Text" xml:space="preserve">
|
<data name="DatabaseType.Text" xml:space="preserve">
|
||||||
<value>Database Type: </value>
|
<value>Type: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Database.Text" xml:space="preserve">
|
<data name="Database.Text" xml:space="preserve">
|
||||||
<value>Database: </value>
|
<value>Database: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IntegratedSecurity.Text" xml:space="preserve">
|
|
||||||
<value>Integrated Security: </value>
|
|
||||||
</data>
|
|
||||||
<data name="DatabaseUsername.Text" xml:space="preserve">
|
|
||||||
<value>Database Username: </value>
|
|
||||||
</data>
|
|
||||||
<data name="DatabasePassword.Text" xml:space="preserve">
|
|
||||||
<value>Database Password: </value>
|
|
||||||
</data>
|
|
||||||
<data name="HostUsername.Text" xml:space="preserve">
|
<data name="HostUsername.Text" xml:space="preserve">
|
||||||
<value>Host Username:</value>
|
<value>Host Username:</value>
|
||||||
</data>
|
</data>
|
||||||
@ -270,4 +243,16 @@
|
|||||||
<data name="Runtime.Text" xml:space="preserve">
|
<data name="Runtime.Text" xml:space="preserve">
|
||||||
<value>Runtime: </value>
|
<value>Runtime: </value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ConnectionString.HelpText" xml:space="preserve">
|
||||||
|
<value>Enter a complete connection string including all parameters and delimiters</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConnectionString.Text" xml:space="preserve">
|
||||||
|
<value>Settings:</value>
|
||||||
|
</data>
|
||||||
|
<data name="EnterConnectionParameters" xml:space="preserve">
|
||||||
|
<value>Enter Connection Parameters</value>
|
||||||
|
</data>
|
||||||
|
<data name="EnterConnectionString" xml:space="preserve">
|
||||||
|
<value>Enter Connection String</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -117,30 +117,75 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="Tenant.Text" xml:space="preserve">
|
<data name="Connection.Text" xml:space="preserve">
|
||||||
<value>Tenant: </value>
|
<value>Connection: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Tenant.Select" xml:space="preserve">
|
<data name="Connection.HelpText" xml:space="preserve">
|
||||||
<value>Select Tenant</value>
|
<value>Select a database connection (from appsettings.json)</value>
|
||||||
|
</data>
|
||||||
|
<data name="Connection.Select" xml:space="preserve">
|
||||||
|
<value>Select Connection</value>
|
||||||
|
</data>
|
||||||
|
<data name="Connection.Add" xml:space="preserve">
|
||||||
|
<value>Add Connection</value>
|
||||||
|
</data>
|
||||||
|
<data name="Name.Text" xml:space="preserve">
|
||||||
|
<value>Name: </value>
|
||||||
|
</data>
|
||||||
|
<data name="Name.HelpText" xml:space="preserve">
|
||||||
|
<value>Enter the name of the connection</value>
|
||||||
|
</data>
|
||||||
|
<data name="DatabaseType.Text" xml:space="preserve">
|
||||||
|
<value>Type: </value>
|
||||||
|
</data>
|
||||||
|
<data name="DatabaseType.HelpText" xml:space="preserve">
|
||||||
|
<value>Select the database type</value>
|
||||||
|
</data>
|
||||||
|
<data name="Type.Select" xml:space="preserve">
|
||||||
|
<value>Select Type</value>
|
||||||
|
</data>
|
||||||
|
<data name="EnterConnectionParameters" xml:space="preserve">
|
||||||
|
<value>Enter Connection Parameters</value>
|
||||||
|
</data>
|
||||||
|
<data name="EnterConnectionString" xml:space="preserve">
|
||||||
|
<value>Enter Connection String</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConnectionString.Text" xml:space="preserve">
|
||||||
|
<value>Settings: </value>
|
||||||
|
</data>
|
||||||
|
<data name="ConnectionString.HelpText" xml:space="preserve">
|
||||||
|
<value>A complete connection string including all parameters and delimiters</value>
|
||||||
|
</data>
|
||||||
|
<data name="Add" xml:space="preserve">
|
||||||
|
<value>Add</value>
|
||||||
|
</data>
|
||||||
|
<data name="Tenant.Text" xml:space="preserve">
|
||||||
|
<value>Database: </value>
|
||||||
|
</data>
|
||||||
|
<data name="Tenant.HelpText" xml:space="preserve">
|
||||||
|
<value>The database using this connection</value>
|
||||||
|
</data>
|
||||||
|
<data name="SqlQuery.Text" xml:space="preserve">
|
||||||
|
<value>SQL Query: </value>
|
||||||
|
</data>
|
||||||
|
<data name="SqlQuery.HelpText" xml:space="preserve">
|
||||||
|
<value>Enter a valid SQL query for the database</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Execute" xml:space="preserve">
|
<data name="Execute" xml:space="preserve">
|
||||||
<value>Execute</value>
|
<value>Execute</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Required.Tenant" xml:space="preserve">
|
<data name="Message.Required.Tenant" xml:space="preserve">
|
||||||
<value>You Must Select A Tenant And Provide A Valid SQL Query</value>
|
<value>You Must Select A Database Type And Provide A Valid SQL Query</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Required.Connection" xml:space="preserve">
|
||||||
|
<value>You Must Provide A Connection Name And Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Connection.Added" xml:space="preserve">
|
||||||
|
<value>Connection Added Successfully</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Return.NoResult" xml:space="preserve">
|
<data name="Return.NoResult" xml:space="preserve">
|
||||||
<value>No Results Returned</value>
|
<value>No Results Returned</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Tenant.HelpText" xml:space="preserve">
|
|
||||||
<value>Select the tenant for the SQL server</value>
|
|
||||||
</data>
|
|
||||||
<data name="SqlQuery.HelpText" xml:space="preserve">
|
|
||||||
<value>Enter the query for the SQL server</value>
|
|
||||||
</data>
|
|
||||||
<data name="SqlQuery.Text" xml:space="preserve">
|
|
||||||
<value>SQL Query: </value>
|
|
||||||
</data>
|
|
||||||
<data name="Success.QueryExecuted" xml:space="preserve">
|
<data name="Success.QueryExecuted" xml:space="preserve">
|
||||||
<value>SQL Query Executed</value>
|
<value>SQL Query Executed</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -209,6 +209,9 @@
|
|||||||
</data>
|
</data>
|
||||||
<data name="Options.Heading" xml:space="preserve">
|
<data name="Options.Heading" xml:space="preserve">
|
||||||
<value>Options</value>
|
<value>Options</value>
|
||||||
|
</data>
|
||||||
|
<data name="Log.Heading" xml:space="preserve">
|
||||||
|
<value>Log</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Register" xml:space="preserve">
|
<data name="Register" xml:space="preserve">
|
||||||
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
||||||
@ -270,4 +273,25 @@
|
|||||||
<data name="WorkingSet.Text" xml:space="preserve">
|
<data name="WorkingSet.Text" xml:space="preserve">
|
||||||
<value>Memory Allocation:</value>
|
<value>Memory Allocation:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Environment.HelpText" xml:space="preserve">
|
||||||
|
<value>Environment Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="Environment.Text" xml:space="preserve">
|
||||||
|
<value>Environment:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Log.Text" xml:space="preserve">
|
||||||
|
<value>Log:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Log.HelpText" xml:space="preserve">
|
||||||
|
<value>System log information for current day</value>
|
||||||
|
</data>
|
||||||
|
<data name="Clear" xml:space="preserve">
|
||||||
|
<value>Clear</value>
|
||||||
|
</data>
|
||||||
|
<data name="Success.ClearLog" xml:space="preserve">
|
||||||
|
<value>System Log Has Been Successfully Cleared</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.ClearLog" xml:space="preserve">
|
||||||
|
<value>Ann Error Occurred Clearing The System Log</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -162,4 +162,10 @@
|
|||||||
<data name="License.HelpText" xml:space="preserve">
|
<data name="License.HelpText" xml:space="preserve">
|
||||||
<value>The license of the theme</value>
|
<value>The license of the theme</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PackageName.HelpText" xml:space="preserve">
|
||||||
|
<value>The unique name of the package from which this module was installed</value>
|
||||||
|
</data>
|
||||||
|
<data name="PackageName.Text" xml:space="preserve">
|
||||||
|
<value>Package Name:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -120,6 +120,9 @@
|
|||||||
<data name="Message.Password.Invalid" xml:space="preserve">
|
<data name="Message.Password.Invalid" xml:space="preserve">
|
||||||
<value>Passwords Entered Do Not Match</value>
|
<value>Passwords Entered Do Not Match</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.Password.Complexity" xml:space="preserve">
|
||||||
|
<value>Password Provided Does Not Meet The Complexity Policy</value>
|
||||||
|
</data>
|
||||||
<data name="From" xml:space="preserve">
|
<data name="From" xml:space="preserve">
|
||||||
<value>From</value>
|
<value>From</value>
|
||||||
</data>
|
</data>
|
||||||
@ -219,4 +222,10 @@
|
|||||||
<data name="DeleteAllNotifications.Text" xml:space="preserve">
|
<data name="DeleteAllNotifications.Text" xml:space="preserve">
|
||||||
<value>Delete ALL Notifications</value>
|
<value>Delete ALL Notifications</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Notifications.Heading" xml:space="preserve">
|
||||||
|
<value>Notifications</value>
|
||||||
|
</data>
|
||||||
|
<data name="Profile.Heading" xml:space="preserve">
|
||||||
|
<value>Profile</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -120,6 +120,9 @@
|
|||||||
<data name="Message.Password.NoMatch" xml:space="preserve">
|
<data name="Message.Password.NoMatch" xml:space="preserve">
|
||||||
<value>Passwords Entered Do Not Match</value>
|
<value>Passwords Entered Do Not Match</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.Password.Complexity" xml:space="preserve">
|
||||||
|
<value>Password Provided Does Not Meet The Complexity Policy</value>
|
||||||
|
</data>
|
||||||
<data name="Identity.Name" xml:space="preserve">
|
<data name="Identity.Name" xml:space="preserve">
|
||||||
<value>Identity</value>
|
<value>Identity</value>
|
||||||
</data>
|
</data>
|
||||||
@ -186,4 +189,16 @@
|
|||||||
<data name="Password.Placeholder" xml:space="preserve">
|
<data name="Password.Placeholder" xml:space="preserve">
|
||||||
<value>Password</value>
|
<value>Password</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="LastIPAddress.HelpText" xml:space="preserve">
|
||||||
|
<value>The IP Address of the user recorded during their last login</value>
|
||||||
|
</data>
|
||||||
|
<data name="LastIPAddress.Text" xml:space="preserve">
|
||||||
|
<value>Last IP Address: </value>
|
||||||
|
</data>
|
||||||
|
<data name="LastLogin.HelpText" xml:space="preserve">
|
||||||
|
<value>The date and time when the user last signed in</value>
|
||||||
|
</data>
|
||||||
|
<data name="LastLogin.Text" xml:space="preserve">
|
||||||
|
<value>Last Login:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -211,25 +211,25 @@
|
|||||||
<value>Allow Login?</value>
|
<value>Allow Login?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Authority.HelpText" xml:space="preserve">
|
<data name="Authority.HelpText" xml:space="preserve">
|
||||||
<value>The Authority Url or Issuer Url associated with the OpenID Connect provider</value>
|
<value>The authority url or issuer url associated with the identity provider</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Authority.Text" xml:space="preserve">
|
<data name="Authority.Text" xml:space="preserve">
|
||||||
<value>Authority:</value>
|
<value>Authority:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthorizationUrl.HelpText" xml:space="preserve">
|
<data name="AuthorizationUrl.HelpText" xml:space="preserve">
|
||||||
<value>The endpoint for obtaining an Authorization Code</value>
|
<value>The endpoint for obtaining an authorization code</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthorizationUrl.Text" xml:space="preserve">
|
<data name="AuthorizationUrl.Text" xml:space="preserve">
|
||||||
<value>Authorization Url:</value>
|
<value>Authorization Url:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ClientID.HelpText" xml:space="preserve">
|
<data name="ClientID.HelpText" xml:space="preserve">
|
||||||
<value>The Client ID from the provider</value>
|
<value>The client id for the identity provider</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ClientID.Text" xml:space="preserve">
|
<data name="ClientID.Text" xml:space="preserve">
|
||||||
<value>Client ID:</value>
|
<value>Client ID:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ClientSecret.HelpText" xml:space="preserve">
|
<data name="ClientSecret.HelpText" xml:space="preserve">
|
||||||
<value>The Client Secret from the provider</value>
|
<value>The client secret for the identity provider</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ClientSecret.Text" xml:space="preserve">
|
<data name="ClientSecret.Text" xml:space="preserve">
|
||||||
<value>Client Secret:</value>
|
<value>Client Secret:</value>
|
||||||
@ -247,7 +247,7 @@
|
|||||||
<value>Domain Filter:</value>
|
<value>Domain Filter:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EmailClaimType.HelpText" xml:space="preserve">
|
<data name="EmailClaimType.HelpText" xml:space="preserve">
|
||||||
<value>The name of the email address claim provided by the provider</value>
|
<value>The name of the email address claim provided by the identity provider</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EmailClaimType.Text" xml:space="preserve">
|
<data name="EmailClaimType.Text" xml:space="preserve">
|
||||||
<value>Email Claim:</value>
|
<value>Email Claim:</value>
|
||||||
@ -259,7 +259,7 @@
|
|||||||
<value>Lockout Settings</value>
|
<value>Lockout Settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MetadataUrl.HelpText" xml:space="preserve">
|
<data name="MetadataUrl.HelpText" xml:space="preserve">
|
||||||
<value>The discovery endpoint for obtaining metadata for this provider. Only specify if the OpenID Connect provider does not use the standard approach (ie. /.well-known/openid-configuration)</value>
|
<value>The discovery endpoint for obtaining metadata for this identity provider. Only specify if the identity provider does not use the standard approach (ie. /.well-known/openid-configuration)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MetadataUrl.Text" xml:space="preserve">
|
<data name="MetadataUrl.Text" xml:space="preserve">
|
||||||
<value>Metadata Url:</value>
|
<value>Metadata Url:</value>
|
||||||
@ -268,7 +268,7 @@
|
|||||||
<value>Password Settings</value>
|
<value>Password Settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PKCE.HelpText" xml:space="preserve">
|
<data name="PKCE.HelpText" xml:space="preserve">
|
||||||
<value>Indicate if the provider supports Proof Key for Code Exchange (PKCE)</value>
|
<value>Indicate if the identity provider supports proof key for code exchange (PKCE)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PKCE.Text" xml:space="preserve">
|
<data name="PKCE.Text" xml:space="preserve">
|
||||||
<value>Use PKCE?</value>
|
<value>Use PKCE?</value>
|
||||||
@ -286,25 +286,25 @@
|
|||||||
<value>Provider Type:</value>
|
<value>Provider Type:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RedirectUrl.HelpText" xml:space="preserve">
|
<data name="RedirectUrl.HelpText" xml:space="preserve">
|
||||||
<value>The Redirect Url (or Callback Url) which usually needs to be registered with the provider</value>
|
<value>The redirect url (or callback url) which usually needs to be registered with the identity provider</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RedirectUrl.Text" xml:space="preserve">
|
<data name="RedirectUrl.Text" xml:space="preserve">
|
||||||
<value>Redirect Url:</value>
|
<value>Redirect Url:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Scopes.HelpText" xml:space="preserve">
|
<data name="Scopes.HelpText" xml:space="preserve">
|
||||||
<value>A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default.</value>
|
<value>A list of scopes to request from the identity provider (separated by commas). If none are specified, standard Scopes will be used by default.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Scopes.Text" xml:space="preserve">
|
<data name="Scopes.Text" xml:space="preserve">
|
||||||
<value>Scopes:</value>
|
<value>Scopes:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TokenUrl.HelpText" xml:space="preserve">
|
<data name="TokenUrl.HelpText" xml:space="preserve">
|
||||||
<value>The endpoint for obtaining an Auth Token</value>
|
<value>The endpoint for obtaining an auth token</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TokenUrl.Text" xml:space="preserve">
|
<data name="TokenUrl.Text" xml:space="preserve">
|
||||||
<value>Token Url:</value>
|
<value>Token Url:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="UserInfoUrl.HelpText" xml:space="preserve">
|
<data name="UserInfoUrl.HelpText" xml:space="preserve">
|
||||||
<value>The endpoint for obtaining user information. This should be an API or Page Url which contains the users email address.</value>
|
<value>The endpoint for obtaining user information. This should be an API endpoint or page url which contains the users email address.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="UserInfoUrl.Text" xml:space="preserve">
|
<data name="UserInfoUrl.Text" xml:space="preserve">
|
||||||
<value>User Info Url:</value>
|
<value>User Info Url:</value>
|
||||||
@ -369,19 +369,31 @@
|
|||||||
<data name="Required" xml:space="preserve">
|
<data name="Required" xml:space="preserve">
|
||||||
<value>Required</value>
|
<value>Required</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CreatedOn" xml:space="preserve">
|
|
||||||
<value>Created On</value>
|
|
||||||
</data>
|
|
||||||
<data name="LastIPAddress" xml:space="preserve">
|
|
||||||
<value>Last IP Address</value>
|
|
||||||
</data>
|
|
||||||
<data name="LastLoginOn" xml:space="preserve">
|
<data name="LastLoginOn" xml:space="preserve">
|
||||||
<value>Last Login</value>
|
<value>Last Login</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IdentifierClaimType.HelpText" xml:space="preserve">
|
<data name="IdentifierClaimType.HelpText" xml:space="preserve">
|
||||||
<value>The name of the unique user identifier claim provided by the provider</value>
|
<value>The name of the unique user identifier claim provided by the identity provider</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
||||||
<value>Identifier Claim:</value>
|
<value>Identifier Claim:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Parameters.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally specify any additional parameters as name/value pairs to send to the identity provider (separated by commas if there are multiple).</value>
|
||||||
|
</data>
|
||||||
|
<data name="Parameters.Text" xml:space="preserve">
|
||||||
|
<value>Parameters:</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoleClaimType.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally provide the name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoleClaimType.Text" xml:space="preserve">
|
||||||
|
<value>Role Claim:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProfileClaimTypes.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally provide a comma delimited list of user profile claims provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProfileClaimTypes.Text" xml:space="preserve">
|
||||||
|
<value>User Profile Claims:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -127,7 +127,7 @@
|
|||||||
<value>Error Loading Files</value>
|
<value>Error Loading Files</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.File.Upload" xml:space="preserve">
|
<data name="Error.File.Upload" xml:space="preserve">
|
||||||
<value>File Upload Failed</value>
|
<value>File Upload Failed Or Is Still In Progress</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.File.NotSelected" xml:space="preserve">
|
<data name="Message.File.NotSelected" xml:space="preserve">
|
||||||
<value>You Have Not Selected A File To Upload</value>
|
<value>You Have Not Selected A File To Upload</value>
|
||||||
@ -141,4 +141,7 @@
|
|||||||
<data name="Success.File.Upload" xml:space="preserve">
|
<data name="Success.File.Upload" xml:space="preserve">
|
||||||
<value>File Upload Succeeded</value>
|
<value>File Upload Succeeded</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.File.Restricted" xml:space="preserve">
|
||||||
|
<value>Files With Extension Of {0} Are Restricted From Upload. Please Contact Your Administrator For More Information.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
123
Oqtane.Client/Resources/Modules/Controls/Pager.resx
Normal file
123
Oqtane.Client/Resources/Modules/Controls/Pager.resx
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="PageOfPages" xml:space="preserve">
|
||||||
|
<value>Page {0} of {1}</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -124,9 +124,9 @@
|
|||||||
<value>User</value>
|
<value>User</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Username.Enter" xml:space="preserve">
|
<data name="Username.Enter" xml:space="preserve">
|
||||||
<value>Enter Username</value>
|
<value>Enter User's Name</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Username.DontExist" xml:space="preserve">
|
<data name="Message.Username.DontExist" xml:space="preserve">
|
||||||
<value>Username Does Not Exist</value>
|
<value>User Does Not Exist With Name Specified</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
@ -123,4 +123,10 @@
|
|||||||
<data name="AllowFileManagement.Text" xml:space="preserve">
|
<data name="AllowFileManagement.Text" xml:space="preserve">
|
||||||
<value>Allow File Management: </value>
|
<value>Allow File Management: </value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AllowRawHtml.HelpText" xml:space="preserve">
|
||||||
|
<value>Specify If Editors Can Enter Raw HTML</value>
|
||||||
|
</data>
|
||||||
|
<data name="AllowRawHtml.Text" xml:space="preserve">
|
||||||
|
<value>Allow Raw HTML:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -318,6 +318,9 @@
|
|||||||
<data name="BlazorWebAssembly" xml:space="preserve">
|
<data name="BlazorWebAssembly" xml:space="preserve">
|
||||||
<value>Blazor WebAssembly</value>
|
<value>Blazor WebAssembly</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="BlazorHybrid" xml:space="preserve">
|
||||||
|
<value>Blazor Hybrid</value>
|
||||||
|
</data>
|
||||||
<data name="Settings" xml:space="preserve">
|
<data name="Settings" xml:space="preserve">
|
||||||
<value>Settings</value>
|
<value>Settings</value>
|
||||||
</data>
|
</data>
|
||||||
@ -327,4 +330,16 @@
|
|||||||
<data name="ShowPassword" xml:space="preserve">
|
<data name="ShowPassword" xml:space="preserve">
|
||||||
<value>Show</value>
|
<value>Show</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PageOfPages" xml:space="preserve">
|
||||||
|
<value>Page {0} of {1}</value>
|
||||||
|
</data>
|
||||||
|
<data name="Url Mappings" xml:space="preserve">
|
||||||
|
<value>Url Mappings</value>
|
||||||
|
</data>
|
||||||
|
<data name="Visitor Management" xml:space="preserve">
|
||||||
|
<value>Visitor Management</value>
|
||||||
|
</data>
|
||||||
|
<data name="Oqtane.Marketplace" xml:space="preserve">
|
||||||
|
<value>Please note that the third party extensions displayed above have been registered in the <a href="https://www.oqtane.net" target="_new">Oqtane Marketplace</a> which enables them to be seamlessly downloaded and installed into the framework.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -138,6 +138,12 @@
|
|||||||
<data name="Register.Text" xml:space="preserve">
|
<data name="Register.Text" xml:space="preserve">
|
||||||
<value>Show Register?</value>
|
<value>Show Register?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Scope.HelpText" xml:space="preserve">
|
||||||
|
<value>Specify if the settings are applicable to this page or the entire site.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Scope.Text" xml:space="preserve">
|
||||||
|
<value>Setting Scope:</value>
|
||||||
|
</data>
|
||||||
<data name="Site" xml:space="preserve">
|
<data name="Site" xml:space="preserve">
|
||||||
<value>Site</value>
|
<value>Site</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -1,25 +1,18 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.JSInterop;
|
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using Oqtane.UI;
|
|
||||||
|
|
||||||
namespace Oqtane.Services
|
namespace Oqtane.Services
|
||||||
{
|
{
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class FileService : ServiceBase, IFileService
|
public class FileService : ServiceBase, IFileService
|
||||||
{
|
{
|
||||||
private readonly IJSRuntime _jsRuntime;
|
public FileService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public FileService(HttpClient http, SiteState siteState, IJSRuntime jsRuntime) : base(http, siteState)
|
|
||||||
{
|
|
||||||
_jsRuntime = jsRuntime;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("File");
|
private string Apiurl => CreateApiUrl("File");
|
||||||
|
|
||||||
@ -30,7 +23,8 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
public async Task<List<File>> GetFilesAsync(string folder)
|
public async Task<List<File>> GetFilesAsync(string folder)
|
||||||
{
|
{
|
||||||
return await GetJsonAsync<List<File>>($"{Apiurl}?folder={folder}");
|
List<File> files = await GetJsonAsync<List<File>>($"{Apiurl}?folder={folder}");
|
||||||
|
return files.OrderBy(item => item.Name).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<File>> GetFilesAsync(int siteId, string folderPath)
|
public async Task<List<File>> GetFilesAsync(int siteId, string folderPath)
|
||||||
@ -42,7 +36,8 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
var path = WebUtility.UrlEncode(folderPath);
|
var path = WebUtility.UrlEncode(folderPath);
|
||||||
|
|
||||||
return await GetJsonAsync<List<File>>($"{Apiurl}/{siteId}/{path}");
|
List<File> files = await GetJsonAsync<List<File>>($"{Apiurl}/{siteId}/{path}");
|
||||||
|
return files?.OrderBy(item => item.Name).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<File> GetFileAsync(int fileId)
|
public async Task<File> GetFileAsync(int fileId)
|
||||||
@ -70,54 +65,6 @@ namespace Oqtane.Services
|
|||||||
return await GetJsonAsync<File>($"{Apiurl}/upload?url={WebUtility.UrlEncode(url)}&folderid={folderId}&name={name}");
|
return await GetJsonAsync<File>($"{Apiurl}/upload?url={WebUtility.UrlEncode(url)}&folderid={folderId}&name={name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> UploadFilesAsync(int folderId, string[] files, string id)
|
|
||||||
{
|
|
||||||
return await UploadFilesAsync(folderId.ToString(), files, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> UploadFilesAsync(string folder, string[] files, string id)
|
|
||||||
{
|
|
||||||
string result = "";
|
|
||||||
|
|
||||||
var interop = new Interop(_jsRuntime);
|
|
||||||
await interop.UploadFiles($"{Apiurl}/upload", folder, id);
|
|
||||||
|
|
||||||
// uploading files is asynchronous so we need to wait for the upload to complete
|
|
||||||
bool success = false;
|
|
||||||
int attempts = 0;
|
|
||||||
while (attempts < 5 && success == false)
|
|
||||||
{
|
|
||||||
Thread.Sleep(2000); // wait 2 seconds
|
|
||||||
result = "";
|
|
||||||
|
|
||||||
List<File> fileList = await GetFilesAsync(folder);
|
|
||||||
if (fileList.Count > 0)
|
|
||||||
{
|
|
||||||
success = true;
|
|
||||||
foreach (string file in files)
|
|
||||||
{
|
|
||||||
if (!fileList.Exists(item => item.Name == file))
|
|
||||||
{
|
|
||||||
success = false;
|
|
||||||
result += file + ",";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
attempts += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
await interop.SetElementAttribute(id + "ProgressInfo", "style", "display: none;");
|
|
||||||
await interop.SetElementAttribute(id + "ProgressBar", "style", "display: none;");
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
result = result.Substring(0, result.Length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<byte[]> DownloadFileAsync(int fileId)
|
public async Task<byte[]> DownloadFileAsync(int fileId)
|
||||||
{
|
{
|
||||||
return await GetByteArrayAsync($"{Apiurl}/download/{fileId}");
|
return await GetByteArrayAsync($"{Apiurl}/download/{fileId}");
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
@ -20,9 +18,7 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
public async Task<List<Folder>> GetFoldersAsync(int siteId)
|
public async Task<List<Folder>> GetFoldersAsync(int siteId)
|
||||||
{
|
{
|
||||||
List<Folder> folders = await GetJsonAsync<List<Folder>>($"{ApiUrl}?siteid={siteId}");
|
return await GetJsonAsync<List<Folder>>($"{ApiUrl}?siteid={siteId}");
|
||||||
folders = GetFoldersHierarchy(folders);
|
|
||||||
return folders;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Folder> GetFolderAsync(int folderId)
|
public async Task<Folder> GetFolderAsync(int folderId)
|
||||||
@ -58,48 +54,5 @@ namespace Oqtane.Services
|
|||||||
{
|
{
|
||||||
await DeleteAsync($"{ApiUrl}/{folderId}");
|
await DeleteAsync($"{ApiUrl}/{folderId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Folder> GetFoldersHierarchy(List<Folder> folders)
|
|
||||||
{
|
|
||||||
List<Folder> hierarchy = new List<Folder>();
|
|
||||||
Action<List<Folder>, Folder> getPath = null;
|
|
||||||
var folders1 = folders;
|
|
||||||
getPath = (folderList, folder) =>
|
|
||||||
{
|
|
||||||
IEnumerable<Folder> children;
|
|
||||||
int level;
|
|
||||||
if (folder == null)
|
|
||||||
{
|
|
||||||
level = -1;
|
|
||||||
children = folders1.Where(item => item.ParentId == null);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
level = folder.Level;
|
|
||||||
children = folders1.Where(item => item.ParentId == folder.FolderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Folder child in children)
|
|
||||||
{
|
|
||||||
child.Level = level + 1;
|
|
||||||
child.HasChildren = folders1.Any(item => item.ParentId == child.FolderId);
|
|
||||||
hierarchy.Add(child);
|
|
||||||
if (getPath != null) getPath(folderList, child);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
folders = folders.OrderBy(item => item.Order).ToList();
|
|
||||||
getPath(folders, null);
|
|
||||||
|
|
||||||
// add any non-hierarchical items to the end of the list
|
|
||||||
foreach (Folder folder in folders)
|
|
||||||
{
|
|
||||||
if (hierarchy.Find(item => item.FolderId == folder.FolderId) == null)
|
|
||||||
{
|
|
||||||
hierarchy.Add(folder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hierarchy;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,27 +66,6 @@ namespace Oqtane.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<File> UploadFileAsync(string url, int folderId, string name);
|
Task<File> UploadFileAsync(string url, int folderId, string name);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Upload one or more files.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folderId">Target <see cref="Folder"/></param>
|
|
||||||
/// <param name="files">The files to upload, serialized as a string.</param>
|
|
||||||
/// <param name="fileUploadName">A task-identifier, to ensure communication about this upload.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<string> UploadFilesAsync(int folderId, string[] files, string fileUploadName);
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Upload one or more files.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folder">Target <see cref="Folder"/>
|
|
||||||
/// TODO: todoc verify exactly from where the folder path must start
|
|
||||||
/// </param>
|
|
||||||
/// <param name="files">The files to upload, serialized as a string.</param>
|
|
||||||
/// <param name="fileUploadName">A task-identifier, to ensure communication about this upload.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<string> UploadFilesAsync(string folder, string[] files, string fileUploadName);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get / download a file (the body).
|
/// Get / download a file (the body).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user