Compare commits
254 Commits
Author | SHA1 | Date | |
---|---|---|---|
a16bc5db28 | |||
51657338f5 | |||
0fe3ea25af | |||
806daaf7c9 | |||
21ff4a83b5 | |||
ecc9aa40d7 | |||
c34ca2a59b | |||
dde7094fe3 | |||
105afdfefc | |||
4c254a8686 | |||
33ca203e57 | |||
2ff4133cd4 | |||
49ad85713e | |||
1978bf151f | |||
506378de82 | |||
53ead7a03f | |||
ab979fd63c | |||
1e84a2238b | |||
5618adf86c | |||
345b0bc95f | |||
b1d6c35e99 | |||
d767f1a101 | |||
c15f2b9a12 | |||
a21a53662b | |||
ebb5340019 | |||
6108bd214e | |||
eed27e101a | |||
2767680bed | |||
4080e30b6f | |||
e89257be62 | |||
d3c40a7e8b | |||
60657d5d25 | |||
71d5ae0e68 | |||
46b8d202c7 | |||
bc193a6470 | |||
577528fa0a | |||
35e78ea633 | |||
d5d4f85003 | |||
c6a49a6f5d | |||
a3ff9373a2 | |||
e6d2e74b17 | |||
e8464206e7 | |||
b6c4934123 | |||
8ee83f738b | |||
c359300375 | |||
eb3361fa07 | |||
1b53da6749 | |||
c701895e29 | |||
e81222821e | |||
cbca8c9e93 | |||
92c4edacf2 | |||
e4c648ee92 | |||
69f6586aa9 | |||
391713b84d | |||
8c6a25e4b4 | |||
250701aff0 | |||
2dfd1bd5a2 | |||
1c7380d4cf | |||
68d9ac88b3 | |||
f6b3874668 | |||
c616878a64 | |||
ad485a68ce | |||
2ebba3b8e7 | |||
4117e6e1c5 | |||
423ee04879 | |||
1625e3ba6c | |||
5a71ab3c20 | |||
b5833bf556 | |||
6c31765965 | |||
6dc1d42d90 | |||
e273a954e6 | |||
a602a942c4 | |||
dd32b33621 | |||
355d0405f4 | |||
c1e1595d58 | |||
ef476ac9b3 | |||
a7aaa7b18b | |||
3abfbab5d1 | |||
b158474e21 | |||
cfbcc41543 | |||
fec0a02b1c | |||
3d93ba1215 | |||
042083c0e7 | |||
e0bb7b7faf | |||
acc4099ac8 | |||
683ad8959a | |||
a559c771cf | |||
8ba0ebf955 | |||
d83ec1827a | |||
314e49f5e1 | |||
412b139796 | |||
9b29487934 | |||
95213e41c4 | |||
644ddfd5e1 | |||
68dd9900c4 | |||
6b100cf70b | |||
6f33e5e8a0 | |||
268e0e72a3 | |||
5380b12294 | |||
2ba1a95c8d | |||
1ad0ee4a71 | |||
fc12903cfd | |||
640d22484d | |||
34dc4d64e6 | |||
bbb547efb6 | |||
5b3640e23d | |||
0fbbe244d8 | |||
57def7da0c | |||
0fcf1c2732 | |||
c15b6cdf79 | |||
06e25e04f8 | |||
f8e04656cd | |||
1c8debd894 | |||
58d8fcd074 | |||
a70f1ee1e0 | |||
271ed3cbe2 | |||
8ddaf57e17 | |||
4f1ead116f | |||
3194c5b600 | |||
717f1a9b76 | |||
b7675a21eb | |||
b0d4c0d578 | |||
b7a1d2df75 | |||
5d31d33804 | |||
a97af42e4b | |||
17c6797afb | |||
c8129607e8 | |||
c2dce38bb1 | |||
8b0b7492f5 | |||
a25cfd87cc | |||
f9432acf1b | |||
c6c468c986 | |||
b92a888583 | |||
692b4b33fb | |||
79f427e10a | |||
340b3e7fe8 | |||
50a44c9416 | |||
3c41493d8e | |||
4566ea436c | |||
499bf3bc28 | |||
489a321763 | |||
9d86d923aa | |||
454529bd6a | |||
ca17dd3ca3 | |||
71c7a3de69 | |||
76fc689337 | |||
af5d25490a | |||
fb161ae783 | |||
b92b20e8d2 | |||
4b19059df1 | |||
baa6ec5cba | |||
1a86b80c61 | |||
3783da3647 | |||
39dfc00693 | |||
c7cad20aa7 | |||
5901365e0e | |||
6324aacba1 | |||
d51ba8f6dd | |||
9b69e135d9 | |||
c3218b2f5a | |||
9bbbff31f8 | |||
432429026b | |||
a47ecbdea9 | |||
f250aff99b | |||
003f14003e | |||
fe4e245cda | |||
668da62519 | |||
fd89254d5a | |||
b4338c1761 | |||
fb3c79617f | |||
b80fe428ac | |||
5806563ba4 | |||
5adecc307f | |||
12d1b5e849 | |||
3f2095870d | |||
1481c76a0d | |||
1cdc80e09b | |||
e568aa8320 | |||
28629aa836 | |||
19f180331b | |||
3292f0b545 | |||
3333bfeeff | |||
eb1ac3bc9b | |||
65ae1a6177 | |||
0fba385b9e | |||
ee65a54684 | |||
82fef82c4f | |||
70383a9b9d | |||
15fdba060c | |||
c1065dab2d | |||
dfb4afc698 | |||
893b09e7e4 | |||
938bcb2b62 | |||
ac45f67a21 | |||
36cd9664b1 | |||
073d330db4 | |||
9ba356c47e | |||
f2bec9b478 | |||
3d0cbdd1a7 | |||
c5f5bf0287 | |||
99986c1b94 | |||
7d669caa3c | |||
b68e3cb10f | |||
e305c488d4 | |||
a2417bbe56 | |||
b3967b36c0 | |||
5fb33dfee9 | |||
c002768e5b | |||
aff33c6a5d | |||
a84b497fae | |||
c33d1bcd3c | |||
4071e14a7e | |||
b5b3f190b7 | |||
d43a3e132c | |||
a90c21f80a | |||
2b768165e5 | |||
e8425ba03a | |||
b0a6f402e9 | |||
02e86a940b | |||
79d03eb43e | |||
b564955f85 | |||
5aed64f614 | |||
a823a4d9b7 | |||
4eed2193f4 | |||
48e7a41af6 | |||
bba5caecf7 | |||
ede6a45f15 | |||
9c65d23229 | |||
5dedfe9295 | |||
95d8c368c8 | |||
49fbfb8bbc | |||
aa3d2a5289 | |||
48ae6df4b7 | |||
c635351a12 | |||
d9ff77fd9a | |||
e1a7954307 | |||
efe6421133 | |||
79b62f4407 | |||
9d17804ac7 | |||
5986355504 | |||
192e6fde92 | |||
ad090e62cc | |||
5c072fea62 | |||
fd01a40810 | |||
7b9a83a273 | |||
f964e0e502 | |||
22acb7c74b | |||
6a99e81e75 | |||
93b6de1caf | |||
1fbab5db2b | |||
950e852dee | |||
826898e3fe | |||
1268149d83 | |||
908299970f |
11
.gitignore
vendored
11
.gitignore
vendored
@ -12,9 +12,7 @@ msbuild.binlog
|
|||||||
*.idea
|
*.idea
|
||||||
|
|
||||||
Oqtane.Server/appsettings.json
|
Oqtane.Server/appsettings.json
|
||||||
Oqtane.Server/Data/*.mdf
|
Oqtane.Server/Data
|
||||||
Oqtane.Server/Data/*.ldf
|
|
||||||
Oqtane.Server/Data/*.db
|
|
||||||
|
|
||||||
/Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml
|
/Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml
|
||||||
Oqtane.Server/Content
|
Oqtane.Server/Content
|
||||||
@ -22,3 +20,10 @@ Oqtane.Server/Packages
|
|||||||
Oqtane.Server/wwwroot/Content
|
Oqtane.Server/wwwroot/Content
|
||||||
Oqtane.Server/wwwroot/Packages/*.log
|
Oqtane.Server/wwwroot/Packages/*.log
|
||||||
|
|
||||||
|
Oqtane.Server/wwwroot/Modules
|
||||||
|
!Oqtane.Server/wwwroot/Modules/Oqtane.Modules.*
|
||||||
|
!Oqtane.Server/wwwroot/Modules/Templates
|
||||||
|
|
||||||
|
Oqtane.Server/wwwroot/Themes
|
||||||
|
!Oqtane.Server/wwwroot/Themes/Oqtane.Themes.*
|
||||||
|
!Oqtane.Server/wwwroot/Themes/Templates
|
||||||
|
@ -45,6 +45,9 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string RemoteIPAddress { get; set; }
|
public string RemoteIPAddress { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string AuthorizationToken { get; set; }
|
||||||
|
|
||||||
private bool _initialized = false;
|
private bool _initialized = false;
|
||||||
private string _display = "display: none;";
|
private string _display = "display: none;";
|
||||||
private Installation _installation = new Installation { Success = false, Message = "" };
|
private Installation _installation = new Installation { Success = false, Message = "" };
|
||||||
@ -55,17 +58,13 @@
|
|||||||
{
|
{
|
||||||
SiteState.RemoteIPAddress = RemoteIPAddress;
|
SiteState.RemoteIPAddress = RemoteIPAddress;
|
||||||
SiteState.AntiForgeryToken = AntiForgeryToken;
|
SiteState.AntiForgeryToken = AntiForgeryToken;
|
||||||
InstallationService.SetAntiForgeryTokenHeader(AntiForgeryToken);
|
SiteState.AuthorizationToken = AuthorizationToken;
|
||||||
|
|
||||||
_installation = await InstallationService.IsInstalled();
|
_installation = await InstallationService.IsInstalled();
|
||||||
if (_installation.Alias != null)
|
if (_installation.Alias != null)
|
||||||
{
|
{
|
||||||
SiteState.Alias = _installation.Alias;
|
SiteState.Alias = _installation.Alias;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
_installation.Message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name";
|
|
||||||
}
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="pwd" type="password" class="form-control" @bind="@_pwd" />
|
<input id="pwd" type="password" class="form-control" @bind="@_pwd" autocomplete="new-password" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="pwd" type="password" class="form-control" @bind="@_pwd" />
|
<input id="pwd" type="password" class="form-control" @bind="@_pwd" autocomplete="new-password" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="pwd" type="password" class="form-control" @bind="@_pwd" />
|
<input id="pwd" type="password" class="form-control" @bind="@_pwd" autocomplete="new-password" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -62,13 +62,19 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="Provide a password for the primary user account" ResourceKey="Password">Password:</Label>
|
<Label Class="col-sm-3" For="password" HelpText="Provide a password for the primary user account" ResourceKey="Password">Password:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="password" type="password" class="form-control" @bind="@_hostPassword" />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" class="form-control" @bind="@_hostPassword" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="confirm" type="password" class="form-control" @bind="@_confirmPassword" />
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirmPassword" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -104,6 +110,8 @@
|
|||||||
|
|
||||||
private string _hostUsername = string.Empty;
|
private string _hostUsername = string.Empty;
|
||||||
private string _hostPassword = string.Empty;
|
private string _hostPassword = string.Empty;
|
||||||
|
private string _passwordtype = "password";
|
||||||
|
private string _togglepassword = string.Empty;
|
||||||
private string _confirmPassword = string.Empty;
|
private string _confirmPassword = string.Empty;
|
||||||
private string _hostEmail = string.Empty;
|
private string _hostEmail = string.Empty;
|
||||||
private bool _register = true;
|
private bool _register = true;
|
||||||
@ -112,6 +120,7 @@
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
_databases = await DatabaseService.GetDatabasesAsync();
|
_databases = await DatabaseService.GetDatabasesAsync();
|
||||||
if (_databases.Exists(item => item.IsDefault))
|
if (_databases.Exists(item => item.IsDefault))
|
||||||
{
|
{
|
||||||
@ -159,7 +168,7 @@
|
|||||||
{
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
await interop.IncludeLink("", "stylesheet", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css", "text/css", "sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==", "anonymous", "");
|
await interop.IncludeLink("", "stylesheet", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css", "text/css", "sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==", "anonymous", "");
|
||||||
await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", "anonymous", "", "head", "");
|
await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", "anonymous", "", "head");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +180,9 @@
|
|||||||
connectionString = databaseConfigControl.GetConnectionString();
|
connectionString = databaseConfigControl.GetConnectionString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && _hostPassword.Length >= 6 && _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))
|
||||||
{
|
{
|
||||||
_loadingDisplay = "";
|
_loadingDisplay = "";
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
@ -207,8 +218,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
_message = Localizer["Message.Password.Invalid"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
_message = Localizer["Message.Require.DbInfo"];
|
_message = Localizer["Message.Require.DbInfo"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,6 @@
|
|||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
|
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
|
||||||
_pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId && !item.IsDeleted).ToList();
|
_pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ else
|
|||||||
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + ".Client." + code)).FirstOrDefault();
|
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + ".Client." + code)).FirstOrDefault();
|
||||||
if (package != null)
|
if (package != null)
|
||||||
{
|
{
|
||||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) > 0);
|
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,6 @@
|
|||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@if (_message != string.Empty)
|
|
||||||
{
|
|
||||||
<ModuleMessage Message="@_message" Type="@_type" />
|
|
||||||
}
|
|
||||||
<AuthorizeView Roles="@RoleNames.Registered">
|
<AuthorizeView Roles="@RoleNames.Registered">
|
||||||
<Authorizing>
|
<Authorizing>
|
||||||
<text>...</text>
|
<text>...</text>
|
||||||
@ -19,42 +15,74 @@
|
|||||||
<div>@Localizer["Info.SignedIn"]</div>
|
<div>@Localizer["Info.SignedIn"]</div>
|
||||||
</Authorized>
|
</Authorized>
|
||||||
<NotAuthorized>
|
<NotAuthorized>
|
||||||
|
@if (!twofactor)
|
||||||
|
{
|
||||||
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<div class="container Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
|
<div class="Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
|
||||||
|
@if (_allowexternallogin)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="ExternalLogin">@Localizer["Use"] @PageState.Site.Settings["ExternalLogin:ProviderName"]</button>
|
||||||
|
<br /><br />
|
||||||
|
}
|
||||||
|
@if (_allowsitelogin)
|
||||||
|
{
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="Username" class="control-label">@SharedLocalizer["Username"] </label>
|
<Label Class="control-label" For="username" HelpText="Please enter your Username" ResourceKey="Username">Username:</Label>
|
||||||
<input type="text" @ref="username" name="Username" class="form-control username" placeholder="Username" @bind="@_username" id="Username" required />
|
<input id="username" type="text" @ref="username" class="form-control" placeholder="@Localizer["Username.Placeholder"]" @bind="@_username" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group mt-2">
|
||||||
<label for="Password" class="control-label">@SharedLocalizer["Password"] </label>
|
<Label Class="control-label" For="password" HelpText="Please enter your Password" ResourceKey="Password">Password:</Label>
|
||||||
<input type="password" name="Password" class="form-control password" placeholder="Password" @bind="@_password" id="Password" required />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" name="Password" class="form-control" placeholder="@Localizer["Password.Placeholder"]" @bind="@_password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-group mt-2">
|
||||||
<label class="form-check-label" for="Remember">@Localizer["RememberMe"]</label>
|
<div class="form-check">
|
||||||
<input type="checkbox" class="form-check-input" name="Remember" @bind="@_remember" id="Remember" />
|
<input id="remember" type="checkbox" class="form-check-input" @bind="@_remember" />
|
||||||
|
<Label Class="control-label" For="remember" HelpText="Specify if you would like to be signed back in automatically the next time you visit this site" ResourceKey="Remember">Remember Me?</Label>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
|
<div class="container Oqtane-Modules-Admin-Login">
|
||||||
|
<div class="form-group">
|
||||||
|
<Label Class="control-label" For="code" HelpText="Please enter the secure verification code which was sent to you by email" ResourceKey="Code">Verification Code:</Label>
|
||||||
|
<input id="code" class="form-control" @bind="@_code" placeholder="@Localizer["Code.Placeholder"]" maxlength="6" required />
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Cancel"]</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
</NotAuthorized>
|
</NotAuthorized>
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _returnUrl = string.Empty;
|
private bool _allowsitelogin = true;
|
||||||
private string _message = string.Empty;
|
private bool _allowexternallogin = false;
|
||||||
private MessageType _type = MessageType.Info;
|
|
||||||
private string _username = string.Empty;
|
|
||||||
private string _password = string.Empty;
|
|
||||||
private bool _remember = false;
|
|
||||||
private bool validated = false;
|
|
||||||
|
|
||||||
private ElementReference login;
|
private ElementReference login;
|
||||||
|
private bool validated = false;
|
||||||
|
private bool twofactor = false;
|
||||||
|
private string _username = string.Empty;
|
||||||
private ElementReference username;
|
private ElementReference username;
|
||||||
|
private string _password = string.Empty;
|
||||||
|
private string _passwordtype = "password";
|
||||||
|
private string _togglepassword = string.Empty;
|
||||||
|
private bool _remember = false;
|
||||||
|
private string _code = string.Empty;
|
||||||
|
|
||||||
|
private string _returnUrl = string.Empty;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||||
|
|
||||||
@ -65,6 +93,20 @@
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
|
if (PageState.Site.Settings.ContainsKey("LoginOptions:AllowSiteLogin") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]))
|
||||||
|
{
|
||||||
|
_allowsitelogin = bool.Parse(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PageState.Site.Settings.ContainsKey("ExternalLogin:ProviderType") && !string.IsNullOrEmpty(PageState.Site.Settings["ExternalLogin:ProviderType"]))
|
||||||
|
{
|
||||||
|
_allowexternallogin = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||||
{
|
{
|
||||||
_returnUrl = PageState.QueryString["returnurl"];
|
_returnUrl = PageState.QueryString["returnurl"];
|
||||||
@ -75,85 +117,113 @@
|
|||||||
_username = PageState.QueryString["name"];
|
_username = PageState.QueryString["name"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("token"))
|
if (PageState.QueryString.ContainsKey("token") && !string.IsNullOrEmpty(_username))
|
||||||
{
|
{
|
||||||
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 = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
|
|
||||||
|
|
||||||
|
if (PageState.QueryString.ContainsKey("key"))
|
||||||
|
{
|
||||||
|
user = await UserService.LinkUserAsync(user, PageState.QueryString["token"], PageState.Site.Settings["ExternalLogin:ProviderType"], PageState.QueryString["key"], PageState.Site.Settings["ExternalLogin:ProviderName"]);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
await logger.LogInformation(LogFunction.Security, "External Login Linkage Successful For Username {Username}", _username);
|
||||||
|
AddModuleMessage(Localizer["Success.Account.Linked"], MessageType.Info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await logger.LogError(LogFunction.Security, "External Login Linkage Failed For Username {Username}", _username);
|
||||||
|
AddModuleMessage(Localizer["Message.Account.NotLinked"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
_username = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
|
||||||
_message = Localizer["Success.Account.Verified"];
|
AddModuleMessage(Localizer["Success.Account.Verified"], MessageType.Info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
|
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
|
||||||
_message = Localizer["Message.Account.NotVerfied"];
|
AddModuleMessage(Localizer["Message.Account.NotVerified"], MessageType.Warning);
|
||||||
_type = MessageType.Warning;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (PageState.QueryString.ContainsKey("status"))
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Login {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.LoadLogin"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender && PageState.User == null)
|
||||||
{
|
|
||||||
if(PageState.User == null)
|
|
||||||
{
|
{
|
||||||
await username.FocusAsync();
|
await username.FocusAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Login()
|
private async Task Login()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(login))
|
if (await interop.FormValid(login))
|
||||||
{
|
{
|
||||||
if (PageState.Runtime == Oqtane.Shared.Runtime.Server)
|
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password, LastIPAddress = SiteState.RemoteIPAddress};
|
||||||
|
|
||||||
|
if (!twofactor)
|
||||||
{
|
{
|
||||||
var user = new User();
|
user = await UserService.LoginUserAsync(user);
|
||||||
user.SiteId = PageState.Site.SiteId;
|
}
|
||||||
user.Username = _username;
|
else
|
||||||
user.Password = _password;
|
{
|
||||||
user = await UserService.LoginUserAsync(user, false, false);
|
user = await UserService.VerifyTwoFactorAsync(user, _code);
|
||||||
|
}
|
||||||
|
|
||||||
if (user.IsAuthenticated)
|
if (user.IsAuthenticated)
|
||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
||||||
// server-side Blazor needs to post to the Login page so that the cookies are set correctly
|
|
||||||
|
// post back to the Login page so that the cookies are set correctly
|
||||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
|
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
|
||||||
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
||||||
await interop.SubmitForm(url, fields);
|
await interop.SubmitForm(url, fields);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await logger.LogError(LogFunction.Security, "Login Failed For Username {Username}", _username);
|
if ((PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && PageState.Site.Settings["LoginOptions:TwoFactor"] == "required") || user.TwoFactorRequired)
|
||||||
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
|
{
|
||||||
}
|
twofactor = true;
|
||||||
|
validated = false;
|
||||||
|
AddModuleMessage(Localizer["Message.TwoFactor"], MessageType.Info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// client-side Blazor
|
if (!twofactor)
|
||||||
var user = new User();
|
|
||||||
user.SiteId = PageState.Site.SiteId;
|
|
||||||
user.Username = _username;
|
|
||||||
user.Password = _password;
|
|
||||||
user = await UserService.LoginUserAsync(user, true, _remember);
|
|
||||||
if (user.IsAuthenticated)
|
|
||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Login Failed For Username {Username}", _username);
|
||||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
|
||||||
authstateprovider.NotifyAuthenticationChanged();
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl(_returnUrl, true));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await logger.LogError(LogFunction.Security, "Login Failed For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Two Factor Verification Failed For Username {Username}", _username);
|
||||||
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.TwoFactor.Fail"], MessageType.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,6 +232,12 @@
|
|||||||
AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Performing Login {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Login"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void Cancel()
|
private void Cancel()
|
||||||
{
|
{
|
||||||
@ -169,6 +245,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task Forgot()
|
private async Task Forgot()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (_username != string.Empty)
|
if (_username != string.Empty)
|
||||||
{
|
{
|
||||||
@ -177,21 +255,35 @@
|
|||||||
{
|
{
|
||||||
await UserService.ForgotPasswordAsync(user);
|
await UserService.ForgotPasswordAsync(user);
|
||||||
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
|
||||||
_message = Localizer["Message.ForgotUser"];
|
AddModuleMessage(Localizer["Message.ForgotUser"], MessageType.Info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_message = Localizer["Message.UserDoesNotExist"];
|
AddModuleMessage(Localizer["Message.UserDoesNotExist"], MessageType.Warning);
|
||||||
_type = MessageType.Warning;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_message = Localizer["Message.ForgotPassword"];
|
AddModuleMessage(Localizer["Message.ForgotPassword"], MessageType.Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Resetting Password {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.ResetPassword"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Reset()
|
||||||
|
{
|
||||||
|
twofactor = false;
|
||||||
|
_username = "";
|
||||||
|
_password = "";
|
||||||
|
ClearModuleMessage();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task KeyPressed(KeyboardEventArgs e)
|
private async Task KeyPressed(KeyboardEventArgs e)
|
||||||
{
|
{
|
||||||
@ -200,4 +292,24 @@
|
|||||||
await Login();
|
await Login();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExternalLogin()
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + _returnUrl), true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
@inject IStringLocalizer<Detail> Localizer
|
@inject IStringLocalizer<Detail> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
|
@if (_initialized)
|
||||||
|
{
|
||||||
<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="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label>
|
<Label Class="col-sm-3" For="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label>
|
||||||
@ -107,10 +109,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
<NavLink class="btn btn-secondary" href="@CloseUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
@code {
|
||||||
|
private bool _initialized = false;
|
||||||
@code {
|
|
||||||
private int _logId;
|
private int _logId;
|
||||||
private string _logDate = string.Empty;
|
private string _logDate = string.Empty;
|
||||||
private string _level = string.Empty;
|
private string _level = string.Empty;
|
||||||
@ -152,7 +155,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log.PageId != null && log.ModuleId != null)
|
if (log.PageId != null && log.ModuleId != null && log.ModuleId != -1)
|
||||||
{
|
{
|
||||||
var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value);
|
var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value);
|
||||||
if (pagemodule != null)
|
if (pagemodule != null)
|
||||||
@ -176,6 +179,7 @@
|
|||||||
_exception = log.Exception;
|
_exception = log.Exception;
|
||||||
_properties = log.Properties;
|
_properties = log.Properties;
|
||||||
_server = log.Server;
|
_server = log.Server;
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -184,4 +188,16 @@
|
|||||||
AddModuleMessage(Localizer["Error.Log.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Log.Load"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string CloseUrl()
|
||||||
|
{
|
||||||
|
if (!PageState.QueryString.ContainsKey("level"))
|
||||||
|
{
|
||||||
|
return NavigateUrl();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NavigateUrl(PageState.Page.Path, "level=" + PageState.QueryString["level"] + "&function=" + PageState.QueryString["function"] + "&rows=" + PageState.QueryString["rows"] + "&page=" + PageState.QueryString["page"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@ else
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<Label For="level" HelpText="Select the log level for event log items" ResourceKey="Level">Level: </Label><br /><br />
|
<Label For="level" HelpText="Select the log level for event log items" ResourceKey="Level">Level: </Label><br /><br />
|
||||||
<select id="level" class="form-select" @onchange="(e => LevelChanged(e))">
|
<select id="level" class="form-select" value="@_level" @onchange="(e => LevelChanged(e))">
|
||||||
<option value="-"><@Localizer["AllLevels"]></option>
|
<option value="-"><@Localizer["AllLevels"]></option>
|
||||||
<option value="Trace">@Localizer["Trace"]</option>
|
<option value="Trace">@Localizer["Trace"]</option>
|
||||||
<option value="Debug">@Localizer["Debug"]</option>
|
<option value="Debug">@Localizer["Debug"]</option>
|
||||||
@ -29,7 +29,7 @@ else
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<Label For="function" HelpText="Select the function for event log items" ResourceKey="Function">Function: </Label><br /><br />
|
<Label For="function" HelpText="Select the function for event log items" ResourceKey="Function">Function: </Label><br /><br />
|
||||||
<select id="function" class="form-select" @onchange="(e => FunctionChanged(e))">
|
<select id="function" class="form-select" value="@_function" @onchange="(e => FunctionChanged(e))">
|
||||||
<option value="-"><@Localizer["AllFunctions"]></option>
|
<option value="-"><@Localizer["AllFunctions"]></option>
|
||||||
<option value="Create">@Localizer["Create"]</option>
|
<option value="Create">@Localizer["Create"]</option>
|
||||||
<option value="Read">@Localizer["Read"]</option>
|
<option value="Read">@Localizer["Read"]</option>
|
||||||
@ -41,7 +41,7 @@ else
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<Label For="rows" HelpText="Select the maximum number of event log items to review. Please note that if you choose more than 10 items the information will be split into pages." ResourceKey="Rows">Maximum Items: </Label><br /><br />
|
<Label For="rows" HelpText="Select the maximum number of event log items to review. Please note that if you choose more than 10 items the information will be split into pages." ResourceKey="Rows">Maximum Items: </Label><br /><br />
|
||||||
<select id="rows" class="form-select" @onchange="(e => RowsChanged(e))">
|
<select id="rows" class="form-select" value="@_rows" @onchange="(e => RowsChanged(e))">
|
||||||
<option value="10">10</option>
|
<option value="10">10</option>
|
||||||
<option value="50">50</option>
|
<option value="50">50</option>
|
||||||
<option value="100">100</option>
|
<option value="100">100</option>
|
||||||
@ -53,7 +53,7 @@ else
|
|||||||
|
|
||||||
@if (_logs.Any())
|
@if (_logs.Any())
|
||||||
{
|
{
|
||||||
<Pager Items="@_logs">
|
<Pager Items="@_logs" CurrentPage="@_page.ToString()" OnPageChange="OnPageChange">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@Localizer["Date"]</th>
|
<th>@Localizer["Date"]</th>
|
||||||
@ -62,7 +62,7 @@ else
|
|||||||
<th>@Localizer["Function"]</th>
|
<th>@Localizer["Function"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString())" ResourceKey="LogDetails" /></td>
|
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString() + "&level=" + _level + "&function=" + _function + "&rows=" + _rows + "&page=" + _page.ToString())" ResourceKey="LogDetails" /></td>
|
||||||
<td class="@GetClass(context.Function)">@context.LogDate</td>
|
<td class="@GetClass(context.Function)">@context.LogDate</td>
|
||||||
<td class="@GetClass(context.Function)">@context.Level</td>
|
<td class="@GetClass(context.Function)">@context.Level</td>
|
||||||
<td class="@GetClass(context.Function)">@context.Feature</td>
|
<td class="@GetClass(context.Function)">@context.Feature</td>
|
||||||
@ -94,6 +94,7 @@ else
|
|||||||
private string _level = "-";
|
private string _level = "-";
|
||||||
private string _function = "-";
|
private string _function = "-";
|
||||||
private string _rows = "10";
|
private string _rows = "10";
|
||||||
|
private int _page = 1;
|
||||||
private List<Log> _logs;
|
private List<Log> _logs;
|
||||||
private string _retention = "";
|
private string _retention = "";
|
||||||
|
|
||||||
@ -103,8 +104,27 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (PageState.QueryString.ContainsKey("level"))
|
||||||
|
{
|
||||||
|
_level = PageState.QueryString["level"];
|
||||||
|
}
|
||||||
|
if (PageState.QueryString.ContainsKey("function"))
|
||||||
|
{
|
||||||
|
_function = PageState.QueryString["function"];
|
||||||
|
}
|
||||||
|
if (PageState.QueryString.ContainsKey("rows"))
|
||||||
|
{
|
||||||
|
_rows = PageState.QueryString["rows"];
|
||||||
|
}
|
||||||
|
if (PageState.QueryString.ContainsKey("page") && int.TryParse(PageState.QueryString["page"], out int page))
|
||||||
|
{
|
||||||
|
_page = page;
|
||||||
|
}
|
||||||
|
|
||||||
await GetLogs();
|
await GetLogs();
|
||||||
_retention = SettingService.GetSetting(PageState.Site.Settings, "LogRetention", "30");
|
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
|
_retention = SettingService.GetSetting(settings, "LogRetention", "30");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -208,4 +228,9 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnPageChange(int page)
|
||||||
|
{
|
||||||
|
_page = page;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -124,6 +124,8 @@ else
|
|||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
|
||||||
{
|
{
|
||||||
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
|
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
|
||||||
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
||||||
@ -135,6 +137,11 @@ else
|
|||||||
GetLocation();
|
GetLocation();
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
|
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Creating Module");
|
await logger.LogError(ex, "Error Creating Module");
|
||||||
|
@ -81,7 +81,7 @@
|
|||||||
private List<Template> _templates;
|
private List<Template> _templates;
|
||||||
private string _template = "-";
|
private string _template = "-";
|
||||||
private string[] _versions;
|
private string[] _versions;
|
||||||
private string _reference = Constants.Version;
|
private string _reference = "local";
|
||||||
private string _minversion = "2.0.0";
|
private string _minversion = "2.0.0";
|
||||||
private string _location = string.Empty;
|
private string _location = string.Empty;
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ else
|
|||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.Version</td>
|
<td>@context.Version</td>
|
||||||
<td>
|
<td>
|
||||||
@if(context.AssemblyName == "Oqtane.Client" || PageState.Modules.Where(m => m.ModuleDefinition.ModuleDefinitionId == context.ModuleDefinitionId).Count() > 0)
|
@if(context.AssemblyName == "Oqtane.Client" || PageState.Modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
|
||||||
{
|
{
|
||||||
<span>@SharedLocalizer["Yes"]</span>
|
<span>@SharedLocalizer["Yes"]</span>
|
||||||
}
|
}
|
||||||
@ -50,9 +50,12 @@ else
|
|||||||
@((MarkupString)PurchaseLink(context.PackageName))
|
@((MarkupString)PurchaseLink(context.PackageName))
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if (UpgradeAvailable(context.PackageName, context.Version))
|
@{
|
||||||
|
var version = UpgradeAvailable(context.PackageName, context.Version);
|
||||||
|
}
|
||||||
|
@if (version != context.Version)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.PackageName, context.Version))>@SharedLocalizer["Upgrade"]</button>
|
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.PackageName, version))>@SharedLocalizer["Upgrade"]</button>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</Row>
|
</Row>
|
||||||
@ -103,19 +106,17 @@ else
|
|||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UpgradeAvailable(string packagename, string version)
|
private string UpgradeAvailable(string packagename, string version)
|
||||||
{
|
{
|
||||||
var upgradeavailable = false;
|
|
||||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||||
{
|
{
|
||||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||||
if (package != null)
|
if (package != null && Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
|
||||||
{
|
{
|
||||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
return package.Version;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return upgradeavailable;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadModule(string packagename, string version)
|
private async Task DownloadModule(string packagename, string version)
|
||||||
|
@ -95,6 +95,12 @@
|
|||||||
<input id="title" class="form-control" @bind="@_title" />
|
<input id="title" class="form-control" @bind="@_title" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="meta" HelpText="Optionally enter meta tags (in exactly the form you want them to be included in the page output)." ResourceKey="Meta">Meta: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="meta" class="form-control" @bind="@_meta" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -164,6 +170,7 @@
|
|||||||
private List<Page> _pageList;
|
private List<Page> _pageList;
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _title;
|
private string _title;
|
||||||
|
private string _meta;
|
||||||
private string _path = string.Empty;
|
private string _path = string.Empty;
|
||||||
private string _parentid = "-1";
|
private string _parentid = "-1";
|
||||||
private string _insert = ">>";
|
private string _insert = ">>";
|
||||||
@ -370,6 +377,7 @@
|
|||||||
page.Permissions = _permissionGrid.GetPermissions();
|
page.Permissions = _permissionGrid.GetPermissions();
|
||||||
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
|
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
|
||||||
page.UserId = null;
|
page.UserId = null;
|
||||||
|
page.Meta = _meta;
|
||||||
|
|
||||||
page = await PageService.AddPageAsync(page);
|
page = await PageService.AddPageAsync(page);
|
||||||
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
||||||
|
@ -102,6 +102,12 @@
|
|||||||
<input id="title" class="form-control" @bind="@_title" maxlength="200"/>
|
<input id="title" class="form-control" @bind="@_title" maxlength="200"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="meta" HelpText="Optionally enter meta tags (in exactly the form you want them to be included in the page output)." ResourceKey="Meta">Meta: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="meta" class="form-control" @bind="@_meta" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -200,6 +206,7 @@
|
|||||||
private int _pageId;
|
private int _pageId;
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _title;
|
private string _title;
|
||||||
|
private string _meta;
|
||||||
private string _path;
|
private string _path;
|
||||||
private string _currentparentid;
|
private string _currentparentid;
|
||||||
private string _parentid = "-1";
|
private string _parentid = "-1";
|
||||||
@ -243,8 +250,9 @@
|
|||||||
{
|
{
|
||||||
_name = page.Name;
|
_name = page.Name;
|
||||||
_title = page.Title;
|
_title = page.Title;
|
||||||
|
_meta = page.Meta;
|
||||||
_path = page.Path;
|
_path = page.Path;
|
||||||
_pageModules = PageState.Modules.Where(m => m.PageId == page.PageId && m.IsDeleted == false).ToList();
|
_pageModules = PageState.Modules.Where(m => m.PageId == page.PageId).ToList();
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_path))
|
if (string.IsNullOrEmpty(_path))
|
||||||
{
|
{
|
||||||
@ -384,6 +392,8 @@
|
|||||||
private void ThemeSettings()
|
private void ThemeSettings()
|
||||||
{
|
{
|
||||||
_themeSettingsType = null;
|
_themeSettingsType = null;
|
||||||
|
if (PageState.QueryString.ContainsKey("cp")) // can only be displayed if invoked from Control Panel
|
||||||
|
{
|
||||||
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
||||||
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
||||||
{
|
{
|
||||||
@ -400,6 +410,7 @@
|
|||||||
_refresh = true;
|
_refresh = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SavePage()
|
private async Task SavePage()
|
||||||
{
|
{
|
||||||
@ -490,6 +501,7 @@
|
|||||||
page.Permissions = _permissionGrid.GetPermissions();
|
page.Permissions = _permissionGrid.GetPermissions();
|
||||||
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
|
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
|
||||||
page.UserId = null;
|
page.UserId = null;
|
||||||
|
page.Meta = _meta;
|
||||||
|
|
||||||
page = await PageService.UpdatePageAsync(page);
|
page = await PageService.UpdatePageAsync(page);
|
||||||
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
<th>@Localizer["DeletedOn"]</th>
|
<th>@Localizer["DeletedOn"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-info" title="Restore">Restore</button></td>
|
<td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-success" title="Restore">Restore</button></td>
|
||||||
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.DeletedBy</td>
|
<td>@context.DeletedBy</td>
|
||||||
@ -56,7 +56,7 @@
|
|||||||
<th>@Localizer["DeletedOn"]</th>
|
<th>@Localizer["DeletedOn"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-info" title="Restore">@Localizer["Restore"]</button></td>
|
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
||||||
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
|
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
|
||||||
<td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td>
|
<td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td>
|
||||||
<td>@context.Title</td>
|
<td>@context.Title</td>
|
||||||
|
@ -27,13 +27,19 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="Please choose a sufficiently secure password and enter it here" ResourceKey="Password"></Label>
|
<Label Class="col-sm-3" For="password" HelpText="Please choose a sufficiently secure password and enter it here" ResourceKey="Password"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="password" type="password" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="confirm" HelpText="Enter your password again to confirm it matches the value entered above" ResourceKey="Confirm"></Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="Enter your password again to confirm it matches the value entered above" ResourceKey="Confirm"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="confirm" type="password" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -66,12 +72,19 @@ else
|
|||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private string _password = string.Empty;
|
private string _password = string.Empty;
|
||||||
|
private string _passwordtype = "password";
|
||||||
|
private string _togglepassword = string.Empty;
|
||||||
private string _confirm = string.Empty;
|
private string _confirm = string.Empty;
|
||||||
private string _email = string.Empty;
|
private string _email = string.Empty;
|
||||||
private string _displayname = string.Empty;
|
private string _displayname = string.Empty;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Register()
|
private async Task Register()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
@ -90,9 +103,10 @@ else
|
|||||||
{
|
{
|
||||||
SiteId = PageState.Site.SiteId,
|
SiteId = PageState.Site.SiteId,
|
||||||
Username = _username,
|
Username = _username,
|
||||||
DisplayName = (_displayname == string.Empty ? _username : _displayname),
|
Password = _password,
|
||||||
Email = _email,
|
Email = _email,
|
||||||
Password = _password
|
DisplayName = (_displayname == string.Empty ? _username : _displayname),
|
||||||
|
PhotoFileId = null
|
||||||
};
|
};
|
||||||
user = await UserService.AddUserAsync(user);
|
user = await UserService.AddUserAsync(user);
|
||||||
|
|
||||||
@ -133,4 +147,18 @@ else
|
|||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,19 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="The new password. It must satisfy complexity rules for the site." ResourceKey="Password">Password: </Label>
|
<Label Class="col-sm-3" For="password" HelpText="The new password. It must satisfy complexity rules for the site." ResourceKey="Password">Password: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="password" type="password" class="form-control" @bind="@_password" required />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="confirm" HelpText="Enter the password again. It must exactly match the password entered above." ResourceKey="Confirm">Confirm: </Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="Enter the password again. It must exactly match the password entered above." ResourceKey="Confirm">Confirm: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="confirm" type="password" class="form-control" @bind="@_confirm" required />
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -36,12 +42,16 @@
|
|||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private string _username = string.Empty;
|
private string _username = string.Empty;
|
||||||
private string _password = string.Empty;
|
private string _password = string.Empty;
|
||||||
|
private string _passwordtype = "password";
|
||||||
|
private string _togglepassword = string.Empty;
|
||||||
private string _confirm = string.Empty;
|
private string _confirm = string.Empty;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("name") && PageState.QueryString.ContainsKey("token"))
|
if (PageState.QueryString.ContainsKey("name") && PageState.QueryString.ContainsKey("token"))
|
||||||
{
|
{
|
||||||
_username = PageState.QueryString["name"];
|
_username = PageState.QueryString["name"];
|
||||||
@ -110,4 +120,18 @@
|
|||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,12 +53,14 @@ else
|
|||||||
<Pager Items="@userroles">
|
<Pager Items="@userroles">
|
||||||
<Header>
|
<Header>
|
||||||
<th>@Localizer["Users"]</th>
|
<th>@Localizer["Users"]</th>
|
||||||
|
<th>@SharedLocalizer["Username"]</th>
|
||||||
<th>@Localizer["Effective"]</th>
|
<th>@Localizer["Effective"]</th>
|
||||||
<th>@Localizer["Expiry"]</th>
|
<th>@Localizer["Expiry"]</th>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td>@context.User.DisplayName</td>
|
<td>@context.User.DisplayName</td>
|
||||||
|
<td>@context.User.Username</td>
|
||||||
<td>@context.EffectiveDate</td>
|
<td>@context.EffectiveDate</td>
|
||||||
<td>@context.ExpiryDate</td>
|
<td>@context.ExpiryDate</td>
|
||||||
<td>
|
<td>
|
||||||
@ -93,11 +95,7 @@ else
|
|||||||
roleid = Int32.Parse(PageState.QueryString["id"]);
|
roleid = Int32.Parse(PageState.QueryString["id"]);
|
||||||
Role role = await RoleService.GetRoleAsync(roleid);
|
Role role = await RoleService.GetRoleAsync(roleid);
|
||||||
name = role.Name;
|
name = role.Name;
|
||||||
users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
||||||
users = users
|
|
||||||
.Where(u => u.Role.Name == RoleNames.Registered)
|
|
||||||
.OrderBy(u => u.User.DisplayName)
|
|
||||||
.ToList();
|
|
||||||
await GetUserRoles();
|
await GetUserRoles();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -111,8 +109,7 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, name);
|
||||||
userroles = userroles.Where(item => item.RoleId == roleid).ToList();
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -120,7 +120,10 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="Enter the password for your SMTP account" ResourceKey="SmtpPassword">Password: </Label>
|
<Label Class="col-sm-3" For="password" HelpText="Enter the password for your SMTP account" ResourceKey="SmtpPassword">Password: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="password" type="password" class="form-control" @bind="@_smtppassword" />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_smtppasswordtype" class="form-control" @bind="@_smtppassword" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleSMTPPassword">@_togglesmtppassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -129,6 +132,12 @@
|
|||||||
<input id="sender" class="form-control" @bind="@_smtpsender" />
|
<input id="sender" class="form-control" @bind="@_smtpsender" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="retention" class="form-control" @bind="@_retention" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
</div>
|
</div>
|
||||||
@ -260,7 +269,10 @@
|
|||||||
private string _smtpssl = "False";
|
private string _smtpssl = "False";
|
||||||
private string _smtpusername = string.Empty;
|
private string _smtpusername = string.Empty;
|
||||||
private string _smtppassword = string.Empty;
|
private string _smtppassword = string.Empty;
|
||||||
|
private string _smtppasswordtype = "password";
|
||||||
|
private string _togglesmtppassword = string.Empty;
|
||||||
private string _smtpsender = string.Empty;
|
private string _smtpsender = string.Empty;
|
||||||
|
private string _retention = string.Empty;
|
||||||
private string _pwaisenabled;
|
private string _pwaisenabled;
|
||||||
private int _pwaappiconfileid = -1;
|
private int _pwaappiconfileid = -1;
|
||||||
private FileManager _pwaappiconfilemanager;
|
private FileManager _pwaappiconfilemanager;
|
||||||
@ -329,7 +341,9 @@
|
|||||||
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
|
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
|
||||||
_smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty);
|
_smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty);
|
||||||
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
|
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
|
||||||
|
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||||
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
||||||
|
_retention = SettingService.GetSetting(settings, "NotificationRetention", "30");
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
@ -393,7 +407,7 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
|
if (_name != string.Empty && _themetype != "-" && _containertype != "-")
|
||||||
{
|
{
|
||||||
var unique = true;
|
var unique = true;
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
@ -479,12 +493,13 @@
|
|||||||
site = await SiteService.UpdateSiteAsync(site);
|
site = await SiteService.UpdateSiteAsync(site);
|
||||||
|
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||||
SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
||||||
SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||||
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||||
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||||
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||||
SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
@ -605,8 +620,10 @@
|
|||||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
await logger.LogInformation("Site SMTP Settings Saved");
|
await logger.LogInformation("Site SMTP Settings Saved");
|
||||||
|
|
||||||
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User.DisplayName, PageState.User.Email, PageState.User.DisplayName, PageState.User.Email, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
|
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
|
||||||
AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info);
|
AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info);
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
await interop.ScrollTo(0, 0, "smooth");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -616,7 +633,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.required.Smtp"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.Smtp"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -633,4 +650,18 @@
|
|||||||
}
|
}
|
||||||
if (string.IsNullOrEmpty(_defaultalias)) _defaultalias = _aliases.First().Name.Trim();
|
if (string.IsNullOrEmpty(_defaultalias)) _defaultalias = _aliases.First().Name.Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ToggleSMTPPassword()
|
||||||
|
{
|
||||||
|
if (_smtppasswordtype == "password")
|
||||||
|
{
|
||||||
|
_smtppasswordtype = "text";
|
||||||
|
_togglesmtppassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_smtppasswordtype = "password";
|
||||||
|
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ else
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="hostPassword" HelpText="Enter the password of an existing host user" ResourceKey="HostPassword">Host Password:</Label>
|
<Label Class="col-sm-3" For="hostPassword" HelpText="Enter the password of an existing host user" ResourceKey="HostPassword">Host Password:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" required />
|
<input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" autocomplete="new-password" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@ -307,7 +307,8 @@ else
|
|||||||
user.SiteId = PageState.Site.SiteId;
|
user.SiteId = PageState.Site.SiteId;
|
||||||
user.Username = _hostusername;
|
user.Username = _hostusername;
|
||||||
user.Password = _hostpassword;
|
user.Password = _hostpassword;
|
||||||
user = await UserService.LoginUserAsync(user, false, false);
|
user.LastIPAddress = PageState.RemoteIPAddress;
|
||||||
|
user = await UserService.LoginUserAsync(user);
|
||||||
if (user.IsAuthenticated)
|
if (user.IsAuthenticated)
|
||||||
{
|
{
|
||||||
var connectionString = String.Empty;
|
var connectionString = String.Empty;
|
||||||
|
@ -27,9 +27,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="serverpath" HelpText="Server Path" ResourceKey="ServerPath">Server Path: </Label>
|
<Label Class="col-sm-3" For="machinename" HelpText="Machine Name" ResourceKey="MachineName">Machine Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="serverpath" class="form-control" @bind="@_serverpath" readonly />
|
<input id="machinename" class="form-control" @bind="@_machinename" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="ipaddress" HelpText="Server IP Address" ResourceKey="IPAddress">IP Address: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="ipaddress" class="form-control" @bind="@_ipaddress" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="contentrootpath" HelpText="Root Path" ResourceKey="ContentRootPath">Root Path: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="contentrootpath" class="form-control" @bind="@_contentrootpath" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="webrootpath" HelpText="Web Path" ResourceKey="WebRootPath">Web Path: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="webrootpath" class="form-control" @bind="@_webrootpath" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -38,6 +56,18 @@
|
|||||||
<input id="servertime" class="form-control" @bind="@_servertime" readonly />
|
<input id="servertime" class="form-control" @bind="@_servertime" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="tickcount" HelpText="Amount Of Time The Service Has Been Available And Operational" ResourceKey="TickCount">Service Uptime: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="tickcount" class="form-control" @bind="@_tickcount" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="workingset" HelpText="Memory Allocation Of Service (in MB)" ResourceKey="WorkingSet">Memory Allocation: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="workingset" class="form-control" @bind="@_workingset" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="installationid" HelpText="The Unique Identifier For Your Installation" ResourceKey="InstallationId">Installation ID: </Label>
|
<Label Class="col-sm-3" For="installationid" HelpText="The Unique Identifier For Your Installation" ResourceKey="InstallationId">Installation ID: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -69,6 +99,21 @@
|
|||||||
<option value="Warning">@Localizer["Warning"]</option>
|
<option value="Warning">@Localizer["Warning"]</option>
|
||||||
<option value="Error">@Localizer["Error"]</option>
|
<option value="Error">@Localizer["Error"]</option>
|
||||||
<option value="Critical">@Localizer["Critical"]</option>
|
<option value="Critical">@Localizer["Critical"]</option>
|
||||||
|
<option value="None">@Localizer["None"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="notificationlevel" HelpText="The Minimum Logging Level For Which Notifications Should Be Sent To Host Users." ResourceKey="NotificationLevel">Notification Level: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="notificationlevel" class="form-select" @bind="@_notificationlevel">
|
||||||
|
<option value="Trace">@Localizer["Trace"]</option>
|
||||||
|
<option value="Debug">@Localizer["Debug"]</option>
|
||||||
|
<option value="Information">@Localizer["Information"]</option>
|
||||||
|
<option value="Warning">@Localizer["Warning"]</option>
|
||||||
|
<option value="Error">@Localizer["Error"]</option>
|
||||||
|
<option value="Critical">@Localizer["Critical"]</option>
|
||||||
|
<option value="None">@Localizer["None"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -104,12 +149,18 @@
|
|||||||
private string _version = string.Empty;
|
private string _version = string.Empty;
|
||||||
private string _clrversion = string.Empty;
|
private string _clrversion = string.Empty;
|
||||||
private string _osversion = string.Empty;
|
private string _osversion = string.Empty;
|
||||||
private string _serverpath = string.Empty;
|
private string _machinename = string.Empty;
|
||||||
|
private string _ipaddress = string.Empty;
|
||||||
|
private string _contentrootpath = string.Empty;
|
||||||
|
private string _webrootpath = string.Empty;
|
||||||
private string _servertime = string.Empty;
|
private string _servertime = string.Empty;
|
||||||
|
private string _tickcount = string.Empty;
|
||||||
|
private string _workingset = string.Empty;
|
||||||
private string _installationid = string.Empty;
|
private string _installationid = string.Empty;
|
||||||
|
|
||||||
private string _detailederrors = string.Empty;
|
private string _detailederrors = string.Empty;
|
||||||
private string _logginglevel = string.Empty;
|
private string _logginglevel = string.Empty;
|
||||||
|
private string _notificationlevel = string.Empty;
|
||||||
private string _swagger = string.Empty;
|
private string _swagger = string.Empty;
|
||||||
private string _packageservice = string.Empty;
|
private string _packageservice = string.Empty;
|
||||||
|
|
||||||
@ -117,19 +168,29 @@
|
|||||||
{
|
{
|
||||||
_version = Constants.Version;
|
_version = Constants.Version;
|
||||||
|
|
||||||
Dictionary<string, string> systeminfo = await SystemService.GetSystemInfoAsync();
|
Dictionary<string, object> systeminfo = await SystemService.GetSystemInfoAsync("environment");
|
||||||
if (systeminfo != null)
|
if (systeminfo != null)
|
||||||
{
|
{
|
||||||
_clrversion = systeminfo["clrversion"];
|
_clrversion = systeminfo["CLRVersion"].ToString();
|
||||||
_osversion = systeminfo["osversion"];
|
_osversion = systeminfo["OSVersion"].ToString();
|
||||||
_serverpath = systeminfo["serverpath"];
|
_machinename = systeminfo["MachineName"].ToString();
|
||||||
_servertime = systeminfo["servertime"] + " UTC";
|
_ipaddress = systeminfo["IPAddress"].ToString();
|
||||||
_installationid = systeminfo["installationid"];
|
_contentrootpath = systeminfo["ContentRootPath"].ToString();
|
||||||
|
_webrootpath = systeminfo["WebRootPath"].ToString();
|
||||||
|
_servertime = systeminfo["ServerTime"].ToString() + " UTC";
|
||||||
|
_tickcount = TimeSpan.FromMilliseconds(Convert.ToInt64(systeminfo["TickCount"].ToString())).ToString();
|
||||||
|
_workingset = (Convert.ToInt64(systeminfo["WorkingSet"].ToString()) / 1000000).ToString() + " MB";
|
||||||
|
}
|
||||||
|
|
||||||
_detailederrors = systeminfo["detailederrors"];
|
systeminfo = await SystemService.GetSystemInfoAsync();
|
||||||
_logginglevel = systeminfo["logginglevel"];
|
if (systeminfo != null)
|
||||||
_swagger = systeminfo["swagger"];
|
{
|
||||||
_packageservice = systeminfo["packageservice"];
|
_installationid = systeminfo["InstallationId"].ToString();
|
||||||
|
_detailederrors = systeminfo["DetailedErrors"].ToString();
|
||||||
|
_logginglevel = systeminfo["Logging:LogLevel:Default"].ToString();
|
||||||
|
_notificationlevel = systeminfo["Logging:LogLevel:Notify"].ToString();
|
||||||
|
_swagger = systeminfo["UseSwagger"].ToString();
|
||||||
|
_packageservice = systeminfo["PackageService"].ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,11 +198,12 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var settings = new Dictionary<string, string>();
|
var settings = new Dictionary<string, object>();
|
||||||
settings.Add("detailederrors", _detailederrors);
|
settings.Add("DetailedErrors", _detailederrors);
|
||||||
settings.Add("logginglevel", _logginglevel);
|
settings.Add("Logging:LogLevel:Default", _logginglevel);
|
||||||
settings.Add("swagger", _swagger);
|
settings.Add("Logging:LogLevel:Notify", _notificationlevel);
|
||||||
settings.Add("packageservice", _packageservice);
|
settings.Add("UseSwagger", _swagger);
|
||||||
|
settings.Add("PackageService", _packageservice);
|
||||||
await SystemService.UpdateSystemInfoAsync(settings);
|
await SystemService.UpdateSystemInfoAsync(settings);
|
||||||
AddModuleMessage(Localizer["Success.UpdateConfig.Restart"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.UpdateConfig.Restart"], MessageType.Success);
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@
|
|||||||
private List<Template> _templates;
|
private List<Template> _templates;
|
||||||
private string _template = "-";
|
private string _template = "-";
|
||||||
private string[] _versions;
|
private string[] _versions;
|
||||||
private string _reference = Constants.Version;
|
private string _reference = "local";
|
||||||
private string _minversion = "2.0.0";
|
private string _minversion = "2.0.0";
|
||||||
private string _location = string.Empty;
|
private string _location = string.Empty;
|
||||||
|
|
||||||
|
@ -40,9 +40,12 @@ else
|
|||||||
@((MarkupString)PurchaseLink(context.PackageName))
|
@((MarkupString)PurchaseLink(context.PackageName))
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if (UpgradeAvailable(context.PackageName, context.Version))
|
@{
|
||||||
|
var version = UpgradeAvailable(context.PackageName, context.Version);
|
||||||
|
}
|
||||||
|
@if (version != context.Version)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.PackageName, context.Version))>@SharedLocalizer["Upgrade"]</button>
|
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.PackageName, version))>@SharedLocalizer["Upgrade"]</button>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
@ -94,18 +97,17 @@ else
|
|||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UpgradeAvailable(string packagename, string version)
|
private string UpgradeAvailable(string packagename, string version)
|
||||||
{
|
{
|
||||||
var upgradeavailable = false;
|
|
||||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||||
{
|
{
|
||||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||||
if (package != null)
|
if (package != null && Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
|
||||||
{
|
{
|
||||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
return package.Version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return upgradeavailable;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadTheme(string packagename, string version)
|
private async Task DownloadTheme(string packagename, string version)
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<Label Class="col-sm-3" For="to" HelpText="Enter the username you wish to send a message to" ResourceKey="To">To: </Label>
|
<Label Class="col-sm-3" For="to" HelpText="Enter the username you wish to send a message to" ResourceKey="To">To: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="to" class="form-control" @bind="@username" />
|
<input id="to" class="form-control" @bind="@username" />
|
||||||
</div> >
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="subject" HelpText="Enter the subject of the message" ResourceKey="Subject">Subject: </Label>
|
<Label Class="col-sm-3" For="subject" HelpText="Enter the subject of the message" ResourceKey="Subject">Subject: </Label>
|
||||||
@ -49,7 +49,7 @@
|
|||||||
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
|
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
var notification = new Notification(PageState.Site.SiteId, PageState.User, user, subject, body, null);
|
var notification = new Notification(PageState.Site.SiteId, PageState.User, user, subject, body);
|
||||||
notification = await NotificationService.AddNotificationAsync(notification);
|
notification = await NotificationService.AddNotificationAsync(notification);
|
||||||
await logger.LogInformation("Notification Created {NotificationId}", notification.NotificationId);
|
await logger.LogInformation("Notification Created {NotificationId}", notification.NotificationId);
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
|
@ -32,15 +32,33 @@ else
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="If you wish to change your password you can enter it here. Please choose a sufficiently secure password." ResourceKey="Password"></Label>
|
<Label Class="col-sm-3" For="password" HelpText="If you wish to change your password you can enter it here. Please choose a sufficiently secure password." ResourceKey="Password"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="password" type="password" class="form-control" @bind="@password" autocomplete="new-password" />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="confirm" HelpText="If you are changing your password you must enter it again to confirm it matches" ResourceKey="Confirm"></Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="If you are changing your password you must enter it again to confirm it matches" ResourceKey="Confirm"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="confirm" type="password" class="form-control" @bind="@confirm" autocomplete="new-password" />
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@confirm" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (allowtwofactor)
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="twofactor" HelpText="Indicates if you are using two factor authentication. Two factor authentication requires you to enter a verification code sent via email after you sign in." ResourceKey="TwoFactor"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="twofactor" class="form-select" @bind="@twofactor" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
|
<Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -142,7 +160,7 @@ else
|
|||||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||||
<td>@context.FromDisplayName</td>
|
<td>@context.FromDisplayName</td>
|
||||||
<td>@context.Subject</td>
|
<td>@context.Subject</td>
|
||||||
<td>@context.CreatedOn</td>
|
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||||
</Row>
|
</Row>
|
||||||
<Detail>
|
<Detail>
|
||||||
<td colspan="2"></td>
|
<td colspan="2"></td>
|
||||||
@ -175,7 +193,7 @@ else
|
|||||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||||
<td>@context.ToDisplayName</td>
|
<td>@context.ToDisplayName</td>
|
||||||
<td>@context.Subject</td>
|
<td>@context.Subject</td>
|
||||||
<td>@context.CreatedOn</td>
|
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||||
</Row>
|
</Row>
|
||||||
<Detail>
|
<Detail>
|
||||||
<td colspan="2"></td>
|
<td colspan="2"></td>
|
||||||
@ -193,6 +211,8 @@ else
|
|||||||
</Detail>
|
</Detail>
|
||||||
</Pager>
|
</Pager>
|
||||||
}
|
}
|
||||||
|
<br />
|
||||||
|
<ActionDialog Header="Clear Notifications" Message="Are You Sure You Wish To Permanently Delete All Notifications ?" Action="Delete All Notifications" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllNotifications())" ResourceKey="DeleteAllNotifications" />
|
||||||
<br /><hr />
|
<br /><hr />
|
||||||
<select class="form-select" @onchange="(e => FilterChanged(e))">
|
<select class="form-select" @onchange="(e => FilterChanged(e))">
|
||||||
<option value="to">@Localizer["Inbox"]</option>
|
<option value="to">@Localizer["Inbox"]</option>
|
||||||
@ -201,11 +221,16 @@ else
|
|||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string username = string.Empty;
|
private string username = string.Empty;
|
||||||
private string password = string.Empty;
|
private string _password = string.Empty;
|
||||||
|
private string _passwordtype = "password";
|
||||||
|
private string _togglepassword = string.Empty;
|
||||||
private string confirm = string.Empty;
|
private string confirm = string.Empty;
|
||||||
|
private bool allowtwofactor = false;
|
||||||
|
private string twofactor = "False";
|
||||||
private string email = string.Empty;
|
private string email = string.Empty;
|
||||||
private string displayname = string.Empty;
|
private string displayname = string.Empty;
|
||||||
private FileManager filemanager;
|
private FileManager filemanager;
|
||||||
@ -224,9 +249,17 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
|
if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"]))
|
||||||
|
{
|
||||||
|
allowtwofactor = (PageState.Site.Settings["LoginOptions:TwoFactor"] == "true");
|
||||||
|
}
|
||||||
|
|
||||||
if (PageState.User != null)
|
if (PageState.User != null)
|
||||||
{
|
{
|
||||||
username = PageState.User.Username;
|
username = PageState.User.Username;
|
||||||
|
twofactor = PageState.User.TwoFactorRequired.ToString();
|
||||||
email = PageState.User.Email;
|
email = PageState.User.Email;
|
||||||
displayname = PageState.User.DisplayName;
|
displayname = PageState.User.DisplayName;
|
||||||
|
|
||||||
@ -280,11 +313,12 @@ else
|
|||||||
{
|
{
|
||||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||||
{
|
{
|
||||||
if (password == confirm)
|
if (_password == confirm)
|
||||||
{
|
{
|
||||||
var user = PageState.User;
|
var user = PageState.User;
|
||||||
user.Username = username;
|
user.Username = username;
|
||||||
user.Password = password;
|
user.Password = _password;
|
||||||
|
user.TwoFactorRequired = bool.Parse(twofactor);
|
||||||
user.Email = email;
|
user.Email = email;
|
||||||
user.DisplayName = (displayname == string.Empty ? username : displayname);
|
user.DisplayName = (displayname == string.Empty ? username : displayname);
|
||||||
user.PhotoFileId = filemanager.GetFileId();
|
user.PhotoFileId = filemanager.GetFileId();
|
||||||
@ -292,12 +326,23 @@ else
|
|||||||
{
|
{
|
||||||
user.PhotoFileId = null;
|
user.PhotoFileId = null;
|
||||||
}
|
}
|
||||||
|
if (user.PhotoFileId != null)
|
||||||
|
{
|
||||||
|
photofileid = user.PhotoFileId.Value;
|
||||||
|
photo = await FileService.GetFileAsync(photofileid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
photofileid = -1;
|
||||||
|
photo = null;
|
||||||
|
}
|
||||||
|
|
||||||
await UserService.UpdateUserAsync(user);
|
await UserService.UpdateUserAsync(user);
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||||
await logger.LogInformation("User Profile Saved");
|
await logger.LogInformation("User Profile Saved");
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
||||||
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -380,4 +425,47 @@ else
|
|||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAllNotifications()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach(var Notification in notifications)
|
||||||
|
{
|
||||||
|
if (!Notification.IsDeleted)
|
||||||
|
{
|
||||||
|
Notification.IsDeleted = true;
|
||||||
|
await NotificationService.UpdateNotificationAsync(Notification);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await NotificationService.DeleteNotificationAsync(Notification.NotificationId);
|
||||||
|
}
|
||||||
|
await logger.LogInformation("Notification Deleted {Notification}", Notification);
|
||||||
|
}
|
||||||
|
await logger.LogInformation("Notifications Permanently Deleted");
|
||||||
|
await LoadNotificationsAsync();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Deleting Notifications {Error}", ex.Message);
|
||||||
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,13 +21,19 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label>
|
<Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="password" type="password" class="form-control" @bind="@password" />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="confirm" type="password" class="form-control" @bind="@confirm" />
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@confirm" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -88,7 +94,9 @@
|
|||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string username = string.Empty;
|
private string username = string.Empty;
|
||||||
private string password = string.Empty;
|
private string _password = string.Empty;
|
||||||
|
private string _passwordtype = "password";
|
||||||
|
private string _togglepassword = string.Empty;
|
||||||
private string confirm = string.Empty;
|
private string confirm = string.Empty;
|
||||||
private string email = string.Empty;
|
private string email = string.Empty;
|
||||||
private string displayname = string.Empty;
|
private string displayname = string.Empty;
|
||||||
@ -102,6 +110,7 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||||
settings = new Dictionary<string, string>();
|
settings = new Dictionary<string, string>();
|
||||||
}
|
}
|
||||||
@ -119,9 +128,9 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (username != string.Empty && password != string.Empty && confirm != string.Empty && email != string.Empty && ValidateProfiles())
|
if (username != string.Empty && _password != string.Empty && confirm != string.Empty && email != string.Empty && ValidateProfiles())
|
||||||
{
|
{
|
||||||
if (password == confirm)
|
if (_password == confirm)
|
||||||
{
|
{
|
||||||
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
|
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
@ -129,7 +138,7 @@
|
|||||||
user = new User();
|
user = new User();
|
||||||
user.SiteId = PageState.Site.SiteId;
|
user.SiteId = PageState.Site.SiteId;
|
||||||
user.Username = username;
|
user.Username = username;
|
||||||
user.Password = password;
|
user.Password = _password;
|
||||||
user.Email = email;
|
user.Email = email;
|
||||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||||
user.PhotoFileId = null;
|
user.PhotoFileId = null;
|
||||||
@ -193,4 +202,17 @@
|
|||||||
settings = SettingService.SetSetting(settings, SettingName, value);
|
settings = SettingService.SetSetting(settings, SettingName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,13 +30,19 @@ else
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label>
|
<Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="password" type="password" class="form-control" @bind="@password" />
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="confirm" type="password" class="form-control" @bind="@confirm" />
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@confirm" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -66,8 +72,19 @@ else
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="lastlogin" HelpText="The date and time when the user last signed in" ResourceKey="LastLogin"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="lastlogin" class="form-control" @bind="@lastlogin" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="lastipaddress" HelpText="The IP Address of the user recorded during their last login" ResourceKey="LastIPAddress"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="lastipaddress" class="form-control" @bind="@lastipaddress" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||||
@ -133,23 +150,29 @@ else
|
|||||||
@code {
|
@code {
|
||||||
private int userid;
|
private int userid;
|
||||||
private string username = string.Empty;
|
private string username = string.Empty;
|
||||||
private string password = string.Empty;
|
private string _password = string.Empty;
|
||||||
|
private string _passwordtype = "password";
|
||||||
|
private string _togglepassword = string.Empty;
|
||||||
private string confirm = string.Empty;
|
private string confirm = string.Empty;
|
||||||
private string email = string.Empty;
|
private string email = string.Empty;
|
||||||
private string displayname = string.Empty;
|
private string displayname = string.Empty;
|
||||||
private FileManager filemanager;
|
private FileManager filemanager;
|
||||||
private int photofileid = -1;
|
private int photofileid = -1;
|
||||||
private File photo = null;
|
private File photo = null;
|
||||||
|
private string isdeleted;
|
||||||
|
private string lastlogin;
|
||||||
|
private string lastipaddress;
|
||||||
|
|
||||||
private List<Profile> profiles;
|
private List<Profile> profiles;
|
||||||
private Dictionary<string, string> settings;
|
private Dictionary<string, string> settings;
|
||||||
private string category = string.Empty;
|
private string category = string.Empty;
|
||||||
|
|
||||||
private string createdby;
|
private string createdby;
|
||||||
private DateTime createdon;
|
private DateTime createdon;
|
||||||
private string modifiedby;
|
private string modifiedby;
|
||||||
private DateTime modifiedon;
|
private DateTime modifiedon;
|
||||||
private string deletedby;
|
private string deletedby;
|
||||||
private DateTime? deletedon;
|
private DateTime? deletedon;
|
||||||
private string isdeleted;
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
@ -157,9 +180,9 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// OnParametersSetAsync is called when the edit modal is closed - in which case there is no id parameter
|
|
||||||
if (PageState.QueryString.ContainsKey("id"))
|
if (PageState.QueryString.ContainsKey("id"))
|
||||||
{
|
{
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||||
userid = Int32.Parse(PageState.QueryString["id"]);
|
userid = Int32.Parse(PageState.QueryString["id"]);
|
||||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||||
@ -178,6 +201,10 @@ else
|
|||||||
photofileid = -1;
|
photofileid = -1;
|
||||||
photo = null;
|
photo = null;
|
||||||
}
|
}
|
||||||
|
isdeleted = user.IsDeleted.ToString();
|
||||||
|
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||||
|
lastipaddress = user.LastIPAddress;
|
||||||
|
|
||||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
||||||
createdby = user.CreatedBy;
|
createdby = user.CreatedBy;
|
||||||
createdon = user.CreatedOn;
|
createdon = user.CreatedOn;
|
||||||
@ -185,7 +212,6 @@ else
|
|||||||
modifiedon = user.ModifiedOn;
|
modifiedon = user.ModifiedOn;
|
||||||
deletedby = user.DeletedBy;
|
deletedby = user.DeletedBy;
|
||||||
deletedon = user.DeletedOn;
|
deletedon = user.DeletedOn;
|
||||||
isdeleted = user.IsDeleted.ToString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,12 +231,12 @@ else
|
|||||||
{
|
{
|
||||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||||
{
|
{
|
||||||
if (password == confirm)
|
if (_password == confirm)
|
||||||
{
|
{
|
||||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||||
user.SiteId = PageState.Site.SiteId;
|
user.SiteId = PageState.Site.SiteId;
|
||||||
user.Username = username;
|
user.Username = username;
|
||||||
user.Password = password;
|
user.Password = _password;
|
||||||
user.Email = email;
|
user.Email = email;
|
||||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||||
user.PhotoFileId = null;
|
user.PhotoFileId = null;
|
||||||
@ -268,4 +294,17 @@ else
|
|||||||
settings = SettingService.SetSetting(settings, SettingName, value);
|
settings = SettingService.SetSetting(settings, SettingName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
|
{
|
||||||
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,9 @@
|
|||||||
@inject ISiteService SiteService
|
@inject ISiteService SiteService
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
@inject SiteState SiteState
|
||||||
|
|
||||||
@if (userroles == null)
|
@if (users == null)
|
||||||
{
|
{
|
||||||
<p>
|
<p>
|
||||||
<em>@SharedLocalizer["Loading"]</em>
|
<em>@SharedLocalizer["Loading"]</em>
|
||||||
@ -30,12 +31,14 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Pager Items="@userroles">
|
<Pager Items="@users" RowClass="align-middle">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
|
<th>@SharedLocalizer["Username"]</th>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
|
<th>@Localizer["LastLoginOn"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td>
|
<td>
|
||||||
@ -47,21 +50,299 @@ else
|
|||||||
<td>
|
<td>
|
||||||
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
|
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
|
||||||
</td>
|
</td>
|
||||||
<td>@context.User.DisplayName</td>
|
<td>@context.User.Username</td>
|
||||||
|
<td>@((MarkupString)string.Format("<a href=\"mailto:{0}\">{1}</a>", @context.User.Email, @context.User.DisplayName))</td>
|
||||||
|
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn)</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<Section Name="User" Heading="User Settings" ResourceKey="UserSettings">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="allowregistration" HelpText="Do you want to allow visitors to be able to register for a user account on the site" ResourceKey="AllowRegistration">Allow User Registration? </Label>
|
<Label Class="col-sm-3" For="allowregistration" HelpText="Do you want anonymous visitors to be able to register for an account on the site" ResourceKey="AllowRegistration">Allow User Registration?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="allowregistration" class="form-select" @bind="@_allowregistration" required>
|
<select id="allowregistration" class="form-select" @bind="@_allowregistration">
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
<option value="False">@SharedLocalizer["No"]</option>
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@if (_providertype != "")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="allowsitelogin" class="form-select" @bind="@_allowsitelogin">
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="allowsitelogin" class="form-control" value="@SharedLocalizer["Yes"]" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="twofactor" HelpText="Do you want users to use two factor authentication? Note that you should use the Disabled option until you have successfully verified that the Notification Job in Scheduled Jobs is enabled and your SMTP options in Site Settings are configured or else you will lock yourself out." ResourceKey="TwoFactor">Two Factor?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="twofactor" class="form-select" @bind="@_twofactor">
|
||||||
|
<option value="false">@Localizer["Disabled"]</option>
|
||||||
|
<option value="true">@Localizer["Optional"]</option>
|
||||||
|
<option value="required">@Localizer["Required"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="cookiename" HelpText="You can choose to use a custom authentication cookie name for each site. However please be aware that if you want to share an authentication cookie between sites on the same domain they need to use a consistent cookie name. Also be aware that changing the authentication cookie name will logout all current users." ResourceKey="CookieName">Cookie Name:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="cookiename" class="form-control" @bind="@_cookiename" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</Section>
|
||||||
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
<Section Name="Password" Heading="Password Settings" ResourceKey="PasswordSettings">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="minimumlength" HelpText="The Minimum Length For A Password" ResourceKey="RequiredLength">Minimum Length:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="minimumlength" class="form-control" @bind="@_minimumlength" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="uniquecharacters" HelpText="The Minimum Number Of Unique Characters Which A Password Must Contain" ResourceKey="UniqueCharacters">Unique Characters:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="uniquecharacters" class="form-control" @bind="@_uniquecharacters" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="requiredigit" HelpText="Indicate If Passwords Must Contain A Digit" ResourceKey="RequireDigit">Require Digit?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="requiredigit" class="form-select" @bind="@_requiredigit" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="requireupper" HelpText="Indicate If Passwords Must Contain An Upper Case Character" ResourceKey="RequireUpper">Require Uppercase?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="requireupper" class="form-select" @bind="@_requireupper" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="requirelower" HelpText="Indicate If Passwords Must Contain A Lower Case Character" ResourceKey="RequireLower">Require Lowercase?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="requirelower" class="form-select" @bind="@_requirelower" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="requirepunctuation" HelpText="Indicate if Passwords Must Contain A Non-alphanumeric Character (ie. Punctuation)" ResourceKey="RequirePunctuation">Require Punctuation?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="requirepunctuation" class="form-select" @bind="@_requirepunctuation" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
<Section Name="Lockout" Heading="Lockout Settings" ResourceKey="LockoutSettings">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="maximum" HelpText="The Maximum Number Of Sign In Attempts Before A User Is Locked Out" ResourceKey="MaximumFailures">Maximum Failures:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="maximum" class="form-control" @bind="@_maximumfailures" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="lockoutduration" HelpText="The Number Of Minutes A User Should Be Locked Out" ResourceKey="LockoutDuration">Lockout Duration:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="lockoutduration" class="form-control" @bind="@_lockoutduration" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
<Section Name="ExternalLogin" Heading="External Login Settings" ResourceKey="ExternalLoginSettings">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="providertype" HelpText="Select the external login provider type" ResourceKey="ProviderType">Provider Type:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="providertype" class="form-select" value="@_providertype" @onchange="(e => ProviderTypeChanged(e))">
|
||||||
|
<option value="" selected>@Localizer["Not Specified"]</option>
|
||||||
|
<option value="@AuthenticationProviderTypes.OpenIDConnect">@Localizer["OpenID Connect"]</option>
|
||||||
|
<option value="@AuthenticationProviderTypes.OAuth2">@Localizer["OAuth 2.0"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (_providertype != "")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="providername" HelpText="The external login provider name which will be displayed on the login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="providername" class="form-control" @bind="@_providername" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="authority" HelpText="The Authority Url or Issuer Url associated with the OpenID Connect provider" ResourceKey="Authority">Authority:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="authority" class="form-control" @bind="@_authority" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="metadataurl" HelpText="The discovery endpoint for obtaining metadata for this provider. Only specify if the OpenID Connect provider does not use the standard approach (ie. /.well-known/openid-configuration)" ResourceKey="MetadataUrl">Metadata Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="metadataurl" class="form-control" @bind="@_metadataurl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (_providertype == AuthenticationProviderTypes.OAuth2)
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="authorizationurl" HelpText="The endpoint for obtaining an Authorization Code" ResourceKey="AuthorizationUrl">Authorization Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="authorizationurl" class="form-control" @bind="@_authorizationurl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="tokenurl" HelpText="The endpoint for obtaining an Auth Token" ResourceKey="TokenUrl">Token Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="tokenurl" class="form-control" @bind="@_tokenurl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="userinfourl" HelpText="The endpoint for obtaining user information. This should be an API or Page Url which contains the users email address." ResourceKey="UserInfoUrl">User Info Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="userinfourl" class="form-control" @bind="@_userinfourl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (_providertype != "")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="clientid" HelpText="The Client ID from the provider" ResourceKey="ClientID">Client ID:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="clientid" class="form-control" @bind="@_clientid" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="clientsecret" HelpText="The Client Secret from the provider" ResourceKey="ClientSecret">Client Secret:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="@_clientsecrettype" id="clientsecret" class="form-control" @bind="@_clientsecret" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleClientSecret">@_toggleclientsecret</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="parameters" HelpText="Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple)." ResourceKey="Parameters">Parameters:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="parameters" class="form-control" @bind="@_parameters" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="pkce" class="form-select" @bind="@_pkce" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="redirecturl" HelpText="The Redirect Url (or Callback Url) which usually needs to be registered with the provider" ResourceKey="RedirectUrl">Redirect Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="The name of the unique user identifier claim provided by the provider" ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="identifierclaimtype" class="form-control" @bind="@_identifierclaimtype" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="emailclaimtype" HelpText="The name of the email address claim provided by the provider" ResourceKey="EmailClaimType">Email Claim:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="domainfilter" class="form-control" @bind="@_domainfilter" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="createusers" HelpText="Do you want new users to be created automatically? If you disable this option, users must already be registered on the site in order to sign in with their external login." ResourceKey="CreateUsers">Create New Users?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="createusers" class="form-select" @bind="@_createusers">
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</Section>
|
||||||
|
<Section Name="Token" Heading="Token Settings" ResourceKey="TokenSettings">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="secret" HelpText="If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated." ResourceKey="Secret">Secret:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="@_secrettype" id="secret" class="form-control" @bind="@_secret" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleSecret">@_togglesecret</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="issuer" HelpText="Optionally provide the issuer of the token" ResourceKey="Issuer">Issuer:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="issuer" class="form-control" @bind="@_issuer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="audience" HelpText="Optionally provide the audience for the token" ResourceKey="Audience">Audience:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="audience" class="form-control" @bind="@_audience" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="lifetime" HelpText="The number of minutes for which a token should be valid" ResourceKey="Lifetime">Lifetime:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="lifetime" class="form-control" @bind="@_lifetime" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="token" HelpText="Select the Create Token button to generate a long-lived access token (valid for 1 year). Be sure to store this token in a safe location as you will not be able to access it in the future." ResourceKey="Token">Access Token:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="token" class="form-control" @bind="@_token" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@CreateToken">@Localizer["CreateToken"]</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||||
@ -70,42 +351,134 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<UserRole> allroles;
|
private List<UserRole> allusers;
|
||||||
private List<UserRole> userroles;
|
private List<UserRole> users;
|
||||||
private string _search;
|
private string _search = "";
|
||||||
|
|
||||||
private string _allowregistration;
|
private string _allowregistration;
|
||||||
|
private string _allowsitelogin;
|
||||||
|
private string _twofactor;
|
||||||
|
private string _cookiename;
|
||||||
|
|
||||||
|
private string _minimumlength;
|
||||||
|
private string _uniquecharacters;
|
||||||
|
private string _requiredigit;
|
||||||
|
private string _requireupper;
|
||||||
|
private string _requirelower;
|
||||||
|
private string _requirepunctuation;
|
||||||
|
private string _maximumfailures;
|
||||||
|
private string _lockoutduration;
|
||||||
|
|
||||||
|
private string _providertype;
|
||||||
|
private string _providername;
|
||||||
|
private string _authority;
|
||||||
|
private string _metadataurl;
|
||||||
|
private string _authorizationurl;
|
||||||
|
private string _tokenurl;
|
||||||
|
private string _userinfourl;
|
||||||
|
private string _clientid;
|
||||||
|
private string _clientsecret;
|
||||||
|
private string _clientsecrettype = "password";
|
||||||
|
private string _toggleclientsecret = string.Empty;
|
||||||
|
private string _scopes;
|
||||||
|
private string _parameters;
|
||||||
|
private string _pkce;
|
||||||
|
private string _redirecturl;
|
||||||
|
private string _identifierclaimtype;
|
||||||
|
private string _emailclaimtype;
|
||||||
|
private string _domainfilter;
|
||||||
|
private string _createusers;
|
||||||
|
|
||||||
|
private string _secret;
|
||||||
|
private string _secrettype = "password";
|
||||||
|
private string _togglesecret = string.Empty;
|
||||||
|
private string _issuer;
|
||||||
|
private string _audience;
|
||||||
|
private string _lifetime;
|
||||||
|
private string _token;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
await LoadUserSettingsAsync();
|
||||||
await LoadSettingsAsync();
|
await LoadUsersAsync(true);
|
||||||
userroles = Search(_search);
|
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
_allowregistration = PageState.Site.AllowRegistration.ToString();
|
_allowregistration = PageState.Site.AllowRegistration.ToString();
|
||||||
|
_allowsitelogin = SettingService.GetSetting(settings, "LoginOptions:AllowSiteLogin", "true");
|
||||||
|
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
_twofactor = SettingService.GetSetting(settings, "LoginOptions:TwoFactor", "false");
|
||||||
|
_cookiename = SettingService.GetSetting(settings, "LoginOptions:CookieName", ".AspNetCore.Identity.Application");
|
||||||
|
|
||||||
|
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
|
||||||
|
_uniquecharacters = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", "1");
|
||||||
|
_requiredigit = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireDigit", "true");
|
||||||
|
_requireupper = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireUppercase", "true");
|
||||||
|
_requirelower = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireLowercase", "true");
|
||||||
|
_requirepunctuation = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", "true");
|
||||||
|
|
||||||
|
_maximumfailures = SettingService.GetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", "5");
|
||||||
|
_lockoutduration = TimeSpan.Parse(SettingService.GetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", "00:05:00")).TotalMinutes.ToString();
|
||||||
|
|
||||||
|
_providertype = SettingService.GetSetting(settings, "ExternalLogin:ProviderType", "");
|
||||||
|
_providername = SettingService.GetSetting(settings, "ExternalLogin:ProviderName", "");
|
||||||
|
_authority = SettingService.GetSetting(settings, "ExternalLogin:Authority", "");
|
||||||
|
_metadataurl = SettingService.GetSetting(settings, "ExternalLogin:MetadataUrl", "");
|
||||||
|
_authorizationurl = SettingService.GetSetting(settings, "ExternalLogin:AuthorizationUrl", "");
|
||||||
|
_tokenurl = SettingService.GetSetting(settings, "ExternalLogin:TokenUrl", "");
|
||||||
|
_userinfourl = SettingService.GetSetting(settings, "ExternalLogin:UserInfoUrl", "");
|
||||||
|
_clientid = SettingService.GetSetting(settings, "ExternalLogin:ClientId", "");
|
||||||
|
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
||||||
|
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
||||||
|
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
||||||
|
_parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", "");
|
||||||
|
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
||||||
|
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||||
|
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
|
||||||
|
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
|
||||||
|
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
|
||||||
|
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "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 List<UserRole> Search(string search)
|
private async Task LoadUsersAsync(bool load)
|
||||||
{
|
{
|
||||||
var results = allroles.Where(item => item.Role.Name == RoleNames.Registered || (item.Role.Name == RoleNames.Host && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)));
|
if (load)
|
||||||
|
{
|
||||||
|
allusers = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
var hosts = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Host);
|
||||||
|
allusers.AddRange(hosts);
|
||||||
|
allusers = allusers.OrderBy(u => u.User.DisplayName).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
users = allusers;
|
||||||
if (!string.IsNullOrEmpty(_search))
|
if (!string.IsNullOrEmpty(_search))
|
||||||
{
|
{
|
||||||
results = results.Where(item =>
|
users = users.Where(item =>
|
||||||
(
|
(
|
||||||
item.User.Username.Contains(search, StringComparison.OrdinalIgnoreCase) ||
|
item.User.Username.Contains(_search, StringComparison.OrdinalIgnoreCase) ||
|
||||||
item.User.Email.Contains(search, StringComparison.OrdinalIgnoreCase) ||
|
item.User.Email.Contains(_search, StringComparison.OrdinalIgnoreCase) ||
|
||||||
item.User.DisplayName.Contains(search, StringComparison.OrdinalIgnoreCase)
|
item.User.DisplayName.Contains(_search, StringComparison.OrdinalIgnoreCase)
|
||||||
)
|
)
|
||||||
);
|
).ToList();
|
||||||
}
|
}
|
||||||
return results.ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnSearch()
|
private async Task OnSearch()
|
||||||
{
|
{
|
||||||
userroles = Search(_search);
|
await UpdateUserSettingsAsync();
|
||||||
await UpdateSettingsAsync();
|
await LoadUsersAsync(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeleteUser(UserRole UserRole)
|
private async Task DeleteUser(UserRole UserRole)
|
||||||
@ -117,8 +490,7 @@ else
|
|||||||
{
|
{
|
||||||
await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId);
|
await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId);
|
||||||
await logger.LogInformation("User Deleted {User}", UserRole.User);
|
await logger.LogInformation("User Deleted {User}", UserRole.User);
|
||||||
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
await LoadUsersAsync(true);
|
||||||
userroles = Search(_search);
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,13 +503,13 @@ else
|
|||||||
|
|
||||||
private string settingSearch = "AU-search";
|
private string settingSearch = "AU-search";
|
||||||
|
|
||||||
private async Task LoadSettingsAsync()
|
private async Task LoadUserSettingsAsync()
|
||||||
{
|
{
|
||||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||||
_search = SettingService.GetSetting(settings, settingSearch, "");
|
_search = SettingService.GetSetting(settings, settingSearch, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateSettingsAsync()
|
private async Task UpdateUserSettingsAsync()
|
||||||
{
|
{
|
||||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||||
SettingService.SetSetting(settings, settingSearch, _search);
|
SettingService.SetSetting(settings, settingSearch, _search);
|
||||||
@ -151,6 +523,57 @@ else
|
|||||||
var site = PageState.Site;
|
var site = PageState.Site;
|
||||||
site.AllowRegistration = bool.Parse(_allowregistration);
|
site.AllowRegistration = bool.Parse(_allowregistration);
|
||||||
await SiteService.UpdateSiteAsync(site);
|
await SiteService.UpdateSiteAsync(site);
|
||||||
|
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||||
|
settings = SettingService.SetSetting(settings, "LoginOptions:AllowSiteLogin", _allowsitelogin, false);
|
||||||
|
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
settings = SettingService.SetSetting(settings, "LoginOptions:TwoFactor", _twofactor, false);
|
||||||
|
settings = SettingService.SetSetting(settings, "LoginOptions:CookieName", _cookiename, true);
|
||||||
|
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredLength", _minimumlength, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", _uniquecharacters, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireDigit", _requiredigit, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireUppercase", _requireupper, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireLowercase", _requirelower, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", _requirepunctuation, true);
|
||||||
|
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", _maximumfailures, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", TimeSpan.FromMinutes(Convert.ToInt64(_lockoutduration)).ToString(), true);
|
||||||
|
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderType", _providertype, false);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderName", _providername, false);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Authority", _authority, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:MetadataUrl", _metadataurl, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:AuthorizationUrl", _authorizationurl, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:TokenUrl", _tokenurl, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:UserInfoUrl", _userinfourl, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Parameters", _parameters, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_secret) && _secret.Length < 16) _secret = (_secret + "????????????????").Substring(0, 16);
|
||||||
|
settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "JwtOptions:Audience", _audience, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "JwtOptions:Lifetime", _lifetime, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||||
|
await SettingService.ClearSiteSettingsCacheAsync();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_secret))
|
||||||
|
{
|
||||||
|
SiteState.AuthorizationToken = await UserService.GetTokenAsync();
|
||||||
|
}
|
||||||
|
|
||||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -160,4 +583,58 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ProviderTypeChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
_providertype = (string)e.Value;
|
||||||
|
if (string.IsNullOrEmpty(_providername))
|
||||||
|
{
|
||||||
|
if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||||
|
{
|
||||||
|
_scopes = "openid,profile,email";
|
||||||
|
_identifierclaimtype = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
|
||||||
|
_emailclaimtype = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_scopes = "";
|
||||||
|
_identifierclaimtype = "sub";
|
||||||
|
_emailclaimtype = "email";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CreateToken()
|
||||||
|
{
|
||||||
|
_token = await UserService.GetPersonalAccessTokenAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleClientSecret()
|
||||||
|
{
|
||||||
|
if (_clientsecrettype == "password")
|
||||||
|
{
|
||||||
|
_clientsecrettype = "text";
|
||||||
|
_toggleclientsecret = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_clientsecrettype = "password";
|
||||||
|
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleSecret()
|
||||||
|
{
|
||||||
|
if (_secrettype == "password")
|
||||||
|
{
|
||||||
|
_secrettype = "text";
|
||||||
|
_togglesecret = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_secrettype = "password";
|
||||||
|
_togglesecret = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,8 +110,7 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, userid);
|
||||||
userroles = userroles.Where(item => item.UserId == userid).ToList();
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
@inject IStringLocalizer<Detail> Localizer
|
@inject IStringLocalizer<Detail> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
|
@if (_initialized)
|
||||||
|
{
|
||||||
<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="ip" HelpText="The last recorded IP address for this visitor" ResourceKey="IP">IP Address: </Label>
|
<Label Class="col-sm-3" For="ip" HelpText="The last recorded IP address for this visitor" ResourceKey="IP">IP Address: </Label>
|
||||||
@ -66,10 +68,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@CloseUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private bool _initialized = false;
|
||||||
private int _visitorId;
|
private int _visitorId;
|
||||||
private string _ip = string.Empty;
|
private string _ip = string.Empty;
|
||||||
private string _language = string.Empty;
|
private string _language = string.Empty;
|
||||||
@ -108,6 +112,7 @@
|
|||||||
_user = user.DisplayName;
|
_user = user.DisplayName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -120,4 +125,16 @@
|
|||||||
AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string CloseUrl()
|
||||||
|
{
|
||||||
|
if (!PageState.QueryString.ContainsKey("type"))
|
||||||
|
{
|
||||||
|
return NavigateUrl();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NavigateUrl(PageState.Page.Path, "type=" + PageState.QueryString["type"] + "&days=" + PageState.QueryString["days"] + "&page=" + PageState.QueryString["page"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,13 +17,13 @@ else
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<select id="type" class="form-select custom-select" @onchange="(e => TypeChanged(e))">
|
<select id="type" class="form-select custom-select" value="@_type" @onchange="(e => TypeChanged(e))">
|
||||||
<option value="false">@Localizer["AllVisitors"]</option>
|
<option value="visitors">@Localizer["AllVisitors"]</option>
|
||||||
<option value="true">@Localizer["UsersOnly"]</option>
|
<option value="users">@Localizer["UsersOnly"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<select id="type" class="form-select custom-select" @onchange="(e => DateChanged(e))">
|
<select id="days" class="form-select custom-select" value="@_days" @onchange="(e => DaysChanged(e))">
|
||||||
<option value="1">@Localizer["PastDay"]</option>
|
<option value="1">@Localizer["PastDay"]</option>
|
||||||
<option value="7">@Localizer["PastWeek"]</option>
|
<option value="7">@Localizer["PastWeek"]</option>
|
||||||
<option value="30">@Localizer["PastMonth"]</option>
|
<option value="30">@Localizer["PastMonth"]</option>
|
||||||
@ -32,7 +32,7 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<Pager Items="@_visitors">
|
<Pager Items="@_visitors" CurrentPage="@_page.ToString()" OnPageChange="OnPageChange">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@Localizer["IP"]</th>
|
<th>@Localizer["IP"]</th>
|
||||||
@ -43,7 +43,7 @@ else
|
|||||||
<th>@Localizer["Created"]</th>
|
<th>@Localizer["Created"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Detail" Parameters="@($"id=" + context.VisitorId.ToString())" ResourceKey="Details" /></td>
|
<td><ActionLink Action="Detail" Parameters="@($"id=" + context.VisitorId.ToString() + "&type=" + _type.ToString() + "&days=" + _days.ToString() + "&page=" + _page.ToString())" ResourceKey="Details" /></td>
|
||||||
<td>@context.IPAddress</td>
|
<td>@context.IPAddress</td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.UserId != null)
|
@if (context.UserId != null)
|
||||||
@ -70,7 +70,7 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="filter" HelpText="Comma delimited list of terms which may exist in IP addresses, user agents, or languages which identify visitors which should not be tracked (ie. bots)" ResourceKey="Filter">Filter: </Label>
|
<Label Class="col-sm-3" For="filter" HelpText="Comma delimited list of terms which may exist in IP addresses, user agents, or languages identifying visitors which should not be tracked" ResourceKey="Filter">Filter: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="filter" class="form-control" @bind="@_filter" rows="3"></textarea>
|
<textarea id="filter" class="form-control" @bind="@_filter" rows="3"></textarea>
|
||||||
</div>
|
</div>
|
||||||
@ -81,6 +81,15 @@ else
|
|||||||
<input id="retention" class="form-control" @bind="@_retention" />
|
<input id="retention" class="form-control" @bind="@_retention" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="correlation" HelpText="Indicate if new visitors to this site should be correlated based on their IP Address" ResourceKey="Correlation">Correlate Visitors? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="correlation" class="form-select" @bind="@_correlation">
|
||||||
|
<option value="true">@SharedLocalizer["True"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["False"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||||
@ -89,28 +98,46 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private bool _users = false;
|
private string _type = "visitors";
|
||||||
private int _days = 1;
|
private int _days = 1;
|
||||||
|
private int _page = 1;
|
||||||
private List<Visitor> _visitors;
|
private List<Visitor> _visitors;
|
||||||
private string _tracking;
|
private string _tracking;
|
||||||
private string _filter = "";
|
private string _filter = "";
|
||||||
private string _retention = "";
|
private string _retention = "";
|
||||||
|
private string _correlation = "true";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
|
if (PageState.QueryString.ContainsKey("type"))
|
||||||
|
{
|
||||||
|
_type = PageState.QueryString["type"];
|
||||||
|
}
|
||||||
|
if (PageState.QueryString.ContainsKey("days") && int.TryParse(PageState.QueryString["days"], out int days))
|
||||||
|
{
|
||||||
|
_days = days;
|
||||||
|
}
|
||||||
|
if (PageState.QueryString.ContainsKey("page") && int.TryParse(PageState.QueryString["page"], out int page))
|
||||||
|
{
|
||||||
|
_page = page;
|
||||||
|
}
|
||||||
|
|
||||||
await GetVisitors();
|
await GetVisitors();
|
||||||
|
|
||||||
_tracking = PageState.Site.VisitorTracking.ToString();
|
_tracking = PageState.Site.VisitorTracking.ToString();
|
||||||
_filter = SettingService.GetSetting(PageState.Site.Settings, "VisitorFilter", "");
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
_retention = SettingService.GetSetting(PageState.Site.Settings, "VisitorRetention", "30");
|
_filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter);
|
||||||
|
_retention = SettingService.GetSetting(settings, "VisitorRetention", "30");
|
||||||
|
_correlation = SettingService.GetSetting(settings, "VisitorCorrelation", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void TypeChanged(ChangeEventArgs e)
|
private async void TypeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_users = bool.Parse(e.Value.ToString());
|
_type = e.Value.ToString();
|
||||||
await GetVisitors();
|
await GetVisitors();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
@ -120,7 +147,7 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void DateChanged(ChangeEventArgs e)
|
private async void DaysChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -137,7 +164,7 @@ else
|
|||||||
private async Task GetVisitors()
|
private async Task GetVisitors()
|
||||||
{
|
{
|
||||||
_visitors = await VisitorService.GetVisitorsAsync(PageState.Site.SiteId, DateTime.UtcNow.AddDays(-_days));
|
_visitors = await VisitorService.GetVisitorsAsync(PageState.Site.SiteId, DateTime.UtcNow.AddDays(-_days));
|
||||||
if (_users)
|
if (_type == "users")
|
||||||
{
|
{
|
||||||
_visitors = _visitors.Where(item => item.UserId != null).ToList();
|
_visitors = _visitors.Where(item => item.UserId != null).ToList();
|
||||||
}
|
}
|
||||||
@ -154,6 +181,7 @@ else
|
|||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
|
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
|
||||||
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention, true);
|
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "VisitorCorrelation", _correlation, true);
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
|
|
||||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||||
@ -164,4 +192,9 @@ else
|
|||||||
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnPageChange(int page)
|
||||||
|
{
|
||||||
|
_page = page;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
<input type="file" id="@_fileinputid" name="file" accept="@_filter" />
|
<input type="file" id="@_fileinputid" name="file" accept="@_filter" />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="col mt-2 text-center">
|
<div class="col mt-2 text-end">
|
||||||
<button type="button" class="btn btn-success" @onclick="UploadFile">@SharedLocalizer["Upload"]</button>
|
<button type="button" class="btn btn-success" @onclick="UploadFile">@SharedLocalizer["Upload"]</button>
|
||||||
@if (ShowFiles && GetFileId() != -1)
|
@if (ShowFiles && GetFileId() != -1)
|
||||||
{
|
{
|
||||||
|
@ -57,12 +57,12 @@
|
|||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="@Class">
|
<table class="@Class">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>@Header</tr>
|
<tr class="@RowClass">@Header</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach (var item in ItemList)
|
@foreach (var item in ItemList)
|
||||||
{
|
{
|
||||||
<tr>@Row(item)</tr>
|
<tr class="@RowClass">@Row(item)</tr>
|
||||||
@if (Detail != null)
|
@if (Detail != null)
|
||||||
{
|
{
|
||||||
<tr>@Detail(item)</tr>
|
<tr>@Detail(item)</tr>
|
||||||
@ -75,28 +75,37 @@
|
|||||||
@if (Format == "Grid" && Row != null)
|
@if (Format == "Grid" && Row != null)
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
int rows = 0;
|
||||||
|
int cols = 0;
|
||||||
if (ItemList != null)
|
if (ItemList != null)
|
||||||
|
{
|
||||||
|
if (_columns == 0)
|
||||||
|
{
|
||||||
|
count = ItemList.Count();
|
||||||
|
rows = 1;
|
||||||
|
cols = count;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
count = (int)Math.Ceiling(ItemList.Count() / (decimal)_columns) * _columns;
|
count = (int)Math.Ceiling(ItemList.Count() / (decimal)_columns) * _columns;
|
||||||
|
rows = count / _columns;
|
||||||
|
cols = _columns;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
<div class="@Class">
|
<div class="@Class">
|
||||||
@if (Header != null)
|
@for (int row = 0; row < rows; row++)
|
||||||
{
|
{
|
||||||
<div class="row"><div class="col">@Header</div></div>
|
<div class="@RowClass">
|
||||||
}
|
@for (int col = 0; col < cols; col++)
|
||||||
@for (int row = 0; row < (count / _columns); row++)
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
@for (int col = 0; col < _columns; col++)
|
|
||||||
{
|
{
|
||||||
int index = (row * _columns) + col;
|
int index = (row * _columns) + col;
|
||||||
if (index < ItemList.Count())
|
if (index < ItemList.Count())
|
||||||
{
|
{
|
||||||
<div class="col">@Row(ItemList.ElementAt(index))</div>
|
<div class="@ColumnClass">@Row(ItemList.ElementAt(index))</div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="col"> </div>
|
<div> </div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@ -160,7 +169,7 @@
|
|||||||
private int _displayPages = 5;
|
private int _displayPages = 5;
|
||||||
private int _startPage = 0;
|
private int _startPage = 0;
|
||||||
private int _endPage = 0;
|
private int _endPage = 0;
|
||||||
private int _columns = 1;
|
private int _columns = 0;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Format { get; set; } // Table or Grid
|
public string Format { get; set; } // Table or Grid
|
||||||
@ -169,10 +178,10 @@
|
|||||||
public string Toolbar { get; set; } // Top, Bottom or Both
|
public string Toolbar { get; set; } // Top, Bottom or Both
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment Header { get; set; } = null;
|
public RenderFragment Header { get; set; } = null; // only applicable to Table layouts
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment<TableItem> Row { get; set; } = null;
|
public RenderFragment<TableItem> Row { get; set; } = null; // required
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
|
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
|
||||||
@ -184,16 +193,25 @@
|
|||||||
public string PageSize { get; set; } // number of items to display on a page
|
public string PageSize { get; set; } // number of items to display on a page
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Columns { get; set; } // only applicable to Grid layouts
|
public string Columns { get; set; } // only applicable to Grid layouts - default is zero indicating use responsive behavior
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string CurrentPage { get; set; } // optional property to set the initial page to display
|
public string CurrentPage { get; set; } // sets the initial page to display
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection
|
public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Class { get; set; }
|
public string Class { get; set; } // class for the containing element - ie. <table> for Table or <div> for Grid
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string RowClass { get; set; } // class for row element - ie. <tr> for Table or <div> for Grid
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string ColumnClass { get; set; } // class for column element - only applicable to Grid format
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Action<int> OnPageChange { get; set; } // a method to be executed in the calling component when the page changes
|
||||||
|
|
||||||
private IEnumerable<TableItem> ItemList { get; set; }
|
private IEnumerable<TableItem> ItemList { get; set; }
|
||||||
|
|
||||||
@ -217,7 +235,31 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Class = "container-fluid px-0";
|
Class = "container-fluid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(RowClass))
|
||||||
|
{
|
||||||
|
if (Format == "Table")
|
||||||
|
{
|
||||||
|
RowClass = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RowClass = "row";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(ColumnClass))
|
||||||
|
{
|
||||||
|
if (Format == "Table")
|
||||||
|
{
|
||||||
|
ColumnClass = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ColumnClass = "col";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,6 +310,7 @@
|
|||||||
{
|
{
|
||||||
_endPage = _pages;
|
_endPage = _pages;
|
||||||
}
|
}
|
||||||
|
OnPageChange?.Invoke(_page);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +129,10 @@
|
|||||||
|
|
||||||
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId);
|
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId);
|
||||||
_roles.Insert(0, new Role { Name = RoleNames.Everyone });
|
_roles.Insert(0, new Role { Name = RoleNames.Everyone });
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
_roles.Add(new Role { Name = RoleNames.Host });
|
||||||
|
}
|
||||||
|
|
||||||
_permissions = new List<PermissionString>();
|
_permissions = new List<PermissionString>();
|
||||||
|
|
||||||
@ -186,9 +190,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private bool GetPermissionDisabled(string roleName)
|
private bool GetPermissionDisabled(string roleName)
|
||||||
=> roleName == RoleNames.Admin
|
=> (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) ? true : false;
|
||||||
? true
|
|
||||||
: false;
|
|
||||||
|
|
||||||
private async Task AddUser()
|
private async Task AddUser()
|
||||||
{
|
{
|
||||||
@ -250,9 +252,19 @@
|
|||||||
for (int i = 0; i < _permissions.Count; i++)
|
for (int i = 0; i < _permissions.Count; i++)
|
||||||
{
|
{
|
||||||
permission = _permissions[i];
|
permission = _permissions[i];
|
||||||
List<string> ids = permission.Permissions.Split(';').ToList();
|
List<string> ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||||
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
||||||
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
|
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
ids.Remove("!" + RoleNames.Admin); // remove deny administrators
|
||||||
|
ids.Remove("!" + RoleNames.Host); // remove deny host users
|
||||||
|
if (!ids.Contains(RoleNames.Host) && !ids.Contains(RoleNames.Admin))
|
||||||
|
{
|
||||||
|
// add administrators role if host user role is not assigned
|
||||||
|
ids.Add(RoleNames.Admin);
|
||||||
|
}
|
||||||
|
}
|
||||||
permission.Permissions = string.Join(";", ids.ToArray());
|
permission.Permissions = string.Join(";", ids.ToArray());
|
||||||
_permissions[i] = permission;
|
_permissions[i] = permission;
|
||||||
}
|
}
|
||||||
|
@ -71,11 +71,11 @@
|
|||||||
</div>
|
</div>
|
||||||
@if (ReadOnly)
|
@if (ReadOnly)
|
||||||
{
|
{
|
||||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_content" rows="10" readonly></textarea>
|
<textarea class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10" readonly></textarea>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_content" rows="10"></textarea>
|
<textarea class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
@ -87,8 +87,10 @@
|
|||||||
private ElementReference _toolBar;
|
private ElementReference _toolBar;
|
||||||
private bool _filemanagervisible = false;
|
private bool _filemanagervisible = false;
|
||||||
private FileManager _fileManager;
|
private FileManager _fileManager;
|
||||||
private string _content = string.Empty;
|
private string _richhtml = string.Empty;
|
||||||
private string _original = string.Empty;
|
private string _originalrichhtml = string.Empty;
|
||||||
|
private string _rawhtml = string.Empty;
|
||||||
|
private string _originalrawhtml = string.Empty;
|
||||||
private string _message = string.Empty;
|
private string _message = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
@ -115,25 +117,26 @@
|
|||||||
|
|
||||||
public override List<Resource> Resources => new List<Resource>()
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
{
|
{
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill1.3.7.min.js" },
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" },
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
_content = Content; // raw HTML
|
_richhtml = Content;
|
||||||
await RefreshRichText();
|
_rawhtml = Content;
|
||||||
|
_originalrawhtml = _rawhtml; // preserve for comparison later
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
|
||||||
if (firstRender)
|
|
||||||
{
|
{
|
||||||
await base.OnAfterRenderAsync(firstRender);
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
|
|
||||||
|
var interop = new RichTextEditorInterop(JSRuntime);
|
||||||
|
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
await interop.CreateEditor(
|
await interop.CreateEditor(
|
||||||
_editorElement,
|
_editorElement,
|
||||||
_toolBar,
|
_toolBar,
|
||||||
@ -142,13 +145,15 @@
|
|||||||
Theme,
|
Theme,
|
||||||
DebugLevel);
|
DebugLevel);
|
||||||
|
|
||||||
await interop.LoadEditorContent(_editorElement, Content);
|
await interop.LoadEditorContent(_editorElement, _richhtml);
|
||||||
|
|
||||||
_content = Content; // raw HTML
|
|
||||||
|
|
||||||
|
// preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor)
|
||||||
|
_originalrichhtml = await interop.GetHtml(_editorElement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await interop.LoadEditorContent(_editorElement, _richhtml);
|
||||||
}
|
}
|
||||||
// preserve a copy of the rich text content ( Quill sanitizes content so we need to retrieve it from the editor )
|
|
||||||
_original = await interop.GetHtml(_editorElement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CloseFileManager()
|
public void CloseFileManager()
|
||||||
@ -158,34 +163,40 @@
|
|||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RefreshRichText()
|
public void RefreshRichText()
|
||||||
{
|
{
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
_richhtml = _rawhtml;
|
||||||
await interop.LoadEditorContent(_editorElement, _content);
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RefreshRawHtml()
|
public async Task RefreshRawHtml()
|
||||||
{
|
{
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
var interop = new RichTextEditorInterop(JSRuntime);
|
||||||
_content = await interop.GetHtml(_editorElement);
|
_rawhtml = await interop.GetHtml(_editorElement);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetHtml()
|
public async Task<string> GetHtml()
|
||||||
{
|
{
|
||||||
// get rich text content
|
// evaluate raw html content as first priority
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
if (_rawhtml != _originalrawhtml)
|
||||||
string content = await interop.GetHtml(_editorElement);
|
|
||||||
|
|
||||||
if (_original != content)
|
|
||||||
{
|
{
|
||||||
// rich text content has changed - return it
|
return _rawhtml;
|
||||||
return content;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// return raw html content
|
// return rich text content if it has changed
|
||||||
return _content;
|
var interop = new RichTextEditorInterop(JSRuntime);
|
||||||
|
var richhtml = await interop.GetHtml(_editorElement);
|
||||||
|
if (richhtml != _originalrichhtml)
|
||||||
|
{
|
||||||
|
return richhtml;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// return original raw html content
|
||||||
|
return _originalrawhtml;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,23 +223,4 @@
|
|||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
// other rich text editor methods which can be used by developers
|
|
||||||
public async Task<string> GetText()
|
|
||||||
{
|
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
|
||||||
return await interop.GetText(_editorElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetContent()
|
|
||||||
{
|
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
|
||||||
return await interop.GetContent(_editorElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task EnableEditor(bool mode)
|
|
||||||
{
|
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
|
||||||
await interop.EnableEditor(_editorElement, mode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,12 @@
|
|||||||
@inject IStringLocalizer<Edit> Localizer
|
@inject IStringLocalizer<Edit> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@if (_content != null)
|
<TabStrip>
|
||||||
{
|
<TabPanel Name="Edit" Heading="Edit" ResourceKey="Edit">
|
||||||
|
@if (_content != null)
|
||||||
|
{
|
||||||
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" @ref="@RichTextEditorHtml"></RichTextEditor>
|
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||||
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
@if (!string.IsNullOrEmpty(_content))
|
@if (!string.IsNullOrEmpty(_content))
|
||||||
@ -20,7 +23,28 @@
|
|||||||
<br />
|
<br />
|
||||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel Name="Versions" Heading="Versions" ResourceKey="Versions">
|
||||||
|
<Pager Items="@_htmltexts">
|
||||||
|
<Header>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th>@SharedLocalizer["CreatedOn"]</th>
|
||||||
|
<th>@SharedLocalizer["CreatedBy"]</th>
|
||||||
|
</Header>
|
||||||
|
<Row>
|
||||||
|
<td><ActionLink Action="View" Security="SecurityAccessLevel.Edit" OnClick="@(async () => await View(context))" ResourceKey="View" /></td>
|
||||||
|
<td><ActionDialog Header="Restore Version" Message="@string.Format(Localizer["Confirm.Restore"], context.CreatedOn)" Action="Restore" Security="SecurityAccessLevel.Edit" Class="btn btn-success" OnClick="@(async () => await Restore(context))" ResourceKey="Restore" /></td>
|
||||||
|
<td><ActionDialog Header="Delete Version" Message="@string.Format(Localizer["Confirm.Delete"], context.CreatedOn)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" ResourceKey="Delete" /></td>
|
||||||
|
<td>@context.CreatedOn</td>
|
||||||
|
<td>@context.CreatedBy</td>
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
|
@((MarkupString)_view)
|
||||||
|
</TabPanel>
|
||||||
|
</TabStrip>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
@ -28,10 +52,10 @@
|
|||||||
public override string Title => "Edit Html/Text";
|
public override string Title => "Edit Html/Text";
|
||||||
|
|
||||||
public override List<Resource> Resources => new List<Resource>()
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
{
|
{
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.7.bubble.css" },
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" },
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.7.snow.css" }
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" }
|
||||||
};
|
};
|
||||||
|
|
||||||
private RichTextEditor RichTextEditorHtml;
|
private RichTextEditor RichTextEditorHtml;
|
||||||
@ -41,13 +65,25 @@
|
|||||||
private DateTime _createdon;
|
private DateTime _createdon;
|
||||||
private string _modifiedby;
|
private string _modifiedby;
|
||||||
private DateTime _modifiedon;
|
private DateTime _modifiedon;
|
||||||
|
private List<Models.HtmlText> _htmltexts;
|
||||||
|
private string _view = "";
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
|
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
|
||||||
|
await LoadContent();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Content {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadContent()
|
||||||
|
{
|
||||||
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
|
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||||
if (htmltext != null)
|
if (htmltext != null)
|
||||||
{
|
{
|
||||||
@ -62,12 +98,11 @@
|
|||||||
{
|
{
|
||||||
_content = string.Empty;
|
_content = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception ex)
|
_htmltexts = await HtmlTextService.GetHtmlTextsAsync(ModuleState.ModuleId);
|
||||||
{
|
_htmltexts = _htmltexts.OrderByDescending(item => item.CreatedOn).ToList();
|
||||||
await logger.LogError(ex, "Error Loading Content {Error}", ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error);
|
_view = "";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveContent()
|
private async Task SaveContent()
|
||||||
@ -77,19 +112,10 @@
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
|
var htmltext = new HtmlText();
|
||||||
if (htmltext != null)
|
|
||||||
{
|
|
||||||
htmltext.Content = content;
|
|
||||||
await HtmlTextService.UpdateHtmlTextAsync(htmltext);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
htmltext = new HtmlText();
|
|
||||||
htmltext.ModuleId = ModuleState.ModuleId;
|
htmltext.ModuleId = ModuleState.ModuleId;
|
||||||
htmltext.Content = content;
|
htmltext.Content = content;
|
||||||
await HtmlTextService.AddHtmlTextAsync(htmltext);
|
await HtmlTextService.AddHtmlTextAsync(htmltext);
|
||||||
}
|
|
||||||
|
|
||||||
await logger.LogInformation("Content Saved {HtmlText}", htmltext);
|
await logger.LogInformation("Content Saved {HtmlText}", htmltext);
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
@ -100,4 +126,69 @@
|
|||||||
AddModuleMessage(Localizer["Error.Content.Save"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Content.Save"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task View(Models.HtmlText htmltext)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
htmltext = await HtmlTextService.GetHtmlTextAsync(htmltext.HtmlTextId, htmltext.ModuleId);
|
||||||
|
if (htmltext != null)
|
||||||
|
{
|
||||||
|
_view = htmltext.Content;
|
||||||
|
_view = Utilities.FormatContent(_view, PageState.Alias, "render");
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Viewing Content {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Content.View"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Restore(Models.HtmlText htmltext)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
htmltext = await HtmlTextService.GetHtmlTextAsync(htmltext.HtmlTextId, ModuleState.ModuleId);
|
||||||
|
if (htmltext != null)
|
||||||
|
{
|
||||||
|
var content = htmltext.Content;
|
||||||
|
htmltext = new HtmlText();
|
||||||
|
htmltext.ModuleId = ModuleState.ModuleId;
|
||||||
|
htmltext.Content = content;
|
||||||
|
await HtmlTextService.AddHtmlTextAsync(htmltext);
|
||||||
|
await logger.LogInformation("Content Restored {HtmlText}", htmltext);
|
||||||
|
AddModuleMessage(Localizer["Message.Content.Restored"], MessageType.Success);
|
||||||
|
await LoadContent();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Restoring Content {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Content.Restore"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Delete(Models.HtmlText htmltext)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
htmltext = await HtmlTextService.GetHtmlTextAsync(htmltext.HtmlTextId, ModuleState.ModuleId);
|
||||||
|
if (htmltext != null)
|
||||||
|
{
|
||||||
|
await HtmlTextService.DeleteHtmlTextAsync(htmltext.HtmlTextId, htmltext.ModuleId);
|
||||||
|
await logger.LogInformation("Content Deleted {HtmlText}", htmltext);
|
||||||
|
AddModuleMessage(Localizer["Message.Content.Deleted"], MessageType.Success);
|
||||||
|
await LoadContent();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Deleting Content {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Content.Delete"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
@ -13,24 +15,29 @@ namespace Oqtane.Modules.HtmlText.Services
|
|||||||
|
|
||||||
private string ApiUrl => CreateApiUrl("HtmlText");
|
private string ApiUrl => CreateApiUrl("HtmlText");
|
||||||
|
|
||||||
|
public async Task<List<Models.HtmlText>> GetHtmlTextsAsync(int moduleId)
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<List<Models.HtmlText>>(CreateAuthorizationPolicyUrl($"{ApiUrl}?moduleid={moduleId}", EntityNames.Module, moduleId));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Models.HtmlText> GetHtmlTextAsync(int moduleId)
|
public async Task<Models.HtmlText> GetHtmlTextAsync(int moduleId)
|
||||||
{
|
{
|
||||||
return await GetJsonAsync<Models.HtmlText>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", EntityNames.Module, moduleId));
|
return await GetJsonAsync<Models.HtmlText>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", EntityNames.Module, moduleId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Models.HtmlText> GetHtmlTextAsync(int htmlTextId, int moduleId)
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<Models.HtmlText>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlTextId}/{moduleId}", EntityNames.Module, moduleId));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task AddHtmlTextAsync(Models.HtmlText htmlText)
|
public async Task AddHtmlTextAsync(Models.HtmlText htmlText)
|
||||||
{
|
{
|
||||||
await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", EntityNames.Module, htmlText.ModuleId), htmlText);
|
await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", EntityNames.Module, htmlText.ModuleId), htmlText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateHtmlTextAsync(Models.HtmlText htmlText)
|
public async Task DeleteHtmlTextAsync(int htmlTextId, int moduleId)
|
||||||
{
|
{
|
||||||
await PutJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlText.HtmlTextId}", EntityNames.Module, htmlText.ModuleId), htmlText);
|
await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlTextId}/{moduleId}", EntityNames.Module, moduleId));
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DeleteHtmlTextAsync(int moduleId)
|
|
||||||
{
|
|
||||||
await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", EntityNames.Module, moduleId));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
using Oqtane.Modules.HtmlText.Models;
|
|
||||||
|
|
||||||
namespace Oqtane.Modules.HtmlText.Services
|
namespace Oqtane.Modules.HtmlText.Services
|
||||||
{
|
{
|
||||||
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
|
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
|
||||||
public interface IHtmlTextService
|
public interface IHtmlTextService
|
||||||
{
|
{
|
||||||
Task<Models.HtmlText> GetHtmlTextAsync(int ModuleId);
|
Task<List<Models.HtmlText>> GetHtmlTextsAsync(int moduleId);
|
||||||
|
|
||||||
|
Task<Models.HtmlText> GetHtmlTextAsync(int moduleId);
|
||||||
|
|
||||||
|
Task<Models.HtmlText> GetHtmlTextAsync(int htmlTextId, int moduleId);
|
||||||
|
|
||||||
Task AddHtmlTextAsync(Models.HtmlText htmltext);
|
Task AddHtmlTextAsync(Models.HtmlText htmltext);
|
||||||
|
|
||||||
Task UpdateHtmlTextAsync(Models.HtmlText htmltext);
|
Task DeleteHtmlTextAsync(int htmlTextId, int moduleId);
|
||||||
|
|
||||||
Task DeleteHtmlTextAsync(int ModuleId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,9 +53,9 @@ namespace Oqtane.Modules
|
|||||||
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
||||||
{
|
{
|
||||||
var scripts = new List<object>();
|
var scripts = new List<object>();
|
||||||
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script && item.Declaration != ResourceDeclaration.Global))
|
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
|
||||||
{
|
{
|
||||||
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "" });
|
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module });
|
||||||
}
|
}
|
||||||
if (scripts.Any())
|
if (scripts.Any())
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RazorLangVersion>3.0</RazorLangVersion>
|
<RazorLangVersion>3.0</RazorLangVersion>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>3.0.2</Version>
|
<Version>3.1.2</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -13,7 +13,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/v3.0.2</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
@ -22,11 +22,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.0" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.3" PrivateAssets="all" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="6.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="6.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.3" />
|
||||||
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
|
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -28,8 +28,9 @@ namespace Oqtane.Client
|
|||||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||||
|
|
||||||
var httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)};
|
var httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)};
|
||||||
|
|
||||||
builder.Services.AddSingleton(httpClient);
|
builder.Services.AddSingleton(httpClient);
|
||||||
|
builder.Services.AddHttpClient(); // IHttpClientFactory for calling remote services via RemoteServiceBase
|
||||||
|
|
||||||
builder.Services.AddOptions();
|
builder.Services.AddOptions();
|
||||||
|
|
||||||
// Register localization services
|
// Register localization services
|
||||||
|
@ -130,10 +130,13 @@
|
|||||||
<value>Install Now</value>
|
<value>Install Now</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.DbConfig.Load" xml:space="preserve">
|
<data name="Error.DbConfig.Load" xml:space="preserve">
|
||||||
<value>Error loading Database Configuration Control</value>
|
<value>Error Loading Database Configuration Control</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Require.DbInfo" xml:space="preserve">
|
<data name="Message.Require.DbInfo" xml:space="preserve">
|
||||||
<value>Please Enter All Required Fields. Ensure Passwords Match And Are Greater Than 5 Characters In Length. Ensure Email Address Provided Is Valid.</value>
|
<value>Please Enter All Required Fields. Ensure Passwords Match And Email Address Provided Is Valid.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Password.Invalid" xml:space="preserve">
|
||||||
|
<value>The Password Provided Does Not Meet The Password Policy. Please Verify The Minimum Password Length And Complexity Requirements.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Register" xml:space="preserve">
|
<data name="Register" xml:space="preserve">
|
||||||
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -117,23 +117,26 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="RememberMe" xml:space="preserve">
|
|
||||||
<value>Remember Me?</value>
|
|
||||||
</data>
|
|
||||||
<data name="ForgotPassword" xml:space="preserve">
|
<data name="ForgotPassword" xml:space="preserve">
|
||||||
<value>Forgot Password</value>
|
<value>Forgot Password</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Success.Account.Verified" xml:space="preserve">
|
<data name="Success.Account.Verified" xml:space="preserve">
|
||||||
<value>User Account Verified Successfully. You Can Now Login With Your Username And Password Below.</value>
|
<value>User Account Verified Successfully. You Can Now Login With Your Username And Password Below.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Account.NotVerfied" xml:space="preserve">
|
<data name="Message.Account.NotVerified" xml:space="preserve">
|
||||||
<value>User Account Could Not Be Verified. Please Contact Your Administrator For Further Instructions.</value>
|
<value>User Account Could Not Be Verified. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Success.Account.Linked" xml:space="preserve">
|
||||||
|
<value>User Account Linked Successfully. You Can Now Login With Your External Login Below.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Account.NotLinked" xml:space="preserve">
|
||||||
|
<value>External Login Could Not Be Linked. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
<data name="Error.Login.Fail" xml:space="preserve">
|
<data name="Error.Login.Fail" xml:space="preserve">
|
||||||
<value>Login Failed. Please Remember That Passwords Are Case Sensitive And User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email.</value>
|
<value>Login Failed. Please Remember That Passwords Are Case Sensitive. If You Have Attempted To Sign In Multiple Times Unsuccessfully, Your Account Will Be Locked Out For A Period Of Time. Note That User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email If You Are A New User.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Required.UserInfo" xml:space="preserve">
|
<data name="Message.Required.UserInfo" xml:space="preserve">
|
||||||
<value>Please Provide Your Username And Password</value>
|
<value>Please Provide All Required Fields</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Info.SignedIn" xml:space="preserve">
|
<data name="Info.SignedIn" xml:space="preserve">
|
||||||
<value>You Are Already Signed In</value>
|
<value>You Are Already Signed In</value>
|
||||||
@ -147,4 +150,79 @@
|
|||||||
<data name="Message.UserDoesNotExist" xml:space="preserve">
|
<data name="Message.UserDoesNotExist" xml:space="preserve">
|
||||||
<value>User Does Not Exist</value>
|
<value>User Does Not Exist</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Code.HelpText" xml:space="preserve">
|
||||||
|
<value>Please Enter The Secure Verification Code Which Was Sent To You By Email.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Code.Placeholder" xml:space="preserve">
|
||||||
|
<value>Verification Code</value>
|
||||||
|
</data>
|
||||||
|
<data name="Code.Text" xml:space="preserve">
|
||||||
|
<value>Verification Code:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.TwoFactor.Fail" xml:space="preserve">
|
||||||
|
<value>Verification Failed. Please Ensure You Entered The Code Exactly In The Form Provided In Your Email. If You Wish To Request A New Verification Code Please Select The Cancel Option And Sign In Again. </value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.TwoFactor" xml:space="preserve">
|
||||||
|
<value>A Secure Verification Code Has Been Sent To Your Email Address. Please Enter The Code That You Received. If You Do Not Receive The Code Or You Have Lost Access To Your Email, Please Contact Your Administrator.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password.HelpText" xml:space="preserve">
|
||||||
|
<value>Please Enter The Password Related To Your Account. Remember That Passwords Are Case Sensitive. If You Attempt Unsuccessfully To Log In To Your Account Multiple Times, You Will Be Locked Out For A Period Of Time.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password.Placeholder" xml:space="preserve">
|
||||||
|
<value>Password</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password.Text" xml:space="preserve">
|
||||||
|
<value>Password:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Remember.HelpText" xml:space="preserve">
|
||||||
|
<value>Specify If You Would Like To Be Signed Back In Automatically The Next Time You Visit This Site</value>
|
||||||
|
</data>
|
||||||
|
<data name="Remember.Text" xml:space="preserve">
|
||||||
|
<value>Remember Me?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Username.HelpText" xml:space="preserve">
|
||||||
|
<value>Please Enter The Username Related To Your Account</value>
|
||||||
|
</data>
|
||||||
|
<data name="Username.Placeholder" xml:space="preserve">
|
||||||
|
<value>Username</value>
|
||||||
|
</data>
|
||||||
|
<data name="Username.Text" xml:space="preserve">
|
||||||
|
<value>Username:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Use" xml:space="preserve">
|
||||||
|
<value>Use</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.LoadLogin" xml:space="preserve">
|
||||||
|
<value>Error Loading Login</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.Login" xml:space="preserve">
|
||||||
|
<value>Error Performing Login</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.ResetPassword" xml:space="preserve">
|
||||||
|
<value>Error Resetting Password</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.DuplicateEmail" xml:space="preserve">
|
||||||
|
<value>Multiple User Accounts Already Exist With The Email Address Of Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.InvalidEmail" xml:space="preserve">
|
||||||
|
<value>The External Login Provider Did Not Provide A Valid Email Address For Your Account. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.ProviderKeyMismatch" xml:space="preserve">
|
||||||
|
<value>An Error Occurred Verifying Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.UserDoesNotExist" xml:space="preserve">
|
||||||
|
<value>A User Account Matching The Email Address Of Your External Login Does Not Exist. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.UserNotCreated" xml:space="preserve">
|
||||||
|
<value>A User Account Could Not Be Created For Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.VerificationRequired" xml:space="preserve">
|
||||||
|
<value>In Order To Link Your External Login With Your User Account You Must Verify Your Identity. Please Check Your Email For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.AccessDenied" xml:space="preserve">
|
||||||
|
<value>Your External Login Was Denied Access. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.RemoteFailure" xml:space="preserve">
|
||||||
|
<value>Your External Login Failed. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -231,4 +231,10 @@
|
|||||||
<data name="Message.Page.Deleted" xml:space="preserve">
|
<data name="Message.Page.Deleted" xml:space="preserve">
|
||||||
<value>A page with path {0} already exists for the selected parent page in the Recycle Bin. Either recover the page or remove from the Recycle Bin and create it again.</value>
|
<value>A page with path {0} already exists for the selected parent page in the Recycle Bin. Either recover the page or remove from the Recycle Bin and create it again.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Meta.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally enter meta tags (in exactly the form you want them to be included in the page output).</value>
|
||||||
|
</data>
|
||||||
|
<data name="Meta.Text" xml:space="preserve">
|
||||||
|
<value>Meta:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -264,4 +264,10 @@
|
|||||||
<data name="Clickable.Text" xml:space="preserve">
|
<data name="Clickable.Text" xml:space="preserve">
|
||||||
<value>Clickable?</value>
|
<value>Clickable?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Meta.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally enter meta tags (in exactly the form you want them to be included in the page output).</value>
|
||||||
|
</data>
|
||||||
|
<data name="Meta.Text" xml:space="preserve">
|
||||||
|
<value>Meta:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -129,8 +129,8 @@
|
|||||||
<data name="OSVersion.HelpText" xml:space="preserve">
|
<data name="OSVersion.HelpText" xml:space="preserve">
|
||||||
<value>Operating System Version</value>
|
<value>Operating System Version</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServerPath.HelpText" xml:space="preserve">
|
<data name="ContentRootPath.HelpText" xml:space="preserve">
|
||||||
<value>Server Path</value>
|
<value>Server Root Path</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServerTime.HelpText" xml:space="preserve">
|
<data name="ServerTime.HelpText" xml:space="preserve">
|
||||||
<value>Server Date/Time (in UTC)</value>
|
<value>Server Date/Time (in UTC)</value>
|
||||||
@ -144,8 +144,8 @@
|
|||||||
<data name="OSVersion.Text" xml:space="preserve">
|
<data name="OSVersion.Text" xml:space="preserve">
|
||||||
<value>OS Version: </value>
|
<value>OS Version: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServerPath.Text" xml:space="preserve">
|
<data name="ContentRootPath.Text" xml:space="preserve">
|
||||||
<value>Server Path: </value>
|
<value>Root Path: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServerTime.Text" xml:space="preserve">
|
<data name="ServerTime.Text" xml:space="preserve">
|
||||||
<value>Server Date/Time: </value>
|
<value>Server Date/Time: </value>
|
||||||
@ -231,4 +231,43 @@
|
|||||||
<data name="RestartApplication.Text" xml:space="preserve">
|
<data name="RestartApplication.Text" xml:space="preserve">
|
||||||
<value>Restart Application</value>
|
<value>Restart Application</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="None" xml:space="preserve">
|
||||||
|
<value>None</value>
|
||||||
|
</data>
|
||||||
|
<data name="NotificationLevel.HelpText" xml:space="preserve">
|
||||||
|
<value>The Minimum Logging Level For Which Notifications Should Be Sent To Host Users.</value>
|
||||||
|
</data>
|
||||||
|
<data name="NotificationLevel.Text" xml:space="preserve">
|
||||||
|
<value>Notification Level:</value>
|
||||||
|
</data>
|
||||||
|
<data name="IPAddress.HelpText" xml:space="preserve">
|
||||||
|
<value>Server IP Address</value>
|
||||||
|
</data>
|
||||||
|
<data name="IPAddress.Text" xml:space="preserve">
|
||||||
|
<value>IP Address:</value>
|
||||||
|
</data>
|
||||||
|
<data name="MachineName.HelpText" xml:space="preserve">
|
||||||
|
<value>Server Machine Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="MachineName.Text" xml:space="preserve">
|
||||||
|
<value>Machine Name:</value>
|
||||||
|
</data>
|
||||||
|
<data name="TickCount.HelpText" xml:space="preserve">
|
||||||
|
<value>Amount Of Time The Service Has Been Available And Operational</value>
|
||||||
|
</data>
|
||||||
|
<data name="TickCount.Text" xml:space="preserve">
|
||||||
|
<value>Service Uptime:</value>
|
||||||
|
</data>
|
||||||
|
<data name="WebRootPath.HelpText" xml:space="preserve">
|
||||||
|
<value>Server Web Root Path</value>
|
||||||
|
</data>
|
||||||
|
<data name="WebRootPath.Text" xml:space="preserve">
|
||||||
|
<value>Web Path:</value>
|
||||||
|
</data>
|
||||||
|
<data name="WorkingSet.HelpText" xml:space="preserve">
|
||||||
|
<value>Memory Allocation Of Service (in MB)</value>
|
||||||
|
</data>
|
||||||
|
<data name="WorkingSet.Text" xml:space="preserve">
|
||||||
|
<value>Memory Allocation:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -169,10 +169,10 @@
|
|||||||
<value>Identity</value>
|
<value>Identity</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Confirm.HelpText" xml:space="preserve">
|
<data name="Confirm.HelpText" xml:space="preserve">
|
||||||
<value>If you are changing your password you must enter it again to confirm it matches</value>
|
<value>If you are changing your password you must enter it again to confirm it matches the value entered above</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Confirm.Text" xml:space="preserve">
|
<data name="Confirm.Text" xml:space="preserve">
|
||||||
<value>Confirm Password:</value>
|
<value>Confirmation:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DisplayName.HelpText" xml:space="preserve">
|
<data name="DisplayName.HelpText" xml:space="preserve">
|
||||||
<value>Your full name</value>
|
<value>Your full name</value>
|
||||||
@ -204,4 +204,19 @@
|
|||||||
<data name="Username.Text" xml:space="preserve">
|
<data name="Username.Text" xml:space="preserve">
|
||||||
<value>Username:</value>
|
<value>Username:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TwoFactor.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicates if you are using two factor authentication. Two factor authentication requires you to enter a verification code sent via email after you sign in.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TwoFactor.Text" xml:space="preserve">
|
||||||
|
<value>Two Factor?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAllNotifications.Header" xml:space="preserve">
|
||||||
|
<value>Clear Notifications</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAllNotifications.Message" xml:space="preserve">
|
||||||
|
<value>Are You Sure You Wish To Permanently Delete All Notifications?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAllNotifications.Text" xml:space="preserve">
|
||||||
|
<value>Delete ALL Notifications</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -168,4 +168,7 @@
|
|||||||
<data name="Username.Text" xml:space="preserve">
|
<data name="Username.Text" xml:space="preserve">
|
||||||
<value>Username:</value>
|
<value>Username:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Password.Placeholder" xml:space="preserve">
|
||||||
|
<value>Password</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -183,4 +183,19 @@
|
|||||||
<data name="Profile.Heading" xml:space="preserve">
|
<data name="Profile.Heading" xml:space="preserve">
|
||||||
<value>Profile</value>
|
<value>Profile</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Password.Placeholder" xml:space="preserve">
|
||||||
|
<value>Password</value>
|
||||||
|
</data>
|
||||||
|
<data name="LastIPAddress.HelpText" xml:space="preserve">
|
||||||
|
<value>The IP Address of the user recorded during their last login</value>
|
||||||
|
</data>
|
||||||
|
<data name="LastIPAddress.Text" xml:space="preserve">
|
||||||
|
<value>Last IP Address: </value>
|
||||||
|
</data>
|
||||||
|
<data name="LastLogin.HelpText" xml:space="preserve">
|
||||||
|
<value>The date and time when the user last signed in</value>
|
||||||
|
</data>
|
||||||
|
<data name="LastLogin.Text" xml:space="preserve">
|
||||||
|
<value>Last Login:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -127,10 +127,10 @@
|
|||||||
<value>Delete User</value>
|
<value>Delete User</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AllowRegistration.HelpText" xml:space="preserve">
|
<data name="AllowRegistration.HelpText" xml:space="preserve">
|
||||||
<value>Do you want the users to be able to register for an account on the site</value>
|
<value>Do you want anonymous visitors to be able to register for an account on the site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AllowRegistration.Text" xml:space="preserve">
|
<data name="AllowRegistration.Text" xml:space="preserve">
|
||||||
<value>Allow User Registration? </value>
|
<value>Allow Registration? </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.SaveSiteSettings" xml:space="preserve">
|
<data name="Error.SaveSiteSettings" xml:space="preserve">
|
||||||
<value>Error Saving Settings</value>
|
<value>Error Saving Settings</value>
|
||||||
@ -153,4 +153,235 @@
|
|||||||
<data name="Roles.Text" xml:space="preserve">
|
<data name="Roles.Text" xml:space="preserve">
|
||||||
<value>Roles</value>
|
<value>Roles</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="LockoutDuration.HelpText" xml:space="preserve">
|
||||||
|
<value>The number of minutes a user should be locked out</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockoutDuration.Text" xml:space="preserve">
|
||||||
|
<value>Lockout Duration:</value>
|
||||||
|
</data>
|
||||||
|
<data name="MaximumFailures.HelpText" xml:space="preserve">
|
||||||
|
<value>The maximum number of sign in attempts before a user is locked out</value>
|
||||||
|
</data>
|
||||||
|
<data name="MaximumFailures.Text" xml:space="preserve">
|
||||||
|
<value>Maximum Failures:</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequireDigit.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if passwords must contain a digit</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequireDigit.Text" xml:space="preserve">
|
||||||
|
<value>Require Digit?</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequiredLength.HelpText" xml:space="preserve">
|
||||||
|
<value>The minimum length for a password</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequiredLength.Text" xml:space="preserve">
|
||||||
|
<value>Minimum Length:</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequireLower.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if passwords must contain a lower case character</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequireLower.Text" xml:space="preserve">
|
||||||
|
<value>Require Lowercase?</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequirePunctuation.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if passwords must contain a non-alphanumeric character (ie. punctuation)</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequirePunctuation.Text" xml:space="preserve">
|
||||||
|
<value>Require Punctuation?</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequireUpper.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if passwords must contain an upper case character</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequireUpper.Text" xml:space="preserve">
|
||||||
|
<value>Require Uppercase?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Success.UpdateConfig.Restart" xml:space="preserve">
|
||||||
|
<value>Configuration Updated. Please Select Restart Application For These Changes To Be Activated.</value>
|
||||||
|
</data>
|
||||||
|
<data name="UniqueCharacters.HelpText" xml:space="preserve">
|
||||||
|
<value>The minimum number of unique characters which a password must contain</value>
|
||||||
|
</data>
|
||||||
|
<data name="UniqueCharacters.Text" xml:space="preserve">
|
||||||
|
<value>Unique Characters:</value>
|
||||||
|
</data>
|
||||||
|
<data name="AllowSiteLogin.HelpText" xml:space="preserve">
|
||||||
|
<value>Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site.</value>
|
||||||
|
</data>
|
||||||
|
<data name="AllowSiteLogin.Text" xml:space="preserve">
|
||||||
|
<value>Allow Login?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Authority.HelpText" xml:space="preserve">
|
||||||
|
<value>The Authority Url or Issuer Url associated with the OpenID Connect provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="Authority.Text" xml:space="preserve">
|
||||||
|
<value>Authority:</value>
|
||||||
|
</data>
|
||||||
|
<data name="AuthorizationUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The endpoint for obtaining an Authorization Code</value>
|
||||||
|
</data>
|
||||||
|
<data name="AuthorizationUrl.Text" xml:space="preserve">
|
||||||
|
<value>Authorization Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClientID.HelpText" xml:space="preserve">
|
||||||
|
<value>The Client ID from the provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClientID.Text" xml:space="preserve">
|
||||||
|
<value>Client ID:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClientSecret.HelpText" xml:space="preserve">
|
||||||
|
<value>The Client Secret from the provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClientSecret.Text" xml:space="preserve">
|
||||||
|
<value>Client Secret:</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreateUsers.HelpText" xml:space="preserve">
|
||||||
|
<value>Do you want new users to be created automatically? If you disable this option, users must already be registered on the site in order to sign in with their external login.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreateUsers.Text" xml:space="preserve">
|
||||||
|
<value>Create New Users?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DomainFilter.HelpText" xml:space="preserve">
|
||||||
|
<value>Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses.</value>
|
||||||
|
</data>
|
||||||
|
<data name="DomainFilter.Text" xml:space="preserve">
|
||||||
|
<value>Domain Filter:</value>
|
||||||
|
</data>
|
||||||
|
<data name="EmailClaimType.HelpText" xml:space="preserve">
|
||||||
|
<value>The name of the email address claim provided by the provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="EmailClaimType.Text" xml:space="preserve">
|
||||||
|
<value>Email Claim:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginSettings.Heading" xml:space="preserve">
|
||||||
|
<value>External Login Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockoutSettings.Heading" xml:space="preserve">
|
||||||
|
<value>Lockout Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="MetadataUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The discovery endpoint for obtaining metadata for this provider. Only specify if the OpenID Connect provider does not use the standard approach (ie. /.well-known/openid-configuration)</value>
|
||||||
|
</data>
|
||||||
|
<data name="MetadataUrl.Text" xml:space="preserve">
|
||||||
|
<value>Metadata Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordSettings.Heading" xml:space="preserve">
|
||||||
|
<value>Password Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="PKCE.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if the provider supports Proof Key for Code Exchange (PKCE)</value>
|
||||||
|
</data>
|
||||||
|
<data name="PKCE.Text" xml:space="preserve">
|
||||||
|
<value>Use PKCE?</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProviderName.HelpText" xml:space="preserve">
|
||||||
|
<value>The external login provider name which will be displayed on the login page</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProviderName.Text" xml:space="preserve">
|
||||||
|
<value>Provider Name:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProviderType.HelpText" xml:space="preserve">
|
||||||
|
<value>Select the external login provider type</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProviderType.Text" xml:space="preserve">
|
||||||
|
<value>Provider Type:</value>
|
||||||
|
</data>
|
||||||
|
<data name="RedirectUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The Redirect Url (or Callback Url) which usually needs to be registered with the provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="RedirectUrl.Text" xml:space="preserve">
|
||||||
|
<value>Redirect Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Scopes.HelpText" xml:space="preserve">
|
||||||
|
<value>A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Scopes.Text" xml:space="preserve">
|
||||||
|
<value>Scopes:</value>
|
||||||
|
</data>
|
||||||
|
<data name="TokenUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The endpoint for obtaining an Auth Token</value>
|
||||||
|
</data>
|
||||||
|
<data name="TokenUrl.Text" xml:space="preserve">
|
||||||
|
<value>Token Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserInfoUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The endpoint for obtaining user information. This should be an API or Page Url which contains the users email address.</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserInfoUrl.Text" xml:space="preserve">
|
||||||
|
<value>User Info Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Audience.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally provide the audience for the token</value>
|
||||||
|
</data>
|
||||||
|
<data name="Audience.Text" xml:space="preserve">
|
||||||
|
<value>Audience:</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserSettings.Heading" xml:space="preserve">
|
||||||
|
<value>User Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="CookieName.HelpText" xml:space="preserve">
|
||||||
|
<value>You can choose to use a custom authentication cookie name for each site. However please be aware that if you want to share an authentication cookie between sites on the same domain they need to use a consistent cookie name. Also be aware that changing the authentication cookie name will logout all current users.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CookieName.Text" xml:space="preserve">
|
||||||
|
<value>Cookie Name:</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreateToken" xml:space="preserve">
|
||||||
|
<value>Create Token</value>
|
||||||
|
</data>
|
||||||
|
<data name="Issuer.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally provide the issuer of the token</value>
|
||||||
|
</data>
|
||||||
|
<data name="Issuer.Text" xml:space="preserve">
|
||||||
|
<value>Issuer:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Lifetime.HelpText" xml:space="preserve">
|
||||||
|
<value>The number of minutes for which a token should be valid</value>
|
||||||
|
</data>
|
||||||
|
<data name="Lifetime.Text" xml:space="preserve">
|
||||||
|
<value>Lifetime:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Secret.HelpText" xml:space="preserve">
|
||||||
|
<value>If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Secret.Text" xml:space="preserve">
|
||||||
|
<value>Secret:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Token.HelpText" xml:space="preserve">
|
||||||
|
<value>Select the Create Token button to generate a long-lived access token (valid for 1 year). Be sure to store this token in a safe location as you will not be able to access it in the future.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Token.Text" xml:space="preserve">
|
||||||
|
<value>Token:</value>
|
||||||
|
</data>
|
||||||
|
<data name="TokenSettings.Heading" xml:space="preserve">
|
||||||
|
<value>Token Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="TwoFactor.HelpText" xml:space="preserve">
|
||||||
|
<value>Do you want users to use two factor authentication? Note that you should use the Disabled option until you have successfully verified that the Notification Job in Scheduled Jobs is enabled and your SMTP options in Site Settings are configured or else you will lock yourself out.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TwoFactor.Text" xml:space="preserve">
|
||||||
|
<value>Two Factor?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Disabled" xml:space="preserve">
|
||||||
|
<value>Disabled</value>
|
||||||
|
</data>
|
||||||
|
<data name="Optional" xml:space="preserve">
|
||||||
|
<value>Optional</value>
|
||||||
|
</data>
|
||||||
|
<data name="Required" xml:space="preserve">
|
||||||
|
<value>Required</value>
|
||||||
|
</data>
|
||||||
|
<data name="LastLoginOn" xml:space="preserve">
|
||||||
|
<value>Last Login</value>
|
||||||
|
</data>
|
||||||
|
<data name="IdentifierClaimType.HelpText" xml:space="preserve">
|
||||||
|
<value>The name of the unique user identifier claim provided by the provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
||||||
|
<value>Identifier Claim:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Parameters.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple).</value>
|
||||||
|
</data>
|
||||||
|
<data name="Parameters.Text" xml:space="preserve">
|
||||||
|
<value>Parameters:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -175,7 +175,7 @@
|
|||||||
<value>Details</value>
|
<value>Details</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Filter.HelpText" xml:space="preserve">
|
<data name="Filter.HelpText" xml:space="preserve">
|
||||||
<value>Comma delimited list of terms which may exist in IP addresses, user agents, or languages which identify visitors which should not be tracked (ie. bots)</value>
|
<value>Comma delimited list of terms which may exist in IP addresses, user agents, or languages identifying visitors which should not be tracked</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Filter.Text" xml:space="preserve">
|
<data name="Filter.Text" xml:space="preserve">
|
||||||
<value>Filter:</value>
|
<value>Filter:</value>
|
||||||
@ -186,4 +186,10 @@
|
|||||||
<data name="Retention.Text" xml:space="preserve">
|
<data name="Retention.Text" xml:space="preserve">
|
||||||
<value>Retention (Days):</value>
|
<value>Retention (Days):</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Correlation.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if new visitors to this site should be correlated based on their IP Address</value>
|
||||||
|
</data>
|
||||||
|
<data name="Correlation.Text" xml:space="preserve">
|
||||||
|
<value>Correlate Visitors?</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -117,10 +117,52 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
|
<data name="Confirm.Delete" xml:space="preserve">
|
||||||
|
<value>Are You Sure You Wish To Delete The {0} Version?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirm.Restore" xml:space="preserve">
|
||||||
|
<value>Are You Sure You Wish To Restore The {0} Version?</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreatedBy" xml:space="preserve">
|
||||||
|
<value>Created By</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreatedOn" xml:space="preserve">
|
||||||
|
<value>Created On</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete.Header" xml:space="preserve">
|
||||||
|
<value>Delete Version</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete.Text" xml:space="preserve">
|
||||||
|
<value>Delete</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.Content.Delete" xml:space="preserve">
|
||||||
|
<value>Error Deleting Version</value>
|
||||||
|
</data>
|
||||||
<data name="Error.Content.Load" xml:space="preserve">
|
<data name="Error.Content.Load" xml:space="preserve">
|
||||||
<value>An Error Occurred Loading Content</value>
|
<value>An Error Occurred Loading Content</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Error.Content.Restore" xml:space="preserve">
|
||||||
|
<value>Error Restoring Version</value>
|
||||||
|
</data>
|
||||||
<data name="Error.Content.Save" xml:space="preserve">
|
<data name="Error.Content.Save" xml:space="preserve">
|
||||||
<value>An Error Occurred Saving Content</value>
|
<value>An Error Occurred Saving Content</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Error.Content.View" xml:space="preserve">
|
||||||
|
<value>Error Viewing Version</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Content.Deleted" xml:space="preserve">
|
||||||
|
<value>Version Deleted</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Content.Restored" xml:space="preserve">
|
||||||
|
<value>Version Restored</value>
|
||||||
|
</data>
|
||||||
|
<data name="Restore.Header" xml:space="preserve">
|
||||||
|
<value>Restore Version</value>
|
||||||
|
</data>
|
||||||
|
<data name="Restore.Text" xml:space="preserve">
|
||||||
|
<value>Restore</value>
|
||||||
|
</data>
|
||||||
|
<data name="View.Text" xml:space="preserve">
|
||||||
|
<value>View</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -259,7 +259,7 @@
|
|||||||
<value>Upgrade</value>
|
<value>Upgrade</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Username" xml:space="preserve">
|
<data name="Username" xml:space="preserve">
|
||||||
<value>Username:</value>
|
<value>Username</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Version" xml:space="preserve">
|
<data name="Version" xml:space="preserve">
|
||||||
<value>Version</value>
|
<value>Version</value>
|
||||||
@ -274,7 +274,7 @@
|
|||||||
<value>Full Name:</value>
|
<value>Full Name:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LocalVersion" xml:space="preserve">
|
<data name="LocalVersion" xml:space="preserve">
|
||||||
<value>Local Version</value>
|
<value>Installed Version</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Search.Source" xml:space="preserve">
|
<data name="Search.Source" xml:space="preserve">
|
||||||
<value>source</value>
|
<value>source</value>
|
||||||
@ -321,4 +321,10 @@
|
|||||||
<data name="Settings" xml:space="preserve">
|
<data name="Settings" xml:space="preserve">
|
||||||
<value>Settings</value>
|
<value>Settings</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HidePassword" xml:space="preserve">
|
||||||
|
<value>Hide</value>
|
||||||
|
</data>
|
||||||
|
<data name="ShowPassword" xml:space="preserve">
|
||||||
|
<value>Show</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -12,18 +12,12 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class AliasService : ServiceBase, IAliasService
|
public class AliasService : ServiceBase, IAliasService
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor - should only be used by Dependency Injection
|
/// Constructor - should only be used by Dependency Injection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AliasService(HttpClient http, SiteState siteState) : base(http)
|
public AliasService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ApiUrl => CreateApiUrl("Alias", _siteState.Alias);
|
private string ApiUrl => CreateApiUrl("Alias");
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<List<Alias>> GetAliasesAsync()
|
public async Task<List<Alias>> GetAliasesAsync()
|
||||||
|
@ -11,15 +11,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class DatabaseService : ServiceBase, IDatabaseService
|
public class DatabaseService : ServiceBase, IDatabaseService
|
||||||
{
|
{
|
||||||
|
public DatabaseService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Database");
|
||||||
|
|
||||||
public DatabaseService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Database", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Database>> GetDatabasesAsync()
|
public async Task<List<Database>> GetDatabasesAsync()
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@ -17,13 +18,13 @@ namespace Oqtane.Services
|
|||||||
private readonly SiteState _siteState;
|
private readonly SiteState _siteState;
|
||||||
private readonly IJSRuntime _jsRuntime;
|
private readonly IJSRuntime _jsRuntime;
|
||||||
|
|
||||||
public FileService(HttpClient http, SiteState siteState, IJSRuntime jsRuntime) : base(http)
|
public FileService(HttpClient http, SiteState siteState, IJSRuntime jsRuntime) : base(http, siteState)
|
||||||
{
|
{
|
||||||
_siteState = siteState;
|
_siteState = siteState;
|
||||||
_jsRuntime = jsRuntime;
|
_jsRuntime = jsRuntime;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("File", _siteState.Alias);
|
private string Apiurl => CreateApiUrl("File");
|
||||||
|
|
||||||
public async Task<List<File>> GetFilesAsync(int folderId)
|
public async Task<List<File>> GetFilesAsync(int folderId)
|
||||||
{
|
{
|
||||||
@ -32,7 +33,8 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
public async Task<List<File>> GetFilesAsync(string folder)
|
public async Task<List<File>> GetFilesAsync(string folder)
|
||||||
{
|
{
|
||||||
return await GetJsonAsync<List<File>>($"{Apiurl}?folder={folder}");
|
List<File> files = await GetJsonAsync<List<File>>($"{Apiurl}?folder={folder}");
|
||||||
|
return files.OrderBy(item => item.Name).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<File>> GetFilesAsync(int siteId, string folderPath)
|
public async Task<List<File>> GetFilesAsync(int siteId, string folderPath)
|
||||||
@ -44,7 +46,8 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
var path = WebUtility.UrlEncode(folderPath);
|
var path = WebUtility.UrlEncode(folderPath);
|
||||||
|
|
||||||
return await GetJsonAsync<List<File>>($"{Apiurl}/{siteId}/{path}");
|
List<File> files = await GetJsonAsync<List<File>>($"{Apiurl}/{siteId}/{path}");
|
||||||
|
return files.OrderBy(item => item.Name).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<File> GetFileAsync(int fileId)
|
public async Task<File> GetFileAsync(int fileId)
|
||||||
@ -82,7 +85,7 @@ namespace Oqtane.Services
|
|||||||
string result = "";
|
string result = "";
|
||||||
|
|
||||||
var interop = new Interop(_jsRuntime);
|
var interop = new Interop(_jsRuntime);
|
||||||
await interop.UploadFiles($"{Apiurl}/upload", folder, id);
|
await interop.UploadFiles($"{Apiurl}/upload", folder, id, _siteState.AntiForgeryToken);
|
||||||
|
|
||||||
// uploading files is asynchronous so we need to wait for the upload to complete
|
// uploading files is asynchronous so we need to wait for the upload to complete
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
@ -14,14 +14,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class FolderService : ServiceBase, IFolderService
|
public class FolderService : ServiceBase, IFolderService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public FolderService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public FolderService(HttpClient http, SiteState siteState) : base(http)
|
private string ApiUrl => CreateApiUrl("Folder");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ApiUrl => CreateApiUrl("Folder", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Folder>> GetFoldersAsync(int siteId)
|
public async Task<List<Folder>> GetFoldersAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
@ -15,13 +15,15 @@ namespace Oqtane.Services
|
|||||||
private readonly NavigationManager _navigationManager;
|
private readonly NavigationManager _navigationManager;
|
||||||
private readonly SiteState _siteState;
|
private readonly SiteState _siteState;
|
||||||
|
|
||||||
public InstallationService(HttpClient http, NavigationManager navigationManager, SiteState siteState) : base(http)
|
public InstallationService(HttpClient http, SiteState siteState, NavigationManager navigationManager) : base(http, siteState)
|
||||||
{
|
{
|
||||||
_navigationManager = navigationManager;
|
_navigationManager = navigationManager;
|
||||||
_siteState = siteState;
|
_siteState = siteState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ApiUrl => CreateApiUrl("Installation", null, ControllerRoutes.ApiRoute); // tenant agnostic
|
private string ApiUrl => (_siteState.Alias == null)
|
||||||
|
? CreateApiUrl("Installation", null, ControllerRoutes.ApiRoute) // tenant agnostic needed for initial installation
|
||||||
|
: CreateApiUrl("Installation", _siteState.Alias);
|
||||||
|
|
||||||
public async Task<Installation> IsInstalled()
|
public async Task<Installation> IsInstalled()
|
||||||
{
|
{
|
||||||
@ -48,14 +50,5 @@ namespace Oqtane.Services
|
|||||||
{
|
{
|
||||||
await PostJsonAsync($"{ApiUrl}/register?email={WebUtility.UrlEncode(email)}", true);
|
await PostJsonAsync($"{ApiUrl}/register?email={WebUtility.UrlEncode(email)}", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetAntiForgeryTokenHeader(string antiforgerytokenvalue)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(antiforgerytokenvalue))
|
|
||||||
{
|
|
||||||
AddRequestHeader(Constants.AntiForgeryTokenHeaderName, antiforgerytokenvalue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,10 +42,5 @@ namespace Oqtane.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task RegisterAsync(string email);
|
Task RegisterAsync(string email);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the antiforgerytoken header so that it is included on all HttpClient calls for the lifetime of the app
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
void SetAntiForgeryTokenHeader(string antiforgerytokenvalue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,12 @@ namespace Oqtane.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task UpdateSiteSettingsAsync(Dictionary<string, string> siteSettings, int siteId);
|
Task UpdateSiteSettingsAsync(Dictionary<string, string> siteSettings, int siteId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears site option cache
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task ClearSiteSettingsCacheAsync();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a key-value dictionary of all page settings for the given page
|
/// Returns a key-value dictionary of all page settings for the given page
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -149,7 +155,6 @@ namespace Oqtane.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<Dictionary<string, string>> GetSettingsAsync(string entityName, int entityId);
|
Task<Dictionary<string, string>> GetSettingsAsync(string entityName, int entityId);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates settings for a given entityName and Id
|
/// Updates settings for a given entityName and Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -166,7 +171,6 @@ namespace Oqtane.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<Setting> GetSettingAsync(string entityName, int settingId);
|
Task<Setting> GetSettingAsync(string entityName, int settingId);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new setting
|
/// Creates a new setting
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -9,16 +9,34 @@ namespace Oqtane.Services
|
|||||||
public interface ISystemService
|
public interface ISystemService
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// returns a key-value directory with the current system information (os-version, clr-version, etc.)
|
/// returns a key-value directory with the current system configuration information
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<Dictionary<string, string>> GetSystemInfoAsync();
|
Task<Dictionary<string, object>> GetSystemInfoAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// returns a key-value directory with the current system information - "environment" or "configuration"
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<Dictionary<string, object>> GetSystemInfoAsync(string type);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// returns a config value
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<object> GetSystemInfoAsync(string settingKey, object defaultValue);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates system information
|
/// Updates system information
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="settings"></param>
|
/// <param name="settings"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task UpdateSystemInfoAsync(Dictionary<string, string> settings);
|
Task UpdateSystemInfoAsync(Dictionary<string, object> settings);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// updates a config value
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task UpdateSystemInfoAsync(string settingKey, object settingValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,31 @@ namespace Oqtane.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<List<UserRole>> GetUserRolesAsync(int siteId);
|
Task<List<UserRole>> GetUserRolesAsync(int siteId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all <see cref="UserRole"/>s on a <see cref="Site"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="siteId">ID-reference to a <see cref="Site"/></param>
|
||||||
|
/// <param name="userId">ID-reference to a <see cref="User"/></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<List<UserRole>> GetUserRolesAsync(int siteId, int userId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all <see cref="UserRole"/>s on a <see cref="Site"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="siteId">ID-reference to a <see cref="Site"/></param>
|
||||||
|
/// <param name="roleName">Name reference a <see cref="Role"/></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<List<UserRole>> GetUserRolesAsync(int siteId, string roleName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all <see cref="UserRole"/>s on a <see cref="Site"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="siteId">ID-reference to a <see cref="Site"/></param>
|
||||||
|
/// <param name="userId">ID-reference to a <see cref="User"/></param>
|
||||||
|
/// <param name="roleName">Name reference a <see cref="Role"/></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<List<UserRole>> GetUserRolesAsync(int siteId, int userId, string roleName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get one specific <see cref="UserRole"/>
|
/// Get one specific <see cref="UserRole"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -54,10 +54,8 @@ namespace Oqtane.Services
|
|||||||
/// Note that this will probably not be a real User, but a user object where the `Username` and `Password` have been filled.
|
/// Note that this will probably not be a real User, but a user object where the `Username` and `Password` have been filled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user">A <see cref="User"/> object which should have at least the <see cref="User.Username"/> and <see cref="User.Password"/> set.</param>
|
/// <param name="user">A <see cref="User"/> object which should have at least the <see cref="User.Username"/> and <see cref="User.Password"/> set.</param>
|
||||||
/// <param name="setCookie">Determines if the login should be stored in the cookie.</param>
|
|
||||||
/// <param name="isPersistent">Determines if the login should be persisted in the cookie for a long time.</param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<User> LoginUserAsync(User user, bool setCookie, bool isPersistent);
|
Task<User> LoginUserAsync(User user);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logout a <see cref="User"/>
|
/// Logout a <see cref="User"/>
|
||||||
@ -88,5 +86,45 @@ namespace Oqtane.Services
|
|||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<User> ResetPasswordAsync(User user, string token);
|
Task<User> ResetPasswordAsync(User user, string token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify the two factor verification code <see cref="User"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<User> VerifyTwoFactorAsync(User user, string token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validate a users password against the password policy
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="password"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<bool> ValidatePasswordAsync(string password);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get token for current user
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<string> GetTokenAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get personal access token for current user (administrators only)
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<string> GetPersonalAccessTokenAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Link an external login with a local user account
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The <see cref="User"/> we're verifying</param>
|
||||||
|
/// <param name="token">A Hash value in the URL which verifies this user got the e-mail (containing this token)</param>
|
||||||
|
/// <param name="type">External Login provider type</param>
|
||||||
|
/// <param name="key">External Login provider key</param>
|
||||||
|
/// <param name="name">External Login provider display name</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<User> LinkUserAsync(User user, string token, string type, string key, string name);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,14 +11,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class JobLogService : ServiceBase, IJobLogService
|
public class JobLogService : ServiceBase, IJobLogService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public JobLogService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public JobLogService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("JobLog");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("JobLog", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<JobLog>> GetJobLogsAsync()
|
public async Task<List<JobLog>> GetJobLogsAsync()
|
||||||
{
|
{
|
||||||
|
@ -11,14 +11,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class JobService : ServiceBase, IJobService
|
public class JobService : ServiceBase, IJobService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public JobService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public JobService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("Job");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Job", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Job>> GetJobsAsync()
|
public async Task<List<Job>> GetJobsAsync()
|
||||||
{
|
{
|
||||||
|
@ -11,15 +11,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class LanguageService : ServiceBase, ILanguageService
|
public class LanguageService : ServiceBase, ILanguageService
|
||||||
{
|
{
|
||||||
|
public LanguageService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Language");
|
||||||
|
|
||||||
public LanguageService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Language", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Language>> GetLanguagesAsync(int siteId)
|
public async Task<List<Language>> GetLanguagesAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
@ -10,14 +10,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class LocalizationService : ServiceBase, ILocalizationService
|
public class LocalizationService : ServiceBase, ILocalizationService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public LocalizationService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public LocalizationService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("Localization");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Localization", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Culture>> GetCulturesAsync() => await GetJsonAsync<IEnumerable<Culture>>(Apiurl);
|
public async Task<IEnumerable<Culture>> GetCulturesAsync() => await GetJsonAsync<IEnumerable<Culture>>(Apiurl);
|
||||||
}
|
}
|
||||||
|
@ -14,18 +14,16 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class LogService : ServiceBase, ILogService
|
public class LogService : ServiceBase, ILogService
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private readonly SiteState _siteState;
|
||||||
private readonly NavigationManager _navigationManager;
|
private readonly NavigationManager _navigationManager;
|
||||||
|
|
||||||
public LogService(HttpClient http, SiteState siteState, NavigationManager navigationManager) : base(http)
|
public LogService(HttpClient http, SiteState siteState, NavigationManager navigationManager) : base(http, siteState)
|
||||||
{
|
{
|
||||||
|
|
||||||
_siteState = siteState;
|
_siteState = siteState;
|
||||||
_navigationManager = navigationManager;
|
_navigationManager = navigationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Log", _siteState.Alias);
|
private string Apiurl => CreateApiUrl("Log");
|
||||||
|
|
||||||
public async Task<List<Log>> GetLogsAsync(int siteId, string level, string function, int rows)
|
public async Task<List<Log>> GetLogsAsync(int siteId, string level, string function, int rows)
|
||||||
{
|
{
|
||||||
|
@ -14,16 +14,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class ModuleDefinitionService : ServiceBase, IModuleDefinitionService
|
public class ModuleDefinitionService : ServiceBase, IModuleDefinitionService
|
||||||
{
|
{
|
||||||
private readonly HttpClient _http;
|
public ModuleDefinitionService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
private readonly SiteState _siteState;
|
|
||||||
|
|
||||||
public ModuleDefinitionService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("ModuleDefinition");
|
||||||
{
|
|
||||||
_http = http;
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("ModuleDefinition", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<ModuleDefinition>> GetModuleDefinitionsAsync(int siteId)
|
public async Task<List<ModuleDefinition>> GetModuleDefinitionsAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
@ -11,15 +11,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class ModuleService : ServiceBase, IModuleService
|
public class ModuleService : ServiceBase, IModuleService
|
||||||
{
|
{
|
||||||
|
public ModuleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Module");
|
||||||
|
|
||||||
public ModuleService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Module", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Module>> GetModulesAsync(int siteId)
|
public async Task<List<Module>> GetModulesAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
@ -11,14 +11,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class NotificationService : ServiceBase, INotificationService
|
public class NotificationService : ServiceBase, INotificationService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public NotificationService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public NotificationService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("Notification");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Notification", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Notification>> GetNotificationsAsync(int siteId, string direction, int userId)
|
public async Task<List<Notification>> GetNotificationsAsync(int siteId, string direction, int userId)
|
||||||
{
|
{
|
||||||
|
@ -12,13 +12,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class PackageService : ServiceBase, IPackageService
|
public class PackageService : ServiceBase, IPackageService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public PackageService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public PackageService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("Package");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
private string Apiurl => CreateApiUrl("Package", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Package>> GetPackagesAsync(string type)
|
public async Task<List<Package>> GetPackagesAsync(string type)
|
||||||
{
|
{
|
||||||
|
@ -9,15 +9,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class PageModuleService : ServiceBase, IPageModuleService
|
public class PageModuleService : ServiceBase, IPageModuleService
|
||||||
{
|
{
|
||||||
|
public PageModuleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("PageModule");
|
||||||
|
|
||||||
public PageModuleService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("PageModule", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<PageModule> GetPageModuleAsync(int pageModuleId)
|
public async Task<PageModule> GetPageModuleAsync(int pageModuleId)
|
||||||
{
|
{
|
||||||
|
@ -13,16 +13,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class PageService : ServiceBase, IPageService
|
public class PageService : ServiceBase, IPageService
|
||||||
{
|
{
|
||||||
|
public PageService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Page");
|
||||||
|
|
||||||
public PageService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Page", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Page>> GetPagesAsync(int siteId)
|
public async Task<List<Page>> GetPagesAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
@ -11,15 +11,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class ProfileService : ServiceBase, IProfileService
|
public class ProfileService : ServiceBase, IProfileService
|
||||||
{
|
{
|
||||||
|
public ProfileService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Profile");
|
||||||
|
|
||||||
public ProfileService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Profile", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Profile>> GetProfilesAsync(int siteId)
|
public async Task<List<Profile>> GetProfilesAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
152
Oqtane.Client/Services/RemoteServiceBase.cs
Normal file
152
Oqtane.Client/Services/RemoteServiceBase.cs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Services
|
||||||
|
{
|
||||||
|
public class RemoteServiceBase
|
||||||
|
{
|
||||||
|
private readonly SiteState _siteState;
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
|
|
||||||
|
protected RemoteServiceBase(IHttpClientFactory httpClientFactory, SiteState siteState)
|
||||||
|
{
|
||||||
|
_siteState = siteState;
|
||||||
|
_httpClientFactory = httpClientFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpClient GetHttpClient()
|
||||||
|
{
|
||||||
|
return GetHttpClient(_siteState?.AuthorizationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpClient GetHttpClient(string AuthorizationToken)
|
||||||
|
{
|
||||||
|
var httpClient = _httpClientFactory.CreateClient("Remote");
|
||||||
|
if (!httpClient.DefaultRequestHeaders.Contains(HeaderNames.Authorization) && !string.IsNullOrEmpty(AuthorizationToken))
|
||||||
|
{
|
||||||
|
httpClient.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + AuthorizationToken);
|
||||||
|
}
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task GetAsync(string uri)
|
||||||
|
{
|
||||||
|
var response = await GetHttpClient().GetAsync(uri);
|
||||||
|
CheckResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<string> GetStringAsync(string uri)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await GetHttpClient().GetStringAsync(uri);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<byte[]> GetByteArrayAsync(string uri)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await GetHttpClient().GetByteArrayAsync(uri);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<T> GetJsonAsync<T>(string uri)
|
||||||
|
{
|
||||||
|
var response = await GetHttpClient().GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None);
|
||||||
|
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
||||||
|
{
|
||||||
|
return await response.Content.ReadFromJsonAsync<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task PutAsync(string uri)
|
||||||
|
{
|
||||||
|
var response = await GetHttpClient().PutAsync(uri, null);
|
||||||
|
CheckResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<T> PutJsonAsync<T>(string uri, T value)
|
||||||
|
{
|
||||||
|
return await PutJsonAsync<T, T>(uri, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<TResult> PutJsonAsync<TValue, TResult>(string uri, TValue value)
|
||||||
|
{
|
||||||
|
var response = await GetHttpClient().PutAsJsonAsync(uri, value);
|
||||||
|
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
||||||
|
{
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<TResult>();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task PostAsync(string uri)
|
||||||
|
{
|
||||||
|
var response = await GetHttpClient().PostAsync(uri, null);
|
||||||
|
CheckResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<T> PostJsonAsync<T>(string uri, T value)
|
||||||
|
{
|
||||||
|
return await PostJsonAsync<T, T>(uri, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<TResult> PostJsonAsync<TValue, TResult>(string uri, TValue value)
|
||||||
|
{
|
||||||
|
var response = await GetHttpClient().PostAsJsonAsync(uri, value);
|
||||||
|
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
||||||
|
{
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<TResult>();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task DeleteAsync(string uri)
|
||||||
|
{
|
||||||
|
var response = await GetHttpClient().DeleteAsync(uri);
|
||||||
|
CheckResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckResponse(HttpResponseMessage response)
|
||||||
|
{
|
||||||
|
if (response.IsSuccessStatusCode) return true;
|
||||||
|
if (response.StatusCode != HttpStatusCode.NoContent && response.StatusCode != HttpStatusCode.NotFound)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Request: {response.RequestMessage.RequestUri}");
|
||||||
|
Console.WriteLine($"Response status: {response.StatusCode} {response.ReasonPhrase}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ValidateJsonContent(HttpContent content)
|
||||||
|
{
|
||||||
|
var mediaType = content?.Headers.ContentType?.MediaType;
|
||||||
|
return mediaType != null && mediaType.Equals("application/json", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,16 +11,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class RoleService : ServiceBase, IRoleService
|
public class RoleService : ServiceBase, IRoleService
|
||||||
{
|
{
|
||||||
|
public RoleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Role");
|
||||||
|
|
||||||
public RoleService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Role", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Role>> GetRolesAsync(int siteId)
|
public async Task<List<Role>> GetRolesAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
@ -5,24 +5,31 @@ 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 Oqtane.Documentation;
|
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
|
||||||
namespace Oqtane.Services
|
namespace Oqtane.Services
|
||||||
{
|
{
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
|
||||||
public class ServiceBase
|
public class ServiceBase
|
||||||
{
|
{
|
||||||
private readonly HttpClient _http;
|
private readonly HttpClient _httpClient;
|
||||||
private readonly SiteState _siteState;
|
private readonly SiteState _siteState;
|
||||||
|
|
||||||
protected ServiceBase(HttpClient client, SiteState siteState)
|
protected ServiceBase(HttpClient httpClient, SiteState siteState)
|
||||||
{
|
{
|
||||||
_http = client;
|
_httpClient = httpClient;
|
||||||
_siteState = siteState;
|
_siteState = siteState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HttpClient GetHttpClient()
|
||||||
|
{
|
||||||
|
if (!_httpClient.DefaultRequestHeaders.Contains(Constants.AntiForgeryTokenHeaderName) && _siteState != null && !string.IsNullOrEmpty(_siteState.AntiForgeryToken))
|
||||||
|
{
|
||||||
|
_httpClient.DefaultRequestHeaders.Add(Constants.AntiForgeryTokenHeaderName, _siteState.AntiForgeryToken);
|
||||||
|
}
|
||||||
|
return _httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
// should be used with new constructor
|
// should be used with new constructor
|
||||||
public string CreateApiUrl(string serviceName)
|
public string CreateApiUrl(string serviceName)
|
||||||
{
|
{
|
||||||
@ -95,24 +102,9 @@ namespace Oqtane.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// note that HttpClient is registered as a Scoped(shared) service and therefore you should not use request headers whose value can vary over the lifetime of the service
|
|
||||||
protected void AddRequestHeader(string name, string value)
|
|
||||||
{
|
|
||||||
RemoveRequestHeader(name);
|
|
||||||
_http.DefaultRequestHeaders.Add(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void RemoveRequestHeader(string name)
|
|
||||||
{
|
|
||||||
if (_http.DefaultRequestHeaders.Contains(name))
|
|
||||||
{
|
|
||||||
_http.DefaultRequestHeaders.Remove(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task GetAsync(string uri)
|
protected async Task GetAsync(string uri)
|
||||||
{
|
{
|
||||||
var response = await _http.GetAsync(uri);
|
var response = await GetHttpClient().GetAsync(uri);
|
||||||
CheckResponse(response);
|
CheckResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +112,7 @@ namespace Oqtane.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await _http.GetStringAsync(uri);
|
return await GetHttpClient().GetStringAsync(uri);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -134,7 +126,7 @@ namespace Oqtane.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await _http.GetByteArrayAsync(uri);
|
return await GetHttpClient().GetByteArrayAsync(uri);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -146,7 +138,7 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
protected async Task<T> GetJsonAsync<T>(string uri)
|
protected async Task<T> GetJsonAsync<T>(string uri)
|
||||||
{
|
{
|
||||||
var response = await _http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None);
|
var response = await GetHttpClient().GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None);
|
||||||
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
||||||
{
|
{
|
||||||
return await response.Content.ReadFromJsonAsync<T>();
|
return await response.Content.ReadFromJsonAsync<T>();
|
||||||
@ -157,7 +149,7 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
protected async Task PutAsync(string uri)
|
protected async Task PutAsync(string uri)
|
||||||
{
|
{
|
||||||
var response = await _http.PutAsync(uri, null);
|
var response = await GetHttpClient().PutAsync(uri, null);
|
||||||
CheckResponse(response);
|
CheckResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +160,7 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
protected async Task<TResult> PutJsonAsync<TValue, TResult>(string uri, TValue value)
|
protected async Task<TResult> PutJsonAsync<TValue, TResult>(string uri, TValue value)
|
||||||
{
|
{
|
||||||
var response = await _http.PutAsJsonAsync(uri, value);
|
var response = await GetHttpClient().PutAsJsonAsync(uri, value);
|
||||||
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
||||||
{
|
{
|
||||||
var result = await response.Content.ReadFromJsonAsync<TResult>();
|
var result = await response.Content.ReadFromJsonAsync<TResult>();
|
||||||
@ -179,7 +171,7 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
protected async Task PostAsync(string uri)
|
protected async Task PostAsync(string uri)
|
||||||
{
|
{
|
||||||
var response = await _http.PostAsync(uri, null);
|
var response = await GetHttpClient().PostAsync(uri, null);
|
||||||
CheckResponse(response);
|
CheckResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +182,7 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
protected async Task<TResult> PostJsonAsync<TValue, TResult>(string uri, TValue value)
|
protected async Task<TResult> PostJsonAsync<TValue, TResult>(string uri, TValue value)
|
||||||
{
|
{
|
||||||
var response = await _http.PostAsJsonAsync(uri, value);
|
var response = await GetHttpClient().PostAsJsonAsync(uri, value);
|
||||||
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
||||||
{
|
{
|
||||||
var result = await response.Content.ReadFromJsonAsync<TResult>();
|
var result = await response.Content.ReadFromJsonAsync<TResult>();
|
||||||
@ -202,7 +194,7 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
protected async Task DeleteAsync(string uri)
|
protected async Task DeleteAsync(string uri)
|
||||||
{
|
{
|
||||||
var response = await _http.DeleteAsync(uri);
|
var response = await GetHttpClient().DeleteAsync(uri);
|
||||||
CheckResponse(response);
|
CheckResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +220,7 @@ namespace Oqtane.Services
|
|||||||
// This constructor is obsolete. Use ServiceBase(HttpClient client, SiteState siteState) : base(http, siteState) {} instead.
|
// This constructor is obsolete. Use ServiceBase(HttpClient client, SiteState siteState) : base(http, siteState) {} instead.
|
||||||
protected ServiceBase(HttpClient client)
|
protected ServiceBase(HttpClient client)
|
||||||
{
|
{
|
||||||
_http = client;
|
_httpClient = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("This method is obsolete. Use CreateApiUrl(string serviceName, Alias alias) in conjunction with ControllerRoutes.ApiRoute in Controllers instead.", false)]
|
[Obsolete("This method is obsolete. Use CreateApiUrl(string serviceName, Alias alias) in conjunction with ControllerRoutes.ApiRoute in Controllers instead.", false)]
|
||||||
@ -240,7 +232,7 @@ namespace Oqtane.Services
|
|||||||
[Obsolete("This property of ServiceBase is deprecated. Cross tenant service calls are not supported.", false)]
|
[Obsolete("This property of ServiceBase is deprecated. Cross tenant service calls are not supported.", false)]
|
||||||
public Alias Alias { get; set; }
|
public Alias Alias { get; set; }
|
||||||
|
|
||||||
[Obsolete("This method is obsolete. Use CreateApiUrl(string entityName, int entityId) instead.", false)]
|
[Obsolete("This method is obsolete. Use CreateAuthorizationPolicyUrl(string url, string entityName, int entityId) where entityName = EntityNames.Module instead.", false)]
|
||||||
public string CreateAuthorizationPolicyUrl(string url, int entityId)
|
public string CreateAuthorizationPolicyUrl(string url, int entityId)
|
||||||
{
|
{
|
||||||
return url + ((url.Contains("?")) ? "&" : "?") + "entityid=" + entityId.ToString();
|
return url + ((url.Contains("?")) ? "&" : "?") + "entityid=" + entityId.ToString();
|
||||||
|
@ -12,15 +12,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class SettingService : ServiceBase, ISettingService
|
public class SettingService : ServiceBase, ISettingService
|
||||||
{
|
{
|
||||||
|
public SettingService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Setting");
|
||||||
|
|
||||||
public SettingService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Setting", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<Dictionary<string, string>> GetTenantSettingsAsync()
|
public async Task<Dictionary<string, string>> GetTenantSettingsAsync()
|
||||||
{
|
{
|
||||||
@ -42,6 +36,11 @@ namespace Oqtane.Services
|
|||||||
await UpdateSettingsAsync(siteSettings, EntityNames.Site, siteId);
|
await UpdateSettingsAsync(siteSettings, EntityNames.Site, siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task ClearSiteSettingsCacheAsync()
|
||||||
|
{
|
||||||
|
await DeleteAsync($"{Apiurl}/clear");
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Dictionary<string, string>> GetPageSettingsAsync(int pageId)
|
public async Task<Dictionary<string, string>> GetPageSettingsAsync(int pageId)
|
||||||
{
|
{
|
||||||
return await GetSettingsAsync(EntityNames.Page, pageId);
|
return await GetSettingsAsync(EntityNames.Page, pageId);
|
||||||
|
@ -12,16 +12,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class SiteService : ServiceBase, ISiteService
|
public class SiteService : ServiceBase, ISiteService
|
||||||
{
|
{
|
||||||
|
public SiteService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Site");
|
||||||
|
|
||||||
public SiteService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Site", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Site>> GetSitesAsync()
|
public async Task<List<Site>> GetSitesAsync()
|
||||||
{
|
{
|
||||||
|
@ -11,13 +11,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class SiteTemplateService : ServiceBase, ISiteTemplateService
|
public class SiteTemplateService : ServiceBase, ISiteTemplateService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public SiteTemplateService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public SiteTemplateService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("SiteTemplate");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
private string Apiurl => CreateApiUrl("SiteTemplate", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<SiteTemplate>> GetSiteTemplatesAsync()
|
public async Task<List<SiteTemplate>> GetSiteTemplatesAsync()
|
||||||
{
|
{
|
||||||
|
@ -9,14 +9,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class SqlService : ServiceBase, ISqlService
|
public class SqlService : ServiceBase, ISqlService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public SqlService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public SqlService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("Sql");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Sql", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<SqlQuery> ExecuteQueryAsync(SqlQuery sqlquery)
|
public async Task<SqlQuery> ExecuteQueryAsync(SqlQuery sqlquery)
|
||||||
{
|
{
|
||||||
|
@ -11,18 +11,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class SyncService : ServiceBase, ISyncService
|
public class SyncService : ServiceBase, ISyncService
|
||||||
{
|
{
|
||||||
|
public SyncService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string ApiUrl => CreateApiUrl("Sync");
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor - should only be used by Dependency Injection
|
|
||||||
/// </summary>
|
|
||||||
public SyncService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ApiUrl => CreateApiUrl("Sync", _siteState.Alias);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<Sync> GetSyncAsync(DateTime lastSyncDate)
|
public async Task<Sync> GetSyncAsync(DateTime lastSyncDate)
|
||||||
|
@ -9,23 +9,32 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class SystemService : ServiceBase, ISystemService
|
public class SystemService : ServiceBase, ISystemService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public SystemService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public SystemService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("System");
|
||||||
|
|
||||||
|
public async Task<Dictionary<string, object>> GetSystemInfoAsync()
|
||||||
{
|
{
|
||||||
_siteState = siteState;
|
return await GetSystemInfoAsync("configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("System", _siteState.Alias);
|
public async Task<Dictionary<string, object>> GetSystemInfoAsync(string type)
|
||||||
|
|
||||||
public async Task<Dictionary<string, string>> GetSystemInfoAsync()
|
|
||||||
{
|
{
|
||||||
return await GetJsonAsync<Dictionary<string, string>>(Apiurl);
|
return await GetJsonAsync<Dictionary<string, object>>($"{Apiurl}?type={type}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateSystemInfoAsync(Dictionary<string, string> settings)
|
public async Task<object> GetSystemInfoAsync(string settingKey, object defaultValue)
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<object>($"{Apiurl}/{settingKey}/{defaultValue}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateSystemInfoAsync(Dictionary<string, object> settings)
|
||||||
{
|
{
|
||||||
await PostJsonAsync(Apiurl, settings);
|
await PostJsonAsync(Apiurl, settings);
|
||||||
}
|
}
|
||||||
|
public async Task UpdateSystemInfoAsync(string settingKey, object settingValue)
|
||||||
|
{
|
||||||
|
await PutJsonAsync($"{Apiurl}/{settingKey}/{settingValue}", "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,14 +11,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class TenantService : ServiceBase, ITenantService
|
public class TenantService : ServiceBase, ITenantService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public TenantService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public TenantService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("Tenant");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Tenant", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Tenant>> GetTenantsAsync()
|
public async Task<List<Tenant>> GetTenantsAsync()
|
||||||
{
|
{
|
||||||
|
@ -11,14 +11,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class ThemeService : ServiceBase, IThemeService
|
public class ThemeService : ServiceBase, IThemeService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public ThemeService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public ThemeService(HttpClient http, SiteState siteState) : base(http)
|
private string ApiUrl => CreateApiUrl("Theme");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ApiUrl => CreateApiUrl("Theme", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Theme>> GetThemesAsync()
|
public async Task<List<Theme>> GetThemesAsync()
|
||||||
{
|
{
|
||||||
|
@ -12,16 +12,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class UrlMappingService : ServiceBase, IUrlMappingService
|
public class UrlMappingService : ServiceBase, IUrlMappingService
|
||||||
{
|
{
|
||||||
|
public UrlMappingService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("UrlMapping");
|
||||||
|
|
||||||
public UrlMappingService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("UrlMapping", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<UrlMapping>> GetUrlMappingsAsync(int siteId, bool isMapped)
|
public async Task<List<UrlMapping>> GetUrlMappingsAsync(int siteId, bool isMapped)
|
||||||
{
|
{
|
||||||
|
@ -10,19 +10,37 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class UserRoleService : ServiceBase, IUserRoleService
|
public class UserRoleService : ServiceBase, IUserRoleService
|
||||||
{
|
{
|
||||||
|
public UserRoleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("UserRole");
|
||||||
|
|
||||||
public UserRoleService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("UserRole", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<UserRole>> GetUserRolesAsync(int siteId)
|
public async Task<List<UserRole>> GetUserRolesAsync(int siteId)
|
||||||
{
|
{
|
||||||
return await GetJsonAsync<List<UserRole>>($"{Apiurl}?siteid={siteId}");
|
return await GetUserRolesAsync(siteId, -1, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<UserRole>> GetUserRolesAsync(int siteId, int userId)
|
||||||
|
{
|
||||||
|
return await GetUserRolesAsync(siteId, userId, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<UserRole>> GetUserRolesAsync(int siteId, string roleName)
|
||||||
|
{
|
||||||
|
return await GetUserRolesAsync(siteId, -1, roleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<UserRole>> GetUserRolesAsync(int siteId, int userId, string roleName)
|
||||||
|
{
|
||||||
|
var url = $"{Apiurl}?siteid={siteId}";
|
||||||
|
if (userId != -1)
|
||||||
|
{
|
||||||
|
url += $"&userid={userId}";
|
||||||
|
}
|
||||||
|
if (roleName != "")
|
||||||
|
{
|
||||||
|
url += $"&rolename={roleName}";
|
||||||
|
}
|
||||||
|
return await GetJsonAsync<List<UserRole>>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UserRole> GetUserRoleAsync(int userRoleId)
|
public async Task<UserRole> GetUserRoleAsync(int userRoleId)
|
||||||
|
@ -3,20 +3,16 @@ using Oqtane.Models;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace Oqtane.Services
|
namespace Oqtane.Services
|
||||||
{
|
{
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class UserService : ServiceBase, IUserService
|
public class UserService : ServiceBase, IUserService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public UserService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public UserService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("User");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("User", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<User> GetUserAsync(int userId, int siteId)
|
public async Task<User> GetUserAsync(int userId, int siteId)
|
||||||
{
|
{
|
||||||
@ -43,9 +39,9 @@ namespace Oqtane.Services
|
|||||||
await DeleteAsync($"{Apiurl}/{userId}?siteid={siteId}");
|
await DeleteAsync($"{Apiurl}/{userId}?siteid={siteId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<User> LoginUserAsync(User user, bool setCookie, bool isPersistent)
|
public async Task<User> LoginUserAsync(User user)
|
||||||
{
|
{
|
||||||
return await PostJsonAsync<User>($"{Apiurl}/login?setcookie={setCookie}&persistent={isPersistent}", user);
|
return await PostJsonAsync<User>($"{Apiurl}/login", user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LogoutUserAsync(User user)
|
public async Task LogoutUserAsync(User user)
|
||||||
@ -68,5 +64,31 @@ namespace Oqtane.Services
|
|||||||
{
|
{
|
||||||
return await PostJsonAsync<User>($"{Apiurl}/reset?token={token}", user);
|
return await PostJsonAsync<User>($"{Apiurl}/reset?token={token}", user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<User> VerifyTwoFactorAsync(User user, string token)
|
||||||
|
{
|
||||||
|
return await PostJsonAsync<User>($"{Apiurl}/twofactor?token={token}", user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ValidatePasswordAsync(string password)
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<bool>($"{Apiurl}/validate/{WebUtility.UrlEncode(password)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetTokenAsync()
|
||||||
|
{
|
||||||
|
return await GetStringAsync($"{Apiurl}/token");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetPersonalAccessTokenAsync()
|
||||||
|
{
|
||||||
|
return await GetStringAsync($"{Apiurl}/personalaccesstoken");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User> LinkUserAsync(User user, string token, string type, string key, string name)
|
||||||
|
{
|
||||||
|
return await PostJsonAsync<User>($"{Apiurl}/link?token={token}&type={type}&key={key}&name={name}", user);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,16 +12,9 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class VisitorService : ServiceBase, IVisitorService
|
public class VisitorService : ServiceBase, IVisitorService
|
||||||
{
|
{
|
||||||
|
public VisitorService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Visitor");
|
||||||
|
|
||||||
public VisitorService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Visitor", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Visitor>> GetVisitorsAsync(int siteId, DateTime fromDate)
|
public async Task<List<Visitor>> GetVisitorsAsync(int siteId, DateTime fromDate)
|
||||||
{
|
{
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
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.0.2/css/bootstrap.min.css", Integrity = "sha512-usVBAd66/NpVNfBge19gws2j6JZinnca12rAe2l+d+QkLU9fiG02O1X8Q6hepIpr/EYKZvKx/I9WsnujJuOmBA==", CrossOrigin = "anonymous" },
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css", Integrity = "sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==", CrossOrigin = "anonymous" },
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", Integrity = "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", CrossOrigin = "anonymous" }
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", Integrity = "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", CrossOrigin = "anonymous" }
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
@attribute [OqtaneIgnore]
|
@attribute [OqtaneIgnore]
|
||||||
|
|
||||||
<span class="app-moduletitle">
|
<span class="app-moduletitle">
|
||||||
|
<a id="@ModuleState.PageModuleId.ToString()">
|
||||||
@((MarkupString)title)
|
@((MarkupString)title)
|
||||||
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
@ -333,9 +333,8 @@
|
|||||||
if (PageId != "-")
|
if (PageId != "-")
|
||||||
{
|
{
|
||||||
_modules = PageState.Modules
|
_modules = PageState.Modules
|
||||||
.Where(module => module.PageId == int.Parse(PageId)
|
.Where(module => module.PageId == int.Parse(PageId) &&
|
||||||
&& !module.IsDeleted
|
UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
|
||||||
&& UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
|
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
ModuleId = "-";
|
ModuleId = "-";
|
||||||
|
@ -3,7 +3,6 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.JSInterop;
|
using Microsoft.JSInterop;
|
||||||
using Oqtane.Enums;
|
using Oqtane.Enums;
|
||||||
using Oqtane.Providers;
|
|
||||||
using Oqtane.Security;
|
using Oqtane.Security;
|
||||||
using Oqtane.Services;
|
using Oqtane.Services;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
@ -32,27 +31,19 @@ namespace Oqtane.Themes.Controls
|
|||||||
|
|
||||||
protected async Task LogoutUser()
|
protected async Task LogoutUser()
|
||||||
{
|
{
|
||||||
await UserService.LogoutUserAsync(PageState.User);
|
await LoggingService.Log(PageState.Alias, PageState.Page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User.Username);
|
||||||
await LoggingService.Log(PageState.Alias, PageState.Page.PageId, PageState.ModuleId, PageState.User.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User.Username);
|
|
||||||
|
|
||||||
PageState.User = null;
|
// check if anonymous user can access page
|
||||||
bool authorizedtoviewpage = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, PageState.Page.Permissions);
|
var url = PageState.Alias.Path + "/" + PageState.Page.Path;
|
||||||
|
if (!UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.Permissions))
|
||||||
if (PageState.Runtime == Oqtane.Shared.Runtime.Server)
|
|
||||||
{
|
{
|
||||||
// server-side Blazor needs to post to the Logout page
|
url = PageState.Alias.Path;
|
||||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = !authorizedtoviewpage ? PageState.Alias.Path : PageState.Alias.Path + "/" + PageState.Page.Path };
|
}
|
||||||
string url = Utilities.TenantUrl(PageState.Alias, "/pages/logout/");
|
|
||||||
|
// post to the Logout page to complete the logout process
|
||||||
|
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url };
|
||||||
var interop = new Interop(jsRuntime);
|
var interop = new Interop(jsRuntime);
|
||||||
await interop.SubmitForm(url, fields);
|
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// client-side Blazor
|
|
||||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
|
||||||
authstateprovider.NotifyAuthenticationChanged();
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl(!authorizedtoviewpage ? PageState.Alias.Path : PageState.Page.Path, true));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ namespace Oqtane.Themes.Controls
|
|||||||
private IEnumerable<Page> GetMenuPages()
|
private IEnumerable<Page> GetMenuPages()
|
||||||
{
|
{
|
||||||
var securityLevel = int.MaxValue;
|
var securityLevel = int.MaxValue;
|
||||||
foreach (Page p in PageState.Pages.Where(item => item.IsNavigation && !item.IsDeleted))
|
foreach (Page p in PageState.Pages.Where(item => item.IsNavigation))
|
||||||
{
|
{
|
||||||
if (p.Level <= securityLevel && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
if (p.Level <= securityLevel && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
||||||
{
|
{
|
||||||
|
@ -32,9 +32,9 @@ namespace Oqtane.Themes
|
|||||||
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
||||||
{
|
{
|
||||||
var scripts = new List<object>();
|
var scripts = new List<object>();
|
||||||
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script && item.Declaration != ResourceDeclaration.Global))
|
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
|
||||||
{
|
{
|
||||||
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "" });
|
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module });
|
||||||
}
|
}
|
||||||
if (scripts.Any())
|
if (scripts.Any())
|
||||||
{
|
{
|
||||||
|
@ -72,13 +72,13 @@ namespace Oqtane.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task IncludeLink(string id, string rel, string href, string type, string integrity, string crossorigin, string key)
|
public Task IncludeLink(string id, string rel, string href, string type, string integrity, string crossorigin, string includebefore)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_jsRuntime.InvokeVoidAsync(
|
_jsRuntime.InvokeVoidAsync(
|
||||||
"Oqtane.Interop.includeLink",
|
"Oqtane.Interop.includeLink",
|
||||||
id, rel, href, type, integrity, crossorigin, key);
|
id, rel, href, type, integrity, crossorigin, includebefore);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -102,13 +102,13 @@ namespace Oqtane.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string content, string location, string key)
|
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string content, string location)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_jsRuntime.InvokeVoidAsync(
|
_jsRuntime.InvokeVoidAsync(
|
||||||
"Oqtane.Interop.includeScript",
|
"Oqtane.Interop.includeScript",
|
||||||
id, src, integrity, crossorigin, content, location, key);
|
id, src, integrity, crossorigin, content, location);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -189,13 +189,13 @@ namespace Oqtane.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task UploadFiles(string posturl, string folder, string id)
|
public Task UploadFiles(string posturl, string folder, string id, string antiforgerytoken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_jsRuntime.InvokeVoidAsync(
|
_jsRuntime.InvokeVoidAsync(
|
||||||
"Oqtane.Interop.uploadFiles",
|
"Oqtane.Interop.uploadFiles",
|
||||||
posturl, folder, id);
|
posturl, folder, id, antiforgerytoken);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -279,5 +279,19 @@ namespace Oqtane.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task ScrollToId(string id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_jsRuntime.InvokeVoidAsync(
|
||||||
|
"Oqtane.Interop.scrollToId",
|
||||||
|
id);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user