From 43c34fcd64962bb9d0452876d2b848a23cb5cdce Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 26 May 2022 01:19:14 -0400 Subject: [PATCH 1/6] fix #2213 - disabling show on all pages --- .../Modules/Admin/Modules/Settings.razor | 207 +++++++++--------- Oqtane.Server/Controllers/ModuleController.cs | 35 ++- Oqtane.Shared/Models/Module.cs | 4 + 3 files changed, 134 insertions(+), 112 deletions(-) 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.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs index 100f30ad..a64b7f88 100644 --- a/Oqtane.Server/Controllers/ModuleController.cs +++ b/Oqtane.Server/Controllers/ModuleController.cs @@ -139,24 +139,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.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; } From 16a6f942c5ca889dbc6b6afcd124fadde365095a Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Fri, 27 May 2022 16:20:35 +0300 Subject: [PATCH 2/6] Avoid toggle password & confirm password as same time when one button is clicked --- Oqtane.Client/Installer/Installer.razor | 43 ++++++++++++++++++------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/Oqtane.Client/Installer/Installer.razor b/Oqtane.Client/Installer/Installer.razor index 5630b592..fddbf529 100644 --- a/Oqtane.Client/Installer/Installer.razor +++ b/Oqtane.Client/Installer/Installer.razor @@ -63,8 +63,8 @@
- - + +
@@ -72,8 +72,8 @@
- - + +
@@ -110,8 +110,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,8 +122,11 @@ protected override async Task OnInitializedAsync() { - _togglepassword = SharedLocalizer["ShowPassword"]; + _togglePassword = SharedLocalizer["ShowPassword"]; + _toggleConfirmPassword = SharedLocalizer["ShowPassword"]; + _databases = await DatabaseService.GetDatabasesAsync(); + if (_databases.Exists(item => item.IsDefault)) { _databaseName = _databases.Find(item => item.IsDefault).Name; @@ -230,15 +235,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"]; } } } From ea5655ae42730d6d912cb6075d95dacc3358a230 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sat, 4 Jun 2022 15:40:26 -0400 Subject: [PATCH 3/6] Improvements for #2221 - validate file extensions client-side before initiating upload, valid file extension server-side before writing part to disk, optimize cleanup logic, add error handling to JavaScript XMLHttpRequest, ensure FileInput gets initialized after upload --- .../Modules/Controls/FileManager.razor | 491 +++++++++--------- .../Modules/Controls/FileManager.resx | 3 + Oqtane.Server/Controllers/FileController.cs | 39 +- Oqtane.Server/wwwroot/js/interop.js | 9 +- 4 files changed, 284 insertions(+), 258 deletions(-) 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/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.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index e13f770c..eca5125f 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -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(); 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 () { From aba3d58df825be816dc6ea62769d7b16c3d30d31 Mon Sep 17 00:00:00 2001 From: isaeed Date: Mon, 6 Jun 2022 10:12:00 +0500 Subject: [PATCH 4/6] missing translation keys for module names --- Oqtane.Client/Resources/SharedResources.resx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Oqtane.Client/Resources/SharedResources.resx b/Oqtane.Client/Resources/SharedResources.resx index 4d41d1a6..33a57601 100644 --- a/Oqtane.Client/Resources/SharedResources.resx +++ b/Oqtane.Client/Resources/SharedResources.resx @@ -330,4 +330,10 @@ Page {0} of {1} + + Url Mappings + + + Visitor Management + \ No newline at end of file From 35e00f61d847e1705d4112395a147d11e2602abd Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 7 Jun 2022 09:38:42 -0400 Subject: [PATCH 5/6] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4dea0f4d..ee693ad0 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,11 @@ 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 From 79c8126c4a9f1148e26a1b2d034197f97360e61b Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 7 Jun 2022 15:25:44 -0400 Subject: [PATCH 6/6] Fix #2230 - add support for an Unauthenticated User global role --- Oqtane.Client/Modules/Admin/Roles/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Users/Roles.razor | 4 +++- .../Modules/Controls/PermissionGrid.razor | 8 ++++---- Oqtane.Server/Infrastructure/UpgradeManager.cs | 13 +++++++++++++ Oqtane.Server/Repository/SiteRepository.cs | 14 ++++++++------ Oqtane.Shared/Security/UserSecurity.cs | 5 ++++- Oqtane.Shared/Shared/Constants.cs | 4 ++-- Oqtane.Shared/Shared/RoleNames.cs | 3 ++- 8 files changed, 37 insertions(+), 16 deletions(-) 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/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/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.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/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.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"; } }