Compare commits
579 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
71d5ae0e68 | |||
46b8d202c7 | |||
bc193a6470 | |||
577528fa0a | |||
35e78ea633 | |||
d5d4f85003 | |||
c6a49a6f5d | |||
a3ff9373a2 | |||
e6d2e74b17 | |||
e8464206e7 | |||
b6c4934123 | |||
8ee83f738b | |||
c359300375 | |||
eb3361fa07 | |||
1b53da6749 | |||
c701895e29 | |||
e81222821e | |||
cbca8c9e93 | |||
92c4edacf2 | |||
e4c648ee92 | |||
69f6586aa9 | |||
391713b84d | |||
8c6a25e4b4 | |||
250701aff0 | |||
2dfd1bd5a2 | |||
1c7380d4cf | |||
68d9ac88b3 | |||
f6b3874668 | |||
c616878a64 | |||
ad485a68ce | |||
2ebba3b8e7 | |||
4117e6e1c5 | |||
423ee04879 | |||
1625e3ba6c | |||
5a71ab3c20 | |||
b5833bf556 | |||
6c31765965 | |||
6dc1d42d90 | |||
e273a954e6 | |||
a602a942c4 | |||
dd32b33621 | |||
355d0405f4 | |||
c1e1595d58 | |||
ef476ac9b3 | |||
a7aaa7b18b | |||
3abfbab5d1 | |||
b158474e21 | |||
cfbcc41543 | |||
fec0a02b1c | |||
3d93ba1215 | |||
042083c0e7 | |||
e0bb7b7faf | |||
acc4099ac8 | |||
683ad8959a | |||
a559c771cf | |||
8ba0ebf955 | |||
d83ec1827a | |||
314e49f5e1 | |||
412b139796 | |||
9b29487934 | |||
95213e41c4 | |||
644ddfd5e1 | |||
68dd9900c4 | |||
6b100cf70b | |||
6f33e5e8a0 | |||
268e0e72a3 | |||
5380b12294 | |||
2ba1a95c8d | |||
1ad0ee4a71 | |||
fc12903cfd | |||
640d22484d | |||
34dc4d64e6 | |||
bbb547efb6 | |||
5b3640e23d | |||
0fbbe244d8 | |||
57def7da0c | |||
0fcf1c2732 | |||
c15b6cdf79 | |||
06e25e04f8 | |||
f8e04656cd | |||
1c8debd894 | |||
58d8fcd074 | |||
a70f1ee1e0 | |||
271ed3cbe2 | |||
8ddaf57e17 | |||
4f1ead116f | |||
3194c5b600 | |||
717f1a9b76 | |||
b7675a21eb | |||
b0d4c0d578 | |||
b7a1d2df75 | |||
5d31d33804 | |||
a97af42e4b | |||
17c6797afb | |||
c8129607e8 | |||
c2dce38bb1 | |||
8b0b7492f5 | |||
a25cfd87cc | |||
f9432acf1b | |||
c6c468c986 | |||
b92a888583 | |||
692b4b33fb | |||
79f427e10a | |||
340b3e7fe8 | |||
50a44c9416 | |||
3c41493d8e | |||
4566ea436c | |||
499bf3bc28 | |||
489a321763 | |||
9d86d923aa | |||
454529bd6a | |||
ca17dd3ca3 | |||
71c7a3de69 | |||
76fc689337 | |||
af5d25490a | |||
fb161ae783 | |||
b92b20e8d2 | |||
4b19059df1 | |||
baa6ec5cba | |||
1a86b80c61 | |||
3783da3647 | |||
39dfc00693 | |||
c7cad20aa7 | |||
5901365e0e | |||
6324aacba1 | |||
d51ba8f6dd | |||
9b69e135d9 | |||
c3218b2f5a | |||
9bbbff31f8 | |||
432429026b | |||
a47ecbdea9 | |||
f250aff99b | |||
003f14003e | |||
fe4e245cda | |||
668da62519 | |||
fd89254d5a | |||
b4338c1761 | |||
fb3c79617f | |||
b80fe428ac | |||
5806563ba4 | |||
5adecc307f | |||
12d1b5e849 | |||
3f2095870d | |||
1481c76a0d | |||
1cdc80e09b | |||
e568aa8320 | |||
28629aa836 | |||
19f180331b | |||
3292f0b545 | |||
3333bfeeff | |||
eb1ac3bc9b | |||
65ae1a6177 | |||
0fba385b9e | |||
ee65a54684 | |||
82fef82c4f | |||
70383a9b9d | |||
15fdba060c | |||
c1065dab2d | |||
dfb4afc698 | |||
893b09e7e4 | |||
938bcb2b62 | |||
ac45f67a21 | |||
36cd9664b1 | |||
073d330db4 | |||
9ba356c47e | |||
f2bec9b478 | |||
3d0cbdd1a7 | |||
c5f5bf0287 | |||
99986c1b94 | |||
7d669caa3c | |||
b68e3cb10f | |||
e305c488d4 | |||
a2417bbe56 | |||
b3967b36c0 | |||
5fb33dfee9 | |||
c002768e5b | |||
aff33c6a5d | |||
a84b497fae | |||
c33d1bcd3c | |||
4071e14a7e | |||
b5b3f190b7 | |||
d43a3e132c | |||
a90c21f80a | |||
2b768165e5 | |||
e8425ba03a | |||
b0a6f402e9 | |||
02e86a940b | |||
79d03eb43e | |||
b564955f85 | |||
5aed64f614 | |||
a823a4d9b7 | |||
4eed2193f4 | |||
48e7a41af6 | |||
bba5caecf7 | |||
ede6a45f15 | |||
9c65d23229 | |||
5dedfe9295 | |||
95d8c368c8 | |||
49fbfb8bbc | |||
aa3d2a5289 | |||
48ae6df4b7 | |||
c635351a12 | |||
d9ff77fd9a | |||
e1a7954307 | |||
efe6421133 | |||
79b62f4407 | |||
9d17804ac7 | |||
5986355504 | |||
192e6fde92 | |||
ad090e62cc | |||
5c072fea62 | |||
fd01a40810 | |||
7b9a83a273 | |||
f964e0e502 | |||
22acb7c74b | |||
6a99e81e75 | |||
93b6de1caf | |||
1fbab5db2b | |||
950e852dee | |||
826898e3fe | |||
1268149d83 | |||
908299970f | |||
861dde8627 | |||
cc9802a0d8 | |||
69d1f3aa53 | |||
ea4587d842 | |||
fb4c95f945 | |||
95a27af5f2 | |||
9d7b25ade6 | |||
3a8f4199cd | |||
11002efc02 | |||
367c1c3568 | |||
9e04230d99 | |||
21304db7c9 | |||
f4f6e98045 | |||
ad41eff38a | |||
dbd6cc4148 | |||
dda71e5ccd | |||
cfe8059176 | |||
8b00784ecc | |||
9bcc6bbad0 | |||
ce7995966d | |||
cea5f86df4 | |||
0912253b1b | |||
5aecc4be03 | |||
e09178c14c | |||
477ded6a4a | |||
311c48becb | |||
ec924a7ddf | |||
e39416a786 | |||
51b356cc0e | |||
66b13bdb8b | |||
4ade58da01 | |||
efcfc0783c | |||
aa22db7fe5 | |||
5e0f008b65 | |||
eaf840e1da | |||
fc9e47778b | |||
35edf78aed | |||
07718f0449 | |||
6759156519 | |||
e2688e6feb | |||
65ba6423b1 | |||
a2f8fe3694 | |||
5273a17ab6 | |||
f7c1e7b706 | |||
45bbc4c681 | |||
6af5682548 | |||
24ed06626d | |||
eeff4af167 | |||
17f46afe14 | |||
224618cf21 | |||
ea93ab2a83 | |||
b9f7c39550 | |||
86b4b8e43a | |||
f54d07548e | |||
037db8a3e4 | |||
8f00e85abd | |||
9ccc4c4059 | |||
8408f98693 | |||
cde271fd5b | |||
c21a097fd2 | |||
83c32d4963 | |||
22c2d56da0 | |||
bd8d6e0480 | |||
825eb700b1 | |||
e59ee70f88 | |||
1173a29ed5 | |||
6a2ff369ea | |||
e22606ae79 | |||
bf56c2a9fa | |||
6567b55ea3 | |||
20e90c0de4 | |||
2892d5ec6f | |||
e034811e92 | |||
ee18bbd145 | |||
e3ebbde767 | |||
6a57980439 | |||
765760f3a5 |
13
.gitignore
vendored
13
.gitignore
vendored
@ -10,11 +10,11 @@ msbuild.binlog
|
|||||||
*.zip
|
*.zip
|
||||||
|
|
||||||
*.idea
|
*.idea
|
||||||
|
_ReSharper.Caches
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
Oqtane.Server/appsettings.json
|
Oqtane.Server/appsettings.json
|
||||||
Oqtane.Server/Data/*.mdf
|
Oqtane.Server/Data
|
||||||
Oqtane.Server/Data/*.ldf
|
|
||||||
Oqtane.Server/Data/*.db
|
|
||||||
|
|
||||||
/Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml
|
/Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml
|
||||||
Oqtane.Server/Content
|
Oqtane.Server/Content
|
||||||
@ -22,3 +22,10 @@ Oqtane.Server/Packages
|
|||||||
Oqtane.Server/wwwroot/Content
|
Oqtane.Server/wwwroot/Content
|
||||||
Oqtane.Server/wwwroot/Packages/*.log
|
Oqtane.Server/wwwroot/Packages/*.log
|
||||||
|
|
||||||
|
Oqtane.Server/wwwroot/Modules
|
||||||
|
!Oqtane.Server/wwwroot/Modules/Oqtane.Modules.*
|
||||||
|
!Oqtane.Server/wwwroot/Modules/Templates
|
||||||
|
|
||||||
|
Oqtane.Server/wwwroot/Themes
|
||||||
|
!Oqtane.Server/wwwroot/Themes/Oqtane.Themes.*
|
||||||
|
!Oqtane.Server/wwwroot/Themes/Templates
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2018-2021 .NET Foundation
|
Copyright (c) 2018-2022 .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
|
||||||
|
@ -30,38 +30,41 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string AntiForgeryToken { get; set; }
|
public string AntiForgeryToken { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Runtime { get; set; }
|
public string Runtime { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string RenderMode { get; set; }
|
public string RenderMode { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public int VisitorId { get; set; }
|
public int VisitorId { get; set; }
|
||||||
|
|
||||||
private bool _initialized = false;
|
[Parameter]
|
||||||
private string _display = "display: none;";
|
public string RemoteIPAddress { get; set; }
|
||||||
private Installation _installation = new Installation { Success = false, Message = "" };
|
|
||||||
|
|
||||||
private PageState PageState { get; set; }
|
[Parameter]
|
||||||
|
public string AuthorizationToken { get; set; }
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
private bool _initialized = false;
|
||||||
{
|
private string _display = "display: none;";
|
||||||
SiteState.AntiForgeryToken = AntiForgeryToken;
|
private Installation _installation = new Installation { Success = false, Message = "" };
|
||||||
InstallationService.SetAntiForgeryTokenHeader(AntiForgeryToken);
|
|
||||||
|
private PageState PageState { get; set; }
|
||||||
|
|
||||||
|
protected override async Task OnParametersSetAsync()
|
||||||
|
{
|
||||||
|
SiteState.RemoteIPAddress = RemoteIPAddress;
|
||||||
|
SiteState.AntiForgeryToken = AntiForgeryToken;
|
||||||
|
SiteState.AuthorizationToken = AuthorizationToken;
|
||||||
|
|
||||||
_installation = await InstallationService.IsInstalled();
|
_installation = await InstallationService.IsInstalled();
|
||||||
if (_installation.Alias != null)
|
if (_installation.Alias != null)
|
||||||
{
|
{
|
||||||
SiteState.Alias = _installation.Alias;
|
SiteState.Alias = _installation.Alias;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
_installation.Message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name";
|
|
||||||
}
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>();
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="pwd" type="password" class="form-control" @bind="@_pwd" />
|
<input id="pwd" type="password" class="form-control" @bind="@_pwd" autocomplete="new-password" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="pwd" type="password" class="form-control" @bind="@_pwd" />
|
<input id="pwd" type="password" class="form-control" @bind="@_pwd" autocomplete="new-password" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
@ -35,7 +35,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="pwd" type="password" class="form-control" @bind="@_pwd" />
|
<input id="pwd" type="password" class="form-control" @bind="@_pwd" autocomplete="new-password" />
|
||||||
</div>
|
</div>
|
||||||
</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,29 +26,42 @@
|
|||||||
<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">
|
||||||
@foreach (var database in _databases)
|
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
||||||
{
|
@foreach (var database in _databases)
|
||||||
if (database.IsDefault)
|
{
|
||||||
{
|
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||||
<option value="@database.Name" selected>@Localizer[@database.Name]</option>
|
}
|
||||||
}
|
</select>
|
||||||
else
|
@if (!_showConnectionString)
|
||||||
{
|
{
|
||||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionString"]</button>
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
</select>
|
{
|
||||||
|
<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
|
||||||
}
|
}
|
||||||
</div>
|
}
|
||||||
|
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">String:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col text-center">
|
<div class="col text-center">
|
||||||
<h2>@Localizer["ApplicationAdmin"]</h2><br />
|
<h2>@Localizer["ApplicationAdmin"]</h2><br />
|
||||||
@ -62,13 +75,19 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<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">
|
||||||
<input id="password" type="password" class="form-control" @bind="@_hostPassword" />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordType" class="form-control" @bind="@_hostPassword" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglePassword</button>
|
||||||
|
</div>
|
||||||
</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="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">
|
||||||
<input id="confirm" type="password" class="form-control" @bind="@_confirmPassword" />
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_confirmPasswordType" class="form-control" @bind="@_confirmPassword" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleConfirmPassword">@_toggleConfirmPassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -101,9 +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 _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;
|
||||||
@ -112,6 +137,9 @@
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
_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))
|
||||||
{
|
{
|
||||||
@ -121,94 +149,146 @@
|
|||||||
{
|
{
|
||||||
_databaseName = "LocalDB";
|
_databaseName = "LocalDB";
|
||||||
}
|
}
|
||||||
LoadDatabaseConfigComponent();
|
LoadDatabaseConfigComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DatabaseChanged(ChangeEventArgs eventArgs)
|
private void DatabaseChanged(ChangeEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_databaseName = (string)eventArgs.Value;
|
_databaseName = (string)eventArgs.Value;
|
||||||
|
_showConnectionString = false;
|
||||||
|
LoadDatabaseConfigComponent();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_message = Localizer["Error.DbConfig.Load"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LoadDatabaseConfigComponent();
|
private void LoadDatabaseConfigComponent()
|
||||||
}
|
{
|
||||||
catch
|
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||||
{
|
if (database != null)
|
||||||
_message = Localizer["Error.DbConfig.Load"];
|
{
|
||||||
}
|
_databaseConfigType = Type.GetType(database.ControlType);
|
||||||
}
|
DatabaseConfigComponent = builder =>
|
||||||
|
{
|
||||||
|
builder.OpenComponent(0, _databaseConfigType);
|
||||||
|
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
||||||
|
builder.CloseComponent();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void LoadDatabaseConfigComponent()
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
if (firstRender)
|
||||||
if (database != null)
|
{
|
||||||
{
|
var interop = new Interop(JSRuntime);
|
||||||
_databaseConfigType = Type.GetType(database.ControlType);
|
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", "");
|
||||||
DatabaseConfigComponent = builder =>
|
await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.bundle.min.js", "sha512-9GacT4119eY3AcosfWtHMsT5JyZudrexyEVzTBWV3viP/YfB9e2pEy3N7WXL3SV6ASXpTU0vzzSxsbfsuUH4sQ==", "anonymous", "", "head");
|
||||||
{
|
}
|
||||||
builder.OpenComponent(0, _databaseConfigType);
|
}
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
|
||||||
builder.CloseComponent();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
private async Task Install()
|
||||||
{
|
{
|
||||||
if (firstRender)
|
var connectionString = String.Empty;
|
||||||
{
|
if (_showConnectionString)
|
||||||
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", "");
|
connectionString = _connectionString;
|
||||||
await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", "anonymous", "", "head", "");
|
}
|
||||||
}
|
else
|
||||||
}
|
{
|
||||||
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
|
{
|
||||||
|
connectionString = databaseConfigControl.GetConnectionString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Install()
|
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
|
||||||
{
|
{
|
||||||
var connectionString = String.Empty;
|
if (await UserService.ValidatePasswordAsync(_hostPassword))
|
||||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
{
|
||||||
{
|
_loadingDisplay = "";
|
||||||
connectionString = databaseConfigControl.GetConnectionString();
|
StateHasChanged();
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && _hostPassword.Length >= 6 && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
|
Uri uri = new Uri(NavigationManager.Uri);
|
||||||
{
|
|
||||||
_loadingDisplay = "";
|
|
||||||
StateHasChanged();
|
|
||||||
|
|
||||||
Uri uri = new Uri(NavigationManager.Uri);
|
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||||
|
|
||||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
var config = new InstallConfig
|
||||||
|
{
|
||||||
|
DatabaseType = database.DBType,
|
||||||
|
ConnectionString = connectionString,
|
||||||
|
Aliases = uri.Authority,
|
||||||
|
HostUsername = _hostUsername,
|
||||||
|
HostPassword = _hostPassword,
|
||||||
|
HostEmail = _hostEmail,
|
||||||
|
HostName = _hostUsername,
|
||||||
|
TenantName = TenantNames.Master,
|
||||||
|
IsNewTenant = true,
|
||||||
|
SiteName = Constants.DefaultSite,
|
||||||
|
Register = _register
|
||||||
|
};
|
||||||
|
|
||||||
var config = new InstallConfig
|
var installation = await InstallationService.Install(config);
|
||||||
{
|
if (installation.Success)
|
||||||
DatabaseType = database.DBType,
|
{
|
||||||
ConnectionString = connectionString,
|
NavigationManager.NavigateTo(uri.Scheme + "://" + uri.Authority, true);
|
||||||
Aliases = uri.Authority,
|
}
|
||||||
HostUsername = _hostUsername,
|
else
|
||||||
HostPassword = _hostPassword,
|
{
|
||||||
HostEmail = _hostEmail,
|
_message = installation.Message;
|
||||||
HostName = _hostUsername,
|
_loadingDisplay = "display: none;";
|
||||||
TenantName = TenantNames.Master,
|
}
|
||||||
IsNewTenant = true,
|
}
|
||||||
SiteName = Constants.DefaultSite,
|
else
|
||||||
Register = _register
|
{
|
||||||
};
|
_message = Localizer["Message.Password.Invalid"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_message = Localizer["Message.Require.DbInfo"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordType == "password")
|
||||||
|
{
|
||||||
|
_passwordType = "text";
|
||||||
|
_togglePassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordType = "password";
|
||||||
|
_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;
|
||||||
|
}
|
||||||
|
|
||||||
var installation = await InstallationService.Install(config);
|
|
||||||
if (installation.Success)
|
|
||||||
{
|
|
||||||
NavigationManager.NavigateTo(uri.Scheme + "://" + uri.Authority, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_message = installation.Message;
|
|
||||||
_loadingDisplay = "display: none;";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_message = Localizer["Message.Require.DbInfo"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
||||||
{
|
{
|
||||||
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>
|
||||||
|
@ -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);
|
||||||
|
@ -36,7 +36,9 @@
|
|||||||
<option value="m">@Localizer["Minute(s)"]</option>
|
<option value="m">@Localizer["Minute(s)"]</option>
|
||||||
<option value="H">@Localizer["Hour(s)"]</option>
|
<option value="H">@Localizer["Hour(s)"]</option>
|
||||||
<option value="d">@Localizer["Day(s)"]</option>
|
<option value="d">@Localizer["Day(s)"]</option>
|
||||||
|
<option value="w">@Localizer["Week(s)"]</option>
|
||||||
<option value="M">@Localizer["Month(s)"]</option>
|
<option value="M">@Localizer["Month(s)"]</option>
|
||||||
|
<option value="O">@Localizer["Once"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -95,126 +97,98 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private int _jobId;
|
private int _jobId;
|
||||||
private string _name = string.Empty;
|
private string _name = string.Empty;
|
||||||
private string _jobType = string.Empty;
|
private string _jobType = string.Empty;
|
||||||
private string _isEnabled = "True";
|
private string _isEnabled = "True";
|
||||||
private string _interval = string.Empty;
|
private string _interval = string.Empty;
|
||||||
private string _frequency = string.Empty;
|
private string _frequency = string.Empty;
|
||||||
private DateTime? _startDate = null;
|
private DateTime? _startDate = null;
|
||||||
private string _startTime = string.Empty;
|
private string _startTime = string.Empty;
|
||||||
private DateTime? _endDate = null;
|
private DateTime? _endDate = null;
|
||||||
private string _endTime = string.Empty;
|
private string _endTime = string.Empty;
|
||||||
private string _retentionHistory = string.Empty;
|
private string _retentionHistory = string.Empty;
|
||||||
private DateTime? _nextDate = null;
|
private DateTime? _nextDate = null;
|
||||||
private string _nextTime = string.Empty;
|
private string _nextTime = 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;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_jobId = Int32.Parse(PageState.QueryString["id"]);
|
_jobId = Int32.Parse(PageState.QueryString["id"]);
|
||||||
Job job = await JobService.GetJobAsync(_jobId);
|
Job job = await JobService.GetJobAsync(_jobId);
|
||||||
if (job != null)
|
if (job != null)
|
||||||
{
|
{
|
||||||
_name = job.Name;
|
_name = job.Name;
|
||||||
_jobType = job.JobType;
|
_jobType = job.JobType;
|
||||||
_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);
|
||||||
{
|
_retentionHistory = job.RetentionHistory.ToString();
|
||||||
_startTime = job.StartDate.Value.ToString("HH:mm");
|
(_nextDate, _nextTime) = Utilities.UtcAsLocalDateAndTime(job.NextExecution);
|
||||||
}
|
createdby = job.CreatedBy;
|
||||||
_endDate = job.EndDate;
|
createdon = job.CreatedOn;
|
||||||
if (job.EndDate != null && job.EndDate.Value.TimeOfDay.TotalSeconds != 0)
|
modifiedby = job.ModifiedBy;
|
||||||
{
|
modifiedon = job.ModifiedOn;
|
||||||
_endTime = job.EndDate.Value.ToString("HH:mm");
|
}
|
||||||
}
|
}
|
||||||
_retentionHistory = job.RetentionHistory.ToString();
|
catch (Exception ex)
|
||||||
_nextDate = job.NextExecution;
|
{
|
||||||
if (job.NextExecution != null && job.NextExecution.Value.TimeOfDay.TotalSeconds != 0)
|
await logger.LogError(ex, "Error Loading Job {JobId} {Error}", _jobId, ex.Message);
|
||||||
{
|
AddModuleMessage(Localizer["Error.Job.Load"], MessageType.Error);
|
||||||
_nextTime = job.NextExecution.Value.ToString("HH:mm");
|
}
|
||||||
}
|
}
|
||||||
createdby = job.CreatedBy;
|
|
||||||
createdon = job.CreatedOn;
|
|
||||||
modifiedby = job.ModifiedBy;
|
|
||||||
modifiedon = job.ModifiedOn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Loading Job {JobId} {Error}", _jobId, ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.Job.Load"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveJob()
|
private async Task SaveJob()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
var job = await JobService.GetJobAsync(_jobId);
|
var job = await JobService.GetJobAsync(_jobId);
|
||||||
job.Name = _name;
|
job.Name = _name;
|
||||||
job.JobType = _jobType;
|
job.JobType = _jobType;
|
||||||
job.IsEnabled = Boolean.Parse(_isEnabled);
|
job.IsEnabled = Boolean.Parse(_isEnabled);
|
||||||
job.Frequency = _frequency;
|
job.Frequency = _frequency;
|
||||||
job.Interval = int.Parse(_interval);
|
if (job.Frequency == "O") // once
|
||||||
job.StartDate = _startDate;
|
{
|
||||||
if (job.StartDate != null)
|
job.Interval = 1;
|
||||||
{
|
}
|
||||||
job.StartDate = job.StartDate.Value.Date;
|
else
|
||||||
if (!string.IsNullOrEmpty(_startTime))
|
{
|
||||||
{
|
job.Interval = int.Parse(_interval);
|
||||||
job.StartDate = DateTime.Parse(job.StartDate.Value.ToShortDateString() + " " + _startTime);
|
}
|
||||||
}
|
job.StartDate = Utilities.LocalDateAndTimeAsUtc(_startDate, _startTime);
|
||||||
}
|
job.EndDate = Utilities.LocalDateAndTimeAsUtc(_endDate, _endTime);
|
||||||
job.EndDate = _endDate;
|
job.RetentionHistory = int.Parse(_retentionHistory);
|
||||||
if (job.EndDate != null)
|
job.NextExecution = Utilities.LocalDateAndTimeAsUtc(_nextDate, _nextTime);
|
||||||
{
|
|
||||||
job.EndDate = job.EndDate.Value.Date;
|
try
|
||||||
if (!string.IsNullOrEmpty(_endTime))
|
{
|
||||||
{
|
job = await JobService.UpdateJobAsync(job);
|
||||||
job.EndDate = DateTime.Parse(job.EndDate.Value.ToShortDateString() + " " + _endTime);
|
await logger.LogInformation("Job Updated {Job}", job);
|
||||||
}
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
job.RetentionHistory = int.Parse(_retentionHistory);
|
catch (Exception ex)
|
||||||
job.NextExecution = _nextDate;
|
{
|
||||||
if (job.NextExecution != null)
|
await logger.LogError(ex, "Error Udate Job {Job} {Error}", job, ex.Message);
|
||||||
{
|
AddModuleMessage(Localizer["Error.Job.Update"], MessageType.Error);
|
||||||
job.NextExecution = job.NextExecution.Value.Date;
|
}
|
||||||
if (!string.IsNullOrEmpty(_nextTime))
|
}
|
||||||
{
|
else
|
||||||
job.NextExecution = DateTime.Parse(job.NextExecution.Value.ToShortDateString() + " " + _nextTime);
|
{
|
||||||
}
|
AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
job = await JobService.UpdateJobAsync(job);
|
|
||||||
await logger.LogInformation("Job Updated {Job}", job);
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Udate Job {Job} {Error}", job, ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.Job.Update"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -33,78 +33,82 @@ 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)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-danger" @onclick="(async () => await StopJob(context.JobId))">@Localizer["Stop"]</button>
|
<button type="button" class="btn btn-danger" @onclick="(async () => await StopJob(context.JobId))">@Localizer["Stop"]</button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick="(async () => await StartJob(context.JobId))">@Localizer["Start"]</button>
|
<button type="button" class="btn btn-success" @onclick="(async () => await StartJob(context.JobId))">@Localizer["Start"]</button>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Job> _jobs;
|
private List<Job> _jobs;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
|
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
var status = string.Empty;
|
var status = string.Empty;
|
||||||
if (!isEnabled)
|
if (!isEnabled)
|
||||||
{
|
{
|
||||||
status = Localizer["Disabled"];
|
status = Localizer["Disabled"];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (isExecuting)
|
if (isExecuting)
|
||||||
{
|
{
|
||||||
status = Localizer["Executing"];
|
status = Localizer["Executing"];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
status = Localizer["Idle"];
|
status = Localizer["Idle"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private string DisplayFrequency(int interval, string frequency)
|
private string DisplayFrequency(int interval, string frequency)
|
||||||
{
|
{
|
||||||
var result = $"{Localizer["Every"]} {interval.ToString()} ";
|
var result = "";
|
||||||
switch (frequency)
|
switch (frequency)
|
||||||
{
|
{
|
||||||
case "m":
|
case "m":
|
||||||
result += Localizer["Minute"];
|
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Minute"];
|
||||||
break;
|
break;
|
||||||
case "H":
|
case "H":
|
||||||
result += Localizer["Hour"];
|
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Hour"];
|
||||||
break;
|
break;
|
||||||
case "d":
|
case "d":
|
||||||
result += Localizer["Day"];
|
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Day"];
|
||||||
break;
|
break;
|
||||||
case "M":
|
case "w":
|
||||||
result += Localizer["Month"];
|
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Week"];
|
||||||
break;
|
break;
|
||||||
}
|
case "M":
|
||||||
|
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Month"];
|
||||||
if (interval > 1)
|
break;
|
||||||
{
|
case "O":
|
||||||
result += Localizer["s"];
|
result = Localizer["Once"];
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,6 +118,7 @@ else
|
|||||||
{
|
{
|
||||||
await JobService.DeleteJobAsync(job.JobId);
|
await JobService.DeleteJobAsync(job.JobId);
|
||||||
await logger.LogInformation("Job Deleted {Job}", job);
|
await logger.LogInformation("Job Deleted {Job}", job);
|
||||||
|
_jobs = await JobService.GetJobsAsync();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -125,12 +130,36 @@ else
|
|||||||
|
|
||||||
private async Task StartJob(int jobId)
|
private async Task StartJob(int jobId)
|
||||||
{
|
{
|
||||||
await JobService.StartJobAsync(jobId);
|
try
|
||||||
|
{
|
||||||
|
await JobService.StartJobAsync(jobId);
|
||||||
|
await logger.LogInformation("Job Started {JobId}", jobId);
|
||||||
|
AddModuleMessage(Localizer["Message.Job.Start"], MessageType.Success);
|
||||||
|
_jobs = await JobService.GetJobsAsync();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Starting Job {JobId} {Error}", jobId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Job.Start"], MessageType.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StopJob(int jobId)
|
private async Task StopJob(int jobId)
|
||||||
{
|
{
|
||||||
await JobService.StopJobAsync(jobId);
|
try
|
||||||
|
{
|
||||||
|
await JobService.StopJobAsync(jobId);
|
||||||
|
await logger.LogInformation("Job Stopped {JobId}", jobId);
|
||||||
|
AddModuleMessage(Localizer["Message.Job.Stop"], MessageType.Success);
|
||||||
|
_jobs = await JobService.GetJobsAsync();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Stopping Job {JobId} {Error}", jobId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Job.Stop"], MessageType.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Refresh()
|
private async Task Refresh()
|
||||||
|
@ -20,6 +20,7 @@ else
|
|||||||
@if (_availableCultures.Count() == 0)
|
@if (_availableCultures.Count() == 0)
|
||||||
{
|
{
|
||||||
<ModuleMessage Type="MessageType.Info" Message="@_message"></ModuleMessage>
|
<ModuleMessage Type="MessageType.Info" Message="@_message"></ModuleMessage>
|
||||||
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -31,7 +32,7 @@ else
|
|||||||
<select id="_code" class="form-select" @bind="@_code" required>
|
<select id="_code" class="form-select" @bind="@_code" required>
|
||||||
@foreach (var culture in _availableCultures)
|
@foreach (var culture in _availableCultures)
|
||||||
{
|
{
|
||||||
<option value="@culture.Name">@culture.DisplayName</option>
|
<option value="@culture.Name">@(culture.DisplayName + " (" + culture.Name + ")")</option>
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -47,11 +48,11 @@ 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>
|
||||||
<TabPanel Name="Download" ResourceKey="Download" Security="SecurityAccessLevel.Host">
|
<TabPanel Name="Translations" Heading="Translations" ResourceKey="Download" Security="SecurityAccessLevel.Host">
|
||||||
<div class="row justify-content-center mb-3">
|
<div class="row justify-content-center mb-3">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -78,6 +79,7 @@ else
|
|||||||
<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;">
|
||||||
@ -108,12 +110,16 @@ else
|
|||||||
}
|
}
|
||||||
<button type="button" class="btn btn-success" @onclick="InstallLanguages">@SharedLocalizer["Install"]</button>
|
<button type="button" class="btn btn-success" @onclick="InstallLanguages">@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"]" />
|
||||||
}
|
}
|
||||||
</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>
|
||||||
@ -159,33 +165,32 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
|
|
||||||
private string _code = string.Empty;
|
private string _code = string.Empty;
|
||||||
private string _isDefault = "False";
|
private string _isDefault = "False";
|
||||||
private string _message;
|
private string _message;
|
||||||
private IEnumerable<Culture> _supportedCultures;
|
private IEnumerable<Culture> _supportedCultures;
|
||||||
private IEnumerable<Culture> _availableCultures;
|
private IEnumerable<Culture> _availableCultures;
|
||||||
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 _license = "";
|
||||||
private string _packageid = "";
|
private string _packageid = "";
|
||||||
private string _version = "";
|
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();
|
var languagesCodes = languages.Select(l => l.Code).ToList();
|
||||||
|
|
||||||
_supportedCultures = await LocalizationService.GetCulturesAsync();
|
_supportedCultures = await LocalizationService.GetCulturesAsync();
|
||||||
_availableCultures = _supportedCultures
|
_availableCultures = _supportedCultures.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !languagesCodes.Contains(c.Name));
|
||||||
.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !languagesCodes.Contains(c.Name));
|
await LoadTranslations();
|
||||||
await LoadTranslations();
|
|
||||||
|
|
||||||
if (_supportedCultures.Count() == 1)
|
if (_supportedCultures.Count() == 1)
|
||||||
{
|
{
|
||||||
|
@ -19,16 +19,18 @@ else
|
|||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
<th>@Localizer["Code"]</th>
|
<th>@Localizer["Code"]</th>
|
||||||
|
<th>@Localizer["Translation"]</th>
|
||||||
<th>@Localizer["Default"]</th>
|
<th>@Localizer["Default"]</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>@context.Version</td>
|
||||||
<td>
|
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
||||||
@if (UpgradeAvailable(context.Code))
|
<td>
|
||||||
|
@if (UpgradeAvailable(context.Code, context.Version))
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button>
|
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button>
|
||||||
}
|
}
|
||||||
@ -38,24 +40,21 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Language> _languages;
|
private List<Language> _languages;
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
|
|
||||||
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);
|
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId, Constants.ClientId);
|
||||||
|
|
||||||
var cultures = await LocalizationService.GetCulturesAsync();
|
var cultures = await LocalizationService.GetCulturesAsync();
|
||||||
var culture = cultures.First(c => c.Name.Equals(Constants.DefaultCulture));
|
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))
|
||||||
{
|
{
|
||||||
_packages = await PackageService.GetPackagesAsync("translation");
|
_packages = await PackageService.GetPackagesAsync("translation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,15 +75,16 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UpgradeAvailable(string code)
|
private bool UpgradeAvailable(string code, string version)
|
||||||
{
|
{
|
||||||
var upgradeavailable = false;
|
var upgradeavailable = false;
|
||||||
if (_packages != null)
|
if (_packages != null && version != null)
|
||||||
{
|
{
|
||||||
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + ".Client." + code)).FirstOrDefault();
|
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + "." + code)).FirstOrDefault();
|
||||||
if (package != null)
|
if (package != null)
|
||||||
{
|
{
|
||||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) > 0);
|
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0) &&
|
||||||
|
(Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ else
|
|||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
await PackageService.DownloadPackageAsync(Constants.PackageId + ".Client." + code, Constants.Version, Constants.PackagesFolder);
|
await PackageService.DownloadPackageAsync(Constants.PackageId + "." + code, Constants.Version, Constants.PackagesFolder);
|
||||||
await logger.LogInformation("Translation Downloaded {Code} {Version}", code, Constants.Version);
|
await logger.LogInformation("Translation Downloaded {Code} {Version}", code, Constants.Version);
|
||||||
await PackageService.InstallPackagesAsync();
|
await PackageService.InstallPackagesAsync();
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Language.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
AddModuleMessage(string.Format(Localizer["Success.Language.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
|
@ -3,14 +3,9 @@
|
|||||||
@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
|
||||||
|
|
||||||
@if (_message != string.Empty)
|
|
||||||
{
|
|
||||||
<ModuleMessage Message="@_message" Type="@_type" />
|
|
||||||
}
|
|
||||||
<AuthorizeView Roles="@RoleNames.Registered">
|
<AuthorizeView Roles="@RoleNames.Registered">
|
||||||
<Authorizing>
|
<Authorizing>
|
||||||
<text>...</text>
|
<text>...</text>
|
||||||
@ -19,185 +14,313 @@
|
|||||||
<div>@Localizer["Info.SignedIn"]</div>
|
<div>@Localizer["Info.SignedIn"]</div>
|
||||||
</Authorized>
|
</Authorized>
|
||||||
<NotAuthorized>
|
<NotAuthorized>
|
||||||
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
@if (!twofactor)
|
||||||
<div class="container Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
|
{
|
||||||
<div class="form-group">
|
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<label for="Username" class="control-label">@SharedLocalizer["Username"] </label>
|
<div class="Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
|
||||||
<input type="text" @ref="username" name="Username" class="form-control username" placeholder="Username" @bind="@_username" id="Username" required />
|
@if (_allowexternallogin)
|
||||||
</div>
|
{
|
||||||
<div class="form-group">
|
<button type="button" class="btn btn-primary" @onclick="ExternalLogin">@Localizer["Use"] @PageState.Site.Settings["ExternalLogin:ProviderName"]</button>
|
||||||
<label for="Password" class="control-label">@SharedLocalizer["Password"] </label>
|
<br /><br />
|
||||||
<input type="password" name="Password" class="form-control password" placeholder="Password" @bind="@_password" id="Password" required />
|
}
|
||||||
</div>
|
@if (_allowsitelogin)
|
||||||
<div class="form-group">
|
{
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-group">
|
||||||
<label class="form-check-label" for="Remember">@Localizer["RememberMe"]</label>
|
<Label Class="control-label" For="username" HelpText="Please enter your Username" ResourceKey="Username">Username:</Label>
|
||||||
<input type="checkbox" class="form-check-input" name="Remember" @bind="@_remember" id="Remember" />
|
<input id="username" type="text" @ref="username" class="form-control" placeholder="@Localizer["Username.Placeholder"]" @bind="@_username" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group mt-2">
|
||||||
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
<Label Class="control-label" For="password" HelpText="Please enter your Password" ResourceKey="Password">Password:</Label>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
<div class="input-group">
|
||||||
<br /><br />
|
<input id="password" type="@_passwordtype" name="Password" class="form-control" placeholder="@Localizer["Password.Placeholder"]" @bind="@_password" required />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["ForgotPassword"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</NotAuthorized>
|
<div class="form-group mt-2">
|
||||||
|
<div class="form-check">
|
||||||
|
<input id="remember" type="checkbox" class="form-check-input" @bind="@_remember" />
|
||||||
|
<Label Class="control-label" For="remember" HelpText="Specify if you would like to be signed back in automatically the next time you visit this site" ResourceKey="Remember">Remember Me?</Label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
|
<br /><br />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["ForgotPassword"]</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
|
<div class="container Oqtane-Modules-Admin-Login">
|
||||||
|
<div class="form-group">
|
||||||
|
<Label Class="control-label" For="code" HelpText="Please enter the secure verification code which was sent to you by email" ResourceKey="Code">Verification Code:</Label>
|
||||||
|
<input id="code" class="form-control" @bind="@_code" placeholder="@Localizer["Code.Placeholder"]" maxlength="6" required />
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Cancel"]</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
</NotAuthorized>
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _returnUrl = string.Empty;
|
private bool _allowsitelogin = true;
|
||||||
private string _message = string.Empty;
|
private bool _allowexternallogin = false;
|
||||||
private MessageType _type = MessageType.Info;
|
private ElementReference login;
|
||||||
private string _username = string.Empty;
|
private bool validated = false;
|
||||||
private string _password = string.Empty;
|
private bool twofactor = false;
|
||||||
private bool _remember = false;
|
private string _username = string.Empty;
|
||||||
private bool validated = false;
|
private ElementReference username;
|
||||||
|
private string _password = string.Empty;
|
||||||
|
private string _passwordtype = "password";
|
||||||
|
private string _togglepassword = string.Empty;
|
||||||
|
private bool _remember = false;
|
||||||
|
private string _code = string.Empty;
|
||||||
|
|
||||||
private ElementReference login;
|
private string _returnUrl = string.Empty;
|
||||||
private ElementReference username;
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||||
|
|
||||||
public override List<Resource> Resources => new List<Resource>()
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
{
|
{
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
try
|
||||||
{
|
{
|
||||||
_returnUrl = PageState.QueryString["returnurl"];
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
}
|
|
||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("name"))
|
if (PageState.Site.Settings.ContainsKey("LoginOptions:AllowSiteLogin") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]))
|
||||||
{
|
{
|
||||||
_username = PageState.QueryString["name"];
|
_allowsitelogin = bool.Parse(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("token"))
|
if (PageState.Site.Settings.ContainsKey("ExternalLogin:ProviderType") && !string.IsNullOrEmpty(PageState.Site.Settings["ExternalLogin:ProviderType"]))
|
||||||
{
|
{
|
||||||
var user = new User();
|
_allowexternallogin = true;
|
||||||
user.SiteId = PageState.Site.SiteId;
|
}
|
||||||
user.Username = _username;
|
|
||||||
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
|
|
||||||
|
|
||||||
if (user != null)
|
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
|
_returnUrl = PageState.QueryString["returnurl"];
|
||||||
_message = Localizer["Success.Account.Verified"];
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
|
|
||||||
_message = Localizer["Message.Account.NotVerfied"];
|
|
||||||
_type = MessageType.Warning;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
if (PageState.QueryString.ContainsKey("name"))
|
||||||
{
|
{
|
||||||
if (firstRender)
|
_username = PageState.QueryString["name"];
|
||||||
{
|
}
|
||||||
if(PageState.User == null)
|
|
||||||
{
|
|
||||||
await username.FocusAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Login()
|
if (PageState.QueryString.ContainsKey("token") && !string.IsNullOrEmpty(_username))
|
||||||
{
|
{
|
||||||
validated = true;
|
var user = new User();
|
||||||
var interop = new Interop(JSRuntime);
|
user.SiteId = PageState.Site.SiteId;
|
||||||
if (await interop.FormValid(login))
|
user.Username = _username;
|
||||||
{
|
|
||||||
if (PageState.Runtime == Oqtane.Shared.Runtime.Server)
|
|
||||||
{
|
|
||||||
var user = new User();
|
|
||||||
user.SiteId = PageState.Site.SiteId;
|
|
||||||
user.Username = _username;
|
|
||||||
user.Password = _password;
|
|
||||||
user = await UserService.LoginUserAsync(user, false, false);
|
|
||||||
|
|
||||||
if (user.IsAuthenticated)
|
if (PageState.QueryString.ContainsKey("key"))
|
||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
user = await UserService.LinkUserAsync(user, PageState.QueryString["token"], PageState.Site.Settings["ExternalLogin:ProviderType"], PageState.QueryString["key"], PageState.Site.Settings["ExternalLogin:ProviderName"]);
|
||||||
// server-side Blazor needs to post to the Login page so that the cookies are set correctly
|
if (user != null)
|
||||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
|
{
|
||||||
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
await logger.LogInformation(LogFunction.Security, "External Login Linkage Successful For Username {Username}", _username);
|
||||||
await interop.SubmitForm(url, fields);
|
AddModuleMessage(Localizer["Success.Account.Linked"], MessageType.Info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await logger.LogError(LogFunction.Security, "Login Failed For Username {Username}", _username);
|
await logger.LogError(LogFunction.Security, "External Login Linkage Failed For Username {Username}", _username);
|
||||||
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
|
AddModuleMessage(Localizer["Message.Account.NotLinked"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
_username = "";
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
// client-side Blazor
|
{
|
||||||
var user = new User();
|
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
|
||||||
user.SiteId = PageState.Site.SiteId;
|
if (user != null)
|
||||||
user.Username = _username;
|
{
|
||||||
user.Password = _password;
|
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
|
||||||
user = await UserService.LoginUserAsync(user, true, _remember);
|
AddModuleMessage(Localizer["Success.Account.Verified"], MessageType.Info);
|
||||||
if (user.IsAuthenticated)
|
}
|
||||||
{
|
else
|
||||||
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
{
|
||||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
|
||||||
authstateprovider.NotifyAuthenticationChanged();
|
AddModuleMessage(Localizer["Message.Account.NotVerified"], MessageType.Warning);
|
||||||
NavigationManager.NavigateTo(NavigateUrl(_returnUrl, true));
|
}
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
await logger.LogError(LogFunction.Security, "Login Failed For Username {Username}", _username);
|
{
|
||||||
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
|
if (PageState.QueryString.ContainsKey("status"))
|
||||||
}
|
{
|
||||||
}
|
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Info);
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
}
|
||||||
AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
|
catch (Exception ex)
|
||||||
}
|
{
|
||||||
}
|
await logger.LogError(ex, "Error Loading Login {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.LoadLogin"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void Cancel()
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(_returnUrl);
|
if (firstRender && PageState.User == null)
|
||||||
}
|
{
|
||||||
|
await username.FocusAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Forgot()
|
private async Task Login()
|
||||||
{
|
{
|
||||||
if (_username != string.Empty)
|
try
|
||||||
{
|
{
|
||||||
var user = await UserService.GetUserAsync(_username, PageState.Site.SiteId);
|
validated = true;
|
||||||
if (user != null)
|
var interop = new Interop(JSRuntime);
|
||||||
{
|
if (await interop.FormValid(login))
|
||||||
await UserService.ForgotPasswordAsync(user);
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
|
var hybrid = (PageState.Runtime == Shared.Runtime.Hybrid);
|
||||||
_message = "Please Check The Email Address Associated To Your User Account For A Password Reset Notification";
|
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password, LastIPAddress = SiteState.RemoteIPAddress};
|
||||||
}
|
|
||||||
else
|
if (!twofactor)
|
||||||
{
|
{
|
||||||
_message = "User Does Not Exist";
|
user = await UserService.LoginUserAsync(user, hybrid, _remember);
|
||||||
_type = MessageType.Warning;
|
}
|
||||||
}
|
else
|
||||||
}
|
{
|
||||||
else
|
user = await UserService.VerifyTwoFactorAsync(user, _code);
|
||||||
{
|
}
|
||||||
_message = "Please Enter The Username Related To Your Account And Then Select The Forgot Password Option Again";
|
|
||||||
}
|
|
||||||
|
|
||||||
StateHasChanged();
|
if (user.IsAuthenticated)
|
||||||
}
|
{
|
||||||
|
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(_returnUrl, true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 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 };
|
||||||
|
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
||||||
|
await interop.SubmitForm(url, fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && PageState.Site.Settings["LoginOptions:TwoFactor"] == "required") || user.TwoFactorRequired)
|
||||||
|
{
|
||||||
|
twofactor = true;
|
||||||
|
validated = false;
|
||||||
|
AddModuleMessage(Localizer["Message.TwoFactor"], MessageType.Info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!twofactor)
|
||||||
|
{
|
||||||
|
await logger.LogInformation(LogFunction.Security, "Login Failed For Username {Username}", _username);
|
||||||
|
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await logger.LogInformation(LogFunction.Security, "Two Factor Verification Failed For Username {Username}", _username);
|
||||||
|
AddModuleMessage(Localizer["Error.TwoFactor.Fail"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Performing Login {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Login"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel()
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(_returnUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Forgot()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_username != string.Empty)
|
||||||
|
{
|
||||||
|
var user = await UserService.GetUserAsync(_username, PageState.Site.SiteId);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
await UserService.ForgotPasswordAsync(user);
|
||||||
|
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
|
||||||
|
AddModuleMessage(Localizer["Message.ForgotUser"], MessageType.Info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.UserDoesNotExist"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.ForgotPassword"], MessageType.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Resetting Password {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.ResetPassword"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Reset()
|
||||||
|
{
|
||||||
|
twofactor = false;
|
||||||
|
_username = "";
|
||||||
|
_password = "";
|
||||||
|
ClearModuleMessage();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task KeyPressed(KeyboardEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Code == "Enter" || e.Code == "NumpadEnter")
|
||||||
|
{
|
||||||
|
await Login();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExternalLogin()
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + _returnUrl), true);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task KeyPressed(KeyboardEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.Code == "Enter" || e.Code == "NumpadEnter")
|
|
||||||
{
|
|
||||||
await Login();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,86 +9,88 @@
|
|||||||
@inject IStringLocalizer<Detail> Localizer
|
@inject IStringLocalizer<Detail> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<div class="container">
|
@if (_initialized)
|
||||||
<div class="row mb-1 align-items-center">
|
{
|
||||||
<Label Class="col-sm-3" For="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label>
|
<div class="container">
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="dateTime" class="form-control" @bind="@_logDate" readonly />
|
<Label Class="col-sm-3" For="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="dateTime" class="form-control" @bind="@_logDate" readonly />
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="level" class="form-control" @bind="@_level" readonly />
|
<Label Class="col-sm-3" For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="level" class="form-control" @bind="@_level" readonly />
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="feature" class="form-control" @bind="@_feature" readonly />
|
<Label Class="col-sm-3" For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="feature" class="form-control" @bind="@_feature" readonly />
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="function" class="form-control" @bind="@_function" readonly />
|
<Label Class="col-sm-3" For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="function" class="form-control" @bind="@_function" readonly />
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="category" class="form-control" @bind="@_category" readonly />
|
<Label Class="col-sm-3" For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="category" class="form-control" @bind="@_category" readonly />
|
||||||
@if (_pageName != string.Empty)
|
</div>
|
||||||
{
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
@if (_pageName != string.Empty)
|
||||||
<Label Class="col-sm-3" For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label>
|
{
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="page" class="form-control" @bind="@_pageName" readonly />
|
<Label Class="col-sm-3" For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="page" class="form-control" @bind="@_pageName" readonly />
|
||||||
}
|
</div>
|
||||||
@if (_moduleTitle != string.Empty)
|
</div>
|
||||||
{
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
@if (_moduleTitle != string.Empty)
|
||||||
<Label Class="col-sm-3" For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label>
|
{
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="module" class="form-control" @bind="@_moduleTitle" readonly />
|
<Label Class="col-sm-3" For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="module" class="form-control" @bind="@_moduleTitle" readonly />
|
||||||
}
|
</div>
|
||||||
@if (_username != string.Empty)
|
</div>
|
||||||
{
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
@if (_username != string.Empty)
|
||||||
<Label Class="col-sm-3" For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label>
|
{
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="user" class="form-control" @bind="@_username" readonly />
|
<Label Class="col-sm-3" For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="user" class="form-control" @bind="@_username" readonly />
|
||||||
}
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label>
|
}
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="url" class="form-control" @bind="@_url" readonly />
|
<Label Class="col-sm-3" For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="url" class="form-control" @bind="@_url" readonly />
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="template" class="form-control" @bind="@_template" readonly />
|
<Label Class="col-sm-3" For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="template" class="form-control" @bind="@_template" readonly />
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="message" HelpText="The message that the system generated" ResourceKey="Message">Message: </Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
|
<Label Class="col-sm-3" For="message" HelpText="The message that the system generated" ResourceKey="Message">Message: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
|
||||||
@if (!string.IsNullOrEmpty(_exception))
|
</div>
|
||||||
{
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
@if (!string.IsNullOrEmpty(_exception))
|
||||||
<Label Class="col-sm-3" For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label>
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea>
|
<textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea>
|
||||||
</div>
|
</div>
|
||||||
@ -107,81 +109,89 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
<NavLink class="btn btn-secondary" href="@CloseUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
@code {
|
||||||
|
private bool _initialized = false;
|
||||||
|
private int _logId;
|
||||||
|
private string _logDate = string.Empty;
|
||||||
|
private string _level = string.Empty;
|
||||||
|
private string _feature = string.Empty;
|
||||||
|
private string _function = string.Empty;
|
||||||
|
private string _category = string.Empty;
|
||||||
|
private string _pageName = string.Empty;
|
||||||
|
private string _moduleTitle = string.Empty;
|
||||||
|
private string _username = string.Empty;
|
||||||
|
private string _url = string.Empty;
|
||||||
|
private string _template = string.Empty;
|
||||||
|
private string _message = string.Empty;
|
||||||
|
private string _exception = string.Empty;
|
||||||
|
private string _properties = string.Empty;
|
||||||
|
private string _server = string.Empty;
|
||||||
|
|
||||||
@code {
|
public override string UrlParametersTemplate => "/{id}";
|
||||||
private int _logId;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
private string _logDate = string.Empty;
|
|
||||||
private string _level = string.Empty;
|
|
||||||
private string _feature = string.Empty;
|
|
||||||
private string _function = string.Empty;
|
|
||||||
private string _category = string.Empty;
|
|
||||||
private string _pageName = string.Empty;
|
|
||||||
private string _moduleTitle = string.Empty;
|
|
||||||
private string _username = string.Empty;
|
|
||||||
private string _url = string.Empty;
|
|
||||||
private string _template = string.Empty;
|
|
||||||
private string _message = string.Empty;
|
|
||||||
private string _exception = string.Empty;
|
|
||||||
private string _properties = string.Empty;
|
|
||||||
private string _server = string.Empty;
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
protected override async Task OnInitializedAsync()
|
try
|
||||||
{
|
{
|
||||||
try
|
_logId = Int32.Parse(UrlParameters["id"]);
|
||||||
|
var log = await LogService.GetLogAsync(_logId);
|
||||||
|
if (log != null)
|
||||||
{
|
{
|
||||||
_logId = Int32.Parse(PageState.QueryString["id"]);
|
_logDate = log.LogDate.ToString(CultureInfo.CurrentCulture);
|
||||||
var log = await LogService.GetLogAsync(_logId);
|
_level = log.Level;
|
||||||
if (log != null)
|
_feature = log.Feature;
|
||||||
|
_function = log.Function;
|
||||||
|
_category = log.Category;
|
||||||
|
|
||||||
|
if (log.PageId != null)
|
||||||
{
|
{
|
||||||
_logDate = log.LogDate.ToString(CultureInfo.CurrentCulture);
|
var page = await PageService.GetPageAsync(log.PageId.Value);
|
||||||
_level = log.Level;
|
if (page != null)
|
||||||
_feature = log.Feature;
|
|
||||||
_function = log.Function;
|
|
||||||
_category = log.Category;
|
|
||||||
|
|
||||||
if (log.PageId != null)
|
|
||||||
{
|
{
|
||||||
var page = await PageService.GetPageAsync(log.PageId.Value);
|
_pageName = page.Name;
|
||||||
if (page != null)
|
|
||||||
{
|
|
||||||
_pageName = page.Name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log.PageId != null && log.ModuleId != null)
|
|
||||||
{
|
|
||||||
var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value);
|
|
||||||
if (pagemodule != null)
|
|
||||||
{
|
|
||||||
_moduleTitle = pagemodule.Title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (log.UserId != null)
|
|
||||||
{
|
|
||||||
var user = await UserService.GetUserAsync(log.UserId.Value, PageState.Site.SiteId);
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
_username = user.Username;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_url = log.Url;
|
|
||||||
_template = log.MessageTemplate;
|
|
||||||
_message = log.Message;
|
|
||||||
_exception = log.Exception;
|
|
||||||
_properties = log.Properties;
|
|
||||||
_server = log.Server;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception ex)
|
if (log.PageId != null && log.ModuleId != null && log.ModuleId != -1)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Log {LogId} {Error}", _logId, ex.Message);
|
var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value);
|
||||||
AddModuleMessage(Localizer["Error.Log.Load"], MessageType.Error);
|
if (pagemodule != null)
|
||||||
|
{
|
||||||
|
_moduleTitle = pagemodule.Title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log.UserId != null)
|
||||||
|
{
|
||||||
|
var user = await UserService.GetUserAsync(log.UserId.Value, PageState.Site.SiteId);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
_username = user.Username;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_url = log.Url;
|
||||||
|
_template = log.MessageTemplate;
|
||||||
|
_message = log.Message;
|
||||||
|
_exception = log.Exception;
|
||||||
|
_properties = log.Properties;
|
||||||
|
_server = log.Server;
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Log {LogId} {Error}", _logId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Log.Load"], MessageType.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string CloseUrl()
|
||||||
|
{
|
||||||
|
return (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : NavigateUrl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
@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 IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@ -10,81 +12,127 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="container g-0">
|
<TabStrip>
|
||||||
<div class="row mb-1 align-items-center">
|
<TabPanel Name="Events" Heading="Events" ResourceKey="Events">
|
||||||
<div class="col-sm-4">
|
<div class="container g-0">
|
||||||
<Label For="level" HelpText="Select the log level for event log items" ResourceKey="Level">Level: </Label><br /><br />
|
<div class="row mb-1 align-items-center">
|
||||||
<select id="level" class="form-select" @onchange="(e => LevelChanged(e))">
|
<div class="col-sm-4">
|
||||||
<option value="-"><@Localizer["AllLevels"]></option>
|
<Label For="level" HelpText="Select the log level for event log items" ResourceKey="Level">Level: </Label><br /><br />
|
||||||
<option value="Trace">@Localizer["Trace"]</option>
|
<select id="level" class="form-select" value="@_level" @onchange="(e => LevelChanged(e))">
|
||||||
<option value="Debug">@Localizer["Debug"]</option>
|
<option value="-"><@Localizer["AllLevels"]></option>
|
||||||
<option value="Information">@Localizer["Information"]</option>
|
<option value="Trace">@Localizer["Trace"]</option>
|
||||||
<option value="Warning">@Localizer["Warning"]</option>
|
<option value="Debug">@Localizer["Debug"]</option>
|
||||||
<option value="Error">@Localizer["Error"]</option>
|
<option value="Information">@Localizer["Information"]</option>
|
||||||
<option value="Critical">@Localizer["Critical"]</option>
|
<option value="Warning">@Localizer["Warning"]</option>
|
||||||
</select>
|
<option value="Error">@Localizer["Error"]</option>
|
||||||
</div>
|
<option value="Critical">@Localizer["Critical"]</option>
|
||||||
<div class="col-sm-4">
|
</select>
|
||||||
<Label For="function" HelpText="Select the function for event log items" ResourceKey="Function">Function: </Label><br /><br />
|
</div>
|
||||||
<select id="function" class="form-select" @onchange="(e => FunctionChanged(e))">
|
<div class="col-sm-4">
|
||||||
<option value="-"><@Localizer["AllFunctions"]></option>
|
<Label For="function" HelpText="Select the function for event log items" ResourceKey="Function">Function: </Label><br /><br />
|
||||||
<option value="Create">@Localizer["Create"]</option>
|
<select id="function" class="form-select" value="@_function" @onchange="(e => FunctionChanged(e))">
|
||||||
<option value="Read">@Localizer["Read"]</option>
|
<option value="-"><@Localizer["AllFunctions"]></option>
|
||||||
<option value="Update">@SharedLocalizer["Update"]</option>
|
<option value="Create">@Localizer["Create"]</option>
|
||||||
<option value="Delete">@SharedLocalizer["Delete"]</option>
|
<option value="Read">@Localizer["Read"]</option>
|
||||||
<option value="Security">@Localizer["Security"]</option>
|
<option value="Update">@SharedLocalizer["Update"]</option>
|
||||||
<option value="Other">@Localizer["Other"]</option>
|
<option value="Delete">@SharedLocalizer["Delete"]</option>
|
||||||
</select>
|
<option value="Security">@Localizer["Security"]</option>
|
||||||
</div>
|
<option value="Other">@Localizer["Other"]</option>
|
||||||
<div class="col-sm-4">
|
</select>
|
||||||
<Label For="rows" HelpText="Select the maximum number of event log items to review. Please note that if you choose more than 10 items the information will be split into pages." ResourceKey="Rows">Maximum Items: </Label><br /><br />
|
</div>
|
||||||
<select id="rows" class="form-select" @onchange="(e => RowsChanged(e))">
|
<div class="col-sm-4">
|
||||||
<option value="10">10</option>
|
<Label For="rows" HelpText="Select the maximum number of event log items to review. Please note that if you choose more than 10 items the information will be split into pages." ResourceKey="Rows">Maximum Items: </Label><br /><br />
|
||||||
<option value="50">50</option>
|
<select id="rows" class="form-select" value="@_rows" @onchange="(e => RowsChanged(e))">
|
||||||
<option value="100">100</option>
|
<option value="10">10</option>
|
||||||
</select>
|
<option value="50">50</option>
|
||||||
</div>
|
<option value="100">100</option>
|
||||||
</div>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
|
||||||
@if (_logs.Any())
|
@if (_logs.Any())
|
||||||
{
|
{
|
||||||
<Pager Items="@_logs">
|
<Pager Items="@_logs" CurrentPage="@_page.ToString()" OnPageChange="OnPageChange">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@Localizer["Date"]</th>
|
<th>@Localizer["Date"]</th>
|
||||||
<th>@Localizer["Level"]</th>
|
<th>@Localizer["Level"]</th>
|
||||||
<th>@Localizer["Feature"]</th>
|
<th>@Localizer["Feature"]</th>
|
||||||
<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())" 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>
|
||||||
<td class="@GetClass(context.Function)">@context.Function</td>
|
<td class="@GetClass(context.Function)">@context.Function</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<p><em>@Localizer["NoLogs"]</em></p>
|
<p><em>@Localizer["NoLogs"]</em></p>
|
||||||
}
|
}
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="retention" HelpText="Number of days of events to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="retention" class="form-control" @bind="@_retention" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||||
|
</TabPanel>
|
||||||
|
</TabStrip>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _level = "-";
|
private string _level = "-";
|
||||||
private string _function = "-";
|
private string _function = "-";
|
||||||
private string _rows = "10";
|
private string _rows = "10";
|
||||||
private List<Log> _logs;
|
private int _page = 1;
|
||||||
|
private List<Log> _logs;
|
||||||
|
private string _retention = "";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}";
|
||||||
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
|
protected override async Task OnParametersSetAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 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}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UrlParameters.ContainsKey("level"))
|
||||||
|
{
|
||||||
|
_level = UrlParameters["level"];
|
||||||
|
}
|
||||||
|
if (UrlParameters.ContainsKey("function"))
|
||||||
|
{
|
||||||
|
_function = UrlParameters["function"];
|
||||||
|
}
|
||||||
|
if (UrlParameters.ContainsKey("rows"))
|
||||||
|
{
|
||||||
|
_rows = UrlParameters["rows"];
|
||||||
|
}
|
||||||
|
if (UrlParameters.ContainsKey("page") && int.TryParse(UrlParameters["page"], out int page))
|
||||||
|
{
|
||||||
|
_page = page;
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await GetLogs();
|
await GetLogs();
|
||||||
|
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
|
_retention = SettingService.GetSetting(settings, "LogRetention", "30");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -170,4 +218,27 @@ else
|
|||||||
}
|
}
|
||||||
return classname;
|
return classname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SaveSiteSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
|
settings = SettingService.SetSetting(settings, "LogRetention", _retention, true);
|
||||||
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
|
|
||||||
|
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPageChange(int page)
|
||||||
|
{
|
||||||
|
_page = page;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -76,64 +76,71 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private string _moduledefinitionname = string.Empty;
|
private string _moduledefinitionname = string.Empty;
|
||||||
private string _owner = string.Empty;
|
private string _owner = string.Empty;
|
||||||
private string _module = string.Empty;
|
private string _module = string.Empty;
|
||||||
private string _description = string.Empty;
|
private string _description = string.Empty;
|
||||||
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;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
_moduledefinitionname = SettingService.GetSetting(ModuleState.Settings, "ModuleDefinitionName", "");
|
_moduledefinitionname = SettingService.GetSetting(ModuleState.Settings, "ModuleDefinitionName", "");
|
||||||
if (string.IsNullOrEmpty(_moduledefinitionname))
|
if (string.IsNullOrEmpty(_moduledefinitionname))
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Info.Module.Creator"], MessageType.Info);
|
AddModuleMessage(Localizer["Info.Module.Creator"], MessageType.Info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Info.Module.Activate"], MessageType.Info);
|
AddModuleMessage(Localizer["Info.Module.Activate"], MessageType.Info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync();
|
_templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync();
|
||||||
_versions = Constants.ReleaseVersions.Split(',').Where(item => Version.Parse(item).CompareTo(Version.Parse("2.0.0")) >= 0).ToArray();
|
_versions = Constants.ReleaseVersions.Split(',').Where(item => Version.Parse(item).CompareTo(Version.Parse("2.0.0")) >= 0).ToArray();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Module Creator");
|
await logger.LogError(ex, "Error Loading Module Creator");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CreateModule()
|
private async Task CreateModule()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
|
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
|
||||||
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
{
|
||||||
|
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
|
||||||
|
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
||||||
|
|
||||||
var settings = ModuleState.Settings;
|
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||||
SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
|
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)
|
||||||
{
|
{
|
||||||
|
@ -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,146 +114,150 @@
|
|||||||
<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 _packagelicense = "";
|
||||||
private string _version = "";
|
private string _packageversion = "";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await LoadModuleDefinitions();
|
await LoadModuleDefinitions();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Packages {Error}", ex.Message);
|
await logger.LogError(ex, "Error Loading Packages {Error}", ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Package.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Package.Load"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadModuleDefinitions()
|
private async Task LoadModuleDefinitions()
|
||||||
{
|
{
|
||||||
var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||||
_packages = await PackageService.GetPackagesAsync("module", _search, _price, "");
|
_packages = await PackageService.GetPackagesAsync("module", _search, _price, "");
|
||||||
|
|
||||||
if (_packages != null)
|
if (_packages != null)
|
||||||
{
|
{
|
||||||
foreach (Package package in _packages.ToArray())
|
foreach (Package package in _packages.ToArray())
|
||||||
{
|
{
|
||||||
if (moduledefinitions.Exists(item => item.PackageName == package.PackageId))
|
if (moduledefinitions.Exists(item => item.PackageName == package.PackageId))
|
||||||
{
|
{
|
||||||
_packages.Remove(package);
|
_packages.Remove(package);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void PriceChanged(ChangeEventArgs e)
|
private async void PriceChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_price = (string)e.Value;
|
_price = (string)e.Value;
|
||||||
_search = "";
|
_search = "";
|
||||||
await LoadModuleDefinitions();
|
await LoadModuleDefinitions();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error On PriceChanged");
|
await logger.LogError(ex, "Error On PriceChanged");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Search()
|
private async Task Search()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await LoadModuleDefinitions();
|
await LoadModuleDefinitions();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error On Search");
|
await logger.LogError(ex, "Error On Search");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Reset()
|
private async Task Reset()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_search = "";
|
_search = "";
|
||||||
await LoadModuleDefinitions();
|
await LoadModuleDefinitions();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error On Reset");
|
await logger.LogError(ex, "Error On Reset");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HideModal()
|
private void HideModal()
|
||||||
{
|
{
|
||||||
_productname = "";
|
_productname = "";
|
||||||
_license = "";
|
_packagelicense = "";
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GetPackage(string packageid, string version)
|
private async Task GetPackage(string packageid, string version)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var package = await PackageService.GetPackageAsync(packageid, version);
|
var package = await PackageService.GetPackageAsync(packageid, version);
|
||||||
if (package != null)
|
if (package != null)
|
||||||
{
|
{
|
||||||
_productname = package.Name;
|
_productname = package.Name;
|
||||||
if (!string.IsNullOrEmpty(package.License))
|
_packageid = package.PackageId;
|
||||||
{
|
if (!string.IsNullOrEmpty(package.License))
|
||||||
_license = package.License.Replace("\n", "<br />");
|
{
|
||||||
}
|
_packagelicense = package.License.Replace("\n", "<br />");
|
||||||
_packageid = package.PackageId;
|
}
|
||||||
_version = package.Version;
|
_packageversion = package.Version;
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version);
|
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version);
|
||||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadPackage()
|
private async Task DownloadPackage()
|
||||||
{
|
{
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task InstallModules()
|
private async Task InstallModules()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
|
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Installing Module");
|
await logger.LogError(ex, "Error Installing Modules");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@
|
|||||||
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;
|
||||||
|
|
||||||
@ -151,7 +151,7 @@
|
|||||||
var template = _templates.FirstOrDefault(item => item.Name == _template);
|
var template = _templates.FirstOrDefault(item => item.Name == _template);
|
||||||
_minversion = template.Version;
|
_minversion = template.Version;
|
||||||
}
|
}
|
||||||
GetLocation();
|
GetLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetLocation()
|
private void GetLocation()
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
@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
|
||||||
@ -43,7 +47,13 @@
|
|||||||
<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">
|
<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">
|
||||||
<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">
|
||||||
<input id="owner" class="form-control" @bind="@_owner" disabled />
|
<input id="owner" class="form-control" @bind="@_owner" disabled />
|
||||||
@ -74,7 +84,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</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">
|
||||||
@ -82,107 +98,278 @@
|
|||||||
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" Permissions="@_permissions" @ref="_permissionGrid" />
|
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" Permissions="@_permissions" @ref="_permissionGrid" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
|
||||||
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
<TabPanel Name="Translations" ResourceKey="Translations">
|
||||||
|
@if (_languages != null && _languages.Count > 0)
|
||||||
|
{
|
||||||
|
<Pager Items="@_languages">
|
||||||
|
<Header>
|
||||||
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
|
<th>@Localizer["Code"]</th>
|
||||||
|
<th>@Localizer["Version"]</th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
</Header>
|
||||||
|
<Row>
|
||||||
|
<td>@context.Name</td>
|
||||||
|
<td>@context.Code</td>
|
||||||
|
<td>@context.Version</td>
|
||||||
|
<td>
|
||||||
|
@if (context.IsDefault)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(_packagename + "." + context.Code))>@SharedLocalizer["Download"]</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (UpgradeAvailable(_packagename + "." + context.Code, context.Version))
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadPackage(_packagename + "." + context.Code))>@SharedLocalizer["Upgrade"]</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
|
<button type="button" class="btn btn-success" @onclick="InstallTranslations">@SharedLocalizer["Install"]</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<br />
|
||||||
|
<div class="mx-auto text-center">
|
||||||
|
@Localizer["Search.NoResults"]
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
}
|
||||||
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
@if (_productname != "")
|
||||||
<br />
|
{
|
||||||
<br />
|
<div class="app-actiondialog">
|
||||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
<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(_packagelicense))
|
||||||
|
{
|
||||||
|
@((MarkupString)_packagelicense)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@SharedLocalizer["License Not Specified"]
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadPackage(_packageid))>@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 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 _owner = "";
|
private string _packagename = "";
|
||||||
private string _url = "";
|
private string _owner = "";
|
||||||
private string _contact = "";
|
private string _url = "";
|
||||||
private string _license = "";
|
private string _contact = "";
|
||||||
private string _runtimes = "";
|
private string _license = "";
|
||||||
private string _permissions;
|
private string _runtimes = "";
|
||||||
private string _createdby;
|
private string _permissions;
|
||||||
private DateTime _createdon;
|
private string _createdby;
|
||||||
private string _modifiedby;
|
private DateTime _createdon;
|
||||||
private DateTime _modifiedon;
|
private string _modifiedby;
|
||||||
|
private DateTime _modifiedon;
|
||||||
|
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
private List<Package> _packages;
|
||||||
|
private List<Language> _languages;
|
||||||
|
private string _productname = "";
|
||||||
|
private string _packagelicense = "";
|
||||||
|
private string _packageid = "";
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_moduleDefinitionId = Int32.Parse(PageState.QueryString["id"]);
|
|
||||||
var moduleDefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId);
|
|
||||||
if (moduleDefinition != null)
|
|
||||||
{
|
|
||||||
_name = moduleDefinition.Name;
|
|
||||||
_version = moduleDefinition.Version;
|
|
||||||
_categories = moduleDefinition.Categories;
|
|
||||||
_moduledefinitionname = moduleDefinition.ModuleDefinitionName;
|
|
||||||
_description = moduleDefinition.Description;
|
|
||||||
_owner = moduleDefinition.Owner;
|
|
||||||
_url = moduleDefinition.Url;
|
|
||||||
_contact = moduleDefinition.Contact;
|
|
||||||
_license = moduleDefinition.License;
|
|
||||||
_runtimes = moduleDefinition.Runtimes;
|
|
||||||
_permissions = moduleDefinition.Permissions;
|
|
||||||
_createdby = moduleDefinition.CreatedBy;
|
|
||||||
_createdon = moduleDefinition.CreatedOn;
|
|
||||||
_modifiedby = moduleDefinition.ModifiedBy;
|
|
||||||
_modifiedon = moduleDefinition.ModifiedOn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Loading ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.Module.Load"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveModuleDefinition()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
validated = true;
|
try
|
||||||
var interop = new Interop(JSRuntime);
|
{
|
||||||
if (await interop.FormValid(form))
|
_moduleDefinitionId = Int32.Parse(PageState.QueryString["id"]);
|
||||||
{
|
var moduleDefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId);
|
||||||
try
|
if (moduleDefinition != null)
|
||||||
{
|
{
|
||||||
var moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId);
|
_name = moduleDefinition.Name;
|
||||||
if (moduledefinition.Name != _name)
|
_description = moduleDefinition.Description;
|
||||||
{
|
_categories = moduleDefinition.Categories;
|
||||||
moduledefinition.Name = _name;
|
_moduledefinitionname = moduleDefinition.ModuleDefinitionName;
|
||||||
}
|
_version = moduleDefinition.Version;
|
||||||
if (moduledefinition.Description != _description)
|
_packagename = moduleDefinition.PackageName;
|
||||||
{
|
_owner = moduleDefinition.Owner;
|
||||||
moduledefinition.Description = _description;
|
_url = moduleDefinition.Url;
|
||||||
}
|
_contact = moduleDefinition.Contact;
|
||||||
if (moduledefinition.Categories != _categories)
|
_license = moduleDefinition.License;
|
||||||
{
|
_runtimes = moduleDefinition.Runtimes;
|
||||||
moduledefinition.Categories = _categories;
|
_permissions = moduleDefinition.Permissions;
|
||||||
}
|
_createdby = moduleDefinition.CreatedBy;
|
||||||
moduledefinition.Permissions = _permissionGrid.GetPermissions();
|
_createdon = moduleDefinition.CreatedOn;
|
||||||
await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition);
|
_modifiedby = moduleDefinition.ModifiedBy;
|
||||||
await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition);
|
_modifiedon = moduleDefinition.ModifiedOn;
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
|
||||||
}
|
if (!string.IsNullOrEmpty(_packagename))
|
||||||
catch (Exception ex)
|
{
|
||||||
{
|
_packages = await PackageService.GetPackagesAsync("translation", "", "", _packagename);
|
||||||
await logger.LogError(ex, "Error Saving ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
|
_languages = await LanguageService.GetLanguagesAsync(-1, _packagename);
|
||||||
AddModuleMessage(Localizer["Error.Module.Save"], MessageType.Error);
|
foreach (var package in _packages)
|
||||||
}
|
{
|
||||||
}
|
var code = package.PackageId.Split('.').Last();
|
||||||
else
|
if (!_languages.Any(item => item.Code == code))
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
_languages.Add(new Language { Code = code, Name = CultureInfo.GetCultureInfo(code).DisplayName, Version = package.Version, IsDefault = true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_languages = _languages.OrderBy(item => item.Name).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Module.Load"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveModuleDefinition()
|
||||||
|
{
|
||||||
|
validated = true;
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
if (await interop.FormValid(form))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId);
|
||||||
|
if (moduledefinition.Name != _name)
|
||||||
|
{
|
||||||
|
moduledefinition.Name = _name;
|
||||||
|
}
|
||||||
|
if (moduledefinition.Description != _description)
|
||||||
|
{
|
||||||
|
moduledefinition.Description = _description;
|
||||||
|
}
|
||||||
|
if (moduledefinition.Categories != _categories)
|
||||||
|
{
|
||||||
|
moduledefinition.Categories = _categories;
|
||||||
|
}
|
||||||
|
moduledefinition.Permissions = _permissionGrid.GetPermissions();
|
||||||
|
await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition);
|
||||||
|
await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition);
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Module.Save"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HideModal()
|
||||||
|
{
|
||||||
|
_productname = "";
|
||||||
|
_packagelicense = "";
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool UpgradeAvailable(string packagename, string version)
|
||||||
|
{
|
||||||
|
var upgradeavailable = false;
|
||||||
|
if (_packages != null)
|
||||||
|
{
|
||||||
|
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||||
|
if (package != null)
|
||||||
|
{
|
||||||
|
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return upgradeavailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GetPackage(string packagename)
|
||||||
|
{
|
||||||
|
var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var package = await PackageService.GetPackageAsync(packagename, version);
|
||||||
|
if (package != null)
|
||||||
|
{
|
||||||
|
_productname = package.Name;
|
||||||
|
if (!string.IsNullOrEmpty(package.License))
|
||||||
|
{
|
||||||
|
_packagelicense = package.License.Replace("\n", "<br />");
|
||||||
|
}
|
||||||
|
_packageid = package.PackageId;
|
||||||
|
}
|
||||||
|
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(string packagename)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version;
|
||||||
|
await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder);
|
||||||
|
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", packagename, version);
|
||||||
|
AddModuleMessage(Localizer["Success.Translation.Download"], MessageType.Success);
|
||||||
|
_productname = "";
|
||||||
|
_packagelicense = "";
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Installing Translations");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,32 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
<div class="container">
|
||||||
@((MarkupString)" ")
|
<div class="row mb-3 align-items-center">
|
||||||
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary" />
|
<div class="col-sm-6">
|
||||||
|
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
||||||
<Pager Items="@_moduleDefinitions">
|
@((MarkupString)" ")
|
||||||
|
<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.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,15 +50,15 @@ 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" />
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<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).Count() > 0)
|
@if (context.AssemblyName == Constants.ClientId || PageState.Modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
|
||||||
{
|
{
|
||||||
<span>@SharedLocalizer["Yes"]</span>
|
<span>@SharedLocalizer["Yes"]</span>
|
||||||
}
|
}
|
||||||
@ -50,9 +71,12 @@ else
|
|||||||
@((MarkupString)PurchaseLink(context.PackageName))
|
@((MarkupString)PurchaseLink(context.PackageName))
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if (UpgradeAvailable(context.PackageName, context.Version))
|
@{
|
||||||
|
var version = UpgradeAvailable(context.PackageName, context.Version);
|
||||||
|
}
|
||||||
|
@if (version != context.Version)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.PackageName, context.Version))>@SharedLocalizer["Upgrade"]</button>
|
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.PackageName, version))>@SharedLocalizer["Upgrade"]</button>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</Row>
|
</Row>
|
||||||
@ -60,92 +84,99 @@ 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;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_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)
|
||||||
if (_moduleDefinitions == null)
|
{
|
||||||
{
|
if (_moduleDefinitions == null)
|
||||||
await logger.LogError(ex, "Error Loading Modules {Error}", ex.Message);
|
{
|
||||||
AddModuleMessage(Localizer["Error.Module.Load"], MessageType.Error);
|
await logger.LogError(ex, "Error Loading Modules {Error}", ex.Message);
|
||||||
}
|
AddModuleMessage(Localizer["Error.Module.Load"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private string PurchaseLink(string packagename)
|
private string PurchaseLink(string packagename)
|
||||||
{
|
{
|
||||||
string link = "";
|
string link = "";
|
||||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||||
{
|
{
|
||||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||||
if (package != null)
|
if (package != null)
|
||||||
{
|
{
|
||||||
if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date)
|
if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date)
|
||||||
{
|
{
|
||||||
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span>";
|
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span>";
|
||||||
if (!string.IsNullOrEmpty(package.PaymentUrl))
|
if (!string.IsNullOrEmpty(package.PaymentUrl))
|
||||||
{
|
{
|
||||||
link += " <a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + SharedLocalizer["Extend"] + "</a>";
|
link += " <a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + SharedLocalizer["Extend"] + "</a>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UpgradeAvailable(string packagename, string version)
|
private string UpgradeAvailable(string packagename, string version)
|
||||||
{
|
{
|
||||||
var upgradeavailable = false;
|
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
{
|
||||||
{
|
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
if (package != null && Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
|
||||||
if (package != null)
|
{
|
||||||
{
|
return package.Version;
|
||||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
}
|
||||||
}
|
}
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
private async Task DownloadModule(string packagename, string version)
|
||||||
return upgradeavailable;
|
{
|
||||||
}
|
try
|
||||||
|
{
|
||||||
|
await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder);
|
||||||
|
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version);
|
||||||
|
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
|
||||||
|
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", packagename, version, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task DownloadModule(string packagename, string version)
|
private async Task DeleteModule(ModuleDefinition moduleDefinition)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder);
|
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
|
||||||
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version);
|
AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success);
|
||||||
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
|
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
catch (Exception ex)
|
{
|
||||||
{
|
await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message);
|
||||||
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", packagename, version, ex.Message);
|
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DeleteModule(ModuleDefinition moduleDefinition)
|
private void CategoryChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
_category = (string)e.Value;
|
||||||
{
|
StateHasChanged();
|
||||||
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
|
}
|
||||||
AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success);
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -90,116 +90,125 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
public override string Title => "Module Settings";
|
public override string Title => "Module Settings";
|
||||||
|
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private List<Theme> _themes;
|
private List<Theme> _themes;
|
||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
private string _title;
|
private string _title;
|
||||||
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 string _permissions = null;
|
||||||
private string _pageId;
|
private string _pageId;
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
private Type _moduleSettingsType;
|
private Type _moduleSettingsType;
|
||||||
private object _moduleSettings;
|
private object _moduleSettings;
|
||||||
private string _moduleSettingsTitle = "Module Settings";
|
private string _moduleSettingsTitle = "Module Settings";
|
||||||
private RenderFragment ModuleSettingsComponent { get; set; }
|
private RenderFragment ModuleSettingsComponent { get; set; }
|
||||||
private Type _containerSettingsType;
|
private Type _containerSettingsType;
|
||||||
private object _containerSettings;
|
private object _containerSettings;
|
||||||
private RenderFragment ContainerSettingsComponent { get; set; }
|
private RenderFragment ContainerSettingsComponent { get; set; }
|
||||||
private string createdby;
|
private string createdby;
|
||||||
private DateTime createdon;
|
private DateTime createdon;
|
||||||
private string modifiedby;
|
private string modifiedby;
|
||||||
private DateTime modifiedon;
|
private DateTime modifiedon;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
_title = ModuleState.Title;
|
_title = ModuleState.Title;
|
||||||
_themes = await ThemeService.GetThemesAsync();
|
_themes = await ThemeService.GetThemesAsync();
|
||||||
_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.Permissions;
|
||||||
_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 (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
if (ModuleState.ModuleDefinition != null)
|
||||||
{
|
{
|
||||||
// module settings type explicitly declared in IModule interface
|
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
||||||
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module )
|
|
||||||
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
|
|
||||||
}
|
|
||||||
if (_moduleSettingsType != null)
|
|
||||||
{
|
|
||||||
var moduleobject = Activator.CreateInstance(_moduleSettingsType) as IModuleControl;
|
|
||||||
if (!string.IsNullOrEmpty(moduleobject.Title))
|
|
||||||
{
|
|
||||||
_moduleSettingsTitle = moduleobject.Title;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleSettingsComponent = builder =>
|
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
||||||
{
|
{
|
||||||
builder.OpenComponent(0, _moduleSettingsType);
|
// module settings type explicitly declared in IModule interface
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
|
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType);
|
||||||
builder.CloseComponent();
|
}
|
||||||
};
|
else
|
||||||
}
|
{
|
||||||
|
// legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module )
|
||||||
|
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
|
||||||
|
}
|
||||||
|
if (_moduleSettingsType != null)
|
||||||
|
{
|
||||||
|
var moduleobject = Activator.CreateInstance(_moduleSettingsType) as IModuleControl;
|
||||||
|
if (!string.IsNullOrEmpty(moduleobject.Title))
|
||||||
|
{
|
||||||
|
_moduleSettingsTitle = moduleobject.Title;
|
||||||
|
}
|
||||||
|
|
||||||
var theme = _themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
|
ModuleSettingsComponent = builder =>
|
||||||
if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType))
|
{
|
||||||
{
|
builder.OpenComponent(0, _moduleSettingsType);
|
||||||
_containerSettingsType = Type.GetType(theme.ContainerSettingsType);
|
builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
|
||||||
if (_containerSettingsType != null)
|
builder.CloseComponent();
|
||||||
{
|
};
|
||||||
ContainerSettingsComponent = builder =>
|
}
|
||||||
{
|
}
|
||||||
builder.OpenComponent(0, _containerSettingsType);
|
else
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); });
|
{
|
||||||
builder.CloseComponent();
|
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], ModuleState.ModuleDefinitionName), MessageType.Error);
|
||||||
};
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveModule()
|
var theme = _themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
|
||||||
{
|
if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType))
|
||||||
validated = true;
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
_containerSettingsType = Type.GetType(theme.ContainerSettingsType);
|
||||||
if (await interop.FormValid(form))
|
if (_containerSettingsType != null)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(_title))
|
ContainerSettingsComponent = builder =>
|
||||||
{
|
{
|
||||||
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
builder.OpenComponent(0, _containerSettingsType);
|
||||||
pagemodule.PageId = int.Parse(_pageId);
|
builder.AddComponentReferenceCapture(1, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); });
|
||||||
pagemodule.Title = _title;
|
builder.CloseComponent();
|
||||||
pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
|
};
|
||||||
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
|
}
|
||||||
{
|
}
|
||||||
pagemodule.ContainerType = string.Empty;
|
}
|
||||||
}
|
|
||||||
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType)
|
|
||||||
{
|
|
||||||
pagemodule.ContainerType = string.Empty;
|
|
||||||
}
|
|
||||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
|
||||||
|
|
||||||
var module = ModuleState;
|
private async Task SaveModule()
|
||||||
module.AllPages = bool.Parse(_allPages);
|
{
|
||||||
module.Permissions = _permissionGrid.GetPermissions();
|
validated = true;
|
||||||
await ModuleService.UpdateModuleAsync(module);
|
var interop = new Interop(JSRuntime);
|
||||||
|
if (await interop.FormValid(form))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_title))
|
||||||
|
{
|
||||||
|
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||||
|
pagemodule.PageId = int.Parse(_pageId);
|
||||||
|
pagemodule.Title = _title;
|
||||||
|
pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
|
||||||
|
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
|
||||||
|
{
|
||||||
|
pagemodule.ContainerType = string.Empty;
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType)
|
||||||
|
{
|
||||||
|
pagemodule.ContainerType = string.Empty;
|
||||||
|
}
|
||||||
|
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||||
|
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||||
|
|
||||||
|
var module = ModuleState;
|
||||||
|
module.AllPages = bool.Parse(_allPages);
|
||||||
|
module.PageModuleId = ModuleState.PageModuleId;
|
||||||
|
module.Permissions = _permissionGrid.GetPermissions();
|
||||||
|
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>
|
||||||
}
|
}
|
||||||
@ -95,6 +95,12 @@
|
|||||||
<input id="title" class="form-control" @bind="@_title" />
|
<input id="title" class="form-control" @bind="@_title" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="meta" HelpText="Optionally enter meta tags (in exactly the form you want them to be included in the page output)." ResourceKey="Meta">Meta: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="meta" class="form-control" @bind="@_meta" 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="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -156,220 +162,225 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
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 _path = string.Empty;
|
private string _path = string.Empty;
|
||||||
private string _parentid = "-1";
|
private string _parentid = "-1";
|
||||||
private string _insert = ">>";
|
private string _insert = ">>";
|
||||||
private List<Page> _children;
|
private List<Page> _children;
|
||||||
private int _childid = -1;
|
private int _childid = -1;
|
||||||
private string _isnavigation = "True";
|
private string _isnavigation = "True";
|
||||||
private string _isclickable = "True";
|
private string _isclickable = "True";
|
||||||
private string _url;
|
private string _url;
|
||||||
private string _ispersonalizable = "False";
|
private string _ispersonalizable = "False";
|
||||||
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 = string.Empty;
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
private Type _themeSettingsType;
|
private Type _themeSettingsType;
|
||||||
private object _themeSettings;
|
private object _themeSettings;
|
||||||
private RenderFragment ThemeSettingsComponent { get; set; }
|
private RenderFragment ThemeSettingsComponent { get; set; }
|
||||||
private bool _refresh = false;
|
private bool _refresh = false;
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_themeList = await ThemeService.GetThemesAsync();
|
_themeList = await ThemeService.GetThemesAsync();
|
||||||
_themes = ThemeService.GetThemeControls(_themeList);
|
_themes = ThemeService.GetThemeControls(_themeList);
|
||||||
_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;
|
||||||
_permissions = string.Empty;
|
ThemeSettings();
|
||||||
ThemeSettings();
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
catch (Exception ex)
|
{
|
||||||
{
|
await logger.LogError(ex, "Error Initializing Page {Error}", ex.Message);
|
||||||
await logger.LogError(ex, "Error Initializing Page {Error}", ex.Message);
|
AddModuleMessage(Localizer["Error.Page.Initialize"], MessageType.Error);
|
||||||
AddModuleMessage(Localizer["Error.Page.Initialize"], MessageType.Error);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private async void ParentChanged(ChangeEventArgs e)
|
private async void ParentChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_parentid = (string)e.Value;
|
_parentid = (string)e.Value;
|
||||||
_children = new List<Page>();
|
_children = new List<Page>();
|
||||||
if (_parentid == "-1")
|
if (_parentid == "-1")
|
||||||
{
|
{
|
||||||
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.Permissions))
|
||||||
{
|
{
|
||||||
_children.Add(p);
|
_children.Add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
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.Permissions))
|
||||||
{
|
{
|
||||||
_children.Add(p);
|
_children.Add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ThemeChanged(ChangeEventArgs e)
|
private async void ThemeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_themetype = (string)e.Value;
|
_themetype = (string)e.Value;
|
||||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||||
_containertype = "-";
|
_containertype = "-";
|
||||||
ThemeSettings();
|
ThemeSettings();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
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 Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThemeSettings()
|
private void ThemeSettings()
|
||||||
{
|
{
|
||||||
_themeSettingsType = null;
|
_themeSettingsType = null;
|
||||||
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
||||||
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
||||||
{
|
{
|
||||||
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
||||||
if (_themeSettingsType != null)
|
if (_themeSettingsType != null)
|
||||||
{
|
{
|
||||||
ThemeSettingsComponent = builder =>
|
ThemeSettingsComponent = builder =>
|
||||||
{
|
{
|
||||||
builder.OpenComponent(0, _themeSettingsType);
|
builder.OpenComponent(0, _themeSettingsType);
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_refresh = true;
|
_refresh = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SavePage()
|
private async Task SavePage()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
Page page = null;
|
Page page = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
||||||
{
|
{
|
||||||
page = new Page();
|
page = new Page();
|
||||||
page.SiteId = PageState.Page.SiteId;
|
page.SiteId = PageState.Page.SiteId;
|
||||||
page.Name = _name;
|
page.Name = _name;
|
||||||
page.Title = _title;
|
page.Title = _title;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_path))
|
if (string.IsNullOrEmpty(_path))
|
||||||
{
|
{
|
||||||
_path = _name;
|
_path = _name;
|
||||||
}
|
}
|
||||||
if (_path.Contains("/"))
|
if (_path.Contains("/"))
|
||||||
{
|
{
|
||||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
if (_path.EndsWith("/") && _path != "/")
|
||||||
}
|
{
|
||||||
|
_path = _path.Substring(0, _path.Length - 1);
|
||||||
|
}
|
||||||
|
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (_parentid == "-1")
|
if (_parentid == "-1")
|
||||||
{
|
{
|
||||||
page.ParentId = null;
|
page.ParentId = null;
|
||||||
page.Path = Utilities.GetFriendlyUrl(_path);
|
page.Path = Utilities.GetFriendlyUrl(_path);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
page.ParentId = Int32.Parse(_parentid);
|
page.ParentId = Int32.Parse(_parentid);
|
||||||
var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault();
|
var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault();
|
||||||
if (parent.Path == string.Empty)
|
if (parent.Path == string.Empty)
|
||||||
{
|
{
|
||||||
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
{
|
||||||
return;
|
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
||||||
}
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
Page child;
|
Page child;
|
||||||
switch (_insert)
|
switch (_insert)
|
||||||
{
|
{
|
||||||
case "<<":
|
case "<<":
|
||||||
page.Order = 0;
|
page.Order = 0;
|
||||||
break;
|
break;
|
||||||
case "<":
|
case "<":
|
||||||
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
||||||
page.Order = child.Order - 1;
|
page.Order = child.Order - 1;
|
||||||
break;
|
break;
|
||||||
case ">":
|
case ">":
|
||||||
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
||||||
page.Order = child.Order + 1;
|
page.Order = child.Order + 1;
|
||||||
break;
|
break;
|
||||||
case ">>":
|
case ">>":
|
||||||
page.Order = int.MaxValue;
|
page.Order = int.MaxValue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
|
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
|
||||||
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
||||||
page.Url = _url;
|
page.Url = _url;
|
||||||
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
|
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
|
||||||
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
||||||
{
|
{
|
||||||
page.ThemeType = string.Empty;
|
page.ThemeType = string.Empty;
|
||||||
}
|
}
|
||||||
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
|
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
|
||||||
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
||||||
{
|
{
|
||||||
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.Permissions = _permissionGrid.GetPermissions();
|
||||||
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 = await PageService.AddPageAsync(page);
|
page = await PageService.AddPageAsync(page);
|
||||||
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
||||||
@ -413,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)
|
||||||
{
|
{
|
||||||
@ -102,6 +102,12 @@
|
|||||||
<input id="title" class="form-control" @bind="@_title" maxlength="200"/>
|
<input id="title" class="form-control" @bind="@_title" maxlength="200"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="meta" HelpText="Optionally enter meta tags (in exactly the form you want them to be included in the page output)." ResourceKey="Meta">Meta: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="meta" class="form-control" @bind="@_meta" 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="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -195,11 +201,11 @@
|
|||||||
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;
|
||||||
private string _title;
|
private string _title;
|
||||||
|
private string _meta;
|
||||||
private string _path;
|
private string _path;
|
||||||
private string _currentparentid;
|
private string _currentparentid;
|
||||||
private string _parentid = "-1";
|
private string _parentid = "-1";
|
||||||
@ -231,7 +237,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);
|
||||||
@ -243,8 +248,9 @@
|
|||||||
{
|
{
|
||||||
_name = page.Name;
|
_name = page.Name;
|
||||||
_title = page.Title;
|
_title = page.Title;
|
||||||
|
_meta = page.Meta;
|
||||||
_path = page.Path;
|
_path = page.Path;
|
||||||
_pageModules = PageState.Modules.Where(m => m.PageId == page.PageId && m.IsDeleted == false).ToList();
|
_pageModules = PageState.Modules.Where(m => m.PageId == page.PageId).ToList();
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_path))
|
if (string.IsNullOrEmpty(_path))
|
||||||
{
|
{
|
||||||
@ -313,183 +319,198 @@
|
|||||||
_pageModules.RemoveAll(item => item.PageModuleId == pagemodule.PageModuleId);
|
_pageModules.RemoveAll(item => item.PageModuleId == pagemodule.PageModuleId);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
NavigationManager.NavigateTo(NavigationManager.Uri + "&tab=PageModules");
|
NavigationManager.NavigateTo(NavigationManager.Uri + "&tab=PageModules");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Deleting Module {Title} {Error}", module.Title, ex.Message);
|
await logger.LogError(ex, "Error Deleting Module {Title} {Error}", module.Title, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ParentChanged(ChangeEventArgs e)
|
private async void ParentChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_parentid = (string)e.Value;
|
_parentid = (string)e.Value;
|
||||||
_children = new List<Page>();
|
_children = new List<Page>();
|
||||||
if (_parentid == "-1")
|
if (_parentid == "-1")
|
||||||
{
|
{
|
||||||
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.Permissions))
|
||||||
{
|
{
|
||||||
_children.Add(p);
|
_children.Add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
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.Permissions))
|
||||||
{
|
{
|
||||||
_children.Add(p);
|
_children.Add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_parentid == _currentparentid)
|
if (_parentid == _currentparentid)
|
||||||
{
|
{
|
||||||
_insert = "=";
|
_insert = "=";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_insert = ">>";
|
_insert = ">>";
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ThemeChanged(ChangeEventArgs e)
|
private async void ThemeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_themetype = (string)e.Value;
|
_themetype = (string)e.Value;
|
||||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||||
_containertype = "-";
|
_containertype = "-";
|
||||||
ThemeSettings();
|
ThemeSettings();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
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 Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThemeSettings()
|
private void ThemeSettings()
|
||||||
{
|
{
|
||||||
_themeSettingsType = null;
|
_themeSettingsType = null;
|
||||||
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
if (PageState.QueryString.ContainsKey("cp")) // can only be displayed if invoked from Control Panel
|
||||||
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
{
|
||||||
{
|
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
||||||
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
||||||
if (_themeSettingsType != null)
|
{
|
||||||
{
|
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
||||||
ThemeSettingsComponent = builder =>
|
if (_themeSettingsType != null)
|
||||||
{
|
{
|
||||||
builder.OpenComponent(0, _themeSettingsType);
|
ThemeSettingsComponent = builder =>
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
{
|
||||||
builder.CloseComponent();
|
builder.OpenComponent(0, _themeSettingsType);
|
||||||
};
|
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
||||||
}
|
builder.CloseComponent();
|
||||||
_refresh = true;
|
};
|
||||||
}
|
}
|
||||||
}
|
_refresh = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SavePage()
|
private async Task SavePage()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
Page page = null;
|
Page page = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
||||||
{
|
{
|
||||||
page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
|
page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
|
||||||
string currentPath = page.Path;
|
string currentPath = page.Path;
|
||||||
|
|
||||||
page.Name = _name;
|
page.Name = _name;
|
||||||
page.Title = _title;
|
page.Title = _title;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_path))
|
if (string.IsNullOrEmpty(_path))
|
||||||
{
|
{
|
||||||
_path = _name;
|
_path = _name;
|
||||||
}
|
}
|
||||||
if (_path.Contains("/"))
|
if (_path.Contains("/"))
|
||||||
{
|
{
|
||||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
if (_path.EndsWith("/") && _path != "/")
|
||||||
}
|
{
|
||||||
|
_path = _path.Substring(0, _path.Length - 1);
|
||||||
|
}
|
||||||
|
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (_parentid == "-1")
|
if (_parentid == "-1")
|
||||||
{
|
{
|
||||||
page.ParentId = null;
|
page.ParentId = null;
|
||||||
page.Path = Utilities.GetFriendlyUrl(_path);
|
page.Path = Utilities.GetFriendlyUrl(_path);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
page.ParentId = Int32.Parse(_parentid);
|
page.ParentId = Int32.Parse(_parentid);
|
||||||
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == page.ParentId);
|
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == page.ParentId);
|
||||||
if (parent.Path == string.Empty)
|
if (parent.Path == string.Empty)
|
||||||
{
|
{
|
||||||
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
{
|
||||||
return;
|
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_insert != "=")
|
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
||||||
{
|
{
|
||||||
Page child;
|
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
||||||
switch (_insert)
|
return;
|
||||||
{
|
}
|
||||||
case "<<":
|
|
||||||
page.Order = 0;
|
if (_insert != "=")
|
||||||
break;
|
{
|
||||||
case "<":
|
Page child;
|
||||||
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
|
switch (_insert)
|
||||||
if (child != null) page.Order = child.Order - 1;
|
{
|
||||||
break;
|
case "<<":
|
||||||
case ">":
|
page.Order = 0;
|
||||||
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
|
break;
|
||||||
if (child != null) page.Order = child.Order + 1;
|
case "<":
|
||||||
break;
|
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
|
||||||
case ">>":
|
if (child != null) page.Order = child.Order - 1;
|
||||||
page.Order = int.MaxValue;
|
break;
|
||||||
break;
|
case ">":
|
||||||
}
|
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
|
||||||
}
|
if (child != null) page.Order = child.Order + 1;
|
||||||
page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation));
|
break;
|
||||||
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
case ">>":
|
||||||
page.Url = _url;
|
page.Order = int.MaxValue;
|
||||||
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
|
break;
|
||||||
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
}
|
||||||
{
|
}
|
||||||
page.ThemeType = string.Empty;
|
page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation));
|
||||||
}
|
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
||||||
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
|
page.Url = _url;
|
||||||
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
|
||||||
{
|
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
||||||
page.DefaultContainerType = string.Empty;
|
{
|
||||||
}
|
page.ThemeType = string.Empty;
|
||||||
page.Icon = _icon ?? string.Empty;
|
}
|
||||||
page.Permissions = _permissionGrid.GetPermissions();
|
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
|
||||||
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
|
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
||||||
page.UserId = null;
|
{
|
||||||
|
page.DefaultContainerType = string.Empty;
|
||||||
|
}
|
||||||
|
page.Icon = _icon ?? string.Empty;
|
||||||
|
page.Permissions = _permissionGrid.GetPermissions();
|
||||||
|
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
|
||||||
|
page.UserId = null;
|
||||||
|
page.Meta = _meta;
|
||||||
|
|
||||||
page = await PageService.UpdatePageAsync(page);
|
page = await PageService.UpdatePageAsync(page);
|
||||||
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
||||||
@ -555,9 +576,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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<Header>
|
<Header>
|
||||||
<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["Name"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
|
@ -7,75 +7,77 @@
|
|||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<TabStrip>
|
@if (_pages == null || _modules == null)
|
||||||
<TabPanel Name="Pages" ResourceKey="Pages">
|
{
|
||||||
@if (_pages == null)
|
<p><em>@SharedLocalizer["Loading"]</em></p>
|
||||||
{
|
}
|
||||||
<br />
|
else
|
||||||
<p>@Localizer["NoPage.Deleted"]</p>
|
{
|
||||||
}
|
<TabStrip>
|
||||||
else
|
<TabPanel Name="Pages" ResourceKey="Pages">
|
||||||
{
|
@if (!_pages.Where(item => item.IsDeleted).Any())
|
||||||
<Pager Items="@_pages">
|
{
|
||||||
<Header>
|
<br />
|
||||||
<th style="width: 1px;"> </th>
|
<p>@Localizer["NoPage.Deleted"]</p>
|
||||||
<th style="width: 1px;"> </th>
|
}
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
else
|
||||||
<th>@Localizer["DeletedBy"]</th>
|
{
|
||||||
<th>@Localizer["DeletedOn"]</th>
|
<Pager Items="@_pages.Where(item => item.IsDeleted)">
|
||||||
</Header>
|
<Header>
|
||||||
<Row>
|
<th style="width: 1px;"> </th>
|
||||||
<td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-info" title="Restore">Restore</button></td>
|
<th style="width: 1px;"> </th>
|
||||||
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
<td>@context.Name</td>
|
<th>@Localizer["DeletedBy"]</th>
|
||||||
<td>@context.DeletedBy</td>
|
<th>@Localizer["DeletedOn"]</th>
|
||||||
<td>@context.DeletedOn</td>
|
</Header>
|
||||||
</Row>
|
<Row>
|
||||||
</Pager>
|
<td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-success" title="Restore">Restore</button></td>
|
||||||
@if (_pages.Any())
|
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
||||||
{
|
<td>@context.Name</td>
|
||||||
<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" />
|
<td>@context.DeletedBy</td>
|
||||||
}
|
<td>@context.DeletedOn</td>
|
||||||
}
|
</Row>
|
||||||
</TabPanel>
|
</Pager>
|
||||||
<TabPanel Name="Modules" ResourceKey="Modules">
|
<br />
|
||||||
@if (_modules == null)
|
<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 />
|
</TabPanel>
|
||||||
<p>@Localizer["NoModule.Deleted"]</p>
|
<TabPanel Name="Modules" ResourceKey="Modules">
|
||||||
}
|
@if (!_modules.Where(item => item.IsDeleted).Any())
|
||||||
else
|
{
|
||||||
{
|
<br />
|
||||||
<Pager Items="@_modules">
|
<p>@Localizer["NoModule.Deleted"]</p>
|
||||||
<Header>
|
}
|
||||||
<th style="width: 1px;"> </th>
|
else
|
||||||
<th style="width: 1px;"> </th>
|
{
|
||||||
<th>@Localizer["Page"]</th>
|
<Pager Items="@_modules.Where(item => item.IsDeleted)">
|
||||||
<th>@Localizer["Module"]</th>
|
<Header>
|
||||||
<th>@Localizer["DeletedBy"]</th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@Localizer["DeletedOn"]</th>
|
<th style="width: 1px;"> </th>
|
||||||
</Header>
|
<th>@Localizer["Page"]</th>
|
||||||
<Row>
|
<th>@Localizer["Module"]</th>
|
||||||
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-info" title="Restore">@Localizer["Restore"]</button></td>
|
<th>@Localizer["DeletedBy"]</th>
|
||||||
<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>
|
<th>@Localizer["DeletedOn"]</th>
|
||||||
<td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td>
|
</Header>
|
||||||
<td>@context.Title</td>
|
<Row>
|
||||||
<td>@context.DeletedBy</td>
|
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
||||||
<td>@context.DeletedOn</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>
|
||||||
</Row>
|
<td>@_pages.Find(item => item.PageId == context.PageId).Name</td>
|
||||||
</Pager>
|
<td>@context.Title</td>
|
||||||
@if (_modules.Any())
|
<td>@context.DeletedBy</td>
|
||||||
{
|
<td>@context.DeletedOn</td>
|
||||||
<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" />
|
</Row>
|
||||||
}
|
</Pager>
|
||||||
|
<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" />
|
||||||
</TabPanel>
|
}
|
||||||
</TabStrip>
|
</TabPanel>
|
||||||
|
</TabStrip>
|
||||||
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Page> _pages;
|
private List<Page> _pages;
|
||||||
private List<Module> _modules;
|
private List<Module> _modules;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
@ -95,10 +97,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 +139,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 +148,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 +156,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,9 +183,8 @@
|
|||||||
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);
|
|
||||||
|
|
||||||
|
// check if there are any remaining module instances in the site
|
||||||
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
|
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
|
||||||
{
|
{
|
||||||
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
||||||
@ -204,12 +205,12 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (Module module in _modules)
|
ModuleInstance.ShowProgressIndicator();
|
||||||
|
foreach (Module module in _modules.Where(item => item.IsDeleted))
|
||||||
{
|
{
|
||||||
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);
|
|
||||||
|
|
||||||
|
// check if there are any remaining module instances in the site
|
||||||
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
|
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
|
||||||
{
|
{
|
||||||
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
||||||
@ -218,12 +219,14 @@
|
|||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,19 +27,25 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="Please choose a sufficiently secure password and enter it here" ResourceKey="Password"></Label>
|
<Label Class="col-sm-3" For="password" HelpText="Please choose a sufficiently secure password and enter it here" ResourceKey="Password"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="password" type="password" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</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="confirm" HelpText="Enter your password again to confirm it matches the value entered above" ResourceKey="Confirm"></Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="Enter your password again to confirm it matches the value entered above" ResourceKey="Confirm"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="confirm" type="password" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</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="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
|
<Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="email" class="form-control" @bind="@_email" maxlength="256" required />
|
<input id="email" class="form-control" @bind="@_email" maxlength="256" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -62,15 +68,22 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _username = string.Empty;
|
private string _username = string.Empty;
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private string _password = string.Empty;
|
private string _password = string.Empty;
|
||||||
private string _confirm = string.Empty;
|
private string _passwordtype = "password";
|
||||||
private string _email = string.Empty;
|
private string _togglepassword = string.Empty;
|
||||||
private string _displayname = string.Empty;
|
private string _confirm = string.Empty;
|
||||||
|
private string _email = string.Empty;
|
||||||
|
private string _displayname = string.Empty;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Register()
|
private async Task Register()
|
||||||
{
|
{
|
||||||
@ -90,9 +103,10 @@ else
|
|||||||
{
|
{
|
||||||
SiteId = PageState.Site.SiteId,
|
SiteId = PageState.Site.SiteId,
|
||||||
Username = _username,
|
Username = _username,
|
||||||
DisplayName = (_displayname == string.Empty ? _username : _displayname),
|
Password = _password,
|
||||||
Email = _email,
|
Email = _email,
|
||||||
Password = _password
|
DisplayName = (_displayname == string.Empty ? _username : _displayname),
|
||||||
|
PhotoFileId = null
|
||||||
};
|
};
|
||||||
user = await UserService.AddUserAsync(user);
|
user = await UserService.AddUserAsync(user);
|
||||||
|
|
||||||
@ -133,4 +147,18 @@ else
|
|||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,41 +7,59 @@
|
|||||||
|
|
||||||
<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="form-group">
|
<div class="row mb-1 align-items-center">
|
||||||
<label for="Username" class="control-label">@SharedLocalizer["Username"] </label>
|
<Label Class="col-sm-3" For="username" HelpText="Your username will be populated from the link you received in the password reset notification" ResourceKey="Username">Username: </Label>
|
||||||
<input type="text" class="form-control" placeholder="Username" @bind="@_username" readonly id="Username" />
|
<div class="col-sm-9">
|
||||||
|
<input id="username" type="text" class="form-control" @bind="@_username" readonly />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="row mb-1 align-items-center">
|
||||||
<label for="Password" class="control-label">@SharedLocalizer["Password"] </label>
|
<Label Class="col-sm-3" For="password" HelpText="The new password. It must satisfy complexity rules for the site." ResourceKey="Password">Password: </Label>
|
||||||
<input type="password" class="form-control" placeholder="Password" @bind="@_password" id="Password" required />
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="row mb-1 align-items-center">
|
||||||
<label for="Confirm" class="control-label">@Localizer["Password.Confirm"] </label>
|
<Label Class="col-sm-3" For="confirm" HelpText="Enter the password again. It must exactly match the password entered above." ResourceKey="Confirm">Confirm: </Label>
|
||||||
<input type="password" class="form-control" placeholder="Password" @bind="@_confirm" id="Confirm" required />
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-primary" @onclick="Reset">@Localizer["Password.Reset"]</button>
|
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="Reset">@Localizer["Password.Reset"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private string _username = string.Empty;
|
private string _username = string.Empty;
|
||||||
private string _password = string.Empty;
|
private string _password = string.Empty;
|
||||||
private string _confirm = string.Empty;
|
private string _passwordtype = "password";
|
||||||
|
private string _togglepassword = string.Empty;
|
||||||
|
private string _confirm = string.Empty;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
if (PageState.QueryString.ContainsKey("name") && PageState.QueryString.ContainsKey("token"))
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
|
if (PageState.QueryString.ContainsKey("name") && PageState.QueryString.ContainsKey("token"))
|
||||||
{
|
{
|
||||||
_username = PageState.QueryString["name"];
|
_username = PageState.QueryString["name"];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
await logger.LogError(LogFunction.Security, "Invalid Attempt To Access User Password Reset");
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl("")); // home page
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,4 +120,18 @@
|
|||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -53,12 +53,14 @@ else
|
|||||||
<Pager Items="@userroles">
|
<Pager Items="@userroles">
|
||||||
<Header>
|
<Header>
|
||||||
<th>@Localizer["Users"]</th>
|
<th>@Localizer["Users"]</th>
|
||||||
|
<th>@SharedLocalizer["Username"]</th>
|
||||||
<th>@Localizer["Effective"]</th>
|
<th>@Localizer["Effective"]</th>
|
||||||
<th>@Localizer["Expiry"]</th>
|
<th>@Localizer["Expiry"]</th>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td>@context.User.DisplayName</td>
|
<td>@context.User.DisplayName</td>
|
||||||
|
<td>@context.User.Username</td>
|
||||||
<td>@context.EffectiveDate</td>
|
<td>@context.EffectiveDate</td>
|
||||||
<td>@context.ExpiryDate</td>
|
<td>@context.ExpiryDate</td>
|
||||||
<td>
|
<td>
|
||||||
@ -93,11 +95,7 @@ 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);
|
users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
||||||
users = users
|
|
||||||
.Where(u => u.Role.Name == RoleNames.Registered)
|
|
||||||
.OrderBy(u => u.User.DisplayName)
|
|
||||||
.ToList();
|
|
||||||
await GetUserRoles();
|
await GetUserRoles();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -111,8 +109,7 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, name);
|
||||||
userroles = userroles.Where(item => item.RoleId == roleid).ToList();
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Admin.Site
|
@namespace Oqtane.Modules.Admin.Site
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
|
@using System.Text.RegularExpressions
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject ISiteService SiteService
|
@inject ISiteService SiteService
|
||||||
@inject ITenantService TenantService
|
@inject ITenantService TenantService
|
||||||
@ -22,81 +23,79 @@
|
|||||||
</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="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="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
|
||||||
{
|
|
||||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" readonly></textarea>
|
|
||||||
}
|
|
||||||
</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="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Is Deleted? </Label>
|
<Label Class="col-sm-3" For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="isDeleted" class="form-select" @bind="@_isdeleted" required>
|
<FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
</div>
|
||||||
<option value="False">@SharedLocalizer["No"]</option>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="defaultTheme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
||||||
|
<option value="-"><@Localizer["Theme.Select"]></option>
|
||||||
|
@foreach (var theme in _themes)
|
||||||
|
{
|
||||||
|
<option value="@theme.TypeName">@theme.Name</option>
|
||||||
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="row mb-1 align-items-center">
|
||||||
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
|
<Label Class="col-sm-3" For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
|
||||||
<div class="container">
|
<div class="col-sm-9">
|
||||||
<div class="row mb-1 align-items-center">
|
<select id="defaultContainer" class="form-select" @bind="@_containertype" required>
|
||||||
<Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
|
<option value="-"><@Localizer["Container.Select"]></option>
|
||||||
<div class="col-sm-9">
|
@foreach (var container in _containers)
|
||||||
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
|
{
|
||||||
</div>
|
<option value="@container.TypeName">@container.Name</option>
|
||||||
</div>
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
</select>
|
||||||
<Label Class="col-sm-3" For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<select id="defaultTheme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
|
||||||
<option value="-"><@Localizer["Theme.Select"]></option>
|
|
||||||
@foreach (var theme in _themes)
|
|
||||||
{
|
|
||||||
<option value="@theme.TypeName">@theme.Name</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<select id="defaultContainer" class="form-select" @bind="@_containertype" required>
|
|
||||||
<option value="-"><@Localizer["Container.Select"]></option>
|
|
||||||
@foreach (var container in _containers)
|
|
||||||
{
|
|
||||||
<option value="@container.TypeName">@container.Name</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<select id="defaultAdminContainer" class="form-select" @bind="@_admincontainertype" required>
|
|
||||||
<option value="-"><@Localizer["Container.Select"]></option>
|
|
||||||
<option value="@Constants.DefaultAdminContainer"><@Localizer["DefaultAdminContainer"]></option>
|
|
||||||
@foreach (var container in _containers)
|
|
||||||
{
|
|
||||||
<option value="@container.TypeName">@container.Name</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="defaultAdminContainer" class="form-select" @bind="@_admincontainertype" required>
|
||||||
|
<option value="-"><@Localizer["Container.Select"]></option>
|
||||||
|
<option value="@Constants.DefaultAdminContainer"><@Localizer["DefaultAdminContainer"]></option>
|
||||||
|
@foreach (var container in _containers)
|
||||||
|
{
|
||||||
|
<option value="@container.TypeName">@container.Name</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</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.Permissions, PermissionNames.View, RoleNames.Everyone))
|
||||||
|
{
|
||||||
|
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Deleted? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="isDeleted" class="form-select" @bind="@_isdeleted" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</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">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -136,7 +135,10 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="Enter the password for your SMTP account" ResourceKey="SmtpPassword">Password: </Label>
|
<Label Class="col-sm-3" For="password" HelpText="Enter the password for your SMTP account" ResourceKey="SmtpPassword">Password: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="password" type="password" class="form-control" @bind="@_smtppassword" />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_smtppasswordtype" class="form-control" @bind="@_smtppassword" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleSMTPPassword">@_togglesmtppassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -145,6 +147,12 @@
|
|||||||
<input id="sender" class="form-control" @bind="@_smtpsender" />
|
<input id="sender" class="form-control" @bind="@_smtpsender" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<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>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="retention" class="form-control" @bind="@_retention" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
</div>
|
</div>
|
||||||
@ -174,8 +182,59 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
@if (_aliases != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
|
<Section Name="Aliases" Heading="Aliases" ResourceKey="Aliases">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="aliases" HelpText="The list of aliases for this site" ResourceKey="Aliases">Aliases: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="AddAlias">@SharedLocalizer["Add"]</button>
|
||||||
|
<Pager Items="@_aliases">
|
||||||
|
<Header>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th>@Localizer["AliasName"]</th>
|
||||||
|
<th>@Localizer["AliasDefault"]</th>
|
||||||
|
</Header>
|
||||||
|
<Row>
|
||||||
|
@if (context.AliasId != _aliasid)
|
||||||
|
{
|
||||||
|
<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>
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
<Section Name="Hosting" Heading="Hosting Model" ResourceKey="Hosting">
|
<Section Name="Hosting" Heading="Hosting Model" ResourceKey="Hosting">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -184,6 +243,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>
|
||||||
@ -238,8 +298,10 @@
|
|||||||
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 string _name = string.Empty;
|
private string _name = string.Empty;
|
||||||
private List<Alias> _aliasList;
|
private List<Alias> _aliases;
|
||||||
private string _urls = string.Empty;
|
private int _aliasid = -1;
|
||||||
|
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;
|
||||||
@ -249,12 +311,16 @@
|
|||||||
private string _themetype = "-";
|
private string _themetype = "-";
|
||||||
private string _containertype = "-";
|
private string _containertype = "-";
|
||||||
private string _admincontainertype = "-";
|
private string _admincontainertype = "-";
|
||||||
|
private string _homepageid = "-";
|
||||||
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";
|
||||||
private string _smtpusername = string.Empty;
|
private string _smtpusername = string.Empty;
|
||||||
private string _smtppassword = string.Empty;
|
private string _smtppassword = string.Empty;
|
||||||
|
private string _smtppasswordtype = "password";
|
||||||
|
private string _togglesmtppassword = string.Empty;
|
||||||
private string _smtpsender = string.Empty;
|
private string _smtpsender = string.Empty;
|
||||||
|
private string _retention = string.Empty;
|
||||||
private string _pwaisenabled;
|
private string _pwaisenabled;
|
||||||
private int _pwaappiconfileid = -1;
|
private int _pwaappiconfileid = -1;
|
||||||
private FileManager _pwaappiconfilemanager;
|
private FileManager _pwaappiconfilemanager;
|
||||||
@ -286,16 +352,7 @@
|
|||||||
_prerender = site.RenderMode.Replace(_runtime, "");
|
_prerender = site.RenderMode.Replace(_runtime, "");
|
||||||
_isdeleted = site.IsDeleted.ToString();
|
_isdeleted = site.IsDeleted.ToString();
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
await GetAliases();
|
||||||
{
|
|
||||||
_aliasList = await AliasService.GetAliasesAsync();
|
|
||||||
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
|
|
||||||
{
|
|
||||||
_urls += alias.Name + ",";
|
|
||||||
}
|
|
||||||
_urls = _urls.Substring(0, _urls.Length - 1);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (site.LogoFileId != null)
|
if (site.LogoFileId != null)
|
||||||
{
|
{
|
||||||
@ -313,6 +370,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)
|
||||||
{
|
{
|
||||||
@ -322,14 +384,16 @@
|
|||||||
{
|
{
|
||||||
_pwasplashiconfileid = site.PwaSplashIconFileId.Value;
|
_pwasplashiconfileid = site.PwaSplashIconFileId.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||||
_smtphost = SettingService.GetSetting(settings, "SMTPHost", string.Empty);
|
_smtphost = SettingService.GetSetting(settings, "SMTPHost", string.Empty);
|
||||||
_smtpport = SettingService.GetSetting(settings, "SMTPPort", string.Empty);
|
_smtpport = SettingService.GetSetting(settings, "SMTPPort", string.Empty);
|
||||||
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
|
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
|
||||||
_smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty);
|
_smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty);
|
||||||
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
|
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
|
||||||
|
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||||
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
||||||
|
_retention = SettingService.GetSetting(settings, "NotificationRetention", "30");
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
@ -393,213 +457,268 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
|
if (_name != string.Empty && _themetype != "-" && _containertype != "-")
|
||||||
{
|
{
|
||||||
var unique = true;
|
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (site != null)
|
||||||
{
|
{
|
||||||
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
bool refresh = false;
|
||||||
|
bool reload = false;
|
||||||
|
|
||||||
|
site.Name = _name;
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
if (_aliasList.Exists(item => item.Name == name && item.SiteId != PageState.Alias.SiteId && item.TenantId != PageState.Alias.TenantId))
|
if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender)
|
||||||
{
|
{
|
||||||
unique = false;
|
site.Runtime = _runtime;
|
||||||
|
site.RenderMode = _runtime + _prerender;
|
||||||
|
reload = true; // needs to be reloaded on server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
|
||||||
|
|
||||||
if (unique)
|
site.LogoFileId = null;
|
||||||
{
|
var logofileid = _logofilemanager.GetFileId();
|
||||||
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
if (logofileid != -1)
|
||||||
if (site != null)
|
|
||||||
{
|
{
|
||||||
bool refresh = false;
|
site.LogoFileId = logofileid;
|
||||||
bool reload = false;
|
}
|
||||||
|
int? faviconFieldId = _faviconfilemanager.GetFileId();
|
||||||
|
if (faviconFieldId == -1) faviconFieldId = null;
|
||||||
|
if (site.FaviconFileId != faviconFieldId)
|
||||||
|
{
|
||||||
|
site.FaviconFileId = faviconFieldId;
|
||||||
|
reload = true; // needs to be reloaded on server
|
||||||
|
}
|
||||||
|
if (site.DefaultThemeType != _themetype)
|
||||||
|
{
|
||||||
|
site.DefaultThemeType = _themetype;
|
||||||
|
refresh = true; // needs to be refreshed on client
|
||||||
|
}
|
||||||
|
if (site.DefaultContainerType != _containertype)
|
||||||
|
{
|
||||||
|
site.DefaultContainerType = _containertype;
|
||||||
|
refresh = true; // needs to be refreshed on client
|
||||||
|
}
|
||||||
|
site.AdminContainerType = _admincontainertype;
|
||||||
|
site.HomePageId = (_homepageid != "-" ? int.Parse(_homepageid) : null);
|
||||||
|
|
||||||
site.Name = _name;
|
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
{
|
||||||
{
|
site.PwaIsEnabled = Boolean.Parse(_pwaisenabled);
|
||||||
if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender)
|
reload = true; // needs to be reloaded on server
|
||||||
{
|
}
|
||||||
site.Runtime = _runtime;
|
int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
|
||||||
site.RenderMode = _runtime + _prerender;
|
if (pwaappiconfileid == -1) pwaappiconfileid = null;
|
||||||
refresh = true;
|
if (site.PwaAppIconFileId != pwaappiconfileid)
|
||||||
reload = true; // needs to be reloaded on server
|
{
|
||||||
}
|
site.PwaAppIconFileId = pwaappiconfileid;
|
||||||
}
|
reload = true; // needs to be reloaded on server
|
||||||
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
|
}
|
||||||
|
int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
|
||||||
|
if (pwasplashiconfileid == -1) pwasplashiconfileid = null;
|
||||||
|
if (site.PwaSplashIconFileId != pwasplashiconfileid)
|
||||||
|
{
|
||||||
|
site.PwaSplashIconFileId = pwasplashiconfileid;
|
||||||
|
reload = true; // needs to be reloaded on server
|
||||||
|
}
|
||||||
|
|
||||||
site.LogoFileId = null;
|
site = await SiteService.UpdateSiteAsync(site);
|
||||||
var logofileid = _logofilemanager.GetFileId();
|
|
||||||
if (logofileid != -1)
|
|
||||||
{
|
|
||||||
site.LogoFileId = logofileid;
|
|
||||||
}
|
|
||||||
int? faviconFieldId = _faviconfilemanager.GetFileId();
|
|
||||||
if (faviconFieldId == -1) faviconFieldId = null;
|
|
||||||
if (site.FaviconFileId != faviconFieldId)
|
|
||||||
{
|
|
||||||
site.FaviconFileId = faviconFieldId;
|
|
||||||
reload = true; // needs to be reloaded on server
|
|
||||||
}
|
|
||||||
if (site.DefaultThemeType != _themetype)
|
|
||||||
{
|
|
||||||
site.DefaultThemeType = _themetype;
|
|
||||||
refresh = true; // needs to be refreshed on client
|
|
||||||
}
|
|
||||||
if (site.DefaultContainerType != _containertype)
|
|
||||||
{
|
|
||||||
site.DefaultContainerType = _containertype;
|
|
||||||
refresh = true; // needs to be refreshed on client
|
|
||||||
}
|
|
||||||
site.AdminContainerType = _admincontainertype;
|
|
||||||
|
|
||||||
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||||
{
|
settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
||||||
site.PwaIsEnabled = Boolean.Parse(_pwaisenabled);
|
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||||
reload = true; // needs to be reloaded on server
|
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||||
}
|
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||||
int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
|
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||||
if (pwaappiconfileid == -1) pwaappiconfileid = null;
|
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||||
if (site.PwaAppIconFileId != pwaappiconfileid)
|
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
||||||
{
|
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||||
site.PwaAppIconFileId = pwaappiconfileid;
|
|
||||||
reload = true; // needs to be reloaded on server
|
|
||||||
}
|
|
||||||
int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
|
|
||||||
if (pwasplashiconfileid == -1) pwasplashiconfileid = null;
|
|
||||||
if (site.PwaSplashIconFileId != pwasplashiconfileid)
|
|
||||||
{
|
|
||||||
site.PwaSplashIconFileId = pwasplashiconfileid;
|
|
||||||
reload = true; // needs to be reloaded on server
|
|
||||||
}
|
|
||||||
|
|
||||||
site = await SiteService.UpdateSiteAsync(site);
|
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||||
|
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
if (refresh || reload)
|
||||||
SettingService.SetSetting(settings, "SMTPHost", _smtphost);
|
{
|
||||||
SettingService.SetSetting(settings, "SMTPPort", _smtpport);
|
NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload
|
||||||
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl);
|
}
|
||||||
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername);
|
else
|
||||||
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword);
|
{
|
||||||
SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
|
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
await interop.ScrollTo(0, 0, "smooth");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.SaveSite"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
private async Task DeleteSite()
|
||||||
{
|
{
|
||||||
var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
try
|
||||||
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
|
{
|
||||||
{
|
var aliases = await AliasService.GetAliasesAsync();
|
||||||
if (!names.Contains(alias.Name))
|
if (aliases.Any(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId))
|
||||||
{
|
{
|
||||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
|
||||||
}
|
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string name in names)
|
foreach (Alias alias in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
|
||||||
{
|
{
|
||||||
if (!_aliasList.Exists(item => item.Name == name))
|
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||||
{
|
}
|
||||||
Alias alias = new Alias();
|
|
||||||
alias.Name = name;
|
|
||||||
alias.TenantId = site.TenantId;
|
|
||||||
alias.SiteId = site.SiteId;
|
|
||||||
await AliasService.AddAliasAsync(alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
var redirect = aliases.First(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId);
|
||||||
|
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + redirect.Name, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.FailAuth.DeleteSite"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Deleting Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.DeleteSite"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (refresh)
|
private async Task SendEmail()
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload
|
if (_smtphost != "" && _smtpport != "" && _smtpsender != "")
|
||||||
}
|
{
|
||||||
else
|
try
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
}
|
SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
||||||
}
|
SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||||
}
|
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||||
else
|
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||||
{
|
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||||
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
|
SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||||
}
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
}
|
await logger.LogInformation("Site SMTP Settings Saved");
|
||||||
else
|
|
||||||
{
|
|
||||||
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.SaveSite"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DeleteSite()
|
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
|
||||||
{
|
AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info);
|
||||||
try
|
var interop = new Interop(JSRuntime);
|
||||||
{
|
await interop.ScrollTo(0, 0, "smooth");
|
||||||
var sites = await SiteService.GetSitesAsync();
|
}
|
||||||
if (sites.Count > 1)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
|
await logger.LogError(ex, "Error Testing SMTP Configuration");
|
||||||
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
|
AddModuleMessage(Localizer["Error.Smtp.TestConfig"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Required.Smtp"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var aliases = await AliasService.GetAliasesAsync();
|
private void ToggleSMTPPassword()
|
||||||
foreach (Alias a in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
|
{
|
||||||
{
|
if (_smtppasswordtype == "password")
|
||||||
await AliasService.DeleteAliasAsync(a.AliasId);
|
{
|
||||||
}
|
_smtppasswordtype = "text";
|
||||||
|
_togglesmtppassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_smtppasswordtype = "password";
|
||||||
|
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl("admin/sites"));
|
private async Task GetAliases()
|
||||||
}
|
{
|
||||||
else
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.FailAuth.DeleteSite"], MessageType.Warning);
|
_aliases = await AliasService.GetAliasesAsync();
|
||||||
}
|
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
}
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Deleting Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.DeleteSite"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SendEmail()
|
private void AddAlias()
|
||||||
{
|
{
|
||||||
if (_smtphost != "" && _smtpport != "" && _smtpsender != "")
|
_aliases.Add(new Alias { AliasId = 0, Name = "", IsDefault = false });
|
||||||
{
|
_aliasid = 0;
|
||||||
try
|
_aliasname = "";
|
||||||
{
|
_defaultalias = "False";
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
StateHasChanged();
|
||||||
SettingService.SetSetting(settings, "SMTPHost", _smtphost);
|
}
|
||||||
SettingService.SetSetting(settings, "SMTPPort", _smtpport);
|
|
||||||
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl);
|
|
||||||
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername);
|
|
||||||
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword);
|
|
||||||
SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
|
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
|
||||||
await logger.LogInformation("Site SMTP Settings Saved");
|
|
||||||
|
|
||||||
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User.DisplayName, PageState.User.Email, PageState.User.DisplayName, PageState.User.Email, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
|
private void EditAlias(Alias alias)
|
||||||
AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info);
|
{
|
||||||
}
|
_aliasid = alias.AliasId;
|
||||||
catch (Exception ex)
|
_aliasname = alias.Name;
|
||||||
{
|
_defaultalias = alias.IsDefault.ToString();
|
||||||
await logger.LogError(ex, "Error Testing SMTP Configuration");
|
StateHasChanged();
|
||||||
AddModuleMessage(Localizer["Error.Smtp.TestConfig"], MessageType.Error);
|
}
|
||||||
}
|
|
||||||
}
|
private async Task DeleteAlias(Alias alias)
|
||||||
else
|
{
|
||||||
{
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
AddModuleMessage(Localizer["Message.required.Smtp"], MessageType.Warning);
|
{
|
||||||
}
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Admin.Sites
|
@namespace Oqtane.Modules.Admin.Sites
|
||||||
@using Oqtane.Interfaces
|
@using Oqtane.Interfaces
|
||||||
|
@using System.Text.RegularExpressions
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject ITenantService TenantService
|
@inject ITenantService TenantService
|
||||||
@ -28,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>
|
||||||
@ -88,7 +89,8 @@ 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>
|
||||||
</select>
|
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -127,25 +129,43 @@ else
|
|||||||
<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 for the tenant" ResourceKey="DatabaseType">Database Type: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
@if (_databases != null)
|
||||||
@foreach (var database in _databases)
|
{
|
||||||
{
|
<div class="input-group">
|
||||||
if (database.IsDefault)
|
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
||||||
{
|
@foreach (var database in _databases)
|
||||||
<option value="@database.Name" selected>@Localizer[@database.Name]</option>
|
{
|
||||||
}
|
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||||
else
|
}
|
||||||
{
|
</select>
|
||||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
@if (!_showConnectionString)
|
||||||
}
|
{
|
||||||
}
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionString"]</button>
|
||||||
</select>
|
}
|
||||||
</div>
|
else
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionParameters"]</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
if (_databaseConfigType != null)
|
@if (!_showConnectionString)
|
||||||
{
|
{
|
||||||
@DatabaseConfigComponent;
|
if (_databaseConfigType != null)
|
||||||
}
|
{
|
||||||
|
@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">String:</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>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -155,7 +175,7 @@ else
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="hostPassword" HelpText="Enter the password of an existing host user" ResourceKey="HostPassword">Host Password:</Label>
|
<Label Class="col-sm-3" For="hostPassword" HelpText="Enter the password of an existing host user" ResourceKey="HostPassword">Host Password:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" required />
|
<input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" autocomplete="new-password" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@ -168,152 +188,171 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
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<ThemeControl> _themes = new List<ThemeControl>();
|
||||||
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
|
private List<SiteTemplate> _siteTemplates;
|
||||||
|
private List<Tenant> _tenants;
|
||||||
|
private string _tenantid = "-";
|
||||||
|
|
||||||
private List<Theme> _themeList;
|
private string _tenantName = string.Empty;
|
||||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
|
||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
|
||||||
private List<SiteTemplate> _siteTemplates;
|
|
||||||
private List<Tenant> _tenants;
|
|
||||||
private string _tenantid = "-";
|
|
||||||
|
|
||||||
private string _tenantName = string.Empty;
|
private string _hostusername = string.Empty;
|
||||||
|
private string _hostpassword = string.Empty;
|
||||||
|
|
||||||
private string _hostusername = string.Empty;
|
private string _name = string.Empty;
|
||||||
private string _hostpassword = string.Empty;
|
private string _urls = string.Empty;
|
||||||
|
private string _themetype = "-";
|
||||||
|
private string _containertype = "-";
|
||||||
|
private string _admincontainertype = "";
|
||||||
|
private string _sitetemplatetype = "-";
|
||||||
|
private string _runtime = "Server";
|
||||||
|
private string _prerender = "Prerendered";
|
||||||
|
|
||||||
private string _name = string.Empty;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
private string _urls = string.Empty;
|
|
||||||
private string _themetype = "-";
|
|
||||||
private string _containertype = "-";
|
|
||||||
private string _admincontainertype = "";
|
|
||||||
private string _sitetemplatetype = "-";
|
|
||||||
private string _runtime = "Server";
|
|
||||||
private string _prerender = "Prerendered";
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
_tenants = await TenantService.GetTenantsAsync();
|
||||||
|
_urls = PageState.Alias.Name;
|
||||||
|
_themeList = await ThemeService.GetThemesAsync();
|
||||||
|
_themes = ThemeService.GetThemeControls(_themeList);
|
||||||
|
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
_databases = await DatabaseService.GetDatabasesAsync();
|
||||||
{
|
if (_databases.Exists(item => item.IsDefault))
|
||||||
_tenants = await TenantService.GetTenantsAsync();
|
{
|
||||||
_urls = PageState.Alias.Name;
|
_databaseName = _databases.Find(item => item.IsDefault).Name;
|
||||||
_themeList = await ThemeService.GetThemesAsync();
|
}
|
||||||
_themes = ThemeService.GetThemeControls(_themeList);
|
else
|
||||||
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
{
|
||||||
_databases = await DatabaseService.GetDatabasesAsync();
|
_databaseName = "LocalDB";
|
||||||
LoadDatabaseConfigComponent();
|
}
|
||||||
}
|
LoadDatabaseConfigComponent();
|
||||||
|
}
|
||||||
|
|
||||||
private void DatabaseChanged(ChangeEventArgs eventArgs)
|
private void DatabaseChanged(ChangeEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_databaseName = (string)eventArgs.Value;
|
_databaseName = (string)eventArgs.Value;
|
||||||
|
_showConnectionString = false;
|
||||||
|
LoadDatabaseConfigComponent();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LoadDatabaseConfigComponent();
|
private void LoadDatabaseConfigComponent()
|
||||||
}
|
{
|
||||||
catch
|
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||||
{
|
if (database != null)
|
||||||
AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error);
|
{
|
||||||
}
|
_databaseConfigType = Type.GetType(database.ControlType);
|
||||||
}
|
DatabaseConfigComponent = builder =>
|
||||||
|
{
|
||||||
|
builder.OpenComponent(0, _databaseConfigType);
|
||||||
|
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
||||||
|
builder.CloseComponent();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void LoadDatabaseConfigComponent()
|
private void TenantChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
_tenantid = (string)e.Value;
|
||||||
if (database != null)
|
if (string.IsNullOrEmpty(_tenantName))
|
||||||
{
|
{
|
||||||
_databaseConfigType = Type.GetType(database.ControlType);
|
_tenantName = _name;
|
||||||
DatabaseConfigComponent = builder =>
|
}
|
||||||
{
|
StateHasChanged();
|
||||||
builder.OpenComponent(0, _databaseConfigType);
|
}
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
|
||||||
builder.CloseComponent();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TenantChanged(ChangeEventArgs e)
|
private async void ThemeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_tenantid = (string)e.Value;
|
try
|
||||||
if (string.IsNullOrEmpty(_tenantName))
|
{
|
||||||
{
|
_themetype = (string)e.Value;
|
||||||
_tenantName = _name;
|
if (_themetype != "-")
|
||||||
}
|
{
|
||||||
StateHasChanged();
|
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_containers = new List<ThemeControl>();
|
||||||
|
}
|
||||||
|
_containertype = "-";
|
||||||
|
_admincontainertype = "";
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Theme.LoadContainers"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async void ThemeChanged(ChangeEventArgs e)
|
private async Task SaveSite()
|
||||||
{
|
{
|
||||||
try
|
validated = true;
|
||||||
{
|
var interop = new Interop(JSRuntime);
|
||||||
_themetype = (string)e.Value;
|
if (await interop.FormValid(form))
|
||||||
if (_themetype != "-")
|
{
|
||||||
{
|
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
|
||||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
{
|
||||||
}
|
_urls = Regex.Replace(_urls, @"\r\n?|\n", ",");
|
||||||
else
|
var duplicates = new List<string>();
|
||||||
{
|
var aliases = await AliasService.GetAliasesAsync();
|
||||||
_containers = new List<ThemeControl>();
|
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
}
|
{
|
||||||
_containertype = "-";
|
if (aliases.Exists(item => item.Name == name))
|
||||||
_admincontainertype = "";
|
{
|
||||||
StateHasChanged();
|
duplicates.Add(name);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
}
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.Theme.LoadContainers"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveSite()
|
if (duplicates.Count == 0)
|
||||||
{
|
{
|
||||||
validated = true;
|
InstallConfig config = new InstallConfig();
|
||||||
var interop = new Interop(JSRuntime);
|
|
||||||
if (await interop.FormValid(form))
|
|
||||||
{
|
|
||||||
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
|
|
||||||
{
|
|
||||||
var duplicates = new List<string>();
|
|
||||||
var aliases = await AliasService.GetAliasesAsync();
|
|
||||||
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
{
|
|
||||||
if (aliases.Exists(item => item.Name == name))
|
|
||||||
{
|
|
||||||
duplicates.Add(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (duplicates.Count == 0)
|
if (_tenantid == "+")
|
||||||
{
|
{
|
||||||
InstallConfig config = new InstallConfig();
|
if (!string.IsNullOrEmpty(_tenantName) && _tenants.FirstOrDefault(item => item.Name == _tenantName) == null)
|
||||||
|
{
|
||||||
if (_tenantid == "+")
|
// validate host credentials
|
||||||
{
|
var user = new User();
|
||||||
if (!string.IsNullOrEmpty(_tenantName) && _tenants.FirstOrDefault(item => item.Name == _tenantName) == null)
|
user.SiteId = PageState.Site.SiteId;
|
||||||
{
|
user.Username = _hostusername;
|
||||||
// validate host credentials
|
user.Password = _hostpassword;
|
||||||
var user = new User();
|
user.LastIPAddress = PageState.RemoteIPAddress;
|
||||||
user.SiteId = PageState.Site.SiteId;
|
user = await UserService.LoginUserAsync(user, false, false);
|
||||||
user.Username = _hostusername;
|
|
||||||
user.Password = _hostpassword;
|
|
||||||
user = await UserService.LoginUserAsync(user, false, false);
|
|
||||||
if (user.IsAuthenticated)
|
if (user.IsAuthenticated)
|
||||||
{
|
{
|
||||||
var connectionString = String.Empty;
|
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
var connectionString = String.Empty;
|
||||||
{
|
if (_showConnectionString)
|
||||||
connectionString = databaseConfigControl.GetConnectionString();
|
{
|
||||||
}
|
connectionString = _connectionString;
|
||||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
|
{
|
||||||
|
connectionString = databaseConfigControl.GetConnectionString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (connectionString != "")
|
if (connectionString != "")
|
||||||
{
|
{
|
||||||
@ -370,8 +409,7 @@ else
|
|||||||
if (installation.Success)
|
if (installation.Success)
|
||||||
{
|
{
|
||||||
var aliasname = config.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
|
var aliasname = config.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
|
||||||
var uri = new Uri(NavigationManager.Uri);
|
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + aliasname, true);
|
||||||
NavigationManager.NavigateTo(uri.Scheme + "://" + aliasname, true);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -395,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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,20 +30,16 @@ else
|
|||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Alias> _sites;
|
private List<Alias> _sites;
|
||||||
private string _scheme;
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
var uri = new Uri(NavigationManager.Uri);
|
|
||||||
_scheme = uri.Scheme + "://";
|
|
||||||
|
|
||||||
var aliases = await AliasService.GetAliasesAsync();
|
var aliases = await AliasService.GetAliasesAsync();
|
||||||
_sites = new List<Alias>();
|
_sites = new List<Alias>();
|
||||||
foreach (Alias alias in aliases)
|
foreach (Alias alias in aliases)
|
||||||
{
|
{
|
||||||
if (!_sites.Exists(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId))
|
if (alias.IsDefault && !_sites.Exists(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId))
|
||||||
{
|
{
|
||||||
_sites.Add(alias);
|
_sites.Add(alias);
|
||||||
}
|
}
|
||||||
@ -52,16 +48,11 @@ else
|
|||||||
|
|
||||||
private void Edit(string name)
|
private void Edit(string name)
|
||||||
{
|
{
|
||||||
if (name.Equals("*"))
|
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + name + "/admin/site", true);
|
||||||
{
|
|
||||||
var uri = new Uri(NavigationManager.Uri);
|
|
||||||
name = uri.Authority;
|
|
||||||
}
|
|
||||||
NavigationManager.NavigateTo(_scheme + name + "/admin/site/?reload");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Browse(string name)
|
private void Browse(string name)
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(_scheme + name + "/?reload");
|
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + name, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,22 +57,20 @@ else
|
|||||||
{
|
{
|
||||||
@if (_results.Count > 0)
|
@if (_results.Count > 0)
|
||||||
{
|
{
|
||||||
<div class="table-responsive">
|
<Pager Class="table table-bordered" Items="@_results">
|
||||||
<Pager Class="table table-bordered" Items="@_results">
|
<Header>
|
||||||
<Header>
|
@foreach (KeyValuePair<string, string> kvp in _results.First())
|
||||||
@foreach (KeyValuePair<string, string> kvp in _results.First())
|
{
|
||||||
{
|
<th>@kvp.Key</th>
|
||||||
<th>@kvp.Key</th>
|
}
|
||||||
}
|
</Header>
|
||||||
</Header>
|
<Row>
|
||||||
<Row>
|
@foreach (KeyValuePair<string, string> kvp in context)
|
||||||
@foreach (KeyValuePair<string, string> kvp in context)
|
{
|
||||||
{
|
<td>@kvp.Value</td>
|
||||||
<td>@kvp.Value</td>
|
}
|
||||||
}
|
</Row>
|
||||||
</Row>
|
</Pager>
|
||||||
</Pager>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -27,17 +27,53 @@
|
|||||||
</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="serverpath" HelpText="Server Path" ResourceKey="ServerPath">Server Path: </Label>
|
<Label Class="col-sm-3" For="machinename" HelpText="Machine Name" ResourceKey="MachineName">Machine Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="serverpath" class="form-control" @bind="@_serverpath" readonly />
|
<input id="machinename" class="form-control" @bind="@_machinename" 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="servertime" HelpText="Server Time" ResourceKey="ServerTime">Server Time: </Label>
|
<Label Class="col-sm-3" For="ipaddress" HelpText="Server IP Address" ResourceKey="IPAddress">IP Address: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="ipaddress" class="form-control" @bind="@_ipaddress" readonly />
|
||||||
|
</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 class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="contentrootpath" HelpText="Root Path" ResourceKey="ContentRootPath">Root Path: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="contentrootpath" class="form-control" @bind="@_contentrootpath" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="webrootpath" HelpText="Web Path" ResourceKey="WebRootPath">Web Path: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="webrootpath" class="form-control" @bind="@_webrootpath" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="servertime" HelpText="Server Date/Time (in UTC)" ResourceKey="ServerTime">Server Date/Time: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="servertime" class="form-control" @bind="@_servertime" readonly />
|
<input id="servertime" class="form-control" @bind="@_servertime" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="tickcount" HelpText="Amount Of Time The Service Has Been Available And Operational" ResourceKey="TickCount">Service Uptime: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="tickcount" class="form-control" @bind="@_tickcount" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="workingset" HelpText="Memory Allocation Of Service (in MB)" ResourceKey="WorkingSet">Memory Allocation: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="workingset" class="form-control" @bind="@_workingset" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="installationid" HelpText="The Unique Identifier For Your Installation" ResourceKey="InstallationId">Installation ID: </Label>
|
<Label Class="col-sm-3" For="installationid" HelpText="The Unique Identifier For Your Installation" ResourceKey="InstallationId">Installation ID: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -69,6 +105,21 @@
|
|||||||
<option value="Warning">@Localizer["Warning"]</option>
|
<option value="Warning">@Localizer["Warning"]</option>
|
||||||
<option value="Error">@Localizer["Error"]</option>
|
<option value="Error">@Localizer["Error"]</option>
|
||||||
<option value="Critical">@Localizer["Critical"]</option>
|
<option value="Critical">@Localizer["Critical"]</option>
|
||||||
|
<option value="None">@Localizer["None"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="notificationlevel" HelpText="The Minimum Logging Level For Which Notifications Should Be Sent To Host Users." ResourceKey="NotificationLevel">Notification Level: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="notificationlevel" class="form-select" @bind="@_notificationlevel">
|
||||||
|
<option value="Trace">@Localizer["Trace"]</option>
|
||||||
|
<option value="Debug">@Localizer["Debug"]</option>
|
||||||
|
<option value="Information">@Localizer["Information"]</option>
|
||||||
|
<option value="Warning">@Localizer["Warning"]</option>
|
||||||
|
<option value="Error">@Localizer["Error"]</option>
|
||||||
|
<option value="Critical">@Localizer["Critical"]</option>
|
||||||
|
<option value="None">@Localizer["None"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -97,51 +148,71 @@
|
|||||||
<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>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
private string _version = string.Empty;
|
private string _version = string.Empty;
|
||||||
private string _clrversion = string.Empty;
|
private string _clrversion = string.Empty;
|
||||||
private string _osversion = string.Empty;
|
private string _osversion = string.Empty;
|
||||||
private string _serverpath = string.Empty;
|
private string _machinename = string.Empty;
|
||||||
private string _servertime = string.Empty;
|
private string _ipaddress = string.Empty;
|
||||||
private string _installationid = string.Empty;
|
private string _environment = string.Empty;
|
||||||
|
private string _contentrootpath = string.Empty;
|
||||||
|
private string _webrootpath = string.Empty;
|
||||||
|
private string _servertime = string.Empty;
|
||||||
|
private string _tickcount = string.Empty;
|
||||||
|
private string _workingset = string.Empty;
|
||||||
|
private string _installationid = string.Empty;
|
||||||
|
|
||||||
private string _detailederrors = string.Empty;
|
private string _detailederrors = string.Empty;
|
||||||
private string _logginglevel = string.Empty;
|
private string _logginglevel = string.Empty;
|
||||||
private string _swagger = string.Empty;
|
private string _notificationlevel = string.Empty;
|
||||||
private string _packageservice = string.Empty;
|
private string _swagger = string.Empty;
|
||||||
|
private string _packageservice = string.Empty;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
_version = Constants.Version;
|
_version = Constants.Version;
|
||||||
|
|
||||||
Dictionary<string, string> systeminfo = await SystemService.GetSystemInfoAsync();
|
Dictionary<string, object> systeminfo = await SystemService.GetSystemInfoAsync("environment");
|
||||||
if (systeminfo != null)
|
if (systeminfo != null)
|
||||||
{
|
{
|
||||||
_clrversion = systeminfo["clrversion"];
|
_clrversion = systeminfo["CLRVersion"].ToString();
|
||||||
_osversion = systeminfo["osversion"];
|
_osversion = systeminfo["OSVersion"].ToString();
|
||||||
_serverpath = systeminfo["serverpath"];
|
_machinename = systeminfo["MachineName"].ToString();
|
||||||
_servertime = systeminfo["servertime"];
|
_ipaddress = systeminfo["IPAddress"].ToString();
|
||||||
_installationid = systeminfo["installationid"];
|
_environment = systeminfo["Environment"].ToString();
|
||||||
|
_contentrootpath = systeminfo["ContentRootPath"].ToString();
|
||||||
|
_webrootpath = systeminfo["WebRootPath"].ToString();
|
||||||
|
_servertime = systeminfo["ServerTime"].ToString() + " UTC";
|
||||||
|
_tickcount = TimeSpan.FromMilliseconds(Convert.ToInt64(systeminfo["TickCount"].ToString())).ToString();
|
||||||
|
_workingset = (Convert.ToInt64(systeminfo["WorkingSet"].ToString()) / 1000000).ToString() + " MB";
|
||||||
|
}
|
||||||
|
|
||||||
_detailederrors = systeminfo["detailederrors"];
|
systeminfo = await SystemService.GetSystemInfoAsync();
|
||||||
_logginglevel = systeminfo["logginglevel"];
|
if (systeminfo != null)
|
||||||
_swagger = systeminfo["swagger"];
|
{
|
||||||
_packageservice = systeminfo["packageservice"];
|
_installationid = systeminfo["InstallationId"].ToString();
|
||||||
}
|
_detailederrors = systeminfo["DetailedErrors"].ToString();
|
||||||
}
|
_logginglevel = systeminfo["Logging:LogLevel:Default"].ToString();
|
||||||
|
_notificationlevel = systeminfo["Logging:LogLevel:Notify"].ToString();
|
||||||
|
_swagger = systeminfo["UseSwagger"].ToString();
|
||||||
|
_packageservice = systeminfo["PackageService"].ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveConfig()
|
private async Task SaveConfig()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var settings = new Dictionary<string, string>();
|
var settings = new Dictionary<string, object>();
|
||||||
settings.Add("detailederrors", _detailederrors);
|
settings.Add("DetailedErrors", _detailederrors);
|
||||||
settings.Add("logginglevel", _logginglevel);
|
settings.Add("Logging:LogLevel:Default", _logginglevel);
|
||||||
settings.Add("swagger", _swagger);
|
settings.Add("Logging:LogLevel:Notify", _notificationlevel);
|
||||||
settings.Add("packageservice", _packageservice);
|
settings.Add("UseSwagger", _swagger);
|
||||||
|
settings.Add("PackageService", _packageservice);
|
||||||
await SystemService.UpdateSystemInfoAsync(settings);
|
await SystemService.UpdateSystemInfoAsync(settings);
|
||||||
AddModuleMessage(Localizer["Success.UpdateConfig.Restart"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.UpdateConfig.Restart"], MessageType.Success);
|
||||||
}
|
}
|
||||||
|
@ -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";
|
||||||
|
@ -72,7 +72,7 @@
|
|||||||
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;
|
||||||
|
|
||||||
|
@ -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" />
|
||||||
}
|
}
|
||||||
@ -40,9 +40,12 @@ else
|
|||||||
@((MarkupString)PurchaseLink(context.PackageName))
|
@((MarkupString)PurchaseLink(context.PackageName))
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if (UpgradeAvailable(context.PackageName, context.Version))
|
@{
|
||||||
|
var version = UpgradeAvailable(context.PackageName, context.Version);
|
||||||
|
}
|
||||||
|
@if (version != context.Version)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.PackageName, context.Version))>@SharedLocalizer["Upgrade"]</button>
|
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.PackageName, version))>@SharedLocalizer["Upgrade"]</button>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
@ -94,18 +97,17 @@ else
|
|||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UpgradeAvailable(string packagename, string version)
|
private string UpgradeAvailable(string packagename, string version)
|
||||||
{
|
{
|
||||||
var upgradeavailable = false;
|
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
{
|
||||||
{
|
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
if (package != null && Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
|
||||||
if (package != null)
|
{
|
||||||
{
|
return package.Version;
|
||||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return upgradeavailable;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadTheme(string packagename, string version)
|
private async Task DownloadTheme(string packagename, string version)
|
||||||
|
@ -25,7 +25,13 @@
|
|||||||
<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">
|
<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">
|
||||||
<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>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="owner" class="form-control" @bind="@_owner" disabled />
|
<input id="owner" class="form-control" @bind="@_owner" disabled />
|
||||||
@ -53,28 +59,30 @@
|
|||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _themeName = "";
|
private string _themeName = "";
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _version;
|
private string _version;
|
||||||
private string _owner = "";
|
private string _packagename;
|
||||||
private string _url = "";
|
private string _owner = "";
|
||||||
private string _contact = "";
|
private string _url = "";
|
||||||
private string _license = "";
|
private string _contact = "";
|
||||||
|
private string _license = "";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_themeName = WebUtility.UrlDecode(PageState.QueryString["name"]);
|
_themeName = WebUtility.UrlDecode(PageState.QueryString["name"]);
|
||||||
var themes = await ThemeService.GetThemesAsync();
|
var themes = await ThemeService.GetThemesAsync();
|
||||||
var theme = themes.FirstOrDefault(item => item.ThemeName == _themeName);
|
var theme = themes.FirstOrDefault(item => item.ThemeName == _themeName);
|
||||||
if (theme != null)
|
if (theme != null)
|
||||||
{
|
{
|
||||||
_name = theme.Name;
|
_name = theme.Name;
|
||||||
_version = theme.Version;
|
_version = theme.Version;
|
||||||
_owner = theme.Owner;
|
_packagename = theme.PackageName;
|
||||||
|
_owner = theme.Owner;
|
||||||
_url = theme.Url;
|
_url = theme.Url;
|
||||||
_contact = theme.Contact;
|
_contact = theme.Contact;
|
||||||
_license = theme.License;
|
_license = theme.License;
|
||||||
|
@ -40,28 +40,46 @@
|
|||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
var route = new Route(_url, PageState.Alias.Path);
|
if (_url != _mappedurl)
|
||||||
var url = route.SiteUrl + "/" + route.PagePath;
|
{
|
||||||
|
var url = PageState.Uri.Scheme + "://" + PageState.Uri.Authority + "/";
|
||||||
|
url = url + (!string.IsNullOrEmpty(PageState.Alias.Path) ? PageState.Alias.Path + "/" : "");
|
||||||
|
|
||||||
var urlmapping = new UrlMapping();
|
_url = (_url.StartsWith("/")) ? _url.Substring(1) : _url;
|
||||||
urlmapping.SiteId = PageState.Site.SiteId;
|
_url = (!_url.StartsWith("http")) ? url + _url : _url;
|
||||||
urlmapping.Url = url;
|
|
||||||
urlmapping.MappedUrl = _mappedurl;
|
|
||||||
urlmapping.Requests = 0;
|
|
||||||
urlmapping.CreatedOn = DateTime.UtcNow;
|
|
||||||
urlmapping.RequestedOn = DateTime.UtcNow;
|
|
||||||
|
|
||||||
try
|
if (_url.StartsWith(url))
|
||||||
{
|
{
|
||||||
urlmapping = await UrlMappingService.AddUrlMappingAsync(urlmapping);
|
var urlmapping = new UrlMapping();
|
||||||
await logger.LogInformation("UrlMapping Saved {UrlMapping}", urlmapping);
|
urlmapping.SiteId = PageState.Site.SiteId;
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
var route = new Route(_url, PageState.Alias.Path);
|
||||||
}
|
urlmapping.Url = route.PagePath;
|
||||||
catch (Exception ex)
|
urlmapping.MappedUrl = _mappedurl.Replace(url, "");
|
||||||
{
|
urlmapping.Requests = 0;
|
||||||
await logger.LogError(ex, "Error Saving UrlMapping {UrlMapping} {Error}", urlmapping, ex.Message);
|
urlmapping.CreatedOn = DateTime.UtcNow;
|
||||||
AddModuleMessage(Localizer["Error.SaveUrlMapping"], MessageType.Error);
|
urlmapping.RequestedOn = DateTime.UtcNow;
|
||||||
}
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
urlmapping = await UrlMappingService.AddUrlMappingAsync(urlmapping);
|
||||||
|
await logger.LogInformation("UrlMapping Saved {UrlMapping}", urlmapping);
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving UrlMapping {UrlMapping} {Error}", urlmapping, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.SaveUrlMapping"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.SaveUrlMapping"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.DuplicateUrlMapping"], MessageType.Warning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -26,55 +26,64 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
|
|
||||||
private int _urlmappingid;
|
private int _urlmappingid;
|
||||||
private string _url = string.Empty;
|
private string _url = string.Empty;
|
||||||
private string _mappedurl = string.Empty;
|
private string _mappedurl = string.Empty;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_urlmappingid = Int32.Parse(PageState.QueryString["id"]);
|
_urlmappingid = Int32.Parse(PageState.QueryString["id"]);
|
||||||
var urlmapping = await UrlMappingService.GetUrlMappingAsync(_urlmappingid);
|
var urlmapping = await UrlMappingService.GetUrlMappingAsync(_urlmappingid);
|
||||||
if (urlmapping != null)
|
if (urlmapping != null)
|
||||||
{
|
{
|
||||||
_url = urlmapping.Url;
|
_url = urlmapping.Url;
|
||||||
_mappedurl = urlmapping.MappedUrl;
|
_mappedurl = urlmapping.MappedUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading UrlMapping {UrlMappingId} {Error}", _urlmappingid, ex.Message);
|
await logger.LogError(ex, "Error Loading UrlMapping {UrlMappingId} {Error}", _urlmappingid, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.LoadUrlMapping"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.LoadUrlMapping"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveUrlMapping()
|
private async Task SaveUrlMapping()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
var urlmapping = await UrlMappingService.GetUrlMappingAsync(_urlmappingid);
|
if (_url != _mappedurl)
|
||||||
urlmapping.MappedUrl = _mappedurl;
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var url = PageState.Uri.Scheme + "://" + PageState.Uri.Authority + "/";
|
||||||
|
url = url + (!string.IsNullOrEmpty(PageState.Alias.Path) ? PageState.Alias.Path + "/" : "");
|
||||||
|
|
||||||
try
|
var urlmapping = await UrlMappingService.GetUrlMappingAsync(_urlmappingid);
|
||||||
{
|
urlmapping.MappedUrl = _mappedurl.Replace(url, "");
|
||||||
urlmapping = await UrlMappingService.UpdateUrlMappingAsync(urlmapping);
|
urlmapping = await UrlMappingService.UpdateUrlMappingAsync(urlmapping);
|
||||||
await logger.LogInformation("UrlMapping Saved {UrlMapping}", urlmapping);
|
await logger.LogInformation("UrlMapping Saved {UrlMapping}", urlmapping);
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Saving UrlMapping {UrlMapping} {Error}", urlmapping, ex.Message);
|
await logger.LogError(ex, "Error Saving UrlMapping {UrlMappingId} {Error}", _urlmappingid, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.SaveUrlMapping"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.SaveUrlMapping"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.DuplicateUrlMapping"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
|
@ -40,10 +40,10 @@ else
|
|||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.UrlMappingId.ToString())" ResourceKey="Edit" /></td>
|
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.UrlMappingId.ToString())" ResourceKey="Edit" /></td>
|
||||||
<td><ActionDialog Header="Delete Url Mapping" Message="@string.Format(Localizer["Confirm.DeleteUrlMapping"], context.Url)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUrlMapping(context))" ResourceKey="DeleteUrlMapping" /></td>
|
<td><ActionDialog Header="Delete Url Mapping" Message="@string.Format(Localizer["Confirm.DeleteUrlMapping"], context.Url)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUrlMapping(context))" ResourceKey="DeleteUrlMapping" /></td>
|
||||||
<td>
|
<td>
|
||||||
<a href="" onclick="@(() => BrowseUrl(context.Url))">@context.Url</a>
|
<a href="@Utilities.TenantUrl(PageState.Alias, context.Url)">@context.Url</a>
|
||||||
@if (_mapped)
|
@if (_mapped)
|
||||||
{
|
{
|
||||||
@((MarkupString)"<br />>> ")<a href="" onclick="@(() => BrowseUrl(context.MappedUrl))">@context.MappedUrl</a>
|
@((MarkupString)"<br />>> ")<a href="@((context.MappedUrl.StartsWith("http") ? context.MappedUrl : Utilities.TenantUrl(PageState.Alias, context.MappedUrl)))">@context.MappedUrl</a>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td>@context.Requests</td>
|
<td>@context.Requests</td>
|
||||||
@ -96,11 +96,6 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BrowseUrl(string url)
|
|
||||||
{
|
|
||||||
NavigationManager.NavigateTo(url, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DeleteUrlMapping(UrlMapping urlMapping)
|
private async Task DeleteUrlMapping(UrlMapping urlMapping)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<Label Class="col-sm-3" For="to" HelpText="Enter the username you wish to send a message to" ResourceKey="To">To: </Label>
|
<Label Class="col-sm-3" For="to" HelpText="Enter the username you wish to send a message to" ResourceKey="To">To: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="to" class="form-control" @bind="@username" />
|
<input id="to" class="form-control" @bind="@username" />
|
||||||
</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="subject" HelpText="Enter the subject of the message" ResourceKey="Subject">Subject: </Label>
|
<Label Class="col-sm-3" For="subject" HelpText="Enter the subject of the message" ResourceKey="Subject">Subject: </Label>
|
||||||
@ -49,7 +49,7 @@
|
|||||||
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
|
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
var notification = new Notification(PageState.Site.SiteId, PageState.User, user, subject, body, null);
|
var notification = new Notification(PageState.Site.SiteId, PageState.User, user, subject, body);
|
||||||
notification = await NotificationService.AddNotificationAsync(notification);
|
notification = await NotificationService.AddNotificationAsync(notification);
|
||||||
await logger.LogInformation("Notification Created {NotificationId}", notification.NotificationId);
|
await logger.LogInformation("Notification Created {NotificationId}", notification.NotificationId);
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
@if (PageState.User != null && photo != null)
|
@if (PageState.User != null && photo != null)
|
||||||
{
|
{
|
||||||
<img src="@ImageUrl(photofileid, 400, 400, "crop")" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
|
<img src="@ImageUrl(photofileid, 400, 400)" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -31,16 +31,34 @@ else
|
|||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="If you wish to change your password you can enter it here. Please choose a sufficiently secure password." ResourceKey="Password"></Label>
|
<Label Class="col-sm-3" For="password" HelpText="If you wish to change your password you can enter it here. Please choose a sufficiently secure password." ResourceKey="Password"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="password" type="password" class="form-control" @bind="@password" autocomplete="new-password" />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</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="confirm" HelpText="If you are changing your password you must enter it again to confirm it matches" ResourceKey="Confirm"></Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="If you are changing your password you must enter it again to confirm it matches" ResourceKey="Confirm"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="confirm" type="password" class="form-control" @bind="@confirm" autocomplete="new-password" />
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@confirm" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@if (allowtwofactor)
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="twofactor" HelpText="Indicates if you are using two factor authentication. Two factor authentication requires you to enter a verification code sent via email after you sign in." ResourceKey="TwoFactor"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="twofactor" class="form-select" @bind="@twofactor" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
|
<Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -142,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>
|
||||||
@ -175,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>
|
||||||
@ -193,6 +211,8 @@ else
|
|||||||
</Detail>
|
</Detail>
|
||||||
</Pager>
|
</Pager>
|
||||||
}
|
}
|
||||||
|
<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" />
|
||||||
<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>
|
||||||
@ -201,11 +221,16 @@ else
|
|||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string username = string.Empty;
|
private string username = string.Empty;
|
||||||
private string password = string.Empty;
|
private string _password = string.Empty;
|
||||||
|
private string _passwordtype = "password";
|
||||||
|
private string _togglepassword = string.Empty;
|
||||||
private string confirm = string.Empty;
|
private string confirm = string.Empty;
|
||||||
|
private bool allowtwofactor = false;
|
||||||
|
private string twofactor = "False";
|
||||||
private string email = string.Empty;
|
private string email = string.Empty;
|
||||||
private string displayname = string.Empty;
|
private string displayname = string.Empty;
|
||||||
private FileManager filemanager;
|
private FileManager filemanager;
|
||||||
@ -224,9 +249,17 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
|
if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"]))
|
||||||
|
{
|
||||||
|
allowtwofactor = (PageState.Site.Settings["LoginOptions:TwoFactor"] == "true");
|
||||||
|
}
|
||||||
|
|
||||||
if (PageState.User != null)
|
if (PageState.User != null)
|
||||||
{
|
{
|
||||||
username = PageState.User.Username;
|
username = PageState.User.Username;
|
||||||
|
twofactor = PageState.User.TwoFactorRequired.ToString();
|
||||||
email = PageState.User.Email;
|
email = PageState.User.Email;
|
||||||
displayname = PageState.User.DisplayName;
|
displayname = PageState.User.DisplayName;
|
||||||
|
|
||||||
@ -280,11 +313,12 @@ else
|
|||||||
{
|
{
|
||||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||||
{
|
{
|
||||||
if (password == confirm)
|
if (_password == confirm)
|
||||||
{
|
{
|
||||||
var user = PageState.User;
|
var user = PageState.User;
|
||||||
user.Username = username;
|
user.Username = username;
|
||||||
user.Password = password;
|
user.Password = _password;
|
||||||
|
user.TwoFactorRequired = bool.Parse(twofactor);
|
||||||
user.Email = email;
|
user.Email = email;
|
||||||
user.DisplayName = (displayname == string.Empty ? username : displayname);
|
user.DisplayName = (displayname == string.Empty ? username : displayname);
|
||||||
user.PhotoFileId = filemanager.GetFileId();
|
user.PhotoFileId = filemanager.GetFileId();
|
||||||
@ -292,12 +326,23 @@ else
|
|||||||
{
|
{
|
||||||
user.PhotoFileId = null;
|
user.PhotoFileId = null;
|
||||||
}
|
}
|
||||||
|
if (user.PhotoFileId != null)
|
||||||
|
{
|
||||||
|
photofileid = user.PhotoFileId.Value;
|
||||||
|
photo = await FileService.GetFileAsync(photofileid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
photofileid = -1;
|
||||||
|
photo = null;
|
||||||
|
}
|
||||||
|
|
||||||
await UserService.UpdateUserAsync(user);
|
await UserService.UpdateUserAsync(user);
|
||||||
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");
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
||||||
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -380,4 +425,51 @@ else
|
|||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAllNotifications()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ModuleInstance.ShowProgressIndicator();
|
||||||
|
foreach(var Notification in notifications)
|
||||||
|
{
|
||||||
|
if (!Notification.IsDeleted)
|
||||||
|
{
|
||||||
|
Notification.IsDeleted = true;
|
||||||
|
await NotificationService.UpdateNotificationAsync(Notification);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await NotificationService.DeleteNotificationAsync(Notification.NotificationId);
|
||||||
|
}
|
||||||
|
await logger.LogInformation("Notification Deleted {Notification}", Notification);
|
||||||
|
}
|
||||||
|
await logger.LogInformation("Notifications Permanently Deleted");
|
||||||
|
await LoadNotificationsAsync();
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Deleting Notifications {Error}", ex.Message);
|
||||||
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,14 +20,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label>
|
<Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="password" type="password" class="form-control" @bind="@password" />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</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="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="confirm" type="password" class="form-control" @bind="@confirm" />
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@confirm" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -88,7 +94,9 @@
|
|||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string username = string.Empty;
|
private string username = string.Empty;
|
||||||
private string password = string.Empty;
|
private string _password = string.Empty;
|
||||||
|
private string _passwordtype = "password";
|
||||||
|
private string _togglepassword = string.Empty;
|
||||||
private string confirm = string.Empty;
|
private string confirm = string.Empty;
|
||||||
private string email = string.Empty;
|
private string email = string.Empty;
|
||||||
private string displayname = string.Empty;
|
private string displayname = string.Empty;
|
||||||
@ -102,6 +110,7 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||||
settings = new Dictionary<string, string>();
|
settings = new Dictionary<string, string>();
|
||||||
}
|
}
|
||||||
@ -119,9 +128,9 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (username != string.Empty && password != string.Empty && confirm != string.Empty && email != string.Empty && ValidateProfiles())
|
if (username != string.Empty && _password != string.Empty && confirm != string.Empty && email != string.Empty && ValidateProfiles())
|
||||||
{
|
{
|
||||||
if (password == confirm)
|
if (_password == confirm)
|
||||||
{
|
{
|
||||||
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
|
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
@ -129,7 +138,7 @@
|
|||||||
user = new User();
|
user = new User();
|
||||||
user.SiteId = PageState.Site.SiteId;
|
user.SiteId = PageState.Site.SiteId;
|
||||||
user.Username = username;
|
user.Username = username;
|
||||||
user.Password = password;
|
user.Password = _password;
|
||||||
user.Email = email;
|
user.Email = email;
|
||||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||||
user.PhotoFileId = null;
|
user.PhotoFileId = null;
|
||||||
@ -193,4 +202,17 @@
|
|||||||
settings = SettingService.SetSetting(settings, SettingName, value);
|
settings = SettingService.SetSetting(settings, SettingName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,14 +29,20 @@ else
|
|||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label>
|
<Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="password" type="password" class="form-control" @bind="@password" />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</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="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="confirm" type="password" class="form-control" @bind="@confirm" />
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@confirm" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -66,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">
|
||||||
@ -131,61 +148,70 @@ else
|
|||||||
<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>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private int userid;
|
private int userid;
|
||||||
private string username = string.Empty;
|
private string username = string.Empty;
|
||||||
private string password = string.Empty;
|
private string _password = string.Empty;
|
||||||
private string confirm = string.Empty;
|
private string _passwordtype = "password";
|
||||||
private string email = string.Empty;
|
private string _togglepassword = string.Empty;
|
||||||
private string displayname = string.Empty;
|
private string confirm = string.Empty;
|
||||||
private FileManager filemanager;
|
private string email = string.Empty;
|
||||||
private int photofileid = -1;
|
private string displayname = string.Empty;
|
||||||
private File photo = null;
|
private FileManager filemanager;
|
||||||
private List<Profile> profiles;
|
private int photofileid = -1;
|
||||||
private Dictionary<string, string> settings;
|
private File photo = null;
|
||||||
private string category = string.Empty;
|
private string isdeleted;
|
||||||
private string createdby;
|
private string lastlogin;
|
||||||
private DateTime createdon;
|
private string lastipaddress;
|
||||||
private string modifiedby;
|
|
||||||
private DateTime modifiedon;
|
|
||||||
private string deletedby;
|
|
||||||
private DateTime? deletedon;
|
|
||||||
private string isdeleted;
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
private List<Profile> profiles;
|
||||||
|
private Dictionary<string, string> settings;
|
||||||
|
private string category = string.Empty;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
private string createdby;
|
||||||
{
|
private DateTime createdon;
|
||||||
try
|
private string modifiedby;
|
||||||
{
|
private DateTime modifiedon;
|
||||||
// OnParametersSetAsync is called when the edit modal is closed - in which case there is no id parameter
|
private string deletedby;
|
||||||
if (PageState.QueryString.ContainsKey("id"))
|
private DateTime? deletedon;
|
||||||
{
|
|
||||||
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
userid = Int32.Parse(PageState.QueryString["id"]);
|
|
||||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
protected override async Task OnParametersSetAsync()
|
||||||
if (user != null)
|
{
|
||||||
{
|
try
|
||||||
username = user.Username;
|
{
|
||||||
email = user.Email;
|
if (PageState.QueryString.ContainsKey("id"))
|
||||||
displayname = user.DisplayName;
|
{
|
||||||
if (user.PhotoFileId != null)
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
{
|
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||||
photofileid = user.PhotoFileId.Value;
|
userid = Int32.Parse(PageState.QueryString["id"]);
|
||||||
photo = await FileService.GetFileAsync(photofileid);
|
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||||
}
|
if (user != null)
|
||||||
else
|
{
|
||||||
{
|
username = user.Username;
|
||||||
photofileid = -1;
|
email = user.Email;
|
||||||
photo = null;
|
displayname = user.DisplayName;
|
||||||
}
|
if (user.PhotoFileId != null)
|
||||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
{
|
||||||
|
photofileid = user.PhotoFileId.Value;
|
||||||
|
photo = await FileService.GetFileAsync(photofileid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
photofileid = -1;
|
||||||
|
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);
|
||||||
createdby = user.CreatedBy;
|
createdby = user.CreatedBy;
|
||||||
createdon = user.CreatedOn;
|
createdon = user.CreatedOn;
|
||||||
modifiedby = user.ModifiedBy;
|
modifiedby = user.ModifiedBy;
|
||||||
modifiedon = user.ModifiedOn;
|
modifiedon = user.ModifiedOn;
|
||||||
deletedby = user.DeletedBy;
|
deletedby = user.DeletedBy;
|
||||||
deletedon = user.DeletedOn;
|
deletedon = user.DeletedOn;
|
||||||
isdeleted = user.IsDeleted.ToString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,12 +231,12 @@ else
|
|||||||
{
|
{
|
||||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||||
{
|
{
|
||||||
if (password == confirm)
|
if (_password == confirm)
|
||||||
{
|
{
|
||||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||||
user.SiteId = PageState.Site.SiteId;
|
user.SiteId = PageState.Site.SiteId;
|
||||||
user.Username = username;
|
user.Username = username;
|
||||||
user.Password = password;
|
user.Password = _password;
|
||||||
user.Email = email;
|
user.Email = email;
|
||||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||||
user.PhotoFileId = null;
|
user.PhotoFileId = null;
|
||||||
@ -268,4 +294,17 @@ else
|
|||||||
settings = SettingService.SetSetting(settings, SettingName, value);
|
settings = SettingService.SetSetting(settings, SettingName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@if (userroles == null)
|
@if (users == null)
|
||||||
{
|
{
|
||||||
<p>
|
<p>
|
||||||
<em>@SharedLocalizer["Loading"]</em>
|
<em>@SharedLocalizer["Loading"]</em>
|
||||||
@ -30,12 +30,14 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Pager Items="@userroles">
|
<Pager Items="@users" RowClass="align-middle">
|
||||||
<Header>
|
<Header>
|
||||||
<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["Name"]</th>
|
||||||
|
<th>@Localizer["LastLoginOn"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td>
|
<td>
|
||||||
@ -47,21 +49,308 @@ else
|
|||||||
<td>
|
<td>
|
||||||
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
|
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
|
||||||
</td>
|
</td>
|
||||||
<td>@context.User.DisplayName</td>
|
<td>@context.User.Username</td>
|
||||||
|
<td>@((MarkupString)string.Format("<a href=\"mailto:{0}\">{1}</a>", @context.User.Email, @context.User.DisplayName))</td>
|
||||||
|
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn)</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<Section Name="User" Heading="User Settings" ResourceKey="UserSettings">
|
||||||
<Label Class="col-sm-3" For="allowregistration" HelpText="Do you want to allow visitors to be able to register for a user account on the site" ResourceKey="AllowRegistration">Allow User Registration? </Label>
|
<div class="row mb-1 align-items-center">
|
||||||
<div class="col-sm-9">
|
<Label Class="col-sm-3" For="allowregistration" HelpText="Do you want anonymous visitors to be able to register for an account on the site" ResourceKey="AllowRegistration">Allow User Registration?</Label>
|
||||||
<select id="allowregistration" class="form-select" @bind="@_allowregistration" required>
|
<div class="col-sm-9">
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
<select id="allowregistration" class="form-select" @bind="@_allowregistration">
|
||||||
<option value="False">@SharedLocalizer["No"]</option>
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
</select>
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
@if (_providertype != "")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="allowsitelogin" class="form-select" @bind="@_allowsitelogin">
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="allowsitelogin" class="form-control" value="@SharedLocalizer["Yes"]" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="twofactor" HelpText="Do you want users to use two factor authentication? Note that you should use the Disabled option until you have successfully verified that the Notification Job in Scheduled Jobs is enabled and your SMTP options in Site Settings are configured or else you will lock yourself out." ResourceKey="TwoFactor">Two Factor?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="twofactor" class="form-select" @bind="@_twofactor">
|
||||||
|
<option value="false">@Localizer["Disabled"]</option>
|
||||||
|
<option value="true">@Localizer["Optional"]</option>
|
||||||
|
<option value="required">@Localizer["Required"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="cookiename" HelpText="You can choose to use a custom authentication cookie name for each site. However please be aware that if you want to share an authentication cookie between sites on the same domain they need to use a consistent cookie name. Also be aware that changing the authentication cookie name will logout all current users." ResourceKey="CookieName">Cookie Name:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="cookiename" class="form-control" @bind="@_cookiename" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</Section>
|
||||||
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
<Section Name="Password" Heading="Password Settings" ResourceKey="PasswordSettings">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="minimumlength" HelpText="The Minimum Length For A Password" ResourceKey="RequiredLength">Minimum Length:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="minimumlength" class="form-control" @bind="@_minimumlength" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="uniquecharacters" HelpText="The Minimum Number Of Unique Characters Which A Password Must Contain" ResourceKey="UniqueCharacters">Unique Characters:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="uniquecharacters" class="form-control" @bind="@_uniquecharacters" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="requiredigit" HelpText="Indicate If Passwords Must Contain A Digit" ResourceKey="RequireDigit">Require Digit?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="requiredigit" class="form-select" @bind="@_requiredigit" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="requireupper" HelpText="Indicate If Passwords Must Contain An Upper Case Character" ResourceKey="RequireUpper">Require Uppercase?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="requireupper" class="form-select" @bind="@_requireupper" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="requirelower" HelpText="Indicate If Passwords Must Contain A Lower Case Character" ResourceKey="RequireLower">Require Lowercase?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="requirelower" class="form-select" @bind="@_requirelower" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="requirepunctuation" HelpText="Indicate if Passwords Must Contain A Non-alphanumeric Character (ie. Punctuation)" ResourceKey="RequirePunctuation">Require Punctuation?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="requirepunctuation" class="form-select" @bind="@_requirepunctuation" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
<Section Name="Lockout" Heading="Lockout Settings" ResourceKey="LockoutSettings">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="maximum" HelpText="The Maximum Number Of Sign In Attempts Before A User Is Locked Out" ResourceKey="MaximumFailures">Maximum Failures:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="maximum" class="form-control" @bind="@_maximumfailures" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="lockoutduration" HelpText="The Number Of Minutes A User Should Be Locked Out" ResourceKey="LockoutDuration">Lockout Duration:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="lockoutduration" class="form-control" @bind="@_lockoutduration" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
<Section Name="ExternalLogin" Heading="External Login Settings" ResourceKey="ExternalLoginSettings">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="providertype" HelpText="Select the external login provider type" ResourceKey="ProviderType">Provider Type:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="providertype" class="form-select" value="@_providertype" @onchange="(e => ProviderTypeChanged(e))">
|
||||||
|
<option value="" selected>@Localizer["Not Specified"]</option>
|
||||||
|
<option value="@AuthenticationProviderTypes.OpenIDConnect">@Localizer["OpenID Connect"]</option>
|
||||||
|
<option value="@AuthenticationProviderTypes.OAuth2">@Localizer["OAuth 2.0"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (_providertype != "")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="providername" HelpText="The external login provider name which will be displayed on the login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="providername" class="form-control" @bind="@_providername" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="authority" HelpText="The Authority Url or Issuer Url associated with the OpenID Connect provider" ResourceKey="Authority">Authority:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="authority" class="form-control" @bind="@_authority" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="metadataurl" HelpText="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)" ResourceKey="MetadataUrl">Metadata Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="metadataurl" class="form-control" @bind="@_metadataurl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (_providertype == AuthenticationProviderTypes.OAuth2)
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="authorizationurl" HelpText="The endpoint for obtaining an Authorization Code" ResourceKey="AuthorizationUrl">Authorization Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="authorizationurl" class="form-control" @bind="@_authorizationurl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="tokenurl" HelpText="The endpoint for obtaining an Auth Token" ResourceKey="TokenUrl">Token Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="tokenurl" class="form-control" @bind="@_tokenurl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="userinfourl" HelpText="The endpoint for obtaining user information. This should be an API or Page Url which contains the users email address." ResourceKey="UserInfoUrl">User Info Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="userinfourl" class="form-control" @bind="@_userinfourl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (_providertype != "")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="clientid" HelpText="The Client ID from the provider" ResourceKey="ClientID">Client ID:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="clientid" class="form-control" @bind="@_clientid" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="clientsecret" HelpText="The Client Secret from the provider" ResourceKey="ClientSecret">Client Secret:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="@_clientsecrettype" id="clientsecret" class="form-control" @bind="@_clientsecret" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleClientSecret">@_toggleclientsecret</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||||
|
</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">
|
||||||
|
<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">
|
||||||
|
<select id="pkce" class="form-select" @bind="@_pkce" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="redirecturl" HelpText="The Redirect Url (or Callback Url) which usually needs to be registered with the provider" ResourceKey="RedirectUrl">Redirect Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="The name of the unique user identifier claim provided by the provider" ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="identifierclaimtype" class="form-control" @bind="@_identifierclaimtype" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="emailclaimtype" HelpText="The name of the email address claim provided by the provider" ResourceKey="EmailClaimType">Email Claim:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
||||||
|
</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="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">
|
||||||
|
<input id="domainfilter" class="form-control" @bind="@_domainfilter" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="createusers" HelpText="Do you want new users to be created automatically? If you disable this option, users must already be registered on the site in order to sign in with their external login." ResourceKey="CreateUsers">Create New Users?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="createusers" class="form-select" @bind="@_createusers">
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</Section>
|
||||||
|
<Section Name="Token" Heading="Token Settings" ResourceKey="TokenSettings">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="secret" HelpText="If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated." ResourceKey="Secret">Secret:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="@_secrettype" id="secret" class="form-control" @bind="@_secret" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleSecret">@_togglesecret</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="issuer" HelpText="Optionally provide the issuer of the token" ResourceKey="Issuer">Issuer:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="issuer" class="form-control" @bind="@_issuer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="audience" HelpText="Optionally provide the audience for the token" ResourceKey="Audience">Audience:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="audience" class="form-control" @bind="@_audience" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="lifetime" HelpText="The number of minutes for which a token should be valid" ResourceKey="Lifetime">Lifetime:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="lifetime" class="form-control" @bind="@_lifetime" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="token" HelpText="Select the Create Token button to generate a long-lived access token (valid for 1 year). Be sure to store this token in a safe location as you will not be able to access it in the future." ResourceKey="Token">Access Token:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="token" class="form-control" @bind="@_token" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@CreateToken">@Localizer["CreateToken"]</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||||
@ -70,79 +359,172 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<UserRole> allroles;
|
private List<UserRole> allusers;
|
||||||
private List<UserRole> userroles;
|
private List<UserRole> users;
|
||||||
private string _search;
|
private string _search = "";
|
||||||
|
|
||||||
private string _allowregistration;
|
private string _allowregistration;
|
||||||
|
private string _allowsitelogin;
|
||||||
|
private string _twofactor;
|
||||||
|
private string _cookiename;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
private string _minimumlength;
|
||||||
|
private string _uniquecharacters;
|
||||||
|
private string _requiredigit;
|
||||||
|
private string _requireupper;
|
||||||
|
private string _requirelower;
|
||||||
|
private string _requirepunctuation;
|
||||||
|
private string _maximumfailures;
|
||||||
|
private string _lockoutduration;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
private string _providertype;
|
||||||
{
|
private string _providername;
|
||||||
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
private string _authority;
|
||||||
await LoadSettingsAsync();
|
private string _metadataurl;
|
||||||
userroles = Search(_search);
|
private string _authorizationurl;
|
||||||
|
private string _tokenurl;
|
||||||
|
private string _userinfourl;
|
||||||
|
private string _clientid;
|
||||||
|
private string _clientsecret;
|
||||||
|
private string _clientsecrettype = "password";
|
||||||
|
private string _toggleclientsecret = string.Empty;
|
||||||
|
private string _scopes;
|
||||||
|
private string _parameters;
|
||||||
|
private string _pkce;
|
||||||
|
private string _redirecturl;
|
||||||
|
private string _identifierclaimtype;
|
||||||
|
private string _emailclaimtype;
|
||||||
|
private string _roleclaimtype;
|
||||||
|
private string _domainfilter;
|
||||||
|
private string _createusers;
|
||||||
|
|
||||||
|
private string _secret;
|
||||||
|
private string _secrettype = "password";
|
||||||
|
private string _togglesecret = string.Empty;
|
||||||
|
private string _issuer;
|
||||||
|
private string _audience;
|
||||||
|
private string _lifetime;
|
||||||
|
private string _token;
|
||||||
|
|
||||||
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await LoadUserSettingsAsync();
|
||||||
|
await LoadUsersAsync(true);
|
||||||
|
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
_allowregistration = PageState.Site.AllowRegistration.ToString();
|
_allowregistration = PageState.Site.AllowRegistration.ToString();
|
||||||
}
|
_allowsitelogin = SettingService.GetSetting(settings, "LoginOptions:AllowSiteLogin", "true");
|
||||||
|
|
||||||
private List<UserRole> Search(string search)
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
var results = allroles.Where(item => item.Role.Name == RoleNames.Registered || (item.Role.Name == RoleNames.Host && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)));
|
_twofactor = SettingService.GetSetting(settings, "LoginOptions:TwoFactor", "false");
|
||||||
|
_cookiename = SettingService.GetSetting(settings, "LoginOptions:CookieName", ".AspNetCore.Identity.Application");
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_search))
|
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
|
||||||
{
|
_uniquecharacters = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", "1");
|
||||||
results = results.Where(item =>
|
_requiredigit = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireDigit", "true");
|
||||||
(
|
_requireupper = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireUppercase", "true");
|
||||||
item.User.Username.Contains(search, StringComparison.OrdinalIgnoreCase) ||
|
_requirelower = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireLowercase", "true");
|
||||||
item.User.Email.Contains(search, StringComparison.OrdinalIgnoreCase) ||
|
_requirepunctuation = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", "true");
|
||||||
item.User.DisplayName.Contains(search, StringComparison.OrdinalIgnoreCase)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return results.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnSearch()
|
_maximumfailures = SettingService.GetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", "5");
|
||||||
{
|
_lockoutduration = TimeSpan.Parse(SettingService.GetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", "00:05:00")).TotalMinutes.ToString();
|
||||||
userroles = Search(_search);
|
|
||||||
await UpdateSettingsAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DeleteUser(UserRole UserRole)
|
_providertype = SettingService.GetSetting(settings, "ExternalLogin:ProviderType", "");
|
||||||
{
|
_providername = SettingService.GetSetting(settings, "ExternalLogin:ProviderName", "");
|
||||||
try
|
_authority = SettingService.GetSetting(settings, "ExternalLogin:Authority", "");
|
||||||
{
|
_metadataurl = SettingService.GetSetting(settings, "ExternalLogin:MetadataUrl", "");
|
||||||
var user = await UserService.GetUserAsync(UserRole.UserId, PageState.Site.SiteId);
|
_authorizationurl = SettingService.GetSetting(settings, "ExternalLogin:AuthorizationUrl", "");
|
||||||
if (user != null)
|
_tokenurl = SettingService.GetSetting(settings, "ExternalLogin:TokenUrl", "");
|
||||||
{
|
_userinfourl = SettingService.GetSetting(settings, "ExternalLogin:UserInfoUrl", "");
|
||||||
await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId);
|
_clientid = SettingService.GetSetting(settings, "ExternalLogin:ClientId", "");
|
||||||
await logger.LogInformation("User Deleted {User}", UserRole.User);
|
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
||||||
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
||||||
userroles = Search(_search);
|
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
||||||
StateHasChanged();
|
_parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", "");
|
||||||
}
|
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
||||||
}
|
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||||
catch (Exception ex)
|
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "sub");
|
||||||
{
|
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email");
|
||||||
await logger.LogError(ex, "Error Deleting User {User} {Error}", UserRole.User, ex.Message);
|
_roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", "");
|
||||||
AddModuleMessage(ex.Message, MessageType.Error);
|
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
|
||||||
}
|
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
|
||||||
}
|
|
||||||
|
|
||||||
private string settingSearch = "AU-search";
|
_secret = SettingService.GetSetting(settings, "JwtOptions:Secret", "");
|
||||||
|
_togglesecret = SharedLocalizer["ShowPassword"];
|
||||||
|
_issuer = SettingService.GetSetting(settings, "JwtOptions:Issuer", PageState.Uri.Scheme + "://" + PageState.Alias.Name);
|
||||||
|
_audience = SettingService.GetSetting(settings, "JwtOptions:Audience", "");
|
||||||
|
_lifetime = SettingService.GetSetting(settings, "JwtOptions:Lifetime", "20"); }
|
||||||
|
}
|
||||||
|
|
||||||
private async Task LoadSettingsAsync()
|
private async Task LoadUsersAsync(bool load)
|
||||||
{
|
{
|
||||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
if (load)
|
||||||
_search = SettingService.GetSetting(settings, settingSearch, "");
|
{
|
||||||
}
|
allusers = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
var hosts = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Host);
|
||||||
|
allusers.AddRange(hosts);
|
||||||
|
allusers = allusers.OrderBy(u => u.User.DisplayName).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UpdateSettingsAsync()
|
users = allusers;
|
||||||
{
|
if (!string.IsNullOrEmpty(_search))
|
||||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
{
|
||||||
SettingService.SetSetting(settings, settingSearch, _search);
|
users = users.Where(item =>
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
(
|
||||||
}
|
item.User.Username.Contains(_search, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
item.User.Email.Contains(_search, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
item.User.DisplayName.Contains(_search, StringComparison.OrdinalIgnoreCase)
|
||||||
|
)
|
||||||
|
).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnSearch()
|
||||||
|
{
|
||||||
|
await UpdateUserSettingsAsync();
|
||||||
|
await LoadUsersAsync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteUser(UserRole UserRole)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var user = await UserService.GetUserAsync(UserRole.UserId, PageState.Site.SiteId);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId);
|
||||||
|
await logger.LogInformation("User Deleted {User}", UserRole.User);
|
||||||
|
await LoadUsersAsync(true);
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Deleting User {User} {Error}", UserRole.User, ex.Message);
|
||||||
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string settingSearch = "AU-search";
|
||||||
|
|
||||||
|
private async Task LoadUserSettingsAsync()
|
||||||
|
{
|
||||||
|
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||||
|
_search = SettingService.GetSetting(settings, settingSearch, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateUserSettingsAsync()
|
||||||
|
{
|
||||||
|
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||||
|
SettingService.SetSetting(settings, settingSearch, _search);
|
||||||
|
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveSiteSettings()
|
private async Task SaveSiteSettings()
|
||||||
{
|
{
|
||||||
@ -151,7 +533,59 @@ else
|
|||||||
var site = PageState.Site;
|
var site = PageState.Site;
|
||||||
site.AllowRegistration = bool.Parse(_allowregistration);
|
site.AllowRegistration = bool.Parse(_allowregistration);
|
||||||
await SiteService.UpdateSiteAsync(site);
|
await SiteService.UpdateSiteAsync(site);
|
||||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||||
|
settings = SettingService.SetSetting(settings, "LoginOptions:AllowSiteLogin", _allowsitelogin, false);
|
||||||
|
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
settings = SettingService.SetSetting(settings, "LoginOptions:TwoFactor", _twofactor, false);
|
||||||
|
settings = SettingService.SetSetting(settings, "LoginOptions:CookieName", _cookiename, true);
|
||||||
|
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredLength", _minimumlength, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", _uniquecharacters, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireDigit", _requiredigit, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireUppercase", _requireupper, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireLowercase", _requirelower, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", _requirepunctuation, true);
|
||||||
|
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", _maximumfailures, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", TimeSpan.FromMinutes(Convert.ToInt64(_lockoutduration)).ToString(), true);
|
||||||
|
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderType", _providertype, false);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderName", _providername, false);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Authority", _authority, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:MetadataUrl", _metadataurl, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:AuthorizationUrl", _authorizationurl, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:TokenUrl", _tokenurl, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:UserInfoUrl", _userinfourl, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, 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:IdentifierClaimType", _identifierclaimtype, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_secret) && _secret.Length < 16) _secret = (_secret + "????????????????").Substring(0, 16);
|
||||||
|
settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "JwtOptions:Audience", _audience, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "JwtOptions:Lifetime", _lifetime, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||||
|
await SettingService.ClearSiteSettingsCacheAsync();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_secret))
|
||||||
|
{
|
||||||
|
SiteState.AuthorizationToken = await UserService.GetTokenAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -160,4 +594,54 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ProviderTypeChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
_providertype = (string)e.Value;
|
||||||
|
if (string.IsNullOrEmpty(_providername))
|
||||||
|
{
|
||||||
|
if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||||
|
{
|
||||||
|
_scopes = "openid,profile,email";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_scopes = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CreateToken()
|
||||||
|
{
|
||||||
|
_token = await UserService.GetPersonalAccessTokenAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleClientSecret()
|
||||||
|
{
|
||||||
|
if (_clientsecrettype == "password")
|
||||||
|
{
|
||||||
|
_clientsecrettype = "text";
|
||||||
|
_toggleclientsecret = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_clientsecrettype = "password";
|
||||||
|
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleSecret()
|
||||||
|
{
|
||||||
|
if (_secrettype == "password")
|
||||||
|
{
|
||||||
|
_secrettype = "text";
|
||||||
|
_togglesecret = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_secrettype = "password";
|
||||||
|
_togglesecret = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
@ -110,8 +112,7 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, userid);
|
||||||
userroles = userroles.Where(item => item.UserId == userid).ToList();
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -7,69 +7,73 @@
|
|||||||
@inject IStringLocalizer<Detail> Localizer
|
@inject IStringLocalizer<Detail> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<div class="container">
|
@if (_initialized)
|
||||||
<div class="row mb-1 align-items-center">
|
{
|
||||||
<Label Class="col-sm-3" For="ip" HelpText="The last recorded IP address for this visitor" ResourceKey="IP">IP Address: </Label>
|
<div class="container">
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="ip" class="form-control" @bind="@_ip" readonly />
|
<Label Class="col-sm-3" For="ip" HelpText="The last recorded IP address for this visitor" ResourceKey="IP">IP Address: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="ip" class="form-control" @bind="@_ip" readonly />
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="language" HelpText="The last recorded language for this visitor" ResourceKey="Language">Language: </Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="language" class="form-control" @bind="@_language" readonly />
|
<Label Class="col-sm-3" For="language" HelpText="The last recorded language for this visitor" ResourceKey="Language">Language: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="language" class="form-control" @bind="@_language" readonly />
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="useragent" HelpText="The last recorded user agent for this visitor" ResourceKey="UserAgent">User Agent: </Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="useragent" class="form-control" @bind="@_useragent" readonly />
|
<Label Class="col-sm-3" For="useragent" HelpText="The last recorded user agent for this visitor" ResourceKey="UserAgent">User Agent: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="useragent" class="form-control" @bind="@_useragent" readonly />
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="url" HelpText="The last recorded url for this visitor" ResourceKey="Url">Url: </Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="url" class="form-control" @bind="@_url" readonly />
|
<Label Class="col-sm-3" For="url" HelpText="The last recorded url for this visitor" ResourceKey="Url">Url: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="url" class="form-control" @bind="@_url" readonly />
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="referrer" HelpText="The last recorded referrer for this visitor" ResourceKey="Referrer">Referrer: </Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="referrer" class="form-control" @bind="@_referrer" readonly />
|
<Label Class="col-sm-3" For="referrer" HelpText="The last recorded referrer for this visitor" ResourceKey="Referrer">Referrer: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="referrer" class="form-control" @bind="@_referrer" readonly />
|
||||||
@if (_user != string.Empty)
|
</div>
|
||||||
{
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
@if (_user != string.Empty)
|
||||||
<Label Class="col-sm-3" For="user" HelpText="The last recorded user associated with this visitor" ResourceKey="User">User: </Label>
|
{
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="user" class="form-control" @bind="@_user" readonly />
|
<Label Class="col-sm-3" For="user" HelpText="The last recorded user associated with this visitor" ResourceKey="User">User: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="user" class="form-control" @bind="@_user" readonly />
|
||||||
}
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="visits" HelpText="The total number of visits by this visitor all time" ResourceKey="Visits">Visits: </Label>
|
}
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="visits" class="form-control" @bind="@_visits" readonly />
|
<Label Class="col-sm-3" For="visits" HelpText="The total number of visits by this visitor all time" ResourceKey="Visits">Visits: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="visits" class="form-control" @bind="@_visits" readonly />
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="visited" HelpText="The last recorded date/time when the visitor visited the site" ResourceKey="Visited">Visited: </Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="visited" class="form-control" @bind="@_visited" readonly />
|
<Label Class="col-sm-3" For="visited" HelpText="The last recorded date/time when the visitor visited the site" ResourceKey="Visited">Visited: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="visited" class="form-control" @bind="@_visited" readonly />
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="created" HelpText="The first recorded date/time when this visitor visited the site" ResourceKey="Created">Created: </Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="created" class="form-control" @bind="@_created" readonly />
|
<Label Class="col-sm-3" For="created" HelpText="The first recorded date/time when this visitor visited the site" ResourceKey="Created">Created: </Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="created" class="form-control" @bind="@_created" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@CloseUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private bool _initialized = false;
|
||||||
private int _visitorId;
|
private int _visitorId;
|
||||||
private string _ip = string.Empty;
|
private string _ip = string.Empty;
|
||||||
private string _language = string.Empty;
|
private string _language = string.Empty;
|
||||||
@ -108,6 +112,7 @@
|
|||||||
_user = user.DisplayName;
|
_user = user.DisplayName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -120,4 +125,9 @@
|
|||||||
AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private string CloseUrl()
|
||||||
|
{
|
||||||
|
return (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : NavigateUrl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject IVisitorService VisitorService
|
@inject IVisitorService VisitorService
|
||||||
@inject ISiteService SiteService
|
@inject ISiteService SiteService
|
||||||
|
@inject ISettingService SettingService
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@ -16,13 +17,13 @@ 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-6">
|
<div class="col-sm-6">
|
||||||
<select id="type" class="form-select custom-select" @onchange="(e => TypeChanged(e))">
|
<select id="type" class="form-select custom-select" value="@_type" @onchange="(e => TypeChanged(e))">
|
||||||
<option value="false">@Localizer["AllVisitors"]</option>
|
<option value="visitors">@Localizer["AllVisitors"]</option>
|
||||||
<option value="true">@Localizer["UsersOnly"]</option>
|
<option value="users">@Localizer["UsersOnly"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<select id="type" class="form-select custom-select" @onchange="(e => DateChanged(e))">
|
<select id="days" class="form-select custom-select" value="@_days" @onchange="(e => DaysChanged(e))">
|
||||||
<option value="1">@Localizer["PastDay"]</option>
|
<option value="1">@Localizer["PastDay"]</option>
|
||||||
<option value="7">@Localizer["PastWeek"]</option>
|
<option value="7">@Localizer["PastWeek"]</option>
|
||||||
<option value="30">@Localizer["PastMonth"]</option>
|
<option value="30">@Localizer["PastMonth"]</option>
|
||||||
@ -31,7 +32,7 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<Pager Items="@_visitors">
|
<Pager Items="@_visitors" CurrentPage="@_page.ToString()" OnPageChange="OnPageChange">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@Localizer["IP"]</th>
|
<th>@Localizer["IP"]</th>
|
||||||
@ -42,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())" 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)
|
||||||
@ -56,18 +57,39 @@ else
|
|||||||
<td>@context.CreatedOn</td>
|
<td>@context.CreatedOn</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
||||||
<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="visitortracking" HelpText="Specify if visitor tracking is enabled" ResourceKey="VisitorTracking">Visitor Tracking Enabled? </Label>
|
<Label Class="col-sm-3" For="tracking" HelpText="Specify if visitor tracking is enabled" ResourceKey="Tracking">Tracking Enabled? </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="visitortracking" class="form-select" @bind="@_visitortracking" >
|
<select id="tracking" class="form-select" @bind="@_tracking" >
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="filter" HelpText="Comma delimited list of terms which may exist in IP addresses, user agents, or languages identifying visitors which should not be tracked" ResourceKey="Filter">Filter: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="filter" class="form-control" @bind="@_filter" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="retention" HelpText="Number of days of visitor activity to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="retention" class="form-control" @bind="@_retention" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="correlation" HelpText="Indicate if new visitors to this site should be correlated based on their IP Address" ResourceKey="Correlation">Correlate Visitors? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="correlation" class="form-select" @bind="@_correlation">
|
||||||
|
<option value="true">@SharedLocalizer["True"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["False"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||||
@ -76,24 +98,46 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private bool _users = false;
|
private string _type = "visitors";
|
||||||
private int _days = 1;
|
private int _days = 1;
|
||||||
|
private int _page = 1;
|
||||||
private List<Visitor> _visitors;
|
private List<Visitor> _visitors;
|
||||||
private string _visitortracking;
|
private string _tracking;
|
||||||
|
private string _filter = "";
|
||||||
|
private string _retention = "";
|
||||||
|
private string _correlation = "true";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
|
if (PageState.QueryString.ContainsKey("type"))
|
||||||
|
{
|
||||||
|
_type = PageState.QueryString["type"];
|
||||||
|
}
|
||||||
|
if (PageState.QueryString.ContainsKey("days") && int.TryParse(PageState.QueryString["days"], out int days))
|
||||||
|
{
|
||||||
|
_days = days;
|
||||||
|
}
|
||||||
|
if (PageState.QueryString.ContainsKey("page") && int.TryParse(PageState.QueryString["page"], out int page))
|
||||||
|
{
|
||||||
|
_page = page;
|
||||||
|
}
|
||||||
|
|
||||||
await GetVisitors();
|
await GetVisitors();
|
||||||
_visitortracking = PageState.Site.VisitorTracking.ToString();
|
|
||||||
|
_tracking = PageState.Site.VisitorTracking.ToString();
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
|
_filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter);
|
||||||
|
_retention = SettingService.GetSetting(settings, "VisitorRetention", "30");
|
||||||
|
_correlation = SettingService.GetSetting(settings, "VisitorCorrelation", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void TypeChanged(ChangeEventArgs e)
|
private async void TypeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_users = bool.Parse(e.Value.ToString());
|
_type = e.Value.ToString();
|
||||||
await GetVisitors();
|
await GetVisitors();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
@ -103,7 +147,7 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void DateChanged(ChangeEventArgs e)
|
private async void DaysChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -120,19 +164,26 @@ else
|
|||||||
private async Task GetVisitors()
|
private async Task GetVisitors()
|
||||||
{
|
{
|
||||||
_visitors = await VisitorService.GetVisitorsAsync(PageState.Site.SiteId, DateTime.UtcNow.AddDays(-_days));
|
_visitors = await VisitorService.GetVisitorsAsync(PageState.Site.SiteId, DateTime.UtcNow.AddDays(-_days));
|
||||||
if (_users)
|
if (_type == "users")
|
||||||
{
|
{
|
||||||
_visitors = _visitors.Where(item => item.UserId != null).ToList();
|
_visitors = _visitors.Where(item => item.UserId != null).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveSiteSettings()
|
private async Task SaveSiteSettings()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var site = PageState.Site;
|
var site = PageState.Site;
|
||||||
site.VisitorTracking = bool.Parse(_visitortracking);
|
site.VisitorTracking = bool.Parse(_tracking);
|
||||||
await SiteService.UpdateSiteAsync(site);
|
await SiteService.UpdateSiteAsync(site);
|
||||||
|
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
|
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "VisitorCorrelation", _correlation, true);
|
||||||
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
|
|
||||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -141,4 +192,9 @@ else
|
|||||||
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnPageChange(int page)
|
||||||
|
{
|
||||||
|
_page = page;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@namespace Oqtane.Modules.Controls
|
@namespace Oqtane.Modules.Controls
|
||||||
|
@using System.Net
|
||||||
@inherits LocalizableComponent
|
@inherits LocalizableComponent
|
||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
|
|
||||||
@ -71,6 +72,9 @@
|
|||||||
[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 OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
base.OnParametersSet();
|
base.OnParametersSet();
|
||||||
@ -116,9 +120,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
|
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
|
||||||
_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);
|
||||||
_authorized = IsAuthorized();
|
if (!string.IsNullOrEmpty(ReturnUrl))
|
||||||
|
{
|
||||||
|
_url += ((_url.Contains("?")) ? "&" : "?") + $"returnurl={WebUtility.UrlEncode(ReturnUrl)}";
|
||||||
|
}
|
||||||
|
_authorized = IsAuthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsAuthorized()
|
private bool IsAuthorized()
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
<input type="file" id="@_fileinputid" name="file" accept="@_filter" />
|
<input type="file" id="@_fileinputid" name="file" accept="@_filter" />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="col mt-2 text-center">
|
<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="UploadFile">@SharedLocalizer["Upload"]</button>
|
||||||
@if (ShowFiles && GetFileId() != -1)
|
@if (ShowFiles && GetFileId() != -1)
|
||||||
{
|
{
|
||||||
@ -86,315 +86,352 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _id;
|
private string _id;
|
||||||
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;
|
||||||
private string _progressinfoid = string.Empty;
|
private string _progressinfoid = string.Empty;
|
||||||
private string _progressbarid = string.Empty;
|
private string _progressbarid = string.Empty;
|
||||||
private string _filter = "*";
|
private string _filter = "*";
|
||||||
private bool _haseditpermission = false;
|
private bool _haseditpermission = false;
|
||||||
private string _image = string.Empty;
|
private string _image = string.Empty;
|
||||||
private File _file = null;
|
private File _file = null;
|
||||||
private string _guid;
|
private string _guid;
|
||||||
private string _message = string.Empty;
|
private string _message = string.Empty;
|
||||||
private MessageType _messagetype;
|
private MessageType _messagetype;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility
|
public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public int FolderId { get; set; } = -1; // optional - for setting a specific default folder by folderid
|
public int FolderId { get; set; } = -1; // optional - for setting a specific default folder by folderid
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Folder { get; set; } = ""; // optional - for setting a specific default folder by folder path
|
public string Folder { get; set; } = ""; // optional - for setting a specific default folder by folder path
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public int FileId { get; set; } = -1; // optional - for selecting a specific file by default
|
public int FileId { get; set; } = -1; // optional - for selecting a specific file by default
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif"
|
public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif"
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool ShowFiles { get; set; } = true; // optional - for indicating whether a list of files should be displayed - default is true
|
public bool ShowFiles { get; set; } = true; // optional - for indicating whether a list of files should be displayed - default is true
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool ShowUpload { get; set; } = true; // optional - for indicating whether a Upload controls should be displayed - default is true
|
public bool ShowUpload { get; set; } = true; // optional - for indicating whether a Upload controls should be displayed - default is true
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true
|
public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool ShowImage { get; set; } = true; // optional - for indicating whether an image thumbnail should be displayed - default is true
|
public bool ShowImage { get; set; } = true; // optional - for indicating whether an image thumbnail should be displayed - default is true
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool ShowSuccess { get; set; } = false; // optional - for indicating whether a success message should be displayed upon successful upload - default is false
|
public bool ShowSuccess { get; set; } = false; // optional - for indicating whether a success message should be displayed upon successful upload - default is false
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false
|
public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<int> OnUpload { get; set; } // optional - executes a method in the calling component when a file is uploaded
|
public EventCallback<int> OnUpload { get; set; } // optional - executes a method in the calling component when a file is uploaded
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<int> OnSelect { get; set; } // optional - executes a method in the calling component when a file is selected
|
public EventCallback<int> OnSelect { get; set; } // optional - executes a method in the calling component when a file is selected
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<int> OnDelete { get; set; } // optional - executes a method in the calling component when a file is deleted
|
public EventCallback<int> OnDelete { get; set; } // optional - executes a method in the calling component when a file is deleted
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(Id))
|
if (!string.IsNullOrEmpty(Id))
|
||||||
{
|
{
|
||||||
_id = 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)
|
||||||
{
|
{
|
||||||
ShowFiles = false;
|
ShowFiles = false;
|
||||||
ShowFolders = false;
|
ShowFolders = false;
|
||||||
Filter = "nupkg";
|
Filter = "nupkg";
|
||||||
ShowSuccess = true;
|
ShowSuccess = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ShowFiles)
|
if (!ShowFiles)
|
||||||
{
|
{
|
||||||
ShowImage = false;
|
ShowImage = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
|
_folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Folder) && Folder != Constants.PackagesFolder)
|
if (!string.IsNullOrEmpty(Folder) && Folder != Constants.PackagesFolder)
|
||||||
{
|
{
|
||||||
Folder folder = await FolderService.GetFolderAsync(ModuleState.SiteId, Folder);
|
Folder folder = await FolderService.GetFolderAsync(ModuleState.SiteId, Folder);
|
||||||
if (folder != null)
|
if (folder != null)
|
||||||
{
|
{
|
||||||
FolderId = folder.FolderId;
|
FolderId = folder.FolderId;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FolderId = -1;
|
FolderId = -1;
|
||||||
_message = "Folder Path " + Folder + "Does Not Exist";
|
_message = "Folder Path " + Folder + "Does Not Exist";
|
||||||
_messagetype = MessageType.Error;
|
_messagetype = MessageType.Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FileId != -1)
|
if (FileId != -1)
|
||||||
{
|
{
|
||||||
File file = await FileService.GetFileAsync(FileId);
|
File file = await FileService.GetFileAsync(FileId);
|
||||||
if (file != null)
|
if (file != null)
|
||||||
{
|
{
|
||||||
FolderId = file.FolderId;
|
FolderId = file.FolderId;
|
||||||
await OnSelect.InvokeAsync(FileId);
|
await OnSelect.InvokeAsync(FileId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FileId = -1; // file does not exist
|
FileId = -1; // file does not exist
|
||||||
_message = "FileId " + FileId.ToString() + "Does Not Exist";
|
_message = "FileId " + FileId.ToString() + "Does Not Exist";
|
||||||
_messagetype = MessageType.Error;
|
_messagetype = MessageType.Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await SetImage();
|
await SetImage();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Filter))
|
if (!string.IsNullOrEmpty(Filter))
|
||||||
{
|
{
|
||||||
_filter = "." + Filter.Replace(",", ",.");
|
_filter = "." + Filter.Replace(",", ",.");
|
||||||
}
|
}
|
||||||
|
|
||||||
await GetFiles();
|
await GetFiles();
|
||||||
|
|
||||||
// create unique id for component
|
// create unique id for component
|
||||||
_guid = Guid.NewGuid().ToString("N");
|
_guid = Guid.NewGuid().ToString("N");
|
||||||
_fileinputid = _guid + "FileInput";
|
_fileinputid = _guid + "FileInput";
|
||||||
_progressinfoid = _guid + "ProgressInfo";
|
_progressinfoid = _guid + "ProgressInfo";
|
||||||
_progressbarid = _guid + "ProgressBar";
|
_progressbarid = _guid + "ProgressBar";
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GetFiles()
|
private async Task GetFiles()
|
||||||
{
|
{
|
||||||
_haseditpermission = false;
|
_haseditpermission = false;
|
||||||
if (Folder == Constants.PackagesFolder)
|
if (Folder == Constants.PackagesFolder)
|
||||||
{
|
{
|
||||||
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host);
|
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host);
|
||||||
_files = new List<File>();
|
_files = new List<File>();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
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.Permissions);
|
||||||
_files = await FileService.GetFilesAsync(FolderId);
|
_files = await FileService.GetFilesAsync(FolderId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_haseditpermission = false;
|
_haseditpermission = false;
|
||||||
_files = new List<File>();
|
_files = new List<File>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_filter != "*")
|
if (_filter != "*")
|
||||||
{
|
{
|
||||||
List<File> filtered = new List<File>();
|
List<File> filtered = new List<File>();
|
||||||
foreach (File file in _files)
|
foreach (File file in _files)
|
||||||
{
|
{
|
||||||
if (_filter.ToUpper().IndexOf("." + file.Extension.ToUpper()) != -1)
|
if (_filter.ToUpper().IndexOf("." + file.Extension.ToUpper()) != -1)
|
||||||
{
|
{
|
||||||
filtered.Add(file);
|
filtered.Add(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_files = filtered;
|
_files = filtered;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task FolderChanged(ChangeEventArgs e)
|
private async Task FolderChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FolderId = int.Parse((string)e.Value);
|
FolderId = int.Parse((string)e.Value);
|
||||||
await GetFiles();
|
await GetFiles();
|
||||||
FileId = -1;
|
FileId = -1;
|
||||||
_file = null;
|
_file = null;
|
||||||
_image = string.Empty;
|
_image = string.Empty;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Files {Error}", ex.Message);
|
await logger.LogError(ex, "Error Loading Files {Error}", ex.Message);
|
||||||
_message = Localizer["Error.File.Load"];
|
_message = Localizer["Error.File.Load"];
|
||||||
_messagetype = MessageType.Error;
|
_messagetype = MessageType.Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task FileChanged(ChangeEventArgs e)
|
private async Task FileChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
FileId = int.Parse((string)e.Value);
|
FileId = int.Parse((string)e.Value);
|
||||||
if (FileId != -1)
|
if (FileId != -1)
|
||||||
{
|
{
|
||||||
await OnSelect.InvokeAsync(FileId);
|
await OnSelect.InvokeAsync(FileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
await SetImage();
|
await SetImage();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetImage()
|
private async Task SetImage()
|
||||||
{
|
{
|
||||||
_image = string.Empty;
|
_image = string.Empty;
|
||||||
_file = null;
|
_file = null;
|
||||||
if (FileId != -1)
|
if (FileId != -1)
|
||||||
{
|
{
|
||||||
_file = await FileService.GetFileAsync(FileId);
|
_file = await FileService.GetFileAsync(FileId);
|
||||||
if (_file != null && ShowImage && _file.ImageHeight != 0 && _file.ImageWidth != 0)
|
if (_file != null && ShowImage && _file.ImageHeight != 0 && _file.ImageWidth != 0)
|
||||||
{
|
{
|
||||||
var maxwidth = 200;
|
var maxwidth = 200;
|
||||||
var maxheight = 200;
|
var maxheight = 200;
|
||||||
|
|
||||||
var ratioX = (double)maxwidth / (double)_file.ImageWidth;
|
var ratioX = (double)maxwidth / (double)_file.ImageWidth;
|
||||||
var ratioY = (double)maxheight / (double)_file.ImageHeight;
|
var ratioY = (double)maxheight / (double)_file.ImageHeight;
|
||||||
var ratio = ratioX < ratioY ? ratioX : ratioY;
|
var ratio = ratioX < ratioY ? ratioX : ratioY;
|
||||||
|
|
||||||
_image = "<img src=\"" + _file.Url + "\" alt=\"" + _file.Name +
|
_image = "<img src=\"" + _file.Url + "\" alt=\"" + _file.Name +
|
||||||
"\" width=\"" + Convert.ToInt32(_file.ImageWidth * ratio).ToString() +
|
"\" width=\"" + Convert.ToInt32(_file.ImageWidth * ratio).ToString() +
|
||||||
"\" height=\"" + Convert.ToInt32(_file.ImageHeight * ratio).ToString() + "\" />";
|
"\" height=\"" + Convert.ToInt32(_file.ImageHeight * ratio).ToString() + "\" />";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UploadFile()
|
private async Task UploadFile()
|
||||||
{
|
{
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
var upload = await interop.GetFiles(_fileinputid);
|
var upload = await interop.GetFiles(_fileinputid);
|
||||||
if (upload.Length > 0)
|
if (upload.Length > 0)
|
||||||
{
|
{
|
||||||
try
|
string restricted = "";
|
||||||
{
|
foreach (var file in upload)
|
||||||
string result;
|
{
|
||||||
if (Folder == Constants.PackagesFolder)
|
var extension = (file.LastIndexOf(".") != -1) ? file.Substring(file.LastIndexOf(".") + 1) : "";
|
||||||
{
|
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||||
result = await FileService.UploadFilesAsync(Folder, upload, _guid);
|
{
|
||||||
}
|
restricted += (restricted == "" ? "" : ",") + extension;
|
||||||
else
|
}
|
||||||
{
|
}
|
||||||
result = await FileService.UploadFilesAsync(FolderId, upload, _guid);
|
if (restricted == "")
|
||||||
}
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
if (Folder == Constants.PackagesFolder)
|
||||||
|
{
|
||||||
|
result = await FileService.UploadFilesAsync(Folder, upload, _guid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = await FileService.UploadFilesAsync(FolderId, upload, _guid);
|
||||||
|
}
|
||||||
|
|
||||||
if (result == string.Empty)
|
if (result == string.Empty)
|
||||||
{
|
{
|
||||||
await logger.LogInformation("File Upload Succeeded {Files}", upload);
|
await logger.LogInformation("File Upload Succeeded {Files}", upload);
|
||||||
if (ShowSuccess)
|
if (ShowSuccess)
|
||||||
{
|
{
|
||||||
_message = Localizer["Success.File.Upload"];
|
_message = Localizer["Success.File.Upload"];
|
||||||
_messagetype = MessageType.Success;
|
_messagetype = MessageType.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 == upload[0]).FirstOrDefault();
|
||||||
if (file != null)
|
if (file != null)
|
||||||
{
|
{
|
||||||
FileId = file.FileId;
|
FileId = file.FileId;
|
||||||
await SetImage();
|
await SetImage();
|
||||||
await OnUpload.InvokeAsync(FileId);
|
await OnUpload.InvokeAsync(FileId);
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await logger.LogError("File Upload Failed For {Files}", result.Replace(",", ", "));
|
await logger.LogError("File Upload Failed For {Files}", result.Replace(",", ", "));
|
||||||
|
|
||||||
_message = Localizer["Error.File.Upload"];
|
_message = Localizer["Error.File.Upload"];
|
||||||
_messagetype = MessageType.Error;
|
_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 = Localizer["Message.File.NotSelected"];
|
_message = string.Format(Localizer["Message.File.Restricted"], restricted);
|
||||||
_messagetype = MessageType.Warning;
|
_messagetype = MessageType.Warning;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_message = Localizer["Message.File.NotSelected"];
|
||||||
|
_messagetype = MessageType.Warning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task DeleteFile()
|
private async Task DeleteFile()
|
||||||
{
|
{
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await FileService.DeleteFileAsync(FileId);
|
await FileService.DeleteFileAsync(FileId);
|
||||||
await logger.LogInformation("File Deleted {File}", FileId);
|
await logger.LogInformation("File Deleted {File}", FileId);
|
||||||
await OnDelete.InvokeAsync(FileId);
|
await OnDelete.InvokeAsync(FileId);
|
||||||
|
|
||||||
_message = Localizer["Success.File.Delete"];
|
_message = Localizer["Success.File.Delete"];
|
||||||
_messagetype = MessageType.Success;
|
_messagetype = MessageType.Success;
|
||||||
|
|
||||||
await GetFiles();
|
await GetFiles();
|
||||||
FileId = -1;
|
FileId = -1;
|
||||||
await SetImage();
|
await SetImage();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Deleting File {File} {Error}", FileId, ex.Message);
|
await logger.LogError(ex, "Error Deleting File {File} {Error}", FileId, ex.Message);
|
||||||
|
|
||||||
_message = Localizer["Error.File.Delete"];
|
_message = Localizer["Error.File.Delete"];
|
||||||
_messagetype = MessageType.Error;
|
_messagetype = MessageType.Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetFileId() => FileId;
|
public int GetFileId() => FileId;
|
||||||
|
|
||||||
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,8 @@ else
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string HelpText { get; set; } // optional - tooltip for this label
|
public string HelpText { get; set; } // optional - tooltip for this label
|
||||||
|
|
||||||
private string _spanclass = "app-tooltip";
|
private string _spanclass;
|
||||||
private string _labelclass = "form-label";
|
private string _labelclass;
|
||||||
private string _helptext = string.Empty;
|
private string _helptext = string.Empty;
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
@ -36,11 +36,15 @@ else
|
|||||||
if (!string.IsNullOrEmpty(HelpText))
|
if (!string.IsNullOrEmpty(HelpText))
|
||||||
{
|
{
|
||||||
_helptext = Localize(nameof(HelpText), HelpText);
|
_helptext = Localize(nameof(HelpText), HelpText);
|
||||||
_spanclass += (!string.IsNullOrEmpty(Class)) ? " " + Class : "";
|
_labelclass = "form-label";
|
||||||
|
|
||||||
|
var spanclass = (!string.IsNullOrEmpty(Class)) ? " " + Class : "";
|
||||||
|
_spanclass = "app-tooltip" + spanclass;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_labelclass += (!string.IsNullOrEmpty(Class)) ? " " + Class : "";
|
var labelclass = (!string.IsNullOrEmpty(Class)) ? " " + Class : "";
|
||||||
|
_labelclass = "form-label" + labelclass;
|
||||||
}
|
}
|
||||||
|
|
||||||
var text = Localize("Text", String.Empty);
|
var text = Localize("Text", String.Empty);
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
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]
|
||||||
public string ResourceKey { get; set; }
|
public string ResourceKey { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string ResourceType { get; set; }
|
||||||
|
|
||||||
protected bool IsLocalizable { get; private set; }
|
protected bool IsLocalizable { get; private set; }
|
||||||
|
|
||||||
protected string Localize(string name) => _localizer?[name] ?? name;
|
protected string Localize(string name) => _localizer?[name] ?? name;
|
||||||
@ -27,22 +30,15 @@ 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;
|
return propertyValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (value == String.Empty)
|
// return localized value
|
||||||
{
|
return value;
|
||||||
// Returns default property value (English version)
|
|
||||||
return propertyValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,19 +46,15 @@ namespace Oqtane.Modules.Controls
|
|||||||
{
|
{
|
||||||
IsLocalizable = false;
|
IsLocalizable = false;
|
||||||
|
|
||||||
if (!String.IsNullOrEmpty(ResourceKey) && ModuleState?.ModuleType != null)
|
if (String.IsNullOrEmpty(ResourceType))
|
||||||
{
|
{
|
||||||
var moduleType = Type.GetType(ModuleState.ModuleType);
|
ResourceType = ModuleState?.ModuleType;
|
||||||
if (moduleType != null)
|
}
|
||||||
{
|
|
||||||
using (var scope = ServiceActivator.GetScope())
|
|
||||||
{
|
|
||||||
var localizerFactory = scope.ServiceProvider.GetService<IStringLocalizerFactory>();
|
|
||||||
_localizer = localizerFactory.Create(moduleType);
|
|
||||||
|
|
||||||
IsLocalizable = true;
|
if (!String.IsNullOrEmpty(ResourceKey) && !String.IsNullOrEmpty(ResourceType))
|
||||||
}
|
{
|
||||||
}
|
_localizer = LocalizerFactory.Create(ResourceType);
|
||||||
|
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)
|
||||||
@ -48,53 +49,67 @@
|
|||||||
<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>
|
||||||
}
|
}
|
||||||
@if (Format == "Table" && Row != null)
|
@if (Format == "Table" && Row != null)
|
||||||
{
|
{
|
||||||
<table class="@Class">
|
<div class="table-responsive">
|
||||||
<thead>
|
<table class="@Class">
|
||||||
<tr>@Header</tr>
|
<thead>
|
||||||
</thead>
|
<tr class="@RowClass">@Header</tr>
|
||||||
<tbody>
|
</thead>
|
||||||
@foreach (var item in ItemList)
|
<tbody>
|
||||||
{
|
@foreach (var item in ItemList)
|
||||||
<tr>@Row(item)</tr>
|
{
|
||||||
@if (Detail != null)
|
<tr class="@RowClass">@Row(item)</tr>
|
||||||
{
|
@if (Detail != null)
|
||||||
<tr>@Detail(item)</tr>
|
{
|
||||||
}
|
<tr>@Detail(item)</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
}
|
||||||
</table>
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="@RowClass">@Footer</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
@if (Format == "Grid" && Row != null)
|
@if (Format == "Grid" && Row != null)
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
if (ItemList != null)
|
int rows = 0;
|
||||||
|
int cols = 0;
|
||||||
|
if (ItemList != null)
|
||||||
{
|
{
|
||||||
count = (int)Math.Ceiling(ItemList.Count() / (decimal)_columns) * _columns;
|
if (_columns == 0)
|
||||||
|
{
|
||||||
|
count = ItemList.Count();
|
||||||
|
rows = 1;
|
||||||
|
cols = count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
count = (int)Math.Ceiling(ItemList.Count() / (decimal)_columns) * _columns;
|
||||||
|
rows = count / _columns;
|
||||||
|
cols = _columns;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
<div class="@Class">
|
<div class="@Class">
|
||||||
@if (Header != null)
|
@for (int row = 0; row < rows; row++)
|
||||||
{
|
{
|
||||||
<div class="row"><div class="col">@Header</div></div>
|
<div class="@RowClass">
|
||||||
}
|
@for (int col = 0; col < cols; col++)
|
||||||
@for (int row = 0; row < (count / _columns); row++)
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
@for (int col = 0; col < _columns; col++)
|
|
||||||
{
|
{
|
||||||
int index = (row * _columns) + col;
|
int index = (row * _columns) + col;
|
||||||
if (index < ItemList.Count())
|
if (index < ItemList.Count())
|
||||||
{
|
{
|
||||||
<div class="col">@Row(ItemList.ElementAt(index))</div>
|
<div class="@ColumnClass">@Row(ItemList.ElementAt(index))</div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="col"> </div>
|
<div> </div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@ -107,8 +122,8 @@
|
|||||||
<li class="page-item@((_page > 1) ? "" : " disabled")">
|
<li class="page-item@((_page > 1) ? "" : " 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)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_page > _displayPages) ? "" : " disabled")">
|
<li class="page-item@((_page > _displayPages) ? "" : " 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>
|
||||||
@ -135,7 +150,7 @@
|
|||||||
<li class="page-item@((_page < _pages) ? "" : " disabled")">
|
<li class="page-item@((_page < _pages) ? "" : " 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)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_endPage < _pages) ? "" : " disabled")">
|
<li class="page-item@((_endPage < _pages) ? "" : " 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>
|
||||||
@ -145,58 +160,76 @@
|
|||||||
<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">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 int _pages = 0;
|
private IStringLocalizer Localizer;
|
||||||
private int _page = 1;
|
private int _pages = 0;
|
||||||
private int _maxItems = 10;
|
private int _page = 1;
|
||||||
private int _displayPages = 5;
|
private int _maxItems = 10;
|
||||||
private int _startPage = 0;
|
private int _displayPages = 5;
|
||||||
private int _endPage = 0;
|
private int _startPage = 0;
|
||||||
private int _columns = 1;
|
private int _endPage = 0;
|
||||||
|
private int _columns = 0;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Format { get; set; } // Table or Grid
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Toolbar { get; set; } // Top, Bottom or Both
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment Header { get; set; } = null; // only applicable to Table layouts
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<TableItem> Row { get; set; } = null; // required
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Format { get; set; } // Table or Grid
|
public RenderFragment Footer { get; set; } = null; // only applicable to Table layouts
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Toolbar { get; set; } // Top, Bottom or Both
|
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment Header { get; set; } = null;
|
public IEnumerable<TableItem> Items { get; set; } // the IEnumerable data source
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment<TableItem> Row { get; set; } = null;
|
public string PageSize { get; set; } // number of items to display on a page
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
|
public string Columns { get; set; } // only applicable to Grid layouts - default is zero indicating use responsive behavior
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public IEnumerable<TableItem> Items { get; set; } // the IEnumerable data source
|
public string CurrentPage { get; set; } // sets the initial page to display
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string PageSize { get; set; } // number of items to display on a page
|
public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Columns { get; set; } // only applicable to Grid layouts
|
public string Class { get; set; } // class for the containing element - ie. <table> for Table or <div> for Grid
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string CurrentPage { get; set; } // optional property to set the initial page to display
|
public string RowClass { get; set; } // class for row element - ie. <tr> for Table or <div> for Grid
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection
|
public string ColumnClass { get; set; } // class for column element - only applicable to Grid format
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Class { get; set; }
|
public Action<int> OnPageChange { get; set; } // a method to be executed in the calling component when the page changes
|
||||||
|
|
||||||
private IEnumerable<TableItem> ItemList { get; set; }
|
private IEnumerable<TableItem> ItemList { get; set; }
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
|
Localizer = LocalizerFactory.Create(GetType().FullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
if (string.IsNullOrEmpty(Format))
|
if (string.IsNullOrEmpty(Format))
|
||||||
{
|
{
|
||||||
Format = "Table";
|
Format = "Table";
|
||||||
@ -215,11 +248,35 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Class = "container-fluid px-0";
|
Class = "container-fluid";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(PageSize))
|
if (string.IsNullOrEmpty(RowClass))
|
||||||
|
{
|
||||||
|
if (Format == "Table")
|
||||||
|
{
|
||||||
|
RowClass = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RowClass = "row";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(ColumnClass))
|
||||||
|
{
|
||||||
|
if (Format == "Table")
|
||||||
|
{
|
||||||
|
ColumnClass = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ColumnClass = "col";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(PageSize))
|
||||||
{
|
{
|
||||||
_maxItems = int.Parse(PageSize);
|
_maxItems = int.Parse(PageSize);
|
||||||
}
|
}
|
||||||
@ -242,6 +299,7 @@
|
|||||||
{
|
{
|
||||||
_page = 1;
|
_page = 1;
|
||||||
}
|
}
|
||||||
|
if (_page < 1) _page = 1;
|
||||||
|
|
||||||
_startPage = 0;
|
_startPage = 0;
|
||||||
_endPage = 0;
|
_endPage = 0;
|
||||||
@ -266,6 +324,7 @@
|
|||||||
{
|
{
|
||||||
_endPage = _pages;
|
_endPage = _pages;
|
||||||
}
|
}
|
||||||
|
OnPageChange?.Invoke(_page);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,160 +100,172 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _permissionnames = string.Empty;
|
private string _permissionnames = string.Empty;
|
||||||
private List<Role> _roles;
|
private List<Role> _roles;
|
||||||
private List<PermissionString> _permissions;
|
private List<PermissionString> _permissions;
|
||||||
private List<User> _users = new List<User>();
|
private List<User> _users = new List<User>();
|
||||||
private string _username = string.Empty;
|
private string _username = string.Empty;
|
||||||
private string _message = string.Empty;
|
private string _message = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string EntityName { get; set; }
|
public string EntityName { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string PermissionNames { get; set; }
|
public string PermissionNames { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Permissions { get; set; }
|
public string Permissions { get; set; }
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(PermissionNames))
|
if (string.IsNullOrEmpty(PermissionNames))
|
||||||
{
|
{
|
||||||
_permissionnames = Shared.PermissionNames.View + "," + Shared.PermissionNames.Edit;
|
_permissionnames = Shared.PermissionNames.View + "," + Shared.PermissionNames.Edit;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_permissionnames = PermissionNames;
|
_permissionnames = PermissionNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId);
|
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId, true);
|
||||||
_roles.Insert(0, new Role { Name = RoleNames.Everyone });
|
if (!UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
_roles.RemoveAll(item => item.Name == RoleNames.Host);
|
||||||
|
}
|
||||||
|
|
||||||
_permissions = new List<PermissionString>();
|
_permissions = new List<PermissionString>();
|
||||||
|
|
||||||
foreach (string permissionname in _permissionnames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
foreach (string permissionname in _permissionnames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
{
|
{
|
||||||
// initialize with admin role
|
// initialize with admin role
|
||||||
_permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = RoleNames.Admin });
|
_permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = RoleNames.Admin });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Permissions))
|
if (!string.IsNullOrEmpty(Permissions))
|
||||||
{
|
{
|
||||||
// populate permissions
|
// populate permissions
|
||||||
foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions))
|
foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions))
|
||||||
{
|
{
|
||||||
if (_permissions.Find(item => item.PermissionName == permissionstring.PermissionName) != null)
|
if (_permissions.Find(item => item.PermissionName == permissionstring.PermissionName) != null)
|
||||||
{
|
{
|
||||||
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions;
|
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permissionstring.Permissions.Contains("["))
|
if (permissionstring.Permissions.Contains("["))
|
||||||
{
|
{
|
||||||
foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries))
|
foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
{
|
{
|
||||||
if (user.Contains("]"))
|
if (user.Contains("]"))
|
||||||
{
|
{
|
||||||
var userid = int.Parse(user.Substring(0, user.IndexOf("]")));
|
var userid = int.Parse(user.Substring(0, user.IndexOf("]")));
|
||||||
if (_users.Where(item => item.UserId == userid).FirstOrDefault() == null)
|
if (_users.Where(item => item.UserId == userid).FirstOrDefault() == null)
|
||||||
{
|
{
|
||||||
_users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId));
|
_users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool? GetPermissionValue(string permissions, string securityKey)
|
private bool? GetPermissionValue(string permissions, string securityKey)
|
||||||
{
|
{
|
||||||
if ((";" + permissions + ";").Contains(";" + "!" + securityKey + ";"))
|
if ((";" + permissions + ";").Contains(";" + "!" + securityKey + ";"))
|
||||||
{
|
{
|
||||||
return false; // deny permission
|
return false; // deny permission
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((";" + permissions + ";").Contains(";" + securityKey + ";"))
|
if ((";" + permissions + ";").Contains(";" + securityKey + ";"))
|
||||||
{
|
{
|
||||||
return true; // grant permission
|
return true; // grant permission
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return null; // not specified
|
return null; // not specified
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool GetPermissionDisabled(string roleName)
|
private bool GetPermissionDisabled(string roleName)
|
||||||
=> roleName == RoleNames.Admin
|
=> (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) ? true : false;
|
||||||
? true
|
|
||||||
: false;
|
|
||||||
|
|
||||||
private async Task AddUser()
|
private async Task AddUser()
|
||||||
{
|
{
|
||||||
if (_users.Where(item => item.Username == _username).FirstOrDefault() == null)
|
if (_users.Where(item => item.Username == _username).FirstOrDefault() == null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var user = await UserService.GetUserAsync(_username, ModuleState.SiteId);
|
var user = await UserService.GetUserAsync(_username, ModuleState.SiteId);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
_users.Add(user);
|
_users.Add(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
_message = Localizer["Message.Username.DontExist"];
|
_message = Localizer["Message.Username.DontExist"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_username = string.Empty;
|
_username = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PermissionChanged(bool? value, string permissionName, string securityId)
|
private void PermissionChanged(bool? value, string permissionName, string securityId)
|
||||||
{
|
{
|
||||||
var selected = value;
|
var selected = value;
|
||||||
var permission = _permissions.Find(item => item.PermissionName == permissionName);
|
var permission = _permissions.Find(item => item.PermissionName == permissionName);
|
||||||
if (permission != null)
|
if (permission != null)
|
||||||
{
|
{
|
||||||
var ids = permission.Permissions.Split(';').ToList();
|
var ids = permission.Permissions.Split(';').ToList();
|
||||||
|
|
||||||
ids.Remove(securityId); // remove grant permission
|
ids.Remove(securityId); // remove grant permission
|
||||||
ids.Remove("!" + securityId); // remove deny permission
|
ids.Remove("!" + securityId); // remove deny permission
|
||||||
|
|
||||||
switch (selected)
|
switch (selected)
|
||||||
{
|
{
|
||||||
case true:
|
case true:
|
||||||
ids.Add(securityId); // add grant permission
|
ids.Add(securityId); // add grant permission
|
||||||
break;
|
break;
|
||||||
case false:
|
case false:
|
||||||
ids.Add("!" + securityId); // add deny permission
|
ids.Add("!" + securityId); // add deny permission
|
||||||
break;
|
break;
|
||||||
case null:
|
case null:
|
||||||
break; // permission not specified
|
break; // permission not specified
|
||||||
}
|
}
|
||||||
|
|
||||||
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionName)].Permissions = string.Join(";", ids.ToArray());
|
_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 UserSecurity.SetPermissionStrings(_permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidatePermissions()
|
private void ValidatePermissions()
|
||||||
{
|
{
|
||||||
PermissionString permission;
|
PermissionString permission;
|
||||||
for (int i = 0; i < _permissions.Count; i++)
|
for (int i = 0; i < _permissions.Count; i++)
|
||||||
{
|
{
|
||||||
permission = _permissions[i];
|
permission = _permissions[i];
|
||||||
List<string> ids = permission.Permissions.Split(';').ToList();
|
List<string> ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||||
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
||||||
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
|
ids.Remove("!" + RoleNames.Unauthenticated); // remove deny unauthenticated
|
||||||
permission.Permissions = string.Join(";", ids.ToArray());
|
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
ids.Remove("!" + RoleNames.Admin); // remove deny administrators
|
||||||
|
ids.Remove("!" + RoleNames.Host); // remove deny host users
|
||||||
|
if (!ids.Contains(RoleNames.Host) && !ids.Contains(RoleNames.Admin))
|
||||||
|
{
|
||||||
|
// add administrators role if host user role is not assigned
|
||||||
|
ids.Add(RoleNames.Admin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
permission.Permissions = string.Join(";", ids.ToArray());
|
||||||
_permissions[i] = permission;
|
_permissions[i] = permission;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,230 +5,276 @@
|
|||||||
<div class="row" style="margin-bottom: 50px;">
|
<div class="row" style="margin-bottom: 50px;">
|
||||||
<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" />
|
||||||
{
|
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
<br />
|
||||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
}
|
||||||
<br />
|
<div class="d-flex justify-content-center mb-2">
|
||||||
}
|
@if (AllowRawHtml)
|
||||||
<div class="d-flex justify-content-center mb-2">
|
{
|
||||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>@((MarkupString)" ")
|
||||||
<button type="button" class="btn btn-primary" @onclick="InsertImage">@Localizer["InsertImage"]</button>
|
}
|
||||||
@if (_filemanagervisible)
|
@if (AllowFileManagement)
|
||||||
{
|
{
|
||||||
@((MarkupString)" ")
|
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="CloseFileManager">@Localizer["Close"]</button>
|
}
|
||||||
}
|
@if (_richfilemanager)
|
||||||
</div>
|
{
|
||||||
}
|
@((MarkupString)" ")
|
||||||
<div class="row">
|
<button type="button" class="btn btn-secondary" @onclick="CloseRichFileManager">@Localizer["Close"]</button>
|
||||||
<div class="col">
|
}
|
||||||
<div @ref="@_toolBar">
|
</div>
|
||||||
@if (ToolbarContent != null)
|
<div class="row">
|
||||||
{
|
<div class="col">
|
||||||
@ToolbarContent
|
<div @ref="@_toolBar">
|
||||||
}
|
@if (ToolbarContent != null)
|
||||||
else
|
{
|
||||||
{
|
@ToolbarContent
|
||||||
<select class="ql-header">
|
}
|
||||||
<option selected=""></option>
|
else
|
||||||
<option value="1"></option>
|
{
|
||||||
<option value="2"></option>
|
<select class="ql-header">
|
||||||
<option value="3"></option>
|
<option selected=""></option>
|
||||||
<option value="4"></option>
|
<option value="1"></option>
|
||||||
<option value="5"></option>
|
<option value="2"></option>
|
||||||
</select>
|
<option value="3"></option>
|
||||||
<span class="ql-formats">
|
<option value="4"></option>
|
||||||
<button class="ql-bold"></button>
|
<option value="5"></option>
|
||||||
<button class="ql-italic"></button>
|
</select>
|
||||||
<button class="ql-underline"></button>
|
<span class="ql-formats">
|
||||||
<button class="ql-strike"></button>
|
<button class="ql-bold"></button>
|
||||||
</span>
|
<button class="ql-italic"></button>
|
||||||
<span class="ql-formats">
|
<button class="ql-underline"></button>
|
||||||
<select class="ql-color"></select>
|
<button class="ql-strike"></button>
|
||||||
<select class="ql-background"></select>
|
</span>
|
||||||
</span>
|
<span class="ql-formats">
|
||||||
<span class="ql-formats">
|
<select class="ql-color"></select>
|
||||||
<button class="ql-list" value="ordered"></button>
|
<select class="ql-background"></select>
|
||||||
<button class="ql-list" value="bullet"></button>
|
</span>
|
||||||
</span>
|
<span class="ql-formats">
|
||||||
<span class="ql-formats">
|
<button class="ql-list" value="ordered"></button>
|
||||||
<button class="ql-link"></button>
|
<button class="ql-list" value="bullet"></button>
|
||||||
</span>
|
</span>
|
||||||
}
|
<span class="ql-formats">
|
||||||
</div>
|
<button class="ql-link"></button>
|
||||||
<div @ref="@_editorElement">
|
</span>
|
||||||
</div>
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div @ref="@_editorElement">
|
||||||
</TabPanel>
|
</div>
|
||||||
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
</div>
|
||||||
<div class="d-flex justify-content-center mb-2">
|
</div>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRawHtml">@Localizer["SynchronizeContent"]</button>
|
</TabPanel>
|
||||||
</div>
|
@if (AllowRawHtml)
|
||||||
@if (ReadOnly)
|
{
|
||||||
{
|
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
||||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_content" rows="10" readonly></textarea>
|
@if (_rawfilemanager)
|
||||||
}
|
{
|
||||||
else
|
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||||
{
|
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_content" rows="10"></textarea>
|
<br />
|
||||||
}
|
}
|
||||||
</TabPanel>
|
<div class="d-flex justify-content-center mb-2">
|
||||||
|
<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>
|
||||||
|
@if (ReadOnly)
|
||||||
|
{
|
||||||
|
<textarea id="rawhtmleditor" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10" readonly></textarea>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<textarea id="rawhtmleditor" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
|
||||||
|
}
|
||||||
|
</TabPanel>
|
||||||
|
}
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@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 _content = string.Empty;
|
private string _richhtml = string.Empty;
|
||||||
private string _original = string.Empty;
|
private string _originalrichhtml = string.Empty;
|
||||||
private string _message = string.Empty;
|
private bool _rawfilemanager = false;
|
||||||
|
private string _rawhtml = string.Empty;
|
||||||
|
private string _originalrawhtml = string.Empty;
|
||||||
|
private string _message = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool ReadOnly { get; set; } = false;
|
public bool ReadOnly { get; set; } = false;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Placeholder { get; set; } = "Enter Your Content...";
|
public string Placeholder { get; set; } = "Enter Your Content...";
|
||||||
|
|
||||||
// parameters only applicable to rich text editor
|
[Parameter]
|
||||||
[Parameter]
|
public bool AllowFileManagement { get; set; } = true;
|
||||||
public RenderFragment ToolbarContent { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Theme { get; set; } = "snow";
|
public bool AllowRawHtml { get; set; } = true;
|
||||||
|
|
||||||
|
// parameters only applicable to rich text editor
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment ToolbarContent { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string DebugLevel { get; set; } = "info";
|
public string Theme { get; set; } = "snow";
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool AllowFileManagement { get; set; } = true;
|
public string DebugLevel { get; set; } = "info";
|
||||||
|
|
||||||
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/quill1.3.7.min.js" },
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" },
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
_content = Content; // raw HTML
|
_richhtml = Content;
|
||||||
}
|
_rawhtml = Content;
|
||||||
|
_originalrawhtml = _rawhtml; // preserve for comparison later
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
|
|
||||||
if (firstRender)
|
|
||||||
{
|
|
||||||
await base.OnAfterRenderAsync(firstRender);
|
|
||||||
|
|
||||||
await interop.CreateEditor(
|
var interop = new RichTextEditorInterop(JSRuntime);
|
||||||
_editorElement,
|
|
||||||
_toolBar,
|
|
||||||
ReadOnly,
|
|
||||||
Placeholder,
|
|
||||||
Theme,
|
|
||||||
DebugLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
await interop.LoadEditorContent(_editorElement, Content);
|
if (firstRender)
|
||||||
|
{
|
||||||
|
await interop.CreateEditor(
|
||||||
|
_editorElement,
|
||||||
|
_toolBar,
|
||||||
|
ReadOnly,
|
||||||
|
Placeholder,
|
||||||
|
Theme,
|
||||||
|
DebugLevel);
|
||||||
|
|
||||||
_content = Content; // raw HTML
|
await interop.LoadEditorContent(_editorElement, _richhtml);
|
||||||
|
|
||||||
// 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)
|
||||||
_original = await interop.GetHtml(_editorElement);
|
_originalrichhtml = await interop.GetHtml(_editorElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
public void CloseRichFileManager()
|
||||||
|
{
|
||||||
|
_richfilemanager = false;
|
||||||
|
_message = string.Empty;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
public void CloseFileManager()
|
public void CloseRawFileManager()
|
||||||
{
|
{
|
||||||
_filemanagervisible = false;
|
_rawfilemanager = false;
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RefreshRichText()
|
public void RefreshRichText()
|
||||||
{
|
{
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
_richhtml = _rawhtml;
|
||||||
await interop.LoadEditorContent(_editorElement, _content);
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RefreshRawHtml()
|
public async Task RefreshRawHtml()
|
||||||
{
|
{
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
var interop = new RichTextEditorInterop(JSRuntime);
|
||||||
_content = await interop.GetHtml(_editorElement);
|
_rawhtml = await interop.GetHtml(_editorElement);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetHtml()
|
public async Task<string> GetHtml()
|
||||||
{
|
{
|
||||||
// get rich text content
|
// evaluate raw html content as first priority
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
if (_rawhtml != _originalrawhtml)
|
||||||
string content = await interop.GetHtml(_editorElement);
|
{
|
||||||
|
return _rawhtml;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// return rich text content if it has changed
|
||||||
|
var interop = new RichTextEditorInterop(JSRuntime);
|
||||||
|
var richhtml = await interop.GetHtml(_editorElement);
|
||||||
|
if (richhtml != _originalrichhtml)
|
||||||
|
{
|
||||||
|
return richhtml;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// return original raw html content
|
||||||
|
return _originalrawhtml;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_original != content)
|
public async Task InsertRichImage()
|
||||||
{
|
{
|
||||||
// rich text content has changed - return it
|
_message = string.Empty;
|
||||||
return content;
|
if (_richfilemanager)
|
||||||
}
|
{
|
||||||
else
|
var file = _fileManager.GetFile();
|
||||||
{
|
if (file != null)
|
||||||
// return raw html content
|
{
|
||||||
return _content;
|
var interop = new RichTextEditorInterop(JSRuntime);
|
||||||
}
|
await interop.InsertImage(_editorElement, file.Url, ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name));
|
||||||
}
|
_richfilemanager = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_message = Localizer["Message.Require.Image"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_richfilemanager = true;
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task InsertImage()
|
public async Task InsertRawImage()
|
||||||
{
|
{
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
if (_filemanagervisible)
|
if (_rawfilemanager)
|
||||||
{
|
{
|
||||||
var file = _fileManager.GetFile();
|
var file = _fileManager.GetFile();
|
||||||
if (file != null)
|
if (file != null)
|
||||||
{
|
{
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
await interop.InsertImage(_editorElement, file.Url, file.Name);
|
int pos = await interop.GetCaretPosition("rawhtmleditor");
|
||||||
_filemanagervisible = false;
|
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);
|
||||||
else
|
_rawfilemanager = false;
|
||||||
{
|
}
|
||||||
_message = Localizer["Message.Require.Image"];
|
else
|
||||||
}
|
{
|
||||||
}
|
_message = Localizer["Message.Require.Image"];
|
||||||
else
|
}
|
||||||
{
|
}
|
||||||
_filemanagervisible = true;
|
else
|
||||||
}
|
{
|
||||||
StateHasChanged();
|
_rawfilemanager = true;
|
||||||
}
|
}
|
||||||
|
StateHasChanged();
|
||||||
// other rich text editor methods which can be used by developers
|
}
|
||||||
public async Task<string> GetText()
|
|
||||||
{
|
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
|
||||||
return await interop.GetText(_editorElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetContent()
|
|
||||||
{
|
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
|
||||||
return await interop.GetContent(_editorElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task EnableEditor(bool mode)
|
|
||||||
{
|
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
|
||||||
await interop.EnableEditor(_editorElement, mode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,95 +9,188 @@
|
|||||||
@inject IStringLocalizer<Edit> Localizer
|
@inject IStringLocalizer<Edit> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@if (_content != null)
|
<TabStrip>
|
||||||
{
|
<TabPanel Name="Edit" Heading="Edit" ResourceKey="Edit">
|
||||||
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" @ref="@RichTextEditorHtml"></RichTextEditor>
|
@if (_content != null)
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
{
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" AllowRawHtml="@_allowrawhtml" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||||
@if (!string.IsNullOrEmpty(_content))
|
<br />
|
||||||
{
|
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
||||||
<br />
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
<br />
|
@if (!string.IsNullOrEmpty(_content))
|
||||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
{
|
||||||
}
|
<br />
|
||||||
}
|
<br />
|
||||||
|
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel Name="Versions" Heading="Versions" ResourceKey="Versions">
|
||||||
|
<Pager Items="@_htmltexts">
|
||||||
|
<Header>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th>@SharedLocalizer["CreatedOn"]</th>
|
||||||
|
<th>@SharedLocalizer["CreatedBy"]</th>
|
||||||
|
</Header>
|
||||||
|
<Row>
|
||||||
|
<td><ActionLink Action="View" Security="SecurityAccessLevel.Edit" OnClick="@(async () => await View(context))" ResourceKey="View" /></td>
|
||||||
|
<td><ActionDialog Header="Restore Version" Message="@string.Format(Localizer["Confirm.Restore"], context.CreatedOn)" Action="Restore" Security="SecurityAccessLevel.Edit" Class="btn btn-success" OnClick="@(async () => await Restore(context))" ResourceKey="Restore" /></td>
|
||||||
|
<td><ActionDialog Header="Delete Version" Message="@string.Format(Localizer["Confirm.Delete"], context.CreatedOn)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" ResourceKey="Delete" /></td>
|
||||||
|
<td>@context.CreatedOn</td>
|
||||||
|
<td>@context.CreatedBy</td>
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
|
@((MarkupString)_view)
|
||||||
|
</TabPanel>
|
||||||
|
</TabStrip>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
public override string Title => "Edit Html/Text";
|
public override string Title => "Edit Html/Text";
|
||||||
|
|
||||||
public override List<Resource> Resources => new List<Resource>()
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
{
|
{
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.7.bubble.css" },
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" },
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.7.snow.css" }
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" }
|
||||||
};
|
};
|
||||||
|
|
||||||
private RichTextEditor RichTextEditorHtml;
|
private RichTextEditor RichTextEditorHtml;
|
||||||
private bool _allowfilemanagement;
|
private bool _allowfilemanagement;
|
||||||
private string _content = null;
|
private bool _allowrawhtml;
|
||||||
private string _createdby;
|
private string _content = null;
|
||||||
private DateTime _createdon;
|
private string _createdby;
|
||||||
private string _modifiedby;
|
private DateTime _createdon;
|
||||||
private DateTime _modifiedon;
|
private string _modifiedby;
|
||||||
|
private DateTime _modifiedon;
|
||||||
|
private List<Models.HtmlText> _htmltexts;
|
||||||
|
private string _view = "";
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
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();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Content {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
|
private async Task LoadContent()
|
||||||
if (htmltext != null)
|
{
|
||||||
{
|
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||||
_content = htmltext.Content;
|
if (htmltext != null)
|
||||||
_content = Utilities.FormatContent(_content, PageState.Alias, "render");
|
{
|
||||||
_createdby = htmltext.CreatedBy;
|
_content = htmltext.Content;
|
||||||
_createdon = htmltext.CreatedOn;
|
_content = Utilities.FormatContent(_content, PageState.Alias, "render");
|
||||||
_modifiedby = htmltext.ModifiedBy;
|
_createdby = htmltext.CreatedBy;
|
||||||
_modifiedon = htmltext.ModifiedOn;
|
_createdon = htmltext.CreatedOn;
|
||||||
}
|
_modifiedby = htmltext.ModifiedBy;
|
||||||
else
|
_modifiedon = htmltext.ModifiedOn;
|
||||||
{
|
}
|
||||||
_content = string.Empty;
|
else
|
||||||
}
|
{
|
||||||
}
|
_content = string.Empty;
|
||||||
catch (Exception ex)
|
}
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Loading Content {Error}", ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveContent()
|
_htmltexts = await HtmlTextService.GetHtmlTextsAsync(ModuleState.ModuleId);
|
||||||
{
|
_htmltexts = _htmltexts.OrderByDescending(item => item.CreatedOn).ToList();
|
||||||
string content = await RichTextEditorHtml.GetHtml();
|
|
||||||
content = Utilities.FormatContent(content, PageState.Alias, "save");
|
|
||||||
|
|
||||||
try
|
_view = "";
|
||||||
{
|
}
|
||||||
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
|
|
||||||
if (htmltext != null)
|
|
||||||
{
|
|
||||||
htmltext.Content = content;
|
|
||||||
await HtmlTextService.UpdateHtmlTextAsync(htmltext);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
htmltext = new HtmlText();
|
|
||||||
htmltext.ModuleId = ModuleState.ModuleId;
|
|
||||||
htmltext.Content = content;
|
|
||||||
await HtmlTextService.AddHtmlTextAsync(htmltext);
|
|
||||||
}
|
|
||||||
|
|
||||||
await logger.LogInformation("Content Saved {HtmlText}", htmltext);
|
private async Task SaveContent()
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
{
|
||||||
}
|
string content = await RichTextEditorHtml.GetHtml();
|
||||||
catch (Exception ex)
|
content = Utilities.FormatContent(content, PageState.Alias, "save");
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Saving Content {Error}", ex.Message);
|
try
|
||||||
AddModuleMessage(Localizer["Error.Content.Save"], MessageType.Error);
|
{
|
||||||
}
|
var htmltext = new HtmlText();
|
||||||
}
|
htmltext.ModuleId = ModuleState.ModuleId;
|
||||||
|
htmltext.Content = content;
|
||||||
|
await HtmlTextService.AddHtmlTextAsync(htmltext);
|
||||||
|
|
||||||
|
await logger.LogInformation("Content Saved {HtmlText}", htmltext);
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving Content {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Content.Save"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task View(Models.HtmlText htmltext)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
htmltext = await HtmlTextService.GetHtmlTextAsync(htmltext.HtmlTextId, htmltext.ModuleId);
|
||||||
|
if (htmltext != null)
|
||||||
|
{
|
||||||
|
_view = htmltext.Content;
|
||||||
|
_view = Utilities.FormatContent(_view, PageState.Alias, "render");
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Viewing Content {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Content.View"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Restore(Models.HtmlText htmltext)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
htmltext = await HtmlTextService.GetHtmlTextAsync(htmltext.HtmlTextId, ModuleState.ModuleId);
|
||||||
|
if (htmltext != null)
|
||||||
|
{
|
||||||
|
var content = htmltext.Content;
|
||||||
|
htmltext = new HtmlText();
|
||||||
|
htmltext.ModuleId = ModuleState.ModuleId;
|
||||||
|
htmltext.Content = content;
|
||||||
|
await HtmlTextService.AddHtmlTextAsync(htmltext);
|
||||||
|
await logger.LogInformation("Content Restored {HtmlText}", htmltext);
|
||||||
|
AddModuleMessage(Localizer["Message.Content.Restored"], MessageType.Success);
|
||||||
|
await LoadContent();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Restoring Content {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Content.Restore"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Delete(Models.HtmlText htmltext)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
htmltext = await HtmlTextService.GetHtmlTextAsync(htmltext.HtmlTextId, ModuleState.ModuleId);
|
||||||
|
if (htmltext != null)
|
||||||
|
{
|
||||||
|
await HtmlTextService.DeleteHtmlTextAsync(htmltext.HtmlTextId, htmltext.ModuleId);
|
||||||
|
await logger.LogInformation("Content Deleted {HtmlText}", htmltext);
|
||||||
|
AddModuleMessage(Localizer["Message.Content.Deleted"], MessageType.Success);
|
||||||
|
await LoadContent();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Deleting Content {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Content.Delete"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
@ -13,24 +15,29 @@ namespace Oqtane.Modules.HtmlText.Services
|
|||||||
|
|
||||||
private string ApiUrl => CreateApiUrl("HtmlText");
|
private string ApiUrl => CreateApiUrl("HtmlText");
|
||||||
|
|
||||||
|
public async Task<List<Models.HtmlText>> GetHtmlTextsAsync(int moduleId)
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<List<Models.HtmlText>>(CreateAuthorizationPolicyUrl($"{ApiUrl}?moduleid={moduleId}", EntityNames.Module, moduleId));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Models.HtmlText> GetHtmlTextAsync(int moduleId)
|
public async Task<Models.HtmlText> GetHtmlTextAsync(int moduleId)
|
||||||
{
|
{
|
||||||
return await GetJsonAsync<Models.HtmlText>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", EntityNames.Module, moduleId));
|
return await GetJsonAsync<Models.HtmlText>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", EntityNames.Module, moduleId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Models.HtmlText> GetHtmlTextAsync(int htmlTextId, int moduleId)
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<Models.HtmlText>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlTextId}/{moduleId}", EntityNames.Module, moduleId));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task AddHtmlTextAsync(Models.HtmlText htmlText)
|
public async Task AddHtmlTextAsync(Models.HtmlText htmlText)
|
||||||
{
|
{
|
||||||
await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", EntityNames.Module, htmlText.ModuleId), htmlText);
|
await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", EntityNames.Module, htmlText.ModuleId), htmlText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateHtmlTextAsync(Models.HtmlText htmlText)
|
public async Task DeleteHtmlTextAsync(int htmlTextId, int moduleId)
|
||||||
{
|
{
|
||||||
await PutJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlText.HtmlTextId}", EntityNames.Module, htmlText.ModuleId), htmlText);
|
await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlTextId}/{moduleId}", EntityNames.Module, moduleId));
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DeleteHtmlTextAsync(int moduleId)
|
|
||||||
{
|
|
||||||
await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", EntityNames.Module, moduleId));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
using Oqtane.Modules.HtmlText.Models;
|
|
||||||
|
|
||||||
namespace Oqtane.Modules.HtmlText.Services
|
namespace Oqtane.Modules.HtmlText.Services
|
||||||
{
|
{
|
||||||
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
|
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
|
||||||
public interface IHtmlTextService
|
public interface IHtmlTextService
|
||||||
{
|
{
|
||||||
Task<Models.HtmlText> GetHtmlTextAsync(int ModuleId);
|
Task<List<Models.HtmlText>> GetHtmlTextsAsync(int moduleId);
|
||||||
|
|
||||||
|
Task<Models.HtmlText> GetHtmlTextAsync(int moduleId);
|
||||||
|
|
||||||
|
Task<Models.HtmlText> GetHtmlTextAsync(int htmlTextId, int moduleId);
|
||||||
|
|
||||||
Task AddHtmlTextAsync(Models.HtmlText htmltext);
|
Task AddHtmlTextAsync(Models.HtmlText htmltext);
|
||||||
|
|
||||||
Task UpdateHtmlTextAsync(Models.HtmlText htmltext);
|
Task DeleteHtmlTextAsync(int htmlTextId, int moduleId);
|
||||||
|
|
||||||
Task DeleteHtmlTextAsync(int ModuleId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,44 +5,57 @@
|
|||||||
@inject IStringLocalizer<Settings> Localizer
|
@inject IStringLocalizer<Settings> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<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="files" ResourceKey="Allow File Management" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
|
<Label Class="col-sm-3" For="files" ResourceKey="AllowFileManagement" ResourceType="@resourceType" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="files" class="form-select" @bind="@_allowfilemanagement">
|
<select id="files" class="form-select" @bind="@_allowfilemanagement">
|
||||||
<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>
|
||||||
</div>
|
|
||||||
</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>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _allowfilemanagement;
|
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
|
||||||
|
private string _allowfilemanagement;
|
||||||
|
private string _allowrawhtml;
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true");
|
||||||
{
|
_allowrawhtml = SettingService.GetSetting(ModuleState.Settings, "AllowRawHtml", "true");
|
||||||
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true");
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UpdateSettings()
|
|
||||||
{
|
{
|
||||||
try
|
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
{
|
|
||||||
var settings = ModuleState.Settings;
|
|
||||||
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
|
|
||||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task UpdateSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||||
|
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
|
||||||
|
settings = SettingService.SetSetting(settings, "AllowRawHtml", _allowrawhtml);
|
||||||
|
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
@ -53,9 +73,10 @@ namespace Oqtane.Modules
|
|||||||
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
||||||
{
|
{
|
||||||
var scripts = new List<object>();
|
var scripts = new List<object>();
|
||||||
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script && item.Declaration != ResourceDeclaration.Global))
|
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 ?? "" });
|
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 });
|
||||||
}
|
}
|
||||||
if (scripts.Any())
|
if (scripts.Any())
|
||||||
{
|
{
|
||||||
@ -70,7 +91,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,35 +145,60 @@ 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)
|
||||||
|
{
|
||||||
|
return ImageUrl(fileid, width, height, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ImageUrl(int fileid, int width, int height, string mode)
|
public string ImageUrl(int fileid, int width, int height, string mode)
|
||||||
{
|
{
|
||||||
return ImageUrl(fileid, width, height, mode, 0);
|
return ImageUrl(fileid, width, height, mode, "", "", 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ImageUrl(int fileid, int width, int height, string mode, int rotate)
|
public string ImageUrl(int fileid, int width, int height, string mode, string position, string background, int rotate, bool recreate)
|
||||||
{
|
{
|
||||||
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, rotate);
|
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)
|
||||||
|
{
|
||||||
|
var url = "";
|
||||||
|
for (var i = 0; i < parameters.Length; i++)
|
||||||
|
{
|
||||||
|
url += "/" + parameters[i].ToString();
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]);
|
||||||
@ -160,39 +206,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 (i < segments.Length)
|
||||||
{
|
{
|
||||||
if (parameters.Length > i)
|
key = segments[i];
|
||||||
|
if (key.StartsWith("{") && key.EndsWith("}"))
|
||||||
{
|
{
|
||||||
if (templateSegments[i] == parameters[i])
|
// dynamic segment
|
||||||
{
|
key = key.Substring(1, key.Length - 2);
|
||||||
urlParameters.TryAdd("parameter" + parameterId, parameters[i]);
|
}
|
||||||
parameterId++;
|
else
|
||||||
}
|
{
|
||||||
else if (templateSegments[i].StartsWith("{") && templateSegments[i].EndsWith("}"))
|
// static segments use generic "parameter#" keys
|
||||||
{
|
key = "parameter" + i.ToString();
|
||||||
var key = templateSegments[i].Replace("{", "");
|
|
||||||
key = key.Replace("}", "");
|
|
||||||
urlParameters.TryAdd(key, parameters[i]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
i = parameters.Length;
|
|
||||||
urlParameters.Clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
@ -213,6 +257,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)
|
||||||
{
|
{
|
||||||
@ -360,5 +416,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.0.1</Version>
|
<Version>3.2.1</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.0.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.1</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>
|
||||||
@ -22,11 +22,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.0" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.3" PrivateAssets="all" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="6.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="6.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.3" />
|
||||||
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
|
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@ -34,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;
|
||||||
@ -15,7 +16,6 @@ using Microsoft.JSInterop;
|
|||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
using Oqtane.Modules;
|
using Oqtane.Modules;
|
||||||
using Oqtane.Services;
|
using Oqtane.Services;
|
||||||
using Oqtane.Shared;
|
|
||||||
using Oqtane.UI;
|
using Oqtane.UI;
|
||||||
|
|
||||||
namespace Oqtane.Client
|
namespace Oqtane.Client
|
||||||
@ -28,11 +28,12 @@ namespace Oqtane.Client
|
|||||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||||
|
|
||||||
var httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)};
|
var httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)};
|
||||||
|
builder.Services.AddSingleton(httpClient);
|
||||||
|
builder.Services.AddHttpClient(); // IHttpClientFactory for calling remote services via RemoteServiceBase
|
||||||
|
|
||||||
builder.Services.AddSingleton(httpClient);
|
|
||||||
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
|
||||||
@ -41,7 +42,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)
|
||||||
@ -53,37 +56,106 @@ namespace Oqtane.Client
|
|||||||
RegisterClientStartups(assembly, builder.Services);
|
RegisterClientStartups(assembly, builder.Services);
|
||||||
}
|
}
|
||||||
|
|
||||||
var host = builder.Build();
|
await builder.Build().RunAsync();
|
||||||
|
|
||||||
await SetCultureFromLocalizationCookie(host.Services);
|
|
||||||
|
|
||||||
ServiceActivator.Configure(host.Services);
|
|
||||||
|
|
||||||
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>();
|
||||||
|
|
||||||
// get assemblies from server and load into client app domain
|
var jsRuntime = serviceProvider.GetRequiredService<IJSRuntime>();
|
||||||
var zip = await http.GetByteArrayAsync($"/api/Installation/load");
|
var interop = new Interop(jsRuntime);
|
||||||
|
var files = await interop.GetIndexedDBKeys(".dll");
|
||||||
|
|
||||||
// asemblies and debug symbols are packaged in a zip file
|
if (files.Count() != 0)
|
||||||
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
|
||||||
{
|
{
|
||||||
var dlls = new Dictionary<string, byte[]>();
|
// get list of assemblies from server
|
||||||
var pdbs = new Dictionary<string, byte[]>();
|
var json = await http.GetStringAsync("/api/Installation/list");
|
||||||
|
var assemblies = JsonSerializer.Deserialize<List<string>>(json);
|
||||||
|
|
||||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
// determine which assemblies need to be downloaded
|
||||||
|
foreach (var assembly in assemblies)
|
||||||
{
|
{
|
||||||
if (!assemblies.Contains(Path.GetFileNameWithoutExtension(entry.FullName)))
|
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
|
||||||
|
var zip = await http.GetByteArrayAsync($"/api/Installation/load?list=" + string.Join(",", list));
|
||||||
|
|
||||||
|
// asemblies and debug symbols are packaged in a zip file
|
||||||
|
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
||||||
|
{
|
||||||
|
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||||
{
|
{
|
||||||
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":
|
||||||
@ -96,23 +168,31 @@ namespace Oqtane.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var item in dlls)
|
// load assemblies into app domain
|
||||||
|
foreach (var item in dlls)
|
||||||
|
{
|
||||||
|
if (pdbs.ContainsKey(item.Key.Replace(".dll", ".pdb")))
|
||||||
{
|
{
|
||||||
if (pdbs.ContainsKey(item.Key))
|
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value), new MemoryStream(pdbs[item.Key.Replace(".dll", ".pdb")]));
|
||||||
{
|
}
|
||||||
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value), new MemoryStream(pdbs[item.Key]));
|
else
|
||||||
}
|
{
|
||||||
else
|
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value));
|
||||||
{
|
|
||||||
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
var implementationTypes = assembly.GetInterfaces<IService>();
|
var implementationTypes = assembly.GetInterfaces<IService>();
|
||||||
foreach (var implementationType in implementationTypes)
|
foreach (var implementationType in implementationTypes)
|
||||||
{
|
{
|
||||||
|
@ -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>
|
||||||
|
@ -130,10 +130,13 @@
|
|||||||
<value>Install Now</value>
|
<value>Install Now</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.DbConfig.Load" xml:space="preserve">
|
<data name="Error.DbConfig.Load" xml:space="preserve">
|
||||||
<value>Error loading Database Configuration Control</value>
|
<value>Error Loading Database Configuration Control</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Require.DbInfo" xml:space="preserve">
|
<data name="Message.Require.DbInfo" xml:space="preserve">
|
||||||
<value>Please Enter All Required Fields. Ensure Passwords Match And Are Greater Than 5 Characters In Length. Ensure Email Address Provided Is Valid.</value>
|
<value>Please Enter All Required Fields. Ensure Passwords Match And Email Address Provided Is Valid.</value>
|
||||||
|
</data>
|
||||||
|
<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>
|
||||||
</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>
|
||||||
@ -165,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>String:</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
|
||||||
@ -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>
|
@ -186,4 +186,10 @@
|
|||||||
<data name="NextExecution.Text" xml:space="preserve">
|
<data name="NextExecution.Text" xml:space="preserve">
|
||||||
<value>Next Execution: </value>
|
<value>Next Execution: </value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Week(s)" xml:space="preserve">
|
||||||
|
<value>Week(s)</value>
|
||||||
|
</data>
|
||||||
|
<data name="Once" xml:space="preserve">
|
||||||
|
<value>Execute Once</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -133,19 +133,16 @@
|
|||||||
<value>Every</value>
|
<value>Every</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Minute" xml:space="preserve">
|
<data name="Minute" xml:space="preserve">
|
||||||
<value>Minute</value>
|
<value>Minute(s)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Hour" xml:space="preserve">
|
<data name="Hour" xml:space="preserve">
|
||||||
<value>Hour</value>
|
<value>Hour(s)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Day" xml:space="preserve">
|
<data name="Day" xml:space="preserve">
|
||||||
<value>Day</value>
|
<value>Day(s)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Month" xml:space="preserve">
|
<data name="Month" xml:space="preserve">
|
||||||
<value>Month</value>
|
<value>Month(s)</value>
|
||||||
</data>
|
|
||||||
<data name="s" xml:space="preserve">
|
|
||||||
<value>s</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.Job.Delete" xml:space="preserve">
|
<data name="Error.Job.Delete" xml:space="preserve">
|
||||||
<value>Error Deleting Job</value>
|
<value>Error Deleting Job</value>
|
||||||
@ -177,4 +174,25 @@
|
|||||||
<data name="JobLog.Text" xml:space="preserve">
|
<data name="JobLog.Text" xml:space="preserve">
|
||||||
<value>Log</value>
|
<value>Log</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Error.Job.Start" xml:space="preserve">
|
||||||
|
<value>An error occurred when starting the job</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.Job.Stop" xml:space="preserve">
|
||||||
|
<value>An error occurred when stopping the job</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Job.Start" xml:space="preserve">
|
||||||
|
<value>The process responsible for executing this job has been started. The next execution will be based on the schedule criteria for the job.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Job.Stop" xml:space="preserve">
|
||||||
|
<value>The process responsible for executing this job has been stopped. In order to restart the process you will need to use the Start button or restart the application.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Week" xml:space="preserve">
|
||||||
|
<value>Week(s)</value>
|
||||||
|
</data>
|
||||||
|
<data name="Once" xml:space="preserve">
|
||||||
|
<value>Execute Once</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.NoJobs" xml:space="preserve">
|
||||||
|
<value>Please Note That After An Initial Installation You Must &lt;a href={0}&gt;Restart&lt;/a&gt; The Application In Order To Activate The Default Scheduled Jobs.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -154,13 +154,13 @@
|
|||||||
<value>No Translations Match The Criteria Provided Or Package Service Is Disabled</value>
|
<value>No Translations Match The Criteria Provided Or Package Service Is Disabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Download.Heading" xml:space="preserve">
|
<data name="Download.Heading" xml:space="preserve">
|
||||||
<value>Download</value>
|
<value>Translations</value>
|
||||||
</data>
|
</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>
|
||||||
|
@ -144,4 +144,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
|
||||||
@ -117,25 +117,112 @@
|
|||||||
<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="RememberMe" xml:space="preserve">
|
|
||||||
<value>Remember Me?</value>
|
|
||||||
</data>
|
|
||||||
<data name="ForgotPassword" xml:space="preserve">
|
<data name="ForgotPassword" xml:space="preserve">
|
||||||
<value>Forgot Password</value>
|
<value>Forgot Password</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Success.Account.Verified" xml:space="preserve">
|
<data name="Success.Account.Verified" xml:space="preserve">
|
||||||
<value>User Account Verified Successfully. You Can Now Login With Your Username And Password Below.</value>
|
<value>User Account Verified Successfully. You Can Now Login With Your Username And Password Below.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Account.NotVerfied" xml:space="preserve">
|
<data name="Message.Account.NotVerified" xml:space="preserve">
|
||||||
<value>User Account Could Not Be Verified. Please Contact Your Administrator For Further Instructions.</value>
|
<value>User Account Could Not Be Verified. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Success.Account.Linked" xml:space="preserve">
|
||||||
|
<value>User Account Linked Successfully. You Can Now Login With Your External Login Below.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Account.NotLinked" xml:space="preserve">
|
||||||
|
<value>External Login Could Not Be Linked. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
<data name="Error.Login.Fail" xml:space="preserve">
|
<data name="Error.Login.Fail" xml:space="preserve">
|
||||||
<value>Login Failed. Please Remember That Passwords Are Case Sensitive And User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email.</value>
|
<value>Login Failed. Please Remember That Passwords Are Case Sensitive. If You Have Attempted To Sign In Multiple Times Unsuccessfully, Your Account Will Be Locked Out For A Period Of Time. Note That User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email If You Are A New User.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Required.UserInfo" xml:space="preserve">
|
<data name="Message.Required.UserInfo" xml:space="preserve">
|
||||||
<value>Please Provide Your Username And Password</value>
|
<value>Please Provide All Required Fields</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Info.SignedIn" xml:space="preserve">
|
<data name="Info.SignedIn" xml:space="preserve">
|
||||||
<value>You Are Already Signed In</value>
|
<value>You Are Already Signed In</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.ForgotPassword" xml:space="preserve">
|
||||||
|
<value>Please Enter The Username Related To Your Account And Then Select The Forgot Password Option Again</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.ForgotUser" xml:space="preserve">
|
||||||
|
<value>Please Check The Email Address Associated To Your User Account For A Password Reset Notification</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.UserDoesNotExist" xml:space="preserve">
|
||||||
|
<value>User Does Not Exist</value>
|
||||||
|
</data>
|
||||||
|
<data name="Code.HelpText" xml:space="preserve">
|
||||||
|
<value>Please Enter The Secure Verification Code Which Was Sent To You By Email.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Code.Placeholder" xml:space="preserve">
|
||||||
|
<value>Verification Code</value>
|
||||||
|
</data>
|
||||||
|
<data name="Code.Text" xml:space="preserve">
|
||||||
|
<value>Verification Code:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.TwoFactor.Fail" xml:space="preserve">
|
||||||
|
<value>Verification Failed. Please Ensure You Entered The Code Exactly In The Form Provided In Your Email. If You Wish To Request A New Verification Code Please Select The Cancel Option And Sign In Again. </value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.TwoFactor" xml:space="preserve">
|
||||||
|
<value>A Secure Verification Code Has Been Sent To Your Email Address. Please Enter The Code That You Received. If You Do Not Receive The Code Or You Have Lost Access To Your Email, Please Contact Your Administrator.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password.HelpText" xml:space="preserve">
|
||||||
|
<value>Please Enter The Password Related To Your Account. Remember That Passwords Are Case Sensitive. If You Attempt Unsuccessfully To Log In To Your Account Multiple Times, You Will Be Locked Out For A Period Of Time.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password.Placeholder" xml:space="preserve">
|
||||||
|
<value>Password</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password.Text" xml:space="preserve">
|
||||||
|
<value>Password:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Remember.HelpText" xml:space="preserve">
|
||||||
|
<value>Specify If You Would Like To Be Signed Back In Automatically The Next Time You Visit This Site</value>
|
||||||
|
</data>
|
||||||
|
<data name="Remember.Text" xml:space="preserve">
|
||||||
|
<value>Remember Me?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Username.HelpText" xml:space="preserve">
|
||||||
|
<value>Please Enter The Username Related To Your Account</value>
|
||||||
|
</data>
|
||||||
|
<data name="Username.Placeholder" xml:space="preserve">
|
||||||
|
<value>Username</value>
|
||||||
|
</data>
|
||||||
|
<data name="Username.Text" xml:space="preserve">
|
||||||
|
<value>Username:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Use" xml:space="preserve">
|
||||||
|
<value>Use</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.LoadLogin" xml:space="preserve">
|
||||||
|
<value>Error Loading Login</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.Login" xml:space="preserve">
|
||||||
|
<value>Error Performing Login</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.ResetPassword" xml:space="preserve">
|
||||||
|
<value>Error Resetting Password</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.DuplicateEmail" xml:space="preserve">
|
||||||
|
<value>Multiple User Accounts Already Exist With The Email Address Of Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.InvalidEmail" xml:space="preserve">
|
||||||
|
<value>The External Login Provider Did Not Provide A Valid Email Address For Your Account. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.ProviderKeyMismatch" xml:space="preserve">
|
||||||
|
<value>An Error Occurred Verifying Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.UserDoesNotExist" xml:space="preserve">
|
||||||
|
<value>A User Account Matching The Email Address Of Your External Login Does Not Exist. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.UserNotCreated" xml:space="preserve">
|
||||||
|
<value>A User Account Could Not Be Created For Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.VerificationRequired" xml:space="preserve">
|
||||||
|
<value>In Order To Link Your External Login With Your User Account You Must Verify Your Identity. Please Check Your Email For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.AccessDenied" xml:space="preserve">
|
||||||
|
<value>Your External Login Was Denied Access. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.RemoteFailure" xml:space="preserve">
|
||||||
|
<value>Your External Login Failed. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -192,4 +192,22 @@
|
|||||||
<data name="LogDetails.Text" xml:space="preserve">
|
<data name="LogDetails.Text" xml:space="preserve">
|
||||||
<value>Details</value>
|
<value>Details</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Error.SaveSiteSettings" xml:space="preserve">
|
||||||
|
<value>Error Saving Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="Events.Heading" xml:space="preserve">
|
||||||
|
<value>Events</value>
|
||||||
|
</data>
|
||||||
|
<data name="Retention.HelpText" xml:space="preserve">
|
||||||
|
<value>Number of days of events to retain</value>
|
||||||
|
</data>
|
||||||
|
<data name="Retention.Text" xml:space="preserve">
|
||||||
|
<value>Retention (Days):</value>
|
||||||
|
</data>
|
||||||
|
<data name="Settings.Heading" xml:space="preserve">
|
||||||
|
<value>Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="Success.SaveSiteSettings" xml:space="preserve">
|
||||||
|
<value>Settings Saved Successfully</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -195,4 +195,25 @@
|
|||||||
<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.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,7 +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">
|
<data name="Meta.HelpText" 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>
|
<value>Optionally enter meta tags (in exactly the form you want them to be included in the page output).</value>
|
||||||
|
</data>
|
||||||
|
<data name="Meta.Text" xml:space="preserve">
|
||||||
|
<value>Meta:</value>
|
||||||
|
</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>
|
</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>
|
||||||
@ -258,13 +258,19 @@
|
|||||||
<data name="ThemeSettings.Heading" xml:space="preserve">
|
<data name="ThemeSettings.Heading" xml:space="preserve">
|
||||||
<value>Theme Settings</value>
|
<value>Theme Settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Appearance.Hea" xml:space="preserve">
|
|
||||||
<value />
|
|
||||||
</data>
|
|
||||||
<data name="Clickable.HelpText" xml:space="preserve">
|
<data name="Clickable.HelpText" xml:space="preserve">
|
||||||
<value>Select whether the link in the site navigation is enabled or disabled</value>
|
<value>Select whether the link in the site navigation is enabled or disabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Clickable.Text" xml:space="preserve">
|
<data name="Clickable.Text" xml:space="preserve">
|
||||||
<value>Clickable?</value>
|
<value>Clickable?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<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>
|
||||||
|
</data>
|
||||||
|
<data name="Meta.Text" xml:space="preserve">
|
||||||
|
<value>Meta:</value>
|
||||||
|
</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>
|
||||||
|
@ -117,22 +117,37 @@
|
|||||||
<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="Password.Confirm" xml:space="preserve">
|
|
||||||
<value>Confirm Password:</value>
|
|
||||||
</data>
|
|
||||||
<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.Required.UserInfo" xml:space="preserve">
|
<data name="Message.Required.UserInfo" xml:space="preserve">
|
||||||
<value>You Must Provide A Username, Password, and Email Address</value>
|
<value>You Must Provide The Password And Confirmation</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Password.Reset" xml:space="preserve">
|
<data name="Password.Reset" xml:space="preserve">
|
||||||
<value>Reset Password</value>
|
<value>Reset Password</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.Password.ResetInfo" xml:space="preserve">
|
<data name="Error.Password.ResetInfo" xml:space="preserve">
|
||||||
<value>Error Resetting User Password. Please Ensure Password Meets Complexity Requirements.</value>
|
<value>Error Resetting User Password. Please Ensure The Request To Reset Your Password Was Made Within The Past 24 Hours And The New Password Meets The Complexity Requirements.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.Password.Reset" xml:space="preserve">
|
<data name="Error.Password.Reset" xml:space="preserve">
|
||||||
<value>Error Resetting User Password</value>
|
<value>Error Resetting User Password</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Confirm.Text" xml:space="preserve">
|
||||||
|
<value>Confirm:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Conform.HelpText" xml:space="preserve">
|
||||||
|
<value>Enter the password again. It must exactly match the password entered above.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password.HelpText" xml:space="preserve">
|
||||||
|
<value>The new password. It must satisfy complexity rules for the site.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password.Text" xml:space="preserve">
|
||||||
|
<value>Password:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Username.HelpText" xml:space="preserve">
|
||||||
|
<value>Your username will be populated from the link you received in the password reset notification</value>
|
||||||
|
</data>
|
||||||
|
<data name="Username.Text" xml:space="preserve">
|
||||||
|
<value>Username:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -129,9 +129,6 @@
|
|||||||
<data name="DefaultContainer.Text" xml:space="preserve">
|
<data name="DefaultContainer.Text" xml:space="preserve">
|
||||||
<value>Default Container: </value>
|
<value>Default Container: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Appearance.Heading" xml:space="preserve">
|
|
||||||
<value>Appearance</value>
|
|
||||||
</data>
|
|
||||||
<data name="DefaultAdminContainer" xml:space="preserve">
|
<data name="DefaultAdminContainer" xml:space="preserve">
|
||||||
<value>Default Admin Container</value>
|
<value>Default Admin Container</value>
|
||||||
</data>
|
</data>
|
||||||
@ -145,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>An Alias Specified 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>
|
||||||
@ -169,7 +166,7 @@
|
|||||||
<value>Enter the tenant for the site</value>
|
<value>Enter the tenant 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>
|
||||||
@ -223,7 +220,7 @@
|
|||||||
<value>Aliases: </value>
|
<value>Aliases: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IsDeleted.Text" xml:space="preserve">
|
<data name="IsDeleted.Text" xml:space="preserve">
|
||||||
<value>Is Deleted? </value>
|
<value>Deleted? </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Logo.Text" xml:space="preserve">
|
<data name="Logo.Text" xml:space="preserve">
|
||||||
<value>Logo: </value>
|
<value>Logo: </value>
|
||||||
@ -318,4 +315,28 @@
|
|||||||
<data name="DeleteSite.Text" xml:space="preserve">
|
<data name="DeleteSite.Text" xml:space="preserve">
|
||||||
<value>Delete Site</value>
|
<value>Delete Site</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DefaultAlias.HelpText" xml:space="preserve">
|
||||||
|
<value>The default alias for the site. Requests for non-default aliases will be redirected to the default alias.</value>
|
||||||
|
</data>
|
||||||
|
<data name="DefaultAlias.Text" xml:space="preserve">
|
||||||
|
<value>Default Alias: </value>
|
||||||
|
</data>
|
||||||
|
<data name="Aliases.Heading" xml:space="preserve">
|
||||||
|
<value>Aliases</value>
|
||||||
|
</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>
|
||||||
</root>
|
</root>
|
@ -270,4 +270,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>String:</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>
|
@ -129,11 +129,11 @@
|
|||||||
<data name="OSVersion.HelpText" xml:space="preserve">
|
<data name="OSVersion.HelpText" xml:space="preserve">
|
||||||
<value>Operating System Version</value>
|
<value>Operating System Version</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServerPath.HelpText" xml:space="preserve">
|
<data name="ContentRootPath.HelpText" xml:space="preserve">
|
||||||
<value>Server Path</value>
|
<value>Server Root Path</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServerTime.HelpText" xml:space="preserve">
|
<data name="ServerTime.HelpText" xml:space="preserve">
|
||||||
<value>Server Time</value>
|
<value>Server Date/Time (in UTC)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FrameworkVersion.Text" xml:space="preserve">
|
<data name="FrameworkVersion.Text" xml:space="preserve">
|
||||||
<value>Framework Version: </value>
|
<value>Framework Version: </value>
|
||||||
@ -144,11 +144,11 @@
|
|||||||
<data name="OSVersion.Text" xml:space="preserve">
|
<data name="OSVersion.Text" xml:space="preserve">
|
||||||
<value>OS Version: </value>
|
<value>OS Version: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServerPath.Text" xml:space="preserve">
|
<data name="ContentRootPath.Text" xml:space="preserve">
|
||||||
<value>Server Path: </value>
|
<value>Root Path: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServerTime.Text" xml:space="preserve">
|
<data name="ServerTime.Text" xml:space="preserve">
|
||||||
<value>Server Time: </value>
|
<value>Server Date/Time: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RestartApplication.Header" xml:space="preserve">
|
<data name="RestartApplication.Header" xml:space="preserve">
|
||||||
<value>Restart Application</value>
|
<value>Restart Application</value>
|
||||||
@ -231,4 +231,49 @@
|
|||||||
<data name="RestartApplication.Text" xml:space="preserve">
|
<data name="RestartApplication.Text" xml:space="preserve">
|
||||||
<value>Restart Application</value>
|
<value>Restart Application</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="None" xml:space="preserve">
|
||||||
|
<value>None</value>
|
||||||
|
</data>
|
||||||
|
<data name="NotificationLevel.HelpText" xml:space="preserve">
|
||||||
|
<value>The Minimum Logging Level For Which Notifications Should Be Sent To Host Users.</value>
|
||||||
|
</data>
|
||||||
|
<data name="NotificationLevel.Text" xml:space="preserve">
|
||||||
|
<value>Notification Level:</value>
|
||||||
|
</data>
|
||||||
|
<data name="IPAddress.HelpText" xml:space="preserve">
|
||||||
|
<value>Server IP Address</value>
|
||||||
|
</data>
|
||||||
|
<data name="IPAddress.Text" xml:space="preserve">
|
||||||
|
<value>IP Address:</value>
|
||||||
|
</data>
|
||||||
|
<data name="MachineName.HelpText" xml:space="preserve">
|
||||||
|
<value>Server Machine Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="MachineName.Text" xml:space="preserve">
|
||||||
|
<value>Machine Name:</value>
|
||||||
|
</data>
|
||||||
|
<data name="TickCount.HelpText" xml:space="preserve">
|
||||||
|
<value>Amount Of Time The Service Has Been Available And Operational</value>
|
||||||
|
</data>
|
||||||
|
<data name="TickCount.Text" xml:space="preserve">
|
||||||
|
<value>Service Uptime:</value>
|
||||||
|
</data>
|
||||||
|
<data name="WebRootPath.HelpText" xml:space="preserve">
|
||||||
|
<value>Server Web Root Path</value>
|
||||||
|
</data>
|
||||||
|
<data name="WebRootPath.Text" xml:space="preserve">
|
||||||
|
<value>Web Path:</value>
|
||||||
|
</data>
|
||||||
|
<data name="WorkingSet.HelpText" xml:space="preserve">
|
||||||
|
<value>Memory Allocation Of Service (in MB)</value>
|
||||||
|
</data>
|
||||||
|
<data name="WorkingSet.Text" xml:space="preserve">
|
||||||
|
<value>Memory Allocation:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Environment.HelpText" xml:space="preserve">
|
||||||
|
<value>Environment Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="Environment.Text" xml:space="preserve">
|
||||||
|
<value>Environment:</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>
|
@ -121,10 +121,10 @@
|
|||||||
<value>Redirect To:</value>
|
<value>Redirect To:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MappedUrl.HelpText" xml:space="preserve">
|
<data name="MappedUrl.HelpText" xml:space="preserve">
|
||||||
<value>A fully qualified Url where the user will be redirected</value>
|
<value>A relative or absolute Url where the user will be redirected</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Url.HelpText" xml:space="preserve">
|
<data name="Url.HelpText" xml:space="preserve">
|
||||||
<value>A fully qualified Url for this site</value>
|
<value>An absolute Url identifying a path to a specific page in the site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Url.Text" xml:space="preserve">
|
<data name="Url.Text" xml:space="preserve">
|
||||||
<value>Url:</value>
|
<value>Url:</value>
|
||||||
@ -135,4 +135,10 @@
|
|||||||
<data name="Message.InfoRequired" xml:space="preserve">
|
<data name="Message.InfoRequired" xml:space="preserve">
|
||||||
<value>Please Provide All Required Information</value>
|
<value>Please Provide All Required Information</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.DuplicateUrlMapping" xml:space="preserve">
|
||||||
|
<value>The Url and Redirect To cannot be the same</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.SaveUrlMapping" xml:space="preserve">
|
||||||
|
<value>The Url must belong to the current site</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -121,10 +121,10 @@
|
|||||||
<value>Redirect To:</value>
|
<value>Redirect To:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MappedUrl.HelpText" xml:space="preserve">
|
<data name="MappedUrl.HelpText" xml:space="preserve">
|
||||||
<value>A fully qualified Url where the user will be redirected</value>
|
<value>A relative or absolute Url where the user will be redirected</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Url.HelpText" xml:space="preserve">
|
<data name="Url.HelpText" xml:space="preserve">
|
||||||
<value>A fully qualified Url for this site</value>
|
<value>A relative Url identifying a path to a specific page in the site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Url.Text" xml:space="preserve">
|
<data name="Url.Text" xml:space="preserve">
|
||||||
<value>Url:</value>
|
<value>Url:</value>
|
||||||
@ -138,4 +138,7 @@
|
|||||||
<data name="Message.InfoRequired" xml:space="preserve">
|
<data name="Message.InfoRequired" xml:space="preserve">
|
||||||
<value>Please Provide All Required Information</value>
|
<value>Please Provide All Required Information</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.DuplicateUrlMapping" xml:space="preserve">
|
||||||
|
<value>The Url and Redirect To cannot be the same</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
|
||||||
@ -169,10 +169,10 @@
|
|||||||
<value>Identity</value>
|
<value>Identity</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Confirm.HelpText" xml:space="preserve">
|
<data name="Confirm.HelpText" xml:space="preserve">
|
||||||
<value>If you are changing your password you must enter it again to confirm it matches</value>
|
<value>If you are changing your password you must enter it again to confirm it matches the value entered above</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Confirm.Text" xml:space="preserve">
|
<data name="Confirm.Text" xml:space="preserve">
|
||||||
<value>Confirm Password:</value>
|
<value>Confirmation:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DisplayName.HelpText" xml:space="preserve">
|
<data name="DisplayName.HelpText" xml:space="preserve">
|
||||||
<value>Your full name</value>
|
<value>Your full name</value>
|
||||||
@ -204,4 +204,25 @@
|
|||||||
<data name="Username.Text" xml:space="preserve">
|
<data name="Username.Text" xml:space="preserve">
|
||||||
<value>Username:</value>
|
<value>Username:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TwoFactor.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicates if you are using two factor authentication. Two factor authentication requires you to enter a verification code sent via email after you sign in.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TwoFactor.Text" xml:space="preserve">
|
||||||
|
<value>Two Factor?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAllNotifications.Header" xml:space="preserve">
|
||||||
|
<value>Clear Notifications</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAllNotifications.Message" xml:space="preserve">
|
||||||
|
<value>Are You Sure You Wish To Permanently Delete All Notifications?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAllNotifications.Text" xml:space="preserve">
|
||||||
|
<value>Delete ALL Notifications</value>
|
||||||
|
</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>
|
@ -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
|
||||||
@ -168,4 +168,7 @@
|
|||||||
<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.Placeholder" xml:space="preserve">
|
||||||
|
<value>Password</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
|
||||||
@ -183,4 +183,19 @@
|
|||||||
<data name="Profile.Heading" xml:space="preserve">
|
<data name="Profile.Heading" xml:space="preserve">
|
||||||
<value>Profile</value>
|
<value>Profile</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Password.Placeholder" xml:space="preserve">
|
||||||
|
<value>Password</value>
|
||||||
|
</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>
|
@ -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
|
||||||
@ -127,10 +127,10 @@
|
|||||||
<value>Delete User</value>
|
<value>Delete User</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AllowRegistration.HelpText" xml:space="preserve">
|
<data name="AllowRegistration.HelpText" xml:space="preserve">
|
||||||
<value>Do you want the users to be able to register for an account on the site</value>
|
<value>Do you want anonymous visitors to be able to register for an account on the site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AllowRegistration.Text" xml:space="preserve">
|
<data name="AllowRegistration.Text" xml:space="preserve">
|
||||||
<value>Allow User Registration? </value>
|
<value>Allow Registration? </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.SaveSiteSettings" xml:space="preserve">
|
<data name="Error.SaveSiteSettings" xml:space="preserve">
|
||||||
<value>Error Saving Settings</value>
|
<value>Error Saving Settings</value>
|
||||||
@ -153,4 +153,241 @@
|
|||||||
<data name="Roles.Text" xml:space="preserve">
|
<data name="Roles.Text" xml:space="preserve">
|
||||||
<value>Roles</value>
|
<value>Roles</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="LockoutDuration.HelpText" xml:space="preserve">
|
||||||
|
<value>The number of minutes a user should be locked out</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockoutDuration.Text" xml:space="preserve">
|
||||||
|
<value>Lockout Duration:</value>
|
||||||
|
</data>
|
||||||
|
<data name="MaximumFailures.HelpText" xml:space="preserve">
|
||||||
|
<value>The maximum number of sign in attempts before a user is locked out</value>
|
||||||
|
</data>
|
||||||
|
<data name="MaximumFailures.Text" xml:space="preserve">
|
||||||
|
<value>Maximum Failures:</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequireDigit.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if passwords must contain a digit</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequireDigit.Text" xml:space="preserve">
|
||||||
|
<value>Require Digit?</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequiredLength.HelpText" xml:space="preserve">
|
||||||
|
<value>The minimum length for a password</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequiredLength.Text" xml:space="preserve">
|
||||||
|
<value>Minimum Length:</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequireLower.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if passwords must contain a lower case character</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequireLower.Text" xml:space="preserve">
|
||||||
|
<value>Require Lowercase?</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequirePunctuation.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if passwords must contain a non-alphanumeric character (ie. punctuation)</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequirePunctuation.Text" xml:space="preserve">
|
||||||
|
<value>Require Punctuation?</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequireUpper.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if passwords must contain an upper case character</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequireUpper.Text" xml:space="preserve">
|
||||||
|
<value>Require Uppercase?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Success.UpdateConfig.Restart" xml:space="preserve">
|
||||||
|
<value>Configuration Updated. Please Select Restart Application For These Changes To Be Activated.</value>
|
||||||
|
</data>
|
||||||
|
<data name="UniqueCharacters.HelpText" xml:space="preserve">
|
||||||
|
<value>The minimum number of unique characters which a password must contain</value>
|
||||||
|
</data>
|
||||||
|
<data name="UniqueCharacters.Text" xml:space="preserve">
|
||||||
|
<value>Unique Characters:</value>
|
||||||
|
</data>
|
||||||
|
<data name="AllowSiteLogin.HelpText" xml:space="preserve">
|
||||||
|
<value>Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site.</value>
|
||||||
|
</data>
|
||||||
|
<data name="AllowSiteLogin.Text" xml:space="preserve">
|
||||||
|
<value>Allow Login?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Authority.HelpText" xml:space="preserve">
|
||||||
|
<value>The authority url or issuer url associated with the identity provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="Authority.Text" xml:space="preserve">
|
||||||
|
<value>Authority:</value>
|
||||||
|
</data>
|
||||||
|
<data name="AuthorizationUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The endpoint for obtaining an authorization code</value>
|
||||||
|
</data>
|
||||||
|
<data name="AuthorizationUrl.Text" xml:space="preserve">
|
||||||
|
<value>Authorization Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClientID.HelpText" xml:space="preserve">
|
||||||
|
<value>The client id for the identity provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClientID.Text" xml:space="preserve">
|
||||||
|
<value>Client ID:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClientSecret.HelpText" xml:space="preserve">
|
||||||
|
<value>The client secret for the identity provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClientSecret.Text" xml:space="preserve">
|
||||||
|
<value>Client Secret:</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreateUsers.HelpText" xml:space="preserve">
|
||||||
|
<value>Do you want new users to be created automatically? If you disable this option, users must already be registered on the site in order to sign in with their external login.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreateUsers.Text" xml:space="preserve">
|
||||||
|
<value>Create New Users?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DomainFilter.HelpText" xml:space="preserve">
|
||||||
|
<value>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.</value>
|
||||||
|
</data>
|
||||||
|
<data name="DomainFilter.Text" xml:space="preserve">
|
||||||
|
<value>Domain Filter:</value>
|
||||||
|
</data>
|
||||||
|
<data name="EmailClaimType.HelpText" xml:space="preserve">
|
||||||
|
<value>The name of the email address claim provided by the identity provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="EmailClaimType.Text" xml:space="preserve">
|
||||||
|
<value>Email Claim:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginSettings.Heading" xml:space="preserve">
|
||||||
|
<value>External Login Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockoutSettings.Heading" xml:space="preserve">
|
||||||
|
<value>Lockout Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="MetadataUrl.HelpText" xml:space="preserve">
|
||||||
|
<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 name="MetadataUrl.Text" xml:space="preserve">
|
||||||
|
<value>Metadata Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordSettings.Heading" xml:space="preserve">
|
||||||
|
<value>Password Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="PKCE.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if the identity provider supports proof key for code exchange (PKCE)</value>
|
||||||
|
</data>
|
||||||
|
<data name="PKCE.Text" xml:space="preserve">
|
||||||
|
<value>Use PKCE?</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProviderName.HelpText" xml:space="preserve">
|
||||||
|
<value>The external login provider name which will be displayed on the login page</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProviderName.Text" xml:space="preserve">
|
||||||
|
<value>Provider Name:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProviderType.HelpText" xml:space="preserve">
|
||||||
|
<value>Select the external login provider type</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProviderType.Text" xml:space="preserve">
|
||||||
|
<value>Provider Type:</value>
|
||||||
|
</data>
|
||||||
|
<data name="RedirectUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The redirect url (or callback url) which usually needs to be registered with the identity provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="RedirectUrl.Text" xml:space="preserve">
|
||||||
|
<value>Redirect Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Scopes.HelpText" xml:space="preserve">
|
||||||
|
<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 name="Scopes.Text" xml:space="preserve">
|
||||||
|
<value>Scopes:</value>
|
||||||
|
</data>
|
||||||
|
<data name="TokenUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The endpoint for obtaining an auth token</value>
|
||||||
|
</data>
|
||||||
|
<data name="TokenUrl.Text" xml:space="preserve">
|
||||||
|
<value>Token Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserInfoUrl.HelpText" xml:space="preserve">
|
||||||
|
<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 name="UserInfoUrl.Text" xml:space="preserve">
|
||||||
|
<value>User Info Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Audience.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally provide the audience for the token</value>
|
||||||
|
</data>
|
||||||
|
<data name="Audience.Text" xml:space="preserve">
|
||||||
|
<value>Audience:</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserSettings.Heading" xml:space="preserve">
|
||||||
|
<value>User Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="CookieName.HelpText" xml:space="preserve">
|
||||||
|
<value>You can choose to use a custom authentication cookie name for each site. However please be aware that if you want to share an authentication cookie between sites on the same domain they need to use a consistent cookie name. Also be aware that changing the authentication cookie name will logout all current users.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CookieName.Text" xml:space="preserve">
|
||||||
|
<value>Cookie Name:</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreateToken" xml:space="preserve">
|
||||||
|
<value>Create Token</value>
|
||||||
|
</data>
|
||||||
|
<data name="Issuer.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally provide the issuer of the token</value>
|
||||||
|
</data>
|
||||||
|
<data name="Issuer.Text" xml:space="preserve">
|
||||||
|
<value>Issuer:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Lifetime.HelpText" xml:space="preserve">
|
||||||
|
<value>The number of minutes for which a token should be valid</value>
|
||||||
|
</data>
|
||||||
|
<data name="Lifetime.Text" xml:space="preserve">
|
||||||
|
<value>Lifetime:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Secret.HelpText" xml:space="preserve">
|
||||||
|
<value>If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Secret.Text" xml:space="preserve">
|
||||||
|
<value>Secret:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Token.HelpText" xml:space="preserve">
|
||||||
|
<value>Select the Create Token button to generate a long-lived access token (valid for 1 year). Be sure to store this token in a safe location as you will not be able to access it in the future.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Token.Text" xml:space="preserve">
|
||||||
|
<value>Token:</value>
|
||||||
|
</data>
|
||||||
|
<data name="TokenSettings.Heading" xml:space="preserve">
|
||||||
|
<value>Token Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="TwoFactor.HelpText" xml:space="preserve">
|
||||||
|
<value>Do you want users to use two factor authentication? Note that you should use the Disabled option until you have successfully verified that the Notification Job in Scheduled Jobs is enabled and your SMTP options in Site Settings are configured or else you will lock yourself out.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TwoFactor.Text" xml:space="preserve">
|
||||||
|
<value>Two Factor?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Disabled" xml:space="preserve">
|
||||||
|
<value>Disabled</value>
|
||||||
|
</data>
|
||||||
|
<data name="Optional" xml:space="preserve">
|
||||||
|
<value>Optional</value>
|
||||||
|
</data>
|
||||||
|
<data name="Required" xml:space="preserve">
|
||||||
|
<value>Required</value>
|
||||||
|
</data>
|
||||||
|
<data name="LastLoginOn" xml:space="preserve">
|
||||||
|
<value>Last Login</value>
|
||||||
|
</data>
|
||||||
|
<data name="IdentifierClaimType.HelpText" xml:space="preserve">
|
||||||
|
<value>The name of the unique user identifier claim provided by the identity provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
||||||
|
<value>Identifier Claim:</value>
|
||||||
|
</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 Type:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -156,11 +156,11 @@
|
|||||||
<data name="Visitors.Heading" xml:space="preserve">
|
<data name="Visitors.Heading" xml:space="preserve">
|
||||||
<value>Visitors</value>
|
<value>Visitors</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="VisitorTracking.HelpText" xml:space="preserve">
|
<data name="Tracking.HelpText" xml:space="preserve">
|
||||||
<value>Specify if visitor tracking is enabled</value>
|
<value>Specify if visitor tracking is enabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="VisitorTracking.Text" xml:space="preserve">
|
<data name="Tracking.Text" xml:space="preserve">
|
||||||
<value>Visitor Tracking Enabled?</value>
|
<value>Tracking Enabled?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IP" xml:space="preserve">
|
<data name="IP" xml:space="preserve">
|
||||||
<value>IP</value>
|
<value>IP</value>
|
||||||
@ -171,4 +171,25 @@
|
|||||||
<data name="Created" xml:space="preserve">
|
<data name="Created" xml:space="preserve">
|
||||||
<value>Created</value>
|
<value>Created</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Details.Text" xml:space="preserve">
|
||||||
|
<value>Details</value>
|
||||||
|
</data>
|
||||||
|
<data name="Filter.HelpText" xml:space="preserve">
|
||||||
|
<value>Comma delimited list of terms which may exist in IP addresses, user agents, or languages identifying visitors which should not be tracked</value>
|
||||||
|
</data>
|
||||||
|
<data name="Filter.Text" xml:space="preserve">
|
||||||
|
<value>Filter:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Retention.HelpText" xml:space="preserve">
|
||||||
|
<value>Number of days of visitor activity to retain</value>
|
||||||
|
</data>
|
||||||
|
<data name="Retention.Text" xml:space="preserve">
|
||||||
|
<value>Retention (Days):</value>
|
||||||
|
</data>
|
||||||
|
<data name="Correlation.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if new visitors to this site should be correlated based on their IP Address</value>
|
||||||
|
</data>
|
||||||
|
<data name="Correlation.Text" xml:space="preserve">
|
||||||
|
<value>Correlate Visitors?</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -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>
|
@ -117,10 +117,52 @@
|
|||||||
<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="Confirm.Delete" xml:space="preserve">
|
||||||
|
<value>Are You Sure You Wish To Delete The {0} Version?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirm.Restore" xml:space="preserve">
|
||||||
|
<value>Are You Sure You Wish To Restore The {0} Version?</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreatedBy" xml:space="preserve">
|
||||||
|
<value>Created By</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreatedOn" xml:space="preserve">
|
||||||
|
<value>Created On</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete.Header" xml:space="preserve">
|
||||||
|
<value>Delete Version</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete.Text" xml:space="preserve">
|
||||||
|
<value>Delete</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.Content.Delete" xml:space="preserve">
|
||||||
|
<value>Error Deleting Version</value>
|
||||||
|
</data>
|
||||||
<data name="Error.Content.Load" xml:space="preserve">
|
<data name="Error.Content.Load" xml:space="preserve">
|
||||||
<value>An Error Occurred Loading Content</value>
|
<value>An Error Occurred Loading Content</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Error.Content.Restore" xml:space="preserve">
|
||||||
|
<value>Error Restoring Version</value>
|
||||||
|
</data>
|
||||||
<data name="Error.Content.Save" xml:space="preserve">
|
<data name="Error.Content.Save" xml:space="preserve">
|
||||||
<value>An Error Occurred Saving Content</value>
|
<value>An Error Occurred Saving Content</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Error.Content.View" xml:space="preserve">
|
||||||
|
<value>Error Viewing Version</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Content.Deleted" xml:space="preserve">
|
||||||
|
<value>Version Deleted</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Content.Restored" xml:space="preserve">
|
||||||
|
<value>Version Restored</value>
|
||||||
|
</data>
|
||||||
|
<data name="Restore.Header" xml:space="preserve">
|
||||||
|
<value>Restore Version</value>
|
||||||
|
</data>
|
||||||
|
<data name="Restore.Text" xml:space="preserve">
|
||||||
|
<value>Restore</value>
|
||||||
|
</data>
|
||||||
|
<data name="View.Text" xml:space="preserve">
|
||||||
|
<value>View</value>
|
||||||
|
</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>
|
@ -259,7 +259,7 @@
|
|||||||
<value>Upgrade</value>
|
<value>Upgrade</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Username" xml:space="preserve">
|
<data name="Username" xml:space="preserve">
|
||||||
<value>Username:</value>
|
<value>Username</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Version" xml:space="preserve">
|
<data name="Version" xml:space="preserve">
|
||||||
<value>Version</value>
|
<value>Version</value>
|
||||||
@ -274,7 +274,7 @@
|
|||||||
<value>Full Name:</value>
|
<value>Full Name:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LocalVersion" xml:space="preserve">
|
<data name="LocalVersion" xml:space="preserve">
|
||||||
<value>Local Version</value>
|
<value>Installed Version</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Search.Source" xml:space="preserve">
|
<data name="Search.Source" xml:space="preserve">
|
||||||
<value>source</value>
|
<value>source</value>
|
||||||
@ -318,7 +318,28 @@
|
|||||||
<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>
|
||||||
|
<data name="HidePassword" xml:space="preserve">
|
||||||
|
<value>Hide</value>
|
||||||
|
</data>
|
||||||
|
<data name="ShowPassword" xml:space="preserve">
|
||||||
|
<value>Show</value>
|
||||||
|
</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>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user