diff --git a/Oqtane.Client/Extensions/OqtaneLocalizationExtensions.cs b/Oqtane.Client/Extensions/OqtaneLocalizationExtensions.cs index 8bb5372a..5a800c7b 100644 --- a/Oqtane.Client/Extensions/OqtaneLocalizationExtensions.cs +++ b/Oqtane.Client/Extensions/OqtaneLocalizationExtensions.cs @@ -1,3 +1,5 @@ +using System; + namespace Microsoft.Extensions.Localization { public static class OqtaneLocalizationExtensions @@ -18,5 +20,42 @@ namespace Microsoft.Extensions.Localization } return localizedValue; } + + /// + /// 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. + /// + /// + /// the full type name ie. GetType().FullName + /// + 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); + } } } diff --git a/Oqtane.Client/Installer/Installer.razor b/Oqtane.Client/Installer/Installer.razor index 5630b592..48dc9f83 100644 --- a/Oqtane.Client/Installer/Installer.razor +++ b/Oqtane.Client/Installer/Installer.razor @@ -29,14 +29,7 @@ } @@ -63,8 +56,8 @@
- - + +
@@ -72,8 +65,8 @@
- - + +
@@ -110,8 +103,10 @@ private string _hostUsername = string.Empty; private string _hostPassword = string.Empty; - private string _passwordtype = "password"; - private string _togglepassword = string.Empty; + private string _passwordType = "password"; + private string _confirmPasswordType = "password"; + private string _togglePassword = string.Empty; + private string _toggleConfirmPassword = string.Empty; private string _confirmPassword = string.Empty; private string _hostEmail = string.Empty; private bool _register = true; @@ -120,7 +115,9 @@ protected override async Task OnInitializedAsync() { - _togglepassword = SharedLocalizer["ShowPassword"]; + _togglePassword = SharedLocalizer["ShowPassword"]; + _toggleConfirmPassword = SharedLocalizer["ShowPassword"]; + _databases = await DatabaseService.GetDatabasesAsync(); if (_databases.Exists(item => item.IsDefault)) { @@ -230,15 +227,29 @@ private void TogglePassword() { - if (_passwordtype == "password") + if (_passwordType == "password") { - _passwordtype = "text"; - _togglepassword = SharedLocalizer["HidePassword"]; + _passwordType = "text"; + _togglePassword = SharedLocalizer["HidePassword"]; } else { - _passwordtype = "password"; - _togglepassword = SharedLocalizer["ShowPassword"]; + _passwordType = "password"; + _togglePassword = SharedLocalizer["ShowPassword"]; + } + } + + private void ToggleConfirmPassword() + { + if (_confirmPasswordType == "password") + { + _confirmPasswordType = "text"; + _toggleConfirmPassword = SharedLocalizer["HidePassword"]; + } + else + { + _confirmPasswordType = "password"; + _toggleConfirmPassword = SharedLocalizer["ShowPassword"]; } } } diff --git a/Oqtane.Client/Modules/Admin/Languages/Add.razor b/Oqtane.Client/Modules/Admin/Languages/Add.razor index 80119785..b5679740 100644 --- a/Oqtane.Client/Modules/Admin/Languages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Languages/Add.razor @@ -20,6 +20,7 @@ else @if (_availableCultures.Count() == 0) { + @SharedLocalizer["Cancel"] } else { @@ -47,9 +48,9 @@ else + @SharedLocalizer["Cancel"] } - @SharedLocalizer["Cancel"]
@@ -78,6 +79,7 @@ else @(String.Format("{0:n0}", context.Downloads)) @SharedLocalizer["Search.Downloads"]  |   @SharedLocalizer["Search.Released"]: @context.ReleaseDate.ToString("MMM dd, yyyy")  |   @SharedLocalizer["Search.Version"]: @context.Version + @((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? "  |  " + SharedLocalizer["Search.Source"] + ": " + new Uri(context.PackageUrl).Host + "" : "")) @((MarkupString)(context.TrialPeriod > 0 ? "  |  " + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "" : "")) diff --git a/Oqtane.Client/Modules/Admin/Logs/Index.razor b/Oqtane.Client/Modules/Admin/Logs/Index.razor index 5bab1f8c..d2af52a0 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Index.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Admin.Logs @inherits ModuleBase +@inject NavigationManager NavigationManager @inject ILogService LogService @inject ISettingService SettingService @inject IStringLocalizer Localizer @@ -91,19 +92,23 @@ else } @code { - private string _level = "-"; - private string _function = "-"; - private string _rows = "10"; + private string _level = "-"; + private string _function = "-"; + private string _rows = "10"; private int _page = 1; - private List _logs; - private string _retention = ""; + private List _logs; + private string _retention = ""; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; - protected override async Task OnInitializedAsync() - { - try - { + protected override async Task OnInitializedAsync() + { + try + { + if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id)) + { + NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"id={id}")); + } if (PageState.QueryString.ContainsKey("level")) { _level = PageState.QueryString["level"]; diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index 7825d0e2..68af3ec7 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -35,6 +35,7 @@ @(String.Format("{0:n0}", context.Downloads)) @SharedLocalizer["Search.Downloads"]  |   @SharedLocalizer["Search.Released"]: @context.ReleaseDate.ToString("MMM dd, yyyy")  |   @SharedLocalizer["Search.Version"]: @context.Version + @((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? "  |  " + SharedLocalizer["Search.Source"] + ": " + new Uri(context.PackageUrl).Host + "" : "")) @((MarkupString)(context.TrialPeriod > 0 ? "  |  " + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "" : "")) @@ -114,145 +115,145 @@ @SharedLocalizer["Cancel"] @code { - private List _packages; - private string _price = "free"; - private string _search = ""; - private string _productname = ""; - private string _license = ""; - private string _packageid = ""; - private string _version = ""; + private List _packages; + private string _price = "free"; + private string _search = ""; + private string _productname = ""; + private string _license = ""; + private string _packageid = ""; + private string _version = ""; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; - protected override async Task OnInitializedAsync() - { - try - { - await LoadModuleDefinitions(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Packages {Error}", ex.Message); - AddModuleMessage(Localizer["Error.Package.Load"], MessageType.Error); - } - } + protected override async Task OnInitializedAsync() + { + try + { + await LoadModuleDefinitions(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Packages {Error}", ex.Message); + AddModuleMessage(Localizer["Error.Package.Load"], MessageType.Error); + } + } - private async Task LoadModuleDefinitions() - { - var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); - _packages = await PackageService.GetPackagesAsync("module", _search, _price, ""); + private async Task LoadModuleDefinitions() + { + var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); + _packages = await PackageService.GetPackagesAsync("module", _search, _price, ""); - if (_packages != null) - { - foreach (Package package in _packages.ToArray()) - { - if (moduledefinitions.Exists(item => item.PackageName == package.PackageId)) - { - _packages.Remove(package); - } - } - } - } + if (_packages != null) + { + foreach (Package package in _packages.ToArray()) + { + if (moduledefinitions.Exists(item => item.PackageName == package.PackageId)) + { + _packages.Remove(package); + } + } + } + } - private async void PriceChanged(ChangeEventArgs e) - { - try - { - _price = (string)e.Value; - _search = ""; - await LoadModuleDefinitions(); - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error On PriceChanged"); - } - } + private async void PriceChanged(ChangeEventArgs e) + { + try + { + _price = (string)e.Value; + _search = ""; + await LoadModuleDefinitions(); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error On PriceChanged"); + } + } - private async Task Search() - { - try - { - await LoadModuleDefinitions(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error On Search"); - } - } + private async Task Search() + { + try + { + await LoadModuleDefinitions(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error On Search"); + } + } - private async Task Reset() - { - try - { - _search = ""; - await LoadModuleDefinitions(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error On Reset"); - } - } + private async Task Reset() + { + try + { + _search = ""; + await LoadModuleDefinitions(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error On Reset"); + } + } - private void HideModal() - { - _productname = ""; - _license = ""; - StateHasChanged(); - } + private void HideModal() + { + _productname = ""; + _license = ""; + StateHasChanged(); + } - private async Task GetPackage(string packageid, string version) - { - try - { - var package = await PackageService.GetPackageAsync(packageid, version); - if (package != null) - { - _productname = package.Name; - if (!string.IsNullOrEmpty(package.License)) - { - _license = package.License.Replace("\n", "
"); - } - _packageid = package.PackageId; - _version = package.Version; - } - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version); - AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error); - } - } + private async Task GetPackage(string packageid, string version) + { + try + { + var package = await PackageService.GetPackageAsync(packageid, version); + if (package != null) + { + _productname = package.Name; + if (!string.IsNullOrEmpty(package.License)) + { + _license = package.License.Replace("\n", "
"); + } + _packageid = package.PackageId; + _version = package.Version; + } + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version); + AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error); + } + } - private async Task DownloadPackage() - { - try - { - await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder); - await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version); - AddModuleMessage(Localizer["Success.Module.Download"], MessageType.Success); - _productname = ""; - _license = ""; - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packageid, _version); - AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error); - } - } + private async Task DownloadPackage() + { + try + { + await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder); + await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version); + AddModuleMessage(Localizer["Success.Module.Download"], MessageType.Success); + _productname = ""; + _license = ""; + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packageid, _version); + AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error); + } + } - private async Task InstallModules() - { - try - { - await ModuleDefinitionService.InstallModuleDefinitionsAsync(); - AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Installing Module"); - } - } + private async Task InstallModules() + { + try + { + await ModuleDefinitionService.InstallModuleDefinitionsAsync(); + AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Installing Module"); + } + } } diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor index 049582c6..3cfbcb7e 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor @@ -12,11 +12,32 @@ } else { - - @((MarkupString)" ") - - - +
+
+
+ + @((MarkupString)" ") + +
+
+ +
+
+
+ +
    @@ -30,9 +51,9 @@ else @if (context.AssemblyName != "Oqtane.Client") - { + { - } + } @context.Name @context.Version @@ -65,6 +86,8 @@ else @code { private List _moduleDefinitions; private List _packages; + private List _categories = new List(); + private string _category = "Common"; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; @@ -74,6 +97,7 @@ else { _moduleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); _packages = await PackageService.GetPackagesAsync("module"); + _categories = _moduleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList(); } catch (Exception ex) { @@ -115,38 +139,44 @@ else { return package.Version; } - } - return version; - } + } + return version; + } - private async Task DownloadModule(string packagename, string version) - { - try - { - await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder); - await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version); - await ModuleDefinitionService.InstallModuleDefinitionsAsync(); - AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", packagename, version, ex.Message); - AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error); - } - } + private async Task DownloadModule(string packagename, string version) + { + try + { + await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder); + await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version); + await ModuleDefinitionService.InstallModuleDefinitionsAsync(); + AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", packagename, version, ex.Message); + AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error); + } + } - private async Task DeleteModule(ModuleDefinition moduleDefinition) - { - try - { - await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId); - AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success); - NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true)); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message); - AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error); - } - } + private async Task DeleteModule(ModuleDefinition moduleDefinition) + { + try + { + await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId); + AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success); + NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true)); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message); + AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error); + } + } + + private void CategoryChanged(ChangeEventArgs e) + { + _category = (string)e.Value; + StateHasChanged(); + } } diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index e86ba978..47f4cd45 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -90,116 +90,117 @@ @code { - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; - public override string Title => "Module Settings"; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; + public override string Title => "Module Settings"; - private ElementReference form; - private bool validated = false; - private List _themes; - private List _containers = new List(); - private string _title; - private string _containerType; - private string _allPages = "false"; - private string _permissionNames = ""; - private string _permissions = null; - private string _pageId; - private PermissionGrid _permissionGrid; - private Type _moduleSettingsType; - private object _moduleSettings; - private string _moduleSettingsTitle = "Module Settings"; - private RenderFragment ModuleSettingsComponent { get; set; } - private Type _containerSettingsType; - private object _containerSettings; - private RenderFragment ContainerSettingsComponent { get; set; } - private string createdby; - private DateTime createdon; - private string modifiedby; - private DateTime modifiedon; + private ElementReference form; + private bool validated = false; + private List _themes; + private List _containers = new List(); + private string _title; + private string _containerType; + private string _allPages = "false"; + private string _permissionNames = ""; + private string _permissions = null; + private string _pageId; + private PermissionGrid _permissionGrid; + private Type _moduleSettingsType; + private object _moduleSettings; + private string _moduleSettingsTitle = "Module Settings"; + private RenderFragment ModuleSettingsComponent { get; set; } + private Type _containerSettingsType; + private object _containerSettings; + private RenderFragment ContainerSettingsComponent { get; set; } + private string createdby; + private DateTime createdon; + private string modifiedby; + private DateTime modifiedon; - protected override async Task OnInitializedAsync() - { - _title = ModuleState.Title; - _themes = await ThemeService.GetThemesAsync(); - _containers = ThemeService.GetContainerControls(_themes, PageState.Page.ThemeType); - _containerType = ModuleState.ContainerType; - _allPages = ModuleState.AllPages.ToString(); - _permissions = ModuleState.Permissions; - _permissionNames = ModuleState.ModuleDefinition.PermissionNames; - _pageId = ModuleState.PageId.ToString(); - createdby = ModuleState.CreatedBy; - createdon = ModuleState.CreatedOn; - modifiedby = ModuleState.ModifiedBy; - modifiedon = ModuleState.ModifiedOn; + protected override async Task OnInitializedAsync() + { + _title = ModuleState.Title; + _themes = await ThemeService.GetThemesAsync(); + _containers = ThemeService.GetContainerControls(_themes, PageState.Page.ThemeType); + _containerType = ModuleState.ContainerType; + _allPages = ModuleState.AllPages.ToString(); + _permissions = ModuleState.Permissions; + _permissionNames = ModuleState.ModuleDefinition.PermissionNames; + _pageId = ModuleState.PageId.ToString(); + createdby = ModuleState.CreatedBy; + createdon = ModuleState.CreatedOn; + modifiedby = ModuleState.ModifiedBy; + modifiedon = ModuleState.ModifiedOn; - if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType)) - { - // module settings type explicitly declared in IModule interface - _moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType); - } - else - { - // legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module ) - _moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true); - } - if (_moduleSettingsType != null) - { - var moduleobject = Activator.CreateInstance(_moduleSettingsType) as IModuleControl; - if (!string.IsNullOrEmpty(moduleobject.Title)) - { - _moduleSettingsTitle = moduleobject.Title; - } + if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType)) + { + // module settings type explicitly declared in IModule interface + _moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType); + } + else + { + // legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module ) + _moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true); + } + if (_moduleSettingsType != null) + { + var moduleobject = Activator.CreateInstance(_moduleSettingsType) as IModuleControl; + if (!string.IsNullOrEmpty(moduleobject.Title)) + { + _moduleSettingsTitle = moduleobject.Title; + } - ModuleSettingsComponent = builder => - { - builder.OpenComponent(0, _moduleSettingsType); - builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); }); - builder.CloseComponent(); - }; - } + ModuleSettingsComponent = builder => + { + builder.OpenComponent(0, _moduleSettingsType); + builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); }); + builder.CloseComponent(); + }; + } - var theme = _themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType))); - if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType)) - { - _containerSettingsType = Type.GetType(theme.ContainerSettingsType); - if (_containerSettingsType != null) - { - ContainerSettingsComponent = builder => - { - builder.OpenComponent(0, _containerSettingsType); - builder.AddComponentReferenceCapture(1, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); }); - builder.CloseComponent(); - }; - } - } - } + var theme = _themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType))); + if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType)) + { + _containerSettingsType = Type.GetType(theme.ContainerSettingsType); + if (_containerSettingsType != null) + { + ContainerSettingsComponent = builder => + { + builder.OpenComponent(0, _containerSettingsType); + builder.AddComponentReferenceCapture(1, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); }); + builder.CloseComponent(); + }; + } + } + } - private async Task SaveModule() - { - validated = true; - var interop = new Interop(JSRuntime); - if (await interop.FormValid(form)) - { - if (!string.IsNullOrEmpty(_title)) - { - var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); - pagemodule.PageId = int.Parse(_pageId); - pagemodule.Title = _title; - pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty; - if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType) - { - pagemodule.ContainerType = string.Empty; - } - if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType) - { - pagemodule.ContainerType = string.Empty; - } - await PageModuleService.UpdatePageModuleAsync(pagemodule); - await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); + private async Task SaveModule() + { + validated = true; + var interop = new Interop(JSRuntime); + if (await interop.FormValid(form)) + { + if (!string.IsNullOrEmpty(_title)) + { + var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); + pagemodule.PageId = int.Parse(_pageId); + pagemodule.Title = _title; + pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty; + if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType) + { + pagemodule.ContainerType = string.Empty; + } + if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType) + { + pagemodule.ContainerType = string.Empty; + } + await PageModuleService.UpdatePageModuleAsync(pagemodule); + await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); - var module = ModuleState; - module.AllPages = bool.Parse(_allPages); - module.Permissions = _permissionGrid.GetPermissions(); - await ModuleService.UpdateModuleAsync(module); + var module = ModuleState; + module.AllPages = bool.Parse(_allPages); + module.PageModuleId = ModuleState.PageModuleId; + module.Permissions = _permissionGrid.GetPermissions(); + await ModuleService.UpdateModuleAsync(module); if (_moduleSettingsType != null) { diff --git a/Oqtane.Client/Modules/Admin/Roles/Index.razor b/Oqtane.Client/Modules/Admin/Roles/Index.razor index 40bc1a86..2bb4ac87 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Index.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Index.razor @@ -59,7 +59,7 @@ else if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) { _roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true); - _roles = _roles.Where(item => item.Name != RoleNames.Everyone).ToList(); + _roles.RemoveAll(item => item.Name == RoleNames.Everyone || item.Name == RoleNames.Unauthenticated); } else { diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index 3d0ce023..77593fcb 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -170,24 +170,54 @@ @if (_aliases != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) {
-
-
- -
- +
+
+ +
+ + +
+   +   + @Localizer["AliasName"] + @Localizer["AliasDefault"] +
+ + @if (context.AliasId != _aliasid) + { + + @if (_aliasid == -1) + { + + } + + + @if (_aliasid == -1) + { + + } + + @context.Name + @context.IsDefault + } + else + { + + + + + + + + + } + +
+
-
-
- -
- -
-
@@ -253,8 +283,9 @@ private List _containers = new List(); private string _name = string.Empty; private List _aliases; - private string _defaultalias = string.Empty; - private string _urls = string.Empty; + private int _aliasid = -1; + private string _aliasname; + private string _defaultalias; private string _runtime = ""; private string _prerender = ""; private int _logofileid = -1; @@ -304,10 +335,7 @@ _prerender = site.RenderMode.Replace(_runtime, ""); _isdeleted = site.IsDeleted.ToString(); - if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) - { - await GetAliases(); - } + await GetAliases(); if (site.LogoFileId != null) { @@ -409,152 +437,92 @@ { if (_name != string.Empty && _themetype != "-" && _containertype != "-") { - var unique = true; - if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) + var site = await SiteService.GetSiteAsync(PageState.Site.SiteId); + if (site != null) { - _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()) + bool refresh = false; + bool reload = false; + + site.Name = _name; + if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) { - var alias = aliases.Where(item => item.Name == name).FirstOrDefault(); - if (alias != null && unique) + if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender) { - unique = (alias.TenantId == PageState.Site.TenantId && alias.SiteId == PageState.Site.SiteId); + site.Runtime = _runtime; + site.RenderMode = _runtime + _prerender; + reload = true; // needs to be reloaded on server } } - if (unique && string.IsNullOrEmpty(_defaultalias)) unique = false; - } + site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted)); - if (unique) - { - var site = await SiteService.GetSiteAsync(PageState.Site.SiteId); - if (site != null) + site.LogoFileId = null; + var logofileid = _logofilemanager.GetFileId(); + if (logofileid != -1) { - bool refresh = false; - bool reload = false; - - site.Name = _name; - if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) - { - if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender) - { - site.Runtime = _runtime; - site.RenderMode = _runtime + _prerender; - reload = true; // needs to be reloaded on server - } - } - site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted)); - - site.LogoFileId = null; - var logofileid = _logofilemanager.GetFileId(); - if (logofileid != -1) - { - site.LogoFileId = logofileid; - } - int? faviconFieldId = _faviconfilemanager.GetFileId(); - if (faviconFieldId == -1) faviconFieldId = null; - if (site.FaviconFileId != faviconFieldId) - { - site.FaviconFileId = faviconFieldId; - reload = true; // needs to be reloaded on server - } - if (site.DefaultThemeType != _themetype) - { - site.DefaultThemeType = _themetype; - refresh = true; // needs to be refreshed on client - } - if (site.DefaultContainerType != _containertype) - { - site.DefaultContainerType = _containertype; - refresh = true; // needs to be refreshed on client - } - site.AdminContainerType = _admincontainertype; - - if (site.PwaIsEnabled.ToString() != _pwaisenabled) - { - site.PwaIsEnabled = Boolean.Parse(_pwaisenabled); - reload = true; // needs to be reloaded on server - } - int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId(); - if (pwaappiconfileid == -1) pwaappiconfileid = null; - if (site.PwaAppIconFileId != pwaappiconfileid) - { - site.PwaAppIconFileId = pwaappiconfileid; - reload = true; // needs to be reloaded on server - } - int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId(); - if (pwasplashiconfileid == -1) pwasplashiconfileid = null; - if (site.PwaSplashIconFileId != pwasplashiconfileid) - { - site.PwaSplashIconFileId = pwasplashiconfileid; - reload = true; // needs to be reloaded on server - } - - site = await SiteService.UpdateSiteAsync(site); - - var settings = await SettingService.GetSiteSettingsAsync(site.SiteId); - settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true); - settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true); - settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true); - settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true); - settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true); - settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true); - settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true); - await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId); - - if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) - { - var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(sValue => sValue.Trim()).ToArray(); - foreach (Alias alias in _aliases) - { - if (!names.Contains(alias.Name.Trim())) - { - await AliasService.DeleteAliasAsync(alias.AliasId); - } - } - - foreach (string name in names) - { - var alias = _aliases.Find(item => item.Name.Trim() == name); - if (alias == null) - { - alias = new Alias(); - alias.Name = name; - alias.TenantId = site.TenantId; - alias.SiteId = site.SiteId; - alias.IsDefault = (name == _defaultalias); - await AliasService.AddAliasAsync(alias); - } - else - { - if (alias.Name != name || alias.IsDefault != (alias.Name.Trim() == _defaultalias)) - { - alias.Name = name; - alias.IsDefault = (name == _defaultalias); - await AliasService.UpdateAliasAsync(alias); - } - } - } - await GetAliases(); - } - - await logger.LogInformation("Site Settings Saved {Site}", site); - - if (refresh || reload) - { - NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload - } - else - { - AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success); - await interop.ScrollTo(0, 0, "smooth"); - } + site.LogoFileId = logofileid; + } + int? faviconFieldId = _faviconfilemanager.GetFileId(); + if (faviconFieldId == -1) faviconFieldId = null; + if (site.FaviconFileId != faviconFieldId) + { + site.FaviconFileId = faviconFieldId; + reload = true; // needs to be reloaded on server + } + if (site.DefaultThemeType != _themetype) + { + site.DefaultThemeType = _themetype; + refresh = true; // needs to be refreshed on client + } + if (site.DefaultContainerType != _containertype) + { + site.DefaultContainerType = _containertype; + refresh = true; // needs to be refreshed on client + } + site.AdminContainerType = _admincontainertype; + + if (site.PwaIsEnabled.ToString() != _pwaisenabled) + { + site.PwaIsEnabled = Boolean.Parse(_pwaisenabled); + reload = true; // needs to be reloaded on server + } + int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId(); + if (pwaappiconfileid == -1) pwaappiconfileid = null; + if (site.PwaAppIconFileId != pwaappiconfileid) + { + site.PwaAppIconFileId = pwaappiconfileid; + reload = true; // needs to be reloaded on server + } + int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId(); + if (pwasplashiconfileid == -1) pwasplashiconfileid = null; + if (site.PwaSplashIconFileId != pwasplashiconfileid) + { + site.PwaSplashIconFileId = pwasplashiconfileid; + reload = true; // needs to be reloaded on server + } + + site = await SiteService.UpdateSiteAsync(site); + + var settings = await SettingService.GetSiteSettingsAsync(site.SiteId); + settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true); + settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true); + settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true); + settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true); + settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true); + settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true); + settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true); + await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId); + + await logger.LogInformation("Site Settings Saved {Site}", site); + + if (refresh || reload) + { + NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload + } + else + { + AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success); + await interop.ScrollTo(0, 0, "smooth"); } - } - else // deuplicate alias or default alias not specified - { - AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning); } } else @@ -578,19 +546,19 @@ { try { - var sites = await SiteService.GetSitesAsync(); - if (sites.Count > 1) + var aliases = await AliasService.GetAliasesAsync(); + if (aliases.Any(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId)) { await SiteService.DeleteSiteAsync(PageState.Site.SiteId); await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId); - var aliases = await AliasService.GetAliasesAsync(); - foreach (Alias a in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId)) + foreach (Alias alias in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId)) { - await AliasService.DeleteAliasAsync(a.AliasId); + await AliasService.DeleteAliasAsync(alias.AliasId); } - NavigationManager.NavigateTo(NavigateUrl("admin/sites")); + aliases = await AliasService.GetAliasesAsync(); + NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + aliases.First().Name, true); } else { @@ -637,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() { if (_smtppasswordtype == "password") @@ -664,4 +618,84 @@ _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(); + } } diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 1c75b701..fe83406a 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -131,14 +131,7 @@ else
@@ -172,12 +165,11 @@ else private List _databases; private ElementReference form; private bool validated = false; - private string _databaseName = "LocalDB"; + private string _databaseName; private Type _databaseConfigType; private object _databaseConfig; private RenderFragment DatabaseConfigComponent { get; set; } - private List _themeList; private List _themes = new List(); private List _containers = new List(); @@ -208,7 +200,16 @@ else _themeList = await ThemeService.GetThemesAsync(); _themes = ThemeService.GetThemeControls(_themeList); _siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync(); + _databases = await DatabaseService.GetDatabasesAsync(); + if (_databases.Exists(item => item.IsDefault)) + { + _databaseName = _databases.Find(item => item.IsDefault).Name; + } + else + { + _databaseName = "LocalDB"; + } LoadDatabaseConfigComponent(); } diff --git a/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor b/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor index 77878a1b..7b3a149f 100644 --- a/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor +++ b/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor @@ -142,6 +142,7 @@
+

@code { public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor index 4d80e645..b8717647 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Add.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor @@ -35,6 +35,7 @@ @(String.Format("{0:n0}", context.Downloads)) @SharedLocalizer["Search.Downloads"]  |   @SharedLocalizer["Search.Released"]: @context.ReleaseDate.ToString("MMM dd, yyyy")  |   @SharedLocalizer["Search.Version"]: @context.Version + @((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? "  |  " + SharedLocalizer["Search.Source"] + ": " + new Uri(context.PackageUrl).Host + "" : "")) @((MarkupString)(context.TrialPeriod > 0 ? "  |  " + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "" : "")) diff --git a/Oqtane.Client/Modules/Admin/Users/Roles.razor b/Oqtane.Client/Modules/Admin/Users/Roles.razor index 4b90b902..0fcc3229 100644 --- a/Oqtane.Client/Modules/Admin/Users/Roles.razor +++ b/Oqtane.Client/Modules/Admin/Users/Roles.razor @@ -88,15 +88,17 @@ else userid = Int32.Parse(PageState.QueryString["id"]); User user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); name = user.DisplayName; + if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) { roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true); - roles = roles.Where(item => item.Name != RoleNames.Everyone).ToList(); + roles.RemoveAll(item => item.Name == RoleNames.Everyone || item.Name == RoleNames.Unauthenticated); } else { roles = await RoleService.GetRolesAsync(PageState.Site.SiteId); } + await GetUserRoles(); } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor index 208e2f54..3ce51e28 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -86,279 +86,296 @@ } @code { - private string _id; - private List _folders; - private List _files = new List(); - private string _fileinputid = string.Empty; - private string _progressinfoid = string.Empty; - private string _progressbarid = string.Empty; - private string _filter = "*"; - private bool _haseditpermission = false; - private string _image = string.Empty; - private File _file = null; - private string _guid; - private string _message = string.Empty; - private MessageType _messagetype; + private string _id; + private List _folders; + private List _files = new List(); + private string _fileinputid = string.Empty; + private string _progressinfoid = string.Empty; + private string _progressbarid = string.Empty; + private string _filter = "*"; + private bool _haseditpermission = false; + private string _image = string.Empty; + private File _file = null; + private string _guid; + private string _message = string.Empty; + private MessageType _messagetype; - [Parameter] - public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility + [Parameter] + public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility - [Parameter] - public int FolderId { get; set; } = -1; // optional - for setting a specific default folder by folderid + [Parameter] + public int FolderId { get; set; } = -1; // optional - for setting a specific default folder by folderid - [Parameter] - public string Folder { get; set; } = ""; // optional - for setting a specific default folder by folder path + [Parameter] + public string Folder { get; set; } = ""; // optional - for setting a specific default folder by folder path - [Parameter] - public int FileId { get; set; } = -1; // optional - for selecting a specific file by default + [Parameter] + public int FileId { get; set; } = -1; // optional - for selecting a specific file by default - [Parameter] - public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif" + [Parameter] + public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif" - [Parameter] - public bool ShowFiles { get; set; } = true; // optional - for indicating whether a list of files should be displayed - default is true + [Parameter] + public bool ShowFiles { get; set; } = true; // optional - for indicating whether a list of files should be displayed - default is true - [Parameter] - public bool ShowUpload { get; set; } = true; // optional - for indicating whether a Upload controls should be displayed - default is true + [Parameter] + public bool ShowUpload { get; set; } = true; // optional - for indicating whether a Upload controls should be displayed - default is true - [Parameter] - public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true + [Parameter] + public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true - [Parameter] - public bool ShowImage { get; set; } = true; // optional - for indicating whether an image thumbnail should be displayed - default is true + [Parameter] + public bool ShowImage { get; set; } = true; // optional - for indicating whether an image thumbnail should be displayed - default is true - [Parameter] - public bool ShowSuccess { get; set; } = false; // optional - for indicating whether a success message should be displayed upon successful upload - default is false + [Parameter] + public bool ShowSuccess { get; set; } = false; // optional - for indicating whether a success message should be displayed upon successful upload - default is false - [Parameter] - public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false + [Parameter] + public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false - [Parameter] - public EventCallback OnUpload { get; set; } // optional - executes a method in the calling component when a file is uploaded + [Parameter] + public EventCallback OnUpload { get; set; } // optional - executes a method in the calling component when a file is uploaded - [Parameter] - public EventCallback OnSelect { get; set; } // optional - executes a method in the calling component when a file is selected + [Parameter] + public EventCallback OnSelect { get; set; } // optional - executes a method in the calling component when a file is selected - [Parameter] - public EventCallback OnDelete { get; set; } // optional - executes a method in the calling component when a file is deleted + [Parameter] + public EventCallback OnDelete { get; set; } // optional - executes a method in the calling component when a file is deleted - protected override async Task OnInitializedAsync() - { - if (!string.IsNullOrEmpty(Id)) - { - _id = Id; - } + protected override async Task OnInitializedAsync() + { + if (!string.IsNullOrEmpty(Id)) + { + _id = Id; + } - // packages folder is a framework folder for uploading installable nuget packages - if (Folder == Constants.PackagesFolder) - { - ShowFiles = false; - ShowFolders = false; - Filter = "nupkg"; - ShowSuccess = true; - } + // packages folder is a framework folder for uploading installable nuget packages + if (Folder == Constants.PackagesFolder) + { + ShowFiles = false; + ShowFolders = false; + Filter = "nupkg"; + ShowSuccess = true; + } - if (!ShowFiles) - { - ShowImage = false; - } + if (!ShowFiles) + { + ShowImage = false; + } - _folders = await FolderService.GetFoldersAsync(ModuleState.SiteId); + _folders = await FolderService.GetFoldersAsync(ModuleState.SiteId); - if (!string.IsNullOrEmpty(Folder) && Folder != Constants.PackagesFolder) - { - Folder folder = await FolderService.GetFolderAsync(ModuleState.SiteId, Folder); - if (folder != null) - { - FolderId = folder.FolderId; - } - else - { - FolderId = -1; - _message = "Folder Path " + Folder + "Does Not Exist"; - _messagetype = MessageType.Error; - } - } + if (!string.IsNullOrEmpty(Folder) && Folder != Constants.PackagesFolder) + { + Folder folder = await FolderService.GetFolderAsync(ModuleState.SiteId, Folder); + if (folder != null) + { + FolderId = folder.FolderId; + } + else + { + FolderId = -1; + _message = "Folder Path " + Folder + "Does Not Exist"; + _messagetype = MessageType.Error; + } + } - if (FileId != -1) - { - File file = await FileService.GetFileAsync(FileId); - if (file != null) - { - FolderId = file.FolderId; - await OnSelect.InvokeAsync(FileId); - } - else - { - FileId = -1; // file does not exist - _message = "FileId " + FileId.ToString() + "Does Not Exist"; - _messagetype = MessageType.Error; - } - } + if (FileId != -1) + { + File file = await FileService.GetFileAsync(FileId); + if (file != null) + { + FolderId = file.FolderId; + await OnSelect.InvokeAsync(FileId); + } + else + { + FileId = -1; // file does not exist + _message = "FileId " + FileId.ToString() + "Does Not Exist"; + _messagetype = MessageType.Error; + } + } - await SetImage(); + await SetImage(); - if (!string.IsNullOrEmpty(Filter)) - { - _filter = "." + Filter.Replace(",", ",."); - } + if (!string.IsNullOrEmpty(Filter)) + { + _filter = "." + Filter.Replace(",", ",."); + } - await GetFiles(); + await GetFiles(); - // create unique id for component - _guid = Guid.NewGuid().ToString("N"); - _fileinputid = _guid + "FileInput"; - _progressinfoid = _guid + "ProgressInfo"; - _progressbarid = _guid + "ProgressBar"; - } + // create unique id for component + _guid = Guid.NewGuid().ToString("N"); + _fileinputid = _guid + "FileInput"; + _progressinfoid = _guid + "ProgressInfo"; + _progressbarid = _guid + "ProgressBar"; + } - private async Task GetFiles() - { - _haseditpermission = false; - if (Folder == Constants.PackagesFolder) - { - _haseditpermission = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host); - _files = new List(); - } - else - { - Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId); - if (folder != null) - { - _haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.Permissions); - _files = await FileService.GetFilesAsync(FolderId); - } - else - { - _haseditpermission = false; - _files = new List(); - } - } - if (_filter != "*") - { - List filtered = new List(); - foreach (File file in _files) - { - if (_filter.ToUpper().IndexOf("." + file.Extension.ToUpper()) != -1) - { - filtered.Add(file); - } - } - _files = filtered; - } - } + private async Task GetFiles() + { + _haseditpermission = false; + if (Folder == Constants.PackagesFolder) + { + _haseditpermission = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host); + _files = new List(); + } + else + { + Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId); + if (folder != null) + { + _haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.Permissions); + _files = await FileService.GetFilesAsync(FolderId); + } + else + { + _haseditpermission = false; + _files = new List(); + } + } + if (_filter != "*") + { + List filtered = new List(); + foreach (File file in _files) + { + if (_filter.ToUpper().IndexOf("." + file.Extension.ToUpper()) != -1) + { + filtered.Add(file); + } + } + _files = filtered; + } + } - private async Task FolderChanged(ChangeEventArgs e) - { - _message = string.Empty; - try - { - FolderId = int.Parse((string)e.Value); - await GetFiles(); - FileId = -1; - _file = null; - _image = string.Empty; - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Files {Error}", ex.Message); - _message = Localizer["Error.File.Load"]; - _messagetype = MessageType.Error; - } - } + private async Task FolderChanged(ChangeEventArgs e) + { + _message = string.Empty; + try + { + FolderId = int.Parse((string)e.Value); + await GetFiles(); + FileId = -1; + _file = null; + _image = string.Empty; + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Files {Error}", ex.Message); + _message = Localizer["Error.File.Load"]; + _messagetype = MessageType.Error; + } + } - private async Task FileChanged(ChangeEventArgs e) - { - _message = string.Empty; - FileId = int.Parse((string)e.Value); - if (FileId != -1) - { - await OnSelect.InvokeAsync(FileId); - } + private async Task FileChanged(ChangeEventArgs e) + { + _message = string.Empty; + FileId = int.Parse((string)e.Value); + if (FileId != -1) + { + await OnSelect.InvokeAsync(FileId); + } - await SetImage(); - StateHasChanged(); - } + await SetImage(); + StateHasChanged(); + } - private async Task SetImage() - { - _image = string.Empty; - _file = null; - if (FileId != -1) - { - _file = await FileService.GetFileAsync(FileId); - if (_file != null && ShowImage && _file.ImageHeight != 0 && _file.ImageWidth != 0) - { - var maxwidth = 200; - var maxheight = 200; + private async Task SetImage() + { + _image = string.Empty; + _file = null; + if (FileId != -1) + { + _file = await FileService.GetFileAsync(FileId); + if (_file != null && ShowImage && _file.ImageHeight != 0 && _file.ImageWidth != 0) + { + var maxwidth = 200; + var maxheight = 200; - var ratioX = (double)maxwidth / (double)_file.ImageWidth; - var ratioY = (double)maxheight / (double)_file.ImageHeight; - var ratio = ratioX < ratioY ? ratioX : ratioY; + var ratioX = (double)maxwidth / (double)_file.ImageWidth; + var ratioY = (double)maxheight / (double)_file.ImageHeight; + var ratio = ratioX < ratioY ? ratioX : ratioY; - _image = "\"""; - } - } - } + _image = "\"""; + } + } + } - private async Task UploadFile() - { - _message = string.Empty; - var interop = new Interop(JSRuntime); - var upload = await interop.GetFiles(_fileinputid); - if (upload.Length > 0) - { - try - { - string result; - if (Folder == Constants.PackagesFolder) - { - result = await FileService.UploadFilesAsync(Folder, upload, _guid); - } - else - { - result = await FileService.UploadFilesAsync(FolderId, upload, _guid); - } + private async Task UploadFile() + { + _message = string.Empty; + var interop = new Interop(JSRuntime); + var upload = await interop.GetFiles(_fileinputid); + if (upload.Length > 0) + { + string restricted = ""; + foreach (var file in upload) + { + var extension = (file.LastIndexOf(".") != -1) ? file.Substring(file.LastIndexOf(".") + 1) : ""; + if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower())) + { + restricted += (restricted == "" ? "" : ",") + extension; + } + } + if (restricted == "") + { + try + { + string result; + if (Folder == Constants.PackagesFolder) + { + result = await FileService.UploadFilesAsync(Folder, upload, _guid); + } + else + { + result = await FileService.UploadFilesAsync(FolderId, upload, _guid); + } - if (result == string.Empty) - { - await logger.LogInformation("File Upload Succeeded {Files}", upload); - if (ShowSuccess) - { - _message = Localizer["Success.File.Upload"]; - _messagetype = MessageType.Success; - } + if (result == string.Empty) + { + await logger.LogInformation("File Upload Succeeded {Files}", upload); + if (ShowSuccess) + { + _message = Localizer["Success.File.Upload"]; + _messagetype = MessageType.Success; + } - // set FileId to first file in upload collection - await GetFiles(); - var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault(); - if (file != null) - { - FileId = file.FileId; - await SetImage(); - await OnUpload.InvokeAsync(FileId); - } - StateHasChanged(); - } - else - { - await logger.LogError("File Upload Failed For {Files}", result.Replace(",", ", ")); + // set FileId to first file in upload collection + await GetFiles(); + var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault(); + if (file != null) + { + FileId = file.FileId; + await SetImage(); + await OnUpload.InvokeAsync(FileId); + } + StateHasChanged(); + } + else + { + await logger.LogError("File Upload Failed For {Files}", result.Replace(",", ", ")); - _message = Localizer["Error.File.Upload"]; - _messagetype = MessageType.Error; - } - } - catch (Exception ex) - { - await logger.LogError(ex, "File Upload Failed {Error}", ex.Message); + _message = Localizer["Error.File.Upload"]; + _messagetype = MessageType.Error; + } + } + catch (Exception ex) + { + await logger.LogError(ex, "File Upload Failed {Error}", ex.Message); - _message = Localizer["Error.File.Upload"]; - _messagetype = MessageType.Error; - } - } + _message = Localizer["Error.File.Upload"]; + _messagetype = MessageType.Error; + } + } + else + { + _message = string.Format(Localizer["Message.File.Restricted"], restricted); + _messagetype = MessageType.Warning; + } + } else { _message = Localizer["Message.File.NotSelected"]; diff --git a/Oqtane.Client/Modules/Controls/LocalizableComponent.cs b/Oqtane.Client/Modules/Controls/LocalizableComponent.cs index 711504c6..82f0bfba 100644 --- a/Oqtane.Client/Modules/Controls/LocalizableComponent.cs +++ b/Oqtane.Client/Modules/Controls/LocalizableComponent.cs @@ -1,13 +1,13 @@ using System; using Microsoft.AspNetCore.Components; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; -using Oqtane.Shared; namespace Oqtane.Modules.Controls { public class LocalizableComponent : ModuleControlBase { + [Inject] public IStringLocalizerFactory LocalizerFactory { get; set; } + private IStringLocalizer _localizer; [Parameter] @@ -30,22 +30,15 @@ namespace Oqtane.Modules.Controls var key = $"{ResourceKey}.{propertyName}"; var value = Localize(key); - if (value == key) + if (value == key || value == String.Empty) { - // Returns default property value (English version) instead of ResourceKey.PropertyName + // return 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; - } - else - { - return value; - } + // return localized value + return value; } } @@ -53,24 +46,15 @@ namespace Oqtane.Modules.Controls { IsLocalizable = false; - if (string.IsNullOrEmpty(ResourceType)) + if (String.IsNullOrEmpty(ResourceType)) { ResourceType = ModuleState?.ModuleType; } - if (!String.IsNullOrEmpty(ResourceKey) && !string.IsNullOrEmpty(ResourceType)) + if (!String.IsNullOrEmpty(ResourceKey) && !String.IsNullOrEmpty(ResourceType)) { - var moduleType = Type.GetType(ResourceType); - if (moduleType != null) - { - using (var scope = ServiceActivator.GetScope()) - { - var localizerFactory = scope.ServiceProvider.GetService(); - _localizer = localizerFactory.Create(moduleType); - - IsLocalizable = true; - } - } + _localizer = LocalizerFactory.Create(ResourceType); + IsLocalizable = true; } } } diff --git a/Oqtane.Client/Modules/Controls/Pager.razor b/Oqtane.Client/Modules/Controls/Pager.razor index 114e9028..e317f38b 100644 --- a/Oqtane.Client/Modules/Controls/Pager.razor +++ b/Oqtane.Client/Modules/Controls/Pager.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Controls @inherits ModuleControlBase +@inject IStringLocalizerFactory LocalizerFactory @typeparam TableItem @if (ItemList != null) @@ -48,7 +49,7 @@ UpdateList(_pages))>
  • - Page @_page of @_pages + @Localizer["PageOfPages", _page, _pages]
  • } @@ -156,67 +157,73 @@ UpdateList(_pages))>
  • - Page @_page of @_pages + @Localizer["PageOfPages", _page, _pages]
  • } } @code { - private int _pages = 0; - private int _page = 1; - private int _maxItems = 10; - private int _displayPages = 5; - private int _startPage = 0; - private int _endPage = 0; - private int _columns = 0; - - [Parameter] - public string Format { get; set; } // Table or Grid - - [Parameter] - public string Toolbar { get; set; } // Top, Bottom or Both - - [Parameter] - public RenderFragment Header { get; set; } = null; // only applicable to Table layouts - - [Parameter] - public RenderFragment Row { get; set; } = null; // required - - [Parameter] - public RenderFragment Detail { get; set; } = null; // only applicable to Table layouts - - [Parameter] - public IEnumerable Items { get; set; } // the IEnumerable data source - - [Parameter] - public string PageSize { get; set; } // number of items to display on a page - - [Parameter] - public string Columns { get; set; } // only applicable to Grid layouts - default is zero indicating use responsive behavior - - [Parameter] - public string CurrentPage { get; set; } // sets the initial page to display - - [Parameter] - public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection - - [Parameter] - public string Class { get; set; } // class for the containing element - ie. for Table or
    for Grid - - [Parameter] - public string RowClass { get; set; } // class for row element - ie.
    for Table or
    for Grid + private IStringLocalizer Localizer; + private int _pages = 0; + private int _page = 1; + private int _maxItems = 10; + private int _displayPages = 5; + private int _startPage = 0; + private int _endPage = 0; + private int _columns = 0; [Parameter] - public string ColumnClass { get; set; } // class for column element - only applicable to Grid format + public string Format { get; set; } // Table or Grid - [Parameter] - public Action OnPageChange { get; set; } // a method to be executed in the calling component when the page changes + [Parameter] + public string Toolbar { get; set; } // Top, Bottom or Both - private IEnumerable ItemList { get; set; } + [Parameter] + public RenderFragment Header { get; set; } = null; // only applicable to Table layouts - protected override void OnParametersSet() - { + [Parameter] + public RenderFragment Row { get; set; } = null; // required + + [Parameter] + public RenderFragment Detail { get; set; } = null; // only applicable to Table layouts + + [Parameter] + public IEnumerable Items { get; set; } // the IEnumerable data source + + [Parameter] + public string PageSize { get; set; } // number of items to display on a page + + [Parameter] + public string Columns { get; set; } // only applicable to Grid layouts - default is zero indicating use responsive behavior + + [Parameter] + public string CurrentPage { get; set; } // sets the initial page to display + + [Parameter] + public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection + + [Parameter] + public string Class { get; set; } // class for the containing element - ie.
    for Table or
    for Grid + + [Parameter] + public string RowClass { get; set; } // class for row element - ie.
    for Table or
    for Grid + + [Parameter] + public string ColumnClass { get; set; } // class for column element - only applicable to Grid format + + [Parameter] + public Action OnPageChange { get; set; } // a method to be executed in the calling component when the page changes + + private IEnumerable ItemList { get; set; } + + protected override void OnInitialized() + { + Localizer = LocalizerFactory.Create(GetType().FullName); + } + + protected override void OnParametersSet() + { if (string.IsNullOrEmpty(Format)) { Format = "Table"; diff --git a/Oqtane.Client/Modules/Controls/PermissionGrid.razor b/Oqtane.Client/Modules/Controls/PermissionGrid.razor index d1777a68..c2e6f87a 100644 --- a/Oqtane.Client/Modules/Controls/PermissionGrid.razor +++ b/Oqtane.Client/Modules/Controls/PermissionGrid.razor @@ -127,11 +127,10 @@ _permissionnames = PermissionNames; } - _roles = await RoleService.GetRolesAsync(ModuleState.SiteId); - _roles.Insert(0, new Role { Name = RoleNames.Everyone }); - if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) + _roles = await RoleService.GetRolesAsync(ModuleState.SiteId, true); + if (!UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) { - _roles.Add(new Role { Name = RoleNames.Host }); + _roles.RemoveAll(item => item.Name == RoleNames.Host); } _permissions = new List(); @@ -254,6 +253,7 @@ permission = _permissions[i]; List ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList(); ids.Remove("!" + RoleNames.Everyone); // remove deny all users + ids.Remove("!" + RoleNames.Unauthenticated); // remove deny unauthenticated ids.Remove("!" + RoleNames.Registered); // remove deny registered users if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) { diff --git a/Oqtane.Client/Modules/Controls/RichTextEditor.razor b/Oqtane.Client/Modules/Controls/RichTextEditor.razor index 402f289b..1e31db60 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -150,10 +150,6 @@ // preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor) _originalrichhtml = await interop.GetHtml(_editorElement); } - else - { - await interop.LoadEditorContent(_editorElement, _richhtml); - } } public void CloseFileManager() diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index 26d2f777..4f714b1c 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -5,7 +5,7 @@ Exe 3.0 Debug;Release - 3.1.2 + 3.1.3 Oqtane Shaun Walker .NET Foundation @@ -13,7 +13,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 https://github.com/oqtane/oqtane.framework Git Oqtane diff --git a/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Index.resx b/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Index.resx index 834bbe33..817b22db 100644 --- a/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Index.resx @@ -150,4 +150,7 @@ Edit + + Modules + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/Site/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Site/Index.resx index 1007685f..66df7731 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Site/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Site/Index.resx @@ -142,7 +142,7 @@ Site Settings Saved - The Default Alias Has Not Been Specified Or An Alias Was Specified That Has Already Been Used For Another Site + An Alias Was Specified That Has Already Been Used For Another Site You Must Provide A Site Name, Alias, And Default Theme/Container @@ -324,4 +324,13 @@ Aliases + + Name + + + Default? + + + Are You Sure You Wish To Delete {0}? + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/UserProfile/Index.resx b/Oqtane.Client/Resources/Modules/Admin/UserProfile/Index.resx index 6eabb548..2d09bacc 100644 --- a/Oqtane.Client/Resources/Modules/Admin/UserProfile/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/UserProfile/Index.resx @@ -219,4 +219,10 @@ Delete ALL Notifications + + Notifications + + + Profile + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Controls/FileManager.resx b/Oqtane.Client/Resources/Modules/Controls/FileManager.resx index 4fcd3641..c2ccac05 100644 --- a/Oqtane.Client/Resources/Modules/Controls/FileManager.resx +++ b/Oqtane.Client/Resources/Modules/Controls/FileManager.resx @@ -141,4 +141,7 @@ File Upload Succeeded + + Files With Extension Of {0} Are Restricted From Upload. Please Contact Your Administrator For More Information. + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Controls/Pager.resx b/Oqtane.Client/Resources/Modules/Controls/Pager.resx new file mode 100644 index 00000000..710d041d --- /dev/null +++ b/Oqtane.Client/Resources/Modules/Controls/Pager.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Page {0} of {1} + + \ No newline at end of file diff --git a/Oqtane.Client/Resources/SharedResources.resx b/Oqtane.Client/Resources/SharedResources.resx index 774b533a..33a57601 100644 --- a/Oqtane.Client/Resources/SharedResources.resx +++ b/Oqtane.Client/Resources/SharedResources.resx @@ -327,4 +327,13 @@ Show + + Page {0} of {1} + + + Url Mappings + + + Visitor Management + \ No newline at end of file diff --git a/Oqtane.Client/Themes/Controls/Theme/MenuItemsHorizontal.razor b/Oqtane.Client/Themes/Controls/Theme/MenuItemsHorizontal.razor index 2bf9a284..76c254e4 100644 --- a/Oqtane.Client/Themes/Controls/Theme/MenuItemsHorizontal.razor +++ b/Oqtane.Client/Themes/Controls/Theme/MenuItemsHorizontal.razor @@ -9,15 +9,19 @@ if (childPage.PageId == PageState.Page.PageId) { + } else { - } } @@ -34,8 +38,10 @@ else { } @@ -43,8 +49,10 @@ else { } diff --git a/Oqtane.Client/Themes/Controls/Theme/MenuItemsVertical.razor b/Oqtane.Client/Themes/Controls/Theme/MenuItemsVertical.razor index 529ae3f3..c7417b28 100644 --- a/Oqtane.Client/Themes/Controls/Theme/MenuItemsVertical.razor +++ b/Oqtane.Client/Themes/Controls/Theme/MenuItemsVertical.razor @@ -9,8 +9,10 @@ { } @@ -18,8 +20,10 @@ { } @@ -38,8 +42,10 @@ else { } @@ -47,8 +53,10 @@ else { } diff --git a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj index 50cb0ebe..f8b649f0 100644 --- a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj +++ b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj @@ -2,7 +2,7 @@ net6.0 - 3.1.2 + 3.1.3 Oqtane Shaun Walker .NET Foundation @@ -10,7 +10,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 https://github.com/oqtane/oqtane.framework Git true diff --git a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.nuspec b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.nuspec index 66196808..a063f387 100644 --- a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.nuspec +++ b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.nuspec @@ -2,7 +2,7 @@ Oqtane.Database.MySQL - 3.1.2 + 3.1.3 Shaun Walker .NET Foundation Oqtane MySQL Provider @@ -12,7 +12,7 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 icon.png oqtane diff --git a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj index ecd79fe8..5d9d5b14 100644 --- a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj +++ b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj @@ -2,7 +2,7 @@ net6.0 - 3.1.2 + 3.1.3 Oqtane Shaun Walker .NET Foundation @@ -10,7 +10,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 https://github.com/oqtane/oqtane.framework Git true diff --git a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.nuspec b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.nuspec index d8b1a0ae..0ad3888e 100644 --- a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.nuspec +++ b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.nuspec @@ -2,7 +2,7 @@ Oqtane.Database.PostgreSQL - 3.1.2 + 3.1.3 Shaun Walker .NET Foundation Oqtane PostgreSQL Provider @@ -12,7 +12,7 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 icon.png oqtane diff --git a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj index a3c7345c..bba54de8 100644 --- a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj +++ b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj @@ -2,7 +2,7 @@ net6.0 - 3.1.2 + 3.1.3 Oqtane Shaun Walker .NET Foundation @@ -10,7 +10,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 https://github.com/oqtane/oqtane.framework Git true diff --git a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.nuspec b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.nuspec index 14033261..33c5e6c1 100644 --- a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.nuspec +++ b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.nuspec @@ -2,7 +2,7 @@ Oqtane.Database.SqlServer - 3.1.2 + 3.1.3 Shaun Walker .NET Foundation Oqtane SQL Server Provider @@ -12,7 +12,7 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 icon.png oqtane diff --git a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj index ef756656..e81f5e85 100644 --- a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj +++ b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj @@ -2,7 +2,7 @@ net6.0 - 3.1.2 + 3.1.3 Oqtane Shaun Walker .NET Foundation @@ -10,7 +10,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 https://github.com/oqtane/oqtane.framework Git true diff --git a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.nuspec b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.nuspec index 610fead4..150fbd49 100644 --- a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.nuspec +++ b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.nuspec @@ -2,7 +2,7 @@ Oqtane.Database.Sqlite - 3.1.2 + 3.1.3 Shaun Walker .NET Foundation Oqtane SQLite Provider @@ -12,7 +12,7 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Client.nuspec b/Oqtane.Package/Oqtane.Client.nuspec index f4b8df17..d402e91d 100644 --- a/Oqtane.Package/Oqtane.Client.nuspec +++ b/Oqtane.Package/Oqtane.Client.nuspec @@ -2,7 +2,7 @@ Oqtane.Client - 3.1.2 + 3.1.3 Shaun Walker .NET Foundation Oqtane Framework @@ -12,7 +12,7 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec index 6a0d5643..2bc3e950 100644 --- a/Oqtane.Package/Oqtane.Framework.nuspec +++ b/Oqtane.Package/Oqtane.Framework.nuspec @@ -2,7 +2,7 @@ Oqtane.Framework - 3.1.2 + 3.1.3 Shaun Walker .NET Foundation Oqtane Framework @@ -11,8 +11,8 @@ .NET Foundation false MIT - https://github.com/oqtane/oqtane.framework/releases/download/v3.1.2/Oqtane.Framework.3.1.2.Upgrade.zip - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/download/v3.1.3/Oqtane.Framework.3.1.2.Upgrade.zip + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 icon.png oqtane framework diff --git a/Oqtane.Package/Oqtane.Server.nuspec b/Oqtane.Package/Oqtane.Server.nuspec index 16367cca..e22f8b93 100644 --- a/Oqtane.Package/Oqtane.Server.nuspec +++ b/Oqtane.Package/Oqtane.Server.nuspec @@ -2,7 +2,7 @@ Oqtane.Server - 3.1.2 + 3.1.3 Shaun Walker .NET Foundation Oqtane Framework @@ -12,7 +12,7 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Shared.nuspec b/Oqtane.Package/Oqtane.Shared.nuspec index b9270d9b..18eab489 100644 --- a/Oqtane.Package/Oqtane.Shared.nuspec +++ b/Oqtane.Package/Oqtane.Shared.nuspec @@ -2,7 +2,7 @@ Oqtane.Shared - 3.1.2 + 3.1.3 Shaun Walker .NET Foundation Oqtane Framework @@ -12,7 +12,7 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Updater.nuspec b/Oqtane.Package/Oqtane.Updater.nuspec index f1088c31..c2148a80 100644 --- a/Oqtane.Package/Oqtane.Updater.nuspec +++ b/Oqtane.Package/Oqtane.Updater.nuspec @@ -2,7 +2,7 @@ Oqtane.Updater - 3.1.2 + 3.1.3 Shaun Walker .NET Foundation Oqtane Framework @@ -12,7 +12,7 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 icon.png oqtane diff --git a/Oqtane.Package/install.ps1 b/Oqtane.Package/install.ps1 index 7c50c513..9537ce0d 100644 --- a/Oqtane.Package/install.ps1 +++ b/Oqtane.Package/install.ps1 @@ -1 +1 @@ -Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.2.Install.zip" -Force \ No newline at end of file +Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.3.Install.zip" -Force \ No newline at end of file diff --git a/Oqtane.Package/upgrade.ps1 b/Oqtane.Package/upgrade.ps1 index b8b3ff2c..aa13b00f 100644 --- a/Oqtane.Package/upgrade.ps1 +++ b/Oqtane.Package/upgrade.ps1 @@ -1 +1 @@ -Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.2.Upgrade.zip" -Force \ No newline at end of file +Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.3.Upgrade.zip" -Force \ No newline at end of file diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index e13f770c..27666b66 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -255,7 +255,7 @@ namespace Oqtane.Controllers } 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 @@ -276,9 +276,17 @@ namespace Oqtane.Controllers 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; } @@ -331,9 +339,9 @@ namespace Oqtane.Controllers { 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 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)); filename = Path.GetFileNameWithoutExtension(filename); // base filename @@ -370,23 +378,15 @@ namespace Oqtane.Controllers System.IO.File.Delete(filepart); } - // check for allowable file extensions - if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(filename)?.ToLower().Replace(".", ""))) + // remove file if it already exists + if (System.IO.File.Exists(Path.Combine(folder, filename))) { - System.IO.File.Delete(Path.Combine(folder, filename + ".tmp")); + System.IO.File.Delete(Path.Combine(folder, filename)); } - else - { - // remove file if it already exists - if (System.IO.File.Exists(Path.Combine(folder, filename))) - { - System.IO.File.Delete(Path.Combine(folder, filename)); - } - // rename file now that the entire process is completed - 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)); - } + // rename file now that the entire process is completed + 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)); 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 ) 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) { var createdDate = System.IO.File.GetCreationTime(file).ToUniversalTime(); @@ -601,9 +600,9 @@ 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 = ""; } diff --git a/Oqtane.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs index 100f30ad..9337ec58 100644 --- a/Oqtane.Server/Controllers/ModuleController.cs +++ b/Oqtane.Server/Controllers/ModuleController.cs @@ -63,6 +63,8 @@ namespace Oqtane.Controllers module.CreatedOn = pagemodule.Module.CreatedOn; module.ModifiedBy = pagemodule.Module.ModifiedBy; module.ModifiedOn = pagemodule.Module.ModifiedOn; + module.DeletedBy = pagemodule.DeletedBy; + module.DeletedOn = pagemodule.DeletedOn; module.IsDeleted = pagemodule.IsDeleted; module.PageModuleId = pagemodule.PageModuleId; @@ -139,24 +141,41 @@ namespace Oqtane.Controllers [Authorize(Roles = RoleNames.Registered)] 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); - if (module.AllPages) - { - var pageModule = _pageModules.GetPageModules(module.SiteId).FirstOrDefault(item => item.ModuleId == module.ModuleId); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module); - var pages = _pages.GetPages(module.SiteId).ToList(); - foreach (Page page in pages) + if (_module.AllPages != module.AllPages) + { + var pageModules = _pageModules.GetPageModules(module.SiteId).ToList(); + if (module.AllPages) { - if (page.PageId != pageModule.PageId && !page.Path.StartsWith("admin/")) + var pageModule = _pageModules.GetPageModule(module.PageModuleId); + var pages = _pages.GetPages(module.SiteId).ToList(); + foreach (Page page in pages) { - _pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType }); + 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 }); + } + } + } + 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); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module); } else { diff --git a/Oqtane.Server/Controllers/SqlController.cs b/Oqtane.Server/Controllers/SqlController.cs index 6b4eca3d..985f91d6 100644 --- a/Oqtane.Server/Controllers/SqlController.cs +++ b/Oqtane.Server/Controllers/SqlController.cs @@ -53,7 +53,7 @@ namespace Oqtane.Controllers catch (Exception ex) { results.Add(new Dictionary() { { "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; return sqlquery; diff --git a/Oqtane.Server/Controllers/UserRoleController.cs b/Oqtane.Server/Controllers/UserRoleController.cs index 1d179a3d..7624e7ad 100644 --- a/Oqtane.Server/Controllers/UserRoleController.cs +++ b/Oqtane.Server/Controllers/UserRoleController.cs @@ -39,32 +39,24 @@ namespace Oqtane.Controllers public IEnumerable Get(string siteid, string userid = null, string rolename = null) { 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(); + if (userid != null) { - var userroles = _userRoles.GetUserRoles(SiteId).ToList(); - if (userid != null) - { - userroles = userroles.Where(item => item.UserId == UserId).ToList(); - } - if (rolename != null) - { - userroles = userroles.Where(item => item.Role.Name == rolename).ToList(); - } - for (int i = 0; i < userroles.Count(); i++) - { - userroles[i] = Filter(userroles[i]); - } - return userroles.OrderBy(u => u.User.DisplayName); + int UserId = int.TryParse(userid, out UserId) ? UserId : -1; + userroles = userroles.Where(item => item.UserId == UserId).ToList(); } - else + if (rolename != null) { - _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; + userroles = userroles.Where(item => item.Role.Name == rolename).ToList(); } + var user = _userPermissions.GetUser(); + for (int i = 0; i < userroles.Count(); i++) + { + userroles[i] = Filter(userroles[i], user.UserId); + } + return userroles.OrderBy(u => u.User.DisplayName); } else { @@ -82,16 +74,7 @@ namespace Oqtane.Controllers var userrole = _userRoles.GetUserRole(id); 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); - } - else - { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Role Get Attempt {UserRoleId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; - return null; - } + return Filter(userrole, _userPermissions.GetUser().UserId); } else { @@ -101,7 +84,7 @@ namespace Oqtane.Controllers } } - private UserRole Filter(UserRole userrole) + private UserRole Filter(UserRole userrole, int userid) { if (userrole != null) { @@ -110,7 +93,7 @@ namespace Oqtane.Controllers userrole.User.TwoFactorCode = ""; 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.PhotoFileId = null; diff --git a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs index a725f862..4d28a043 100644 --- a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs @@ -187,7 +187,7 @@ namespace Oqtane.Extensions catch (Exception ex) { var _logger = context.HttpContext.RequestServices.GetRequiredService(); - _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); } } @@ -417,7 +417,7 @@ namespace Oqtane.Extensions { _logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Identifier To Uniquely Identify The User. The Identifier Claim Specified Was {IdentifierCLaimType} And Actual Claim Types Are {Claims}. Login Denied.", httpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", ""), claims); } - + return identity; } diff --git a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs index 7818d9d3..9ad9bbfd 100644 --- a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs +++ b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs @@ -120,6 +120,10 @@ namespace Oqtane.Infrastructure mailMessage.Body += "Subject: " + notification.Subject + "\n\n"; mailMessage.Body += notification.Body; + // set encoding + mailMessage.SubjectEncoding = System.Text.Encoding.UTF8; + mailMessage.BodyEncoding = System.Text.Encoding.UTF8; + // send mail try { diff --git a/Oqtane.Server/Infrastructure/LogManager.cs b/Oqtane.Server/Infrastructure/LogManager.cs index 949d01b9..900f5e91 100644 --- a/Oqtane.Server/Infrastructure/LogManager.cs +++ b/Oqtane.Server/Infrastructure/LogManager.cs @@ -203,12 +203,15 @@ namespace Oqtane.Infrastructure } if (Enum.Parse(log.Level) >= notifylevel) { + var alias = _tenantManager.GetAlias(); foreach (var userrole in _userRoles.GetUserRoles(log.SiteId.Value)) { if (userrole.Role.Name == RoleNames.Host) { - var url = _accessor.HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name + "/admin/log"; - var notification = new Notification(log.SiteId.Value, userrole.User, "Site " + log.Level + " Notification", "Please visit " + url + " for more information"); + var subject = $"{alias.Name} Site {log.Level} Notification"; + 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); } } diff --git a/Oqtane.Server/Infrastructure/UpgradeManager.cs b/Oqtane.Server/Infrastructure/UpgradeManager.cs index 124652c9..2074dd0e 100644 --- a/Oqtane.Server/Infrastructure/UpgradeManager.cs +++ b/Oqtane.Server/Infrastructure/UpgradeManager.cs @@ -50,6 +50,9 @@ namespace Oqtane.Infrastructure case "3.0.1": Upgrade_3_0_1(tenant, scope); break; + case "3.1.3": + Upgrade_3_1_3(tenant, scope); + break; } } } @@ -182,5 +185,15 @@ namespace Oqtane.Infrastructure sites.CreatePages(site, pageTemplates); } } + + private void Upgrade_3_1_3(Tenant tenant, IServiceScope scope) + { + var roles = scope.ServiceProvider.GetRequiredService(); + 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 }); + } + } + } } diff --git a/Oqtane.Server/Migrations/EntityBuilders/BaseEntityBuilder.cs b/Oqtane.Server/Migrations/EntityBuilders/BaseEntityBuilder.cs index 1bde5b0a..d537bc9b 100644 --- a/Oqtane.Server/Migrations/EntityBuilders/BaseEntityBuilder.cs +++ b/Oqtane.Server/Migrations/EntityBuilders/BaseEntityBuilder.cs @@ -18,6 +18,7 @@ namespace Oqtane.Migrations.EntityBuilders _migrationBuilder = migrationBuilder; ActiveDatabase = database; ForeignKeys = new List>(); + Schema = null; } protected IDatabase ActiveDatabase { get; } @@ -30,6 +31,8 @@ namespace Oqtane.Migrations.EntityBuilders protected List> ForeignKeys { get; } + protected string Schema { get; init; } + private string RewriteName(string name) { return ActiveDatabase.RewriteName(name); @@ -319,7 +322,7 @@ namespace Oqtane.Migrations.EntityBuilders /// public void Create() { - _migrationBuilder.CreateTable(RewriteName(EntityTableName), BuildTable, null, AddKeys); + _migrationBuilder.CreateTable(RewriteName(EntityTableName), BuildTable, Schema, AddKeys); } /// diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 9860fdd2..3e782492 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -3,7 +3,7 @@ net6.0 Debug;Release - 3.1.2 + 3.1.3 Oqtane Shaun Walker .NET Foundation @@ -11,7 +11,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 https://github.com/oqtane/oqtane.framework Git Oqtane diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index e5ca3265..ded32453 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -94,16 +94,18 @@ namespace Oqtane.Repository List roles = _roleRepository.GetRoles(site.SiteId, true).ToList(); 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()) { - _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 = "Registered Users", IsAutoAssigned = true, IsSystem = true}); - _roleRepository.AddRole(new Role {SiteId = site.SiteId, Name = RoleNames.Admin, Description = "Site Administrators", 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.Admin, Description = RoleNames.Admin, IsAutoAssigned = false, IsSystem = true}); _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 = ""}); diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak index c345491a..7ce78d52 100644 Binary files a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak and b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak differ diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.PostgreSQL.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.PostgreSQL.nupkg.bak index 9440bbed..5b123cb0 100644 Binary files a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.PostgreSQL.nupkg.bak and b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.PostgreSQL.nupkg.bak differ diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.SqlServer.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.SqlServer.nupkg.bak index 1e0d539d..5013ce33 100644 Binary files a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.SqlServer.nupkg.bak and b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.SqlServer.nupkg.bak differ diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.Sqlite.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.Sqlite.nupkg.bak index cbe39e9b..e015c2be 100644 Binary files a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.Sqlite.nupkg.bak and b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.Sqlite.nupkg.bak differ diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index d65abfa5..b62c006c 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -344,10 +344,17 @@ Oqtane.Interop = { progressinfo.innerHTML = file.name + ' 100%'; progressbar.value = 1; }; + request.upload.onerror = function () { + progressinfo.innerHTML = file.name + ' Error: ' + xhr.status; + progressbar.value = 0; + }; request.send(data); } + + if (i === files.length - 1) { + fileinput.value = ''; + } } - fileinput.value = ''; }, refreshBrowser: function (reload, wait) { setInterval(function () { diff --git a/Oqtane.Shared/Models/Module.cs b/Oqtane.Shared/Models/Module.cs index 52c70d99..472ddcbc 100644 --- a/Oqtane.Shared/Models/Module.cs +++ b/Oqtane.Shared/Models/Module.cs @@ -60,6 +60,7 @@ namespace Oqtane.Models public Dictionary Settings { get; set; } #region PageModule properties + [NotMapped] public int PageModuleId { get; set; } @@ -68,6 +69,7 @@ namespace Oqtane.Models /// [NotMapped] public int PageId { get; set; } + [NotMapped] public string Title { get; set; } @@ -76,8 +78,10 @@ namespace Oqtane.Models /// [NotMapped] public string Pane { get; set; } + [NotMapped] public int Order { get; set; } + [NotMapped] public string ContainerType { get; set; } diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index 886b17a4..80e06e10 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -3,7 +3,7 @@ net6.0 Debug;Release - 3.1.2 + 3.1.3 Oqtane Shaun Walker .NET Foundation @@ -11,7 +11,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 https://github.com/oqtane/oqtane.framework Git Oqtane diff --git a/Oqtane.Shared/Security/UserSecurity.cs b/Oqtane.Shared/Security/UserSecurity.cs index 1830765f..bda57c59 100644 --- a/Oqtane.Shared/Security/UserSecurity.cs +++ b/Oqtane.Shared/Security/UserSecurity.cs @@ -104,11 +104,14 @@ namespace Oqtane.Security private static bool IsAllowed(int userId, string roles, string permission) { + if (permission == RoleNames.Unauthenticated) + { + return userId == -1; + } if ("[" + userId + "]" == permission) { return true; } - if (roles != null) { return roles.IndexOf(";" + permission + ";") != -1; diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 66a0f9c0..1d1b4fe7 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -4,8 +4,8 @@ namespace Oqtane.Shared { public class Constants { - public static readonly string Version = "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"; + 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,3.1.3"; public const string PackageId = "Oqtane.Framework"; public const string UpdaterPackageId = "Oqtane.Updater"; public const string PackageRegistryUrl = "https://www.oqtane.net"; diff --git a/Oqtane.Shared/Shared/RoleNames.cs b/Oqtane.Shared/Shared/RoleNames.cs index f526d613..4c935c50 100644 --- a/Oqtane.Shared/Shared/RoleNames.cs +++ b/Oqtane.Shared/Shared/RoleNames.cs @@ -1,8 +1,9 @@ -namespace Oqtane.Shared { +namespace Oqtane.Shared { public class RoleNames { public const string Everyone = "All Users"; public const string Host = "Host Users"; public const string Admin = "Administrators"; public const string Registered = "Registered Users"; + public const string Unauthenticated = "Unauthenticated Users"; } } diff --git a/Oqtane.Test/Oqtane.Test.csproj b/Oqtane.Test/Oqtane.Test.csproj index 561f0b2a..6cf81bb6 100644 --- a/Oqtane.Test/Oqtane.Test.csproj +++ b/Oqtane.Test/Oqtane.Test.csproj @@ -3,7 +3,7 @@ net6.0 Debug;Release - 3.1.2 + 3.1.3 Oqtane Shaun Walker .NET Foundation @@ -11,7 +11,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 https://github.com/oqtane/oqtane.framework Git Oqtane diff --git a/Oqtane.Updater/Oqtane.Updater.csproj b/Oqtane.Updater/Oqtane.Updater.csproj index 30a140d8..da67af36 100644 --- a/Oqtane.Updater/Oqtane.Updater.csproj +++ b/Oqtane.Updater/Oqtane.Updater.csproj @@ -3,7 +3,7 @@ net6.0 Exe - 3.1.2 + 3.1.3 Oqtane Shaun Walker .NET Foundation @@ -11,7 +11,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3 https://github.com/oqtane/oqtane.framework Git Oqtane diff --git a/README.md b/README.md index 9711d850..ee693ad0 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,14 @@ There is a separate [Documentation repository](https://github.com/oqtane/oqtane. # Roadmap This project is open source, and therefore is a work in progress... -Backlog (Not Yet Assigned) -- [ ] Allow language specification in Url (#1731) +V.4.0.0 ( Q4 2022 ) +- [ ] MAUI / Blazor Hybrid support + +V.3.1.3 ( June 2022 ) +- [ ] Stabilization improvements + +V.3.1.2 ( May 14, 2022 ) +- [x] Stabilization improvements V.3.1.1 ( May 3, 2022 ) - [x] Stabilization improvements