Compare commits
137 Commits
Author | SHA1 | Date | |
---|---|---|---|
8950e315f8 | |||
a703df40c0 | |||
225fce8810 | |||
bdc0f0fcdd | |||
5bbb8c4858 | |||
35b9551bfb | |||
c8c5a05b39 | |||
2771f0301a | |||
c4f04edc59 | |||
5530422846 | |||
ce77c81fb5 | |||
bc488d4ac2 | |||
fe72a10346 | |||
0da88398b4 | |||
c42de3da20 | |||
4bf9f36baa | |||
380cb192c7 | |||
8882e19ec5 | |||
7f52059b98 | |||
1ce3cc4d7c | |||
657c71e94d | |||
c8cfb3c7b7 | |||
6e7e90acf4 | |||
6d3a556d34 | |||
7d9188b659 | |||
4f0a805c79 | |||
53f3320492 | |||
f9ce51b4a5 | |||
f8bf432c0d | |||
a822482172 | |||
b22f8a0b02 | |||
227331bf24 | |||
744688cbe1 | |||
79c8126c4a | |||
0b7c8e4ef7 | |||
35e00f61d8 | |||
aba3d58df8 | |||
45984a8166 | |||
ea5655ae42 | |||
f06cb0dfbb | |||
1abae55976 | |||
a83ed40ec4 | |||
75ecae4672 | |||
16a6f942c5 | |||
aa98508e57 | |||
583383aee1 | |||
13f69f81d7 | |||
43c34fcd64 | |||
c272238539 | |||
6e0d2706a8 | |||
25a7289ce8 | |||
1ba7d045e4 | |||
a4d75befe7 | |||
88377529bc | |||
91b9a0280f | |||
25173ae85c | |||
ad3350705e | |||
a16bc5db28 | |||
51657338f5 | |||
0fe3ea25af | |||
806daaf7c9 | |||
21ff4a83b5 | |||
ecc9aa40d7 | |||
c34ca2a59b | |||
dde7094fe3 | |||
105afdfefc | |||
4c254a8686 | |||
33ca203e57 | |||
2ff4133cd4 | |||
49ad85713e | |||
1978bf151f | |||
506378de82 | |||
53ead7a03f | |||
ab979fd63c | |||
1e84a2238b | |||
5618adf86c | |||
345b0bc95f | |||
b1d6c35e99 | |||
d767f1a101 | |||
c15f2b9a12 | |||
a21a53662b | |||
ebb5340019 | |||
6108bd214e | |||
eed27e101a | |||
2767680bed | |||
4080e30b6f | |||
e89257be62 | |||
d3c40a7e8b | |||
60657d5d25 | |||
71d5ae0e68 | |||
46b8d202c7 | |||
bc193a6470 | |||
577528fa0a | |||
35e78ea633 | |||
d5d4f85003 | |||
c6a49a6f5d | |||
a3ff9373a2 | |||
e6d2e74b17 | |||
e8464206e7 | |||
b6c4934123 | |||
8ee83f738b | |||
c359300375 | |||
eb3361fa07 | |||
1b53da6749 | |||
c701895e29 | |||
e81222821e | |||
cbca8c9e93 | |||
92c4edacf2 | |||
e4c648ee92 | |||
69f6586aa9 | |||
391713b84d | |||
8c6a25e4b4 | |||
250701aff0 | |||
2dfd1bd5a2 | |||
1c7380d4cf | |||
68d9ac88b3 | |||
f6b3874668 | |||
c616878a64 | |||
ad485a68ce | |||
2ebba3b8e7 | |||
4117e6e1c5 | |||
423ee04879 | |||
1625e3ba6c | |||
5a71ab3c20 | |||
b5833bf556 | |||
6c31765965 | |||
6dc1d42d90 | |||
e273a954e6 | |||
a602a942c4 | |||
dd32b33621 | |||
355d0405f4 | |||
c1e1595d58 | |||
ef476ac9b3 | |||
a7aaa7b18b | |||
3abfbab5d1 | |||
b158474e21 | |||
cfbcc41543 |
11
.gitignore
vendored
11
.gitignore
vendored
@ -12,9 +12,7 @@ msbuild.binlog
|
||||
*.idea
|
||||
|
||||
Oqtane.Server/appsettings.json
|
||||
Oqtane.Server/Data/*.mdf
|
||||
Oqtane.Server/Data/*.ldf
|
||||
Oqtane.Server/Data/*.db
|
||||
Oqtane.Server/Data
|
||||
|
||||
/Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml
|
||||
Oqtane.Server/Content
|
||||
@ -22,3 +20,10 @@ Oqtane.Server/Packages
|
||||
Oqtane.Server/wwwroot/Content
|
||||
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
|
||||
|
@ -1,3 +1,5 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.Localization
|
||||
{
|
||||
public static class OqtaneLocalizationExtensions
|
||||
@ -18,5 +20,42 @@ namespace Microsoft.Extensions.Localization
|
||||
}
|
||||
return localizedValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an IStringLocalizer based on a type name. This extension method is useful in scenarios where the default IStringLocalizer is unable to locate the resources.
|
||||
/// </summary>
|
||||
/// <param name="localizerFactory"></param>
|
||||
/// <param name="fullTypeName">the full type name ie. GetType().FullName</param>
|
||||
/// <returns></returns>
|
||||
public static IStringLocalizer Create(this IStringLocalizerFactory localizerFactory, string fullTypeName)
|
||||
{
|
||||
var typename = fullTypeName;
|
||||
|
||||
// handle generic types
|
||||
var type = Type.GetType(fullTypeName);
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
typename = type.GetGenericTypeDefinition().FullName;
|
||||
typename = typename.Substring(0, typename.IndexOf("`")); // remove generic type info
|
||||
}
|
||||
|
||||
// format typename
|
||||
if (typename.Contains(","))
|
||||
{
|
||||
typename = typename.Substring(0, typename.IndexOf(",")); // remove assembly info
|
||||
}
|
||||
|
||||
// remove rootnamespace
|
||||
var rootnamespace = "";
|
||||
var attributes = type.Assembly.GetCustomAttributes(typeof(RootNamespaceAttribute), false);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
rootnamespace = ((RootNamespaceAttribute)attributes[0]).RootNamespace;
|
||||
}
|
||||
typename = typename.Replace(rootnamespace + ".", "");
|
||||
|
||||
// create IStringLocalizer using factory
|
||||
return localizerFactory.Create(typename, type.Assembly.GetName().Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
<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>
|
||||
<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>
|
||||
|
||||
|
@ -40,7 +40,7 @@
|
||||
<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>
|
||||
<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>
|
||||
}
|
||||
|
@ -35,7 +35,7 @@
|
||||
<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>
|
||||
<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>
|
||||
}
|
||||
|
@ -28,15 +28,8 @@
|
||||
{
|
||||
<select id="databasetype" class="form-select custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))">
|
||||
@foreach (var database in _databases)
|
||||
{
|
||||
if (database.IsDefault)
|
||||
{
|
||||
<option value="@database.Name" selected>@Localizer[@database.Name]</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
}
|
||||
@ -62,13 +55,19 @@
|
||||
<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>
|
||||
<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 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>
|
||||
<div class="col-sm-9">
|
||||
<input id="confirm" type="password" class="form-control" @bind="@_confirmPassword" />
|
||||
<div class="input-group">
|
||||
<input id="confirm" type="@_confirmPasswordType" class="form-control" @bind="@_confirmPassword" autocomplete="new-password" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleConfirmPassword">@_toggleConfirmPassword</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -104,6 +103,10 @@
|
||||
|
||||
private string _hostUsername = string.Empty;
|
||||
private string _hostPassword = string.Empty;
|
||||
private string _passwordType = "password";
|
||||
private string _confirmPasswordType = "password";
|
||||
private string _togglePassword = string.Empty;
|
||||
private string _toggleConfirmPassword = string.Empty;
|
||||
private string _confirmPassword = string.Empty;
|
||||
private string _hostEmail = string.Empty;
|
||||
private bool _register = true;
|
||||
@ -112,6 +115,9 @@
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
||||
|
||||
_databases = await DatabaseService.GetDatabasesAsync();
|
||||
if (_databases.Exists(item => item.IsDefault))
|
||||
{
|
||||
@ -218,4 +224,32 @@
|
||||
_message = Localizer["Message.Require.DbInfo"];
|
||||
}
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
{
|
||||
if (_passwordType == "password")
|
||||
{
|
||||
_passwordType = "text";
|
||||
_togglePassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_passwordType = "password";
|
||||
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleConfirmPassword()
|
||||
{
|
||||
if (_confirmPasswordType == "password")
|
||||
{
|
||||
_confirmPasswordType = "text";
|
||||
_toggleConfirmPassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_confirmPasswordType = "password";
|
||||
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ else
|
||||
@if (_availableCultures.Count() == 0)
|
||||
{
|
||||
<ModuleMessage Type="MessageType.Info" Message="@_message"></ModuleMessage>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -47,9 +48,9 @@ else
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</form>
|
||||
}
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Download" ResourceKey="Download" Security="SecurityAccessLevel.Host">
|
||||
<div class="row justify-content-center mb-3">
|
||||
@ -78,6 +79,7 @@ else
|
||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||
</td>
|
||||
<td style="width: 1px; vertical-align: middle;">
|
||||
|
@ -84,7 +84,7 @@ else
|
||||
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + ".Client." + code)).FirstOrDefault();
|
||||
if (package != null)
|
||||
{
|
||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) > 0);
|
||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -95,7 +95,7 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
_togglepassword = Localizer["ShowPassword"];
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
|
||||
if (PageState.Site.Settings.ContainsKey("LoginOptions:AllowSiteLogin") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]))
|
||||
{
|
||||
@ -117,13 +117,30 @@
|
||||
_username = PageState.QueryString["name"];
|
||||
}
|
||||
|
||||
if (PageState.QueryString.ContainsKey("token"))
|
||||
if (PageState.QueryString.ContainsKey("token") && !string.IsNullOrEmpty(_username))
|
||||
{
|
||||
var user = new User();
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
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)
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
|
||||
@ -132,7 +149,15 @@
|
||||
else
|
||||
{
|
||||
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Message.Account.NotVerfied"], MessageType.Warning);
|
||||
AddModuleMessage(Localizer["Message.Account.NotVerified"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("status"))
|
||||
{
|
||||
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -159,11 +184,11 @@
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(login))
|
||||
{
|
||||
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password};
|
||||
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password, LastIPAddress = SiteState.RemoteIPAddress};
|
||||
|
||||
if (!twofactor)
|
||||
{
|
||||
user = await UserService.LoginUserAsync(user, false, false);
|
||||
user = await UserService.LoginUserAsync(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -174,23 +199,14 @@
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
||||
|
||||
if (PageState.Runtime == Oqtane.Shared.Runtime.Server)
|
||||
{
|
||||
// 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 };
|
||||
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
||||
await interop.SubmitForm(url, fields);
|
||||
}
|
||||
else
|
||||
{
|
||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||
authstateprovider.NotifyAuthenticationChanged();
|
||||
NavigationManager.NavigateTo(NavigateUrl(_returnUrl, true));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (user.TwoFactorRequired)
|
||||
if ((PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && PageState.Site.Settings["LoginOptions:TwoFactor"] == "required") || user.TwoFactorRequired)
|
||||
{
|
||||
twofactor = true;
|
||||
validated = false;
|
||||
@ -282,12 +298,12 @@
|
||||
if (_passwordtype == "password")
|
||||
{
|
||||
_passwordtype = "text";
|
||||
_togglepassword = Localizer["HidePassword"];
|
||||
_togglepassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_passwordtype = "password";
|
||||
_togglepassword = Localizer["ShowPassword"];
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Admin.Logs
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ILogService LogService
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@ -104,6 +105,10 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
|
||||
{
|
||||
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"id={id}"));
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("level"))
|
||||
{
|
||||
_level = PageState.QueryString["level"];
|
||||
|
@ -124,6 +124,8 @@ else
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
|
||||
{
|
||||
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
|
||||
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
||||
@ -135,6 +137,11 @@ else
|
||||
GetLocation();
|
||||
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Creating Module");
|
||||
|
@ -35,6 +35,7 @@
|
||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||
</td>
|
||||
<td style="width: 1px; vertical-align: middle;">
|
||||
|
@ -81,7 +81,7 @@
|
||||
private List<Template> _templates;
|
||||
private string _template = "-";
|
||||
private string[] _versions;
|
||||
private string _reference = Constants.Version;
|
||||
private string _reference = "local";
|
||||
private string _minversion = "2.0.0";
|
||||
private string _location = string.Empty;
|
||||
|
||||
|
@ -12,11 +12,32 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row mb-3 align-items-center">
|
||||
<div class="col-sm-6">
|
||||
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
||||
@((MarkupString)" ")
|
||||
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary" />
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<select class="form-select" @onchange="(e => CategoryChanged(e))">
|
||||
@foreach (var category in _categories)
|
||||
{
|
||||
if (category == _category)
|
||||
{
|
||||
<option value="@category" selected>@category @Localizer["Modules"]</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@category">@category @Localizer["Modules"]</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Pager Items="@_moduleDefinitions">
|
||||
<Pager Items="@_moduleDefinitions.Where(item => item.Categories.Contains(_category))">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
@ -50,9 +71,12 @@ else
|
||||
@((MarkupString)PurchaseLink(context.PackageName))
|
||||
</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>
|
||||
</Row>
|
||||
@ -62,6 +86,8 @@ else
|
||||
@code {
|
||||
private List<ModuleDefinition> _moduleDefinitions;
|
||||
private List<Package> _packages;
|
||||
private List<string> _categories = new List<string>();
|
||||
private string _category = "Common";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
@ -71,6 +97,7 @@ else
|
||||
{
|
||||
_moduleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||
_packages = await PackageService.GetPackagesAsync("module");
|
||||
_categories = _moduleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -103,19 +130,17 @@ else
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@ -148,4 +173,10 @@ else
|
||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void CategoryChanged(ChangeEventArgs e)
|
||||
{
|
||||
_category = (string)e.Value;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
@ -198,6 +198,7 @@
|
||||
|
||||
var module = ModuleState;
|
||||
module.AllPages = bool.Parse(_allPages);
|
||||
module.PageModuleId = ModuleState.PageModuleId;
|
||||
module.Permissions = _permissionGrid.GetPermissions();
|
||||
await ModuleService.UpdateModuleAsync(module);
|
||||
|
||||
|
@ -27,13 +27,19 @@
|
||||
<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>
|
||||
<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 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>
|
||||
<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 class="row mb-1 align-items-center">
|
||||
@ -66,12 +72,19 @@ else
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string _confirm = string.Empty;
|
||||
private string _email = string.Empty;
|
||||
private string _displayname = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
|
||||
private async Task Register()
|
||||
{
|
||||
validated = true;
|
||||
@ -134,4 +147,18 @@ else
|
||||
{
|
||||
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">
|
||||
<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">
|
||||
<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 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>
|
||||
<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>
|
||||
@ -36,12 +42,16 @@
|
||||
private bool validated = false;
|
||||
private string _username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string _confirm = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
|
||||
if (PageState.QueryString.ContainsKey("name") && PageState.QueryString.ContainsKey("token"))
|
||||
{
|
||||
_username = PageState.QueryString["name"];
|
||||
@ -110,4 +120,18 @@
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
{
|
||||
if (_passwordtype == "password")
|
||||
{
|
||||
_passwordtype = "text";
|
||||
_togglepassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_passwordtype = "password";
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ else
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true);
|
||||
_roles = _roles.Where(item => item.Name != RoleNames.Everyone).ToList();
|
||||
_roles.RemoveAll(item => item.Name == RoleNames.Everyone || item.Name == RoleNames.Unauthenticated);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -95,11 +95,7 @@ else
|
||||
roleid = Int32.Parse(PageState.QueryString["id"]);
|
||||
Role role = await RoleService.GetRoleAsync(roleid);
|
||||
name = role.Name;
|
||||
users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
||||
users = users
|
||||
.Where(u => u.Role.Name == RoleNames.Registered)
|
||||
.OrderBy(u => u.User.DisplayName)
|
||||
.ToList();
|
||||
users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
||||
await GetUserRoles();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -113,8 +109,7 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
||||
userroles = userroles.Where(item => item.RoleId == roleid).ToList();
|
||||
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, name);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -172,20 +172,50 @@
|
||||
<Section Name="Aliases" Heading="Aliases" ResourceKey="Aliases">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="alias" HelpText="The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they should be separated by commas." ResourceKey="Aliases">Aliases: </Label>
|
||||
<Label Class="col-sm-3" For="aliases" HelpText="The list of aliases for this site" ResourceKey="Aliases">Aliases: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="defaultalias" HelpText="The default alias for the site. Requests for non-default aliases will be redirected to the default alias." ResourceKey="DefaultAlias">Default Alias: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="defaultalias" class="form-select" @bind="@_defaultalias" required>
|
||||
@foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
||||
<button type="button" class="btn btn-primary" @onclick="AddAlias">@SharedLocalizer["Add"]</button>
|
||||
<Pager Items="@_aliases">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["AliasName"]</th>
|
||||
<th>@Localizer["AliasDefault"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
@if (context.AliasId != _aliasid)
|
||||
{
|
||||
<option value="@name">@name</option>
|
||||
<td>
|
||||
@if (_aliasid == -1)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="@(() => EditAlias(context))">@SharedLocalizer["Edit"]</button>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (_aliasid == -1)
|
||||
{
|
||||
<ActionDialog Action="Delete" OnClick="@(async () => await DeleteAlias(context))" ResourceKey="DeleteModule" Class="btn btn-danger" Header="Delete Alias" Message="@string.Format(Localizer["Confirm.Alias.Delete", context.Name])" />
|
||||
}
|
||||
</td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.IsDefault</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td><button type="button" class="btn btn-success" @onclick="@(async () => await SaveAlias())">@SharedLocalizer["Save"]</button></td>
|
||||
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => await CancelAlias())">@SharedLocalizer["Cancel"]</button></td>
|
||||
<td>
|
||||
<input id="aliasname" class="form-control" @bind="@_aliasname" />
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultaias" class="form-select" @bind="@_defaultalias" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</td>
|
||||
}
|
||||
</Row>
|
||||
</Pager>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -253,8 +283,9 @@
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private string _name = string.Empty;
|
||||
private List<Alias> _aliases;
|
||||
private string _defaultalias = string.Empty;
|
||||
private string _urls = string.Empty;
|
||||
private int _aliasid = -1;
|
||||
private string _aliasname;
|
||||
private string _defaultalias;
|
||||
private string _runtime = "";
|
||||
private string _prerender = "";
|
||||
private int _logofileid = -1;
|
||||
@ -304,10 +335,7 @@
|
||||
_prerender = site.RenderMode.Replace(_runtime, "");
|
||||
_isdeleted = site.IsDeleted.ToString();
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
await GetAliases();
|
||||
}
|
||||
|
||||
if (site.LogoFileId != null)
|
||||
{
|
||||
@ -341,7 +369,7 @@
|
||||
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
|
||||
_smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty);
|
||||
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
|
||||
_togglesmtppassword = Localizer["Show"];
|
||||
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
||||
_retention = SettingService.GetSetting(settings, "NotificationRetention", "30");
|
||||
|
||||
@ -407,25 +435,7 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
|
||||
{
|
||||
var unique = true;
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_urls = Regex.Replace(_urls, @"\r\n?|\n", ","); // convert line breaks to commas
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
||||
{
|
||||
var alias = aliases.Where(item => item.Name == name).FirstOrDefault();
|
||||
if (alias != null && unique)
|
||||
{
|
||||
unique = (alias.TenantId == PageState.Site.TenantId && alias.SiteId == PageState.Site.SiteId);
|
||||
}
|
||||
}
|
||||
if (unique && string.IsNullOrEmpty(_defaultalias)) unique = false;
|
||||
}
|
||||
|
||||
if (unique)
|
||||
if (_name != string.Empty && _themetype != "-" && _containertype != "-")
|
||||
{
|
||||
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
||||
if (site != null)
|
||||
@ -502,43 +512,6 @@
|
||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(sValue => sValue.Trim()).ToArray();
|
||||
foreach (Alias alias in _aliases)
|
||||
{
|
||||
if (!names.Contains(alias.Name.Trim()))
|
||||
{
|
||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string name in names)
|
||||
{
|
||||
var alias = _aliases.Find(item => item.Name.Trim() == name);
|
||||
if (alias == null)
|
||||
{
|
||||
alias = new Alias();
|
||||
alias.Name = name;
|
||||
alias.TenantId = site.TenantId;
|
||||
alias.SiteId = site.SiteId;
|
||||
alias.IsDefault = (name == _defaultalias);
|
||||
await AliasService.AddAliasAsync(alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (alias.Name != name || alias.IsDefault != (alias.Name.Trim() == _defaultalias))
|
||||
{
|
||||
alias.Name = name;
|
||||
alias.IsDefault = (name == _defaultalias);
|
||||
await AliasService.UpdateAliasAsync(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
await GetAliases();
|
||||
}
|
||||
|
||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||
|
||||
if (refresh || reload)
|
||||
@ -552,11 +525,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
else // deuplicate alias or default alias not specified
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
|
||||
@ -578,19 +546,19 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
var sites = await SiteService.GetSitesAsync();
|
||||
if (sites.Count > 1)
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
if (aliases.Any(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId))
|
||||
{
|
||||
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
|
||||
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
|
||||
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
foreach (Alias a in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
|
||||
foreach (Alias alias in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
|
||||
{
|
||||
await AliasService.DeleteAliasAsync(a.AliasId);
|
||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||
}
|
||||
|
||||
NavigationManager.NavigateTo(NavigateUrl("admin/sites"));
|
||||
aliases = await AliasService.GetAliasesAsync();
|
||||
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + aliases.First().Name, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -637,31 +605,97 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetAliases()
|
||||
{
|
||||
_urls = string.Empty;
|
||||
_defaultalias = string.Empty;
|
||||
_aliases = await AliasService.GetAliasesAsync();
|
||||
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
|
||||
foreach (Alias alias in _aliases)
|
||||
{
|
||||
_urls += (_urls == string.Empty) ? alias.Name.Trim() : ", " + alias.Name.Trim();
|
||||
if (alias.IsDefault && string.IsNullOrEmpty(_defaultalias)) _defaultalias = alias.Name.Trim();
|
||||
}
|
||||
if (string.IsNullOrEmpty(_defaultalias)) _defaultalias = _aliases.First().Name.Trim();
|
||||
}
|
||||
|
||||
private void ToggleSMTPPassword()
|
||||
{
|
||||
if (_smtppasswordtype == "password")
|
||||
{
|
||||
_smtppasswordtype = "text";
|
||||
_togglesmtppassword = Localizer["Hide"];
|
||||
_togglesmtppassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_smtppasswordtype = "password";
|
||||
_togglesmtppassword = Localizer["Show"];
|
||||
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetAliases()
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_aliases = await AliasService.GetAliasesAsync();
|
||||
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAlias()
|
||||
{
|
||||
_aliases.Add(new Alias { AliasId = 0, Name = "", IsDefault = false });
|
||||
_aliasid = 0;
|
||||
_aliasname = "";
|
||||
_defaultalias = "False";
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void EditAlias(Alias alias)
|
||||
{
|
||||
_aliasid = alias.AliasId;
|
||||
_aliasname = alias.Name;
|
||||
_defaultalias = alias.IsDefault.ToString();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task DeleteAlias(Alias alias)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||
await GetAliases();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveAlias()
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_aliasname))
|
||||
{
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
var alias = aliases.Where(item => item.Name == _aliasname).FirstOrDefault();
|
||||
bool unique = (alias == null || alias.AliasId == _aliasid);
|
||||
if (unique)
|
||||
{
|
||||
if (_aliasid == 0)
|
||||
{
|
||||
alias = new Alias { SiteId = PageState.Site.SiteId, TenantId = PageState.Site.TenantId, Name = _aliasname, IsDefault = bool.Parse(_defaultalias) };
|
||||
await AliasService.AddAliasAsync(alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
alias = _aliases.Single(item => item.AliasId == _aliasid);
|
||||
alias.Name = _aliasname;
|
||||
alias.IsDefault = bool.Parse(_defaultalias);
|
||||
await AliasService.UpdateAliasAsync(alias);
|
||||
}
|
||||
}
|
||||
else // duplicate alias
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
await GetAliases();
|
||||
_aliasid = -1;
|
||||
_aliasname = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CancelAlias()
|
||||
{
|
||||
await GetAliases();
|
||||
_aliasid = -1;
|
||||
_aliasname = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
@ -130,16 +130,9 @@ else
|
||||
<div class="col-sm-9">
|
||||
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
||||
@foreach (var database in _databases)
|
||||
{
|
||||
if (database.IsDefault)
|
||||
{
|
||||
<option value="@database.Name" selected>@Localizer[@database.Name]</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -156,7 +149,7 @@ else
|
||||
<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>
|
||||
<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>
|
||||
}
|
||||
@ -172,12 +165,11 @@ else
|
||||
private List<Database> _databases;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private string _databaseName = "LocalDB";
|
||||
private string _databaseName;
|
||||
private Type _databaseConfigType;
|
||||
private object _databaseConfig;
|
||||
private RenderFragment DatabaseConfigComponent { get; set; }
|
||||
|
||||
|
||||
private List<Theme> _themeList;
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
@ -208,7 +200,16 @@ else
|
||||
_themeList = await ThemeService.GetThemesAsync();
|
||||
_themes = ThemeService.GetThemeControls(_themeList);
|
||||
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
||||
|
||||
_databases = await DatabaseService.GetDatabasesAsync();
|
||||
if (_databases.Exists(item => item.IsDefault))
|
||||
{
|
||||
_databaseName = _databases.Find(item => item.IsDefault).Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
_databaseName = "LocalDB";
|
||||
}
|
||||
LoadDatabaseConfigComponent();
|
||||
}
|
||||
|
||||
@ -307,7 +308,8 @@ else
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = _hostusername;
|
||||
user.Password = _hostpassword;
|
||||
user = await UserService.LoginUserAsync(user, false, false);
|
||||
user.LastIPAddress = PageState.RemoteIPAddress;
|
||||
user = await UserService.LoginUserAsync(user);
|
||||
if (user.IsAuthenticated)
|
||||
{
|
||||
var connectionString = String.Empty;
|
||||
|
@ -142,6 +142,7 @@
|
||||
<ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" />
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
<br /><br />
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
@ -35,6 +35,7 @@
|
||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||
</td>
|
||||
<td style="width: 1px; vertical-align: middle;">
|
||||
|
@ -72,7 +72,7 @@
|
||||
private List<Template> _templates;
|
||||
private string _template = "-";
|
||||
private string[] _versions;
|
||||
private string _reference = Constants.Version;
|
||||
private string _reference = "local";
|
||||
private string _minversion = "2.0.0";
|
||||
private string _location = string.Empty;
|
||||
|
||||
|
@ -40,9 +40,12 @@ else
|
||||
@((MarkupString)PurchaseLink(context.PackageName))
|
||||
</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>
|
||||
@ -94,18 +97,17 @@ else
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
@ -32,13 +32,19 @@ else
|
||||
<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>
|
||||
<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 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>
|
||||
<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>
|
||||
@if (allowtwofactor)
|
||||
@ -154,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>@context.FromDisplayName</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@context.CreatedOn</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
</Row>
|
||||
<Detail>
|
||||
<td colspan="2"></td>
|
||||
@ -187,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>@context.ToDisplayName</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@context.CreatedOn</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
</Row>
|
||||
<Detail>
|
||||
<td colspan="2"></td>
|
||||
@ -205,6 +211,8 @@ else
|
||||
</Detail>
|
||||
</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 />
|
||||
<select class="form-select" @onchange="(e => FilterChanged(e))">
|
||||
<option value="to">@Localizer["Inbox"]</option>
|
||||
@ -217,7 +225,9 @@ else
|
||||
|
||||
@code {
|
||||
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 bool allowtwofactor = false;
|
||||
private string twofactor = "False";
|
||||
@ -239,9 +249,11 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
|
||||
if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"]))
|
||||
{
|
||||
allowtwofactor = bool.Parse(PageState.Site.Settings["LoginOptions:TwoFactor"]);
|
||||
allowtwofactor = (PageState.Site.Settings["LoginOptions:TwoFactor"] == "true");
|
||||
}
|
||||
|
||||
if (PageState.User != null)
|
||||
@ -301,11 +313,11 @@ else
|
||||
{
|
||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||
{
|
||||
if (password == confirm)
|
||||
if (_password == confirm)
|
||||
{
|
||||
var user = PageState.User;
|
||||
user.Username = username;
|
||||
user.Password = password;
|
||||
user.Password = _password;
|
||||
user.TwoFactorRequired = bool.Parse(twofactor);
|
||||
user.Email = email;
|
||||
user.DisplayName = (displayname == string.Empty ? username : displayname);
|
||||
@ -413,4 +425,47 @@ else
|
||||
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">
|
||||
<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">
|
||||
<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 class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
|
||||
<div class="col-sm-9">
|
||||
<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 class="row mb-1 align-items-center">
|
||||
@ -88,7 +94,9 @@
|
||||
|
||||
@code {
|
||||
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 email = string.Empty;
|
||||
private string displayname = string.Empty;
|
||||
@ -102,6 +110,7 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||
settings = new Dictionary<string, string>();
|
||||
}
|
||||
@ -119,9 +128,9 @@
|
||||
{
|
||||
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);
|
||||
if (user == null)
|
||||
@ -129,7 +138,7 @@
|
||||
user = new User();
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = username;
|
||||
user.Password = password;
|
||||
user.Password = _password;
|
||||
user.Email = email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||
user.PhotoFileId = null;
|
||||
@ -193,4 +202,17 @@
|
||||
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">
|
||||
<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">
|
||||
<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 class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
|
||||
<div class="col-sm-9">
|
||||
<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 class="row mb-1 align-items-center">
|
||||
@ -66,8 +72,19 @@ else
|
||||
</select>
|
||||
</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>
|
||||
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||
@ -133,23 +150,29 @@ else
|
||||
@code {
|
||||
private int userid;
|
||||
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 email = string.Empty;
|
||||
private string displayname = string.Empty;
|
||||
private FileManager filemanager;
|
||||
private int photofileid = -1;
|
||||
private File photo = null;
|
||||
private string isdeleted;
|
||||
private string lastlogin;
|
||||
private string lastipaddress;
|
||||
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> settings;
|
||||
private string category = string.Empty;
|
||||
|
||||
private string createdby;
|
||||
private DateTime createdon;
|
||||
private string modifiedby;
|
||||
private DateTime modifiedon;
|
||||
private string deletedby;
|
||||
private DateTime? deletedon;
|
||||
private string isdeleted;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
@ -157,9 +180,9 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
// OnParametersSetAsync is called when the edit modal is closed - in which case there is no id parameter
|
||||
if (PageState.QueryString.ContainsKey("id"))
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||
userid = Int32.Parse(PageState.QueryString["id"]);
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
@ -178,6 +201,10 @@ else
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
isdeleted = user.IsDeleted.ToString();
|
||||
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||
lastipaddress = user.LastIPAddress;
|
||||
|
||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
||||
createdby = user.CreatedBy;
|
||||
createdon = user.CreatedOn;
|
||||
@ -185,7 +212,6 @@ else
|
||||
modifiedon = user.ModifiedOn;
|
||||
deletedby = user.DeletedBy;
|
||||
deletedon = user.DeletedOn;
|
||||
isdeleted = user.IsDeleted.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,12 +231,12 @@ else
|
||||
{
|
||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||
{
|
||||
if (password == confirm)
|
||||
if (_password == confirm)
|
||||
{
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = username;
|
||||
user.Password = password;
|
||||
user.Password = _password;
|
||||
user.Email = email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||
user.PhotoFileId = null;
|
||||
@ -268,4 +294,17 @@ else
|
||||
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 IStringLocalizer<Index> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@inject SiteState SiteState
|
||||
|
||||
@if (userroles == null)
|
||||
@if (users == null)
|
||||
{
|
||||
<p>
|
||||
<em>@SharedLocalizer["Loading"]</em>
|
||||
@ -30,13 +31,14 @@ else
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Pager Items="@userroles">
|
||||
<Pager Items="@users" RowClass="align-middle">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
<th>@SharedLocalizer["Username"]</th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
<th>@Localizer["LastLoginOn"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td>
|
||||
@ -48,8 +50,9 @@ else
|
||||
<td>
|
||||
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
|
||||
</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>
|
||||
</Pager>
|
||||
</TabPanel>
|
||||
@ -86,28 +89,28 @@ else
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="twofactor" HelpText="Do you want to allow users to use two factor authentication? Note that the Notification Job in Scheduled Jobs needs to be enabled for this option to work properly." ResourceKey="TwoFactor">Allow Two Factor?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="twofactor" class="form-select" @bind="@_twofactor">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(PageState.Alias.Path))
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="cookietype" HelpText="Cookies are usually managed per domain. However you can also choose to have distinct cookies for each site (this option is only applicable to micro-sites)." ResourceKey="CookieType">Cookie Type:</Label>
|
||||
<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="cookietype" class="form-select" @bind="@_cookietype">
|
||||
<option value="domain">@Localizer["Domain"]</option>
|
||||
<option value="site">@Localizer["Site"]</option>
|
||||
<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>
|
||||
@ -251,6 +254,12 @@ else
|
||||
<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">
|
||||
@ -266,15 +275,18 @@ else
|
||||
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
|
||||
</div>
|
||||
</div>
|
||||
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="emailclaimtype" HelpText="The type name for the email address claim provided by the provider" ResourceKey="EmailClaimType">Email Claim Type:</Label>
|
||||
<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">
|
||||
@ -330,6 +342,7 @@ else
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
}
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||
@ -338,14 +351,14 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<UserRole> allroles;
|
||||
private List<UserRole> userroles;
|
||||
private string _search;
|
||||
private List<UserRole> allusers;
|
||||
private List<UserRole> users;
|
||||
private string _search = "";
|
||||
|
||||
private string _allowregistration;
|
||||
private string _allowsitelogin;
|
||||
private string _twofactor;
|
||||
private string _cookietype;
|
||||
private string _cookiename;
|
||||
|
||||
private string _minimumlength;
|
||||
private string _uniquecharacters;
|
||||
@ -368,8 +381,10 @@ else
|
||||
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;
|
||||
@ -386,15 +401,17 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
||||
await LoadSettingsAsync();
|
||||
userroles = Search(_search);
|
||||
await LoadUserSettingsAsync();
|
||||
await LoadUsersAsync(true);
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_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");
|
||||
_cookietype = SettingService.GetSetting(settings, "LoginOptions:CookieType", "domain");
|
||||
_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");
|
||||
@ -415,42 +432,53 @@ else
|
||||
_userinfourl = SettingService.GetSetting(settings, "ExternalLogin:UserInfoUrl", "");
|
||||
_clientid = SettingService.GetSetting(settings, "ExternalLogin:ClientId", "");
|
||||
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
||||
_toggleclientsecret = Localizer["Show"];
|
||||
_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 = Localizer["Show"];
|
||||
_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");
|
||||
_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))
|
||||
{
|
||||
results = results.Where(item =>
|
||||
users = users.Where(item =>
|
||||
(
|
||||
item.User.Username.Contains(search, StringComparison.OrdinalIgnoreCase) ||
|
||||
item.User.Email.Contains(search, StringComparison.OrdinalIgnoreCase) ||
|
||||
item.User.DisplayName.Contains(search, StringComparison.OrdinalIgnoreCase)
|
||||
item.User.Username.Contains(_search, StringComparison.OrdinalIgnoreCase) ||
|
||||
item.User.Email.Contains(_search, StringComparison.OrdinalIgnoreCase) ||
|
||||
item.User.DisplayName.Contains(_search, StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
);
|
||||
).ToList();
|
||||
}
|
||||
return results.ToList();
|
||||
}
|
||||
|
||||
private async Task OnSearch()
|
||||
{
|
||||
userroles = Search(_search);
|
||||
await UpdateSettingsAsync();
|
||||
await UpdateUserSettingsAsync();
|
||||
await LoadUsersAsync(false);
|
||||
}
|
||||
|
||||
private async Task DeleteUser(UserRole UserRole)
|
||||
@ -462,8 +490,7 @@ else
|
||||
{
|
||||
await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId);
|
||||
await logger.LogInformation("User Deleted {User}", UserRole.User);
|
||||
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
||||
userroles = Search(_search);
|
||||
await LoadUsersAsync(true);
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
@ -476,13 +503,13 @@ else
|
||||
|
||||
private string settingSearch = "AU-search";
|
||||
|
||||
private async Task LoadSettingsAsync()
|
||||
private async Task LoadUserSettingsAsync()
|
||||
{
|
||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
_search = SettingService.GetSetting(settings, settingSearch, "");
|
||||
}
|
||||
|
||||
private async Task UpdateSettingsAsync()
|
||||
private async Task UpdateUserSettingsAsync()
|
||||
{
|
||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
SettingService.SetSetting(settings, settingSearch, _search);
|
||||
@ -499,8 +526,11 @@ else
|
||||
|
||||
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:CookieType", _cookietype, true);
|
||||
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);
|
||||
@ -522,7 +552,9 @@ else
|
||||
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);
|
||||
@ -532,10 +564,16 @@ else
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true);
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Audience", _audience, true);
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Lifetime", _lifetime, true);
|
||||
}
|
||||
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||
await SettingService.ClearSiteSettingsCacheAsync();
|
||||
|
||||
if (!string.IsNullOrEmpty(_secret))
|
||||
{
|
||||
SiteState.AuthorizationToken = await UserService.GetTokenAsync();
|
||||
}
|
||||
|
||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -548,13 +586,20 @@ 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();
|
||||
@ -562,7 +607,7 @@ else
|
||||
|
||||
private async Task CreateToken()
|
||||
{
|
||||
_token = await UserService.GetTokenAsync();
|
||||
_token = await UserService.GetPersonalAccessTokenAsync();
|
||||
}
|
||||
|
||||
private void ToggleClientSecret()
|
||||
@ -570,12 +615,12 @@ else
|
||||
if (_clientsecrettype == "password")
|
||||
{
|
||||
_clientsecrettype = "text";
|
||||
_toggleclientsecret = Localizer["Hide"];
|
||||
_toggleclientsecret = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_clientsecrettype = "password";
|
||||
_toggleclientsecret = Localizer["Show"];
|
||||
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
|
||||
@ -584,12 +629,12 @@ else
|
||||
if (_secrettype == "password")
|
||||
{
|
||||
_secrettype = "text";
|
||||
_togglesecret = Localizer["Hide"];
|
||||
_togglesecret = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_secrettype = "password";
|
||||
_togglesecret = Localizer["Show"];
|
||||
_togglesecret = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,15 +88,17 @@ else
|
||||
userid = Int32.Parse(PageState.QueryString["id"]);
|
||||
User user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
name = user.DisplayName;
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true);
|
||||
roles = roles.Where(item => item.Name != RoleNames.Everyone).ToList();
|
||||
roles.RemoveAll(item => item.Name == RoleNames.Everyone || item.Name == RoleNames.Unauthenticated);
|
||||
}
|
||||
else
|
||||
{
|
||||
roles = await RoleService.GetRolesAsync(PageState.Site.SiteId);
|
||||
}
|
||||
|
||||
await GetUserRoles();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -110,8 +112,7 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
||||
userroles = userroles.Where(item => item.UserId == userid).ToList();
|
||||
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, userid);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -310,6 +310,17 @@
|
||||
var interop = new Interop(JSRuntime);
|
||||
var upload = await interop.GetFiles(_fileinputid);
|
||||
if (upload.Length > 0)
|
||||
{
|
||||
string restricted = "";
|
||||
foreach (var file in upload)
|
||||
{
|
||||
var extension = (file.LastIndexOf(".") != -1) ? file.Substring(file.LastIndexOf(".") + 1) : "";
|
||||
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||
{
|
||||
restricted += (restricted == "" ? "" : ",") + extension;
|
||||
}
|
||||
}
|
||||
if (restricted == "")
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -360,6 +371,12 @@
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = string.Format(Localizer["Message.File.Restricted"], restricted);
|
||||
_messagetype = MessageType.Warning;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.File.NotSelected"];
|
||||
_messagetype = MessageType.Warning;
|
||||
|
@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Modules.Controls
|
||||
{
|
||||
public class LocalizableComponent : ModuleControlBase
|
||||
{
|
||||
[Inject] public IStringLocalizerFactory LocalizerFactory { get; set; }
|
||||
|
||||
private IStringLocalizer _localizer;
|
||||
|
||||
[Parameter]
|
||||
@ -30,48 +30,32 @@ namespace Oqtane.Modules.Controls
|
||||
var key = $"{ResourceKey}.{propertyName}";
|
||||
var value = Localize(key);
|
||||
|
||||
if (value == key)
|
||||
if (value == key || value == String.Empty)
|
||||
{
|
||||
// Returns default property value (English version) instead of ResourceKey.PropertyName
|
||||
return propertyValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value == String.Empty)
|
||||
{
|
||||
// Returns default property value (English version)
|
||||
// return default property value if key does not exist in resource file or value is empty
|
||||
return propertyValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// return localized value
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
IsLocalizable = false;
|
||||
|
||||
if (string.IsNullOrEmpty(ResourceType))
|
||||
if (String.IsNullOrEmpty(ResourceType))
|
||||
{
|
||||
ResourceType = ModuleState?.ModuleType;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(ResourceKey) && !string.IsNullOrEmpty(ResourceType))
|
||||
if (!String.IsNullOrEmpty(ResourceKey) && !String.IsNullOrEmpty(ResourceType))
|
||||
{
|
||||
var moduleType = Type.GetType(ResourceType);
|
||||
if (moduleType != null)
|
||||
{
|
||||
using (var scope = ServiceActivator.GetScope())
|
||||
{
|
||||
var localizerFactory = scope.ServiceProvider.GetService<IStringLocalizerFactory>();
|
||||
_localizer = localizerFactory.Create(moduleType);
|
||||
|
||||
_localizer = LocalizerFactory.Create(ResourceType);
|
||||
IsLocalizable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleControlBase
|
||||
@inject IStringLocalizerFactory LocalizerFactory
|
||||
@typeparam TableItem
|
||||
|
||||
@if (ItemList != null)
|
||||
@ -48,7 +49,7 @@
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" style="white-space: nowrap;">Page @_page of @_pages</a>
|
||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
@ -156,13 +157,14 @@
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" style="white-space: nowrap;">Page @_page of @_pages</a>
|
||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
private IStringLocalizer Localizer;
|
||||
private int _pages = 0;
|
||||
private int _page = 1;
|
||||
private int _maxItems = 10;
|
||||
@ -215,6 +217,11 @@
|
||||
|
||||
private IEnumerable<TableItem> ItemList { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Localizer = LocalizerFactory.Create(GetType().FullName);
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Format))
|
||||
|
@ -127,11 +127,10 @@
|
||||
_permissionnames = PermissionNames;
|
||||
}
|
||||
|
||||
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId);
|
||||
_roles.Insert(0, new Role { Name = RoleNames.Everyone });
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId, true);
|
||||
if (!UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_roles.Add(new Role { Name = RoleNames.Host });
|
||||
_roles.RemoveAll(item => item.Name == RoleNames.Host);
|
||||
}
|
||||
|
||||
_permissions = new List<PermissionString>();
|
||||
@ -254,6 +253,7 @@
|
||||
permission = _permissions[i];
|
||||
List<string> ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
||||
ids.Remove("!" + RoleNames.Unauthenticated); // remove deny unauthenticated
|
||||
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
|
@ -150,10 +150,6 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseFileManager()
|
||||
|
@ -5,7 +5,7 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>3.1.0</Version>
|
||||
<Version>3.1.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -13,7 +13,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@ -123,9 +123,15 @@
|
||||
<data name="Success.Account.Verified" xml:space="preserve">
|
||||
<value>User Account Verified Successfully. You Can Now Login With Your Username And Password Below.</value>
|
||||
</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>
|
||||
</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">
|
||||
<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>
|
||||
@ -183,12 +189,6 @@
|
||||
<data name="Username.Text" xml:space="preserve">
|
||||
<value>Username:</value>
|
||||
</data>
|
||||
<data name="HidePassword" xml:space="preserve">
|
||||
<value>Hide</value>
|
||||
</data>
|
||||
<data name="ShowPassword" xml:space="preserve">
|
||||
<value>Show</value>
|
||||
</data>
|
||||
<data name="Use" xml:space="preserve">
|
||||
<value>Use</value>
|
||||
</data>
|
||||
@ -201,4 +201,28 @@
|
||||
<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>
|
@ -150,4 +150,7 @@
|
||||
<data name="EditModule.Text" xml:space="preserve">
|
||||
<value>Edit</value>
|
||||
</data>
|
||||
<data name="Modules" xml:space="preserve">
|
||||
<value>Modules</value>
|
||||
</data>
|
||||
</root>
|
@ -142,7 +142,7 @@
|
||||
<value>Site Settings Saved</value>
|
||||
</data>
|
||||
<data name="Message.Aliases.Taken" xml:space="preserve">
|
||||
<value>The Default Alias Has Not Been Specified Or An Alias Was Specified That Has Already Been Used For Another Site</value>
|
||||
<value>An Alias Was Specified That Has Already Been Used For Another Site</value>
|
||||
</data>
|
||||
<data name="Message.Required.SiteName" xml:space="preserve">
|
||||
<value>You Must Provide A Site Name, Alias, And Default Theme/Container</value>
|
||||
@ -324,10 +324,13 @@
|
||||
<data name="Aliases.Heading" xml:space="preserve">
|
||||
<value>Aliases</value>
|
||||
</data>
|
||||
<data name="Hide" xml:space="preserve">
|
||||
<value>Hide</value>
|
||||
<data name="AliasName" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="Show" xml:space="preserve">
|
||||
<value>Show</value>
|
||||
<data name="AliasDefault" xml:space="preserve">
|
||||
<value>Default?</value>
|
||||
</data>
|
||||
<data name="Confirm.Alias.Delete" xml:space="preserve">
|
||||
<value>Are You Sure You Wish To Delete {0}?</value>
|
||||
</data>
|
||||
</root>
|
@ -210,4 +210,19 @@
|
||||
<data name="TwoFactor.Text" xml:space="preserve">
|
||||
<value>Two Factor?</value>
|
||||
</data>
|
||||
<data name="DeleteAllNotifications.Header" xml:space="preserve">
|
||||
<value>Clear Notifications</value>
|
||||
</data>
|
||||
<data name="DeleteAllNotifications.Message" xml:space="preserve">
|
||||
<value>Are You Sure You Wish To Permanently Delete All Notifications?</value>
|
||||
</data>
|
||||
<data name="DeleteAllNotifications.Text" xml:space="preserve">
|
||||
<value>Delete ALL Notifications</value>
|
||||
</data>
|
||||
<data name="Notifications.Heading" xml:space="preserve">
|
||||
<value>Notifications</value>
|
||||
</data>
|
||||
<data name="Profile.Heading" xml:space="preserve">
|
||||
<value>Profile</value>
|
||||
</data>
|
||||
</root>
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@ -168,4 +168,7 @@
|
||||
<data name="Username.Text" xml:space="preserve">
|
||||
<value>Username:</value>
|
||||
</data>
|
||||
<data name="Password.Placeholder" xml:space="preserve">
|
||||
<value>Password</value>
|
||||
</data>
|
||||
</root>
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@ -183,4 +183,19 @@
|
||||
<data name="Profile.Heading" xml:space="preserve">
|
||||
<value>Profile</value>
|
||||
</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>
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@ -247,10 +247,10 @@
|
||||
<value>Domain Filter:</value>
|
||||
</data>
|
||||
<data name="EmailClaimType.HelpText" xml:space="preserve">
|
||||
<value>The type name for the email address claim provided by the provider</value>
|
||||
<value>The name of the email address claim provided by the provider</value>
|
||||
</data>
|
||||
<data name="EmailClaimType.Text" xml:space="preserve">
|
||||
<value>Email Claim Type:</value>
|
||||
<value>Email Claim:</value>
|
||||
</data>
|
||||
<data name="ExternalLoginSettings.Heading" xml:space="preserve">
|
||||
<value>External Login Settings</value>
|
||||
@ -318,11 +318,11 @@
|
||||
<data name="UserSettings.Heading" xml:space="preserve">
|
||||
<value>User Settings</value>
|
||||
</data>
|
||||
<data name="CookieType.HelpText" xml:space="preserve">
|
||||
<value>Cookies are usually managed per domain. However you can also choose to have distinct cookies for each site (this option is only applicable to micro-sites).</value>
|
||||
<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="CookieType.Text" xml:space="preserve">
|
||||
<value>Login Cookie Type:</value>
|
||||
<data name="CookieName.Text" xml:space="preserve">
|
||||
<value>Cookie Name:</value>
|
||||
</data>
|
||||
<data name="CreateToken" xml:space="preserve">
|
||||
<value>Create Token</value>
|
||||
@ -355,15 +355,33 @@
|
||||
<value>Token Settings</value>
|
||||
</data>
|
||||
<data name="TwoFactor.HelpText" xml:space="preserve">
|
||||
<value>Do you want to allow users to use two factor authentication? Note that the Notification Job in Scheduled Jobs needs to be enabled and your SMTP options need to be configured in Site Settings for this option to work properly.</value>
|
||||
<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>Allow Two Factor?</value>
|
||||
<value>Two Factor?</value>
|
||||
</data>
|
||||
<data name="Hide" xml:space="preserve">
|
||||
<value>Hide</value>
|
||||
<data name="Disabled" xml:space="preserve">
|
||||
<value>Disabled</value>
|
||||
</data>
|
||||
<data name="Show" xml:space="preserve">
|
||||
<value>Show</value>
|
||||
<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>
|
@ -141,4 +141,7 @@
|
||||
<data name="Success.File.Upload" xml:space="preserve">
|
||||
<value>File Upload Succeeded</value>
|
||||
</data>
|
||||
<data name="Message.File.Restricted" xml:space="preserve">
|
||||
<value>Files With Extension Of {0} Are Restricted From Upload. Please Contact Your Administrator For More Information.</value>
|
||||
</data>
|
||||
</root>
|
123
Oqtane.Client/Resources/Modules/Controls/Pager.resx
Normal file
123
Oqtane.Client/Resources/Modules/Controls/Pager.resx
Normal file
@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="PageOfPages" xml:space="preserve">
|
||||
<value>Page {0} of {1}</value>
|
||||
</data>
|
||||
</root>
|
@ -274,7 +274,7 @@
|
||||
<value>Full Name:</value>
|
||||
</data>
|
||||
<data name="LocalVersion" xml:space="preserve">
|
||||
<value>Local Version</value>
|
||||
<value>Installed Version</value>
|
||||
</data>
|
||||
<data name="Search.Source" xml:space="preserve">
|
||||
<value>source</value>
|
||||
@ -321,4 +321,19 @@
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="HidePassword" xml:space="preserve">
|
||||
<value>Hide</value>
|
||||
</data>
|
||||
<data name="ShowPassword" xml:space="preserve">
|
||||
<value>Show</value>
|
||||
</data>
|
||||
<data name="PageOfPages" xml:space="preserve">
|
||||
<value>Page {0} of {1}</value>
|
||||
</data>
|
||||
<data name="Url Mappings" xml:space="preserve">
|
||||
<value>Url Mappings</value>
|
||||
</data>
|
||||
<data name="Visitor Management" xml:space="preserve">
|
||||
<value>Visitor Management</value>
|
||||
</data>
|
||||
</root>
|
@ -12,18 +12,12 @@ namespace Oqtane.Services
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
public class AliasService : ServiceBase, IAliasService
|
||||
{
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor - should only be used by Dependency Injection
|
||||
/// </summary>
|
||||
public AliasService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
public AliasService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private string ApiUrl => CreateApiUrl("Alias", _siteState.Alias);
|
||||
private string ApiUrl => CreateApiUrl("Alias");
|
||||
|
||||
/// <inheritdoc />
|
||||
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")]
|
||||
public class DatabaseService : ServiceBase, IDatabaseService
|
||||
{
|
||||
public DatabaseService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public DatabaseService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Database", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Database");
|
||||
|
||||
public async Task<List<Database>> GetDatabasesAsync()
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
@ -17,13 +18,13 @@ namespace Oqtane.Services
|
||||
private readonly SiteState _siteState;
|
||||
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;
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("File", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("File");
|
||||
|
||||
public async Task<List<File>> GetFilesAsync(int folderId)
|
||||
{
|
||||
@ -32,7 +33,8 @@ namespace Oqtane.Services
|
||||
|
||||
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)
|
||||
@ -44,7 +46,8 @@ namespace Oqtane.Services
|
||||
|
||||
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)
|
||||
@ -82,7 +85,7 @@ namespace Oqtane.Services
|
||||
string result = "";
|
||||
|
||||
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
|
||||
bool success = false;
|
||||
|
@ -14,14 +14,9 @@ namespace Oqtane.Services
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
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)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string ApiUrl => CreateApiUrl("Folder", _siteState.Alias);
|
||||
private string ApiUrl => CreateApiUrl("Folder");
|
||||
|
||||
public async Task<List<Folder>> GetFoldersAsync(int siteId)
|
||||
{
|
||||
|
@ -15,7 +15,7 @@ namespace Oqtane.Services
|
||||
private readonly NavigationManager _navigationManager;
|
||||
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;
|
||||
_siteState = siteState;
|
||||
|
@ -16,6 +16,31 @@ namespace Oqtane.Services
|
||||
/// <returns></returns>
|
||||
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>
|
||||
/// Get one specific <see cref="UserRole"/>
|
||||
/// </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.
|
||||
/// </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="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>
|
||||
Task<User> LoginUserAsync(User user, bool setCookie, bool isPersistent);
|
||||
Task<User> LoginUserAsync(User user);
|
||||
|
||||
/// <summary>
|
||||
/// Logout a <see cref="User"/>
|
||||
@ -109,5 +107,24 @@ namespace Oqtane.Services
|
||||
/// </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")]
|
||||
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)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("JobLog", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("JobLog");
|
||||
|
||||
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")]
|
||||
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)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Job", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Job");
|
||||
|
||||
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")]
|
||||
public class LanguageService : ServiceBase, ILanguageService
|
||||
{
|
||||
public LanguageService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public LanguageService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Language", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Language");
|
||||
|
||||
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")]
|
||||
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)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Localization", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Localization");
|
||||
|
||||
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")]
|
||||
public class LogService : ServiceBase, ILogService
|
||||
{
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
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;
|
||||
_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)
|
||||
{
|
||||
|
@ -14,16 +14,9 @@ namespace Oqtane.Services
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
public class ModuleDefinitionService : ServiceBase, IModuleDefinitionService
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
private readonly SiteState _siteState;
|
||||
public ModuleDefinitionService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
public ModuleDefinitionService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_http = http;
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("ModuleDefinition", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("ModuleDefinition");
|
||||
|
||||
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")]
|
||||
public class ModuleService : ServiceBase, IModuleService
|
||||
{
|
||||
public ModuleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public ModuleService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Module", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Module");
|
||||
|
||||
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")]
|
||||
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)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Notification", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Notification");
|
||||
|
||||
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")]
|
||||
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)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
private string Apiurl => CreateApiUrl("Package", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Package");
|
||||
|
||||
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")]
|
||||
public class PageModuleService : ServiceBase, IPageModuleService
|
||||
{
|
||||
public PageModuleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public PageModuleService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("PageModule", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("PageModule");
|
||||
|
||||
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")]
|
||||
public class PageService : ServiceBase, IPageService
|
||||
{
|
||||
public PageService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public PageService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Page", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Page");
|
||||
|
||||
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")]
|
||||
public class ProfileService : ServiceBase, IProfileService
|
||||
{
|
||||
public ProfileService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public ProfileService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Profile", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Profile");
|
||||
|
||||
public async Task<List<Profile>> GetProfilesAsync(int siteId)
|
||||
{
|
||||
|
@ -22,10 +22,15 @@ namespace Oqtane.Services
|
||||
|
||||
private HttpClient GetHttpClient()
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient("Remote");
|
||||
if (!httpClient.DefaultRequestHeaders.Contains(HeaderNames.Authorization) && _siteState != null && !string.IsNullOrEmpty(_siteState.AuthorizationToken))
|
||||
return GetHttpClient(_siteState?.AuthorizationToken);
|
||||
}
|
||||
|
||||
private HttpClient GetHttpClient(string AuthorizationToken)
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + _siteState.AuthorizationToken);
|
||||
var httpClient = _httpClientFactory.CreateClient("Remote");
|
||||
if (!httpClient.DefaultRequestHeaders.Contains(HeaderNames.Authorization) && !string.IsNullOrEmpty(AuthorizationToken))
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + AuthorizationToken);
|
||||
}
|
||||
return httpClient;
|
||||
}
|
||||
|
@ -11,16 +11,9 @@ namespace Oqtane.Services
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
public class RoleService : ServiceBase, IRoleService
|
||||
{
|
||||
public RoleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public RoleService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Role", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Role");
|
||||
|
||||
public async Task<List<Role>> GetRolesAsync(int siteId)
|
||||
{
|
||||
|
@ -12,15 +12,9 @@ namespace Oqtane.Services
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
public class SettingService : ServiceBase, ISettingService
|
||||
{
|
||||
public SettingService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public SettingService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Setting", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Setting");
|
||||
|
||||
public async Task<Dictionary<string, string>> GetTenantSettingsAsync()
|
||||
{
|
||||
|
@ -12,15 +12,9 @@ namespace Oqtane.Services
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
public class SiteService : ServiceBase, ISiteService
|
||||
{
|
||||
public SiteService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public SiteService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Site", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Site");
|
||||
|
||||
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")]
|
||||
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)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
private string Apiurl => CreateApiUrl("SiteTemplate", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("SiteTemplate");
|
||||
|
||||
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")]
|
||||
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)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Sql", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Sql");
|
||||
|
||||
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")]
|
||||
public class SyncService : ServiceBase, ISyncService
|
||||
{
|
||||
public SyncService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
/// <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);
|
||||
private string ApiUrl => CreateApiUrl("Sync");
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Sync> GetSyncAsync(DateTime lastSyncDate)
|
||||
|
@ -9,14 +9,9 @@ namespace Oqtane.Services
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
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)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("System", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("System");
|
||||
|
||||
public async Task<Dictionary<string, object>> GetSystemInfoAsync()
|
||||
{
|
||||
|
@ -11,14 +11,9 @@ namespace Oqtane.Services
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
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)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Tenant", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Tenant");
|
||||
|
||||
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")]
|
||||
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)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string ApiUrl => CreateApiUrl("Theme", _siteState.Alias);
|
||||
private string ApiUrl => CreateApiUrl("Theme");
|
||||
|
||||
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")]
|
||||
public class UrlMappingService : ServiceBase, IUrlMappingService
|
||||
{
|
||||
public UrlMappingService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public UrlMappingService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("UrlMapping", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("UrlMapping");
|
||||
|
||||
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")]
|
||||
public class UserRoleService : ServiceBase, IUserRoleService
|
||||
{
|
||||
public UserRoleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public UserRoleService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("UserRole", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("UserRole");
|
||||
|
||||
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)
|
||||
|
@ -10,14 +10,9 @@ namespace Oqtane.Services
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
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)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("User", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("User");
|
||||
|
||||
public async Task<User> GetUserAsync(int userId, int siteId)
|
||||
{
|
||||
@ -44,9 +39,9 @@ namespace Oqtane.Services
|
||||
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)
|
||||
@ -84,5 +79,16 @@ namespace Oqtane.Services
|
||||
{
|
||||
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")]
|
||||
public class VisitorService : ServiceBase, IVisitorService
|
||||
{
|
||||
public VisitorService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public VisitorService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Visitor", _siteState.Alias);
|
||||
private string Apiurl => CreateApiUrl("Visitor");
|
||||
|
||||
public async Task<List<Visitor>> GetVisitorsAsync(int siteId, DateTime fromDate)
|
||||
{
|
||||
|
@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Providers;
|
||||
using Oqtane.Security;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
@ -33,28 +31,19 @@ namespace Oqtane.Themes.Controls
|
||||
|
||||
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);
|
||||
PageState.User = null;
|
||||
|
||||
// check if anonymous user can access page
|
||||
var url = PageState.Alias.Path + "/" + PageState.Page.Path;
|
||||
if (!UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, PageState.Page.Permissions))
|
||||
if (!UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.Permissions))
|
||||
{
|
||||
url = PageState.Alias.Path;
|
||||
}
|
||||
|
||||
if (PageState.Runtime == Shared.Runtime.Server)
|
||||
{
|
||||
// server-side Blazor needs to redirect to the Logout page
|
||||
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/logout/") + "?returnurl=" + WebUtility.UrlEncode(url), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// client-side Blazor
|
||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||
authstateprovider.NotifyAuthenticationChanged();
|
||||
NavigationManager.NavigateTo(NavigateUrl(url, true));
|
||||
}
|
||||
// post to the Logout page to complete the logout process
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url };
|
||||
var interop = new Interop(jsRuntime);
|
||||
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,15 +9,19 @@
|
||||
if (childPage.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<a class="nav-link active px-3" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
</span>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a class="nav-link px-3" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</span>
|
||||
</a>
|
||||
}
|
||||
}
|
||||
@ -34,8 +38,10 @@ else
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
@ -43,8 +49,10 @@ else
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
|
@ -9,8 +9,10 @@
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
@ -18,8 +20,10 @@
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
@ -38,8 +42,10 @@ else
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
@ -47,8 +53,10 @@ else
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
_jsRuntime.InvokeVoidAsync(
|
||||
"Oqtane.Interop.uploadFiles",
|
||||
posturl, folder, id);
|
||||
posturl, folder, id, antiforgerytoken);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
|
@ -80,24 +80,34 @@
|
||||
var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
|
||||
|
||||
Route route = new Route(_absoluteUri, SiteState.Alias.Path);
|
||||
var moduleid = (int.TryParse(route.ModuleId, out int mid)) ? mid : -1;
|
||||
int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1;
|
||||
var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction;
|
||||
var querystring = ParseQueryString(route.Query);
|
||||
|
||||
// reload the client application if there is a forced reload or the user navigated to a site with a different alias
|
||||
if (querystring.ContainsKey("reload") || (!route.AbsolutePath.Substring(1).ToLower().StartsWith(SiteState.Alias.Path.ToLower()) && !string.IsNullOrEmpty(SiteState.Alias.Path)))
|
||||
// reload the client application from the server if there is a forced reload or the user navigated to a site with a different alias
|
||||
if (querystring.ContainsKey("reload") || (!NavigationManager.ToBaseRelativePath(_absoluteUri).ToLower().StartsWith(SiteState.Alias.Path.ToLower()) && !string.IsNullOrEmpty(SiteState.Alias.Path)))
|
||||
{
|
||||
NavigationManager.NavigateTo(_absoluteUri.Replace("?reload", ""), true);
|
||||
if (querystring["reload"] == "post")
|
||||
{
|
||||
// post back so that the cookies are set correctly - required on any change to the principal
|
||||
var interop = new Interop(JSRuntime);
|
||||
var fields = new { returnurl = "/" + NavigationManager.ToBaseRelativePath(_absoluteUri) };
|
||||
string url = Utilities.TenantUrl(SiteState.Alias, "/pages/external/");
|
||||
await interop.SubmitForm(url, fields);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the refresh parameter is used to refresh the PageState
|
||||
NavigationManager.NavigateTo(_absoluteUri.Replace("?reload", ""), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// the refresh parameter is used to refresh the client-side PageState
|
||||
if (querystring.ContainsKey("refresh"))
|
||||
{
|
||||
refresh = UI.Refresh.Site;
|
||||
}
|
||||
}
|
||||
|
||||
if (PageState != null)
|
||||
{
|
||||
@ -260,7 +270,6 @@
|
||||
{
|
||||
if (route.PagePath != "404")
|
||||
{
|
||||
await LogService.Log(null, null, user.UserId, "SiteRouter", "SiteRouter", LogFunction.Other, LogLevel.Information, null, "Page Path /{Path} Does Not Exist Or User Is Not Authorized To View", route.PagePath);
|
||||
// redirect to 404 page
|
||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "404", ""));
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.0</Version>
|
||||
<Version>3.1.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.MySQL</id>
|
||||
<version>3.1.0</version>
|
||||
<version>3.1.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane MySQL Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.0</Version>
|
||||
<Version>3.1.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.PostgreSQL</id>
|
||||
<version>3.1.0</version>
|
||||
<version>3.1.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane PostgreSQL Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.0</Version>
|
||||
<Version>3.1.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.SqlServer</id>
|
||||
<version>3.1.0</version>
|
||||
<version>3.1.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQL Server Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -32,6 +32,21 @@ namespace Oqtane.Database.SqlServer
|
||||
return table.Column<int>(name: name, nullable: false).Annotation("SqlServer:Identity", "1, 1");
|
||||
}
|
||||
|
||||
public override void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode, string index)
|
||||
{
|
||||
var elements = index.Split(':', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (elements.Length != 0)
|
||||
{
|
||||
builder.DropIndex(elements[0], table);
|
||||
}
|
||||
builder.AlterColumn<string>(name, table, maxLength: length, nullable: nullable, unicode: unicode);
|
||||
if (elements.Length != 0)
|
||||
{
|
||||
var columns = elements[1].Split(',');
|
||||
builder.CreateIndex(elements[0], table, columns, null, bool.Parse(elements[2]), null);
|
||||
}
|
||||
}
|
||||
|
||||
public override int ExecuteNonQuery(string connectionString, string query)
|
||||
{
|
||||
var conn = new SqlConnection(FormatConnectionString(connectionString));
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.0</Version>
|
||||
<Version>3.1.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.Sqlite</id>
|
||||
<version>3.1.0</version>
|
||||
<version>3.1.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQLite Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -35,7 +35,7 @@ namespace Oqtane.Database.Sqlite
|
||||
// not implemented as SQLite does not support dropping columns
|
||||
}
|
||||
|
||||
public override void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode)
|
||||
public override void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode, string index)
|
||||
{
|
||||
// not implemented as SQLite does not support altering columns
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Client</id>
|
||||
<version>3.1.0</version>
|
||||
<version>3.1.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>3.1.0</version>
|
||||
<version>3.1.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -11,8 +11,8 @@
|
||||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v3.1.0/Oqtane.Framework.3.1.0.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</releaseNotes>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v3.1.3/Oqtane.Framework.3.1.2.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane framework</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Server</id>
|
||||
<version>3.1.0</version>
|
||||
<version>3.1.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Shared</id>
|
||||
<version>3.1.0</version>
|
||||
<version>3.1.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Updater</id>
|
||||
<version>3.1.0</version>
|
||||
<version>3.1.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -1 +1 @@
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.0.Install.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.3.Install.zip" -Force
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user