Compare commits
449 Commits
Author | SHA1 | Date | |
---|---|---|---|
8d7845a44d | |||
3a15e6e5e9 | |||
3b8a51e855 | |||
f2cb34cc35 | |||
723ce62a34 | |||
2c9a2ea021 | |||
2be008d6d1 | |||
7fb51bdd0a | |||
abdbe3694f | |||
bd87e5012f | |||
55e18f2364 | |||
655e84072d | |||
ab5409d5b6 | |||
5a5da6486c | |||
7e99252429 | |||
10e0dcef8b | |||
80c8433aad | |||
5b0ae372f8 | |||
b5a1b529ab | |||
af821dcd9a | |||
10d3c81520 | |||
e3811b453a | |||
ca0fb05baa | |||
2b4b01bf6e | |||
3a1244bddc | |||
b8fd922b19 | |||
03f856025e | |||
45f04d24c3 | |||
2435d610c7 | |||
06572bcd14 | |||
3fab79afc0 | |||
2b7dd3fed5 | |||
d65efed032 | |||
c6896ea936 | |||
b7a41bddec | |||
6fd80c3737 | |||
0aa690b3b1 | |||
6d5bcfc6ed | |||
b60de69fa5 | |||
d991b57d08 | |||
1133d7fcba | |||
6fbf0383bb | |||
0296230219 | |||
dedfbba27a | |||
dc5441da07 | |||
585648b7f3 | |||
cd0ee1c26d | |||
d7a7be5af4 | |||
13e4267c11 | |||
15bc47e3e8 | |||
1a4380dcd7 | |||
5ace34b5cd | |||
f010c0f1fa | |||
2c721ad5dd | |||
8a7c2ce2c2 | |||
b2a7b813de | |||
e85cf04b99 | |||
ab6fa48172 | |||
c81905882f | |||
f0d31c1114 | |||
497b255216 | |||
d96286d771 | |||
2441647d75 | |||
77b780d631 | |||
cdd03bf3d4 | |||
e786c35f7d | |||
e83399acb1 | |||
ffea9e3210 | |||
f71a3a1ce3 | |||
a5ccc23604 | |||
1ed4c8a094 | |||
4a74549c1b | |||
a499cfb98f | |||
01038c8296 | |||
7407f79b3d | |||
a845dd1976 | |||
9d7549da70 | |||
f5cc61384f | |||
844778d36a | |||
871b0a274e | |||
737740a3ca | |||
ae8d600600 | |||
2f1691bfb0 | |||
a3d25f91c8 | |||
ff84b50817 | |||
0be8242284 | |||
e25a6259ea | |||
1578f82efb | |||
b5f75f0c5e | |||
601caab3b6 | |||
6d3092f440 | |||
ef27937c7a | |||
f4a7b79c4f | |||
2531776a48 | |||
ced80419aa | |||
ad2816f4e8 | |||
3528b8c674 | |||
80c83c626d | |||
6a355f2aea | |||
7d94e4a53a | |||
823c04742e | |||
043fb1abd1 | |||
f01e85b690 | |||
7eb1298847 | |||
a5480c9a96 | |||
f948600e86 | |||
420182b9bf | |||
8c430ce1a6 | |||
d3717dbe19 | |||
caa83d769f | |||
365f87828f | |||
f780887866 | |||
43627d4bb8 | |||
5d7b276cd1 | |||
23597eb997 | |||
b6948367f8 | |||
db6dd5abee | |||
702eb9e466 | |||
3c99006226 | |||
aaaf5683a5 | |||
92aa2236c0 | |||
d2592f72d6 | |||
27120d6cc9 | |||
a2669d35c3 | |||
574164081b | |||
00c2f8dcd8 | |||
0202bf60e5 | |||
16436a171b | |||
4cf4e0eabd | |||
5edb98dfb4 | |||
899bf22e15 | |||
9a33167a6c | |||
2dc068aa21 | |||
0f2aa4d2e1 | |||
a699f5c7bc | |||
ab807de3c0 | |||
68514bcb36 | |||
422bf8da59 | |||
de92dc93dd | |||
5a91b143b6 | |||
c745e85706 | |||
0f698e0c50 | |||
cd16d77bf0 | |||
fdbdd0ef4c | |||
71485f4a82 | |||
013056a6e5 | |||
263498fbdc | |||
f46ac2c007 | |||
c23841b6db | |||
18b1b5fca5 | |||
6707ac2697 | |||
d85a2fc8ce | |||
a8997e8f17 | |||
c8a22d9537 | |||
910669f786 | |||
89312c6796 | |||
a6aa96fdb0 | |||
7deb0b06af | |||
784f3771b3 | |||
80316824f7 | |||
cbaebb7b7c | |||
97d3764b6e | |||
002f8117cb | |||
0dfdb12431 | |||
d77e898929 | |||
4c937be884 | |||
c25cce4398 | |||
58c422285a | |||
f2f22f35e8 | |||
15867a7807 | |||
22e3161a9b | |||
b0c8203b24 | |||
e2c404d8bb | |||
5ee1731c92 | |||
06e8d3b660 | |||
f09709aedb | |||
598d5decac | |||
7832a6053e | |||
588748230e | |||
8668165c72 | |||
a967332f89 | |||
6719d242bd | |||
3565185808 | |||
ce51262197 | |||
992a786c2b | |||
038df95aa0 | |||
4ebd660de2 | |||
a9ea41a488 | |||
196594b490 | |||
1516d5af10 | |||
ffcd1595a9 | |||
42e5c6e111 | |||
8a9651dc50 | |||
4be2f4f2d9 | |||
369bf7a235 | |||
136545b404 | |||
73ea17ae0f | |||
23010acef4 | |||
4f74962ce2 | |||
7f978c7845 | |||
731fd46ea2 | |||
859759d691 | |||
5e9567158f | |||
51d244f3aa | |||
4c5a07edd5 | |||
8113a754a1 | |||
3cd40c6195 | |||
56cfb2ce06 | |||
72087823ac | |||
bcf7866fe2 | |||
b64772e484 | |||
088d665942 | |||
7d6c10befb | |||
f88e3d04b8 | |||
f57676a22b | |||
8618cb62e4 | |||
c31c88ed1f | |||
6022acd21f | |||
b3071b9272 | |||
ec6a6d6653 | |||
52f552b4de | |||
2643d3396b | |||
62d59a09cf | |||
a68ff8a4f0 | |||
93d4bfcd7a | |||
5fb80c1a7b | |||
04b38444ce | |||
ca3df02002 | |||
d952c33fab | |||
93bc1cd5af | |||
0e5b370ee8 | |||
f4fd4e28c9 | |||
ec8433eb45 | |||
0d4a40e9bb | |||
c3668f4179 | |||
3adb7ecb1c | |||
aa5b84a214 | |||
3d83fccbf1 | |||
4c4255be6b | |||
ed6054b082 | |||
f60f7a4dc1 | |||
998dc95cb2 | |||
12f5d7b846 | |||
906ae0a43e | |||
485b774876 | |||
3121cf5b75 | |||
ce7570dae2 | |||
b5ea0dfbc7 | |||
dd0f8f4772 | |||
9d0ab34274 | |||
e6038be6f7 | |||
bd2153a0ed | |||
e526deac20 | |||
b65f165dcf | |||
f9fbe5adc2 | |||
d1e73571a1 | |||
1047058676 | |||
29a1e77da8 | |||
290547e482 | |||
3df45ca20f | |||
cc06258484 | |||
2c262d0655 | |||
1875e1e158 | |||
1c95967b31 | |||
2ae98929de | |||
6a1dd38cbb | |||
c458a77d27 | |||
352c23f389 | |||
6c5a1dc2e1 | |||
004ff1e91d | |||
f7de4c567b | |||
b2ad1010ac | |||
660e164ff8 | |||
eb6dc80b50 | |||
a9882cc96a | |||
fb5a2ce178 | |||
d441b31dc7 | |||
d4239fe7e0 | |||
6a98999105 | |||
5caa1fe7d4 | |||
ef33bdb65e | |||
14a463382b | |||
1ad79874c8 | |||
1a61a58d28 | |||
3e3c973679 | |||
9dede84d20 | |||
e78b11cf62 | |||
341ca5a330 | |||
e5d8c02def | |||
947aa08c42 | |||
9a04d436bd | |||
be0754f568 | |||
93c4bbc0d1 | |||
b98535810b | |||
393cf8da1f | |||
ea2846973a | |||
3398c1cbfe | |||
39c79ea68e | |||
66900f4a32 | |||
df71dd14f7 | |||
8113ca3069 | |||
d468e675c2 | |||
1e84cedf82 | |||
7f4087e3de | |||
facd3c8956 | |||
3e50deecb7 | |||
628c504f84 | |||
e1ada78c1f | |||
09fa1e365c | |||
28b6b03d06 | |||
a4395b62ff | |||
4511acf273 | |||
fde53a2d83 | |||
f6cd04fdb8 | |||
a5eede6c7a | |||
2e83817c83 | |||
82aea40ae4 | |||
1b289eae24 | |||
81420b2c88 | |||
775731b745 | |||
489e7d4a67 | |||
b6508764d8 | |||
6dedd87305 | |||
89fa29b310 | |||
be5df9c22a | |||
db17739716 | |||
1439362a5e | |||
65c1b04772 | |||
83d30ebdc4 | |||
b7928a5255 | |||
a83ff9253d | |||
8cdcdaf6d9 | |||
d208edf153 | |||
8ada4765b1 | |||
9d3a808c24 | |||
828eb80266 | |||
0639f6c1d1 | |||
49b971280f | |||
3682a1010d | |||
f8b58866dc | |||
5d8d815d84 | |||
67743c7597 | |||
b33cc90447 | |||
78177f7890 | |||
3c417bfa99 | |||
f2c8d80ff8 | |||
8c1eb1e19f | |||
eb7188e81b | |||
f352fc5c67 | |||
ac313722f9 | |||
64c7f1962c | |||
c83f994b21 | |||
10f38a72f7 | |||
da35434f58 | |||
c79409e094 | |||
355ce00968 | |||
56c832f3ba | |||
40abb2720e | |||
9b6051afee | |||
dcf6f26792 | |||
85734c1146 | |||
c3fb8fcb6e | |||
013bbc1638 | |||
b0669a3b60 | |||
6f5da1ce7c | |||
3351732a2f | |||
d4d4034ecd | |||
b45e2742c3 | |||
42eba290e4 | |||
32d1e08b57 | |||
7b7d19da7c | |||
f78e400918 | |||
be4e9bf7e9 | |||
48f2079f88 | |||
36ad1ceef2 | |||
1f2e2148d5 | |||
76f3d345f9 | |||
f2c854b53a | |||
e2d336d90b | |||
c74065ff26 | |||
b6d97dc5d5 | |||
1c1c26948a | |||
d954e3ffb7 | |||
d9759a95eb | |||
d196402dd0 | |||
0f2b5531de | |||
26e4398905 | |||
06995f22fe | |||
caa2073d48 | |||
d9466fe4bc | |||
72e623a3a7 | |||
69bc06685f | |||
bf175984f3 | |||
4d4a7bfd0d | |||
044cee30a5 | |||
3c0c5aed60 | |||
e194971727 | |||
bbe85def23 | |||
9c2d53f2ae | |||
1cf36e2156 | |||
ce96e309af | |||
9ea7dc8b3c | |||
282242efd2 | |||
351ba22d64 | |||
2916625747 | |||
744dbeb7a3 | |||
fbb3c309ee | |||
473c265bac | |||
01b06c2c3c | |||
8b1f95c743 | |||
ff5dbec579 | |||
9620c5a98f | |||
7e849a2e95 | |||
229aed306e | |||
d718969cbd | |||
8593f4c3a9 | |||
d4f71d5026 | |||
a683a5f206 | |||
7917cc3eb5 | |||
6cd7ca755e | |||
fb7dfdc800 | |||
e096af320f | |||
65bff8f511 | |||
ca19d8a842 | |||
cde08c64ce | |||
3d6d7b7cb9 | |||
a56ef6f398 | |||
5a38b6614d | |||
3d5c44e8aa | |||
46a68bd5e7 | |||
c77ded51a9 | |||
59bd9fdc22 | |||
b81941394c | |||
197d5ca1f2 | |||
97eb986fa6 | |||
aba3110e31 | |||
51c27be236 | |||
592255284f | |||
82fd41dd4c | |||
4ae8df9652 | |||
759b19e444 | |||
36f705e46f | |||
4b17847ea5 | |||
2addcc3ab5 | |||
370b39a139 | |||
7b7e64576f | |||
286928d59e | |||
cc65555c3d | |||
63e3923349 |
11
.gitignore
vendored
11
.gitignore
vendored
@ -22,10 +22,17 @@ 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/_content/*
|
||||||
|
!Oqtane.Server/wwwroot/_content/Placeholder.txt
|
||||||
|
|
||||||
|
Oqtane.Server/wwwroot/Modules/*
|
||||||
!Oqtane.Server/wwwroot/Modules/Oqtane.Modules.*
|
!Oqtane.Server/wwwroot/Modules/Oqtane.Modules.*
|
||||||
!Oqtane.Server/wwwroot/Modules/Templates
|
!Oqtane.Server/wwwroot/Modules/Templates
|
||||||
|
Oqtane.Server/wwwroot/Modules/Templates/*
|
||||||
|
!Oqtane.Server/wwwroot/Modules/Templates/External
|
||||||
|
|
||||||
Oqtane.Server/wwwroot/Themes
|
Oqtane.Server/wwwroot/Themes/*
|
||||||
!Oqtane.Server/wwwroot/Themes/Oqtane.Themes.*
|
!Oqtane.Server/wwwroot/Themes/Oqtane.Themes.*
|
||||||
!Oqtane.Server/wwwroot/Themes/Templates
|
!Oqtane.Server/wwwroot/Themes/Templates
|
||||||
|
Oqtane.Server/wwwroot/Themes/Templates/*
|
||||||
|
Oqtane.Server/wwwroot/Themes/Templates/External
|
||||||
|
24
CONTRIBUTING.md
Normal file
24
CONTRIBUTING.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Contributing to Oqtane
|
||||||
|
|
||||||
|
## How to Contribute
|
||||||
|
|
||||||
|
We track all of our issues on Github. If you want to contribute, everything starts with an issue. If you don't have an issue yet, you can add one. Then a core contributor will tag it as either an enhancement [ENH] or a bug [BUG]. Tagged issues are open for contribution.
|
||||||
|
|
||||||
|
## Use GitHub-flow process
|
||||||
|
- Make a comment on the issue that you intend to work on it and read all the comments to gain a full understanding.
|
||||||
|
- Fork the repository
|
||||||
|
- Create a new branch and update your comment on the issue with a llink to the branch
|
||||||
|
- Make your changes and commit them
|
||||||
|
- Push to the branch
|
||||||
|
- Create a pull request
|
||||||
|
|
||||||
|
## Reporting Bugs
|
||||||
|
|
||||||
|
- Check if the issue has already been reported.
|
||||||
|
- Open a new issue if it hasn’t been reported.
|
||||||
|
|
||||||
|
## Requesting Features
|
||||||
|
|
||||||
|
- Use the feature request template in the Issues tab.
|
||||||
|
|
||||||
|
Thank you for contributing!
|
@ -51,6 +51,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
services.AddScoped<IUrlMappingService, UrlMappingService>();
|
services.AddScoped<IUrlMappingService, UrlMappingService>();
|
||||||
services.AddScoped<IVisitorService, VisitorService>();
|
services.AddScoped<IVisitorService, VisitorService>();
|
||||||
services.AddScoped<ISyncService, SyncService>();
|
services.AddScoped<ISyncService, SyncService>();
|
||||||
|
services.AddScoped<ILocalizationCookieService, LocalizationCookieService>();
|
||||||
|
|
||||||
// providers
|
// providers
|
||||||
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="mx-auto text-center">
|
<div class="mx-auto text-center">
|
||||||
<img src="oqtane-black.png" />
|
<img src="oqtane-black.png" />
|
||||||
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET 8)</div>
|
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET 9)</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="app-rule" />
|
<hr class="app-rule" />
|
||||||
@ -162,7 +162,7 @@
|
|||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
// include CSS
|
// include CSS
|
||||||
var content = "<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css\" integrity=\"sha512-t4GWSVZO1eC8BM339Xd7Uphw5s17a86tIZIj8qRxhnKub6WoyhnrxeCIMeAqBPgdZGlCcG2PrZjMc+Wr78+5Xg==\" crossorigin=\"anonymous\" type=\"text/css\"/>";
|
var content = $"<link rel=\"stylesheet\" href=\"{Constants.BootstrapStylesheetUrl}\" integrity=\"{Constants.BootstrapStylesheetIntegrity}\" crossorigin=\"anonymous\" type=\"text/css\"/>";
|
||||||
SiteState.AppendHeadContent(content);
|
SiteState.AppendHeadContent(content);
|
||||||
|
|
||||||
_togglePassword = SharedLocalizer["ShowPassword"];
|
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||||
@ -217,7 +217,7 @@
|
|||||||
{
|
{
|
||||||
// include JavaScript
|
// include JavaScript
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js", "sha512-VK2zcvntEufaimc+efOYi622VN5ZacdnufnmX7zIhCPmjhKnOi9ZDMtg1/ug5l183f19gG1/cBstPO4D8N/Img==", "anonymous", "", "head");
|
await interop.IncludeScript("", Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous", "", "head");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +238,8 @@
|
|||||||
|
|
||||||
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
|
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
|
||||||
{
|
{
|
||||||
if (await UserService.ValidatePasswordAsync(_hostPassword))
|
var result = await UserService.ValidateUserAsync(_hostUsername, _hostEmail, _hostPassword);
|
||||||
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
_loadingDisplay = "";
|
_loadingDisplay = "";
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
@ -278,7 +279,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_message = Localizer["Message.Password.Invalid"];
|
_message = string.Join("<br />", result.Errors.Select(i => !string.IsNullOrEmpty(i.Value) ? i.Value : Localizer[i.Key]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Admin.Languages
|
@namespace Oqtane.Modules.Admin.Languages
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@using Microsoft.AspNetCore.Localization
|
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject ILocalizationService LocalizationService
|
@inject ILocalizationService LocalizationService
|
||||||
@inject ILanguageService LanguageService
|
@inject ILanguageService LanguageService
|
||||||
@ -94,7 +93,6 @@ else
|
|||||||
var language = new Language
|
var language = new Language
|
||||||
{
|
{
|
||||||
SiteId = PageState.Page.SiteId,
|
SiteId = PageState.Page.SiteId,
|
||||||
Name = CultureInfo.GetCultureInfo(_code).DisplayName,
|
|
||||||
Code = _code,
|
Code = _code,
|
||||||
IsDefault = (_default == null ? false : Boolean.Parse(_default))
|
IsDefault = (_default == null ? false : Boolean.Parse(_default))
|
||||||
};
|
};
|
||||||
@ -130,7 +128,7 @@ else
|
|||||||
{
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360, true, "Lax");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Admin.Languages
|
@namespace Oqtane.Modules.Admin.Languages
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@using Microsoft.AspNetCore.Localization
|
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject ILocalizationService LocalizationService
|
@inject ILocalizationService LocalizationService
|
||||||
@inject ILanguageService LanguageService
|
@inject ILanguageService LanguageService
|
||||||
@ -103,7 +102,7 @@ else
|
|||||||
{
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360, true, "Lax");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,14 +8,12 @@
|
|||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<AuthorizeView Roles="@RoleNames.Registered">
|
@if (PageState.User != null)
|
||||||
<Authorizing>
|
{
|
||||||
<text>...</text>
|
|
||||||
</Authorizing>
|
|
||||||
<Authorized>
|
|
||||||
<ModuleMessage Message="@Localizer["Info.SignedIn"]" Type="MessageType.Info" />
|
<ModuleMessage Message="@Localizer["Info.SignedIn"]" Type="MessageType.Info" />
|
||||||
</Authorized>
|
}
|
||||||
<NotAuthorized>
|
else
|
||||||
|
{
|
||||||
@if (!twofactor)
|
@if (!twofactor)
|
||||||
{
|
{
|
||||||
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
@ -23,7 +21,9 @@
|
|||||||
@if (_allowexternallogin)
|
@if (_allowexternallogin)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-primary" @onclick="ExternalLogin">@Localizer["Use"] @PageState.Site.Settings["ExternalLogin:ProviderName"]</button>
|
<button type="button" class="btn btn-primary" @onclick="ExternalLogin">@Localizer["Use"] @PageState.Site.Settings["ExternalLogin:ProviderName"]</button>
|
||||||
<br /><br />
|
<br />
|
||||||
|
|
||||||
|
<br />
|
||||||
}
|
}
|
||||||
@if (_allowsitelogin)
|
@if (_allowsitelogin)
|
||||||
{
|
{
|
||||||
@ -49,11 +49,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
<br /><br />
|
<br />
|
||||||
|
|
||||||
|
<br />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["ForgotPassword"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["ForgotPassword"]</button>
|
||||||
@if (PageState.Site.AllowRegistration)
|
@if (PageState.Site.AllowRegistration)
|
||||||
{
|
{
|
||||||
<br /><br />
|
<br />
|
||||||
|
|
||||||
|
<br />
|
||||||
<NavLink href="@NavigateUrl("register")">@Localizer["Register"]</NavLink>
|
<NavLink href="@NavigateUrl("register")">@Localizer["Register"]</NavLink>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,8 +78,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
</NotAuthorized>
|
}
|
||||||
</AuthorizeView>
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private bool _allowsitelogin = true;
|
private bool _allowsitelogin = true;
|
||||||
@ -204,9 +207,9 @@
|
|||||||
user = await UserService.VerifyTwoFactorAsync(user, _code);
|
user = await UserService.VerifyTwoFactorAsync(user, _code);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.IsAuthenticated)
|
if (user != null && user.IsAuthenticated)
|
||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Login Successful For {Username} From IP Address {IPAddress}", _username, SiteState.RemoteIPAddress);
|
||||||
|
|
||||||
// return url is not specified if user navigated directly to login page
|
// return url is not specified if user navigated directly to login page
|
||||||
var returnurl = (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : PageState.Alias.Path;
|
var returnurl = (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : PageState.Alias.Path;
|
||||||
@ -228,7 +231,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "required" || user.TwoFactorRequired)
|
if (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "required" || (user != null && user.TwoFactorRequired))
|
||||||
{
|
{
|
||||||
twofactor = true;
|
twofactor = true;
|
||||||
validated = false;
|
validated = false;
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
|
<Label Class="col-sm-3" For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="description" class="form-control" @bind="@_description" rows="3" maxlength="2000" required></textarea>
|
<textarea id="description" class="form-control" @bind="@_description" rows="3" maxlength="2000"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -118,6 +118,7 @@
|
|||||||
{
|
{
|
||||||
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
|
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(_description)) _description = _module;
|
||||||
if (IsValidXML(_description))
|
if (IsValidXML(_description))
|
||||||
{
|
{
|
||||||
var template = _templates.FirstOrDefault(item => item.Name == _template);
|
var template = _templates.FirstOrDefault(item => item.Name == _template);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@using Microsoft.AspNetCore.Localization
|
|
||||||
@inject IModuleDefinitionService ModuleDefinitionService
|
@inject IModuleDefinitionService ModuleDefinitionService
|
||||||
@inject IPackageService PackageService
|
@inject IPackageService PackageService
|
||||||
@inject ILanguageService LanguageService
|
@inject ILanguageService LanguageService
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="button" class="btn btn-success" @onclick="ExportModule">@Localizer["Export"]</button>
|
<button type="button" class="btn btn-success" @onclick="ExportModule">@Localizer["Export"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _content = string.Empty;
|
private string _content = string.Empty;
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="button" class="btn btn-success" @onclick="ImportModule">@Localizer["Import"]</button>
|
<button type="button" class="btn btn-success" @onclick="ImportModule">@Localizer["Import"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
@ -9,8 +9,10 @@
|
|||||||
@inject IStringLocalizer<Settings> Localizer
|
@inject IStringLocalizer<Settings> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
@if (_initialized)
|
||||||
<TabStrip>
|
{
|
||||||
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
|
<TabStrip ActiveTab="@_activetab">
|
||||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
||||||
@if (_containers != null)
|
@if (_containers != null)
|
||||||
{
|
{
|
||||||
@ -127,11 +129,13 @@
|
|||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
|
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
|
||||||
</form>
|
</form>
|
||||||
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
|
private bool _initialized = false;
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
@ -158,12 +162,12 @@
|
|||||||
private DateTime? _effectivedate = null;
|
private DateTime? _effectivedate = null;
|
||||||
private DateTime? _expirydate = null;
|
private DateTime? _expirydate = null;
|
||||||
private List<Page> _pages;
|
private List<Page> _pages;
|
||||||
|
private string _activetab = "";
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
SetModuleTitle(Localizer["ModuleSettings.Title"]);
|
SetModuleTitle(Localizer["ModuleSettings.Title"]);
|
||||||
|
|
||||||
_module = ModuleState.ModuleDefinition.Name;
|
|
||||||
_title = ModuleState.Title;
|
_title = ModuleState.Title;
|
||||||
_moduleSettingsTitle = Localizer["ModuleSettings.Heading"];
|
_moduleSettingsTitle = Localizer["ModuleSettings.Heading"];
|
||||||
_pane = ModuleState.Pane;
|
_pane = ModuleState.Pane;
|
||||||
@ -182,6 +186,7 @@
|
|||||||
|
|
||||||
if (ModuleState.ModuleDefinition != null)
|
if (ModuleState.ModuleDefinition != null)
|
||||||
{
|
{
|
||||||
|
_module = ModuleState.ModuleDefinition.Name;
|
||||||
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
||||||
@ -231,10 +236,13 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveModule()
|
private async Task SaveModule()
|
||||||
{
|
{
|
||||||
|
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
@ -294,11 +302,13 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_activetab = "Settings";
|
||||||
AddModuleMessage(Localizer["Message.Required.Title"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.Title"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_activetab = "Settings";
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,9 +155,16 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
||||||
@foreach (var theme in _themes)
|
@foreach (var theme in _themes)
|
||||||
|
{
|
||||||
|
@if (theme.TypeName == PageState.Site.DefaultThemeType)
|
||||||
|
{
|
||||||
|
<option value="@theme.TypeName">*@theme.Name*</option>
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
<option value="@theme.TypeName">@theme.Name</option>
|
<option value="@theme.TypeName">@theme.Name</option>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -309,10 +316,11 @@
|
|||||||
{
|
{
|
||||||
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);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThemeChanged(ChangeEventArgs e)
|
private async Task ThemeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_themetype = (string)e.Value;
|
_themetype = (string)e.Value;
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||||
@ -323,6 +331,7 @@
|
|||||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,6 +347,7 @@
|
|||||||
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(_themetype) && !string.IsNullOrEmpty(_containertype))
|
if (!string.IsNullOrEmpty(_themetype) && !string.IsNullOrEmpty(_containertype))
|
||||||
@ -388,12 +398,14 @@
|
|||||||
if (_pages.Any(item => item.Path == page.Path))
|
if (_pages.Any(item => item.Path == page.Path))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,6 +473,7 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -468,11 +481,13 @@
|
|||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
|
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,9 +171,16 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
||||||
@foreach (var theme in _themes)
|
@foreach (var theme in _themes)
|
||||||
|
{
|
||||||
|
@if (theme.TypeName == PageState.Site.DefaultThemeType)
|
||||||
|
{
|
||||||
|
<option value="@theme.TypeName">*@theme.Name*</option>
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
<option value="@theme.TypeName">@theme.Name</option>
|
<option value="@theme.TypeName">@theme.Name</option>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -261,9 +268,16 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="theme" class="form-select" @bind="@_themetype" required>
|
<select id="theme" class="form-select" @bind="@_themetype" required>
|
||||||
@foreach (var theme in _themes)
|
@foreach (var theme in _themes)
|
||||||
|
{
|
||||||
|
@if (theme.TypeName == PageState.Site.DefaultThemeType)
|
||||||
|
{
|
||||||
|
<option value="@theme.TypeName">*@theme.Name*</option>
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
<option value="@theme.TypeName">@theme.Name</option>
|
<option value="@theme.TypeName">@theme.Name</option>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -465,10 +479,11 @@
|
|||||||
{
|
{
|
||||||
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);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThemeChanged(ChangeEventArgs e)
|
private async Task ThemeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_themetype = (string)e.Value;
|
_themetype = (string)e.Value;
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||||
@ -480,6 +495,7 @@
|
|||||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,6 +533,7 @@
|
|||||||
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
||||||
@ -567,12 +584,14 @@
|
|||||||
if (_pages.Any(item => item.Path == _page.Path && item.PageId != _page.PageId))
|
if (_pages.Any(item => item.Path == _page.Path && item.PageId != _page.PageId))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_page.ParentId == null && Constants.ReservedRoutes.Contains(_page.Name.ToLower()))
|
if (_page.ParentId == null && Constants.ReservedRoutes.Contains(_page.Name.ToLower()))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], _page.Name), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], _page.Name), MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -657,17 +676,20 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Saving Page {Page} {Error}", _page, ex.Message);
|
await logger.LogError(ex, "Error Saving Page {Page} {Error}", _page, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ else
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<Pager Items="@_pages.Where(item => item.IsDeleted)" CurrentPage="@_pagePage.ToString()" OnPageChange="OnPageChangePage">
|
<Pager Items="@_pages.Where(item => item.IsDeleted).OrderByDescending(item => item.DeletedOn)" CurrentPage="@_pagePage.ToString()" OnPageChange="OnPageChangePage">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
@ -50,7 +50,7 @@ else
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<Pager Items="@_modules.Where(item => item.IsDeleted)" CurrentPage="@_pageModule.ToString()" OnPageChange="OnPageChangeModule">
|
<Pager Items="@_modules.Where(item => item.IsDeleted).OrderByDescending(item => item.DeletedOn)" CurrentPage="@_pageModule.ToString()" OnPageChange="OnPageChangeModule">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
|
@ -11,14 +11,12 @@
|
|||||||
{
|
{
|
||||||
if (!_userCreated)
|
if (!_userCreated)
|
||||||
{
|
{
|
||||||
<AuthorizeView Roles="@RoleNames.Registered">
|
if (PageState.User != null)
|
||||||
<Authorizing>
|
{
|
||||||
<text>...</text>
|
|
||||||
</Authorizing>
|
|
||||||
<Authorized>
|
|
||||||
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
|
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
|
||||||
</Authorized>
|
}
|
||||||
<NotAuthorized>
|
else
|
||||||
|
{
|
||||||
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
|
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
|
||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -64,12 +62,13 @@
|
|||||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
@if (_allowsitelogin)
|
@if (_allowsitelogin)
|
||||||
{
|
{
|
||||||
<br /><br />
|
<br />
|
||||||
|
|
||||||
|
<br />
|
||||||
<NavLink href="@NavigateUrl("login")">@Localizer["Login"]</NavLink>
|
<NavLink href="@NavigateUrl("login")">@Localizer["Login"]</NavLink>
|
||||||
}
|
}
|
||||||
</form>
|
</form>
|
||||||
</NotAuthorized>
|
}
|
||||||
</AuthorizeView>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
private string _enabled = "True";
|
private string _enabled = "True";
|
||||||
private string _lastIndexedOn = "";
|
private string _lastIndexedOn = "";
|
||||||
private string _ignorePages = "";
|
private string _ignorePages = "";
|
||||||
private string _ignoreEntities = "";
|
private string _ignoreEntities = "File";
|
||||||
private string _minimumWordLength = "3";
|
private string _minimumWordLength = "3";
|
||||||
private string _ignoreWords = "the,be,to,of,and,a,i,in,that,have,it,for,not,on,with,he,as,you,do,at,this,but,his,by,from,they,we,say,her,she,or,an,will,my,one,all,would,there,their,what,so,up,out,if,about,who,get,which,go,me,when,make,can,like,time,no,just,him,know,take,people,into,year,your,good,some,could,them,see,other,than,then,now,look,only,come,its,over,think,also,back,after,use,two,how,our,work,first,well,way,even,new,want,because,any,these,give,day,most,us";
|
private string _ignoreWords = "the,be,to,of,and,a,i,in,that,have,it,for,not,on,with,he,as,you,do,at,this,but,his,by,from,they,we,say,her,she,or,an,will,my,one,all,would,there,their,what,so,up,out,if,about,who,get,which,go,me,when,make,can,like,time,no,just,him,know,take,people,into,year,your,good,some,could,them,see,other,than,then,now,look,only,come,its,over,think,also,back,after,use,two,how,our,work,first,well,way,even,new,want,because,any,these,give,day,most,us";
|
||||||
|
|
||||||
@ -85,7 +85,7 @@
|
|||||||
{
|
{
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
settings = SettingService.SetSetting(settings, "Search_SearchProvider", _searchProvider);
|
settings = SettingService.SetSetting(settings, "Search_SearchProvider", _searchProvider);
|
||||||
settings = SettingService.SetSetting(settings, "Search_Enabled", _enabled, true);
|
settings = SettingService.SetSetting(settings, "Search_Enabled", _enabled);
|
||||||
settings = SettingService.SetSetting(settings, "Search_LastIndexedOn", _lastIndexedOn, true);
|
settings = SettingService.SetSetting(settings, "Search_LastIndexedOn", _lastIndexedOn, true);
|
||||||
settings = SettingService.SetSetting(settings, "Search_IgnorePages", _ignorePages, true);
|
settings = SettingService.SetSetting(settings, "Search_IgnorePages", _ignorePages, true);
|
||||||
settings = SettingService.SetSetting(settings, "Search_IgnoreEntities", _ignoreEntities, true);
|
settings = SettingService.SetSetting(settings, "Search_IgnoreEntities", _ignoreEntities, true);
|
||||||
@ -106,9 +106,7 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_lastIndexedOn = DateTime.MinValue.ToString();
|
_lastIndexedOn = DateTime.MinValue.ToString();
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
await Save();
|
||||||
settings = SettingService.SetSetting(settings, "Search_LastIndexedOn", _lastIndexedOn, true);
|
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
|
||||||
AddModuleMessage(Localizer["Message.Reindex"], MessageType.Success);
|
AddModuleMessage(Localizer["Message.Reindex"], MessageType.Success);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
@inject ISettingService SettingService
|
@inject ISettingService SettingService
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
@attribute [StreamRendering] // attribute allows the progress indicator to be displayed
|
||||||
|
|
||||||
<div class="search-result-container">
|
<div class="search-result-container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -207,14 +207,14 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmtpUsername">Username: </Label>
|
<Label Class="col-sm-3" For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmtpUsername">Username: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="username" class="form-control" @bind="@_smtpusername" />
|
<input id="username" class="form-control" @bind="@_smtpusername" autocomplete="off"/>
|
||||||
</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="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">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="password" type="@_smtppasswordtype" class="form-control" @bind="@_smtppassword" />
|
<input id="password" type="@_smtppasswordtype" class="form-control" @bind="@_smtppassword" autocomplete="off"/>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleSMTPPassword" tabindex="-1">@_togglesmtppassword</button>
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleSMTPPassword" tabindex="-1">@_togglesmtppassword</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -376,7 +376,7 @@
|
|||||||
<Section Name="TenantInformation" Heading="Database" ResourceKey="TenantInformation">
|
<Section Name="TenantInformation" Heading="Database" ResourceKey="TenantInformation">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="tenant" HelpText="The name of the database used for the site" ResourceKey="Tenant">Database: </Label>
|
<Label Class="col-sm-3" For="tenant" HelpText="The name of the database used for the site. Note that this is not the physical database name but rather the tenant name which is used within the framework to identify a database." ResourceKey="Tenant">Database: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
|
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
|
||||||
</div>
|
</div>
|
||||||
@ -388,7 +388,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
|
<Label Class="col-sm-3" For="connectionstring" HelpText="The name of the connection string in appsettings.json which will be used to connect to the database" ResourceKey="ConnectionString">Connection: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="connectionstring" class="form-control" @bind="@_connectionstring" readonly />
|
<input id="connectionstring" class="form-control" @bind="@_connectionstring" readonly />
|
||||||
</div>
|
</div>
|
||||||
@ -481,6 +481,11 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (PageState.QueryString.ContainsKey("updated"))
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
|
||||||
|
}
|
||||||
|
|
||||||
Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
||||||
if (site != null)
|
if (site != null)
|
||||||
{
|
{
|
||||||
@ -571,7 +576,7 @@
|
|||||||
if (tenant != null)
|
if (tenant != null)
|
||||||
{
|
{
|
||||||
_tenant = tenant.Name;
|
_tenant = tenant.Name;
|
||||||
_database = _databases.Find(item => item.DBType == tenant.DBType)?.Name;
|
_database = _databases.Find(item => item.DBType == tenant.DBType && item.Name != "LocalDB")?.Name;
|
||||||
_connectionstring = tenant.DBConnectionString;
|
_connectionstring = tenant.DBConnectionString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -736,7 +741,7 @@
|
|||||||
|
|
||||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl(), true); // reload
|
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "updated=true"), true); // reload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -109,7 +109,7 @@ else
|
|||||||
<hr class="app-rule" />
|
<hr class="app-rule" />
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="name" HelpText="Enter the name for the database" ResourceKey="TenantName">Name: </Label>
|
<Label Class="col-sm-3" For="name" HelpText="Enter the name for the database. Note that this will be the tenant name which is used within the framework to identify the database." ResourceKey="TenantName">Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required />
|
<input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required />
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,23 +83,14 @@ else
|
|||||||
{
|
{
|
||||||
@if (_connection != "-")
|
@if (_connection != "-")
|
||||||
{
|
{
|
||||||
|
@if (!string.IsNullOrEmpty(_tenant))
|
||||||
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="databasetype" HelpText="The database type" ResourceKey="DatabaseType">Type: </Label>
|
<Label Class="col-sm-3" For="databasetype" HelpText="The database type" ResourceKey="DatabaseType">Type: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@if (_databases != null)
|
<input id="databasetype" class="form-control" @bind="@_databasetype" readonly />
|
||||||
{
|
|
||||||
<select id="databasetype" class="form-select" @bind="@_databasetype" required>
|
|
||||||
<option value="-"><@Localizer["Type.Select"]></option>
|
|
||||||
@foreach (var database in _databases)
|
|
||||||
{
|
|
||||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (!string.IsNullOrEmpty(_tenant))
|
|
||||||
{
|
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="tenant" HelpText="The database using this connection" ResourceKey="Tenant">Database: </Label>
|
<Label Class="col-sm-3" For="tenant" HelpText="The database using this connection" ResourceKey="Tenant">Database: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -204,12 +195,12 @@ else
|
|||||||
{
|
{
|
||||||
_connectionstring = _connections[_connection].ToString();
|
_connectionstring = _connections[_connection].ToString();
|
||||||
_tenant = "";
|
_tenant = "";
|
||||||
_databasetype = "-";
|
_databasetype = "";
|
||||||
var tenant = _tenants.FirstOrDefault(item => item.DBConnectionString == _connection);
|
var tenant = _tenants.FirstOrDefault(item => item.DBConnectionString == _connection);
|
||||||
if (tenant != null)
|
if (tenant != null)
|
||||||
{
|
{
|
||||||
_tenant = tenant.Name;
|
_tenant = tenant.Name;
|
||||||
_databasetype = _databases.FirstOrDefault(item => item.DBType == tenant.DBType).Name;
|
_databasetype = _databases.FirstOrDefault(item => item.DBType == tenant.DBType && item.Name != "LocalDB").Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -54,6 +54,8 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
AddModuleMessage(Localizer["Disclaimer.Text"], MessageType.Warning);
|
||||||
|
|
||||||
List<Package> packages = await PackageService.GetPackagesAsync("framework", "", "", "");
|
List<Package> packages = await PackageService.GetPackagesAsync("framework", "", "", "");
|
||||||
if (packages != null)
|
if (packages != null)
|
||||||
{
|
{
|
||||||
@ -97,13 +99,16 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
ShowProgressIndicator();
|
||||||
await PackageService.DownloadPackageAsync(packageid, version);
|
await PackageService.DownloadPackageAsync(packageid, version);
|
||||||
await PackageService.DownloadPackageAsync(Constants.UpdaterPackageId, version);
|
await PackageService.DownloadPackageAsync(Constants.UpdaterPackageId, version);
|
||||||
|
HideProgressIndicator();
|
||||||
AddModuleMessage(Localizer["Success.Framework.Download"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.Framework.Download"], MessageType.Success);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Downloading Framework Package {Error}", ex.Message);
|
await logger.LogError(ex, "Error Downloading Framework Package {Error}", ex.Message);
|
||||||
|
HideProgressIndicator();
|
||||||
AddModuleMessage(Localizer["Error.Framework.Download"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Framework.Download"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
@inject INotificationService NotificationService
|
@inject INotificationService NotificationService
|
||||||
@inject IFileService FileService
|
@inject IFileService FileService
|
||||||
@inject IFolderService FolderService
|
@inject IFolderService FolderService
|
||||||
|
@inject IJSRuntime jsRuntime
|
||||||
|
@inject IServiceProvider ServiceProvider
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@ -84,6 +86,7 @@
|
|||||||
<br />
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="Save">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="Save">@SharedLocalizer["Save"]</button>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
|
<button type="button" class="btn btn-danger" @onclick="Logout">@Localizer["Logout Everywhere"]</button>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -146,22 +149,26 @@
|
|||||||
{
|
{
|
||||||
@if (p.IsRequired)
|
@if (p.IsRequired)
|
||||||
{
|
{
|
||||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete" />
|
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete" />
|
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@if (p.IsRequired)
|
@if (p.IsRequired)
|
||||||
{
|
{
|
||||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
|
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,22 +178,26 @@
|
|||||||
{
|
{
|
||||||
@if (p.IsRequired)
|
@if (p.IsRequired)
|
||||||
{
|
{
|
||||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"></textarea>
|
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"></textarea>
|
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@if (p.IsRequired)
|
@if (p.IsRequired)
|
||||||
{
|
{
|
||||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
|
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
|
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,13 +238,13 @@
|
|||||||
|
|
||||||
@if (context.IsRead)
|
@if (context.IsRead)
|
||||||
{
|
{
|
||||||
<td>@context.FromDisplayName</td>
|
<td>@(string.IsNullOrEmpty(context.FromDisplayName) ? SharedLocalizer["System"] : context.FromDisplayName)</td>
|
||||||
<td>@context.Subject</td>
|
<td>@context.Subject</td>
|
||||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<td><b>@context.FromDisplayName</b></td>
|
<td><b>@(string.IsNullOrEmpty(context.FromDisplayName) ? SharedLocalizer["System"] : context.FromDisplayName)</b></td>
|
||||||
<td><b>@context.Subject</b></td>
|
<td><b>@context.Subject</b></td>
|
||||||
<td><b>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</b></td>
|
<td><b>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</b></td>
|
||||||
}
|
}
|
||||||
@ -360,7 +371,7 @@
|
|||||||
private File photo = null;
|
private File photo = null;
|
||||||
private string _ImageFiles = string.Empty;
|
private string _ImageFiles = string.Empty;
|
||||||
private List<Profile> profiles;
|
private List<Profile> profiles;
|
||||||
private Dictionary<string, string> settings;
|
private Dictionary<string, string> userSettings;
|
||||||
private string category = string.Empty;
|
private string category = string.Empty;
|
||||||
|
|
||||||
private string filter = "to";
|
private string filter = "to";
|
||||||
@ -408,9 +419,9 @@
|
|||||||
photo = null;
|
photo = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
userSettings = PageState.User.Settings;
|
||||||
var sitesettings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var sitesettings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
_ImageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
|
_ImageFiles = SettingService.GetSetting(userSettings, "ImageFiles", Constants.ImageFiles);
|
||||||
_ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;
|
_ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;
|
||||||
|
|
||||||
await LoadNotificationsAsync();
|
await LoadNotificationsAsync();
|
||||||
@ -437,7 +448,7 @@
|
|||||||
|
|
||||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||||
{
|
{
|
||||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
string value = SettingService.GetSetting(userSettings, SettingName, DefaultValue);
|
||||||
if (value.Contains("]"))
|
if (value.Contains("]"))
|
||||||
{
|
{
|
||||||
value = value.Substring(value.IndexOf("]") + 1);
|
value = value.Substring(value.IndexOf("]") + 1);
|
||||||
@ -480,7 +491,7 @@
|
|||||||
user = await UserService.UpdateUserAsync(user);
|
user = await UserService.UpdateUserAsync(user);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
await SettingService.UpdateUserSettingsAsync(userSettings, PageState.User.UserId);
|
||||||
await logger.LogInformation("User Profile Saved");
|
await logger.LogInformation("User Profile Saved");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
||||||
@ -518,6 +529,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task Logout()
|
||||||
|
{
|
||||||
|
await logger.LogInformation("User Logout Everywhere For Username {Username}", PageState.User?.Username);
|
||||||
|
|
||||||
|
var url = NavigateUrl(""); // home page
|
||||||
|
|
||||||
|
if (PageState.Runtime == Shared.Runtime.Hybrid)
|
||||||
|
{
|
||||||
|
if (PageState.User != null)
|
||||||
|
{
|
||||||
|
// hybrid apps utilize an interactive logout
|
||||||
|
await UserService.LogoutUserEverywhereAsync(PageState.User);
|
||||||
|
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||||
|
authstateprovider.NotifyAuthenticationChanged();
|
||||||
|
NavigationManager.NavigateTo(url, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// post to the Logout page to complete the logout process
|
||||||
|
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url, everywhere = true };
|
||||||
|
var interop = new Interop(jsRuntime);
|
||||||
|
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool ValidateProfiles()
|
private bool ValidateProfiles()
|
||||||
{
|
{
|
||||||
foreach (Profile profile in profiles)
|
foreach (Profile profile in profiles)
|
||||||
@ -525,7 +562,7 @@
|
|||||||
var value = GetProfileValue(profile.Name, string.Empty);
|
var value = GetProfileValue(profile.Name, string.Empty);
|
||||||
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||||
{
|
{
|
||||||
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
|
userSettings = SettingService.SetSetting(userSettings, profile.Name, profile.DefaultValue);
|
||||||
}
|
}
|
||||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
{
|
{
|
||||||
@ -557,7 +594,7 @@
|
|||||||
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
||||||
{
|
{
|
||||||
var value = (string)e.Value;
|
var value = (string)e.Value;
|
||||||
settings = SettingService.SetSetting(settings, SettingName, value);
|
userSettings = SettingService.SetSetting(userSettings, SettingName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Delete(Notification Notification)
|
private async Task Delete(Notification Notification)
|
||||||
|
@ -128,7 +128,7 @@
|
|||||||
createdon = notification.CreatedOn.ToString();
|
createdon = notification.CreatedOn.ToString();
|
||||||
body = notification.Body;
|
body = notification.Body;
|
||||||
|
|
||||||
if (title == "From")
|
if (title == "From" && !notification.IsRead)
|
||||||
{
|
{
|
||||||
notification.IsRead = true;
|
notification.IsRead = true;
|
||||||
notification = await NotificationService.UpdateNotificationAsync(notification);
|
notification = await NotificationService.UpdateNotificationAsync(notification);
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
<TabPanel Name="Identity" ResourceKey="Identity">
|
<TabPanel Name="Identity" ResourceKey="Identity">
|
||||||
@if (profiles != null)
|
@if (profiles != null)
|
||||||
{
|
{
|
||||||
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
|
|
||||||
<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="username" HelpText="A unique username for a user. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
|
<Label Class="col-sm-3" For="username" HelpText="A unique username for a user. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
|
||||||
@ -22,24 +21,6 @@
|
|||||||
<input id="username" class="form-control" @bind="@_username" />
|
<input id="username" class="form-control" @bind="@_username" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<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>
|
|
||||||
<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" tabindex="-1">@_togglepassword</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
<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" tabindex="-1">@_togglepassword</button>
|
|
||||||
</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="The email address where the user will receive notifications" ResourceKey="Email"></Label>
|
<Label Class="col-sm-3" For="email" HelpText="The email address where the user will receive notifications" ResourceKey="Email"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -100,11 +81,11 @@
|
|||||||
{
|
{
|
||||||
@if (p.Rows == 1)
|
@if (p.Rows == 1)
|
||||||
{
|
{
|
||||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" @attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
|
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" @attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@ -123,12 +104,7 @@
|
|||||||
|
|
||||||
@code {
|
@code {
|
||||||
private bool _initialized = false;
|
private bool _initialized = false;
|
||||||
private string _passwordrequirements;
|
|
||||||
private string _username = string.Empty;
|
private string _username = string.Empty;
|
||||||
private string _password = string.Empty;
|
|
||||||
private string _passwordtype = "password";
|
|
||||||
private string _togglepassword = 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;
|
||||||
private string _notify = "True";
|
private string _notify = "True";
|
||||||
@ -142,8 +118,6 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
|
|
||||||
_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>();
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
@ -169,16 +143,14 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_username != string.Empty && _password != string.Empty && _confirm != string.Empty && _email != string.Empty)
|
if (_username != string.Empty && _email != string.Empty)
|
||||||
{
|
|
||||||
if (_password == _confirm)
|
|
||||||
{
|
{
|
||||||
if (ValidateProfiles())
|
if (ValidateProfiles())
|
||||||
{
|
{
|
||||||
var user = new User();
|
var user = new User();
|
||||||
user.SiteId = PageState.Site.SiteId;
|
user.SiteId = PageState.Site.SiteId;
|
||||||
user.Username = _username;
|
user.Username = _username;
|
||||||
user.Password = _password;
|
user.Password = ""; // will be auto generated
|
||||||
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;
|
||||||
@ -200,11 +172,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
AddModuleMessage(Localizer["Message.Password.NoMatch"], MessageType.Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Required.ProfileInfo"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.ProfileInfo"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
@ -252,18 +219,4 @@
|
|||||||
var value = (string)e.Value;
|
var value = (string)e.Value;
|
||||||
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"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -110,11 +110,11 @@
|
|||||||
{
|
{
|
||||||
@if (p.Rows == 1)
|
@if (p.Rows == 1)
|
||||||
{
|
{
|
||||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" @attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
|
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" @attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@ -132,7 +132,7 @@
|
|||||||
<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 bool _initialized = false;
|
private bool _initialized = false;
|
||||||
private string _passwordrequirements;
|
private string _passwordrequirements;
|
||||||
private int userid;
|
private int userid;
|
||||||
@ -148,7 +148,7 @@
|
|||||||
private string lastipaddress;
|
private string lastipaddress;
|
||||||
|
|
||||||
private List<Profile> profiles;
|
private List<Profile> profiles;
|
||||||
private Dictionary<string, string> settings;
|
private Dictionary<string, string> userSettings;
|
||||||
private string category = string.Empty;
|
private string category = string.Empty;
|
||||||
|
|
||||||
private string createdby;
|
private string createdby;
|
||||||
@ -181,7 +181,7 @@
|
|||||||
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||||
lastipaddress = user.LastIPAddress;
|
lastipaddress = user.LastIPAddress;
|
||||||
|
|
||||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
userSettings = user.Settings;
|
||||||
createdby = user.CreatedBy;
|
createdby = user.CreatedBy;
|
||||||
createdon = user.CreatedOn;
|
createdon = user.CreatedOn;
|
||||||
modifiedby = user.ModifiedBy;
|
modifiedby = user.ModifiedBy;
|
||||||
@ -202,7 +202,7 @@
|
|||||||
|
|
||||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||||
{
|
{
|
||||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
string value = SettingService.GetSetting(userSettings, SettingName, DefaultValue);
|
||||||
if (value.Contains("]"))
|
if (value.Contains("]"))
|
||||||
{
|
{
|
||||||
value = value.Substring(value.IndexOf("]") + 1);
|
value = value.Substring(value.IndexOf("]") + 1);
|
||||||
@ -232,7 +232,7 @@
|
|||||||
user = await UserService.UpdateUserAsync(user);
|
user = await UserService.UpdateUserAsync(user);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
await SettingService.UpdateUserSettingsAsync(userSettings, user.UserId);
|
||||||
await logger.LogInformation("User Saved {User}", user);
|
await logger.LogInformation("User Saved {User}", user);
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
@ -266,7 +266,7 @@
|
|||||||
var value = GetProfileValue(profile.Name, string.Empty);
|
var value = GetProfileValue(profile.Name, string.Empty);
|
||||||
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||||
{
|
{
|
||||||
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
|
userSettings = SettingService.SetSetting(userSettings, profile.Name, profile.DefaultValue);
|
||||||
}
|
}
|
||||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
{
|
{
|
||||||
@ -293,7 +293,7 @@
|
|||||||
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
||||||
{
|
{
|
||||||
var value = (string)e.Value;
|
var value = (string)e.Value;
|
||||||
settings = SettingService.SetSetting(settings, SettingName, value);
|
userSettings = SettingService.SetSetting(userSettings, SettingName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TogglePassword()
|
private void TogglePassword()
|
||||||
|
@ -182,13 +182,31 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
<Section Name="ExternalLogin" Heading="External Login Settings" ResourceKey="ExternalLoginSettings">
|
<Section Name="ExternalLogin" Heading="External Login Settings" ResourceKey="ExternalLoginSettings">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="provider" HelpText="Select the external login provider" ResourceKey="Provider">Provider:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<select id="provider" class="form-select" value="@_provider" @onchange="(e => ProviderChanged(e))">
|
||||||
|
@foreach (var provider in Shared.ExternalLoginProviders.Providers)
|
||||||
|
{
|
||||||
|
<option value="@provider.Name">@Localizer[provider.Name]</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
@if (!string.IsNullOrEmpty(_providerurl))
|
||||||
|
{
|
||||||
|
<a href="@_providerurl" class="btn btn-secondary" target="_new">@Localizer["Info"]</a>
|
||||||
|
}
|
||||||
|
</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="providertype" HelpText="Select the external login provider type" ResourceKey="ProviderType">Provider Type:</Label>
|
<Label Class="col-sm-3" For="providertype" HelpText="Select the external login provider type" ResourceKey="ProviderType">Provider Type:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="providertype" class="form-select" value="@_providertype" @onchange="(e => ProviderTypeChanged(e))">
|
<select id="providertype" class="form-select" value="@_providertype" @onchange="(e => ProviderTypeChanged(e))">
|
||||||
<option value="" selected>@Localizer["Not Specified"]</option>
|
<option value="" selected><@Localizer["Not Specified"]></option>
|
||||||
<option value="@AuthenticationProviderTypes.OpenIDConnect">@Localizer["OpenID Connect"]</option>
|
<option value="@AuthenticationProviderTypes.OpenIDConnect">@Localizer["OIDC"]</option>
|
||||||
<option value="@AuthenticationProviderTypes.OAuth2">@Localizer["OAuth 2.0"]</option>
|
<option value="@AuthenticationProviderTypes.OAuth2">@Localizer["OAuth2"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -333,11 +351,28 @@ 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="roleclaimtype" HelpText="The name of the role claim provided by the provider" ResourceKey="RoleClaimType">Role Claim:</Label>
|
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the roles claim provided by the provider" ResourceKey="RoleClaimType">Roles Claim:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="roleclaimmappings" HelpText="Optionally provide a comma delimited list of role names provided by the identity provider, as well as mappings to your site roles." ResourceKey="RoleClaimMappings">Role Claim Mappings:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="roleclaimmappings" class="form-control" @bind="@_roleclaimmappings" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="synchronizeroles" HelpText="This option will add or remove role assignments so that the site roles exactly match the roles provided by the identity provider" ResourceKey="SynchronizeRoles">Synchronize Roles?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<select id="synchronizeroles" class="form-select" @bind="@_synchronizeroles" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -435,6 +470,8 @@ else
|
|||||||
private string _maximumfailures;
|
private string _maximumfailures;
|
||||||
private string _lockoutduration;
|
private string _lockoutduration;
|
||||||
|
|
||||||
|
private string _provider;
|
||||||
|
private string _providerurl;
|
||||||
private string _providertype;
|
private string _providertype;
|
||||||
private string _providername;
|
private string _providername;
|
||||||
private string _authority;
|
private string _authority;
|
||||||
@ -457,6 +494,8 @@ else
|
|||||||
private string _nameclaimtype;
|
private string _nameclaimtype;
|
||||||
private string _emailclaimtype;
|
private string _emailclaimtype;
|
||||||
private string _roleclaimtype;
|
private string _roleclaimtype;
|
||||||
|
private string _roleclaimmappings;
|
||||||
|
private string _synchronizeroles;
|
||||||
private string _profileclaimtypes;
|
private string _profileclaimtypes;
|
||||||
private string _domainfilter;
|
private string _domainfilter;
|
||||||
private string _createusers;
|
private string _createusers;
|
||||||
@ -500,6 +539,20 @@ else
|
|||||||
_maximumfailures = SettingService.GetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", "5");
|
_maximumfailures = SettingService.GetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", "5");
|
||||||
_lockoutduration = TimeSpan.Parse(SettingService.GetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", "00:05:00")).TotalMinutes.ToString();
|
_lockoutduration = TimeSpan.Parse(SettingService.GetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", "00:05:00")).TotalMinutes.ToString();
|
||||||
|
|
||||||
|
LoadExternalLoginSettings(settings);
|
||||||
|
|
||||||
|
_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 void LoadExternalLoginSettings(Dictionary<string, string> settings)
|
||||||
|
{
|
||||||
|
_provider = SettingService.GetSetting(settings, "ExternalLogin:Provider", "<Custom>");
|
||||||
|
_providerurl = SettingService.GetSetting(settings, "ExternalLogin:ProviderUrl", "");
|
||||||
_providertype = SettingService.GetSetting(settings, "ExternalLogin:ProviderType", "");
|
_providertype = SettingService.GetSetting(settings, "ExternalLogin:ProviderType", "");
|
||||||
_providername = SettingService.GetSetting(settings, "ExternalLogin:ProviderName", "");
|
_providername = SettingService.GetSetting(settings, "ExternalLogin:ProviderName", "");
|
||||||
_authority = SettingService.GetSetting(settings, "ExternalLogin:Authority", "");
|
_authority = SettingService.GetSetting(settings, "ExternalLogin:Authority", "");
|
||||||
@ -521,17 +574,12 @@ else
|
|||||||
_nameclaimtype = SettingService.GetSetting(settings, "ExternalLogin:NameClaimType", "name");
|
_nameclaimtype = SettingService.GetSetting(settings, "ExternalLogin:NameClaimType", "name");
|
||||||
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email");
|
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email");
|
||||||
_roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", "");
|
_roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", "");
|
||||||
|
_roleclaimmappings = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimMappings", "");
|
||||||
|
_synchronizeroles = SettingService.GetSetting(settings, "ExternalLogin:SynchronizeRoles", "false");
|
||||||
_profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", "");
|
_profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", "");
|
||||||
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
|
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
|
||||||
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
|
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
|
||||||
_verifyusers = SettingService.GetSetting(settings, "ExternalLogin:VerifyUsers", "true");
|
_verifyusers = SettingService.GetSetting(settings, "ExternalLogin:VerifyUsers", "true");
|
||||||
|
|
||||||
_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 LoadUsersAsync(bool load)
|
private async Task LoadUsersAsync(bool load)
|
||||||
@ -596,6 +644,7 @@ else
|
|||||||
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", _maximumfailures, 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, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", TimeSpan.FromMinutes(Convert.ToInt64(_lockoutduration)).ToString(), true);
|
||||||
|
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Provider", _provider, false);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderType", _providertype, false);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderType", _providertype, false);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderName", _providername, false);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderName", _providername, false);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Authority", _authority, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Authority", _authority, true);
|
||||||
@ -614,6 +663,8 @@ else
|
|||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:NameClaimType", _nameclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:NameClaimType", _nameclaimtype, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimMappings", _roleclaimmappings, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:SynchronizeRoles", _synchronizeroles, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
||||||
@ -640,6 +691,21 @@ else
|
|||||||
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
|
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await ScrollToPageTop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProviderChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
_provider = (string)e.Value;
|
||||||
|
var provider = Shared.ExternalLoginProviders.Providers.FirstOrDefault(item => item.Name == _provider);
|
||||||
|
if (provider != null)
|
||||||
|
{
|
||||||
|
LoadExternalLoginSettings(provider.Settings);
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProviderTypeChanged(ChangeEventArgs e)
|
private void ProviderTypeChanged(ChangeEventArgs e)
|
||||||
|
@ -173,6 +173,12 @@ else
|
|||||||
_editmode = bool.Parse(EditMode);
|
_editmode = bool.Parse(EditMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Text = Localize(nameof(Text), Text);
|
||||||
|
Header = Localize(nameof(Header), Header);
|
||||||
|
Message = Localize(nameof(Message), Message);
|
||||||
|
|
||||||
|
_openText = Text;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(IconName))
|
if (!string.IsNullOrEmpty(IconName))
|
||||||
{
|
{
|
||||||
if (IconOnly)
|
if (IconOnly)
|
||||||
@ -191,11 +197,6 @@ else
|
|||||||
_iconSpan = $"<span class=\"{IconName}\"></span> ";
|
_iconSpan = $"<span class=\"{IconName}\"></span> ";
|
||||||
}
|
}
|
||||||
|
|
||||||
Text = Localize(nameof(Text), Text);
|
|
||||||
Header = Localize(nameof(Header), Header);
|
|
||||||
Message = Localize(nameof(Message), Message);
|
|
||||||
|
|
||||||
_openText = Text;
|
|
||||||
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
|
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
|
||||||
_authorized = IsAuthorized();
|
_authorized = IsAuthorized();
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@
|
|||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,9 +226,9 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
FileId = -1; // file does not exist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,12 +359,6 @@
|
|||||||
}
|
}
|
||||||
if (restricted == "")
|
if (restricted == "")
|
||||||
{
|
{
|
||||||
if (!ShowProgress)
|
|
||||||
{
|
|
||||||
_uploading = true;
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// upload the files
|
// upload the files
|
||||||
@ -374,7 +368,21 @@
|
|||||||
if (PageState.Runtime == Shared.Runtime.Hybrid)
|
if (PageState.Runtime == Shared.Runtime.Hybrid)
|
||||||
{
|
{
|
||||||
jwt = await UserService.GetTokenAsync();
|
jwt = await UserService.GetTokenAsync();
|
||||||
|
if (string.IsNullOrEmpty(jwt))
|
||||||
|
{
|
||||||
|
await logger.LogInformation("File Upload Failed From .NET MAUI Due To Missing Security Token. Token Options Must Be Set In User Settings.");
|
||||||
|
_message = "Security Token Not Specified";
|
||||||
|
_messagetype = MessageType.Error;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ShowProgress)
|
||||||
|
{
|
||||||
|
_uploading = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
await interop.UploadFiles(posturl, folder, _guid, SiteState.AntiForgeryToken, jwt);
|
await interop.UploadFiles(posturl, folder, _guid, SiteState.AntiForgeryToken, jwt);
|
||||||
|
|
||||||
// uploading is asynchronous so we need to poll to determine if uploads are completed
|
// uploading is asynchronous so we need to poll to determine if uploads are completed
|
||||||
@ -387,7 +395,7 @@
|
|||||||
|
|
||||||
var size = Int64.Parse(uploads[upload].Split(':')[1]); // bytes
|
var size = Int64.Parse(uploads[upload].Split(':')[1]); // bytes
|
||||||
var megabits = (size / 1048576.0) * 8; // binary conversion
|
var megabits = (size / 1048576.0) * 8; // binary conversion
|
||||||
var uploadspeed = 2; // 2 Mbps (3G ranges from 300Kbps to 3Mbps)
|
var uploadspeed = (PageState.Alias.Name.Contains("localhost")) ? 100 : 3; // 3 Mbps is FCC minimum for broadband upload
|
||||||
var uploadtime = (megabits / uploadspeed); // seconds
|
var uploadtime = (megabits / uploadspeed); // seconds
|
||||||
var maxattempts = 5; // polling (minimum timeout duration will be 5 seconds)
|
var maxattempts = 5; // polling (minimum timeout duration will be 5 seconds)
|
||||||
var sleep = (int)Math.Ceiling(uploadtime / maxattempts) * 1000; // milliseconds
|
var sleep = (int)Math.Ceiling(uploadtime / maxattempts) * 1000; // milliseconds
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
{
|
{
|
||||||
<NavLink class="ms-2" href="@NavigateUrl("admin/log")">View Details</NavLink>
|
<NavLink class="ms-2" href="@NavigateUrl("admin/log")">View Details</NavLink>
|
||||||
}
|
}
|
||||||
|
@if (ModuleState != null)
|
||||||
|
{
|
||||||
@if (ModuleState.RenderMode == RenderModes.Static)
|
@if (ModuleState.RenderMode == RenderModes.Static)
|
||||||
{
|
{
|
||||||
<a href="@NavigationManager.Uri" class="btn-close" data-dismiss="alert" aria-label="close"></a>
|
<a href="@NavigationManager.Uri" class="btn-close" data-dismiss="alert" aria-label="close"></a>
|
||||||
@ -18,6 +20,7 @@
|
|||||||
{
|
{
|
||||||
<button type="button" class="btn-close" data-dismiss="alert" aria-label="close" @onclick="CloseMessage"></button>
|
<button type="button" class="btn-close" data-dismiss="alert" aria-label="close" @onclick="CloseMessage"></button>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@ -84,7 +84,7 @@
|
|||||||
|
|
||||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@ -200,7 +200,7 @@
|
|||||||
{
|
{
|
||||||
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@ -248,7 +248,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@ -368,6 +368,12 @@
|
|||||||
[SupplyParameterFromForm(FormName = "PagerForm")]
|
[SupplyParameterFromForm(FormName = "PagerForm")]
|
||||||
public string _Search { get => ""; set => _search = value; }
|
public string _Search { get => ""; set => _search = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Accepted values are Start or Center or End. The default value is Center
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string PaginationAlignment { get; set; } = "center"; // Alignment of the Page Numbering start, center, end
|
||||||
|
|
||||||
private IEnumerable<TableItem> ItemList { get; set; }
|
private IEnumerable<TableItem> ItemList { get; set; }
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
@ -452,9 +458,9 @@
|
|||||||
_displayPages = int.Parse(DisplayPages);
|
_displayPages = int.Parse(DisplayPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("page"))
|
if (PageState.QueryString.ContainsKey("page") && int.TryParse(PageState.QueryString["page"], out int page))
|
||||||
{
|
{
|
||||||
_page = int.Parse(PageState.QueryString["page"]);
|
_page = page;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -111,7 +111,7 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public List<Permission> PermissionList { get; set; }
|
public List<Permission> PermissionList { get; set; }
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(Permissions))
|
if (!string.IsNullOrEmpty(Permissions))
|
||||||
{
|
{
|
||||||
|
@ -277,7 +277,7 @@
|
|||||||
{
|
{
|
||||||
// include CSS theme
|
// include CSS theme
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
await interop.IncludeLink("", "stylesheet", $"css/quill/quill.{_theme}.css", "text/css", "", "", "");
|
await interop.IncludeLink("", "stylesheet", $"{PageState?.Alias.BaseUrl}/css/quill/quill.{_theme}.css", "text/css", "", "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
await base.OnAfterRenderAsync(firstRender);
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
|
@ -36,14 +36,7 @@ else
|
|||||||
|
|
||||||
Parent.AddTabPanel((TabPanel)this);
|
Parent.AddTabPanel((TabPanel)this);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Heading))
|
Heading = string.IsNullOrEmpty(Heading) ? Localize(nameof(Name), Name) : Localize(nameof(Heading), Heading);
|
||||||
{
|
|
||||||
Heading = Localize(nameof(Name), Name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Heading = Localize(nameof(Heading), Heading);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DisplayHeading()
|
public string DisplayHeading()
|
||||||
|
@ -98,17 +98,17 @@ namespace Oqtane.Modules
|
|||||||
var inline = 0;
|
var inline = 0;
|
||||||
foreach (Resource resource in resources)
|
foreach (Resource resource in resources)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive)
|
if ((string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive) && !resource.Reload)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(resource.Url))
|
if (!string.IsNullOrEmpty(resource.Url))
|
||||||
{
|
{
|
||||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||||
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
|
scripts.Add(new { href = url, type = resource.Type ?? "", bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", location = resource.Location.ToString().ToLower(), dataAttributes = resource.DataAttributes });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inline += 1;
|
inline += 1;
|
||||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Type ?? "", resource.Content, resource.Location.ToString().ToLower());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,6 +134,7 @@ namespace Oqtane.Modules
|
|||||||
|
|
||||||
// url methods
|
// url methods
|
||||||
|
|
||||||
|
// navigate url
|
||||||
public string NavigateUrl()
|
public string NavigateUrl()
|
||||||
{
|
{
|
||||||
return NavigateUrl(PageState.Page.Path);
|
return NavigateUrl(PageState.Page.Path);
|
||||||
@ -149,24 +150,65 @@ namespace Oqtane.Modules
|
|||||||
return NavigateUrl(PageState.Page.Path, refresh);
|
return NavigateUrl(PageState.Page.Path, refresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string NavigateUrl(string path, string parameters)
|
public string NavigateUrl(string path, string querystring)
|
||||||
{
|
{
|
||||||
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
|
return Utilities.NavigateUrl(PageState.Alias.Path, path, querystring);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(string path, Dictionary<string, string> querystring)
|
||||||
|
{
|
||||||
|
return NavigateUrl(path, Utilities.CreateQueryString(querystring));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string NavigateUrl(string path, bool refresh)
|
public string NavigateUrl(string path, bool refresh)
|
||||||
{
|
{
|
||||||
return Utilities.NavigateUrl(PageState.Alias.Path, path, refresh ? "refresh" : "");
|
return NavigateUrl(path, refresh ? "refresh" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(int moduleId, string action)
|
||||||
|
{
|
||||||
|
return EditUrl(PageState.Page.Path, moduleId, action, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(int moduleId, string action, string querystring)
|
||||||
|
{
|
||||||
|
return EditUrl(PageState.Page.Path, moduleId, action, querystring);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(int moduleId, string action, Dictionary<string, string> querystring)
|
||||||
|
{
|
||||||
|
return EditUrl(PageState.Page.Path, moduleId, action, querystring);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(string path, int moduleId, string action)
|
||||||
|
{
|
||||||
|
return EditUrl(path, moduleId, action, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(string path, int moduleId, string action, string querystring)
|
||||||
|
{
|
||||||
|
return EditUrl(path, moduleId, action, querystring);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(string path, int moduleId, string action, Dictionary<string, string> querystring)
|
||||||
|
{
|
||||||
|
return EditUrl(path, moduleId, action, querystring);
|
||||||
|
}
|
||||||
|
|
||||||
|
// edit url
|
||||||
public string EditUrl(string action)
|
public string EditUrl(string action)
|
||||||
{
|
{
|
||||||
return EditUrl(ModuleState.ModuleId, action);
|
return EditUrl(ModuleState.ModuleId, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string EditUrl(string action, string parameters)
|
public string EditUrl(string action, string querystring)
|
||||||
{
|
{
|
||||||
return EditUrl(ModuleState.ModuleId, action, parameters);
|
return EditUrl(ModuleState.ModuleId, action, querystring);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string EditUrl(string action, Dictionary<string, string> querystring)
|
||||||
|
{
|
||||||
|
return EditUrl(ModuleState.ModuleId, action, querystring);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string EditUrl(int moduleId, string action)
|
public string EditUrl(int moduleId, string action)
|
||||||
@ -174,16 +216,27 @@ namespace Oqtane.Modules
|
|||||||
return EditUrl(moduleId, action, "");
|
return EditUrl(moduleId, action, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string EditUrl(int moduleId, string action, string parameters)
|
public string EditUrl(int moduleId, string action, string querystring)
|
||||||
{
|
{
|
||||||
return EditUrl(PageState.Page.Path, moduleId, action, parameters);
|
return EditUrl(PageState.Page.Path, moduleId, action, querystring);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string EditUrl(string path, int moduleid, string action, string parameters)
|
public string EditUrl(int moduleId, string action, Dictionary<string, string> querystring)
|
||||||
{
|
{
|
||||||
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
|
return EditUrl(PageState.Page.Path, moduleId, action, querystring);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string EditUrl(string path, int moduleid, string action, string querystring)
|
||||||
|
{
|
||||||
|
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, querystring);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string EditUrl(string path, int moduleid, string action, Dictionary<string, string> querystring)
|
||||||
|
{
|
||||||
|
return EditUrl(path, moduleid, action, Utilities.CreateQueryString(querystring));
|
||||||
|
}
|
||||||
|
|
||||||
|
// file url
|
||||||
public string FileUrl(string folderpath, string filename)
|
public string FileUrl(string folderpath, string filename)
|
||||||
{
|
{
|
||||||
return FileUrl(folderpath, filename, false);
|
return FileUrl(folderpath, filename, false);
|
||||||
@ -203,6 +256,8 @@ namespace Oqtane.Modules
|
|||||||
return Utilities.FileUrl(PageState.Alias, fileid, download);
|
return Utilities.FileUrl(PageState.Alias, fileid, download);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// image url
|
||||||
|
|
||||||
public string ImageUrl(int fileid, int width, int height)
|
public string ImageUrl(int fileid, int width, int height)
|
||||||
{
|
{
|
||||||
return ImageUrl(fileid, width, height, "");
|
return ImageUrl(fileid, width, height, "");
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>5.2.1</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<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/v5.2.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.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,10 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.8" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
@ -13,13 +12,13 @@ using System.Text.Json;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
using Microsoft.AspNetCore.Localization;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.JSInterop;
|
using Microsoft.JSInterop;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
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
|
||||||
@ -258,7 +257,7 @@ namespace Oqtane.Client
|
|||||||
var jsRuntime = serviceProvider.GetRequiredService<IJSRuntime>();
|
var jsRuntime = serviceProvider.GetRequiredService<IJSRuntime>();
|
||||||
var interop = new Interop(jsRuntime);
|
var interop = new Interop(jsRuntime);
|
||||||
var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName);
|
var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName);
|
||||||
var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICultures?[0].Value;
|
var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICulture.Name;
|
||||||
var localizationService = serviceProvider.GetRequiredService<ILocalizationService>();
|
var localizationService = serviceProvider.GetRequiredService<ILocalizationService>();
|
||||||
var cultures = await localizationService.GetCulturesAsync(false);
|
var cultures = await localizationService.GetCulturesAsync(false);
|
||||||
|
|
||||||
|
@ -183,4 +183,7 @@
|
|||||||
<data name="Template" xml:space="preserve">
|
<data name="Template" xml:space="preserve">
|
||||||
<value>Select a site template</value>
|
<value>Select a site template</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.Username.Invalid" xml:space="preserve">
|
||||||
|
<value>The Username Provided Does Not Meet The System Requirement, It Can Only Contains Letters Or Digits.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -139,7 +139,7 @@
|
|||||||
<value>Ignore Entities: </value>
|
<value>Ignore Entities: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IgnoreEntities.HelpText" xml:space="preserve">
|
<data name="IgnoreEntities.HelpText" xml:space="preserve">
|
||||||
<value>Comma delimited list of entities which should be ignored</value>
|
<value>Comma delimited list of entities which should be ignored. By default File entities are ignored.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MinimumWordLength.Text" xml:space="preserve">
|
<data name="MinimumWordLength.Text" xml:space="preserve">
|
||||||
<value>Word Length: </value>
|
<value>Word Length: </value>
|
||||||
@ -154,7 +154,7 @@
|
|||||||
<value>Comma delimited list of words which should be ignored</value>
|
<value>Comma delimited list of words which should be ignored</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Success.Save" xml:space="preserve">
|
<data name="Success.Save" xml:space="preserve">
|
||||||
<value>Search Settings Saved Successfully</value>
|
<value>Search Settings Saved Successfully. You Will Need Reindex For Your Changes To Be Reflected In The Search Results.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.Save" xml:space="preserve">
|
<data name="Error.Save" xml:space="preserve">
|
||||||
<value>Error Saving Search Settings</value>
|
<value>Error Saving Search Settings</value>
|
||||||
|
@ -163,7 +163,7 @@
|
|||||||
<value>Enter the site name</value>
|
<value>Enter the site name</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Tenant.HelpText" xml:space="preserve">
|
<data name="Tenant.HelpText" xml:space="preserve">
|
||||||
<value>The name of the database used for the site</value>
|
<value>The name of the database used for the site. Note that this is not the physical database name but rather the tenant name which is used within the framework to identify a database.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Aliases.HelpText" xml:space="preserve">
|
<data name="Aliases.HelpText" xml:space="preserve">
|
||||||
<value>The urls for the site. This can include domain names (ie. domain.com), subdomains (ie. sub.domain.com) or virtual folders (ie. domain.com/folder).</value>
|
<value>The urls for the site. This can include domain names (ie. domain.com), subdomains (ie. sub.domain.com) or virtual folders (ie. domain.com/folder).</value>
|
||||||
@ -307,7 +307,7 @@
|
|||||||
<value>Type:</value>
|
<value>Type:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ConnectionString.HelpText" xml:space="preserve">
|
<data name="ConnectionString.HelpText" xml:space="preserve">
|
||||||
<value>The connection information for the database</value>
|
<value>The name of the connection string in appsettings.json which will be used to connect to the database</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Database.HelpText" xml:space="preserve">
|
<data name="Database.HelpText" xml:space="preserve">
|
||||||
<value>The type of database</value>
|
<value>The type of database</value>
|
||||||
@ -435,4 +435,7 @@
|
|||||||
<data name="Functionality" xml:space="preserve">
|
<data name="Functionality" xml:space="preserve">
|
||||||
<value>Functionality</value>
|
<value>Functionality</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="System" xml:space="preserve">
|
||||||
|
<value>System</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -187,7 +187,7 @@
|
|||||||
<value>Select the database for the site</value>
|
<value>Select the database for the site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TenantName.HelpText" xml:space="preserve">
|
<data name="TenantName.HelpText" xml:space="preserve">
|
||||||
<value>Enter the name for the database</value>
|
<value>Enter the name for the database. Note that this will be the tenant name which is used within the framework to identify the database.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DatabaseType.HelpText" xml:space="preserve">
|
<data name="DatabaseType.HelpText" xml:space="preserve">
|
||||||
<value>Select the database type</value>
|
<value>Select the database type</value>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -150,4 +150,7 @@
|
|||||||
<data name="Localhost.Text" xml:space="preserve">
|
<data name="Localhost.Text" xml:space="preserve">
|
||||||
<value>You Cannot Perform A System Update In A Development Environment</value>
|
<value>You Cannot Perform A System Update In A Development Environment</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Disclaimer.Text" xml:space="preserve">
|
||||||
|
<value>Please Note That The System Update Capability Is A Simplified Upgrade Process Intended For Small To Medium Sized Installations. For Larger Enterprise Installations You Will Want To Use A Manual Upgrade Process. Also Note That The System Update Capability Is Not Recommended When Using Microsoft Azure Due To Environmental Limitations.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -243,4 +243,7 @@
|
|||||||
<data name="NoNotificationsSent.Text" xml:space="preserve">
|
<data name="NoNotificationsSent.Text" xml:space="preserve">
|
||||||
<value>No notifications have been sent</value>
|
<value>No notifications have been sent</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Logout Everywhere" xml:space="preserve">
|
||||||
|
<value>Logout Everywhere</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -117,12 +117,6 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="Error.User.AddCheckPass" xml:space="preserve">
|
|
||||||
<value>Error Adding User. Please Ensure Password Meets Complexity Requirements And Username And Email Is Not Already In Use.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Message.Password.NoMatch" xml:space="preserve">
|
|
||||||
<value>Passwords Entered Do Not Match</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error.User.Add" xml:space="preserve">
|
<data name="Error.User.Add" xml:space="preserve">
|
||||||
<value>Error Adding User</value>
|
<value>Error Adding User</value>
|
||||||
</data>
|
</data>
|
||||||
@ -133,17 +127,11 @@
|
|||||||
<value>Identity</value>
|
<value>Identity</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Required.ProfileInfo" xml:space="preserve">
|
<data name="Message.Required.ProfileInfo" xml:space="preserve">
|
||||||
<value>You Must Provide A Username, Password, Email Address And All Required Profile Information</value>
|
<value>You Must Provide A Username, Email Address And All Required Profile Information</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Username.Exists" xml:space="preserve">
|
<data name="Message.Username.Exists" xml:space="preserve">
|
||||||
<value>Username Already Exists</value>
|
<value>Username Already Exists</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Confirm.HelpText" xml:space="preserve">
|
|
||||||
<value>Please enter the password again to confirm it matches with the value above</value>
|
|
||||||
</data>
|
|
||||||
<data name="Confirm.Text" xml:space="preserve">
|
|
||||||
<value>Confirm Password:</value>
|
|
||||||
</data>
|
|
||||||
<data name="DisplayName.HelpText" xml:space="preserve">
|
<data name="DisplayName.HelpText" xml:space="preserve">
|
||||||
<value>The full name of the user</value>
|
<value>The full name of the user</value>
|
||||||
</data>
|
</data>
|
||||||
@ -156,21 +144,12 @@
|
|||||||
<data name="Email.Text" xml:space="preserve">
|
<data name="Email.Text" xml:space="preserve">
|
||||||
<value>Email:</value>
|
<value>Email:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Password.HelpText" xml:space="preserve">
|
|
||||||
<value>The user's password. Please choose a password which is sufficiently secure.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Password.Text" xml:space="preserve">
|
|
||||||
<value>Password:</value>
|
|
||||||
</data>
|
|
||||||
<data name="Username.HelpText" xml:space="preserve">
|
<data name="Username.HelpText" xml:space="preserve">
|
||||||
<value>A unique username for a user. Note that this field can not be modified once it is saved.</value>
|
<value>A unique username for a user. Note that this field can not be modified once it is saved.</value>
|
||||||
</data>
|
</data>
|
||||||
<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>
|
|
||||||
<data name="Notify.HelpText" xml:space="preserve">
|
<data name="Notify.HelpText" xml:space="preserve">
|
||||||
<value>Indicate if new users should receive an email notification</value>
|
<value>Indicate if new users should receive an email notification</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -385,10 +385,22 @@
|
|||||||
<value>Parameters:</value>
|
<value>Parameters:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RoleClaimType.HelpText" xml:space="preserve">
|
<data name="RoleClaimType.HelpText" xml:space="preserve">
|
||||||
<value>Optionally provide the type 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>
|
<value>Optionally provide the type name of the roles claim provided by the identity provider (the standard default is 'roles'). If role names from the identity provider do not exactly match your site role names, please use the Role Claim Mappings.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RoleClaimType.Text" xml:space="preserve">
|
<data name="RoleClaimType.Text" xml:space="preserve">
|
||||||
<value>Role Claim:</value>
|
<value>Roles Claim:</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoleClaimMappings.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally provide a comma delimited list of role names provided by the identity provider, as well as mappings to your site roles. For example if the identity provider includes an 'Admin' role name and you want it to map to the 'Administrators' site role you should specify 'Admin:Administrators'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoleClaimMappings.Text" xml:space="preserve">
|
||||||
|
<value>Role Claim Mappings:</value>
|
||||||
|
</data>
|
||||||
|
<data name="SynchronizeRoles.HelpText" xml:space="preserve">
|
||||||
|
<value>This option will add or remove role assignments so that the site roles exactly match the roles provided by the identity provider for a user</value>
|
||||||
|
</data>
|
||||||
|
<data name="SynchronizeRoles.Text" xml:space="preserve">
|
||||||
|
<value>Synchronize Roles?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ProfileClaimTypes.HelpText" xml:space="preserve">
|
<data name="ProfileClaimTypes.HelpText" xml:space="preserve">
|
||||||
<value>Optionally provide a comma delimited list of user profile claim type names provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'.</value>
|
<value>Optionally provide a comma delimited list of user profile claim type names provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'.</value>
|
||||||
@ -468,4 +480,19 @@
|
|||||||
<data name="NameClaimType.Text" xml:space="preserve">
|
<data name="NameClaimType.Text" xml:space="preserve">
|
||||||
<value>Name Claim:</value>
|
<value>Name Claim:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Provider.HelpText" xml:space="preserve">
|
||||||
|
<value>Select the external login provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="Provider.Text" xml:space="preserve">
|
||||||
|
<value>Provider:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Info" xml:space="preserve">
|
||||||
|
<value>Info</value>
|
||||||
|
</data>
|
||||||
|
<data name="OAuth2" xml:space="preserve">
|
||||||
|
<value>OAuth 2.0</value>
|
||||||
|
</data>
|
||||||
|
<data name="OIDC" xml:space="preserve">
|
||||||
|
<value>OpenID Connect (OIDC)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -0,0 +1,17 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Oqtane.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Service to set localization cookie
|
||||||
|
/// </summary>
|
||||||
|
public interface ILocalizationCookieService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Set the localization cookie
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="culture"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task SetLocalizationCookieAsync(string culture);
|
||||||
|
}
|
||||||
|
}
|
@ -75,6 +75,13 @@ namespace Oqtane.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task LogoutUserAsync(User user);
|
Task LogoutUserAsync(User user);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logout a <see cref="User"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task LogoutUserEverywhereAsync(User user);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update e-mail verification status of a user.
|
/// Update e-mail verification status of a user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -106,6 +113,15 @@ namespace Oqtane.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<User> VerifyTwoFactorAsync(User user, string token);
|
Task<User> VerifyTwoFactorAsync(User user, string token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validate identity user info.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="username"></param>
|
||||||
|
/// <param name="email"></param>
|
||||||
|
/// <param name="password"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<UserValidateResult> ValidateUserAsync(string username, string email, string password);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validate a users password against the password policy
|
/// Validate a users password against the password policy
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
18
Oqtane.Client/Services/LocalizationCookieService.cs
Normal file
18
Oqtane.Client/Services/LocalizationCookieService.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Services
|
||||||
|
{
|
||||||
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
|
public class LocalizationCookieService : ServiceBase, ILocalizationCookieService
|
||||||
|
{
|
||||||
|
public LocalizationCookieService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
|
public Task SetLocalizationCookieAsync(string culture)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask; // only used in server side rendering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,6 @@ using System.Net.Http;
|
|||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Net.Http.Headers;
|
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
|
||||||
namespace Oqtane.Services
|
namespace Oqtane.Services
|
||||||
@ -28,9 +27,9 @@ namespace Oqtane.Services
|
|||||||
private HttpClient GetHttpClient(string AuthorizationToken)
|
private HttpClient GetHttpClient(string AuthorizationToken)
|
||||||
{
|
{
|
||||||
var httpClient = _httpClientFactory.CreateClient("Remote");
|
var httpClient = _httpClientFactory.CreateClient("Remote");
|
||||||
if (!httpClient.DefaultRequestHeaders.Contains(HeaderNames.Authorization) && !string.IsNullOrEmpty(AuthorizationToken))
|
if (!httpClient.DefaultRequestHeaders.Contains("Authorization") && !string.IsNullOrEmpty(AuthorizationToken))
|
||||||
{
|
{
|
||||||
httpClient.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + AuthorizationToken);
|
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + AuthorizationToken);
|
||||||
}
|
}
|
||||||
return httpClient;
|
return httpClient;
|
||||||
}
|
}
|
||||||
|
@ -61,10 +61,14 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
public async Task LogoutUserAsync(User user)
|
public async Task LogoutUserAsync(User user)
|
||||||
{
|
{
|
||||||
// best practices recommend post is preferrable to get for logout
|
|
||||||
await PostJsonAsync($"{Apiurl}/logout", user);
|
await PostJsonAsync($"{Apiurl}/logout", user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task LogoutUserEverywhereAsync(User user)
|
||||||
|
{
|
||||||
|
await PostJsonAsync($"{Apiurl}/logouteverywhere", user);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<User> VerifyEmailAsync(User user, string token)
|
public async Task<User> VerifyEmailAsync(User user, string token)
|
||||||
{
|
{
|
||||||
return await PostJsonAsync<User>($"{Apiurl}/verify?token={token}", user);
|
return await PostJsonAsync<User>($"{Apiurl}/verify?token={token}", user);
|
||||||
@ -85,6 +89,11 @@ namespace Oqtane.Services
|
|||||||
return await PostJsonAsync<User>($"{Apiurl}/twofactor?token={token}", user);
|
return await PostJsonAsync<User>($"{Apiurl}/twofactor?token={token}", user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<UserValidateResult> ValidateUserAsync(string username, string email, string password)
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<UserValidateResult>($"{Apiurl}/validateuser?username={WebUtility.UrlEncode(username)}&email={WebUtility.UrlEncode(email)}&password={WebUtility.UrlEncode(password)}");
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> ValidatePasswordAsync(string password)
|
public async Task<bool> ValidatePasswordAsync(string password)
|
||||||
{
|
{
|
||||||
return await GetJsonAsync<bool>($"{Apiurl}/validate/{WebUtility.UrlEncode(password)}");
|
return await GetJsonAsync<bool>($"{Apiurl}/validate/{WebUtility.UrlEncode(password)}");
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<div class="row flex-xl-nowrap gx-0">
|
<div class="row flex-xl-nowrap gx-0">
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<nav class="navbar">
|
<nav class="navbar">
|
||||||
<Logo />
|
<Logo UseSiteNameAsFallback="true" />
|
||||||
<Menu Orientation="Vertical" />
|
<Menu Orientation="Vertical" />
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
@ -37,13 +37,8 @@
|
|||||||
public override List<Resource> Resources => new List<Resource>()
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
{
|
{
|
||||||
// obtained from https://cdnjs.com/libraries
|
// obtained from https://cdnjs.com/libraries
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css",
|
new Stylesheet("https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css", "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==", "anonymous"),
|
||||||
Integrity = "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==",
|
new Stylesheet(ThemePath() + "Theme.css"),
|
||||||
CrossOrigin = "anonymous" },
|
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
|
|
||||||
new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js",
|
|
||||||
Integrity = "sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg==",
|
|
||||||
CrossOrigin = "anonymous", Location = ResourceLocation.Body },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,6 @@ using System.Net;
|
|||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Oqtane.UI;
|
using Oqtane.UI;
|
||||||
|
|
||||||
// ReSharper disable UnassignedGetOnlyAutoProperty
|
|
||||||
// ReSharper disable MemberCanBePrivate.Global
|
|
||||||
|
|
||||||
namespace Oqtane.Themes.Controls
|
namespace Oqtane.Themes.Controls
|
||||||
{
|
{
|
||||||
public class ModuleActionsBase : ComponentBase
|
public class ModuleActionsBase : ComponentBase
|
||||||
@ -92,20 +89,21 @@ namespace Oqtane.Themes.Controls
|
|||||||
return actionList;
|
return actionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> EditUrlAsync(string url, int moduleId, string import)
|
|
||||||
{
|
|
||||||
await Task.Yield();
|
|
||||||
return Utilities.EditUrl(PageState.Alias.Path, PageState.Page.Path, moduleId, import, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task ModuleAction(ActionViewModel action)
|
protected async Task ModuleAction(ActionViewModel action)
|
||||||
{
|
{
|
||||||
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.PermissionList))
|
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.PermissionList))
|
||||||
{
|
{
|
||||||
PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
var url = NavigationManager.Uri.Substring(NavigationManager.BaseUri.Length - 1);
|
||||||
|
if (!url.Contains("edit="))
|
||||||
string url = Utilities.NavigateUrl(PageState.Alias.Path, PageState.Page.Path, "edit=true&refresh");
|
{
|
||||||
|
url += (!url.Contains("?") ? "?" : "&") + "edit=true";
|
||||||
|
}
|
||||||
|
if (!url.Contains("refresh="))
|
||||||
|
{
|
||||||
|
url += (!url.Contains("?") ? "?" : "&") + "refresh=true";
|
||||||
|
}
|
||||||
|
|
||||||
|
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||||
if (action.Action != null)
|
if (action.Action != null)
|
||||||
{
|
{
|
||||||
url = await action.Action(url, pagemodule);
|
url = await action.Action(url, pagemodule);
|
||||||
@ -115,31 +113,10 @@ namespace Oqtane.Themes.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> MoveToPane(string url, string newPane, PageModule pagemodule)
|
private Task<string> Settings(string url, PageModule pagemodule)
|
||||||
{
|
{
|
||||||
string oldPane = pagemodule.Pane;
|
url = Utilities.EditUrl(PageState.Alias.Path, PageState.Page.Path, pagemodule.ModuleId, "Settings", "returnurl=" + WebUtility.UrlEncode(url));
|
||||||
pagemodule.Pane = newPane;
|
return Task.FromResult(url);
|
||||||
pagemodule.Order = int.MaxValue; // add to bottom of pane
|
|
||||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, oldPane);
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> DeleteModule(string url, PageModule pagemodule)
|
|
||||||
{
|
|
||||||
pagemodule.IsDeleted = true;
|
|
||||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> Settings(string url, PageModule pagemodule)
|
|
||||||
{
|
|
||||||
await Task.Yield();
|
|
||||||
var returnurl = Utilities.NavigateUrl(PageState.Alias.Path, PageState.Page.Path, "edit=true");
|
|
||||||
url = Utilities.EditUrl(PageState.Alias.Path, PageState.Page.Path, pagemodule.ModuleId, "Settings", "returnurl=" + WebUtility.UrlEncode(returnurl));
|
|
||||||
return url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> Publish(string url, PageModule pagemodule)
|
private async Task<string> Publish(string url, PageModule pagemodule)
|
||||||
@ -174,6 +151,20 @@ namespace Oqtane.Themes.Controls
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<string> DeleteModule(string url, PageModule pagemodule)
|
||||||
|
{
|
||||||
|
pagemodule.IsDeleted = true;
|
||||||
|
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||||
|
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<string> EditUrlAsync(string url, int moduleId, string import)
|
||||||
|
{
|
||||||
|
url = Utilities.EditUrl(PageState.Alias.Path, PageState.Page.Path, moduleId, import, "returnurl=" + WebUtility.UrlEncode(url));
|
||||||
|
return Task.FromResult(url);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> MoveTop(string url, PageModule pagemodule)
|
private async Task<string> MoveTop(string url, PageModule pagemodule)
|
||||||
{
|
{
|
||||||
pagemodule.Order = 0;
|
pagemodule.Order = 0;
|
||||||
@ -206,6 +197,17 @@ namespace Oqtane.Themes.Controls
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<string> MoveToPane(string url, string newPane, PageModule pagemodule)
|
||||||
|
{
|
||||||
|
string oldPane = pagemodule.Pane;
|
||||||
|
pagemodule.Pane = newPane;
|
||||||
|
pagemodule.Order = int.MaxValue; // add to bottom of pane
|
||||||
|
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||||
|
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||||
|
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, oldPane);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
public class ActionViewModel
|
public class ActionViewModel
|
||||||
{
|
{
|
||||||
public string Icon { get; set; }
|
public string Icon { get; set; }
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<LanguageSwitcher ButtonClass="@ButtonClass" DropdownAlignment="@LanguageDropdownAlignment" />
|
<LanguageSwitcher ButtonClass="@ButtonClass" DropdownAlignment="@LanguageDropdownAlignment" />
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (_showEditMode || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)))
|
@if (ShowEditMode && (_showEditMode || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered))))
|
||||||
{
|
{
|
||||||
<form method="post" class="app-form-inline" @formname="EditModeForm" @onsubmit="@(async () => await ToggleEditMode(PageState.EditMode))" data-enhance>
|
<form method="post" class="app-form-inline" @formname="EditModeForm" @onsubmit="@(async () => await ToggleEditMode(PageState.EditMode))" data-enhance>
|
||||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||||
@ -59,9 +59,15 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string LanguageDropdownAlignment { get; set; } = string.Empty; // Empty or Left or Right
|
public string LanguageDropdownAlignment { get; set; } = string.Empty; // Empty or Left or Right
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ability to hide the Edit Mode toggle button
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowEditMode { get; set; } = true;
|
||||||
|
|
||||||
private PageState _pageState;
|
private PageState _pageState;
|
||||||
private bool _canViewAdminDashboard = false;
|
private bool _canViewAdminDashboard = false;
|
||||||
private bool _showEditMode = false;
|
private bool _showEditMode = false; // internal state (not the same as ShowEditMode parameter)
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
@ -147,8 +153,7 @@
|
|||||||
{
|
{
|
||||||
if (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered))
|
if (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered))
|
||||||
{
|
{
|
||||||
PageState.EditMode = true;
|
NavigationManager.NavigateTo(NavigateUrl(page.Path, "edit=" + PageState.EditMode.ToString()));
|
||||||
NavigationManager.NavigateTo(NavigateUrl(page.Path, "edit=" + ((PageState.EditMode) ? "true" : "false")));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
@inject ILogService LoggingService
|
@inject ILogService LoggingService
|
||||||
@inject IStringLocalizer<ControlPanelInteractive> Localizer
|
@inject IStringLocalizer<ControlPanelInteractive> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
@inject IServiceProvider ServiceProvider
|
|
||||||
|
|
||||||
<button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel" @onclick="ClearMessage">
|
<button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel" @onclick="ClearMessage">
|
||||||
<span class="oi oi-cog"></span>
|
<span class="oi oi-cog"></span>
|
||||||
@ -331,7 +330,7 @@
|
|||||||
if (_pageId != "-")
|
if (_pageId != "-")
|
||||||
{
|
{
|
||||||
_modules = await ModuleService.GetModulesAsync(PageState.Page.SiteId);
|
_modules = await ModuleService.GetModulesAsync(PageState.Page.SiteId);
|
||||||
_modules = _modules.Where(module => module.PageId == int.Parse(_pageId) &&
|
_modules = _modules.Where(module => module.PageId == int.Parse(_pageId) && module.IsDeleted == false &&
|
||||||
UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList) &&
|
UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList) &&
|
||||||
(_moduleType == "add" || module.ModuleDefinition.IsPortable))
|
(_moduleType == "add" || module.ModuleDefinition.IsPortable))
|
||||||
.ToList();
|
.ToList();
|
||||||
@ -454,7 +453,7 @@
|
|||||||
{
|
{
|
||||||
foreach (var permission in PageState.Page.PermissionList.Where(item => item.PermissionName == pagePermission))
|
foreach (var permission in PageState.Page.PermissionList.Where(item => item.PermissionName == pagePermission))
|
||||||
{
|
{
|
||||||
permissions.Add(new Permission { SiteId = siteId, EntityName = EntityNames.Module, PermissionName = modulePermission, RoleId = permission.RoleId, UserId = permission.UserId, IsAuthorized = permission.IsAuthorized });
|
permissions.Add(new Permission { SiteId = siteId, EntityName = EntityNames.Module, PermissionName = modulePermission, RoleName = permission.RoleName, UserId = permission.UserId, IsAuthorized = permission.IsAuthorized });
|
||||||
}
|
}
|
||||||
return permissions;
|
return permissions;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@using Microsoft.AspNetCore.Localization
|
|
||||||
@using Microsoft.AspNetCore.Http
|
|
||||||
@using Oqtane.Models
|
@using Oqtane.Models
|
||||||
@namespace Oqtane.Themes.Controls
|
@namespace Oqtane.Themes.Controls
|
||||||
@inherits ThemeControlBase
|
@inherits ThemeControlBase
|
||||||
@inject ILanguageService LanguageService
|
@inject ILanguageService LanguageService
|
||||||
|
@inject ILocalizationCookieService LocalizationCookieService
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
@if (_supportedCultures?.Count() > 1)
|
@if (_supportedCultures?.Count() > 1)
|
||||||
@ -22,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<a class="dropdown-item @(CultureInfo.CurrentUICulture.Name == culture.Name ? "active" : String.Empty)" href="@NavigateUrl(PageState.Page.Path, "culture=" + culture.Name)">@culture.DisplayName</a>
|
<a class="dropdown-item @(CultureInfo.CurrentUICulture.Name == culture.Name ? "active" : String.Empty)" href="@NavigateUrl(PageState.Page.Path, "culture=" + culture.Name)" data-enhance-nav="false">@culture.DisplayName</a>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@ -38,25 +37,20 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string ButtonClass { get; set; } = "btn-outline-secondary";
|
public string ButtonClass { get; set; } = "btn-outline-secondary";
|
||||||
|
|
||||||
[CascadingParameter]
|
protected override async Task OnParametersSetAsync()
|
||||||
HttpContext HttpContext { get; set; }
|
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
|
||||||
{
|
{
|
||||||
MenuAlignment = DropdownAlignment.ToLower() == "right" ? "dropdown-menu-end" : string.Empty;
|
MenuAlignment = DropdownAlignment.ToLower() == "right" ? "dropdown-menu-end" : string.Empty;
|
||||||
|
|
||||||
var languages = PageState.Languages;
|
_supportedCultures = PageState.Languages.Select(l => new Culture { Name = l.Code, DisplayName = l.Name });
|
||||||
_supportedCultures = languages.Select(l => new Culture { Name = l.Code, DisplayName = l.Name });
|
|
||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("culture"))
|
if (PageState.QueryString.ContainsKey("culture"))
|
||||||
{
|
{
|
||||||
var culture = PageState.QueryString["culture"];
|
var culture = PageState.QueryString["culture"];
|
||||||
if (_supportedCultures.Any(item => item.Name == culture))
|
if (_supportedCultures.Any(item => item.Name == culture))
|
||||||
{
|
{
|
||||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
await LocalizationCookieService.SetLocalizationCookieAsync(culture);
|
||||||
HttpContext.Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, new CookieOptions { Path = "/", Expires = DateTimeOffset.UtcNow.AddYears(365) });
|
|
||||||
}
|
}
|
||||||
NavigationManager.NavigateTo(NavigationManager.Uri.Replace($"?culture={culture}", ""), forceLoad: true);
|
NavigationManager.NavigateTo(NavigationManager.Uri.Replace($"?culture={culture}", ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,8 +60,8 @@
|
|||||||
{
|
{
|
||||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360, true, "Lax");
|
||||||
NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true);
|
NavigationManager.NavigateTo(NavigationManager.Uri, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,8 @@
|
|||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<span class="app-login">
|
<span class="app-login">
|
||||||
<AuthorizeView Roles="@RoleNames.Registered">
|
@if (PageState.User != null)
|
||||||
<Authorizing>
|
{
|
||||||
<text>...</text>
|
|
||||||
</Authorizing>
|
|
||||||
<Authorized>
|
|
||||||
@if (PageState.Runtime == Runtime.Hybrid)
|
@if (PageState.Runtime == Runtime.Hybrid)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-primary" @onclick="LogoutUser">@Localizer["Logout"]</button>
|
<button type="button" class="btn btn-primary" @onclick="LogoutUser">@Localizer["Logout"]</button>
|
||||||
@ -21,14 +18,14 @@
|
|||||||
<button type="submit" class="btn btn-primary">@Localizer["Logout"]</button>
|
<button type="submit" class="btn btn-primary">@Localizer["Logout"]</button>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
</Authorized>
|
}
|
||||||
<NotAuthorized>
|
else
|
||||||
|
{
|
||||||
@if (ShowLogin)
|
@if (ShowLogin)
|
||||||
{
|
{
|
||||||
<a href="@loginurl" class="btn btn-primary">@SharedLocalizer["Login"]</a>
|
<a href="@loginurl" class="btn btn-primary">@SharedLocalizer["Login"]</a>
|
||||||
}
|
}
|
||||||
</NotAuthorized>
|
}
|
||||||
</AuthorizeView>
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@code
|
@code
|
||||||
|
@ -9,3 +9,18 @@
|
|||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (UseSiteNameAsFallback)
|
||||||
|
{
|
||||||
|
<span class="app-logo">
|
||||||
|
<a class="navbar-brand" href="@PageState.Alias.Path">@PageState.Site.Name</a>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public bool UseSiteNameAsFallback { get; set; } = false; // indicates if the site name should be displayed in scenarios where a site does not have a logo defined
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,30 +1,32 @@
|
|||||||
@namespace Oqtane.Themes.Controls
|
@namespace Oqtane.Themes.Controls
|
||||||
@using System.Net
|
@using System.Net
|
||||||
@using Microsoft.AspNetCore.Http
|
|
||||||
@inherits ThemeControlBase
|
@inherits ThemeControlBase
|
||||||
|
@inject ISettingService SettingService
|
||||||
@inject IStringLocalizer<Search> Localizer
|
@inject IStringLocalizer<Search> Localizer
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
@if (_searchResultsPage != null)
|
@if (_searchResultsPage != null)
|
||||||
{
|
{
|
||||||
<span class="app-search @CssClass">
|
<span class="@_defaultCssClass @CssClass">
|
||||||
<form method="post" class="app-form-inline" @formname="@($"SearchForm")" @onsubmit="@PerformSearch" data-enhance>
|
<form method="post" class="app-form-inline" @formname="@($"SearchForm")" @onsubmit="@PerformSearch" data-enhance>
|
||||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||||
|
@if (AllowTextInput)
|
||||||
|
{
|
||||||
<input type="text" name="keywords" maxlength="50"
|
<input type="text" name="keywords" maxlength="50"
|
||||||
class="form-control d-inline-block pe-5 shadow-none"
|
class="form-control d-inline-block pe-5 shadow-none"
|
||||||
@bind="_keywords"
|
@bind="_keywords"
|
||||||
placeholder="@Localizer["SearchPlaceHolder"]"
|
placeholder="@Localizer["SearchPlaceHolder"]"
|
||||||
aria-label="Search" />
|
aria-label="Search" />
|
||||||
<button type="submit" class="btn btn-search">
|
}
|
||||||
|
<button type="submit" class="btn btn-search" aria-label="Search Button">
|
||||||
<span class="oi oi-magnifying-glass align-middle"></span>
|
<span class="oi oi-magnifying-glass align-middle"></span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private string _defaultCssClass;
|
||||||
private Page _searchResultsPage;
|
private Page _searchResultsPage;
|
||||||
private string _keywords = "";
|
private string _keywords = "";
|
||||||
|
|
||||||
@ -32,21 +34,25 @@
|
|||||||
public string CssClass { get; set; }
|
public string CssClass { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string SearchResultPagePath { get; set; } = "search";
|
public bool AllowTextInput { get; set; } = true; // setting to false will display only the search icon button - not the textbox
|
||||||
|
|
||||||
[CascadingParameter]
|
[Parameter]
|
||||||
HttpContext HttpContext { get; set; }
|
public string SearchResultPagePath { get; set; } = "search"; // setting to "" will disable search
|
||||||
|
|
||||||
[SupplyParameterFromForm(FormName = "SearchForm")]
|
[SupplyParameterFromForm(FormName = "SearchForm")]
|
||||||
public string KeyWords { get => ""; set => _keywords = value; }
|
public string KeyWords { get => ""; set => _keywords = value; }
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
if(!string.IsNullOrEmpty(SearchResultPagePath))
|
if (bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "Search_Enabled", "True")))
|
||||||
|
{
|
||||||
|
_defaultCssClass = (AllowTextInput) ? "app-search" : "app-search-noinput";
|
||||||
|
if (!string.IsNullOrEmpty(SearchResultPagePath))
|
||||||
{
|
{
|
||||||
_searchResultsPage = PageState.Pages.FirstOrDefault(i => i.Path == SearchResultPagePath);
|
_searchResultsPage = PageState.Pages.FirstOrDefault(i => i.Path == SearchResultPagePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void PerformSearch()
|
private void PerformSearch()
|
||||||
{
|
{
|
||||||
|
@ -6,20 +6,17 @@
|
|||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
<span class="app-profile">
|
<span class="app-profile">
|
||||||
<AuthorizeView Roles="@RoleNames.Registered">
|
@if (PageState.User != null)
|
||||||
<Authorizing>
|
{
|
||||||
<text>...</text>
|
<a href="@NavigateUrl("profile", "returnurl=" + _returnurl)" class="btn btn-primary">@PageState.User.Username</a>
|
||||||
</Authorizing>
|
}
|
||||||
<Authorized>
|
else
|
||||||
<a href="@NavigateUrl("profile", "returnurl=" + _returnurl)" class="btn btn-primary">@context.User.Identity.Name</a>
|
{
|
||||||
</Authorized>
|
|
||||||
<NotAuthorized>
|
|
||||||
@if (ShowRegister && PageState.Site.AllowRegistration)
|
@if (ShowRegister && PageState.Site.AllowRegistration)
|
||||||
{
|
{
|
||||||
<a href="@NavigateUrl("register", "returnurl=" + _returnurl)" class="btn btn-primary">@Localizer["Register"]</a>
|
<a href="@NavigateUrl("register", "returnurl=" + _returnurl)" class="btn btn-primary">@Localizer["Register"]</a>
|
||||||
}
|
}
|
||||||
</NotAuthorized>
|
}
|
||||||
</AuthorizeView>
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
@ -17,13 +17,9 @@ namespace Oqtane.Themes.OqtaneTheme
|
|||||||
Resources = new List<Resource>()
|
Resources = new List<Resource>()
|
||||||
{
|
{
|
||||||
// obtained from https://cdnjs.com/libraries
|
// obtained from https://cdnjs.com/libraries
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.3/cyborg/bootstrap.min.css",
|
new Stylesheet("https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.3/cyborg/bootstrap.min.css", "sha512-M+Wrv9LTvQe81gFD2ZE3xxPTN5V2n1iLCXsldIxXvfs6tP+6VihBCwCMBkkjkQUZVmEHBsowb9Vqsq1et1teEg==", "anonymous"),
|
||||||
Integrity = "sha512-M+Wrv9LTvQe81gFD2ZE3xxPTN5V2n1iLCXsldIxXvfs6tP+6VihBCwCMBkkjkQUZVmEHBsowb9Vqsq1et1teEg==",
|
new Stylesheet("~/Theme.css"),
|
||||||
CrossOrigin = "anonymous" },
|
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" },
|
|
||||||
new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js",
|
|
||||||
Integrity = "sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg==",
|
|
||||||
CrossOrigin = "anonymous", Location = ResourceLocation.Body },
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<main role="main">
|
<main role="main">
|
||||||
<nav class="navbar navbar-dark bg-primary fixed-top">
|
<nav class="navbar navbar-dark bg-primary fixed-top">
|
||||||
<Logo /><Menu Orientation="Horizontal" />
|
<Logo UseSiteNameAsFallback="true" /><Menu Orientation="Horizontal" />
|
||||||
<div class="controls ms-auto">
|
<div class="controls ms-auto">
|
||||||
<div class="controls-group">
|
<div class="controls-group">
|
||||||
<Search CssClass="me-3 text-center bg-primary" />
|
<Search CssClass="me-3 text-center bg-primary" />
|
||||||
|
@ -62,17 +62,17 @@ namespace Oqtane.Themes
|
|||||||
var inline = 0;
|
var inline = 0;
|
||||||
foreach (Resource resource in resources)
|
foreach (Resource resource in resources)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive)
|
if ((string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive) && !resource.Reload)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(resource.Url))
|
if (!string.IsNullOrEmpty(resource.Url))
|
||||||
{
|
{
|
||||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||||
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
|
scripts.Add(new { href = url, type = resource.Type ?? "", bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", location = resource.Location.ToString().ToLower(), dataAttributes = resource.DataAttributes });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inline += 1;
|
inline += 1;
|
||||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Type ?? "", resource.Content, resource.Location.ToString().ToLower());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,6 +93,7 @@ namespace Oqtane.Themes
|
|||||||
|
|
||||||
// url methods
|
// url methods
|
||||||
|
|
||||||
|
// navigate url
|
||||||
public string NavigateUrl()
|
public string NavigateUrl()
|
||||||
{
|
{
|
||||||
return NavigateUrl(PageState.Page.Path);
|
return NavigateUrl(PageState.Page.Path);
|
||||||
@ -108,31 +109,78 @@ namespace Oqtane.Themes
|
|||||||
return NavigateUrl(PageState.Page.Path, refresh);
|
return NavigateUrl(PageState.Page.Path, refresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(string path, string querystring)
|
||||||
|
{
|
||||||
|
return Utilities.NavigateUrl(PageState.Alias.Path, path, querystring);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(string path, Dictionary<string, string> querystring)
|
||||||
|
{
|
||||||
|
return NavigateUrl(path, Utilities.CreateQueryString(querystring));
|
||||||
|
}
|
||||||
|
|
||||||
public string NavigateUrl(string path, bool refresh)
|
public string NavigateUrl(string path, bool refresh)
|
||||||
{
|
{
|
||||||
return Utilities.NavigateUrl(PageState.Alias.Path, path, refresh ? "refresh" : "");
|
return NavigateUrl(path, refresh ? "refresh" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string NavigateUrl(string path, string parameters)
|
public string NavigateUrl(int moduleid, string action)
|
||||||
{
|
{
|
||||||
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
|
return EditUrl(moduleid, action, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(int moduleid, string action, string querystring)
|
||||||
|
{
|
||||||
|
return EditUrl(PageState.Page.Path, moduleid, action, querystring);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(int moduleid, string action, Dictionary<string, string> querystring)
|
||||||
|
{
|
||||||
|
return EditUrl(PageState.Page.Path, moduleid, action, Utilities.CreateQueryString(querystring));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(string path, int moduleId, string action)
|
||||||
|
{
|
||||||
|
return EditUrl(path, moduleId, action, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(string path, int moduleid, string action, string querystring)
|
||||||
|
{
|
||||||
|
return EditUrl(path, moduleid, action, querystring);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NavigateUrl(string path, int moduleid, string action, Dictionary<string, string> querystring)
|
||||||
|
{
|
||||||
|
return EditUrl(path, moduleid, action, querystring);
|
||||||
|
}
|
||||||
|
|
||||||
|
// edit url
|
||||||
public string EditUrl(int moduleid, string action)
|
public string EditUrl(int moduleid, string action)
|
||||||
{
|
{
|
||||||
return EditUrl(moduleid, action, "");
|
return EditUrl(moduleid, action, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string EditUrl(int moduleid, string action, string parameters)
|
public string EditUrl(int moduleid, string action, string querystring)
|
||||||
{
|
{
|
||||||
return EditUrl(PageState.Page.Path, moduleid, action, parameters);
|
return EditUrl(PageState.Page.Path, moduleid, action, querystring);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string EditUrl(string path, int moduleid, string action, string parameters)
|
public string EditUrl(int moduleid, string action, Dictionary<string, string> querystring)
|
||||||
{
|
{
|
||||||
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
|
return EditUrl(PageState.Page.Path, moduleid, action, querystring);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string EditUrl(string path, int moduleid, string action, string querystring)
|
||||||
|
{
|
||||||
|
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, querystring);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string EditUrl(string path, int moduleid, string action, Dictionary<string, string> querystring)
|
||||||
|
{
|
||||||
|
return EditUrl(path, moduleid, action, Utilities.CreateQueryString(querystring));
|
||||||
|
}
|
||||||
|
|
||||||
|
// file url
|
||||||
public string FileUrl(string folderpath, string filename)
|
public string FileUrl(string folderpath, string filename)
|
||||||
{
|
{
|
||||||
return FileUrl(folderpath, filename, false);
|
return FileUrl(folderpath, filename, false);
|
||||||
@ -152,6 +200,7 @@ namespace Oqtane.Themes
|
|||||||
return Utilities.FileUrl(PageState.Alias, fileid, download);
|
return Utilities.FileUrl(PageState.Alias, fileid, download);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// image url
|
||||||
public string ImageUrl(int fileid, int width, int height)
|
public string ImageUrl(int fileid, int width, int height)
|
||||||
{
|
{
|
||||||
return ImageUrl(fileid, width, height, "");
|
return ImageUrl(fileid, width, height, "");
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "HeadContent":
|
case "HeadContent":
|
||||||
var content = RemoveScripts(SiteState.Properties.HeadContent) + "\n";
|
var content = FormatScripts(SiteState.Properties.HeadContent) + "\n";
|
||||||
if (content != _content)
|
if (content != _content)
|
||||||
{
|
{
|
||||||
_content = content;
|
_content = content;
|
||||||
@ -51,16 +51,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string RemoveScripts(string headcontent)
|
private string FormatScripts(string headcontent)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(headcontent) && RenderMode == RenderModes.Interactive)
|
if (!string.IsNullOrEmpty(headcontent))
|
||||||
{
|
{
|
||||||
var index = headcontent.IndexOf("<script");
|
var index = headcontent.IndexOf("<script");
|
||||||
while (index >= 0)
|
while (index >= 0)
|
||||||
{
|
{
|
||||||
headcontent = headcontent.Remove(index, headcontent.IndexOf("</script>") + 9 - index);
|
var script = headcontent.Substring(index, headcontent.IndexOf("</script>", index) - index + 9);
|
||||||
|
if (RenderMode == RenderModes.Interactive)
|
||||||
|
{
|
||||||
|
// remove scripts when interactive rendering
|
||||||
|
headcontent = headcontent.Remove(index, script.Length);
|
||||||
index = headcontent.IndexOf("<script");
|
index = headcontent.IndexOf("<script");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!script.Contains("><") && !script.Contains("data-reload"))
|
||||||
|
{
|
||||||
|
// add data-reload attribute to inline script
|
||||||
|
headcontent = headcontent.Replace(script, script.Replace("<script", "<script data-reload=\"true\""));
|
||||||
|
}
|
||||||
|
index = headcontent.IndexOf("<script", index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return headcontent;
|
return headcontent;
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,18 @@ namespace Oqtane.UI
|
|||||||
_jsRuntime = jsRuntime;
|
_jsRuntime = jsRuntime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SetCookie(string name, string value, int days)
|
public async Task SetCookie(string name, string value, int days)
|
||||||
|
{
|
||||||
|
await SetCookie(name, value, days, true, "Lax");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SetCookie(string name, string value, int days, bool secure, string sameSite)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_jsRuntime.InvokeVoidAsync(
|
_jsRuntime.InvokeVoidAsync(
|
||||||
"Oqtane.Interop.setCookie",
|
"Oqtane.Interop.setCookie",
|
||||||
name, value, days);
|
name, value, days, secure, sameSite);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -112,12 +117,17 @@ namespace Oqtane.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string type, string content, string location)
|
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string type, string content, string location)
|
||||||
|
{
|
||||||
|
return IncludeScript(id, src, integrity, crossorigin, type, content, location, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string type, string content, string location, Dictionary<string, string> dataAttributes)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_jsRuntime.InvokeVoidAsync(
|
_jsRuntime.InvokeVoidAsync(
|
||||||
"Oqtane.Interop.includeScript",
|
"Oqtane.Interop.includeScript",
|
||||||
id, src, integrity, crossorigin, type, content, location);
|
id, src, integrity, crossorigin, type, content, location, dataAttributes);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
@namespace Oqtane.UI
|
@namespace Oqtane.UI
|
||||||
@using Microsoft.AspNetCore.Http
|
|
||||||
@inject IInstallationService InstallationService
|
@inject IInstallationService InstallationService
|
||||||
@inject IJSRuntime JSRuntime
|
|
||||||
@inject SiteState SiteState
|
@inject SiteState SiteState
|
||||||
|
|
||||||
@if (_initialized)
|
@if (_initialized)
|
||||||
@ -48,9 +46,6 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string Platform { get; set; } = "";
|
public string Platform { get; set; } = "";
|
||||||
|
|
||||||
[CascadingParameter]
|
|
||||||
HttpContext HttpContext { get; set; }
|
|
||||||
|
|
||||||
private bool _initialized = false;
|
private bool _initialized = false;
|
||||||
private bool _installed = false;
|
private bool _installed = false;
|
||||||
private string _display = "display: none;";
|
private string _display = "display: none;";
|
||||||
@ -61,9 +56,8 @@
|
|||||||
{
|
{
|
||||||
SiteState.AntiForgeryToken = AntiForgeryToken;
|
SiteState.AntiForgeryToken = AntiForgeryToken;
|
||||||
SiteState.AuthorizationToken = AuthorizationToken;
|
SiteState.AuthorizationToken = AuthorizationToken;
|
||||||
SiteState.RemoteIPAddress = (_pageState != null) ? _pageState.RemoteIPAddress : "";
|
|
||||||
SiteState.Platform = Platform;
|
SiteState.Platform = Platform;
|
||||||
SiteState.IsPrerendering = (HttpContext != null) ? true : false;
|
SiteState.IsPrerendering = !RendererInfo.IsInteractive;
|
||||||
|
|
||||||
if (Runtime == Runtimes.Hybrid)
|
if (Runtime == Runtimes.Hybrid)
|
||||||
{
|
{
|
||||||
@ -80,6 +74,7 @@
|
|||||||
{
|
{
|
||||||
_pageState = PageState;
|
_pageState = PageState;
|
||||||
SiteState.Alias = PageState.Alias;
|
SiteState.Alias = PageState.Alias;
|
||||||
|
SiteState.RemoteIPAddress = (PageState != null) ? PageState.RemoteIPAddress : "";
|
||||||
_installed = true;
|
_installed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
@using System.Diagnostics.CodeAnalysis
|
@using System.Diagnostics.CodeAnalysis
|
||||||
@using System.Net
|
@using System.Net
|
||||||
@using Microsoft.AspNetCore.Http
|
|
||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@using System.Security.Claims
|
@using System.Security.Claims
|
||||||
@namespace Oqtane.UI
|
@namespace Oqtane.UI
|
||||||
@ -157,7 +156,7 @@
|
|||||||
|
|
||||||
// verify user is authenticated for current site
|
// verify user is authenticated for current site
|
||||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||||
if (authState.User.Identity.IsAuthenticated && authState.User.Claims.Any(item => item.Type == "sitekey" && item.Value == SiteState.Alias.SiteKey))
|
if (authState.User.Identity.IsAuthenticated && authState.User.Claims.Any(item => item.Type == Constants.SiteKeyClaimType && item.Value == SiteState.Alias.SiteKey))
|
||||||
{
|
{
|
||||||
// get user
|
// get user
|
||||||
var userid = int.Parse(authState.User.Claims.First(item => item.Type == ClaimTypes.NameIdentifier).Value);
|
var userid = int.Parse(authState.User.Claims.First(item => item.Type == ClaimTypes.NameIdentifier).Value);
|
||||||
@ -287,10 +286,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// load additional metadata for current page
|
// load additional metadata for current page
|
||||||
page = ProcessPage(page, site, user, SiteState.Alias);
|
page = ProcessPage(page, site, user, SiteState.Alias, action);
|
||||||
|
|
||||||
// load additional metadata for modules
|
// load additional metadata for modules
|
||||||
(page, modules) = ProcessModules(page, modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType, SiteState.Alias);
|
(page, modules) = ProcessModules(site, page, modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType, SiteState.Alias);
|
||||||
|
|
||||||
// populate page state (which acts as a client-side cache for subsequent requests)
|
// populate page state (which acts as a client-side cache for subsequent requests)
|
||||||
_pagestate = new PageState
|
_pagestate = new PageState
|
||||||
@ -366,7 +365,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Page ProcessPage(Page page, Site site, User user, Alias alias)
|
private Page ProcessPage(Page page, Site site, User user, Alias alias, string action)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -403,6 +402,16 @@
|
|||||||
page.Resources = ManagePageResources(page.Resources, themeobject.Resources, ResourceLevel.Page, alias, "Themes", themetype.Namespace);
|
page.Resources = ManagePageResources(page.Resources, themeobject.Resources, ResourceLevel.Page, alias, "Themes", themetype.Namespace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// theme settings components are dynamically loaded within the framework Page Management module
|
||||||
|
if (page.Path == "admin/pages" && action.ToLower() == "edit" && theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
||||||
|
{
|
||||||
|
var settingsType = Type.GetType(theme.ThemeSettingsType);
|
||||||
|
if (settingsType != null)
|
||||||
|
{
|
||||||
|
var objSettings = Activator.CreateInstance(settingsType) as IModuleControl;
|
||||||
|
page.Resources = ManagePageResources(page.Resources, objSettings.Resources, ResourceLevel.Module, alias, "Modules", settingsType.Namespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(panes))
|
if (!string.IsNullOrEmpty(panes))
|
||||||
{
|
{
|
||||||
@ -426,7 +435,7 @@
|
|||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
private (Page Page, List<Module> Modules) ProcessModules(Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype, Alias alias)
|
private (Page Page, List<Module> Modules) ProcessModules(Site site, Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype, Alias alias)
|
||||||
{
|
{
|
||||||
var paneindex = new Dictionary<string, int>();
|
var paneindex = new Dictionary<string, int>();
|
||||||
|
|
||||||
@ -494,15 +503,40 @@
|
|||||||
module.Prerender = moduleobject.Prerender;
|
module.Prerender = moduleobject.Prerender;
|
||||||
|
|
||||||
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
||||||
|
|
||||||
|
// settings components are dynamically loaded within the framework Settings module
|
||||||
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
||||||
{
|
{
|
||||||
// settings components are embedded within a framework settings module
|
// module settings component
|
||||||
moduletype = Type.GetType(module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action), false, true);
|
var settingsType = "";
|
||||||
|
if (!string.IsNullOrEmpty(module.ModuleDefinition.SettingsType))
|
||||||
|
{
|
||||||
|
// module settings type explicitly declared in IModule interface
|
||||||
|
settingsType = module.ModuleDefinition.SettingsType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// legacy support - module settings type determined by convention
|
||||||
|
settingsType = module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action);
|
||||||
|
}
|
||||||
|
moduletype = Type.GetType(settingsType, false, true);
|
||||||
if (moduletype != null)
|
if (moduletype != null)
|
||||||
{
|
{
|
||||||
moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||||
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// container settings component
|
||||||
|
var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType));
|
||||||
|
if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType))
|
||||||
|
{
|
||||||
|
moduletype = Type.GetType(theme.ContainerSettingsType);
|
||||||
|
if (moduletype != null)
|
||||||
|
{
|
||||||
|
moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||||
|
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// additional metadata needed for admin components
|
// additional metadata needed for admin components
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
RenderFragment DynamicComponent { get; set; }
|
RenderFragment DynamicComponent { get; set; }
|
||||||
|
|
||||||
|
private string lastPagePath = "";
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
// handle page redirection
|
// handle page redirection
|
||||||
@ -20,6 +22,13 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// force authenticated user to provide email address (email may be missing if using external login)
|
||||||
|
if (PageState.User != null && PageState.User.IsAuthenticated && string.IsNullOrEmpty(PageState.User.Email) && PageState.Route.PagePath != "profile")
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, "profile", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// set page title
|
// set page title
|
||||||
if (!string.IsNullOrEmpty(PageState.Page.Title))
|
if (!string.IsNullOrEmpty(PageState.Page.Title))
|
||||||
{
|
{
|
||||||
@ -44,7 +53,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// head content
|
// head content
|
||||||
AddHeadContent(headcontent, PageState.Site.HeadContent);
|
|
||||||
if (!string.IsNullOrEmpty(PageState.Site.HeadContent))
|
if (!string.IsNullOrEmpty(PageState.Site.HeadContent))
|
||||||
{
|
{
|
||||||
headcontent = AddHeadContent(headcontent, PageState.Site.HeadContent);
|
headcontent = AddHeadContent(headcontent, PageState.Site.HeadContent);
|
||||||
@ -66,32 +74,34 @@
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(content))
|
if (!string.IsNullOrEmpty(content))
|
||||||
{
|
{
|
||||||
if (PageState.RenderMode == RenderModes.Interactive)
|
var elements = content.Split('<', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
foreach (var element in elements)
|
||||||
{
|
{
|
||||||
// remove scripts
|
if (PageState.RenderMode == RenderModes.Static || (!element.ToLower().StartsWith("script") && !element.ToLower().StartsWith("/script")))
|
||||||
var index = content.IndexOf("<script");
|
|
||||||
while (index >= 0)
|
|
||||||
{
|
{
|
||||||
content = content.Remove(index, content.IndexOf("</script>") + 9 - index);
|
if (!headcontent.Contains("<" + element) || element.StartsWith("/"))
|
||||||
index = content.IndexOf("<script");
|
{
|
||||||
|
headcontent += "<" + element;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
headcontent += content + "\n";
|
}
|
||||||
|
headcontent += "\n";
|
||||||
}
|
}
|
||||||
return headcontent;
|
return headcontent;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
// force authenticated user to provide email address (email may be missing if using external login)
|
if (!firstRender && PageState.Page.Path != lastPagePath)
|
||||||
if (PageState.User != null && PageState.User.IsAuthenticated && string.IsNullOrEmpty(PageState.User.Email) && PageState.Route.PagePath != "profile")
|
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, "profile", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
|
if (!string.IsNullOrEmpty(PageState.Site.HeadContent) && PageState.Site.HeadContent.Contains("<script"))
|
||||||
return;
|
{
|
||||||
|
await InjectScripts(PageState.Site.HeadContent, ResourceLocation.Head);
|
||||||
}
|
}
|
||||||
|
if (!string.IsNullOrEmpty(PageState.Site.BodyContent) && PageState.Site.BodyContent.Contains("<script"))
|
||||||
if (!firstRender)
|
|
||||||
{
|
{
|
||||||
|
await InjectScripts(PageState.Site.BodyContent, ResourceLocation.Body);
|
||||||
|
}
|
||||||
if (!string.IsNullOrEmpty(PageState.Page.HeadContent) && PageState.Page.HeadContent.Contains("<script"))
|
if (!string.IsNullOrEmpty(PageState.Page.HeadContent) && PageState.Page.HeadContent.Contains("<script"))
|
||||||
{
|
{
|
||||||
await InjectScripts(PageState.Page.HeadContent, ResourceLocation.Head);
|
await InjectScripts(PageState.Page.HeadContent, ResourceLocation.Head);
|
||||||
@ -100,6 +110,7 @@
|
|||||||
{
|
{
|
||||||
await InjectScripts(PageState.Page.BodyContent, ResourceLocation.Body);
|
await InjectScripts(PageState.Page.BodyContent, ResourceLocation.Body);
|
||||||
}
|
}
|
||||||
|
lastPagePath = PageState.Page.Path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// style sheets
|
// style sheets
|
||||||
@ -176,18 +187,20 @@
|
|||||||
if (!string.IsNullOrEmpty(src))
|
if (!string.IsNullOrEmpty(src))
|
||||||
{
|
{
|
||||||
src = (src.Contains("://")) ? src : PageState.Alias.BaseUrl + src;
|
src = (src.Contains("://")) ? src : PageState.Alias.BaseUrl + src;
|
||||||
scripts.Add(new { href = src, bundle = "", integrity = integrity, crossorigin = crossorigin, es6module = (type == "module"), location = location.ToString().ToLower(), dataAttributes = dataAttributes });
|
scripts.Add(new { href = src, type = type, bundle = "", integrity = integrity, crossorigin = crossorigin, location = location.ToString().ToLower(), dataAttributes = dataAttributes });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// inline script must have an id attribute
|
if (dataAttributes == null || !dataAttributes.ContainsKey("data-reload") || dataAttributes["data-reload"] != "false")
|
||||||
|
{
|
||||||
if (id == "")
|
if (id == "")
|
||||||
{
|
{
|
||||||
count += 1;
|
count += 1;
|
||||||
id = $"page{PageState.Page.PageId}-script{count}";
|
id = $"page{PageState.Page.PageId}-script{count}";
|
||||||
}
|
}
|
||||||
index = script.IndexOf(">") + 1;
|
var pos = script.IndexOf(">") + 1;
|
||||||
await interop.IncludeScript(id, "", "", "", "", script.Substring(index, script.IndexOf("</script>") - index), location.ToString().ToLower());
|
await interop.IncludeScript(id, "", "", "", type, script.Substring(pos, script.IndexOf("</script>") - pos), location.ToString().ToLower(), dataAttributes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
index = content.IndexOf("<script", index + 1);
|
index = content.IndexOf("<script", index + 1);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Version>5.2.0</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<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/v5.2.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
@ -33,8 +33,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="8.0.5" />
|
<PackageReference Include="MySql.EntityFrameworkCore" Version="9.0.0-preview" />
|
||||||
<PackageReference Include="MySql.Data" Version="9.0.0" />
|
<PackageReference Include="MySql.Data" Version="9.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -42,7 +42,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<MySQLFiles Include="$(OutputPath)Oqtane.Database.MySQL.dll;$(OutputPath)Oqtane.Database.MySQL.pdb;$(OutputPath)MySql.EntityFrameworkCore.dll;$(OutputPath)MySql.Data.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net8.0\%(Filename)%(Extension)" />
|
<MySQLFiles Include="$(OutputPath)Oqtane.Database.MySQL.dll;$(OutputPath)Oqtane.Database.MySQL.pdb;$(OutputPath)MySql.EntityFrameworkCore.dll;$(OutputPath)MySql.Data.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net9.0\%(Filename)%(Extension)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(MySQLFiles)" Outputs="@(MySQLFiles->'%(DestinationPath)')">
|
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(MySQLFiles)" Outputs="@(MySQLFiles->'%(DestinationPath)')">
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Version>5.2.1</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<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/v5.2.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
<NoWarn>1701;1702;EF1001;AD0001;NU1608</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
@ -33,9 +33,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
|
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.0" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -43,7 +43,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PostgreSQLFiles Include="$(OutputPath)Oqtane.Database.PostgreSQL.dll;$(OutputPath)Oqtane.Database.PostgreSQL.pdb;$(OutputPath)EFCore.NamingConventions.dll;$(OutputPath)Npgsql.EntityFrameworkCore.PostgreSQL.dll;$(OutputPath)Npgsql.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net8.0\%(Filename)%(Extension)" />
|
<PostgreSQLFiles Include="$(OutputPath)Oqtane.Database.PostgreSQL.dll;$(OutputPath)Oqtane.Database.PostgreSQL.pdb;$(OutputPath)EFCore.NamingConventions.dll;$(OutputPath)Npgsql.EntityFrameworkCore.PostgreSQL.dll;$(OutputPath)Npgsql.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net9.0\%(Filename)%(Extension)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(PostgreSQLFiles)" Outputs="@(PostgreSQLFiles->'%(DestinationPath)')">
|
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(PostgreSQLFiles)" Outputs="@(PostgreSQLFiles->'%(DestinationPath)')">
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Version>5.2.1</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<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/v5.2.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
@ -33,7 +33,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -41,7 +41,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<SqlServerFiles Include="$(OutputPath)Oqtane.Database.SqlServer.dll;$(OutputPath)Oqtane.Database.SqlServer.pdb;$(OutputPath)Microsoft.EntityFrameworkCore.SqlServer.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net8.0\%(Filename)%(Extension)" />
|
<SqlServerFiles Include="$(OutputPath)Oqtane.Database.SqlServer.dll;$(OutputPath)Oqtane.Database.SqlServer.pdb;$(OutputPath)Microsoft.EntityFrameworkCore.SqlServer.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net9.0\%(Filename)%(Extension)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(SqlServerFiles)" Outputs="@(SqlServerFiles->'%(DestinationPath)')">
|
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(SqlServerFiles)" Outputs="@(SqlServerFiles->'%(DestinationPath)')">
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Version>5.2.1</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<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/v5.2.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
@ -33,7 +33,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -41,7 +41,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<SqliteFiles Include="$(OutputPath)Oqtane.Database.Sqlite.dll;$(OutputPath)Oqtane.Database.Sqlite.pdb;$(OutputPath)Microsoft.EntityFrameworkCore.Sqlite.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net8.0\%(Filename)%(Extension)" />
|
<SqliteFiles Include="$(OutputPath)Oqtane.Database.Sqlite.dll;$(OutputPath)Oqtane.Database.Sqlite.pdb;$(OutputPath)Microsoft.EntityFrameworkCore.Sqlite.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net9.0\%(Filename)%(Extension)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(SqliteFiles)" Outputs="@(SqliteFiles->'%(DestinationPath)')">
|
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(SqliteFiles)" Outputs="@(SqliteFiles->'%(DestinationPath)')">
|
||||||
|
@ -5,7 +5,10 @@ public partial class App : Application
|
|||||||
public App()
|
public App()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
MainPage = new MainPage();
|
protected override Window CreateWindow(IActivationState activationState)
|
||||||
|
{
|
||||||
|
return new Window(new MainPage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
|
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net9.0-windows10.0.19041.0</TargetFrameworks>
|
||||||
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
|
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
|
||||||
<!-- <TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks> -->
|
<!-- <TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks> -->
|
||||||
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
|
<!-- <TargetFrameworks>$(TargetFrameworks);net9.0-tizen</TargetFrameworks> -->
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Version>5.2.1</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<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/v5.2.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.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.Maui</RootNamespace>
|
<RootNamespace>Oqtane.Maui</RootNamespace>
|
||||||
@ -28,14 +28,16 @@
|
|||||||
|
|
||||||
<!-- App Identifier -->
|
<!-- App Identifier -->
|
||||||
<ApplicationId>com.oqtane.maui</ApplicationId>
|
<ApplicationId>com.oqtane.maui</ApplicationId>
|
||||||
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
|
|
||||||
|
|
||||||
<!-- Versions -->
|
<!-- Versions -->
|
||||||
<ApplicationDisplayVersion>5.2.1</ApplicationDisplayVersion>
|
<ApplicationDisplayVersion>6.0.1</ApplicationDisplayVersion>
|
||||||
<ApplicationVersion>1</ApplicationVersion>
|
<ApplicationVersion>1</ApplicationVersion>
|
||||||
|
|
||||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
|
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
|
||||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">14.0</SupportedOSPlatformVersion>
|
<WindowsPackageType>None</WindowsPackageType>
|
||||||
|
|
||||||
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.0</SupportedOSPlatformVersion>
|
||||||
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">15.0</SupportedOSPlatformVersion>
|
||||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>
|
||||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
|
||||||
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
|
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
|
||||||
@ -65,23 +67,22 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.8" />
|
<PackageReference Include="System.Net.Http.Json" Version="9.0.0" />
|
||||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.80" />
|
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.80" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.80" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Oqtane.Client">
|
<Reference Include="Oqtane.Client">
|
||||||
<HintPath>..\Oqtane.Server\bin\Debug\net8.0\Oqtane.Client.dll</HintPath>
|
<HintPath>..\Oqtane.Server\bin\Debug\net9.0\Oqtane.Client.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Oqtane.Shared">
|
<Reference Include="Oqtane.Shared">
|
||||||
<HintPath>..\Oqtane.Server\bin\Debug\net8.0\Oqtane.Shared.dll</HintPath>
|
<HintPath>..\Oqtane.Server\bin\Debug\net9.0\Oqtane.Shared.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"Windows Machine": {
|
"Windows Machine": {
|
||||||
"commandName": "MsixPackage",
|
"commandName": "Project",
|
||||||
"nativeDebugging": false
|
"nativeDebugging": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,9 @@ app {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Action Dialog */
|
/* Action Dialog */
|
||||||
|
.app-actiondialog{
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
.app-actiondialog .modal {
|
.app-actiondialog .modal {
|
||||||
position: fixed; /* Stay in place */
|
position: fixed; /* Stay in place */
|
||||||
z-index: 9999; /* Sit on top */
|
z-index: 9999; /* Sit on top */
|
||||||
@ -230,5 +233,41 @@ app {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app-form-inline {
|
.app-form-inline {
|
||||||
display: inline-block;
|
display: inline;
|
||||||
|
}
|
||||||
|
.app-search{
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.app-search input + button{
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.app-search input + button .oi{
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.app-search-noinput {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.app-search-noinput button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--bs-heading-color);
|
||||||
|
}
|
||||||
|
.app-search-noinput button:hover {
|
||||||
|
color: var(--bs-heading-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text Editor */
|
||||||
|
.text-area-editor > textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-logo .navbar-brand {
|
||||||
|
padding: 5px 20px 5px 20px;
|
||||||
}
|
}
|
@ -1,11 +1,18 @@
|
|||||||
var Oqtane = Oqtane || {};
|
var Oqtane = Oqtane || {};
|
||||||
|
|
||||||
Oqtane.Interop = {
|
Oqtane.Interop = {
|
||||||
setCookie: function (name, value, days) {
|
setCookie: function (name, value, days, secure, sameSite) {
|
||||||
var d = new Date();
|
var d = new Date();
|
||||||
d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000));
|
d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||||
var expires = "expires=" + d.toUTCString();
|
var expires = "expires=" + d.toUTCString();
|
||||||
document.cookie = name + "=" + value + ";" + expires + ";path=/";
|
var cookieString = name + "=" + value + ";" + expires + ";path=/";
|
||||||
|
if (secure) {
|
||||||
|
cookieString += "; secure";
|
||||||
|
}
|
||||||
|
if (sameSite === "Lax" || sameSite === "Strict" || sameSite === "None") {
|
||||||
|
cookieString += "; SameSite=" + sameSite;
|
||||||
|
}
|
||||||
|
document.cookie = cookieString;
|
||||||
},
|
},
|
||||||
getCookie: function (name) {
|
getCookie: function (name) {
|
||||||
name = name + "=";
|
name = name + "=";
|
||||||
@ -198,7 +205,9 @@ Oqtane.Interop = {
|
|||||||
}
|
}
|
||||||
promises.push(new Promise((resolve, reject) => {
|
promises.push(new Promise((resolve, reject) => {
|
||||||
if (loadjs.isDefined(bundles[b])) {
|
if (loadjs.isDefined(bundles[b])) {
|
||||||
|
loadjs.ready(bundles[b], () => {
|
||||||
resolve(true);
|
resolve(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
loadjs(urls, bundles[b], {
|
loadjs(urls, bundles[b], {
|
||||||
@ -206,21 +215,28 @@ Oqtane.Interop = {
|
|||||||
returnPromise: true,
|
returnPromise: true,
|
||||||
before: function (path, element) {
|
before: function (path, element) {
|
||||||
for (let s = 0; s < scripts.length; s++) {
|
for (let s = 0; s < scripts.length; s++) {
|
||||||
if (path === scripts[s].href && scripts[s].integrity !== '') {
|
if (path === scripts[s].href) {
|
||||||
|
if (scripts[s].integrity !== '') {
|
||||||
element.integrity = scripts[s].integrity;
|
element.integrity = scripts[s].integrity;
|
||||||
}
|
}
|
||||||
if (path === scripts[s].href && scripts[s].crossorigin !== '') {
|
if (scripts[s].crossorigin !== '') {
|
||||||
element.crossOrigin = scripts[s].crossorigin;
|
element.crossOrigin = scripts[s].crossorigin;
|
||||||
}
|
}
|
||||||
if (path === scripts[s].href && scripts[s].es6module === true) {
|
if (scripts[s].es6module === true) {
|
||||||
element.type = "module";
|
element.type = "module";
|
||||||
}
|
}
|
||||||
if (path === scripts[s].href && scripts[s].location === 'body') {
|
if (typeof scripts[s].dataAttributes !== "undefined" && scripts[s].dataAttributes !== null) {
|
||||||
|
for (var key in scripts[s].dataAttributes) {
|
||||||
|
element.setAttribute(key, scripts[s].dataAttributes[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scripts[s].location === 'body') {
|
||||||
document.body.appendChild(element);
|
document.body.appendChild(element);
|
||||||
return false; // return false to bypass default DOM insertion mechanism
|
return false; // return false to bypass default DOM insertion mechanism
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.then(function () { resolve(true) })
|
.then(function () { resolve(true) })
|
||||||
.catch(function (pathsNotFound) { reject(false) });
|
.catch(function (pathsNotFound) { reject(false) });
|
||||||
@ -286,41 +302,49 @@ Oqtane.Interop = {
|
|||||||
},
|
},
|
||||||
uploadFiles: function (posturl, folder, id, antiforgerytoken, jwt) {
|
uploadFiles: function (posturl, folder, id, antiforgerytoken, jwt) {
|
||||||
var fileinput = document.getElementById('FileInput_' + id);
|
var fileinput = document.getElementById('FileInput_' + id);
|
||||||
var files = fileinput.files;
|
|
||||||
var progressinfo = document.getElementById('ProgressInfo_' + id);
|
var progressinfo = document.getElementById('ProgressInfo_' + id);
|
||||||
var progressbar = document.getElementById('ProgressBar_' + id);
|
var progressbar = document.getElementById('ProgressBar_' + id);
|
||||||
|
|
||||||
if (progressinfo !== null && progressbar !== null) {
|
if (progressinfo !== null && progressbar !== null) {
|
||||||
progressinfo.setAttribute("style", "display: inline;");
|
progressinfo.setAttribute("style", "display: inline;");
|
||||||
|
progressinfo.innerHTML = '';
|
||||||
progressbar.setAttribute("style", "width: 100%; display: inline;");
|
progressbar.setAttribute("style", "width: 100%; display: inline;");
|
||||||
|
progressbar.value = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var files = fileinput.files;
|
||||||
|
var totalSize = 0;
|
||||||
|
for (var i = 0; i < files.length; i++) {
|
||||||
|
totalSize = totalSize + files[i].size;
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxChunkSizeMB = 1;
|
||||||
|
var bufferChunkSize = maxChunkSizeMB * (1024 * 1024);
|
||||||
|
var uploadedSize = 0;
|
||||||
|
|
||||||
for (var i = 0; i < files.length; i++) {
|
for (var i = 0; i < files.length; i++) {
|
||||||
var FileChunk = [];
|
var fileChunk = [];
|
||||||
var file = files[i];
|
var file = files[i];
|
||||||
var MaxFileSizeMB = 1;
|
var fileStreamPos = 0;
|
||||||
var BufferChunkSize = MaxFileSizeMB * (1024 * 1024);
|
var endPos = bufferChunkSize;
|
||||||
var FileStreamPos = 0;
|
|
||||||
var EndPos = BufferChunkSize;
|
|
||||||
var Size = file.size;
|
|
||||||
|
|
||||||
while (FileStreamPos < Size) {
|
while (fileStreamPos < file.size) {
|
||||||
FileChunk.push(file.slice(FileStreamPos, EndPos));
|
fileChunk.push(file.slice(fileStreamPos, endPos));
|
||||||
FileStreamPos = EndPos;
|
fileStreamPos = endPos;
|
||||||
EndPos = FileStreamPos + BufferChunkSize;
|
endPos = fileStreamPos + bufferChunkSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
var TotalParts = FileChunk.length;
|
var totalParts = fileChunk.length;
|
||||||
var PartCount = 0;
|
var partCount = 0;
|
||||||
|
|
||||||
while (Chunk = FileChunk.shift()) {
|
while (chunk = fileChunk.shift()) {
|
||||||
PartCount++;
|
partCount++;
|
||||||
var FileName = file.name + ".part_" + PartCount.toString().padStart(3, '0') + "_" + TotalParts.toString().padStart(3, '0');
|
var fileName = file.name + ".part_" + partCount.toString().padStart(3, '0') + "_" + totalParts.toString().padStart(3, '0');
|
||||||
|
|
||||||
var data = new FormData();
|
var data = new FormData();
|
||||||
data.append('__RequestVerificationToken', antiforgerytoken);
|
data.append('__RequestVerificationToken', antiforgerytoken);
|
||||||
data.append('folder', folder);
|
data.append('folder', folder);
|
||||||
data.append('formfile', Chunk, FileName);
|
data.append('formfile', chunk, fileName);
|
||||||
var request = new XMLHttpRequest();
|
var request = new XMLHttpRequest();
|
||||||
request.open('POST', posturl, true);
|
request.open('POST', posturl, true);
|
||||||
if (jwt !== "") {
|
if (jwt !== "") {
|
||||||
@ -328,28 +352,36 @@ Oqtane.Interop = {
|
|||||||
request.withCredentials = true;
|
request.withCredentials = true;
|
||||||
}
|
}
|
||||||
request.upload.onloadstart = function (e) {
|
request.upload.onloadstart = function (e) {
|
||||||
if (progressinfo !== null && progressbar !== null) {
|
if (progressinfo !== null && progressbar !== null && progressinfo.innerHTML === '') {
|
||||||
progressinfo.innerHTML = file.name + ' 0%';
|
if (files.length === 1) {
|
||||||
progressbar.value = 0;
|
progressinfo.innerHTML = file.name;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
progressinfo.innerHTML = file.name + ", ...";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
request.upload.onprogress = function (e) {
|
request.upload.onprogress = function (e) {
|
||||||
if (progressinfo !== null && progressbar !== null) {
|
if (progressinfo !== null && progressbar !== null) {
|
||||||
var percent = Math.ceil((e.loaded / e.total) * 100);
|
var percent = Math.ceil(((uploadedSize + e.loaded) / totalSize) * 100);
|
||||||
progressinfo.innerHTML = file.name + '[' + PartCount + '] ' + percent + '%';
|
|
||||||
progressbar.value = (percent / 100);
|
progressbar.value = (percent / 100);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
request.upload.onloadend = function (e) {
|
request.upload.onloadend = function (e) {
|
||||||
if (progressinfo !== null && progressbar !== null) {
|
if (progressinfo !== null && progressbar !== null) {
|
||||||
progressinfo.innerHTML = file.name + ' 100%';
|
uploadedSize = uploadedSize + e.total;
|
||||||
progressbar.value = 1;
|
var percent = Math.ceil((uploadedSize / totalSize) * 100);
|
||||||
|
progressbar.value = (percent / 100);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
request.upload.onerror = function() {
|
request.upload.onerror = function() {
|
||||||
if (progressinfo !== null && progressbar !== null) {
|
if (progressinfo !== null && progressbar !== null) {
|
||||||
|
if (files.length === 1) {
|
||||||
progressinfo.innerHTML = file.name + ' Error: ' + request.statusText;
|
progressinfo.innerHTML = file.name + ' Error: ' + request.statusText;
|
||||||
progressbar.value = 0;
|
}
|
||||||
|
else {
|
||||||
|
progressinfo.innerHTML = ' Error: ' + request.statusText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
request.send(data);
|
request.send(data);
|
||||||
@ -385,11 +417,20 @@ Oqtane.Interop = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
scrollTo: function (top, left, behavior) {
|
scrollTo: function (top, left, behavior) {
|
||||||
|
const modal = document.querySelector('.modal');
|
||||||
|
if (modal) {
|
||||||
|
modal.scrollTo({
|
||||||
|
top: top,
|
||||||
|
left: left,
|
||||||
|
behavior: behavior
|
||||||
|
});
|
||||||
|
} else {
|
||||||
window.scrollTo({
|
window.scrollTo({
|
||||||
top: top,
|
top: top,
|
||||||
left: left,
|
left: left,
|
||||||
behavior: behavior
|
behavior: behavior
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
scrollToId: function (id) {
|
scrollToId: function (id) {
|
||||||
var element = document.getElementById(id);
|
var element = document.getElementById(id);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Client</id>
|
<id>Oqtane.Client</id>
|
||||||
<version>5.2.1</version>
|
<version>6.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,13 +12,15 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</releaseNotes>
|
||||||
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="..\Oqtane.Client\bin\Release\net8.0\Oqtane.Client.dll" target="lib\net8.0" />
|
<file src="..\Oqtane.Client\bin\Release\net9.0\Oqtane.Client.dll" target="lib\net9.0" />
|
||||||
<file src="..\Oqtane.Client\bin\Release\net8.0\Oqtane.Client.pdb" target="lib\net8.0" />
|
<file src="..\Oqtane.Client\bin\Release\net9.0\Oqtane.Client.pdb" target="lib\net9.0" />
|
||||||
<file src="icon.png" target="" />
|
<file src="icon.png" target="" />
|
||||||
|
<file src="readme.md" target="" />
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Framework</id>
|
<id>Oqtane.Framework</id>
|
||||||
<version>5.2.1</version>
|
<version>6.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -11,12 +11,14 @@
|
|||||||
<copyright>.NET Foundation</copyright>
|
<copyright>.NET Foundation</copyright>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v5.2.1/Oqtane.Framework.5.2.1.Upgrade.zip</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v6.0.1/Oqtane.Framework.6.0.1.Upgrade.zip</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</releaseNotes>
|
||||||
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane framework</tags>
|
<tags>oqtane framework</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="icon.png" target="" />
|
<file src="icon.png" target="" />
|
||||||
|
<file src="readme.md" target="" />
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Server</id>
|
<id>Oqtane.Server</id>
|
||||||
<version>5.2.1</version>
|
<version>6.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,13 +12,15 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</releaseNotes>
|
||||||
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="..\Oqtane.Server\bin\Release\net8.0\Oqtane.Server.dll" target="lib\net8.0" />
|
<file src="..\Oqtane.Server\bin\Release\net9.0\Oqtane.Server.dll" target="lib\net9.0" />
|
||||||
<file src="..\Oqtane.Server\bin\Release\net8.0\Oqtane.Server.pdb" target="lib\net8.0" />
|
<file src="..\Oqtane.Server\bin\Release\net9.0\Oqtane.Server.pdb" target="lib\net9.0" />
|
||||||
<file src="icon.png" target="" />
|
<file src="icon.png" target="" />
|
||||||
|
<file src="readme.md" target="" />
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Shared</id>
|
<id>Oqtane.Shared</id>
|
||||||
<version>5.2.1</version>
|
<version>6.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,13 +12,15 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</releaseNotes>
|
||||||
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="..\Oqtane.Shared\bin\Release\net8.0\Oqtane.Shared.dll" target="lib\net8.0" />
|
<file src="..\Oqtane.Shared\bin\Release\net9.0\Oqtane.Shared.dll" target="lib\net9.0" />
|
||||||
<file src="..\Oqtane.Shared\bin\Release\net8.0\Oqtane.Shared.pdb" target="lib\net8.0" />
|
<file src="..\Oqtane.Shared\bin\Release\net9.0\Oqtane.Shared.pdb" target="lib\net9.0" />
|
||||||
<file src="icon.png" target="" />
|
<file src="icon.png" target="" />
|
||||||
|
<file src="readme.md" target="" />
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Updater</id>
|
<id>Oqtane.Updater</id>
|
||||||
<version>5.2.1</version>
|
<version>6.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,12 +12,14 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</releaseNotes>
|
||||||
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="..\Oqtane.Updater\bin\Release\net8.0\publish\*.*" target="lib\net8.0" />
|
<file src="..\Oqtane.Updater\bin\Release\net9.0\publish\*.*" target="lib\net9.0" />
|
||||||
<file src="icon.png" target="" />
|
<file src="icon.png" target="" />
|
||||||
|
<file src="readme.md" target="" />
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
||||||
|
@ -1 +1 @@
|
|||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.2.1.Install.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.0.1.Install.zip" -Force
|
||||||
|
Binary file not shown.
9
Oqtane.Package/readme.md
Normal file
9
Oqtane.Package/readme.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Oqtane Framework
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Oqtane is an open source CMS and Application Framework that provides advanced functionality for developing web, mobile, and desktop applications on .NET. It leverages Blazor to compose a fully dynamic digital experience which can be hosted on Static Blazor, Blazor Server, Blazor WebAssembly, or Blazor Hybrid (via .NET MAUI).
|
||||||
|
|
||||||
|
Oqtane was created by [Shaun Walker](https://www.linkedin.com/in/shaunbrucewalker/) and was inspired by his earlier efforts with DotNetNuke... however Oqtane is a native Blazor application written from the ground up using modern .NET Core technology and a Single Page Application (SPA) architecture. It is a modular application framework offering a fully dynamic page compositing model, multi-site support, designer friendly themes, and extensibility via third party modules.
|
||||||
|
|
||||||
|
More information about Oqtane can be found at: [https://www.oqtane.org](https://www.oqtane.org)
|
@ -6,14 +6,22 @@ nuget.exe pack Oqtane.Client.nuspec
|
|||||||
nuget.exe pack Oqtane.Server.nuspec
|
nuget.exe pack Oqtane.Server.nuspec
|
||||||
nuget.exe pack Oqtane.Shared.nuspec
|
nuget.exe pack Oqtane.Shared.nuspec
|
||||||
nuget.exe pack Oqtane.Framework.nuspec
|
nuget.exe pack Oqtane.Framework.nuspec
|
||||||
del /F/Q/S "..\Oqtane.Server\bin\Release\net8.0\publish" > NUL
|
del /F/Q/S "..\Oqtane.Server\bin\Release\net9.0\publish" > NUL
|
||||||
rmdir /Q/S "..\Oqtane.Server\bin\Release\net8.0\publish"
|
rmdir /Q/S "..\Oqtane.Server\bin\Release\net9.0\publish"
|
||||||
dotnet publish ..\Oqtane.Server\Oqtane.Server.csproj /p:Configuration=Release
|
dotnet publish ..\Oqtane.Server\Oqtane.Server.csproj /p:Configuration=Release
|
||||||
del /F/Q/S "..\Oqtane.Server\bin\Release\net8.0\publish\wwwroot\Content" > NUL
|
del /F/Q/S "..\Oqtane.Server\bin\Release\net9.0\publish\wwwroot\Content" > NUL
|
||||||
rmdir /Q/S "..\Oqtane.Server\bin\Release\net8.0\publish\wwwroot\Content"
|
rmdir /Q/S "..\Oqtane.Server\bin\Release\net9.0\publish\wwwroot\Content"
|
||||||
setlocal ENABLEDELAYEDEXPANSION
|
setlocal ENABLEDELAYEDEXPANSION
|
||||||
|
set retain=Placeholder.txt
|
||||||
|
for /D %%i in ("..\Oqtane.Server\bin\Release\net9.0\publish\wwwroot\_content\*") do (
|
||||||
|
set /A found=0
|
||||||
|
for %%j in (%retain%) do (
|
||||||
|
if "%%~nxi" == "%%j" set /A found=1
|
||||||
|
)
|
||||||
|
if not !found! == 1 rmdir /Q/S "%%i"
|
||||||
|
)
|
||||||
set retain=Oqtane.Modules.Admin.Login,Oqtane.Modules.HtmlText
|
set retain=Oqtane.Modules.Admin.Login,Oqtane.Modules.HtmlText
|
||||||
for /D %%i in ("..\Oqtane.Server\bin\Release\net8.0\publish\wwwroot\Modules\*") do (
|
for /D %%i in ("..\Oqtane.Server\bin\Release\net9.0\publish\wwwroot\Modules\*") do (
|
||||||
set /A found=0
|
set /A found=0
|
||||||
for %%j in (%retain%) do (
|
for %%j in (%retain%) do (
|
||||||
if "%%~nxi" == "%%j" set /A found=1
|
if "%%~nxi" == "%%j" set /A found=1
|
||||||
@ -21,18 +29,18 @@ if "%%~nxi" == "%%j" set /A found=1
|
|||||||
if not !found! == 1 rmdir /Q/S "%%i"
|
if not !found! == 1 rmdir /Q/S "%%i"
|
||||||
)
|
)
|
||||||
set retain=Oqtane.Themes.BlazorTheme,Oqtane.Themes.OqtaneTheme
|
set retain=Oqtane.Themes.BlazorTheme,Oqtane.Themes.OqtaneTheme
|
||||||
for /D %%i in ("..\Oqtane.Server\bin\Release\net8.0\publish\wwwroot\Themes\*") do (
|
for /D %%i in ("..\Oqtane.Server\bin\Release\net9.0\publish\wwwroot\Themes\*") do (
|
||||||
set /A found=0
|
set /A found=0
|
||||||
for %%j in (%retain%) do (
|
for %%j in (%retain%) do (
|
||||||
if "%%~nxi" == "%%j" set /A found=1
|
if "%%~nxi" == "%%j" set /A found=1
|
||||||
)
|
)
|
||||||
if not !found! == 1 rmdir /Q/S "%%i"
|
if not !found! == 1 rmdir /Q/S "%%i"
|
||||||
)
|
)
|
||||||
del "..\Oqtane.Server\bin\Release\net8.0\publish\appsettings.json"
|
del "..\Oqtane.Server\bin\Release\net9.0\publish\appsettings.json"
|
||||||
ren "..\Oqtane.Server\bin\Release\net8.0\publish\appsettings.release.json" "appsettings.json"
|
ren "..\Oqtane.Server\bin\Release\net9.0\publish\appsettings.release.json" "appsettings.json"
|
||||||
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\install.ps1"
|
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\install.ps1"
|
||||||
del "..\Oqtane.Server\bin\Release\net8.0\publish\appsettings.json"
|
del "..\Oqtane.Server\bin\Release\net9.0\publish\appsettings.json"
|
||||||
del "..\Oqtane.Server\bin\Release\net8.0\publish\web.config"
|
del "..\Oqtane.Server\bin\Release\net9.0\publish\web.config"
|
||||||
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\upgrade.ps1"
|
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\upgrade.ps1"
|
||||||
dotnet clean -c Release ..\Oqtane.Updater.sln
|
dotnet clean -c Release ..\Oqtane.Updater.sln
|
||||||
dotnet build -c Release ..\Oqtane.Updater.sln
|
dotnet build -c Release ..\Oqtane.Updater.sln
|
||||||
|
@ -1 +1 @@
|
|||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.2.1.Upgrade.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.0.1.Upgrade.zip" -Force
|
||||||
|
@ -76,6 +76,10 @@
|
|||||||
|
|
||||||
@((MarkupString)_scripts)
|
@((MarkupString)_scripts)
|
||||||
@((MarkupString)_bodyResources)
|
@((MarkupString)_bodyResources)
|
||||||
|
@if (_renderMode == RenderModes.Static)
|
||||||
|
{
|
||||||
|
<page-script src="./js/reload.js"></page-script>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -179,10 +183,6 @@
|
|||||||
ManageScripts(resources, alias);
|
ManageScripts(resources, alias);
|
||||||
|
|
||||||
// generate scripts
|
// generate scripts
|
||||||
if (_renderMode == RenderModes.Interactive && _runtime == Runtimes.Server)
|
|
||||||
{
|
|
||||||
_scripts += CreateReconnectScript();
|
|
||||||
}
|
|
||||||
if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null)
|
if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null)
|
||||||
{
|
{
|
||||||
_scripts += CreatePWAScript(alias, site, route);
|
_scripts += CreatePWAScript(alias, site, route);
|
||||||
@ -192,32 +192,30 @@
|
|||||||
_scripts += CreateScrollPositionScript();
|
_scripts += CreateScrollPositionScript();
|
||||||
}
|
}
|
||||||
|
|
||||||
_headResources += ParseScripts(site.HeadContent);
|
|
||||||
_bodyResources += ParseScripts(site.BodyContent);
|
|
||||||
|
|
||||||
// set culture if not specified
|
// set culture if not specified
|
||||||
string culture = Context.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName];
|
string cultureCookie = Context.Request.Cookies[Shared.CookieRequestCultureProvider.DefaultCookieName];
|
||||||
if (culture == null)
|
if (cultureCookie == null)
|
||||||
{
|
{
|
||||||
// get default language for site
|
// get default language for site
|
||||||
if (site.Languages.Any())
|
if (site.Languages.Any())
|
||||||
{
|
{
|
||||||
// use default language if specified otherwise use first language in collection
|
// use default language if specified otherwise use first language in collection
|
||||||
culture = (site.Languages.Where(l => l.IsDefault).SingleOrDefault() ?? site.Languages.First()).Code;
|
cultureCookie = (site.Languages.Where(l => l.IsDefault).SingleOrDefault() ?? site.Languages.First()).Code;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
culture = LocalizationManager.GetDefaultCulture();
|
// fallback language
|
||||||
|
cultureCookie = LocalizationManager.GetDefaultCulture();
|
||||||
}
|
}
|
||||||
SetLocalizationCookie(culture);
|
// convert language code to culture cookie format (ie. "c=en|uic=en")
|
||||||
|
cultureCookie = Shared.CookieRequestCultureProvider.MakeCookieValue(new Models.RequestCulture(cultureCookie));
|
||||||
|
SetLocalizationCookie(cultureCookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set language for page
|
// set language for page
|
||||||
if (!string.IsNullOrEmpty(culture))
|
if (!string.IsNullOrEmpty(cultureCookie))
|
||||||
{
|
{
|
||||||
// localization cookie value in form of c=en|uic=en
|
_language = Shared.CookieRequestCultureProvider.ParseCookieValue(cultureCookie).Culture.Name;
|
||||||
_language = culture.Split('|')[0];
|
|
||||||
_language = _language.Replace("c=", "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create initial PageState
|
// create initial PageState
|
||||||
@ -494,25 +492,6 @@
|
|||||||
"</script>" + Environment.NewLine;
|
"</script>" + Environment.NewLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CreateReconnectScript()
|
|
||||||
{
|
|
||||||
return Environment.NewLine +
|
|
||||||
"<script>" + Environment.NewLine +
|
|
||||||
" // Interactive Blazor Server Reconnect" + Environment.NewLine +
|
|
||||||
" new MutationObserver((mutations, observer) => {" + Environment.NewLine +
|
|
||||||
" if (document.querySelector('#components-reconnect-modal h5 a')) {" + Environment.NewLine +
|
|
||||||
" async function attemptReload() {" + Environment.NewLine +
|
|
||||||
" await fetch('');" + Environment.NewLine +
|
|
||||||
" location.reload();" + Environment.NewLine +
|
|
||||||
" }" + Environment.NewLine +
|
|
||||||
" observer.disconnect();" + Environment.NewLine +
|
|
||||||
" attemptReload();" + Environment.NewLine +
|
|
||||||
" setInterval(attemptReload, 5000);" + Environment.NewLine +
|
|
||||||
" }" + Environment.NewLine +
|
|
||||||
" }).observe(document.body, { childList: true, subtree: true });" + Environment.NewLine +
|
|
||||||
"</script>" + Environment.NewLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string CreateScrollPositionScript()
|
private string CreateScrollPositionScript()
|
||||||
{
|
{
|
||||||
return Environment.NewLine +
|
return Environment.NewLine +
|
||||||
@ -522,7 +501,7 @@
|
|||||||
" let currentUrl = window.location.pathname;" + Environment.NewLine +
|
" let currentUrl = window.location.pathname;" + Environment.NewLine +
|
||||||
" Blazor.addEventListener('enhancedload', () => {" + Environment.NewLine +
|
" Blazor.addEventListener('enhancedload', () => {" + Environment.NewLine +
|
||||||
" let newUrl = window.location.pathname;" + Environment.NewLine +
|
" let newUrl = window.location.pathname;" + Environment.NewLine +
|
||||||
" if (currentUrl != newUrl) {" + Environment.NewLine +
|
" if (currentUrl !== newUrl || window.location.hash === '#top') {" + Environment.NewLine +
|
||||||
" window.scrollTo({ top: 0, left: 0, behavior: 'instant' });" + Environment.NewLine +
|
" window.scrollTo({ top: 0, left: 0, behavior: 'instant' });" + Environment.NewLine +
|
||||||
" }" + Environment.NewLine +
|
" }" + Environment.NewLine +
|
||||||
" currentUrl = newUrl;" + Environment.NewLine +
|
" currentUrl = newUrl;" + Environment.NewLine +
|
||||||
@ -532,26 +511,10 @@
|
|||||||
"</script>" + Environment.NewLine;
|
"</script>" + Environment.NewLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ParseScripts(string content)
|
|
||||||
{
|
|
||||||
// iterate scripts
|
|
||||||
var scripts = "";
|
|
||||||
if (!string.IsNullOrEmpty(content))
|
|
||||||
{
|
|
||||||
var index = content.IndexOf("<script");
|
|
||||||
while (index >= 0)
|
|
||||||
{
|
|
||||||
scripts += content.Substring(index, content.IndexOf("</script>", index) + 9 - index);
|
|
||||||
index = content.IndexOf("<script", index + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return scripts;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddScript(Resource resource, Alias alias)
|
private void AddScript(Resource resource, Alias alias)
|
||||||
{
|
{
|
||||||
var script = CreateScript(resource, alias);
|
var script = CreateScript(resource, alias);
|
||||||
if (resource.Location == Shared.ResourceLocation.Head)
|
if (resource.Location == Shared.ResourceLocation.Head && !resource.Reload)
|
||||||
{
|
{
|
||||||
if (!_headResources.Contains(script))
|
if (!_headResources.Contains(script))
|
||||||
{
|
{
|
||||||
@ -568,53 +531,46 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private string CreateScript(Resource resource, Alias alias)
|
private string CreateScript(Resource resource, Alias alias)
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(resource.Url))
|
|
||||||
{
|
{
|
||||||
if (!resource.Reload)
|
if (!resource.Reload)
|
||||||
{
|
{
|
||||||
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
|
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
|
||||||
return "<script" +
|
|
||||||
((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
|
var dataAttributes = "";
|
||||||
((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
|
if (resource.DataAttributes != null && resource.DataAttributes.Count > 0)
|
||||||
((resource.ES6Module) ? " type=\"module\"" : "") +
|
|
||||||
" src=\"" + url + "\"></script>"; // src at end of element due to enhanced navigation patch algorithm
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// use custom element which can execute script on every page transition
|
foreach (var attribute in resource.DataAttributes)
|
||||||
@if (string.IsNullOrEmpty(resource.Integrity) && string.IsNullOrEmpty(resource.CrossOrigin))
|
|
||||||
{
|
{
|
||||||
return "<page-script src=\"" + resource.Url + "\"></page-script>";
|
dataAttributes += " " + attribute.Key + "=\"" + attribute.Value + "\"";
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// use modulepreload for external resources
|
|
||||||
return "<link rel=\"modulepreload\" href=\"" + resource.Url + "\" integrity=\"" + resource.Integrity + "\" crossorigin=\"" + resource.CrossOrigin + "\" />\n" +
|
|
||||||
"<page-script src=\"" + resource.Url + "\"></page-script>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// inline script
|
|
||||||
return "<script>" + resource.Content + "</script>";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetLocalizationCookie(string culture)
|
return "<script src=\"" + url + "\"" +
|
||||||
|
((!string.IsNullOrEmpty(resource.Type)) ? " type=\"" + resource.Type + "\"" : "") +
|
||||||
|
((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
|
||||||
|
((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
|
||||||
|
((!string.IsNullOrEmpty(dataAttributes)) ? dataAttributes : "") +
|
||||||
|
"></script>";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "<page-script src=\"" + resource.Url + "\"></page-script>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetLocalizationCookie(string cookieValue)
|
||||||
{
|
{
|
||||||
var cookieOptions = new Microsoft.AspNetCore.Http.CookieOptions
|
var cookieOptions = new Microsoft.AspNetCore.Http.CookieOptions
|
||||||
{
|
{
|
||||||
Expires = DateTimeOffset.UtcNow.AddYears(1),
|
Expires = DateTimeOffset.UtcNow.AddYears(1),
|
||||||
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax, // Set SameSite attribute
|
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax, // Set SameSite attribute
|
||||||
Secure = true, // Ensure the cookie is only sent over HTTPS
|
Secure = true, // Ensure the cookie is only sent over HTTPS
|
||||||
HttpOnly = true // Optional: Helps mitigate XSS attacks
|
HttpOnly = false // cookie is updated using JS Interop in Interactive render mode
|
||||||
};
|
};
|
||||||
|
|
||||||
Context.Response.Cookies.Append(
|
Context.Response.Cookies.Append(
|
||||||
CookieRequestCultureProvider.DefaultCookieName,
|
Shared.CookieRequestCultureProvider.DefaultCookieName,
|
||||||
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
|
cookieValue,
|
||||||
cookieOptions
|
cookieOptions
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -644,6 +600,16 @@
|
|||||||
resources = AddResources(resources, obj.Resources, ResourceLevel.Page, alias, "Themes", type.Namespace, site.RenderMode);
|
resources = AddResources(resources, obj.Resources, ResourceLevel.Page, alias, "Themes", type.Namespace, site.RenderMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// theme settings components are dynamically loaded within the framework Page Management module
|
||||||
|
if (page.Path == "admin/pages" && action.ToLower() == "edit" && theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
||||||
|
{
|
||||||
|
var settingsType = Type.GetType(theme.ThemeSettingsType);
|
||||||
|
if (settingsType != null)
|
||||||
|
{
|
||||||
|
var objSettings = Activator.CreateInstance(settingsType) as IModuleControl;
|
||||||
|
resources = AddResources(resources, objSettings.Resources, ResourceLevel.Module, alias, "Modules", settingsType.Namespace, site.RenderMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (Module module in modules.Where(item => item.PageId == page.PageId || item.ModuleId == moduleid))
|
foreach (Module module in modules.Where(item => item.PageId == page.PageId || item.ModuleId == moduleid))
|
||||||
{
|
{
|
||||||
@ -686,25 +652,49 @@
|
|||||||
|
|
||||||
// ensure component exists and implements IModuleControl
|
// ensure component exists and implements IModuleControl
|
||||||
module.ModuleType = "";
|
module.ModuleType = "";
|
||||||
Type moduletype = Type.GetType(typename, false, true); // case insensitive
|
var moduletype = Type.GetType(typename, false, true); // case insensitive
|
||||||
if (moduletype != null && moduletype.GetInterfaces().Contains(typeof(IModuleControl)))
|
if (moduletype != null && moduletype.GetInterfaces().Contains(typeof(IModuleControl)))
|
||||||
{
|
{
|
||||||
module.ModuleType = Utilities.GetFullTypeName(moduletype.AssemblyQualifiedName); // get actual type name
|
module.ModuleType = Utilities.GetFullTypeName(moduletype.AssemblyQualifiedName); // get actual type name
|
||||||
}
|
}
|
||||||
if (moduletype != null && module.ModuleType != "")
|
if (moduletype != null && module.ModuleType != "")
|
||||||
{
|
{
|
||||||
var obj = Activator.CreateInstance(moduletype) as IModuleControl;
|
var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||||
if (obj != null)
|
if (moduleobject != null)
|
||||||
{
|
{
|
||||||
resources = AddResources(resources, obj.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace, site.RenderMode);
|
resources = AddResources(resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace, site.RenderMode);
|
||||||
|
|
||||||
|
// settings components are dynamically loaded within the framework Settings module
|
||||||
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
||||||
{
|
{
|
||||||
// settings components are embedded within a framework settings module
|
// module settings component
|
||||||
moduletype = Type.GetType(module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action), false, true);
|
var settingsType = "";
|
||||||
|
if (!string.IsNullOrEmpty(module.ModuleDefinition.SettingsType))
|
||||||
|
{
|
||||||
|
// module settings type explicitly declared in IModule interface
|
||||||
|
settingsType = module.ModuleDefinition.SettingsType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// legacy support - module settings type determined by convention
|
||||||
|
settingsType = module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action);
|
||||||
|
}
|
||||||
|
moduletype = Type.GetType(settingsType, false, true);
|
||||||
if (moduletype != null)
|
if (moduletype != null)
|
||||||
{
|
{
|
||||||
obj = Activator.CreateInstance(moduletype) as IModuleControl;
|
moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||||
resources = AddResources(resources, obj.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace, site.RenderMode);
|
resources = AddResources(resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace, site.RenderMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// container settings component
|
||||||
|
if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType))
|
||||||
|
{
|
||||||
|
moduletype = Type.GetType(theme.ContainerSettingsType);
|
||||||
|
if (moduletype != null)
|
||||||
|
{
|
||||||
|
moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||||
|
resources = AddResources(resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace, site.RenderMode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,10 @@ using Oqtane.Infrastructure;
|
|||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using Oqtane.Extensions;
|
using Oqtane.Extensions;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Processing;
|
|
||||||
using SixLabors.ImageSharp.Formats.Png;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
|
using Oqtane.Services;
|
||||||
|
|
||||||
// ReSharper disable StringIndexOfIsCultureSpecific.1
|
// ReSharper disable StringIndexOfIsCultureSpecific.1
|
||||||
|
|
||||||
@ -38,7 +37,9 @@ namespace Oqtane.Controllers
|
|||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
private readonly Alias _alias;
|
private readonly Alias _alias;
|
||||||
private readonly ISettingRepository _settingRepository;
|
private readonly ISettingRepository _settingRepository;
|
||||||
public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ISettingRepository settingRepository, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
|
private readonly IImageService _imageService;
|
||||||
|
|
||||||
|
public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ISettingRepository settingRepository, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager, IImageService imageService)
|
||||||
{
|
{
|
||||||
_environment = environment;
|
_environment = environment;
|
||||||
_files = files;
|
_files = files;
|
||||||
@ -48,6 +49,7 @@ namespace Oqtane.Controllers
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
_alias = tenantManager.GetAlias();
|
_alias = tenantManager.GetAlias();
|
||||||
_settingRepository = settingRepository;
|
_settingRepository = settingRepository;
|
||||||
|
_imageService = imageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/<controller>?folder=x
|
// GET: api/<controller>?folder=x
|
||||||
@ -425,11 +427,11 @@ namespace Oqtane.Controllers
|
|||||||
// POST api/<controller>/upload
|
// POST api/<controller>/upload
|
||||||
[EnableCors(Constants.MauiCorsPolicy)]
|
[EnableCors(Constants.MauiCorsPolicy)]
|
||||||
[HttpPost("upload")]
|
[HttpPost("upload")]
|
||||||
public async Task UploadFile(string folder, IFormFile formfile)
|
public async Task<IActionResult> UploadFile(string folder, IFormFile formfile)
|
||||||
{
|
{
|
||||||
if (formfile == null || formfile.Length <= 0)
|
if (formfile == null || formfile.Length <= 0)
|
||||||
{
|
{
|
||||||
return;
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure filename is valid
|
// ensure filename is valid
|
||||||
@ -437,7 +439,7 @@ namespace Oqtane.Controllers
|
|||||||
if (!formfile.FileName.IsPathOrFileValid() || !formfile.FileName.Contains(token) || !HasValidFileExtension(formfile.FileName.Substring(0, formfile.FileName.IndexOf(token))))
|
if (!formfile.FileName.IsPathOrFileValid() || !formfile.FileName.Contains(token) || !HasValidFileExtension(formfile.FileName.Substring(0, formfile.FileName.IndexOf(token))))
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "File Name Is Invalid Or Contains Invalid Extension {File}", formfile.FileName);
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "File Name Is Invalid Or Contains Invalid Extension {File}", formfile.FileName);
|
||||||
return;
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
string folderPath = "";
|
string folderPath = "";
|
||||||
@ -492,6 +494,8 @@ namespace Oqtane.Controllers
|
|||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Upload Attempt {Folder} {File}", folder, formfile.FileName);
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Upload Attempt {Folder} {File}", folder, formfile.FileName);
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> MergeFile(string folder, string filename)
|
private async Task<string> MergeFile(string folder, string filename)
|
||||||
@ -513,7 +517,7 @@ namespace Oqtane.Controllers
|
|||||||
bool success = true;
|
bool success = true;
|
||||||
using (var stream = new FileStream(Path.Combine(folder, filename + ".tmp"), FileMode.Create))
|
using (var stream = new FileStream(Path.Combine(folder, filename + ".tmp"), FileMode.Create))
|
||||||
{
|
{
|
||||||
foreach (string filepart in fileparts)
|
foreach (string filepart in fileparts.Order())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -679,22 +683,18 @@ namespace Oqtane.Controllers
|
|||||||
var filepath = _files.GetFilePath(file);
|
var filepath = _files.GetFilePath(file);
|
||||||
if (System.IO.File.Exists(filepath))
|
if (System.IO.File.Exists(filepath))
|
||||||
{
|
{
|
||||||
// validation
|
|
||||||
if (!Enum.TryParse(mode, true, out ResizeMode _)) mode = "crop";
|
|
||||||
if (!Enum.TryParse(position, true, out AnchorPositionMode _)) position = "center";
|
|
||||||
if (!Color.TryParseHex("#" + background, out _)) background = "transparent";
|
|
||||||
if (!int.TryParse(rotate, out _)) rotate = "0";
|
|
||||||
rotate = (int.Parse(rotate) < 0 || int.Parse(rotate) > 360) ? "0" : rotate;
|
|
||||||
if (!bool.TryParse(recreate, out _)) recreate = "false";
|
if (!bool.TryParse(recreate, out _)) recreate = "false";
|
||||||
|
|
||||||
string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + ".png");
|
string format = "png";
|
||||||
|
|
||||||
|
string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + "." + format);
|
||||||
if (!System.IO.File.Exists(imagepath) || bool.Parse(recreate))
|
if (!System.IO.File.Exists(imagepath) || bool.Parse(recreate))
|
||||||
{
|
{
|
||||||
// user has edit access to folder or folder supports the image size being created
|
// user has edit access to folder or folder supports the image size being created
|
||||||
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.PermissionList) ||
|
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.PermissionList) ||
|
||||||
(!string.IsNullOrEmpty(file.Folder.ImageSizes) && (file.Folder.ImageSizes == "*" || file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString()))))
|
(!string.IsNullOrEmpty(file.Folder.ImageSizes) && (file.Folder.ImageSizes == "*" || file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString()))))
|
||||||
{
|
{
|
||||||
imagepath = CreateImage(filepath, width, height, mode, position, background, rotate, imagepath);
|
imagepath = _imageService.CreateImage(filepath, width, height, mode, position, background, rotate, format, imagepath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -741,70 +741,6 @@ namespace Oqtane.Controllers
|
|||||||
return System.IO.File.Exists(errorPath) ? PhysicalFile(errorPath, MimeUtilities.GetMimeType(errorPath)) : null;
|
return System.IO.File.Exists(errorPath) ? PhysicalFile(errorPath, MimeUtilities.GetMimeType(errorPath)) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CreateImage(string filepath, int width, int height, string mode, string position, string background, string rotate, string imagepath)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var stream = new FileStream(filepath, FileMode.Open, FileAccess.Read))
|
|
||||||
{
|
|
||||||
stream.Position = 0;
|
|
||||||
using (var image = Image.Load(stream))
|
|
||||||
{
|
|
||||||
int.TryParse(rotate, out int angle);
|
|
||||||
Enum.TryParse(mode, true, out ResizeMode resizemode);
|
|
||||||
Enum.TryParse(position, true, out AnchorPositionMode anchorpositionmode);
|
|
||||||
|
|
||||||
PngEncoder encoder;
|
|
||||||
|
|
||||||
if (background != "transparent")
|
|
||||||
{
|
|
||||||
image.Mutate(x => x
|
|
||||||
.AutoOrient() // auto orient the image
|
|
||||||
.Rotate(angle)
|
|
||||||
.Resize(new ResizeOptions
|
|
||||||
{
|
|
||||||
Mode = resizemode,
|
|
||||||
Position = anchorpositionmode,
|
|
||||||
Size = new Size(width, height),
|
|
||||||
PadColor = Color.ParseHex("#" + background)
|
|
||||||
}));
|
|
||||||
|
|
||||||
encoder = new PngEncoder();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
image.Mutate(x => x
|
|
||||||
.AutoOrient() // auto orient the image
|
|
||||||
.Rotate(angle)
|
|
||||||
.Resize(new ResizeOptions
|
|
||||||
{
|
|
||||||
Mode = resizemode,
|
|
||||||
Position = anchorpositionmode,
|
|
||||||
Size = new Size(width, height)
|
|
||||||
}));
|
|
||||||
|
|
||||||
encoder = new PngEncoder
|
|
||||||
{
|
|
||||||
ColorType = PngColorType.RgbWithAlpha,
|
|
||||||
TransparentColorMode = PngTransparentColorMode.Preserve,
|
|
||||||
BitDepth = PngBitDepth.Bit8,
|
|
||||||
CompressionLevel = PngCompressionLevel.BestSpeed
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
image.Save(imagepath, encoder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, ex, "Error Creating Image For File {FilePath} {Width} {Height} {Mode} {Rotate} {Error}", filepath, width, height, mode, rotate, ex.Message);
|
|
||||||
imagepath = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return imagepath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetFolderPath(string folder)
|
private string GetFolderPath(string folder)
|
||||||
{
|
{
|
||||||
return Utilities.PathCombine(_environment.ContentRootPath, folder);
|
return Utilities.PathCombine(_environment.ContentRootPath, folder);
|
||||||
|
@ -55,6 +55,10 @@ namespace Oqtane.Controllers
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
languages = _languages.GetLanguages(SiteId).ToList();
|
languages = _languages.GetLanguages(SiteId).ToList();
|
||||||
|
foreach (Language language in languages)
|
||||||
|
{
|
||||||
|
language.Name = CultureInfo.GetCultureInfo(language.Code).DisplayName;
|
||||||
|
}
|
||||||
if (!string.IsNullOrEmpty(packagename))
|
if (!string.IsNullOrEmpty(packagename))
|
||||||
{
|
{
|
||||||
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"{packagename}*{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
|
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"{packagename}*{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
|
||||||
@ -85,6 +89,7 @@ namespace Oqtane.Controllers
|
|||||||
var language = _languages.GetLanguage(id);
|
var language = _languages.GetLanguage(id);
|
||||||
if (language != null && language.SiteId == _alias.SiteId)
|
if (language != null && language.SiteId == _alias.SiteId)
|
||||||
{
|
{
|
||||||
|
language.Name = CultureInfo.GetCultureInfo(language.Code).DisplayName;
|
||||||
return language;
|
return language;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -351,9 +351,9 @@ namespace Oqtane.Controllers
|
|||||||
return new Dictionary<string, object>()
|
return new Dictionary<string, object>()
|
||||||
{
|
{
|
||||||
{ "FrameworkVersion", Constants.Version },
|
{ "FrameworkVersion", Constants.Version },
|
||||||
{ "ClientReference", $"<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll</HintPath></Reference>" },
|
{ "ClientReference", $"<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net9.0\\Oqtane.Client.dll</HintPath></Reference>" },
|
||||||
{ "ServerReference", $"<Reference Include=\"Oqtane.Server\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Server.dll</HintPath></Reference>" },
|
{ "ServerReference", $"<Reference Include=\"Oqtane.Server\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net9.0\\Oqtane.Server.dll</HintPath></Reference>" },
|
||||||
{ "SharedReference", $"<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll</HintPath></Reference>" },
|
{ "SharedReference", $"<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net9.0\\Oqtane.Shared.dll</HintPath></Reference>" },
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,6 @@ using Oqtane.Infrastructure;
|
|||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using Oqtane.Security;
|
using Oqtane.Security;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection.Metadata;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
@ -187,7 +183,7 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && notification.NotificationId == id && _notifications.GetNotification(notification.NotificationId, false) != null && (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)))
|
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && notification.NotificationId == id && _notifications.GetNotification(notification.NotificationId, false) != null && (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)))
|
||||||
{
|
{
|
||||||
if (!User.IsInRole(RoleNames.Admin))
|
if (!User.IsInRole(RoleNames.Admin) && notification.FromUserId != null)
|
||||||
{
|
{
|
||||||
// content must be HTML encoded for non-admins to prevent HTML injection
|
// content must be HTML encoded for non-admins to prevent HTML injection
|
||||||
notification.Subject = WebUtility.HtmlEncode(notification.Subject);
|
notification.Subject = WebUtility.HtmlEncode(notification.Subject);
|
||||||
@ -227,7 +223,7 @@ namespace Oqtane.Controllers
|
|||||||
|
|
||||||
private bool IsAuthorized(int? userid)
|
private bool IsAuthorized(int? userid)
|
||||||
{
|
{
|
||||||
bool authorized = true;
|
bool authorized = false;
|
||||||
if (userid != null)
|
if (userid != null)
|
||||||
{
|
{
|
||||||
authorized = (_userPermissions.GetUser(User).UserId == userid);
|
authorized = (_userPermissions.GetUser(User).UserId == userid);
|
||||||
|
@ -9,7 +9,7 @@ using System.Net;
|
|||||||
using Oqtane.Enums;
|
using Oqtane.Enums;
|
||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using System.IO;
|
using System;
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
|
@ -189,7 +189,7 @@ namespace Oqtane.Controllers
|
|||||||
public void Delete(string entityName, int entityId, string settingName)
|
public void Delete(string entityName, int entityId, string settingName)
|
||||||
{
|
{
|
||||||
Setting setting = _settings.GetSetting(entityName, entityId, settingName);
|
Setting setting = _settings.GetSetting(entityName, entityId, settingName);
|
||||||
if (IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit))
|
if (setting != null && IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
_settings.DeleteSetting(setting.EntityName, setting.SettingId);
|
_settings.DeleteSetting(setting.EntityName, setting.SettingId);
|
||||||
AddSyncEvent(setting.EntityName, setting.EntityId, setting.SettingId, SyncEventActions.Delete);
|
AddSyncEvent(setting.EntityName, setting.EntityId, setting.SettingId, SyncEventActions.Delete);
|
||||||
@ -199,7 +199,7 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
if (entityName != EntityNames.Visitor)
|
if (entityName != EntityNames.Visitor)
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Setting {Setting}", setting);
|
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "Setting Does Not Exist Or User Not Authorized To Delete Setting For Entity {EntityName} Id {EntityId} Name {SettingName}", entityName, entityId, settingName);
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,11 +269,7 @@ namespace Oqtane.Controllers
|
|||||||
authorized = _userPermissions.IsAuthorized(User, _alias.SiteId, entityName, entityId, permissionName);
|
authorized = _userPermissions.IsAuthorized(User, _alias.SiteId, entityName, entityId, permissionName);
|
||||||
break;
|
break;
|
||||||
case EntityNames.User:
|
case EntityNames.User:
|
||||||
authorized = true;
|
|
||||||
if (permissionName == PermissionNames.Edit)
|
|
||||||
{
|
|
||||||
authorized = _userPermissions.IsAuthorized(User, _alias.SiteId, entityName, -1, PermissionNames.Write, RoleNames.Admin) || (_userPermissions.GetUser(User).UserId == entityId);
|
authorized = _userPermissions.IsAuthorized(User, _alias.SiteId, entityName, -1, PermissionNames.Write, RoleNames.Admin) || (_userPermissions.GetUser(User).UserId == entityId);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case EntityNames.Visitor:
|
case EntityNames.Visitor:
|
||||||
authorized = User.IsInRole(RoleNames.Admin);
|
authorized = User.IsInRole(RoleNames.Admin);
|
||||||
@ -319,7 +315,7 @@ namespace Oqtane.Controllers
|
|||||||
filter = !_userPermissions.IsAuthorized(User, _alias.SiteId, entityName, entityId, PermissionNames.Edit);
|
filter = !_userPermissions.IsAuthorized(User, _alias.SiteId, entityName, entityId, PermissionNames.Edit);
|
||||||
break;
|
break;
|
||||||
case EntityNames.User:
|
case EntityNames.User:
|
||||||
filter = !User.IsInRole(RoleNames.Admin) && _userPermissions.GetUser(User).UserId != entityId;
|
filter = !_userPermissions.IsAuthorized(User, _alias.SiteId, entityName, -1, PermissionNames.Write, RoleNames.Admin) && _userPermissions.GetUser(User).UserId != entityId;
|
||||||
break;
|
break;
|
||||||
case EntityNames.Visitor:
|
case EntityNames.Visitor:
|
||||||
if (!User.IsInRole(RoleNames.Admin))
|
if (!User.IsInRole(RoleNames.Admin))
|
||||||
|
@ -267,8 +267,8 @@ namespace Oqtane.Controllers
|
|||||||
return new Dictionary<string, object>()
|
return new Dictionary<string, object>()
|
||||||
{
|
{
|
||||||
{ "FrameworkVersion", Constants.Version },
|
{ "FrameworkVersion", Constants.Version },
|
||||||
{ "ClientReference", $"<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll</HintPath></Reference>" },
|
{ "ClientReference", $"<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net9.0\\Oqtane.Client.dll</HintPath></Reference>" },
|
||||||
{ "SharedReference", $"<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll</HintPath></Reference>" },
|
{ "SharedReference", $"<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net9.0\\Oqtane.Shared.dll</HintPath></Reference>" },
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ using System.Threading.Tasks;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using System;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Oqtane.Enums;
|
using Oqtane.Enums;
|
||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
@ -28,9 +27,11 @@ namespace Oqtane.Controllers
|
|||||||
private readonly IUserPermissions _userPermissions;
|
private readonly IUserPermissions _userPermissions;
|
||||||
private readonly IJwtManager _jwtManager;
|
private readonly IJwtManager _jwtManager;
|
||||||
private readonly IFileRepository _files;
|
private readonly IFileRepository _files;
|
||||||
|
private readonly ISettingRepository _settings;
|
||||||
|
private readonly ISyncManager _syncManager;
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
|
|
||||||
public UserController(IUserRepository users, ITenantManager tenantManager, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, IFileRepository files, ILogManager logger)
|
public UserController(IUserRepository users, ITenantManager tenantManager, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, IFileRepository files, ISettingRepository settings, ISyncManager syncManager, ILogManager logger)
|
||||||
{
|
{
|
||||||
_users = users;
|
_users = users;
|
||||||
_tenantManager = tenantManager;
|
_tenantManager = tenantManager;
|
||||||
@ -39,6 +40,8 @@ namespace Oqtane.Controllers
|
|||||||
_userPermissions = userPermissions;
|
_userPermissions = userPermissions;
|
||||||
_jwtManager = jwtManager;
|
_jwtManager = jwtManager;
|
||||||
_files = files;
|
_files = files;
|
||||||
|
_settings = settings;
|
||||||
|
_syncManager = syncManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,31 +113,45 @@ namespace Oqtane.Controllers
|
|||||||
|
|
||||||
private User Filter(User user)
|
private User Filter(User user)
|
||||||
{
|
{
|
||||||
|
// clone object to avoid mutating cache
|
||||||
|
User filtered = null;
|
||||||
|
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
user.Password = "";
|
filtered = new User();
|
||||||
user.IsAuthenticated = false;
|
|
||||||
user.TwoFactorCode = "";
|
|
||||||
user.TwoFactorExpiry = null;
|
|
||||||
|
|
||||||
if (!_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) && User.Identity.Name?.ToLower() != user.Username.ToLower())
|
// public properties
|
||||||
|
filtered.SiteId = user.SiteId;
|
||||||
|
filtered.UserId = user.UserId;
|
||||||
|
filtered.Username = user.Username;
|
||||||
|
filtered.DisplayName = user.DisplayName;
|
||||||
|
|
||||||
|
// restricted properties
|
||||||
|
filtered.Password = "";
|
||||||
|
filtered.TwoFactorCode = "";
|
||||||
|
filtered.SecurityStamp = "";
|
||||||
|
|
||||||
|
// include private properties if authenticated user is accessing their own user account os is an administrator
|
||||||
|
if (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) || _userPermissions.GetUser(User).UserId == user.UserId)
|
||||||
{
|
{
|
||||||
user.Email = "";
|
filtered.Email = user.Email;
|
||||||
user.PhotoFileId = null;
|
filtered.PhotoFileId = user.PhotoFileId;
|
||||||
user.LastLoginOn = DateTime.MinValue;
|
filtered.LastLoginOn = user.LastLoginOn;
|
||||||
user.LastIPAddress = "";
|
filtered.LastIPAddress = user.LastIPAddress;
|
||||||
user.Roles = "";
|
filtered.TwoFactorRequired = user.TwoFactorRequired;
|
||||||
user.CreatedBy = "";
|
filtered.Roles = user.Roles;
|
||||||
user.CreatedOn = DateTime.MinValue;
|
filtered.CreatedBy = user.CreatedBy;
|
||||||
user.ModifiedBy = "";
|
filtered.CreatedOn = user.CreatedOn;
|
||||||
user.ModifiedOn = DateTime.MinValue;
|
filtered.ModifiedBy = user.ModifiedBy;
|
||||||
user.DeletedBy = "";
|
filtered.ModifiedOn = user.ModifiedOn;
|
||||||
user.DeletedOn = DateTime.MinValue;
|
filtered.DeletedBy = user.DeletedBy;
|
||||||
user.IsDeleted = false;
|
filtered.DeletedOn = user.DeletedOn;
|
||||||
user.TwoFactorRequired = false;
|
filtered.IsDeleted = user.IsDeleted;
|
||||||
|
filtered.Settings = user.Settings; // include all settings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return user;
|
|
||||||
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST api/<controller>
|
// POST api/<controller>
|
||||||
@ -147,11 +164,13 @@ namespace Oqtane.Controllers
|
|||||||
if (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin))
|
if (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin))
|
||||||
{
|
{
|
||||||
user.EmailConfirmed = true;
|
user.EmailConfirmed = true;
|
||||||
|
user.IsAuthenticated = true;
|
||||||
allowregistration = true;
|
allowregistration = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
user.EmailConfirmed = false;
|
user.EmailConfirmed = false;
|
||||||
|
user.IsAuthenticated = false;
|
||||||
allowregistration = _sites.GetSite(user.SiteId).AllowRegistration;
|
allowregistration = _sites.GetSite(user.SiteId).AllowRegistration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,10 +251,28 @@ namespace Oqtane.Controllers
|
|||||||
[HttpPost("logout")]
|
[HttpPost("logout")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task Logout([FromBody] User user)
|
public async Task Logout([FromBody] User user)
|
||||||
|
{
|
||||||
|
if (_userPermissions.GetUser(User).UserId == user.UserId)
|
||||||
{
|
{
|
||||||
await HttpContext.SignOutAsync(Constants.AuthenticationScheme);
|
await HttpContext.SignOutAsync(Constants.AuthenticationScheme);
|
||||||
|
_syncManager.AddSyncEvent(_tenantManager.GetAlias(), EntityNames.User, user.UserId, "Logout");
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout {Username}", (user != null) ? user.Username : "");
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout {Username}", (user != null) ? user.Username : "");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/<controller>/logout
|
||||||
|
[HttpPost("logouteverywhere")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task LogoutEverywhere([FromBody] User user)
|
||||||
|
{
|
||||||
|
if (_userPermissions.GetUser(User).UserId == user.UserId)
|
||||||
|
{
|
||||||
|
await _userManager.LogoutUserEverywhere(user);
|
||||||
|
await HttpContext.SignOutAsync(Constants.AuthenticationScheme);
|
||||||
|
_syncManager.AddSyncEvent(_tenantManager.GetAlias(), EntityNames.User, user.UserId, "Logout");
|
||||||
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout Everywhere {Username}", (user != null) ? user.Username : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// POST api/<controller>/verify
|
// POST api/<controller>/verify
|
||||||
[HttpPost("verify")]
|
[HttpPost("verify")]
|
||||||
@ -301,6 +338,13 @@ namespace Oqtane.Controllers
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET api/<controller>/validate/x
|
||||||
|
[HttpGet("validateuser")]
|
||||||
|
public async Task<UserValidateResult> ValidateUser(string username, string email, string password)
|
||||||
|
{
|
||||||
|
return await _userManager.ValidateUser(username, email, password);
|
||||||
|
}
|
||||||
|
|
||||||
// GET api/<controller>/validate/x
|
// GET api/<controller>/validate/x
|
||||||
[HttpGet("validate/{password}")]
|
[HttpGet("validate/{password}")]
|
||||||
public async Task<bool> Validate(string password)
|
public async Task<bool> Validate(string password)
|
||||||
@ -355,6 +399,7 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
if (roles != "") roles = ";" + roles;
|
if (roles != "") roles = ";" + roles;
|
||||||
user.Roles = roles;
|
user.Roles = roles;
|
||||||
|
user.SecurityStamp = User.SecurityStamp();
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Oqtane.Security;
|
using Oqtane.Security;
|
||||||
using System;
|
using System;
|
||||||
using Oqtane.Modules.Admin.Roles;
|
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
@ -40,12 +39,14 @@ namespace Oqtane.Controllers
|
|||||||
public IEnumerable<UserRole> Get(string siteid, string userid = null, string rolename = null)
|
public IEnumerable<UserRole> Get(string siteid, string userid = null, string rolename = null)
|
||||||
{
|
{
|
||||||
int SiteId;
|
int SiteId;
|
||||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId && (userid != null || rolename != null))
|
int UserId = -1;
|
||||||
|
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId && (userid != null && int.TryParse(userid, out UserId) || rolename != null))
|
||||||
|
{
|
||||||
|
if (IsAuthorized(UserId, rolename))
|
||||||
{
|
{
|
||||||
var userroles = _userRoles.GetUserRoles(SiteId).ToList();
|
var userroles = _userRoles.GetUserRoles(SiteId).ToList();
|
||||||
if (userid != null)
|
if (UserId != -1)
|
||||||
{
|
{
|
||||||
int UserId = int.TryParse(userid, out UserId) ? UserId : -1;
|
|
||||||
userroles = userroles.Where(item => item.UserId == UserId).ToList();
|
userroles = userroles.Where(item => item.UserId == UserId).ToList();
|
||||||
}
|
}
|
||||||
if (rolename != null)
|
if (rolename != null)
|
||||||
@ -58,6 +59,14 @@ namespace Oqtane.Controllers
|
|||||||
userroles[i] = Filter(userroles[i], user.UserId);
|
userroles[i] = Filter(userroles[i], user.UserId);
|
||||||
}
|
}
|
||||||
return userroles.OrderBy(u => u.User.DisplayName);
|
return userroles.OrderBy(u => u.User.DisplayName);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UserRole Get Attempt For Site {SiteId} User {UserId} Role {RoleName}", siteid, userid, rolename);
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -73,7 +82,7 @@ namespace Oqtane.Controllers
|
|||||||
public UserRole Get(int id)
|
public UserRole Get(int id)
|
||||||
{
|
{
|
||||||
var userrole = _userRoles.GetUserRole(id);
|
var userrole = _userRoles.GetUserRole(id);
|
||||||
if (userrole != null && SiteValid(userrole.Role.SiteId))
|
if (userrole != null && SiteValid(userrole.Role.SiteId) && IsAuthorized(userrole.UserId, userrole.Role.Name))
|
||||||
{
|
{
|
||||||
return Filter(userrole, _userPermissions.GetUser().UserId);
|
return Filter(userrole, _userPermissions.GetUser().UserId);
|
||||||
}
|
}
|
||||||
@ -92,33 +101,57 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsAuthorized(int userId, string roleName)
|
||||||
|
{
|
||||||
|
bool authorized = true;
|
||||||
|
if (userId != -1)
|
||||||
|
{
|
||||||
|
authorized = _userPermissions.GetUser(User).UserId == userId;
|
||||||
|
}
|
||||||
|
if (authorized && !string.IsNullOrEmpty(roleName))
|
||||||
|
{
|
||||||
|
authorized = User.IsInRole(roleName);
|
||||||
|
}
|
||||||
|
return authorized;
|
||||||
|
}
|
||||||
|
|
||||||
private UserRole Filter(UserRole userrole, int userid)
|
private UserRole Filter(UserRole userrole, int userid)
|
||||||
{
|
{
|
||||||
|
// clone object to avoid mutating cache
|
||||||
|
UserRole filtered = null;
|
||||||
|
|
||||||
if (userrole != null)
|
if (userrole != null)
|
||||||
{
|
{
|
||||||
userrole.User.Password = "";
|
filtered = new UserRole();
|
||||||
userrole.User.IsAuthenticated = false;
|
|
||||||
userrole.User.TwoFactorCode = "";
|
|
||||||
userrole.User.TwoFactorExpiry = null;
|
|
||||||
|
|
||||||
if (!_userPermissions.IsAuthorized(User, userrole.User.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) && userid != userrole.User.UserId)
|
// public properties
|
||||||
|
filtered.UserRoleId = userrole.UserRoleId;
|
||||||
|
filtered.UserId = userrole.UserId;
|
||||||
|
filtered.RoleId = userrole.RoleId;
|
||||||
|
|
||||||
|
filtered.User = new User();
|
||||||
|
filtered.User.SiteId = userrole.User.SiteId;
|
||||||
|
filtered.User.UserId = userrole.User.UserId;
|
||||||
|
filtered.User.Username = userrole.User.Username;
|
||||||
|
filtered.User.DisplayName = userrole.User.DisplayName;
|
||||||
|
|
||||||
|
filtered.Role = new Role();
|
||||||
|
filtered.Role.SiteId = userrole.Role.SiteId;
|
||||||
|
filtered.Role.RoleId = userrole.Role.RoleId;
|
||||||
|
filtered.Role.Name = userrole.Role.Name;
|
||||||
|
|
||||||
|
// include private properties if administrator
|
||||||
|
if (_userPermissions.IsAuthorized(User, filtered.User.SiteId, EntityNames.UserRole, -1, PermissionNames.Write, RoleNames.Admin))
|
||||||
{
|
{
|
||||||
userrole.User.Email = "";
|
filtered.User.Email = userrole.User.Email;
|
||||||
userrole.User.PhotoFileId = null;
|
filtered.User.PhotoFileId = userrole.User.PhotoFileId;
|
||||||
userrole.User.LastLoginOn = DateTime.MinValue;
|
filtered.User.LastLoginOn = userrole.User.LastLoginOn;
|
||||||
userrole.User.LastIPAddress = "";
|
filtered.User.LastIPAddress = userrole.User.LastIPAddress;
|
||||||
userrole.User.Roles = "";
|
filtered.User.CreatedOn = userrole.User.CreatedOn;
|
||||||
userrole.User.CreatedBy = "";
|
|
||||||
userrole.User.CreatedOn = DateTime.MinValue;
|
|
||||||
userrole.User.ModifiedBy = "";
|
|
||||||
userrole.User.ModifiedOn = DateTime.MinValue;
|
|
||||||
userrole.User.DeletedBy = "";
|
|
||||||
userrole.User.DeletedOn = DateTime.MinValue;
|
|
||||||
userrole.User.IsDeleted = false;
|
|
||||||
userrole.User.TwoFactorRequired = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return userrole;
|
|
||||||
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST api/<controller>
|
// POST api/<controller>
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Oqtane.Databases.Interfaces;
|
using Oqtane.Databases.Interfaces;
|
||||||
using Oqtane.Interfaces;
|
|
||||||
|
|
||||||
namespace Oqtane.Repository.Databases.Interfaces
|
namespace Oqtane.Repository.Databases.Interfaces
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Oqtane.Models;
|
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
|
||||||
namespace Oqtane.Extensions
|
namespace Oqtane.Extensions
|
||||||
@ -41,9 +40,9 @@ namespace Oqtane.Extensions
|
|||||||
|
|
||||||
public static string SiteKey(this ClaimsPrincipal claimsPrincipal)
|
public static string SiteKey(this ClaimsPrincipal claimsPrincipal)
|
||||||
{
|
{
|
||||||
if (claimsPrincipal.HasClaim(item => item.Type == "sitekey"))
|
if (claimsPrincipal.HasClaim(item => item.Type == Constants.SiteKeyClaimType))
|
||||||
{
|
{
|
||||||
return claimsPrincipal.Claims.FirstOrDefault(item => item.Type == "sitekey").Value;
|
return claimsPrincipal.Claims.FirstOrDefault(item => item.Type == Constants.SiteKeyClaimType).Value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -71,6 +70,18 @@ namespace Oqtane.Extensions
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string SecurityStamp(this ClaimsPrincipal claimsPrincipal)
|
||||||
|
{
|
||||||
|
if (claimsPrincipal.HasClaim(item => item.Type == Constants.SecurityStampClaimType))
|
||||||
|
{
|
||||||
|
return claimsPrincipal.Claims.FirstOrDefault(item => item.Type == Constants.SecurityStampClaimType).Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsOnlyInRole(this ClaimsPrincipal claimsPrincipal, string role)
|
public static bool IsOnlyInRole(this ClaimsPrincipal claimsPrincipal, string role)
|
||||||
{
|
{
|
||||||
var identity = claimsPrincipal.Identities.FirstOrDefault(item => item.AuthenticationType == Constants.AuthenticationScheme);
|
var identity = claimsPrincipal.Identities.FirstOrDefault(item => item.AuthenticationType == Constants.AuthenticationScheme);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
using Oqtane.Databases.Interfaces;
|
using Oqtane.Databases.Interfaces;
|
||||||
// ReSharper disable ConvertToUsingDeclaration
|
// ReSharper disable ConvertToUsingDeclaration
|
||||||
|
|
||||||
@ -9,7 +10,8 @@ namespace Oqtane.Extensions
|
|||||||
{
|
{
|
||||||
public static DbContextOptionsBuilder UseOqtaneDatabase([NotNull] this DbContextOptionsBuilder optionsBuilder, IDatabase database, string connectionString)
|
public static DbContextOptionsBuilder UseOqtaneDatabase([NotNull] this DbContextOptionsBuilder optionsBuilder, IDatabase database, string connectionString)
|
||||||
{
|
{
|
||||||
database.UseDatabase(optionsBuilder, connectionString);
|
database.UseDatabase(optionsBuilder, connectionString)
|
||||||
|
.ConfigureWarnings(warnings => warnings.Log(RelationalEventId.PendingModelChangesWarning));
|
||||||
|
|
||||||
return optionsBuilder;
|
return optionsBuilder;
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
services.AddScoped<ISearchResultsService, SearchResultsService>();
|
services.AddScoped<ISearchResultsService, SearchResultsService>();
|
||||||
services.AddScoped<ISearchService, SearchService>();
|
services.AddScoped<ISearchService, SearchService>();
|
||||||
services.AddScoped<ISearchProvider, DatabaseSearchProvider>();
|
services.AddScoped<ISearchProvider, DatabaseSearchProvider>();
|
||||||
|
services.AddScoped<IImageService, ImageService>();
|
||||||
|
|
||||||
// providers
|
// providers
|
||||||
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
||||||
@ -112,8 +113,11 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
|
|
||||||
internal static IServiceCollection AddOqtaneTransientServices(this IServiceCollection services)
|
internal static IServiceCollection AddOqtaneTransientServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
// repositories
|
// services
|
||||||
services.AddTransient<ISiteService, ServerSiteService>();
|
services.AddTransient<ISiteService, ServerSiteService>();
|
||||||
|
services.AddTransient<ILocalizationCookieService, ServerLocalizationCookieService>();
|
||||||
|
|
||||||
|
// repositories
|
||||||
services.AddTransient<IModuleDefinitionRepository, ModuleDefinitionRepository>();
|
services.AddTransient<IModuleDefinitionRepository, ModuleDefinitionRepository>();
|
||||||
services.AddTransient<IThemeRepository, ThemeRepository>();
|
services.AddTransient<IThemeRepository, ThemeRepository>();
|
||||||
services.AddTransient<IAliasRepository, AliasRepository>();
|
services.AddTransient<IAliasRepository, AliasRepository>();
|
||||||
@ -129,7 +133,6 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
services.AddTransient<IPermissionRepository, PermissionRepository>();
|
services.AddTransient<IPermissionRepository, PermissionRepository>();
|
||||||
services.AddTransient<ISettingRepository, SettingRepository>();
|
services.AddTransient<ISettingRepository, SettingRepository>();
|
||||||
services.AddTransient<ILogRepository, LogRepository>();
|
services.AddTransient<ILogRepository, LogRepository>();
|
||||||
services.AddTransient<ILocalizationManager, LocalizationManager>();
|
|
||||||
services.AddTransient<IJobRepository, JobRepository>();
|
services.AddTransient<IJobRepository, JobRepository>();
|
||||||
services.AddTransient<IJobLogRepository, JobLogRepository>();
|
services.AddTransient<IJobLogRepository, JobLogRepository>();
|
||||||
services.AddTransient<INotificationRepository, NotificationRepository>();
|
services.AddTransient<INotificationRepository, NotificationRepository>();
|
||||||
@ -152,12 +155,12 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
services.AddTransient<ILogManager, LogManager>();
|
services.AddTransient<ILogManager, LogManager>();
|
||||||
services.AddTransient<IUpgradeManager, UpgradeManager>();
|
services.AddTransient<IUpgradeManager, UpgradeManager>();
|
||||||
services.AddTransient<IUserManager, UserManager>();
|
services.AddTransient<IUserManager, UserManager>();
|
||||||
|
services.AddTransient<ILocalizationManager, LocalizationManager>();
|
||||||
// obsolete - replaced by ITenantManager
|
|
||||||
services.AddTransient<ITenantResolver, TenantResolver>();
|
|
||||||
|
|
||||||
services.AddTransient<ITokenReplace, TokenReplace>();
|
services.AddTransient<ITokenReplace, TokenReplace>();
|
||||||
|
|
||||||
|
// obsolete
|
||||||
|
services.AddTransient<ITenantResolver, TenantResolver>(); // replaced by ITenantManager
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,6 +172,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
options.Cookie.HttpOnly = true;
|
options.Cookie.HttpOnly = true;
|
||||||
options.Cookie.SameSite = SameSiteMode.Lax;
|
options.Cookie.SameSite = SameSiteMode.Lax;
|
||||||
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
|
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
|
||||||
|
options.LoginPath = "/login"; // overrides .NET Identity default of /Account/Login
|
||||||
options.Events.OnRedirectToLogin = context =>
|
options.Events.OnRedirectToLogin = context =>
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user