Compare commits
89 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 |
@ -1,3 +1,5 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.Localization
|
namespace Microsoft.Extensions.Localization
|
||||||
{
|
{
|
||||||
public static class OqtaneLocalizationExtensions
|
public static class OqtaneLocalizationExtensions
|
||||||
@ -18,5 +20,42 @@ namespace Microsoft.Extensions.Localization
|
|||||||
}
|
}
|
||||||
return localizedValue;
|
return localizedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an IStringLocalizer based on a type name. This extension method is useful in scenarios where the default IStringLocalizer is unable to locate the resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localizerFactory"></param>
|
||||||
|
/// <param name="fullTypeName">the full type name ie. GetType().FullName</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IStringLocalizer Create(this IStringLocalizerFactory localizerFactory, string fullTypeName)
|
||||||
|
{
|
||||||
|
var typename = fullTypeName;
|
||||||
|
|
||||||
|
// handle generic types
|
||||||
|
var type = Type.GetType(fullTypeName);
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
typename = type.GetGenericTypeDefinition().FullName;
|
||||||
|
typename = typename.Substring(0, typename.IndexOf("`")); // remove generic type info
|
||||||
|
}
|
||||||
|
|
||||||
|
// format typename
|
||||||
|
if (typename.Contains(","))
|
||||||
|
{
|
||||||
|
typename = typename.Substring(0, typename.IndexOf(",")); // remove assembly info
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove rootnamespace
|
||||||
|
var rootnamespace = "";
|
||||||
|
var attributes = type.Assembly.GetCustomAttributes(typeof(RootNamespaceAttribute), false);
|
||||||
|
if (attributes.Length > 0)
|
||||||
|
{
|
||||||
|
rootnamespace = ((RootNamespaceAttribute)attributes[0]).RootNamespace;
|
||||||
|
}
|
||||||
|
typename = typename.Replace(rootnamespace + ".", "");
|
||||||
|
|
||||||
|
// create IStringLocalizer using factory
|
||||||
|
return localizerFactory.Create(typename, type.Assembly.GetName().Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,15 +28,8 @@
|
|||||||
{
|
{
|
||||||
<select id="databasetype" class="form-select custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))">
|
<select id="databasetype" class="form-select custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))">
|
||||||
@foreach (var database in _databases)
|
@foreach (var database in _databases)
|
||||||
{
|
|
||||||
if (database.IsDefault)
|
|
||||||
{
|
|
||||||
<option value="@database.Name" selected>@Localizer[@database.Name]</option>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
}
|
}
|
||||||
@ -63,8 +56,8 @@
|
|||||||
<Label Class="col-sm-3" For="password" HelpText="Provide a password for the primary user account" ResourceKey="Password">Password:</Label>
|
<Label Class="col-sm-3" For="password" HelpText="Provide a password for the primary user account" ResourceKey="Password">Password:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_hostPassword" autocomplete="new-password" />
|
<input id="password" type="@_passwordType" class="form-control" @bind="@_hostPassword" autocomplete="new-password" />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglePassword</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -72,8 +65,8 @@
|
|||||||
<Label Class="col-sm-3" For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirmPassword" autocomplete="new-password" />
|
<input id="confirm" type="@_confirmPasswordType" class="form-control" @bind="@_confirmPassword" autocomplete="new-password" />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleConfirmPassword">@_toggleConfirmPassword</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -110,8 +103,10 @@
|
|||||||
|
|
||||||
private string _hostUsername = string.Empty;
|
private string _hostUsername = string.Empty;
|
||||||
private string _hostPassword = string.Empty;
|
private string _hostPassword = string.Empty;
|
||||||
private string _passwordtype = "password";
|
private string _passwordType = "password";
|
||||||
private string _togglepassword = string.Empty;
|
private string _confirmPasswordType = "password";
|
||||||
|
private string _togglePassword = string.Empty;
|
||||||
|
private string _toggleConfirmPassword = string.Empty;
|
||||||
private string _confirmPassword = string.Empty;
|
private string _confirmPassword = string.Empty;
|
||||||
private string _hostEmail = string.Empty;
|
private string _hostEmail = string.Empty;
|
||||||
private bool _register = true;
|
private bool _register = true;
|
||||||
@ -120,7 +115,9 @@
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||||
|
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
_databases = await DatabaseService.GetDatabasesAsync();
|
_databases = await DatabaseService.GetDatabasesAsync();
|
||||||
if (_databases.Exists(item => item.IsDefault))
|
if (_databases.Exists(item => item.IsDefault))
|
||||||
{
|
{
|
||||||
@ -230,15 +227,29 @@
|
|||||||
|
|
||||||
private void TogglePassword()
|
private void TogglePassword()
|
||||||
{
|
{
|
||||||
if (_passwordtype == "password")
|
if (_passwordType == "password")
|
||||||
{
|
{
|
||||||
_passwordtype = "text";
|
_passwordType = "text";
|
||||||
_togglepassword = SharedLocalizer["HidePassword"];
|
_togglePassword = SharedLocalizer["HidePassword"];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_passwordtype = "password";
|
_passwordType = "password";
|
||||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleConfirmPassword()
|
||||||
|
{
|
||||||
|
if (_confirmPasswordType == "password")
|
||||||
|
{
|
||||||
|
_confirmPasswordType = "text";
|
||||||
|
_toggleConfirmPassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_confirmPasswordType = "password";
|
||||||
|
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ else
|
|||||||
@if (_availableCultures.Count() == 0)
|
@if (_availableCultures.Count() == 0)
|
||||||
{
|
{
|
||||||
<ModuleMessage Type="MessageType.Info" Message="@_message"></ModuleMessage>
|
<ModuleMessage Type="MessageType.Info" Message="@_message"></ModuleMessage>
|
||||||
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -47,9 +48,9 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
|
||||||
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Download" ResourceKey="Download" Security="SecurityAccessLevel.Host">
|
<TabPanel Name="Download" ResourceKey="Download" Security="SecurityAccessLevel.Host">
|
||||||
<div class="row justify-content-center mb-3">
|
<div class="row justify-content-center mb-3">
|
||||||
@ -78,6 +79,7 @@ else
|
|||||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||||
|
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||||
</td>
|
</td>
|
||||||
<td style="width: 1px; vertical-align: middle;">
|
<td style="width: 1px; vertical-align: middle;">
|
||||||
|
@ -184,7 +184,7 @@
|
|||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(login))
|
if (await interop.FormValid(login))
|
||||||
{
|
{
|
||||||
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password};
|
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password, LastIPAddress = SiteState.RemoteIPAddress};
|
||||||
|
|
||||||
if (!twofactor)
|
if (!twofactor)
|
||||||
{
|
{
|
||||||
@ -206,7 +206,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (PageState.Site.Settings["LoginOptions:TwoFactor"] == "required" || user.TwoFactorRequired)
|
if ((PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && PageState.Site.Settings["LoginOptions:TwoFactor"] == "required") || user.TwoFactorRequired)
|
||||||
{
|
{
|
||||||
twofactor = true;
|
twofactor = true;
|
||||||
validated = false;
|
validated = false;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Admin.Logs
|
@namespace Oqtane.Modules.Admin.Logs
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
@inject ILogService LogService
|
@inject ILogService LogService
|
||||||
@inject ISettingService SettingService
|
@inject ISettingService SettingService
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@ -104,6 +105,10 @@ else
|
|||||||
{
|
{
|
||||||
try
|
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"))
|
if (PageState.QueryString.ContainsKey("level"))
|
||||||
{
|
{
|
||||||
_level = PageState.QueryString["level"];
|
_level = PageState.QueryString["level"];
|
||||||
|
@ -124,6 +124,8 @@ else
|
|||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
|
||||||
{
|
{
|
||||||
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
|
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
|
||||||
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
||||||
@ -135,6 +137,11 @@ else
|
|||||||
GetLocation();
|
GetLocation();
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
|
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Creating Module");
|
await logger.LogError(ex, "Error Creating Module");
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||||
|
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||||
</td>
|
</td>
|
||||||
<td style="width: 1px; vertical-align: middle;">
|
<td style="width: 1px; vertical-align: middle;">
|
||||||
|
@ -12,11 +12,32 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-3 align-items-center">
|
||||||
|
<div class="col-sm-6">
|
||||||
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
||||||
@((MarkupString)" ")
|
@((MarkupString)" ")
|
||||||
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary" />
|
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary" />
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<select class="form-select" @onchange="(e => CategoryChanged(e))">
|
||||||
|
@foreach (var category in _categories)
|
||||||
|
{
|
||||||
|
if (category == _category)
|
||||||
|
{
|
||||||
|
<option value="@category" selected>@category @Localizer["Modules"]</option>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<option value="@category">@category @Localizer["Modules"]</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Pager Items="@_moduleDefinitions">
|
<Pager Items="@_moduleDefinitions.Where(item => item.Categories.Contains(_category))">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
@ -65,6 +86,8 @@ else
|
|||||||
@code {
|
@code {
|
||||||
private List<ModuleDefinition> _moduleDefinitions;
|
private List<ModuleDefinition> _moduleDefinitions;
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
|
private List<string> _categories = new List<string>();
|
||||||
|
private string _category = "Common";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
@ -74,6 +97,7 @@ else
|
|||||||
{
|
{
|
||||||
_moduleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
_moduleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||||
_packages = await PackageService.GetPackagesAsync("module");
|
_packages = await PackageService.GetPackagesAsync("module");
|
||||||
|
_categories = _moduleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -149,4 +173,10 @@ else
|
|||||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CategoryChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
_category = (string)e.Value;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,6 +198,7 @@
|
|||||||
|
|
||||||
var module = ModuleState;
|
var module = ModuleState;
|
||||||
module.AllPages = bool.Parse(_allPages);
|
module.AllPages = bool.Parse(_allPages);
|
||||||
|
module.PageModuleId = ModuleState.PageModuleId;
|
||||||
module.Permissions = _permissionGrid.GetPermissions();
|
module.Permissions = _permissionGrid.GetPermissions();
|
||||||
await ModuleService.UpdateModuleAsync(module);
|
await ModuleService.UpdateModuleAsync(module);
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ else
|
|||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
_roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true);
|
_roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true);
|
||||||
_roles = _roles.Where(item => item.Name != RoleNames.Everyone).ToList();
|
_roles.RemoveAll(item => item.Name == RoleNames.Everyone || item.Name == RoleNames.Unauthenticated);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -172,20 +172,50 @@
|
|||||||
<Section Name="Aliases" Heading="Aliases" ResourceKey="Aliases">
|
<Section Name="Aliases" Heading="Aliases" ResourceKey="Aliases">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="alias" HelpText="The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they should be separated by commas." ResourceKey="Aliases">Aliases: </Label>
|
<Label Class="col-sm-3" For="aliases" HelpText="The list of aliases for this site" ResourceKey="Aliases">Aliases: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
<button type="button" class="btn btn-primary" @onclick="AddAlias">@SharedLocalizer["Add"]</button>
|
||||||
</div>
|
<Pager Items="@_aliases">
|
||||||
</div>
|
<Header>
|
||||||
<div class="row mb-1 align-items-center">
|
<th style="width: 1px;"> </th>
|
||||||
<Label Class="col-sm-3" For="defaultalias" HelpText="The default alias for the site. Requests for non-default aliases will be redirected to the default alias." ResourceKey="DefaultAlias">Default Alias: </Label>
|
<th style="width: 1px;"> </th>
|
||||||
<div class="col-sm-9">
|
<th>@Localizer["AliasName"]</th>
|
||||||
<select id="defaultalias" class="form-select" @bind="@_defaultalias" required>
|
<th>@Localizer["AliasDefault"]</th>
|
||||||
@foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
</Header>
|
||||||
|
<Row>
|
||||||
|
@if (context.AliasId != _aliasid)
|
||||||
{
|
{
|
||||||
<option value="@name">@name</option>
|
<td>
|
||||||
|
@if (_aliasid == -1)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="@(() => EditAlias(context))">@SharedLocalizer["Edit"]</button>
|
||||||
}
|
}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@if (_aliasid == -1)
|
||||||
|
{
|
||||||
|
<ActionDialog Action="Delete" OnClick="@(async () => await DeleteAlias(context))" ResourceKey="DeleteModule" Class="btn btn-danger" Header="Delete Alias" Message="@string.Format(Localizer["Confirm.Alias.Delete", context.Name])" />
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td>@context.Name</td>
|
||||||
|
<td>@context.IsDefault</td>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<td><button type="button" class="btn btn-success" @onclick="@(async () => await SaveAlias())">@SharedLocalizer["Save"]</button></td>
|
||||||
|
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => await CancelAlias())">@SharedLocalizer["Cancel"]</button></td>
|
||||||
|
<td>
|
||||||
|
<input id="aliasname" class="form-control" @bind="@_aliasname" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select id="defaultaias" class="form-select" @bind="@_defaultalias" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
</select>
|
</select>
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -253,8 +283,9 @@
|
|||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
private string _name = string.Empty;
|
private string _name = string.Empty;
|
||||||
private List<Alias> _aliases;
|
private List<Alias> _aliases;
|
||||||
private string _defaultalias = string.Empty;
|
private int _aliasid = -1;
|
||||||
private string _urls = string.Empty;
|
private string _aliasname;
|
||||||
|
private string _defaultalias;
|
||||||
private string _runtime = "";
|
private string _runtime = "";
|
||||||
private string _prerender = "";
|
private string _prerender = "";
|
||||||
private int _logofileid = -1;
|
private int _logofileid = -1;
|
||||||
@ -304,10 +335,7 @@
|
|||||||
_prerender = site.RenderMode.Replace(_runtime, "");
|
_prerender = site.RenderMode.Replace(_runtime, "");
|
||||||
_isdeleted = site.IsDeleted.ToString();
|
_isdeleted = site.IsDeleted.ToString();
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
|
||||||
{
|
|
||||||
await GetAliases();
|
await GetAliases();
|
||||||
}
|
|
||||||
|
|
||||||
if (site.LogoFileId != null)
|
if (site.LogoFileId != null)
|
||||||
{
|
{
|
||||||
@ -408,24 +436,6 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_name != string.Empty && _themetype != "-" && _containertype != "-")
|
if (_name != string.Empty && _themetype != "-" && _containertype != "-")
|
||||||
{
|
|
||||||
var unique = true;
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
|
||||||
{
|
|
||||||
_urls = Regex.Replace(_urls, @"\r\n?|\n", ","); // convert line breaks to commas
|
|
||||||
var aliases = await AliasService.GetAliasesAsync();
|
|
||||||
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
|
||||||
{
|
|
||||||
var alias = aliases.Where(item => item.Name == name).FirstOrDefault();
|
|
||||||
if (alias != null && unique)
|
|
||||||
{
|
|
||||||
unique = (alias.TenantId == PageState.Site.TenantId && alias.SiteId == PageState.Site.SiteId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (unique && string.IsNullOrEmpty(_defaultalias)) unique = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unique)
|
|
||||||
{
|
{
|
||||||
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
||||||
if (site != null)
|
if (site != null)
|
||||||
@ -502,43 +512,6 @@
|
|||||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
|
||||||
{
|
|
||||||
var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
|
||||||
.Select(sValue => sValue.Trim()).ToArray();
|
|
||||||
foreach (Alias alias in _aliases)
|
|
||||||
{
|
|
||||||
if (!names.Contains(alias.Name.Trim()))
|
|
||||||
{
|
|
||||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string name in names)
|
|
||||||
{
|
|
||||||
var alias = _aliases.Find(item => item.Name.Trim() == name);
|
|
||||||
if (alias == null)
|
|
||||||
{
|
|
||||||
alias = new Alias();
|
|
||||||
alias.Name = name;
|
|
||||||
alias.TenantId = site.TenantId;
|
|
||||||
alias.SiteId = site.SiteId;
|
|
||||||
alias.IsDefault = (name == _defaultalias);
|
|
||||||
await AliasService.AddAliasAsync(alias);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (alias.Name != name || alias.IsDefault != (alias.Name.Trim() == _defaultalias))
|
|
||||||
{
|
|
||||||
alias.Name = name;
|
|
||||||
alias.IsDefault = (name == _defaultalias);
|
|
||||||
await AliasService.UpdateAliasAsync(alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await GetAliases();
|
|
||||||
}
|
|
||||||
|
|
||||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||||
|
|
||||||
if (refresh || reload)
|
if (refresh || reload)
|
||||||
@ -552,11 +525,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // deuplicate alias or default alias not specified
|
|
||||||
{
|
|
||||||
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
|
||||||
@ -578,19 +546,19 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var sites = await SiteService.GetSitesAsync();
|
var aliases = await AliasService.GetAliasesAsync();
|
||||||
if (sites.Count > 1)
|
if (aliases.Any(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId))
|
||||||
{
|
{
|
||||||
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
|
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
|
||||||
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
|
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
|
||||||
|
|
||||||
var aliases = await AliasService.GetAliasesAsync();
|
foreach (Alias alias in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
|
||||||
foreach (Alias a in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
|
|
||||||
{
|
{
|
||||||
await AliasService.DeleteAliasAsync(a.AliasId);
|
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl("admin/sites"));
|
aliases = await AliasService.GetAliasesAsync();
|
||||||
|
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + aliases.First().Name, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -637,20 +605,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GetAliases()
|
|
||||||
{
|
|
||||||
_urls = string.Empty;
|
|
||||||
_defaultalias = string.Empty;
|
|
||||||
_aliases = await AliasService.GetAliasesAsync();
|
|
||||||
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
|
|
||||||
foreach (Alias alias in _aliases)
|
|
||||||
{
|
|
||||||
_urls += (_urls == string.Empty) ? alias.Name.Trim() : ", " + alias.Name.Trim();
|
|
||||||
if (alias.IsDefault && string.IsNullOrEmpty(_defaultalias)) _defaultalias = alias.Name.Trim();
|
|
||||||
}
|
|
||||||
if (string.IsNullOrEmpty(_defaultalias)) _defaultalias = _aliases.First().Name.Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ToggleSMTPPassword()
|
private void ToggleSMTPPassword()
|
||||||
{
|
{
|
||||||
if (_smtppasswordtype == "password")
|
if (_smtppasswordtype == "password")
|
||||||
@ -664,4 +618,84 @@
|
|||||||
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task GetAliases()
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
_aliases = await AliasService.GetAliasesAsync();
|
||||||
|
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddAlias()
|
||||||
|
{
|
||||||
|
_aliases.Add(new Alias { AliasId = 0, Name = "", IsDefault = false });
|
||||||
|
_aliasid = 0;
|
||||||
|
_aliasname = "";
|
||||||
|
_defaultalias = "False";
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditAlias(Alias alias)
|
||||||
|
{
|
||||||
|
_aliasid = alias.AliasId;
|
||||||
|
_aliasname = alias.Name;
|
||||||
|
_defaultalias = alias.IsDefault.ToString();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAlias(Alias alias)
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||||
|
await GetAliases();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveAlias()
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_aliasname))
|
||||||
|
{
|
||||||
|
var aliases = await AliasService.GetAliasesAsync();
|
||||||
|
var alias = aliases.Where(item => item.Name == _aliasname).FirstOrDefault();
|
||||||
|
bool unique = (alias == null || alias.AliasId == _aliasid);
|
||||||
|
if (unique)
|
||||||
|
{
|
||||||
|
if (_aliasid == 0)
|
||||||
|
{
|
||||||
|
alias = new Alias { SiteId = PageState.Site.SiteId, TenantId = PageState.Site.TenantId, Name = _aliasname, IsDefault = bool.Parse(_defaultalias) };
|
||||||
|
await AliasService.AddAliasAsync(alias);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alias = _aliases.Single(item => item.AliasId == _aliasid);
|
||||||
|
alias.Name = _aliasname;
|
||||||
|
alias.IsDefault = bool.Parse(_defaultalias);
|
||||||
|
await AliasService.UpdateAliasAsync(alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // duplicate alias
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await GetAliases();
|
||||||
|
_aliasid = -1;
|
||||||
|
_aliasname = "";
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CancelAlias()
|
||||||
|
{
|
||||||
|
await GetAliases();
|
||||||
|
_aliasid = -1;
|
||||||
|
_aliasname = "";
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,16 +130,9 @@ else
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
||||||
@foreach (var database in _databases)
|
@foreach (var database in _databases)
|
||||||
{
|
|
||||||
if (database.IsDefault)
|
|
||||||
{
|
|
||||||
<option value="@database.Name" selected>@Localizer[@database.Name]</option>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -172,12 +165,11 @@ else
|
|||||||
private List<Database> _databases;
|
private List<Database> _databases;
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private string _databaseName = "LocalDB";
|
private string _databaseName;
|
||||||
private Type _databaseConfigType;
|
private Type _databaseConfigType;
|
||||||
private object _databaseConfig;
|
private object _databaseConfig;
|
||||||
private RenderFragment DatabaseConfigComponent { get; set; }
|
private RenderFragment DatabaseConfigComponent { get; set; }
|
||||||
|
|
||||||
|
|
||||||
private List<Theme> _themeList;
|
private List<Theme> _themeList;
|
||||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
@ -208,7 +200,16 @@ else
|
|||||||
_themeList = await ThemeService.GetThemesAsync();
|
_themeList = await ThemeService.GetThemesAsync();
|
||||||
_themes = ThemeService.GetThemeControls(_themeList);
|
_themes = ThemeService.GetThemeControls(_themeList);
|
||||||
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
||||||
|
|
||||||
_databases = await DatabaseService.GetDatabasesAsync();
|
_databases = await DatabaseService.GetDatabasesAsync();
|
||||||
|
if (_databases.Exists(item => item.IsDefault))
|
||||||
|
{
|
||||||
|
_databaseName = _databases.Find(item => item.IsDefault).Name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_databaseName = "LocalDB";
|
||||||
|
}
|
||||||
LoadDatabaseConfigComponent();
|
LoadDatabaseConfigComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,6 +308,7 @@ else
|
|||||||
user.SiteId = PageState.Site.SiteId;
|
user.SiteId = PageState.Site.SiteId;
|
||||||
user.Username = _hostusername;
|
user.Username = _hostusername;
|
||||||
user.Password = _hostpassword;
|
user.Password = _hostpassword;
|
||||||
|
user.LastIPAddress = PageState.RemoteIPAddress;
|
||||||
user = await UserService.LoginUserAsync(user);
|
user = await UserService.LoginUserAsync(user);
|
||||||
if (user.IsAuthenticated)
|
if (user.IsAuthenticated)
|
||||||
{
|
{
|
||||||
|
@ -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" />
|
<ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||||
|
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||||
</td>
|
</td>
|
||||||
<td style="width: 1px; vertical-align: middle;">
|
<td style="width: 1px; vertical-align: middle;">
|
||||||
|
@ -160,7 +160,7 @@ else
|
|||||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||||
<td>@context.FromDisplayName</td>
|
<td>@context.FromDisplayName</td>
|
||||||
<td>@context.Subject</td>
|
<td>@context.Subject</td>
|
||||||
<td>@context.CreatedOn</td>
|
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||||
</Row>
|
</Row>
|
||||||
<Detail>
|
<Detail>
|
||||||
<td colspan="2"></td>
|
<td colspan="2"></td>
|
||||||
@ -193,7 +193,7 @@ else
|
|||||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||||
<td>@context.ToDisplayName</td>
|
<td>@context.ToDisplayName</td>
|
||||||
<td>@context.Subject</td>
|
<td>@context.Subject</td>
|
||||||
<td>@context.CreatedOn</td>
|
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||||
</Row>
|
</Row>
|
||||||
<Detail>
|
<Detail>
|
||||||
<td colspan="2"></td>
|
<td colspan="2"></td>
|
||||||
|
@ -72,8 +72,19 @@ else
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="lastlogin" HelpText="The date and time when the user last signed in" ResourceKey="LastLogin"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="lastlogin" class="form-control" @bind="@lastlogin" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="lastipaddress" HelpText="The IP Address of the user recorded during their last login" ResourceKey="LastIPAddress"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="lastipaddress" class="form-control" @bind="@lastipaddress" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||||
@ -148,16 +159,20 @@ else
|
|||||||
private FileManager filemanager;
|
private FileManager filemanager;
|
||||||
private int photofileid = -1;
|
private int photofileid = -1;
|
||||||
private File photo = null;
|
private File photo = null;
|
||||||
|
private string isdeleted;
|
||||||
|
private string lastlogin;
|
||||||
|
private string lastipaddress;
|
||||||
|
|
||||||
private List<Profile> profiles;
|
private List<Profile> profiles;
|
||||||
private Dictionary<string, string> settings;
|
private Dictionary<string, string> settings;
|
||||||
private string category = string.Empty;
|
private string category = string.Empty;
|
||||||
|
|
||||||
private string createdby;
|
private string createdby;
|
||||||
private DateTime createdon;
|
private DateTime createdon;
|
||||||
private string modifiedby;
|
private string modifiedby;
|
||||||
private DateTime modifiedon;
|
private DateTime modifiedon;
|
||||||
private string deletedby;
|
private string deletedby;
|
||||||
private DateTime? deletedon;
|
private DateTime? deletedon;
|
||||||
private string isdeleted;
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
@ -186,6 +201,10 @@ else
|
|||||||
photofileid = -1;
|
photofileid = -1;
|
||||||
photo = null;
|
photo = null;
|
||||||
}
|
}
|
||||||
|
isdeleted = user.IsDeleted.ToString();
|
||||||
|
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||||
|
lastipaddress = user.LastIPAddress;
|
||||||
|
|
||||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
||||||
createdby = user.CreatedBy;
|
createdby = user.CreatedBy;
|
||||||
createdon = user.CreatedOn;
|
createdon = user.CreatedOn;
|
||||||
@ -193,7 +212,6 @@ else
|
|||||||
modifiedon = user.ModifiedOn;
|
modifiedon = user.ModifiedOn;
|
||||||
deletedby = user.DeletedBy;
|
deletedby = user.DeletedBy;
|
||||||
deletedon = user.DeletedOn;
|
deletedon = user.DeletedOn;
|
||||||
isdeleted = user.IsDeleted.ToString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,11 +36,9 @@ else
|
|||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
|
||||||
<th>@SharedLocalizer["Username"]</th>
|
<th>@SharedLocalizer["Username"]</th>
|
||||||
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
<th>@Localizer["LastLoginOn"]</th>
|
<th>@Localizer["LastLoginOn"]</th>
|
||||||
<th>@Localizer["LastIPAddress"]</th>
|
|
||||||
<th>@Localizer["CreatedOn"]</th>
|
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td>
|
<td>
|
||||||
@ -52,12 +50,9 @@ else
|
|||||||
<td>
|
<td>
|
||||||
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
|
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
|
||||||
</td>
|
</td>
|
||||||
<td>@context.User.DisplayName</td>
|
|
||||||
<td>@context.User.Username</td>
|
<td>@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>
|
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn)</td>
|
||||||
<td>@context.User.LastIPAddress</td>
|
|
||||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}",context.User.CreatedOn)</td>
|
|
||||||
|
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
@ -259,6 +254,12 @@ else
|
|||||||
<input id="scopes" class="form-control" @bind="@_scopes" />
|
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="parameters" HelpText="Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple)." ResourceKey="Parameters">Parameters:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="parameters" class="form-control" @bind="@_parameters" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -380,6 +381,7 @@ else
|
|||||||
private string _clientsecrettype = "password";
|
private string _clientsecrettype = "password";
|
||||||
private string _toggleclientsecret = string.Empty;
|
private string _toggleclientsecret = string.Empty;
|
||||||
private string _scopes;
|
private string _scopes;
|
||||||
|
private string _parameters;
|
||||||
private string _pkce;
|
private string _pkce;
|
||||||
private string _redirecturl;
|
private string _redirecturl;
|
||||||
private string _identifierclaimtype;
|
private string _identifierclaimtype;
|
||||||
@ -432,6 +434,7 @@ else
|
|||||||
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
||||||
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
||||||
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
||||||
|
_parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", "");
|
||||||
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
||||||
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||||
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
|
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
|
||||||
@ -549,6 +552,7 @@ else
|
|||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Parameters", _parameters, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
||||||
|
@ -88,15 +88,17 @@ else
|
|||||||
userid = Int32.Parse(PageState.QueryString["id"]);
|
userid = Int32.Parse(PageState.QueryString["id"]);
|
||||||
User user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
User user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||||
name = user.DisplayName;
|
name = user.DisplayName;
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true);
|
roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true);
|
||||||
roles = roles.Where(item => item.Name != RoleNames.Everyone).ToList();
|
roles.RemoveAll(item => item.Name == RoleNames.Everyone || item.Name == RoleNames.Unauthenticated);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
roles = await RoleService.GetRolesAsync(PageState.Site.SiteId);
|
roles = await RoleService.GetRolesAsync(PageState.Site.SiteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
await GetUserRoles();
|
await GetUserRoles();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -310,6 +310,17 @@
|
|||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
var upload = await interop.GetFiles(_fileinputid);
|
var upload = await interop.GetFiles(_fileinputid);
|
||||||
if (upload.Length > 0)
|
if (upload.Length > 0)
|
||||||
|
{
|
||||||
|
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
|
try
|
||||||
{
|
{
|
||||||
@ -360,6 +371,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
_message = string.Format(Localizer["Message.File.Restricted"], restricted);
|
||||||
|
_messagetype = MessageType.Warning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
_message = Localizer["Message.File.NotSelected"];
|
_message = Localizer["Message.File.NotSelected"];
|
||||||
_messagetype = MessageType.Warning;
|
_messagetype = MessageType.Warning;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Oqtane.Shared;
|
|
||||||
|
|
||||||
namespace Oqtane.Modules.Controls
|
namespace Oqtane.Modules.Controls
|
||||||
{
|
{
|
||||||
public class LocalizableComponent : ModuleControlBase
|
public class LocalizableComponent : ModuleControlBase
|
||||||
{
|
{
|
||||||
|
[Inject] public IStringLocalizerFactory LocalizerFactory { get; set; }
|
||||||
|
|
||||||
private IStringLocalizer _localizer;
|
private IStringLocalizer _localizer;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
@ -30,48 +30,32 @@ namespace Oqtane.Modules.Controls
|
|||||||
var key = $"{ResourceKey}.{propertyName}";
|
var key = $"{ResourceKey}.{propertyName}";
|
||||||
var value = Localize(key);
|
var value = Localize(key);
|
||||||
|
|
||||||
if (value == key)
|
if (value == key || value == String.Empty)
|
||||||
{
|
{
|
||||||
// Returns default property value (English version) instead of ResourceKey.PropertyName
|
// return default property value if key does not exist in resource file or value is empty
|
||||||
return propertyValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (value == String.Empty)
|
|
||||||
{
|
|
||||||
// Returns default property value (English version)
|
|
||||||
return propertyValue;
|
return propertyValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// return localized value
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
IsLocalizable = false;
|
IsLocalizable = false;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(ResourceType))
|
if (String.IsNullOrEmpty(ResourceType))
|
||||||
{
|
{
|
||||||
ResourceType = ModuleState?.ModuleType;
|
ResourceType = ModuleState?.ModuleType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!String.IsNullOrEmpty(ResourceKey) && !string.IsNullOrEmpty(ResourceType))
|
if (!String.IsNullOrEmpty(ResourceKey) && !String.IsNullOrEmpty(ResourceType))
|
||||||
{
|
{
|
||||||
var moduleType = Type.GetType(ResourceType);
|
_localizer = LocalizerFactory.Create(ResourceType);
|
||||||
if (moduleType != null)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceActivator.GetScope())
|
|
||||||
{
|
|
||||||
var localizerFactory = scope.ServiceProvider.GetService<IStringLocalizerFactory>();
|
|
||||||
_localizer = localizerFactory.Create(moduleType);
|
|
||||||
|
|
||||||
IsLocalizable = true;
|
IsLocalizable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Controls
|
@namespace Oqtane.Modules.Controls
|
||||||
@inherits ModuleControlBase
|
@inherits ModuleControlBase
|
||||||
|
@inject IStringLocalizerFactory LocalizerFactory
|
||||||
@typeparam TableItem
|
@typeparam TableItem
|
||||||
|
|
||||||
@if (ItemList != null)
|
@if (ItemList != null)
|
||||||
@ -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>
|
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item disabled">
|
<li class="page-item disabled">
|
||||||
<a class="page-link" style="white-space: nowrap;">Page @_page of @_pages</a>
|
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
@ -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>
|
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item disabled">
|
<li class="page-item disabled">
|
||||||
<a class="page-link" style="white-space: nowrap;">Page @_page of @_pages</a>
|
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private IStringLocalizer Localizer;
|
||||||
private int _pages = 0;
|
private int _pages = 0;
|
||||||
private int _page = 1;
|
private int _page = 1;
|
||||||
private int _maxItems = 10;
|
private int _maxItems = 10;
|
||||||
@ -215,6 +217,11 @@
|
|||||||
|
|
||||||
private IEnumerable<TableItem> ItemList { get; set; }
|
private IEnumerable<TableItem> ItemList { get; set; }
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
Localizer = LocalizerFactory.Create(GetType().FullName);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(Format))
|
if (string.IsNullOrEmpty(Format))
|
||||||
|
@ -127,11 +127,10 @@
|
|||||||
_permissionnames = PermissionNames;
|
_permissionnames = PermissionNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId);
|
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId, true);
|
||||||
_roles.Insert(0, new Role { Name = RoleNames.Everyone });
|
if (!UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
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>();
|
_permissions = new List<PermissionString>();
|
||||||
@ -254,6 +253,7 @@
|
|||||||
permission = _permissions[i];
|
permission = _permissions[i];
|
||||||
List<string> ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
|
List<string> ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||||
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
||||||
|
ids.Remove("!" + RoleNames.Unauthenticated); // remove deny unauthenticated
|
||||||
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
|
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
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)
|
// preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor)
|
||||||
_originalrichhtml = await interop.GetHtml(_editorElement);
|
_originalrichhtml = await interop.GetHtml(_editorElement);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
await interop.LoadEditorContent(_editorElement, _richhtml);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CloseFileManager()
|
public void CloseFileManager()
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RazorLangVersion>3.0</RazorLangVersion>
|
<RazorLangVersion>3.0</RazorLangVersion>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>3.1.1</Version>
|
<Version>3.1.3</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -13,7 +13,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
@ -150,4 +150,7 @@
|
|||||||
<data name="EditModule.Text" xml:space="preserve">
|
<data name="EditModule.Text" xml:space="preserve">
|
||||||
<value>Edit</value>
|
<value>Edit</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Modules" xml:space="preserve">
|
||||||
|
<value>Modules</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -142,7 +142,7 @@
|
|||||||
<value>Site Settings Saved</value>
|
<value>Site Settings Saved</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Aliases.Taken" xml:space="preserve">
|
<data name="Message.Aliases.Taken" xml:space="preserve">
|
||||||
<value>The Default Alias Has Not Been Specified Or An Alias Was Specified That Has Already Been Used For Another Site</value>
|
<value>An Alias Was Specified That Has Already Been Used For Another Site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Required.SiteName" xml:space="preserve">
|
<data name="Message.Required.SiteName" xml:space="preserve">
|
||||||
<value>You Must Provide A Site Name, Alias, And Default Theme/Container</value>
|
<value>You Must Provide A Site Name, Alias, And Default Theme/Container</value>
|
||||||
@ -324,4 +324,13 @@
|
|||||||
<data name="Aliases.Heading" xml:space="preserve">
|
<data name="Aliases.Heading" xml:space="preserve">
|
||||||
<value>Aliases</value>
|
<value>Aliases</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AliasName" xml:space="preserve">
|
||||||
|
<value>Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="AliasDefault" xml:space="preserve">
|
||||||
|
<value>Default?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirm.Alias.Delete" xml:space="preserve">
|
||||||
|
<value>Are You Sure You Wish To Delete {0}?</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -219,4 +219,10 @@
|
|||||||
<data name="DeleteAllNotifications.Text" xml:space="preserve">
|
<data name="DeleteAllNotifications.Text" xml:space="preserve">
|
||||||
<value>Delete ALL Notifications</value>
|
<value>Delete ALL Notifications</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Notifications.Heading" xml:space="preserve">
|
||||||
|
<value>Notifications</value>
|
||||||
|
</data>
|
||||||
|
<data name="Profile.Heading" xml:space="preserve">
|
||||||
|
<value>Profile</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -186,4 +186,16 @@
|
|||||||
<data name="Password.Placeholder" xml:space="preserve">
|
<data name="Password.Placeholder" xml:space="preserve">
|
||||||
<value>Password</value>
|
<value>Password</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="LastIPAddress.HelpText" xml:space="preserve">
|
||||||
|
<value>The IP Address of the user recorded during their last login</value>
|
||||||
|
</data>
|
||||||
|
<data name="LastIPAddress.Text" xml:space="preserve">
|
||||||
|
<value>Last IP Address: </value>
|
||||||
|
</data>
|
||||||
|
<data name="LastLogin.HelpText" xml:space="preserve">
|
||||||
|
<value>The date and time when the user last signed in</value>
|
||||||
|
</data>
|
||||||
|
<data name="LastLogin.Text" xml:space="preserve">
|
||||||
|
<value>Last Login:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -369,12 +369,6 @@
|
|||||||
<data name="Required" xml:space="preserve">
|
<data name="Required" xml:space="preserve">
|
||||||
<value>Required</value>
|
<value>Required</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CreatedOn" xml:space="preserve">
|
|
||||||
<value>Created On</value>
|
|
||||||
</data>
|
|
||||||
<data name="LastIPAddress" xml:space="preserve">
|
|
||||||
<value>Last IP Address</value>
|
|
||||||
</data>
|
|
||||||
<data name="LastLoginOn" xml:space="preserve">
|
<data name="LastLoginOn" xml:space="preserve">
|
||||||
<value>Last Login</value>
|
<value>Last Login</value>
|
||||||
</data>
|
</data>
|
||||||
@ -384,4 +378,10 @@
|
|||||||
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
||||||
<value>Identifier Claim:</value>
|
<value>Identifier Claim:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Parameters.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple).</value>
|
||||||
|
</data>
|
||||||
|
<data name="Parameters.Text" xml:space="preserve">
|
||||||
|
<value>Parameters:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -141,4 +141,7 @@
|
|||||||
<data name="Success.File.Upload" xml:space="preserve">
|
<data name="Success.File.Upload" xml:space="preserve">
|
||||||
<value>File Upload Succeeded</value>
|
<value>File Upload Succeeded</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.File.Restricted" xml:space="preserve">
|
||||||
|
<value>Files With Extension Of {0} Are Restricted From Upload. Please Contact Your Administrator For More Information.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
123
Oqtane.Client/Resources/Modules/Controls/Pager.resx
Normal file
123
Oqtane.Client/Resources/Modules/Controls/Pager.resx
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="PageOfPages" xml:space="preserve">
|
||||||
|
<value>Page {0} of {1}</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
@ -327,4 +327,13 @@
|
|||||||
<data name="ShowPassword" xml:space="preserve">
|
<data name="ShowPassword" xml:space="preserve">
|
||||||
<value>Show</value>
|
<value>Show</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PageOfPages" xml:space="preserve">
|
||||||
|
<value>Page {0} of {1}</value>
|
||||||
|
</data>
|
||||||
|
<data name="Url Mappings" xml:space="preserve">
|
||||||
|
<value>Url Mappings</value>
|
||||||
|
</data>
|
||||||
|
<data name="Visitor Management" xml:space="preserve">
|
||||||
|
<value>Visitor Management</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@ -14,10 +15,12 @@ namespace Oqtane.Services
|
|||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class FileService : ServiceBase, IFileService
|
public class FileService : ServiceBase, IFileService
|
||||||
{
|
{
|
||||||
|
private readonly SiteState _siteState;
|
||||||
private readonly IJSRuntime _jsRuntime;
|
private readonly IJSRuntime _jsRuntime;
|
||||||
|
|
||||||
public FileService(HttpClient http, SiteState siteState, IJSRuntime jsRuntime) : base(http, siteState)
|
public FileService(HttpClient http, SiteState siteState, IJSRuntime jsRuntime) : base(http, siteState)
|
||||||
{
|
{
|
||||||
|
_siteState = siteState;
|
||||||
_jsRuntime = jsRuntime;
|
_jsRuntime = jsRuntime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +33,8 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
public async Task<List<File>> GetFilesAsync(string folder)
|
public async Task<List<File>> GetFilesAsync(string folder)
|
||||||
{
|
{
|
||||||
return await GetJsonAsync<List<File>>($"{Apiurl}?folder={folder}");
|
List<File> files = await GetJsonAsync<List<File>>($"{Apiurl}?folder={folder}");
|
||||||
|
return files.OrderBy(item => item.Name).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<File>> GetFilesAsync(int siteId, string folderPath)
|
public async Task<List<File>> GetFilesAsync(int siteId, string folderPath)
|
||||||
@ -42,7 +46,8 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
var path = WebUtility.UrlEncode(folderPath);
|
var path = WebUtility.UrlEncode(folderPath);
|
||||||
|
|
||||||
return await GetJsonAsync<List<File>>($"{Apiurl}/{siteId}/{path}");
|
List<File> files = await GetJsonAsync<List<File>>($"{Apiurl}/{siteId}/{path}");
|
||||||
|
return files.OrderBy(item => item.Name).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<File> GetFileAsync(int fileId)
|
public async Task<File> GetFileAsync(int fileId)
|
||||||
@ -80,7 +85,7 @@ namespace Oqtane.Services
|
|||||||
string result = "";
|
string result = "";
|
||||||
|
|
||||||
var interop = new Interop(_jsRuntime);
|
var interop = new Interop(_jsRuntime);
|
||||||
await interop.UploadFiles($"{Apiurl}/upload", folder, id);
|
await interop.UploadFiles($"{Apiurl}/upload", folder, id, _siteState.AntiForgeryToken);
|
||||||
|
|
||||||
// uploading files is asynchronous so we need to wait for the upload to complete
|
// uploading files is asynchronous so we need to wait for the upload to complete
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
@ -9,15 +9,19 @@
|
|||||||
if (childPage.PageId == PageState.Page.PageId)
|
if (childPage.PageId == PageState.Page.PageId)
|
||||||
{
|
{
|
||||||
<a class="nav-link active px-3" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
<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" />
|
<span class="@childPage.Icon" aria-hidden="true" />
|
||||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<a class="nav-link px-3" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
<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" />
|
<span class="@childPage.Icon" aria-hidden="true" />
|
||||||
@childPage.Name
|
@childPage.Name
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,8 +38,10 @@ else
|
|||||||
{
|
{
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
<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" />
|
<span class="@childPage.Icon" aria-hidden="true" />
|
||||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
@ -43,8 +49,10 @@ else
|
|||||||
{
|
{
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
<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" />
|
<span class="@childPage.Icon" aria-hidden="true" />
|
||||||
@childPage.Name
|
@childPage.Name
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,10 @@
|
|||||||
{
|
{
|
||||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
<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" />
|
<span class="@childPage.Icon" aria-hidden="true" />
|
||||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
@ -18,8 +20,10 @@
|
|||||||
{
|
{
|
||||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
<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" />
|
<span class="@childPage.Icon" aria-hidden="true" />
|
||||||
@childPage.Name
|
@childPage.Name
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
@ -38,8 +42,10 @@ else
|
|||||||
{
|
{
|
||||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
<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" />
|
<span class="@childPage.Icon" aria-hidden="true" />
|
||||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
@ -47,8 +53,10 @@ else
|
|||||||
{
|
{
|
||||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
<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" />
|
<span class="@childPage.Icon" aria-hidden="true" />
|
||||||
@childPage.Name
|
@childPage.Name
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</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
|
try
|
||||||
{
|
{
|
||||||
_jsRuntime.InvokeVoidAsync(
|
_jsRuntime.InvokeVoidAsync(
|
||||||
"Oqtane.Interop.uploadFiles",
|
"Oqtane.Interop.uploadFiles",
|
||||||
posturl, folder, id);
|
posturl, folder, id, antiforgerytoken);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Version>3.1.1</Version>
|
<Version>3.1.3</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Database.MySQL</id>
|
<id>Oqtane.Database.MySQL</id>
|
||||||
<version>3.1.1</version>
|
<version>3.1.3</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane MySQL Provider</title>
|
<title>Oqtane MySQL Provider</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Version>3.1.1</Version>
|
<Version>3.1.3</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Database.PostgreSQL</id>
|
<id>Oqtane.Database.PostgreSQL</id>
|
||||||
<version>3.1.1</version>
|
<version>3.1.3</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane PostgreSQL Provider</title>
|
<title>Oqtane PostgreSQL Provider</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Version>3.1.1</Version>
|
<Version>3.1.3</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Database.SqlServer</id>
|
<id>Oqtane.Database.SqlServer</id>
|
||||||
<version>3.1.1</version>
|
<version>3.1.3</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane SQL Server Provider</title>
|
<title>Oqtane SQL Server Provider</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -32,6 +32,21 @@ namespace Oqtane.Database.SqlServer
|
|||||||
return table.Column<int>(name: name, nullable: false).Annotation("SqlServer:Identity", "1, 1");
|
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)
|
public override int ExecuteNonQuery(string connectionString, string query)
|
||||||
{
|
{
|
||||||
var conn = new SqlConnection(FormatConnectionString(connectionString));
|
var conn = new SqlConnection(FormatConnectionString(connectionString));
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Version>3.1.1</Version>
|
<Version>3.1.3</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Database.Sqlite</id>
|
<id>Oqtane.Database.Sqlite</id>
|
||||||
<version>3.1.1</version>
|
<version>3.1.3</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane SQLite Provider</title>
|
<title>Oqtane SQLite Provider</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -35,7 +35,7 @@ namespace Oqtane.Database.Sqlite
|
|||||||
// not implemented as SQLite does not support dropping columns
|
// 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
|
// not implemented as SQLite does not support altering columns
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Client</id>
|
<id>Oqtane.Client</id>
|
||||||
<version>3.1.1</version>
|
<version>3.1.3</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Framework</id>
|
<id>Oqtane.Framework</id>
|
||||||
<version>3.1.1</version>
|
<version>3.1.3</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -11,8 +11,8 @@
|
|||||||
<copyright>.NET Foundation</copyright>
|
<copyright>.NET Foundation</copyright>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v3.1.1/Oqtane.Framework.3.1.1.Upgrade.zip</projectUrl>
|
<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.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane framework</tags>
|
<tags>oqtane framework</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Server</id>
|
<id>Oqtane.Server</id>
|
||||||
<version>3.1.1</version>
|
<version>3.1.3</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Shared</id>
|
<id>Oqtane.Shared</id>
|
||||||
<version>3.1.1</version>
|
<version>3.1.3</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Updater</id>
|
<id>Oqtane.Updater</id>
|
||||||
<version>3.1.1</version>
|
<version>3.1.3</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -1 +1 @@
|
|||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.1.Install.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.3.Install.zip" -Force
|
@ -1 +1 @@
|
|||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.1.Upgrade.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.3.Upgrade.zip" -Force
|
@ -255,7 +255,7 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url} {Error}", url, ex.Message);
|
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "File Could Not Be Downloaded From Url {Url} {Error}", url, ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -276,9 +276,17 @@ namespace Oqtane.Controllers
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!formfile.FileName.IsPathOrFileValid())
|
// ensure filename is valid
|
||||||
|
string token = ".part_";
|
||||||
|
if (!formfile.FileName.IsPathOrFileValid() || !formfile.FileName.Contains(token))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for allowable file extensions (ignore token)
|
||||||
|
var extension = Path.GetExtension(formfile.FileName.Substring(0, formfile.FileName.IndexOf(token))).Replace(".", "");
|
||||||
|
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||||
{
|
{
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,9 +339,9 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
string merged = "";
|
string merged = "";
|
||||||
|
|
||||||
// parse the filename which is in the format of filename.ext.part_x_y
|
// parse the filename which is in the format of filename.ext.part_001_999
|
||||||
string token = ".part_";
|
string token = ".part_";
|
||||||
string parts = Path.GetExtension(filename)?.Replace(token, ""); // returns "x_y"
|
string parts = Path.GetExtension(filename)?.Replace(token, ""); // returns "001_999"
|
||||||
int totalparts = int.Parse(parts?.Substring(parts.IndexOf("_") + 1));
|
int totalparts = int.Parse(parts?.Substring(parts.IndexOf("_") + 1));
|
||||||
|
|
||||||
filename = Path.GetFileNameWithoutExtension(filename); // base filename
|
filename = Path.GetFileNameWithoutExtension(filename); // base filename
|
||||||
@ -370,13 +378,6 @@ namespace Oqtane.Controllers
|
|||||||
System.IO.File.Delete(filepart);
|
System.IO.File.Delete(filepart);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for allowable file extensions
|
|
||||||
if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(filename)?.ToLower().Replace(".", "")))
|
|
||||||
{
|
|
||||||
System.IO.File.Delete(Path.Combine(folder, filename + ".tmp"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// remove file if it already exists
|
// remove file if it already exists
|
||||||
if (System.IO.File.Exists(Path.Combine(folder, filename)))
|
if (System.IO.File.Exists(Path.Combine(folder, filename)))
|
||||||
{
|
{
|
||||||
@ -386,7 +387,6 @@ namespace Oqtane.Controllers
|
|||||||
// rename file now that the entire process is completed
|
// rename file now that the entire process is completed
|
||||||
System.IO.File.Move(Path.Combine(folder, filename + ".tmp"), Path.Combine(folder, filename));
|
System.IO.File.Move(Path.Combine(folder, filename + ".tmp"), Path.Combine(folder, filename));
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "File Uploaded {File}", Path.Combine(folder, filename));
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "File Uploaded {File}", Path.Combine(folder, filename));
|
||||||
}
|
|
||||||
|
|
||||||
merged = filename;
|
merged = filename;
|
||||||
}
|
}
|
||||||
@ -394,8 +394,7 @@ namespace Oqtane.Controllers
|
|||||||
|
|
||||||
// clean up file parts which are more than 2 hours old ( which can happen if a prior file upload failed )
|
// clean up file parts which are more than 2 hours old ( which can happen if a prior file upload failed )
|
||||||
var cleanupFiles = Directory.EnumerateFiles(folder, "*" + token + "*")
|
var cleanupFiles = Directory.EnumerateFiles(folder, "*" + token + "*")
|
||||||
.Where(f => Path.GetExtension(f).StartsWith(token));
|
.Where(f => Path.GetExtension(f).StartsWith(token) && !Path.GetFileName(f).StartsWith(filename));
|
||||||
|
|
||||||
foreach (var file in cleanupFiles)
|
foreach (var file in cleanupFiles)
|
||||||
{
|
{
|
||||||
var createdDate = System.IO.File.GetCreationTime(file).ToUniversalTime();
|
var createdDate = System.IO.File.GetCreationTime(file).ToUniversalTime();
|
||||||
@ -603,7 +602,7 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Error Creating Image For File {FilePath} {Width} {Height} {Mode} {Rotate} {Error}", filepath, width, height, mode, rotate, ex.Message);
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, ex, "Error Creating Image For File {FilePath} {Width} {Height} {Mode} {Rotate} {Error}", filepath, width, height, mode, rotate, ex.Message);
|
||||||
imagepath = "";
|
imagepath = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +63,8 @@ namespace Oqtane.Controllers
|
|||||||
module.CreatedOn = pagemodule.Module.CreatedOn;
|
module.CreatedOn = pagemodule.Module.CreatedOn;
|
||||||
module.ModifiedBy = pagemodule.Module.ModifiedBy;
|
module.ModifiedBy = pagemodule.Module.ModifiedBy;
|
||||||
module.ModifiedOn = pagemodule.Module.ModifiedOn;
|
module.ModifiedOn = pagemodule.Module.ModifiedOn;
|
||||||
|
module.DeletedBy = pagemodule.DeletedBy;
|
||||||
|
module.DeletedOn = pagemodule.DeletedOn;
|
||||||
module.IsDeleted = pagemodule.IsDeleted;
|
module.IsDeleted = pagemodule.IsDeleted;
|
||||||
|
|
||||||
module.PageModuleId = pagemodule.PageModuleId;
|
module.PageModuleId = pagemodule.PageModuleId;
|
||||||
@ -139,24 +141,41 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Roles = RoleNames.Registered)]
|
[Authorize(Roles = RoleNames.Registered)]
|
||||||
public Module Put(int id, [FromBody] Module module)
|
public Module Put(int id, [FromBody] Module module)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _modules.GetModule(module.ModuleId, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
var _module = _modules.GetModule(module.ModuleId, false);
|
||||||
|
|
||||||
|
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _module != null && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
module = _modules.UpdateModule(module);
|
module = _modules.UpdateModule(module);
|
||||||
|
|
||||||
|
if (_module.AllPages != module.AllPages)
|
||||||
|
{
|
||||||
|
var pageModules = _pageModules.GetPageModules(module.SiteId).ToList();
|
||||||
if (module.AllPages)
|
if (module.AllPages)
|
||||||
{
|
{
|
||||||
var pageModule = _pageModules.GetPageModules(module.SiteId).FirstOrDefault(item => item.ModuleId == module.ModuleId);
|
var pageModule = _pageModules.GetPageModule(module.PageModuleId);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module);
|
|
||||||
|
|
||||||
var pages = _pages.GetPages(module.SiteId).ToList();
|
var pages = _pages.GetPages(module.SiteId).ToList();
|
||||||
foreach (Page page in pages)
|
foreach (Page page in pages)
|
||||||
{
|
{
|
||||||
if (page.PageId != pageModule.PageId && !page.Path.StartsWith("admin/"))
|
if (!pageModules.Exists(item => item.ModuleId == module.ModuleId && item.PageId == page.PageId) && !page.Path.StartsWith("admin/"))
|
||||||
{
|
{
|
||||||
_pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType });
|
_pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var pageModule in pageModules)
|
||||||
|
{
|
||||||
|
if (pageModule.ModuleId == module.ModuleId && pageModule.PageModuleId != module.PageModuleId)
|
||||||
|
{
|
||||||
|
_pageModules.DeletePageModule(pageModule.PageModuleId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
||||||
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -53,7 +53,7 @@ namespace Oqtane.Controllers
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
results.Add(new Dictionary<string, string>() { { "Error", ex.Message } });
|
results.Add(new Dictionary<string, string>() { { "Error", ex.Message } });
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId} Resulted In An Error {Error}", sqlquery.Query, sqlquery.TenantId, ex.Message);
|
_logger.Log(LogLevel.Error, this, LogFunction.Other, ex, "Sql Query {Query} Executed on Tenant {TenantId} Resulted In An Error {Error}", sqlquery.Query, sqlquery.TenantId, ex.Message);
|
||||||
}
|
}
|
||||||
sqlquery.Results = results;
|
sqlquery.Results = results;
|
||||||
return sqlquery;
|
return sqlquery;
|
||||||
|
@ -327,6 +327,8 @@ namespace Oqtane.Controllers
|
|||||||
var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, true);
|
var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, true);
|
||||||
if (result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
|
var LastIPAddress = user.LastIPAddress ?? "";
|
||||||
|
|
||||||
user = _users.GetUser(user.Username);
|
user = _users.GetUser(user.Username);
|
||||||
if (user.TwoFactorRequired)
|
if (user.TwoFactorRequired)
|
||||||
{
|
{
|
||||||
@ -353,7 +355,7 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
loginUser.IsAuthenticated = true;
|
loginUser.IsAuthenticated = true;
|
||||||
loginUser.LastLoginOn = DateTime.UtcNow;
|
loginUser.LastLoginOn = DateTime.UtcNow;
|
||||||
loginUser.LastIPAddress = HttpContext.Connection.RemoteIpAddress.ToString();
|
loginUser.LastIPAddress = LastIPAddress;
|
||||||
_users.UpdateUser(loginUser);
|
_users.UpdateUser(loginUser);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", user.Username);
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", user.Username);
|
||||||
}
|
}
|
||||||
|
@ -39,23 +39,22 @@ namespace Oqtane.Controllers
|
|||||||
public IEnumerable<UserRole> Get(string siteid, string userid = null, string rolename = null)
|
public IEnumerable<UserRole> Get(string siteid, string userid = null, string rolename = null)
|
||||||
{
|
{
|
||||||
int SiteId;
|
int SiteId;
|
||||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId && (userid != null || rolename != null))
|
||||||
{
|
|
||||||
int UserId = (int.TryParse(userid, out UserId)) ? UserId : -1;
|
|
||||||
if (User.IsInRole(RoleNames.Admin) || ((userid == null || _userPermissions.GetUser().UserId == UserId) && (rolename == null || (User.IsInRole(rolename) && rolename != RoleNames.Registered))))
|
|
||||||
{
|
{
|
||||||
var userroles = _userRoles.GetUserRoles(SiteId).ToList();
|
var userroles = _userRoles.GetUserRoles(SiteId).ToList();
|
||||||
if (userid != null)
|
if (userid != null)
|
||||||
{
|
{
|
||||||
|
int UserId = int.TryParse(userid, out UserId) ? UserId : -1;
|
||||||
userroles = userroles.Where(item => item.UserId == UserId).ToList();
|
userroles = userroles.Where(item => item.UserId == UserId).ToList();
|
||||||
}
|
}
|
||||||
if (rolename != null)
|
if (rolename != null)
|
||||||
{
|
{
|
||||||
userroles = userroles.Where(item => item.Role.Name == rolename).ToList();
|
userroles = userroles.Where(item => item.Role.Name == rolename).ToList();
|
||||||
}
|
}
|
||||||
|
var user = _userPermissions.GetUser();
|
||||||
for (int i = 0; i < userroles.Count(); i++)
|
for (int i = 0; i < userroles.Count(); i++)
|
||||||
{
|
{
|
||||||
userroles[i] = Filter(userroles[i]);
|
userroles[i] = Filter(userroles[i], user.UserId);
|
||||||
}
|
}
|
||||||
return userroles.OrderBy(u => u.User.DisplayName);
|
return userroles.OrderBy(u => u.User.DisplayName);
|
||||||
}
|
}
|
||||||
@ -66,13 +65,6 @@ namespace Oqtane.Controllers
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UserRole Get Attempt For Site {SiteId} User {UserId} Role {RoleName}", siteid, userid, rolename);
|
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET api/<controller>/5
|
// GET api/<controller>/5
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
@ -82,16 +74,7 @@ namespace Oqtane.Controllers
|
|||||||
var userrole = _userRoles.GetUserRole(id);
|
var userrole = _userRoles.GetUserRole(id);
|
||||||
if (userrole != null && SiteValid(userrole.Role.SiteId))
|
if (userrole != null && SiteValid(userrole.Role.SiteId))
|
||||||
{
|
{
|
||||||
if (User.IsInRole(RoleNames.Admin) || User.Identity.Name?.ToLower() != userrole.User.Username.ToLower() || User.IsInRole(userrole.Role.Name))
|
return Filter(userrole, _userPermissions.GetUser().UserId);
|
||||||
{
|
|
||||||
return Filter(userrole);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Role Get Attempt {UserRoleId}", id);
|
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -101,7 +84,7 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserRole Filter(UserRole userrole)
|
private UserRole Filter(UserRole userrole, int userid)
|
||||||
{
|
{
|
||||||
if (userrole != null)
|
if (userrole != null)
|
||||||
{
|
{
|
||||||
@ -110,7 +93,7 @@ namespace Oqtane.Controllers
|
|||||||
userrole.User.TwoFactorCode = "";
|
userrole.User.TwoFactorCode = "";
|
||||||
userrole.User.TwoFactorExpiry = null;
|
userrole.User.TwoFactorExpiry = null;
|
||||||
|
|
||||||
if (!User.IsInRole(RoleNames.Admin) && User.Identity.Name?.ToLower() != userrole.User.Username.ToLower())
|
if (!User.IsInRole(RoleNames.Admin) && userid != userrole.User.UserId)
|
||||||
{
|
{
|
||||||
userrole.User.Email = "";
|
userrole.User.Email = "";
|
||||||
userrole.User.PhotoFileId = null;
|
userrole.User.PhotoFileId = null;
|
||||||
|
@ -81,7 +81,7 @@ namespace Oqtane.Databases
|
|||||||
builder.DropColumn(name, table);
|
builder.DropColumn(name, table);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode)
|
public virtual void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode, string index)
|
||||||
{
|
{
|
||||||
builder.AlterColumn<string>(RewriteName(name), RewriteName(table), maxLength: length, nullable: nullable, unicode: unicode);
|
builder.AlterColumn<string>(RewriteName(name), RewriteName(table), maxLength: length, nullable: nullable, unicode: unicode);
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ namespace Oqtane.Databases.Interfaces
|
|||||||
|
|
||||||
public void DropColumn(MigrationBuilder builder, string name, string table);
|
public void DropColumn(MigrationBuilder builder, string name, string table);
|
||||||
|
|
||||||
public void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode);
|
public void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode, string index);
|
||||||
|
|
||||||
public DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString);
|
public DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString);
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,20 @@ namespace Oqtane.Extensions
|
|||||||
options.Events.OnTokenValidated = OnTokenValidated;
|
options.Events.OnTokenValidated = OnTokenValidated;
|
||||||
options.Events.OnAccessDenied = OnAccessDenied;
|
options.Events.OnAccessDenied = OnAccessDenied;
|
||||||
options.Events.OnRemoteFailure = OnRemoteFailure;
|
options.Events.OnRemoteFailure = OnRemoteFailure;
|
||||||
|
if (sitesettings.GetValue("ExternalLogin:Parameters", "") != "")
|
||||||
|
{
|
||||||
|
options.Events = new OpenIdConnectEvents
|
||||||
|
{
|
||||||
|
OnRedirectToIdentityProvider = context =>
|
||||||
|
{
|
||||||
|
foreach(var parameter in sitesettings.GetValue("ExternalLogin:Parameters", "").Split(","))
|
||||||
|
{
|
||||||
|
context.ProtocolMessage.SetParameter(parameter.Split("=")[0], parameter.Split("=")[1]);
|
||||||
|
}
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -100,6 +114,22 @@ namespace Oqtane.Extensions
|
|||||||
options.Events.OnTicketReceived = OnTicketReceived;
|
options.Events.OnTicketReceived = OnTicketReceived;
|
||||||
options.Events.OnAccessDenied = OnAccessDenied;
|
options.Events.OnAccessDenied = OnAccessDenied;
|
||||||
options.Events.OnRemoteFailure = OnRemoteFailure;
|
options.Events.OnRemoteFailure = OnRemoteFailure;
|
||||||
|
if (sitesettings.GetValue("ExternalLogin:Parameters", "") != "")
|
||||||
|
{
|
||||||
|
options.Events = new OAuthEvents
|
||||||
|
{
|
||||||
|
OnRedirectToAuthorizationEndpoint = context =>
|
||||||
|
{
|
||||||
|
var url = context.RedirectUri;
|
||||||
|
foreach (var parameter in sitesettings.GetValue("ExternalLogin:Parameters", "").Split(","))
|
||||||
|
{
|
||||||
|
url += (!url.Contains("?")) ? "?" + parameter : "&" + parameter;
|
||||||
|
}
|
||||||
|
context.Response.Redirect(url);
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -111,6 +141,7 @@ namespace Oqtane.Extensions
|
|||||||
// OAuth 2.0
|
// OAuth 2.0
|
||||||
var email = "";
|
var email = "";
|
||||||
var id = "";
|
var id = "";
|
||||||
|
var claims = "";
|
||||||
|
|
||||||
if (context.Options.UserInformationEndpoint != "")
|
if (context.Options.UserInformationEndpoint != "")
|
||||||
{
|
{
|
||||||
@ -123,16 +154,16 @@ namespace Oqtane.Extensions
|
|||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
|
||||||
var response = await context.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
|
var response = await context.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
var output = await response.Content.ReadAsStringAsync();
|
claims = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
// parse json output
|
// parse json output
|
||||||
var idClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", "");
|
var idClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", "");
|
||||||
var emailClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", "");
|
var emailClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", "");
|
||||||
if (!output.StartsWith("[") && !output.EndsWith("]"))
|
if (!claims.StartsWith("[") && !claims.EndsWith("]"))
|
||||||
{
|
{
|
||||||
output = "[" + output + "]"; // convert to json array
|
claims = "[" + claims + "]"; // convert to json array
|
||||||
}
|
}
|
||||||
JsonNode items = JsonNode.Parse(output)!;
|
JsonNode items = JsonNode.Parse(claims)!;
|
||||||
foreach (var item in items.AsArray())
|
foreach (var item in items.AsArray())
|
||||||
{
|
{
|
||||||
if (item[emailClaimType] != null)
|
if (item[emailClaimType] != null)
|
||||||
@ -156,12 +187,12 @@ namespace Oqtane.Extensions
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
var _logger = context.HttpContext.RequestServices.GetRequiredService<ILogManager>();
|
var _logger = context.HttpContext.RequestServices.GetRequiredService<ILogManager>();
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "An Error Occurred Accessing The User Info Endpoint - {Error}", ex.Message);
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, ex, "An Error Occurred Accessing The User Info Endpoint - {Error}", ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate user
|
// validate user
|
||||||
var identity = await ValidateUser(email, id, context.HttpContext);
|
var identity = await ValidateUser(email, id, claims, context.HttpContext);
|
||||||
if (identity.Label == ExternalLoginStatus.Success)
|
if (identity.Label == ExternalLoginStatus.Success)
|
||||||
{
|
{
|
||||||
identity.AddClaim(new Claim("access_token", context.AccessToken));
|
identity.AddClaim(new Claim("access_token", context.AccessToken));
|
||||||
@ -193,9 +224,10 @@ namespace Oqtane.Extensions
|
|||||||
var id = context.Principal.FindFirstValue(idClaimType);
|
var id = context.Principal.FindFirstValue(idClaimType);
|
||||||
var emailClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", "");
|
var emailClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", "");
|
||||||
var email = context.Principal.FindFirstValue(emailClaimType);
|
var email = context.Principal.FindFirstValue(emailClaimType);
|
||||||
|
var claims = string.Join(", ", context.Principal.Claims.Select(item => item.Type).ToArray());
|
||||||
|
|
||||||
// validate user
|
// validate user
|
||||||
var identity = await ValidateUser(email, id, context.HttpContext);
|
var identity = await ValidateUser(email, id, claims, context.HttpContext);
|
||||||
if (identity.Label == ExternalLoginStatus.Success)
|
if (identity.Label == ExternalLoginStatus.Success)
|
||||||
{
|
{
|
||||||
identity.AddClaim(new Claim("access_token", context.SecurityToken.RawData));
|
identity.AddClaim(new Claim("access_token", context.SecurityToken.RawData));
|
||||||
@ -229,7 +261,7 @@ namespace Oqtane.Extensions
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<ClaimsIdentity> ValidateUser(string email, string id, HttpContext httpContext)
|
private static async Task<ClaimsIdentity> ValidateUser(string email, string id, string claims, HttpContext httpContext)
|
||||||
{
|
{
|
||||||
var _logger = httpContext.RequestServices.GetRequiredService<ILogManager>();
|
var _logger = httpContext.RequestServices.GetRequiredService<ILogManager>();
|
||||||
ClaimsIdentity identity = new ClaimsIdentity(Constants.AuthenticationScheme);
|
ClaimsIdentity identity = new ClaimsIdentity(Constants.AuthenticationScheme);
|
||||||
@ -241,7 +273,9 @@ namespace Oqtane.Extensions
|
|||||||
var _users = httpContext.RequestServices.GetRequiredService<IUserRepository>();
|
var _users = httpContext.RequestServices.GetRequiredService<IUserRepository>();
|
||||||
User user = null;
|
User user = null;
|
||||||
|
|
||||||
// verify if external user is already registerd for this site
|
if (!string.IsNullOrEmpty(id))
|
||||||
|
{
|
||||||
|
// verify if external user is already registered for this site
|
||||||
var _identityUserManager = httpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
|
var _identityUserManager = httpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
|
||||||
var identityuser = await _identityUserManager.FindByLoginAsync(providerType + ":" + alias.SiteId.ToString(), id);
|
var identityuser = await _identityUserManager.FindByLoginAsync(providerType + ":" + alias.SiteId.ToString(), id);
|
||||||
if (identityuser != null)
|
if (identityuser != null)
|
||||||
@ -359,7 +393,7 @@ namespace Oqtane.Extensions
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email To Uniquely Identify The User.");
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email Address To Uniquely Identify The User. The Email Claim Specified Was {EmailCLaimType} And Actual Claim Types Are {Claims}. Login Denied.", httpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", ""), claims);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -378,6 +412,11 @@ namespace Oqtane.Extensions
|
|||||||
_users.UpdateUser(user);
|
_users.UpdateUser(user);
|
||||||
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName);
|
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else // id invalid
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Identifier To Uniquely Identify The User. The Identifier Claim Specified Was {IdentifierCLaimType} And Actual Claim Types Are {Claims}. Login Denied.", httpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", ""), claims);
|
||||||
|
}
|
||||||
|
|
||||||
return identity;
|
return identity;
|
||||||
}
|
}
|
||||||
|
@ -120,6 +120,10 @@ namespace Oqtane.Infrastructure
|
|||||||
mailMessage.Body += "Subject: " + notification.Subject + "\n\n";
|
mailMessage.Body += "Subject: " + notification.Subject + "\n\n";
|
||||||
mailMessage.Body += notification.Body;
|
mailMessage.Body += notification.Body;
|
||||||
|
|
||||||
|
// set encoding
|
||||||
|
mailMessage.SubjectEncoding = System.Text.Encoding.UTF8;
|
||||||
|
mailMessage.BodyEncoding = System.Text.Encoding.UTF8;
|
||||||
|
|
||||||
// send mail
|
// send mail
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -203,12 +203,15 @@ namespace Oqtane.Infrastructure
|
|||||||
}
|
}
|
||||||
if (Enum.Parse<LogLevel>(log.Level) >= notifylevel)
|
if (Enum.Parse<LogLevel>(log.Level) >= notifylevel)
|
||||||
{
|
{
|
||||||
|
var alias = _tenantManager.GetAlias();
|
||||||
foreach (var userrole in _userRoles.GetUserRoles(log.SiteId.Value))
|
foreach (var userrole in _userRoles.GetUserRoles(log.SiteId.Value))
|
||||||
{
|
{
|
||||||
if (userrole.Role.Name == RoleNames.Host)
|
if (userrole.Role.Name == RoleNames.Host)
|
||||||
{
|
{
|
||||||
var url = _accessor.HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name + "/admin/log";
|
var subject = $"{alias.Name} Site {log.Level} Notification";
|
||||||
var notification = new Notification(log.SiteId.Value, userrole.User, "Site " + log.Level + " Notification", "Please visit " + url + " for more information");
|
var url = $"{_accessor.HttpContext.Request.Scheme}://{alias.Name}/admin/log?id={log.LogId}";
|
||||||
|
string body = $"Log Message: {log.Message}\n\nPlease visit {url} for more information";
|
||||||
|
var notification = new Notification(log.SiteId.Value, userrole.User, subject, body);
|
||||||
_notifications.AddNotification(notification);
|
_notifications.AddNotification(notification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,9 @@ namespace Oqtane.Infrastructure
|
|||||||
case "3.0.1":
|
case "3.0.1":
|
||||||
Upgrade_3_0_1(tenant, scope);
|
Upgrade_3_0_1(tenant, scope);
|
||||||
break;
|
break;
|
||||||
|
case "3.1.3":
|
||||||
|
Upgrade_3_1_3(tenant, scope);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,5 +185,15 @@ namespace Oqtane.Infrastructure
|
|||||||
sites.CreatePages(site, pageTemplates);
|
sites.CreatePages(site, pageTemplates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Upgrade_3_1_3(Tenant tenant, IServiceScope scope)
|
||||||
|
{
|
||||||
|
var roles = scope.ServiceProvider.GetRequiredService<IRoleRepository>();
|
||||||
|
if (!roles.GetRoles(-1, true).ToList().Where(item => item.Name == RoleNames.Unauthenticated).Any())
|
||||||
|
{
|
||||||
|
roles.AddRole(new Role { SiteId = null, Name = RoleNames.Unauthenticated, Description = RoleNames.Unauthenticated, IsAutoAssigned = false, IsSystem = true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ namespace Oqtane.Migrations.EntityBuilders
|
|||||||
|
|
||||||
protected override AspNetUserLoginsEntityBuilder BuildTable(ColumnsBuilder table)
|
protected override AspNetUserLoginsEntityBuilder BuildTable(ColumnsBuilder table)
|
||||||
{
|
{
|
||||||
LoginProvider = AddStringColumn(table, "LoginProvider", 450);
|
LoginProvider = AddStringColumn(table, "LoginProvider", 128);
|
||||||
ProviderKey = AddStringColumn(table, "ProviderKey", 450);
|
ProviderKey = AddStringColumn(table, "ProviderKey", 450);
|
||||||
ProviderDisplayName = AddMaxStringColumn(table, "ProviderDisplayName", true);
|
ProviderDisplayName = AddMaxStringColumn(table, "ProviderDisplayName", true);
|
||||||
UserId = AddStringColumn(table, "UserId", 450);
|
UserId = AddStringColumn(table, "UserId", 450);
|
||||||
|
@ -18,6 +18,7 @@ namespace Oqtane.Migrations.EntityBuilders
|
|||||||
_migrationBuilder = migrationBuilder;
|
_migrationBuilder = migrationBuilder;
|
||||||
ActiveDatabase = database;
|
ActiveDatabase = database;
|
||||||
ForeignKeys = new List<ForeignKey<TEntityBuilder>>();
|
ForeignKeys = new List<ForeignKey<TEntityBuilder>>();
|
||||||
|
Schema = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IDatabase ActiveDatabase { get; }
|
protected IDatabase ActiveDatabase { get; }
|
||||||
@ -30,6 +31,8 @@ namespace Oqtane.Migrations.EntityBuilders
|
|||||||
|
|
||||||
protected List<ForeignKey<TEntityBuilder>> ForeignKeys { get; }
|
protected List<ForeignKey<TEntityBuilder>> ForeignKeys { get; }
|
||||||
|
|
||||||
|
protected string Schema { get; init; }
|
||||||
|
|
||||||
private string RewriteName(string name)
|
private string RewriteName(string name)
|
||||||
{
|
{
|
||||||
return ActiveDatabase.RewriteName(name);
|
return ActiveDatabase.RewriteName(name);
|
||||||
@ -187,9 +190,20 @@ namespace Oqtane.Migrations.EntityBuilders
|
|||||||
return table.Column<decimal>(name: RewriteName(name), nullable: nullable, precision: precision, scale: scale, defaultValue: defaultValue);
|
return table.Column<decimal>(name: RewriteName(name), nullable: nullable, precision: precision, scale: scale, defaultValue: defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AlterStringColumn(string name, int length, bool nullable = false, bool unicode = true)
|
public void AlterStringColumn(string name, int length, bool nullable = false, bool unicode = true, string index = "")
|
||||||
{
|
{
|
||||||
ActiveDatabase.AlterStringColumn(_migrationBuilder, RewriteName(name), RewriteName(EntityTableName), length, nullable, unicode);
|
if (index != "")
|
||||||
|
{
|
||||||
|
// indexes are in the form IndexName:Column1,Column2:Unique
|
||||||
|
var elements = index.Split(':');
|
||||||
|
index = RewriteName(elements[0]) + ":";
|
||||||
|
foreach (var column in elements[1].Split(','))
|
||||||
|
{
|
||||||
|
index += RewriteName(column) + ",";
|
||||||
|
}
|
||||||
|
index = index.Substring(0, index.Length - 1) + ":" + elements[2];
|
||||||
|
}
|
||||||
|
ActiveDatabase.AlterStringColumn(_migrationBuilder, RewriteName(name), RewriteName(EntityTableName), length, nullable, unicode, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DropColumn(string name)
|
public void DropColumn(string name)
|
||||||
@ -308,7 +322,7 @@ namespace Oqtane.Migrations.EntityBuilders
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Create()
|
public void Create()
|
||||||
{
|
{
|
||||||
_migrationBuilder.CreateTable(RewriteName(EntityTableName), BuildTable, null, AddKeys);
|
_migrationBuilder.CreateTable(RewriteName(EntityTableName), BuildTable, Schema, AddKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -17,21 +17,15 @@ namespace Oqtane.Migrations.Tenant
|
|||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
var folderEntityBuilder = new FolderEntityBuilder(migrationBuilder, ActiveDatabase);
|
var folderEntityBuilder = new FolderEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
// Drop the index is needed because the Path is already associated with IX_Folder
|
|
||||||
folderEntityBuilder.DropIndex("IX_Folder");
|
|
||||||
folderEntityBuilder.AlterStringColumn("Name", 256);
|
folderEntityBuilder.AlterStringColumn("Name", 256);
|
||||||
folderEntityBuilder.AlterStringColumn("Path", 512);
|
folderEntityBuilder.AlterStringColumn("Path", 512, false, true, "IX_Folder:SiteId,Path:true");
|
||||||
folderEntityBuilder.AddIndex("IX_Folder", new[] { "SiteId", "Path" }, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
var folderEntityBuilder = new FolderEntityBuilder(migrationBuilder, ActiveDatabase);
|
var folderEntityBuilder = new FolderEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
// Drop the index is needed because the Path is already associated with IX_Folder
|
|
||||||
folderEntityBuilder.DropIndex("IX_Folder");
|
|
||||||
folderEntityBuilder.AlterStringColumn("Path", 50);
|
|
||||||
folderEntityBuilder.AlterStringColumn("Name", 50);
|
folderEntityBuilder.AlterStringColumn("Name", 50);
|
||||||
folderEntityBuilder.AddIndex("IX_Folder", new[] { "SiteId", "Path" }, true);
|
folderEntityBuilder.AlterStringColumn("Path", 50, false, true, "IX_Folder:SiteId,Path:true");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,19 +17,13 @@ namespace Oqtane.Migrations.Tenant
|
|||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
var fileEntityBuilder = new FileEntityBuilder(migrationBuilder, ActiveDatabase);
|
var fileEntityBuilder = new FileEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
// Drop the index is needed because the Name is already associated with IX_File
|
fileEntityBuilder.AlterStringColumn("Name", 256, false, true, "IX_File:FolderId,Name:true");
|
||||||
fileEntityBuilder.DropIndex("IX_File");
|
|
||||||
fileEntityBuilder.AlterStringColumn("Name", 256);
|
|
||||||
fileEntityBuilder.AddIndex("IX_File", new[] { "FolderId", "Name" }, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
var fileEntityBuilder = new FileEntityBuilder(migrationBuilder, ActiveDatabase);
|
var fileEntityBuilder = new FileEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
// Drop the index is needed because the Name is already associated with IX_File
|
fileEntityBuilder.AlterStringColumn("Name", 50, false, true, "IX_File:FolderId,Name:true");
|
||||||
fileEntityBuilder.DropIndex("IX_File");
|
|
||||||
fileEntityBuilder.AlterStringColumn("Name", 50);
|
|
||||||
fileEntityBuilder.AddIndex("IX_File", new[] { "FolderId", "Name" }, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,9 @@ namespace Oqtane.Migrations.Tenant
|
|||||||
visitorEntityBuilder.AlterStringColumn("Url", 2048);
|
visitorEntityBuilder.AlterStringColumn("Url", 2048);
|
||||||
|
|
||||||
var urlMappingEntityBuilder = new UrlMappingEntityBuilder(migrationBuilder, ActiveDatabase);
|
var urlMappingEntityBuilder = new UrlMappingEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
// Drop the index is needed because the Url is already associated with IX_UrlMapping
|
|
||||||
urlMappingEntityBuilder.DropIndex("IX_UrlMapping");
|
|
||||||
urlMappingEntityBuilder.AlterStringColumn("Url", 2048);
|
|
||||||
urlMappingEntityBuilder.AlterStringColumn("MappedUrl", 2048);
|
urlMappingEntityBuilder.AlterStringColumn("MappedUrl", 2048);
|
||||||
urlMappingEntityBuilder.AddIndex("IX_UrlMapping", new[] { "SiteId", "Url" }, true);
|
// Url is an index column and MySQL only supports indexes of 3072 bytes (this index will be 750X4+4=3004 bytes)
|
||||||
|
urlMappingEntityBuilder.AlterStringColumn("Url", 750, false, true, "IX_UrlMapping:SiteId,Url:true");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
@ -33,11 +31,8 @@ namespace Oqtane.Migrations.Tenant
|
|||||||
visitorEntityBuilder.AlterStringColumn("Url", 500);
|
visitorEntityBuilder.AlterStringColumn("Url", 500);
|
||||||
|
|
||||||
var urlMappingEntityBuilder = new UrlMappingEntityBuilder(migrationBuilder, ActiveDatabase);
|
var urlMappingEntityBuilder = new UrlMappingEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
// Drop the index is needed because the Url is already associated with IX_UrlMapping
|
|
||||||
urlMappingEntityBuilder.DropIndex("IX_UrlMapping");
|
|
||||||
urlMappingEntityBuilder.AlterStringColumn("Url", 500);
|
|
||||||
urlMappingEntityBuilder.AlterStringColumn("MappedUrl", 500);
|
urlMappingEntityBuilder.AlterStringColumn("MappedUrl", 500);
|
||||||
urlMappingEntityBuilder.AddIndex("IX_UrlMapping", new[] { "SiteId", "Url" }, true);
|
urlMappingEntityBuilder.AlterStringColumn("Url", 500, false, true, "IX_UrlMapping:SiteId,Url:true");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>3.1.1</Version>
|
<Version>3.1.3</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
@ -96,7 +96,7 @@ namespace Oqtane.Repository
|
|||||||
alias = new Alias();
|
alias = new Alias();
|
||||||
alias.TenantId = aliases.First().TenantId;
|
alias.TenantId = aliases.First().TenantId;
|
||||||
alias.SiteId = aliases.First().SiteId;
|
alias.SiteId = aliases.First().SiteId;
|
||||||
alias.Name = url;
|
alias.Name = segments[0]; // root domain
|
||||||
alias.IsDefault = false;
|
alias.IsDefault = false;
|
||||||
alias = AddAlias(alias);
|
alias = AddAlias(alias);
|
||||||
}
|
}
|
||||||
|
@ -94,16 +94,18 @@ namespace Oqtane.Repository
|
|||||||
List<Role> roles = _roleRepository.GetRoles(site.SiteId, true).ToList();
|
List<Role> roles = _roleRepository.GetRoles(site.SiteId, true).ToList();
|
||||||
if (!roles.Where(item => item.Name == RoleNames.Everyone).Any())
|
if (!roles.Where(item => item.Name == RoleNames.Everyone).Any())
|
||||||
{
|
{
|
||||||
_roleRepository.AddRole(new Role {SiteId = null, Name = RoleNames.Everyone, Description = "All Users", IsAutoAssigned = false, IsSystem = true});
|
_roleRepository.AddRole(new Role {SiteId = null, Name = RoleNames.Everyone, Description = RoleNames.Everyone, IsAutoAssigned = false, IsSystem = true});
|
||||||
|
}
|
||||||
|
if (!roles.Where(item => item.Name == RoleNames.Unauthenticated).Any())
|
||||||
|
{
|
||||||
|
_roleRepository.AddRole(new Role { SiteId = null, Name = RoleNames.Unauthenticated, Description = RoleNames.Unauthenticated, IsAutoAssigned = false, IsSystem = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!roles.Where(item => item.Name == RoleNames.Host).Any())
|
if (!roles.Where(item => item.Name == RoleNames.Host).Any())
|
||||||
{
|
{
|
||||||
_roleRepository.AddRole(new Role {SiteId = null, Name = RoleNames.Host, Description = "Application Administrators", IsAutoAssigned = false, IsSystem = true});
|
_roleRepository.AddRole(new Role {SiteId = null, Name = RoleNames.Host, Description = RoleNames.Host, IsAutoAssigned = false, IsSystem = true});
|
||||||
}
|
}
|
||||||
|
_roleRepository.AddRole(new Role {SiteId = site.SiteId, Name = RoleNames.Registered, Description = RoleNames.Registered, IsAutoAssigned = true, IsSystem = true});
|
||||||
_roleRepository.AddRole(new Role {SiteId = site.SiteId, Name = RoleNames.Registered, Description = "Registered Users", IsAutoAssigned = true, IsSystem = true});
|
_roleRepository.AddRole(new Role {SiteId = site.SiteId, Name = RoleNames.Admin, Description = RoleNames.Admin, IsAutoAssigned = false, IsSystem = true});
|
||||||
_roleRepository.AddRole(new Role {SiteId = site.SiteId, Name = RoleNames.Admin, Description = "Site Administrators", IsAutoAssigned = false, IsSystem = true});
|
|
||||||
|
|
||||||
_profileRepository.AddProfile(new Profile
|
_profileRepository.AddProfile(new Profile
|
||||||
{SiteId = site.SiteId, Name = "FirstName", Title = "First Name", Description = "Your First Or Given Name", Category = "Name", ViewOrder = 1, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false, Options = ""});
|
{SiteId = site.SiteId, Name = "FirstName", Title = "First Name", Description = "Your First Or Given Name", Category = "Name", ViewOrder = 1, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false, Options = ""});
|
||||||
|
@ -62,6 +62,7 @@ namespace Oqtane.Repository
|
|||||||
|
|
||||||
public UrlMapping GetUrlMapping(int siteId, string url)
|
public UrlMapping GetUrlMapping(int siteId, string url)
|
||||||
{
|
{
|
||||||
|
url = (url.Length > 750) ? url.Substring(0, 750) : url;
|
||||||
var urlMapping = _db.UrlMapping.Where(item => item.SiteId == siteId && item.Url == url).FirstOrDefault();
|
var urlMapping = _db.UrlMapping.Where(item => item.SiteId == siteId && item.Url == url).FirstOrDefault();
|
||||||
if (urlMapping == null)
|
if (urlMapping == null)
|
||||||
{
|
{
|
||||||
|
@ -2,19 +2,24 @@ using System;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Antiforgery;
|
using Microsoft.AspNetCore.Antiforgery;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Extensions;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
namespace Oqtane.Security
|
namespace Oqtane.Security
|
||||||
{
|
{
|
||||||
public class AutoValidateAntiforgeryTokenFilter : IAsyncAuthorizationFilter, IAntiforgeryPolicy
|
public class AutoValidateAntiforgeryTokenFilter : IAsyncAuthorizationFilter, IAntiforgeryPolicy
|
||||||
{
|
{
|
||||||
private readonly IAntiforgery _antiforgery;
|
private readonly IAntiforgery _antiforgery;
|
||||||
|
private readonly ILogger<AutoValidateAntiforgeryTokenFilter> _filelogger;
|
||||||
|
|
||||||
public AutoValidateAntiforgeryTokenFilter(IAntiforgery antiforgery)
|
public AutoValidateAntiforgeryTokenFilter(IAntiforgery antiforgery, ILogger<AutoValidateAntiforgeryTokenFilter> filelogger)
|
||||||
{
|
{
|
||||||
_antiforgery = antiforgery;
|
_antiforgery = antiforgery;
|
||||||
|
_filelogger = filelogger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||||
@ -38,6 +43,7 @@ namespace Oqtane.Security
|
|||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
context.Result = new AntiforgeryValidationFailedResult();
|
context.Result = new AntiforgeryValidationFailedResult();
|
||||||
|
_filelogger.LogError(Utilities.LogMessage(this, $"AutoValidateAntiforgeryTokenFilter Failure For {context.HttpContext.Request.GetEncodedUrl()}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -294,7 +294,7 @@ Oqtane.Interop = {
|
|||||||
}
|
}
|
||||||
return files;
|
return files;
|
||||||
},
|
},
|
||||||
uploadFiles: function (posturl, folder, id) {
|
uploadFiles: function (posturl, folder, id, antiforgerytoken) {
|
||||||
var fileinput = document.getElementById(id + 'FileInput');
|
var fileinput = document.getElementById(id + 'FileInput');
|
||||||
var files = fileinput.files;
|
var files = fileinput.files;
|
||||||
var progressinfo = document.getElementById(id + 'ProgressInfo');
|
var progressinfo = document.getElementById(id + 'ProgressInfo');
|
||||||
@ -326,6 +326,7 @@ Oqtane.Interop = {
|
|||||||
var FileName = file.name + ".part_" + PartCount.toString().padStart(3, '0') + "_" + TotalParts.toString().padStart(3, '0');
|
var FileName = file.name + ".part_" + PartCount.toString().padStart(3, '0') + "_" + TotalParts.toString().padStart(3, '0');
|
||||||
|
|
||||||
var data = new FormData();
|
var data = new FormData();
|
||||||
|
data.append('__RequestVerificationToken', antiforgerytoken);
|
||||||
data.append('folder', folder);
|
data.append('folder', folder);
|
||||||
data.append('formfile', Chunk, FileName);
|
data.append('formfile', Chunk, FileName);
|
||||||
var request = new XMLHttpRequest();
|
var request = new XMLHttpRequest();
|
||||||
@ -343,10 +344,17 @@ Oqtane.Interop = {
|
|||||||
progressinfo.innerHTML = file.name + ' 100%';
|
progressinfo.innerHTML = file.name + ' 100%';
|
||||||
progressbar.value = 1;
|
progressbar.value = 1;
|
||||||
};
|
};
|
||||||
|
request.upload.onerror = function () {
|
||||||
|
progressinfo.innerHTML = file.name + ' Error: ' + xhr.status;
|
||||||
|
progressbar.value = 0;
|
||||||
|
};
|
||||||
request.send(data);
|
request.send(data);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (i === files.length - 1) {
|
||||||
fileinput.value = '';
|
fileinput.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
refreshBrowser: function (reload, wait) {
|
refreshBrowser: function (reload, wait) {
|
||||||
setInterval(function () {
|
setInterval(function () {
|
||||||
|
@ -60,6 +60,7 @@ namespace Oqtane.Models
|
|||||||
public Dictionary<string, string> Settings { get; set; }
|
public Dictionary<string, string> Settings { get; set; }
|
||||||
|
|
||||||
#region PageModule properties
|
#region PageModule properties
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int PageModuleId { get; set; }
|
public int PageModuleId { get; set; }
|
||||||
|
|
||||||
@ -68,6 +69,7 @@ namespace Oqtane.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int PageId { get; set; }
|
public int PageId { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
|
|
||||||
@ -76,8 +78,10 @@ namespace Oqtane.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public string Pane { get; set; }
|
public string Pane { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int Order { get; set; }
|
public int Order { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public string ContainerType { get; set; }
|
public string ContainerType { get; set; }
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>3.1.1</Version>
|
<Version>3.1.3</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
@ -104,11 +104,14 @@ namespace Oqtane.Security
|
|||||||
|
|
||||||
private static bool IsAllowed(int userId, string roles, string permission)
|
private static bool IsAllowed(int userId, string roles, string permission)
|
||||||
{
|
{
|
||||||
|
if (permission == RoleNames.Unauthenticated)
|
||||||
|
{
|
||||||
|
return userId == -1;
|
||||||
|
}
|
||||||
if ("[" + userId + "]" == permission)
|
if ("[" + userId + "]" == permission)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (roles != null)
|
if (roles != null)
|
||||||
{
|
{
|
||||||
return roles.IndexOf(";" + permission + ";") != -1;
|
return roles.IndexOf(";" + permission + ";") != -1;
|
||||||
|
@ -4,8 +4,8 @@ namespace Oqtane.Shared
|
|||||||
{
|
{
|
||||||
public class Constants
|
public class Constants
|
||||||
{
|
{
|
||||||
public static readonly string Version = "3.1.1";
|
public static readonly string Version = "3.1.3";
|
||||||
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1";
|
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3";
|
||||||
public const string PackageId = "Oqtane.Framework";
|
public const string PackageId = "Oqtane.Framework";
|
||||||
public const string UpdaterPackageId = "Oqtane.Updater";
|
public const string UpdaterPackageId = "Oqtane.Updater";
|
||||||
public const string PackageRegistryUrl = "https://www.oqtane.net";
|
public const string PackageRegistryUrl = "https://www.oqtane.net";
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
namespace Oqtane.Shared {
|
namespace Oqtane.Shared {
|
||||||
public class RoleNames {
|
public class RoleNames {
|
||||||
public const string Everyone = "All Users";
|
public const string Everyone = "All Users";
|
||||||
public const string Host = "Host Users";
|
public const string Host = "Host Users";
|
||||||
public const string Admin = "Administrators";
|
public const string Admin = "Administrators";
|
||||||
public const string Registered = "Registered Users";
|
public const string Registered = "Registered Users";
|
||||||
|
public const string Unauthenticated = "Unauthenticated Users";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>3.1.1</Version>
|
<Version>3.1.3</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Version>3.1.1</Version>
|
<Version>3.1.3</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
13
README.md
13
README.md
@ -57,8 +57,17 @@ There is a separate [Documentation repository](https://github.com/oqtane/oqtane.
|
|||||||
# Roadmap
|
# Roadmap
|
||||||
This project is open source, and therefore is a work in progress...
|
This project is open source, and therefore is a work in progress...
|
||||||
|
|
||||||
Backlog (Not Yet Assigned)
|
V.4.0.0 ( Q4 2022 )
|
||||||
- [ ] Allow language specification in Url (#1731)
|
- [ ] MAUI / Blazor Hybrid support
|
||||||
|
|
||||||
|
V.3.1.3 ( June 2022 )
|
||||||
|
- [ ] Stabilization improvements
|
||||||
|
|
||||||
|
V.3.1.2 ( May 14, 2022 )
|
||||||
|
- [x] Stabilization improvements
|
||||||
|
|
||||||
|
V.3.1.1 ( May 3, 2022 )
|
||||||
|
- [x] Stabilization improvements
|
||||||
|
|
||||||
V.3.1.0 ( April 5, 2022 )
|
V.3.1.0 ( April 5, 2022 )
|
||||||
- [x] User account lockout support
|
- [x] User account lockout support
|
||||||
|
Reference in New Issue
Block a user