Merge pull request #2257 from oqtane/dev

3.1.3 release
This commit is contained in:
Shaun Walker 2022-06-27 16:12:05 -04:00 committed by GitHub
commit a703df40c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 1304 additions and 956 deletions

View File

@ -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);
}
} }
} }

View File

@ -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"];
} }
} }
} }

View File

@ -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"]&nbsp;&nbsp;|&nbsp;&nbsp; <strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp; @SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong> @SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? "&nbsp;&nbsp;|&nbsp;&nbsp;" + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
@((MarkupString)(context.TrialPeriod > 0 ? "&nbsp;&nbsp;|&nbsp;&nbsp;<strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : "")) @((MarkupString)(context.TrialPeriod > 0 ? "&nbsp;&nbsp;|&nbsp;&nbsp;<strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
</td> </td>
<td style="width: 1px; vertical-align: middle;"> <td style="width: 1px; vertical-align: middle;">

View File

@ -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"];

View File

@ -35,6 +35,7 @@
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp; <strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp; @SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong> @SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? "&nbsp;&nbsp;|&nbsp;&nbsp;" + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
@((MarkupString)(context.TrialPeriod > 0 ? "&nbsp;&nbsp;|&nbsp;&nbsp;<strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : "")) @((MarkupString)(context.TrialPeriod > 0 ? "&nbsp;&nbsp;|&nbsp;&nbsp;<strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
</td> </td>
<td style="width: 1px; vertical-align: middle;"> <td style="width: 1px; vertical-align: middle;">

View File

@ -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)"&nbsp;") @((MarkupString)"&nbsp;")
<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;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</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();
}
} }

View File

@ -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);

View File

@ -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
{ {

View File

@ -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;">&nbsp;</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;">&nbsp;</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();
}
} }

View File

@ -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();
} }

View File

@ -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;

View File

@ -35,6 +35,7 @@
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp; <strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp; @SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong> @SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? "&nbsp;&nbsp;|&nbsp;&nbsp;" + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
@((MarkupString)(context.TrialPeriod > 0 ? "&nbsp;&nbsp;|&nbsp;&nbsp;<strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : "")) @((MarkupString)(context.TrialPeriod > 0 ? "&nbsp;&nbsp;|&nbsp;&nbsp;<strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
</td> </td>
<td style="width: 1px; vertical-align: middle;"> <td style="width: 1px; vertical-align: middle;">

View File

@ -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)

View File

@ -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;

View File

@ -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;
} }
} }
} }
} }
}
}

View File

@ -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))

View File

@ -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))
{ {

View File

@ -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()

View File

@ -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.2</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.2</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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View File

@ -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>

View File

@ -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>
} }

View File

@ -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>
} }

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Version>3.1.2</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.2</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>

View File

@ -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.2</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.2</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>

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Version>3.1.2</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.2</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>

View File

@ -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.2</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.2</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>

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Version>3.1.2</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.2</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>

View File

@ -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.2</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.2</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>

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Version>3.1.2</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.2</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>

View File

@ -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.2</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.2</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>

View File

@ -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.2</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.2</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>

View File

@ -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.2</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.2/Oqtane.Framework.3.1.2.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.2</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>

View File

@ -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.2</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.2</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>

View File

@ -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.2</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.2</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>

View File

@ -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.2</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.2</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>

View File

@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.2.Install.zip" -Force Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.3.Install.zip" -Force

View File

@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.2.Upgrade.zip" -Force Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.3.Upgrade.zip" -Force

View File

@ -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 = "";
} }

View File

@ -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
{ {

View File

@ -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;

View File

@ -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;

View File

@ -187,7 +187,7 @@ 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);
} }
} }

View File

@ -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
{ {

View File

@ -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);
} }
} }

View File

@ -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 });
}
}
} }
} }

View File

@ -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);
@ -319,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>

View File

@ -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.2</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.2</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>

View File

@ -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 = ""});

View File

@ -344,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 () {

View File

@ -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; }

View File

@ -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.2</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.2</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>

View File

@ -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;

View File

@ -4,8 +4,8 @@ namespace Oqtane.Shared
{ {
public class Constants public class Constants
{ {
public static readonly string Version = "3.1.2"; 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,3.1.2"; 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";

View File

@ -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";
} }
} }

View File

@ -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.2</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.2</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>

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<Version>3.1.2</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.2</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>

View File

@ -57,8 +57,14 @@ 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 ) V.3.1.1 ( May 3, 2022 )
- [x] Stabilization improvements - [x] Stabilization improvements