From 6a92c9f7645faf950fc1a93731c1898c07039fc5 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 11 Feb 2020 14:25:38 -0500 Subject: [PATCH] Folder and file management service --- Oqtane.Client/Modules/Admin/Files/Add.razor | 128 +++++--- Oqtane.Client/Modules/Admin/Files/Edit.razor | 178 ++++++++--- Oqtane.Client/Modules/Admin/Files/Index.razor | 91 ++++-- .../Modules/Admin/ModuleDefinitions/Add.razor | 52 +--- Oqtane.Client/Modules/Admin/Pages/Add.razor | 2 +- Oqtane.Client/Modules/Admin/Sites/Add.razor | 11 +- Oqtane.Client/Modules/Admin/Sites/Edit.razor | 37 ++- Oqtane.Client/Modules/Admin/Themes/Add.razor | 52 +--- .../Modules/Admin/Upgrade/Index.razor | 54 +--- .../Modules/Admin/UserProfile/Index.razor | 35 ++- Oqtane.Client/Modules/Admin/Users/Add.razor | 15 + Oqtane.Client/Modules/Admin/Users/Edit.razor | 28 ++ .../Modules/Controls/FileManager.razor | 285 ++++++++++++++++++ .../Modules/Controls/FileUpload.razor | 54 ---- .../Modules/Controls/PermissionGrid.razor | 25 +- Oqtane.Client/Modules/ModuleBase.cs | 11 + Oqtane.Client/Services/FileService.cs | 53 +++- .../Services/Interfaces/IFileService.cs | 12 +- Oqtane.Client/Shared/Installer.razor | 6 +- Oqtane.Client/Shared/Interop.cs | 8 +- Oqtane.Client/Startup.cs | 3 +- .../Themes/Controls/ControlPanel.razor | 2 +- Oqtane.Client/Themes/Controls/Logo.razor | 4 +- Oqtane.Client/Themes/ThemeControlBase.cs | 11 + Oqtane.Client/wwwroot/js/interop.js | 12 +- .../Tenants/1/Sites/1/logo.png} | Bin Oqtane.Server/Controllers/FileController.cs | 192 ++++++++++-- Oqtane.Server/Controllers/FolderController.cs | 12 + Oqtane.Server/Controllers/SiteController.cs | 5 - Oqtane.Server/Controllers/UserController.cs | 18 +- Oqtane.Server/Repository/FileRepository.cs | 8 +- Oqtane.Server/Repository/FolderRepository.cs | 13 +- .../Interfaces/IFolderRepository.cs | 1 + Oqtane.Server/Repository/JobRepository.cs | 11 +- .../Repository/PageModuleRepository.cs | 4 +- Oqtane.Server/Repository/PageRepository.cs | 4 +- Oqtane.Server/Repository/SiteRepository.cs | 17 +- Oqtane.Server/Scripts/00.00.00.sql | 10 +- Oqtane.Server/Scripts/Master.sql | 2 +- Oqtane.Server/Startup.cs | 10 +- Oqtane.Server/wwwroot/js/interop.js | 12 +- Oqtane.Shared/Models/File.cs | 2 + Oqtane.Shared/Models/Folder.cs | 1 + Oqtane.Shared/Models/Site.cs | 2 +- Oqtane.Shared/Models/User.cs | 3 + 45 files changed, 1075 insertions(+), 421 deletions(-) create mode 100644 Oqtane.Client/Modules/Controls/FileManager.razor delete mode 100644 Oqtane.Client/Modules/Controls/FileUpload.razor rename Oqtane.Server/{wwwroot/Tenants/1/Sites/1/oqtane.png => Content/Tenants/1/Sites/1/logo.png} (100%) diff --git a/Oqtane.Client/Modules/Admin/Files/Add.razor b/Oqtane.Client/Modules/Admin/Files/Add.razor index 26feaf57..fa041fed 100644 --- a/Oqtane.Client/Modules/Admin/Files/Add.razor +++ b/Oqtane.Client/Modules/Admin/Files/Add.razor @@ -2,55 +2,109 @@ @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFileService FileService +@inject IFolderService FolderService - - - - - -
- - - -
- -Cancel +@if (folders != null) +{ +
+
+ + + +
+
+ + + + + +
+ + + +
+
+
+ + + + + + + + + +
+ + + +
+ + + +
+ +
+
+
+
+
+ Cancel +} @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - FileUpload fileupload; + string url = ""; + List folders; + int folderid = -1; - private async Task UploadFile() + protected override async Task OnInitializedAsync() { - string[] files = await fileupload.GetFiles(); - if (files.Length > 0) - { - try - { - ShowProgressIndicator(); + folders = await FolderService.GetFoldersAsync(ModuleState.SiteId); - string result = await FileService.UploadFilesAsync(PageState.Site.SiteRootPath, files, ""); - if (result == "") - { - await logger.LogInformation("Files Uploaded Successfully"); - AddModuleMessage("Files Uploaded Successfully", MessageType.Success); - } - else - { - await logger.LogError("Upload Failed For {Files}", result.Replace(",",", ")); - AddModuleMessage("Upload Failed For " + result.Replace(",",", ") + ". This Could Be Due To A Network Error Or Because A File Type Is Restricted.", MessageType.Error); - } - } - catch (Exception ex) + if (PageState.QueryString.ContainsKey("id")) + { + folderid = int.Parse(PageState.QueryString["id"]); + } + } + + private async Task Download() + { + try + { + if (url != "" && folderid != -1) { - await logger.LogError(ex, "Upload Failed {Error}", ex.Message); - AddModuleMessage("Upload Failed. " + ex.Message, MessageType.Error); + await FileService.UploadFileAsync(url, folderid); + await logger.LogInformation("File Downloaded Successfully From Url {Url}", url); + AddModuleMessage("File Downloaded Successfully From Url", MessageType.Success); + } + else + { + AddModuleMessage("You Must Enter A Url And Select A Folder", MessageType.Warning); } } - else + catch (Exception ex) { - AddModuleMessage("You Must Select Some Files To Upload", MessageType.Warning); + await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", url, ex.Message); + AddModuleMessage("Error Downloading File From Url. Please Verify That The Url Is Valid.", MessageType.Error); } } } diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index 8ad0f1ca..52ebeff1 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -3,36 +3,70 @@ @inject IFolderService FolderService @inject NavigationManager NavigationManager - - - - - - - - - -
- - - -
- - - -
- -Cancel -
-
- +@if (folders != null) +{ + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ @if (!issystem) + { + + } + Cancel + @if (!issystem && PageState.QueryString.ContainsKey("id")) + { + + } +
+
+ @if (PageState.QueryString.ContainsKey("id")) + { + + } +} @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } + public override string Title { get { return "Folder Management"; } } - int FolderId; + List folders; + int folderid; string name; - string permissions; + int parentid = -1; + bool issystem = false; + string permissions = ""; string createdby; DateTime createdon; string modifiedby; @@ -44,22 +78,38 @@ { try { - FolderId = Int32.Parse(PageState.QueryString["id"]); - Folder folder = await FolderService.GetFolderAsync(FolderId); - if (folder != null) + folders = await FolderService.GetFoldersAsync(PageState.Site.SiteId); + + if (PageState.QueryString.ContainsKey("id")) { - name = folder.Name; - permissions = folder.Permissions; - createdby = folder.CreatedBy; - createdon = folder.CreatedOn; - modifiedby = folder.ModifiedBy; - modifiedon = folder.ModifiedOn; + folderid = Int32.Parse(PageState.QueryString["id"]); + Folder folder = await FolderService.GetFolderAsync(folderid); + if (folder != null) + { + parentid = (folder.ParentId == null) ? -1 : folder.ParentId.Value; + name = folder.Name; + issystem = folder.IsSystem; + permissions = folder.Permissions; + createdby = folder.CreatedBy; + createdon = folder.CreatedOn; + modifiedby = folder.ModifiedBy; + modifiedon = folder.ModifiedOn; + } + } + else + { + parentid = folders[0].FolderId; + List permissionstrings = new List(); + permissionstrings.Add(new PermissionString { PermissionName = "Browse", Permissions = Constants.AdminRole }); + permissionstrings.Add(new PermissionString { PermissionName = "View", Permissions = Constants.AdminRole }); + permissionstrings.Add(new PermissionString { PermissionName = "Edit", Permissions = Constants.AdminRole }); + permissions = UserSecurity.SetPermissionStrings(permissionstrings); } } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Folder {FolderId} {Error}", FolderId, ex.Message); - AddModuleMessage("Error Loading Module", MessageType.Error); + await logger.LogError(ex, "Error Loading Folder {FolderId} {Error}", folderid, ex.Message); + AddModuleMessage("Error Loading Folder", MessageType.Error); } } @@ -67,19 +117,67 @@ { try { - Folder folder = await FolderService.GetFolderAsync(FolderId); - if (folder != null) + if (name != "" && parentid != -1) { + Folder folder; + if (folderid != -1) + { + folder = await FolderService.GetFolderAsync(folderid); + } + else + { + folder = new Folder(); + } + + folder.SiteId = PageState.Site.SiteId; + if (parentid == -1) + { + folder.ParentId = null; + } + else + { + folder.ParentId = parentid; + } + folder.Name = name; + folder.IsSystem = issystem; folder.Permissions = permissiongrid.GetPermissions(); - await FolderService.UpdateFolderAsync(folder); + + if (folderid != -1) + { + folder = await FolderService.UpdateFolderAsync(folder); + } + else + { + folder = await FolderService.AddFolderAsync(folder); + } + await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); await logger.LogInformation("Folder Saved {Folder}", folder); NavigationManager.NavigateTo(NavigateUrl(Reload.Site)); } + else + { + AddModuleMessage("Folders Must Have A Parent And A Name", MessageType.Warning); + } } catch (Exception ex) { - await logger.LogError(ex, "Error Saving Folder {FolderId} {Error}", FolderId, ex.Message); + await logger.LogError(ex, "Error Saving Folder {FolderId} {Error}", folderid, ex.Message); AddModuleMessage("Error Saving Module", MessageType.Error); } } + + private async Task DeleteFolder() + { + try + { + await FolderService.DeleteFolderAsync(folderid); + await logger.LogInformation("Folder Deleted {Folder}", folderid); + AddModuleMessage("Folder Deleted", MessageType.Success); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting Folder {Folder} {Error}", folderid, ex.Message); + AddModuleMessage("Error Deleting Folder", MessageType.Error); + } + } } diff --git a/Oqtane.Client/Modules/Admin/Files/Index.razor b/Oqtane.Client/Modules/Admin/Files/Index.razor index 35269a70..677b117b 100644 --- a/Oqtane.Client/Modules/Admin/Files/Index.razor +++ b/Oqtane.Client/Modules/Admin/Files/Index.razor @@ -1,39 +1,71 @@ @namespace Oqtane.Modules.Admin.Files @inherits ModuleBase @inject NavigationManager NavigationManager +@inject IFolderService FolderService @inject IFileService FileService -@if (Files == null) +@if (Files != null) { -

Loading...

-} -else -{ - - + + + + + + +
+ + + + +   +   + +
  Name + Modified + Type + Size
- - @context + + @context.Name + @context.ModifiedOn + @context.Extension.ToUpper() File + @(context.Size / 1000) KB
+ @if (Files.Count == 0) + { +
No Files Exist In Selected Folder
+ } } @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - List Files; + List Folders; + int folderid; + List Files; Uri uri; protected override async Task OnParametersSetAsync() { try { - Files = await FileService.GetFilesAsync(PageState.Site.SiteRootPath); + Folders = await FolderService.GetFoldersAsync(PageState.Site.SiteId); + if (Folders.Count > 0) + { + folderid = Folders[0].FolderId; + await GetFiles(); + } uri = new Uri(NavigationManager.Uri); } catch (Exception ex) @@ -43,19 +75,40 @@ else } } - private async Task DeleteFile(string filename) + private async Task GetFiles() + { + Files = await FileService.GetFilesAsync(folderid); + } + + private async void FolderChanged(ChangeEventArgs e) { try { - await FileService.DeleteFileAsync(PageState.Site.SiteRootPath, filename); - Files = await FileService.GetFilesAsync(PageState.Site.SiteRootPath); - await logger.LogInformation("File Deleted {File}", filename); - AddModuleMessage("File " + filename + " Deleted", MessageType.Success); + folderid = int.Parse((string)e.Value); + await GetFiles(); + StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Deleting File {File} {Error}", filename, ex.Message); - AddModuleMessage("Error Deleting File " + filename, MessageType.Error); + await logger.LogError(ex, "Error Loading Files {Error}", ex.Message); + AddModuleMessage("Error Loading Files", MessageType.Error); } } -} \ No newline at end of file + + private async Task DeleteFile(File file) + { + try + { + await FileService.DeleteFileAsync(file.FileId); + await logger.LogInformation("File Deleted {File}", file.Name); + AddModuleMessage("File " + file.Name + " Deleted", MessageType.Success); + await GetFiles(); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting File {File} {Error}", file.Name, ex.Message); + AddModuleMessage("Error Deleting File " + file.Name, MessageType.Error); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index e234650f..4e534cc7 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -11,11 +11,10 @@ - + - @if (packages != null) { @@ -38,19 +37,14 @@ } -@if (uploaded) -{ - -} + Cancel @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - bool uploaded = false; List packages; - FileUpload fileupload; protected override async Task OnInitializedAsync() { @@ -73,47 +67,6 @@ } } - private async Task UploadFile() - { - string[] files = await fileupload.GetFiles(); - if (files.Length > 0) - { - if (files[0].Contains(".Module.")) - { - try - { - string result = await FileService.UploadFilesAsync("Modules", files, ""); - if (result == "") - { - await logger.LogInformation("Module Uploaded Successfully {Package}", files[0]); - AddModuleMessage("Module Uploaded Successfully. Click Install To Complete Installation.", MessageType.Success); - uploaded = true; - StateHasChanged(); - } - else - { - await logger.LogError("Module Upload Failed For {Package}", files[0]); - AddModuleMessage("Module Upload Failed For " + result.Replace(",",", ") + ". This Could Be Due To A Network Error Or Because A File Type Is Restricted.", MessageType.Error); - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Module Upload Failed For {Package} {Error}", files[0], ex.Message); - AddModuleMessage("Module Upload Failed.", MessageType.Error); - } - } - else - { - await logger.LogError("Invalid Module Package {Package}", files[0]); - AddModuleMessage("Invalid Module Package", MessageType.Error); - } - } - else - { - AddModuleMessage("You Must Select A Module To Upload", MessageType.Warning); - } - } - private async Task InstallModules() { try @@ -134,7 +87,6 @@ await PackageService.DownloadPackageAsync(moduledefinitionname, version, "Modules"); await logger.LogInformation("Module {ModuleDefinitionName} {Version} Downloaded Successfully", moduledefinitionname, version); AddModuleMessage("Module Downloaded Successfully. Click Install To Complete Installation.", MessageType.Success); - uploaded = true; StateHasChanged(); } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index fe217df0..9b642b02 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -161,7 +161,7 @@ string themetype = ""; string layouttype = ""; string icon = ""; - string permissions = ""; // need to set default permissions + string permissions = ""; PermissionGrid permissiongrid; diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 5c1532cb..94face09 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -49,7 +49,7 @@ else - + @@ -129,7 +129,7 @@ else string tenantid = "-1"; string name = ""; string urls = ""; - string logo = ""; + FileManager filemanager; string themetype = ""; string layouttype = ""; string containertype = ""; @@ -223,7 +223,12 @@ else Site site = new Site(); site.TenantId = int.Parse(tenantid); site.Name = name; - site.Logo = (logo == null ? "" : logo); + site.LogoFileId = null; + int logofileid = filemanager.GetFileId(); + if (logofileid != -1) + { + site.LogoFileId = logofileid; + } site.DefaultThemeType = themetype; site.DefaultLayoutType = (layouttype == null ? "" : layouttype); site.DefaultContainerType = containertype; diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Sites/Edit.razor index 42c0e108..34fda2bf 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Edit.razor @@ -6,12 +6,8 @@ @inject IThemeService ThemeService @inject ISettingService SettingService -@if (themes == null) -{ -

Loading...

-} -else -{ +@if (themes != null) +{ @@ -157,16 +153,17 @@ else @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - Dictionary themes = new Dictionary(); - Dictionary panelayouts = new Dictionary(); - Dictionary containers = new Dictionary(); + Dictionary themes; + Dictionary panelayouts; + Dictionary containers; Alias Alias; int siteid; string name = ""; List aliases; string urls = ""; - string logo = ""; + int logofileid = -1; + FileManager filemanager; string themetype; string layouttype; string containertype; @@ -189,9 +186,6 @@ else { try { - themes = ThemeService.GetThemeTypes(PageState.Themes); - containers = ThemeService.GetContainerTypes(PageState.Themes); - Alias = PageState.Aliases.Where(item => item.AliasId == Int32.Parse(PageState.QueryString["id"])).FirstOrDefault(); siteid = Alias.SiteId; Site site = await SiteService.GetSiteAsync(siteid, Alias); @@ -203,7 +197,10 @@ else { urls += alias.Name + "\n"; } - logo = site.Logo; + if (site.LogoFileId != null) + { + logofileid = site.LogoFileId.Value; + } themetype = site.DefaultThemeType; panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes, themetype); layouttype = site.DefaultLayoutType; @@ -224,6 +221,9 @@ else deletedon = site.DeletedOn; isdeleted = site.IsDeleted.ToString(); } + + themes = ThemeService.GetThemeTypes(PageState.Themes); + containers = ThemeService.GetContainerTypes(PageState.Themes); } catch (Exception ex) { @@ -264,7 +264,12 @@ else if (site != null) { site.Name = name; - site.Logo = (logo == null ? "" : logo); + site.LogoFileId = null; + int logofileid = filemanager.GetFileId(); + if (logofileid != -1) + { + site.LogoFileId = logofileid; + } site.DefaultThemeType = themetype; site.DefaultLayoutType = (layouttype == null ? "" : layouttype); site.DefaultContainerType = containertype; diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor index bd243c9e..5ea8f8c5 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Add.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor @@ -11,11 +11,10 @@
@@ -34,7 +30,7 @@ else - +
- +
- @if (packages != null) { @@ -38,18 +37,13 @@ } -@if (uploaded) -{ - -} + Cancel @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - bool uploaded = false; List packages; - FileUpload fileupload; protected override async Task OnInitializedAsync() { @@ -64,47 +58,6 @@ } } - private async Task UploadTheme() - { - string[] files = await fileupload.GetFiles(); - if (files.Length > 0) - { - if (files[0].Contains(".Theme.")) - { - try - { - string result = await FileService.UploadFilesAsync("Themes", files, ""); - if (result == "") - { - await logger.LogInformation("Theme Uploaded {Package}", files[0]); - AddModuleMessage("Theme Uploaded Successfully. Click Install To Complete Installation.", MessageType.Success); - uploaded = true; - StateHasChanged(); - } - else - { - await logger.LogInformation("Theme Upload Failed For {Package}", result.Replace(",",", ")); - AddModuleMessage("Upload Failed For " + result.Replace(",",", ") + ". This Could Be Due To A Network Error Or Because A File Type Is Restricted.", MessageType.Error); - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Theme Upload Failed {Package} {Error}", files[0], ex.Message); - AddModuleMessage("Theme Upload Failed", MessageType.Error); - } - } - else - { - await logger.LogError("Invalid Theme Package {Package}", files[0]); - AddModuleMessage("Invalid Theme Package", MessageType.Error); - } - } - else - { - AddModuleMessage("You Must Select A Theme To Upload", MessageType.Warning); - } - } - private async Task InstallThemes() { await ThemeService.InstallThemesAsync(); @@ -115,7 +68,6 @@ { await PackageService.DownloadPackageAsync(packageid, version, "Themes"); AddModuleMessage("Theme Downloaded Successfully. Click Install To Complete Installation.", MessageType.Success); - uploaded = true; StateHasChanged(); } } diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor index 2ae44820..7448f435 100644 --- a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor +++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor @@ -11,18 +11,11 @@ - + -@if (uploaded) -{ - -} -else -{ - -} + @if (upgradeavailable) { @@ -35,9 +28,7 @@ else @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - bool uploaded = false; bool upgradeavailable = false; - FileUpload fileupload; protected override async Task OnInitializedAsync() { @@ -53,47 +44,6 @@ else } } - private async Task UploadFile() - { - string[] files = await fileupload.GetFiles(); - if (files.Length > 0) - { - if (files[0].Contains(".Framework.")) - { - try - { - string result = await FileService.UploadFilesAsync("Framework", files, ""); - if (result == "") - { - await logger.LogInformation("Framework Uploaded {Package}", files[0]); - AddModuleMessage("Framework Uploaded Successfully. Click Upgrade To Complete Installation.", MessageType.Success); - uploaded = true; - StateHasChanged(); - } - else - { - await logger.LogInformation("Framework Upload Failed For {Package}", result.Replace(",",", ")); - AddModuleMessage("Upload Failed For " + result.Replace(",",", ") + ". This Could Be Due To A Network Error Or Because A File Type Is Restricted.", MessageType.Error); - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Framework Upload Failed {Package} {Error}", files[0], ex.Message); - AddModuleMessage("Framework Upload Failed. " + ex.Message, MessageType.Error); - } - } - else - { - await logger.LogError("Invalid Framework Package {Package}", files[0]); - AddModuleMessage("Invalid Framework Package", MessageType.Error); - } - } - else - { - AddModuleMessage("You Must Select A Framework Package To Upload", MessageType.Warning); - } - } - private async Task Upgrade() { await InstallationService.Upgrade(); diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index b5d76c3c..e0d8cedd 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -26,7 +26,14 @@
-
+ @if (photofileid != -1) + { + @displayname + } + else + { +
+ } + + + + @foreach (Profile profile in profiles) { @@ -164,6 +179,8 @@ string confirm = ""; string email = ""; string displayname = ""; + FileManager filemanager; + int photofileid = -1; List profiles; Dictionary settings; string category = ""; @@ -179,6 +196,10 @@ username = PageState.User.Username; email = PageState.User.Email; displayname = PageState.User.DisplayName; + if (PageState.User.PhotoFileId != null) + { + photofileid = PageState.User.PhotoFileId.Value; + } profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId); await LoadNotificationsAsync(); @@ -210,7 +231,7 @@ { try { - if (password != "" && confirm != "" && email != "") + if (username != "" && email != "") { if (password == confirm) { @@ -219,11 +240,15 @@ user.Password = password; user.Email = email; user.DisplayName = (displayname == "" ? username : displayname); + user.PhotoFileId = null; + photofileid = filemanager.GetFileId(); + if (photofileid != -1) + { + user.PhotoFileId = photofileid; + } await UserService.UpdateUserAsync(user); await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId); await logger.LogInformation("User Profile Saved"); - - NavigationManager.NavigateTo(NavigateUrl("")); } else { @@ -232,7 +257,7 @@ } else { - AddModuleMessage("You Must Provide A Username, Password, and Email Address", MessageType.Warning); + AddModuleMessage("You Must Provide A Username and Email Address", MessageType.Warning); } } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor index a687cad4..781e958f 100644 --- a/Oqtane.Client/Modules/Admin/Users/Add.razor +++ b/Oqtane.Client/Modules/Admin/Users/Add.razor @@ -48,6 +48,14 @@ + + + + @foreach (Profile profile in profiles) { @@ -83,6 +91,7 @@ string confirm = ""; string email = ""; string displayname = ""; + FileManager filemanager; List profiles; Dictionary settings; string category = ""; @@ -115,6 +124,12 @@ user.Password = password; user.Email = email; user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; + user.PhotoFileId = null; + int photofileid = filemanager.GetFileId(); + if (photofileid != -1) + { + user.PhotoFileId = photofileid; + } user = await UserService.AddUserAsync(user); diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor index c3a005de..e8c40d2d 100644 --- a/Oqtane.Client/Modules/Admin/Users/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor @@ -7,6 +7,14 @@ @if (profiles != null) { + @if (photofileid != -1) + { + @displayname + } + else + { +
+ }
@@ -68,6 +75,14 @@
+ + + +
+ + + +
+ + + + @foreach (Profile profile in profiles) { @@ -98,6 +114,8 @@ string confirm = ""; string email = ""; string displayname = ""; + FileManager filemanager; + int photofileid = -1; List profiles; Dictionary settings; string category = ""; @@ -122,6 +140,10 @@ username = user.Username; email = user.Email; displayname = user.DisplayName; + if (user.PhotoFileId != null) + { + photofileid = user.PhotoFileId.Value; + } settings = await SettingService.GetUserSettingsAsync(user.UserId); createdby = user.CreatedBy; createdon = user.CreatedOn; @@ -158,6 +180,12 @@ user.Password = password; user.Email = email; user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; + user.PhotoFileId = null; + photofileid = filemanager.GetFileId(); + if (photofileid != -1) + { + user.PhotoFileId = photofileid; + } user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted)); user = await UserService.UpdateUserAsync(user); diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor new file mode 100644 index 00000000..d61e05c2 --- /dev/null +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -0,0 +1,285 @@ +@namespace Oqtane.Modules.Controls +@inherits ModuleBase +@inject IFolderService FolderService +@inject IFileService FileService +@inject IJSRuntime jsRuntime + +@if (folders != null) +{ + + @if (showfiles) + { + + } + @if (haseditpermission) + { +
+ @if (uploadmultiple) + { + + } + else + { + + } + + @if (showfiles && GetFileId() != -1) + { + + } + +
+ @((MarkupString)@message) + } +} + +@code { + [Parameter] + public string Folder { get; set; } // optional - for setting a specific folder by default + + [Parameter] + public string FolderId { get; set; } // optional - for setting a specific folderid by default + + [Parameter] + public string ShowFiles { get; set; } // optional - for indicating whether a list of files should be displayed - default is true + + [Parameter] + public string FileId { get; set; } // optional - for setting 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 UploadMultiple { get; set; } // optional - enable multiple file uploads - default false + + string id; + List folders; + int folderid = -1; + List files = new List(); + int fileid = -1; + bool showfiles = true; + string fileinputid = ""; + string progressinfoid = ""; + string progressbarid = ""; + string filter = "*"; + bool uploadmultiple = false; + bool haseditpermission = false; + string message = ""; + + protected override async Task OnInitializedAsync() + { + if (!string.IsNullOrEmpty(Folder)) + { + folders = new List(); + folders.Add(new Folder { FolderId = -1, Name = Folder }); + folderid = -1; + } + else + { + folders = await FolderService.GetFoldersAsync(ModuleState.SiteId); + if (!string.IsNullOrEmpty(FolderId)) + { + folderid = int.Parse(FolderId); + } + } + + if (!string.IsNullOrEmpty(FileId)) + { + fileid = int.Parse(FileId); + if (fileid != -1) + { + File file = await FileService.GetFileAsync(int.Parse(FileId)); + if (file != null) + { + folderid = file.FolderId; + } + } + } + if (!string.IsNullOrEmpty(ShowFiles)) + { + showfiles = bool.Parse(ShowFiles); + } + + if (!string.IsNullOrEmpty(Filter)) + { + filter = Filter; + } + + await GetFiles(); + + // create unique id for component + id = Guid.NewGuid().ToString("N"); + fileinputid = id + "FileInput"; + progressinfoid = id + "ProgressInfo"; + progressbarid = id + "ProgressBar"; + + if (!string.IsNullOrEmpty(UploadMultiple)) + { + uploadmultiple = bool.Parse(UploadMultiple); + } + } + + private async Task GetFiles() + { + haseditpermission = false; + if (!string.IsNullOrEmpty(Folder)) + { + haseditpermission = UserSecurity.IsAuthorized(PageState.User, Constants.HostRole); + files = await FileService.GetFilesAsync(Folder); + } + else + { + Folder folder = folders.Where(item => item.FolderId == folderid).FirstOrDefault(); + if (folder != null) + { + haseditpermission = UserSecurity.IsAuthorized(PageState.User, "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 void FolderChanged(ChangeEventArgs e) + { + message = ""; + try + { + folderid = int.Parse((string)e.Value); + await GetFiles(); + fileid = -1; + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Files {Error}", ex.Message); + message = "
Error Loading Files
"; + } + } + + private void FileChanged(ChangeEventArgs e) + { + message = ""; + fileid = int.Parse((string)e.Value); + StateHasChanged(); + } + + private async Task UploadFile() + { + var interop = new Interop(jsRuntime); + string[] upload = await interop.GetFiles(fileinputid); + if (upload.Length > 0) + { + try + { + string result; + if (!string.IsNullOrEmpty(Folder)) + { + result = await FileService.UploadFilesAsync(Folder, upload, id); + } + else + { + result = await FileService.UploadFilesAsync(folderid, upload, id); + } + if (result == "") + { + await logger.LogInformation("File Upload Succeeded {Files}", upload); + message = "
File Upload Succeeded
"; + await GetFiles(); + if (upload.Length == 1) + { + File file = files.Where(item => item.Name == upload[0]).FirstOrDefault(); + if (file != null) + { + fileid = file.FileId; + } + } + StateHasChanged(); + } + else + { + await logger.LogError("File Upload Failed For {Files}", result.Replace(",", ", ")); + message = "
File Upload Failed
"; + } + } + catch (Exception ex) + { + await logger.LogError(ex, "File Upload Failed {Error}", ex.Message); + message = "
File Upload Failed
"; + } + } + else + { + message = "
You Have Not Selected A File To Upload
"; + } + } + + private async Task DeleteFile() + { + message = ""; + try + { + await FileService.DeleteFileAsync(fileid); + await logger.LogInformation("File Deleted {File}", fileid); + message = "
File Deleted
"; + await GetFiles(); + fileid = -1; + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting File {File} {Error}", fileid, ex.Message); + message = "
Error Deleting File
"; + } + } + + public int GetFileId() + { + return fileid; + } + +} diff --git a/Oqtane.Client/Modules/Controls/FileUpload.razor b/Oqtane.Client/Modules/Controls/FileUpload.razor deleted file mode 100644 index 43f0228a..00000000 --- a/Oqtane.Client/Modules/Controls/FileUpload.razor +++ /dev/null @@ -1,54 +0,0 @@ -@namespace Oqtane.Modules.Controls -@inject IJSRuntime jsRuntime - -@if (multiple) -{ - -} -else -{ - -} - - -@code { - [Parameter] - public string Name { get; set; } // optional - can be used for managing multiple file upload controls on a page - - [Parameter] - public string Filter { get; set; } // optional - for restricting types of files that can be selected - - [Parameter] - public string Multiple { get; set; } // optional - enable multiple file uploads - - string fileid = ""; - string progressinfoid = ""; - string progressbarid = ""; - string filter = "*"; - string files = ""; - bool multiple = false; - - protected override void OnInitialized() - { - fileid = Name + "FileInput"; - progressinfoid = Name + "ProgressInfo"; - progressbarid = Name + "ProgressBar"; - - if (!string.IsNullOrEmpty(Filter)) - { - filter = Filter; - } - - if (!string.IsNullOrEmpty(Multiple)) - { - multiple = bool.Parse(Multiple); - } - } - - public async Task GetFiles() - { - var interop = new Interop(jsRuntime); - string[] files = await interop.GetFiles(fileid); - return files; - } -} diff --git a/Oqtane.Client/Modules/Controls/PermissionGrid.razor b/Oqtane.Client/Modules/Controls/PermissionGrid.razor index 2345eb37..fda498e1 100644 --- a/Oqtane.Client/Modules/Controls/PermissionGrid.razor +++ b/Oqtane.Client/Modules/Controls/PermissionGrid.razor @@ -107,22 +107,25 @@ { permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = "" }); } - foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions)) + if (Permissions != "") { - if (permissions.Find(item => item.PermissionName == permissionstring.PermissionName) != null) + foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions)) { - permissions[permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions; - } - if (permissionstring.Permissions.Contains("[")) - { - foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries)) + if (permissions.Find(item => item.PermissionName == permissionstring.PermissionName) != null) { - if (user.Contains("]")) + permissions[permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions; + } + if (permissionstring.Permissions.Contains("[")) + { + foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries)) { - int userid = int.Parse(user.Substring(0, user.IndexOf("]"))); - if (users.Where(item => item.UserId == userid).FirstOrDefault() == null) + if (user.Contains("]")) { - users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId)); + int userid = int.Parse(user.Substring(0, user.IndexOf("]"))); + if (users.Where(item => item.UserId == userid).FirstOrDefault() == null) + { + users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId)); + } } } } diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 397daa2b..c2ae550e 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -109,6 +109,17 @@ namespace Oqtane.Modules return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters); } + public string ContentUrl(int fileid) + { + string apiurl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/"; + if (PageState.Alias.Path == "") + { + apiurl += "~/"; + } + apiurl += "api/File/Download/" + fileid.ToString(); + return apiurl; + } + // user feedback methods public void AddModuleMessage(string message, MessageType type) { diff --git a/Oqtane.Client/Services/FileService.cs b/Oqtane.Client/Services/FileService.cs index 240bb144..f706756f 100644 --- a/Oqtane.Client/Services/FileService.cs +++ b/Oqtane.Client/Services/FileService.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using Oqtane.Models; using Oqtane.Shared; namespace Oqtane.Services @@ -28,17 +30,52 @@ namespace Oqtane.Services get { return CreateApiUrl(sitestate.Alias, NavigationManager.Uri, "File"); } } - public async Task> GetFilesAsync(string Folder) + public async Task> GetFilesAsync(int FolderId) { - return await http.GetJsonAsync>(apiurl + "?folder=" + Folder); + return await GetFilesAsync(FolderId.ToString()); } - public async Task UploadFilesAsync(string Folder, string[] Files, string FileUploadName) + public async Task> GetFilesAsync(string Folder) + { + return await http.GetJsonAsync>(apiurl + "?folder=" + Folder); + } + + public async Task GetFileAsync(int FileId) + { + return await http.GetJsonAsync(apiurl + "/" + FileId.ToString()); + } + + public async Task AddFileAsync(File File) + { + return await http.PostJsonAsync(apiurl, File); + } + + public async Task UpdateFileAsync(File File) + { + return await http.PutJsonAsync(apiurl + "/" + File.FileId.ToString(), File); + } + + public async Task DeleteFileAsync(int FileId) + { + await http.DeleteAsync(apiurl + "/" + FileId.ToString()); + } + + public async Task UploadFileAsync(string Url, int FolderId) + { + return await http.GetJsonAsync(apiurl + "/upload?url=" + WebUtility.UrlEncode(Url) + "&folderid=" + FolderId.ToString()); + } + + public async Task UploadFilesAsync(int FolderId, string[] Files, string Id) + { + return await UploadFilesAsync(FolderId.ToString(), Files, Id); + } + + public async Task UploadFilesAsync(string Folder, string[] Files, string Id) { string result = ""; var interop = new Interop(jsRuntime); - await interop.UploadFiles(apiurl + "/upload", Folder, FileUploadName); + await interop.UploadFiles(apiurl + "/upload", Folder, Id); // uploading files is asynchronous so we need to wait for the upload to complete bool success = false; @@ -48,13 +85,13 @@ namespace Oqtane.Services Thread.Sleep(2000); // wait 2 seconds result = ""; - List files = await GetFilesAsync(Folder); + List files = await GetFilesAsync(Folder); if (files.Count > 0) { success = true; foreach (string file in Files) { - if (!files.Contains(file)) + if (!files.Exists(item => item.Name == file)) { success = false; result += file + ","; @@ -71,9 +108,9 @@ namespace Oqtane.Services return result; } - public async Task DeleteFileAsync(string Folder, string File) + public async Task DownloadFileAsync(int FileId) { - await http.DeleteAsync(apiurl + "?folder=" + Folder + "&file=" + File); + return await http.GetByteArrayAsync(apiurl + "/download/" + FileId.ToString()); } } } diff --git a/Oqtane.Client/Services/Interfaces/IFileService.cs b/Oqtane.Client/Services/Interfaces/IFileService.cs index b840397b..2a6234b6 100644 --- a/Oqtane.Client/Services/Interfaces/IFileService.cs +++ b/Oqtane.Client/Services/Interfaces/IFileService.cs @@ -6,8 +6,16 @@ namespace Oqtane.Services { public interface IFileService { - Task> GetFilesAsync(string Folder); + Task> GetFilesAsync(int FolderId); + Task> GetFilesAsync(string Folder); + Task GetFileAsync(int FileId); + Task AddFileAsync(File File); + Task UpdateFileAsync(File File); + Task DeleteFileAsync(int FileId); + Task UploadFileAsync(string Url, int FolderId); + Task UploadFilesAsync(int FolderId, string[] Files, string FileUploadName); Task UploadFilesAsync(string Folder, string[] Files, string FileUploadName); - Task DeleteFileAsync(string Folder, string File); + Task DownloadFileAsync(int FileId); + } } diff --git a/Oqtane.Client/Shared/Installer.razor b/Oqtane.Client/Shared/Installer.razor index b4962476..477384f4 100644 --- a/Oqtane.Client/Shared/Installer.razor +++ b/Oqtane.Client/Shared/Installer.razor @@ -98,7 +98,7 @@
@@ -48,6 +56,14 @@
+ + + +
- + @@ -142,7 +142,7 @@ private void SetIntegratedSecurity(ChangeEventArgs e) { - if (Convert.ToBoolean(e.Value)) + if (Convert.ToBoolean((string)e.Value)) { IntegratedSecurityDisplay = "display: none;"; } @@ -183,7 +183,7 @@ Site site = new Site(); site.TenantId = -1; // will be populated on server site.Name = "Default Site"; - site.Logo = "oqtane.png"; + site.LogoFileId = null; site.DefaultThemeType = Constants.DefaultTheme; site.DefaultLayoutType = Constants.DefaultLayout; site.DefaultContainerType = Constants.DefaultContainer; diff --git a/Oqtane.Client/Shared/Interop.cs b/Oqtane.Client/Shared/Interop.cs index f5bc53cc..0a21512b 100644 --- a/Oqtane.Client/Shared/Interop.cs +++ b/Oqtane.Client/Shared/Interop.cs @@ -87,13 +87,13 @@ namespace Oqtane.Shared } } - public ValueTask GetFiles(string name) + public ValueTask GetFiles(string id) { try { return jsRuntime.InvokeAsync( "interop.getFiles", - name); + id); } catch { @@ -101,13 +101,13 @@ namespace Oqtane.Shared } } - public Task UploadFiles(string posturl, string folder, string name) + public Task UploadFiles(string posturl, string folder, string id) { try { jsRuntime.InvokeAsync( "interop.uploadFiles", - posturl, folder, name); + posturl, folder, id); return Task.CompletedTask; } catch diff --git a/Oqtane.Client/Startup.cs b/Oqtane.Client/Startup.cs index b5c57b17..817e36fa 100644 --- a/Oqtane.Client/Startup.cs +++ b/Oqtane.Client/Startup.cs @@ -51,12 +51,13 @@ namespace Oqtane.Client services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); // dynamically register module contexts and repository services Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index 3747108b..9a5e2198 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -263,7 +263,7 @@ } else { - moduledefinitions = PageState.ModuleDefinitions.Where(item => item.Categories.Contains(e.Value.ToString())).ToList(); + moduledefinitions = PageState.ModuleDefinitions.Where(item => item.Categories.Contains(category)).ToList(); } moduledefinitionname = "-"; StateHasChanged(); diff --git a/Oqtane.Client/Themes/Controls/Logo.razor b/Oqtane.Client/Themes/Controls/Logo.razor index 00ab7f89..afe78f5e 100644 --- a/Oqtane.Client/Themes/Controls/Logo.razor +++ b/Oqtane.Client/Themes/Controls/Logo.razor @@ -9,10 +9,10 @@ protected override void OnParametersSet() { - if (PageState.Site.Logo != "") + if (PageState.Site.LogoFileId != null) { Uri uri = new Uri(NavigationManager.Uri); - logo = "\"""; + logo = "\"""; } } } \ No newline at end of file diff --git a/Oqtane.Client/Themes/ThemeControlBase.cs b/Oqtane.Client/Themes/ThemeControlBase.cs index 014cd5b6..6e3777dc 100644 --- a/Oqtane.Client/Themes/ThemeControlBase.cs +++ b/Oqtane.Client/Themes/ThemeControlBase.cs @@ -52,5 +52,16 @@ namespace Oqtane.Themes { return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters); } + + public string ContentUrl(int fileid) + { + string apiurl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/"; + if (PageState.Alias.Path == "") + { + apiurl += "~/"; + } + apiurl += "api/File/Download/" + fileid.ToString(); + return apiurl; + } } } diff --git a/Oqtane.Client/wwwroot/js/interop.js b/Oqtane.Client/wwwroot/js/interop.js index 96ddf060..f2a2421f 100644 --- a/Oqtane.Client/wwwroot/js/interop.js +++ b/Oqtane.Client/wwwroot/js/interop.js @@ -62,9 +62,9 @@ window.interop = { document.body.appendChild(form); form.submit(); }, - getFiles: function (name) { + getFiles: function (id) { var files = []; - var fileinput = document.getElementById(name); + var fileinput = document.getElementById(id); if (fileinput !== null) { for (var i = 0; i < fileinput.files.length; i++) { files.push(fileinput.files[i].name); @@ -72,10 +72,10 @@ window.interop = { } return files; }, - uploadFiles: function (posturl, folder, name) { - var files = document.getElementById(name + 'FileInput').files; - var progressinfo = document.getElementById(name + 'ProgressInfo'); - var progressbar = document.getElementById(name + 'ProgressBar'); + uploadFiles: function (posturl, folder, id) { + var files = document.getElementById(id + 'FileInput').files; + var progressinfo = document.getElementById(id + 'ProgressInfo'); + var progressbar = document.getElementById(id + 'ProgressBar'); var filename = ''; for (var i = 0; i < files.length; i++) { diff --git a/Oqtane.Server/wwwroot/Tenants/1/Sites/1/oqtane.png b/Oqtane.Server/Content/Tenants/1/Sites/1/logo.png similarity index 100% rename from Oqtane.Server/wwwroot/Tenants/1/Sites/1/oqtane.png rename to Oqtane.Server/Content/Tenants/1/Sites/1/logo.png diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 72bf7bcb..eeea4998 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -3,12 +3,16 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Oqtane.Infrastructure; +using Oqtane.Repository; +using Oqtane.Models; using Oqtane.Shared; using System; using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; +using Oqtane.Security; +using System.Linq; namespace Oqtane.Controllers { @@ -16,53 +20,163 @@ namespace Oqtane.Controllers public class FileController : Controller { private readonly IWebHostEnvironment environment; + private readonly IFileRepository Files; + private readonly IFolderRepository Folders; + private readonly IUserPermissions UserPermissions; + private readonly ITenantResolver Tenants; private readonly ILogManager logger; private readonly string WhiteList = "jpg,jpeg,jpe,gif,bmp,png,mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg"; - public FileController(IWebHostEnvironment environment, ILogManager logger) + public FileController(IWebHostEnvironment environment, IFileRepository Files, IFolderRepository Folders, IUserPermissions UserPermissions, ITenantResolver Tenants, ILogManager logger) { this.environment = environment; + this.Files = Files; + this.Folders = Folders; + this.UserPermissions = UserPermissions; + this.Tenants = Tenants; this.logger = logger; } // GET: api/?folder=x [HttpGet] - public IEnumerable Get(string folder) + public IEnumerable Get(string folder) { - List files = new List(); - folder = GetFolder(folder); - if (Directory.Exists(folder)) + List files = new List(); + int folderid; + if (int.TryParse(folder, out folderid)) { - foreach (string file in Directory.GetFiles(folder)) + Folder Folder = Folders.GetFolder(folderid); + if (Folder != null && UserPermissions.IsAuthorized(User, "Browse", Folder.Permissions)) { - files.Add(Path.GetFileName(file)); + files = Files.GetFiles(folderid).ToList(); + } + } + else + { + if (User.IsInRole(Constants.HostRole)) + { + folder = GetFolderPath(folder); + if (Directory.Exists(folder)) + { + foreach (string file in Directory.GetFiles(folder)) + { + files.Add(new Models.File { Name = Path.GetFileName(file), Extension = Path.GetExtension(file).Replace(".","") }); + } + } } } return files; } + // GET api//5 + [HttpGet("{id}")] + public Models.File Get(int id) + { + return Files.GetFile(id); + } + + // PUT api//5 + [HttpPut("{id}")] + [Authorize(Roles = Constants.RegisteredRole)] + public Models.File Put(int id, [FromBody] Models.File File) + { + if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Folder", File.Folder.FolderId, "Edit")) + { + File = Files.UpdateFile(File); + logger.Log(LogLevel.Information, this, LogFunction.Update, "File Updated {File}", File); + } + return File; + } + + // DELETE api//5 + [HttpDelete("{id}")] + [Authorize(Roles = Constants.RegisteredRole)] + public void Delete(int id) + { + Models.File File = Files.GetFile(id); + if (UserPermissions.IsAuthorized(User, "Folder", File.Folder.FolderId, "Edit")) + { + Files.DeleteFile(id); + + string filepath = Path.Combine(GetFolderPath(File.Folder) + File.Name); + if (System.IO.File.Exists(filepath)) + { + System.IO.File.Delete(filepath); + } + logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", File); + } + } + + // GET api//upload?url=x&folderid=y + [HttpGet("upload")] + public Models.File UploadFile(string url, string folderid) + { + Models.File file = null; + Folder folder = Folders.GetFolder(int.Parse(folderid)); + if (folder != null && UserPermissions.IsAuthorized(User, "Edit", folder.Permissions)) + { + string folderpath = GetFolderPath(folder); + CreateDirectory(folderpath); + string filename = url.Substring(url.LastIndexOf("/") + 1); + try + { + var client = new System.Net.WebClient(); + client.DownloadFile(url, folderpath + filename); + FileInfo fileinfo = new FileInfo(folderpath + filename); + file = Files.AddFile(new Models.File { Name = filename, FolderId = folder.FolderId, Extension = fileinfo.Extension.Replace(".",""), Size = (int)fileinfo.Length }); + } + catch + { + logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url}", url); + } + } + return file; + } + // POST api//upload [HttpPost("upload")] - [Authorize(Roles = Constants.AdminRole)] public async Task UploadFile(string folder, IFormFile file) { if (file.Length > 0) { - folder = GetFolder(folder); - if (!Directory.Exists(folder)) + string folderpath = ""; + int folderid = -1; + if (int.TryParse(folder, out folderid)) { - Directory.CreateDirectory(folder); + Folder Folder = Folders.GetFolder(folderid); + if (Folder != null && UserPermissions.IsAuthorized(User, "Edit", Folder.Permissions)) + { + folderpath = GetFolderPath(Folder); + } } - using (var stream = new FileStream(Path.Combine(folder, file.FileName), FileMode.Create)) + else { - await file.CopyToAsync(stream); + if (User.IsInRole(Constants.HostRole)) + { + folderpath = GetFolderPath(folder); + } + } + if (folderpath != "") + { + CreateDirectory(folderpath); + using (var stream = new FileStream(Path.Combine(folderpath, file.FileName), FileMode.Create)) + { + await file.CopyToAsync(stream); + } + string upload = await MergeFile(folderpath, file.FileName); + if (upload != "" && folderid != -1) + { + FileInfo fileinfo = new FileInfo(folderpath + upload); + Files.AddFile(new Models.File { Name = upload, FolderId = folderid, Extension = fileinfo.Extension.Replace(".", ""), Size = (int)fileinfo.Length }); + } } - await MergeFile(folder, file.FileName); } } - private async Task MergeFile(string folder, string filename) + private async Task MergeFile(string folder, string filename) { + string merged = ""; + // parse the filename which is in the format of filename.ext.part_x_y string token = ".part_"; string parts = Path.GetExtension(filename).Replace(token, ""); // returns "x_y" @@ -112,6 +226,7 @@ namespace Oqtane.Controllers 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; } } @@ -125,6 +240,8 @@ namespace Oqtane.Controllers System.IO.File.Delete(filepart); } } + + return merged; } private bool CanAccessFiles(string[] files) @@ -164,24 +281,47 @@ namespace Oqtane.Controllers return canaccess; } - // DELETE api//?folder=x&file=y - [HttpDelete] - [Authorize(Roles = Constants.AdminRole)] - public void Delete(string folder, string file) + // GET api//download/5 + [HttpGet("download/{id}")] + public IActionResult Download(int id) { - file = Path.Combine(GetFolder(folder) + file); - if (System.IO.File.Exists(file)) + Models.File file = Files.GetFile(id); + if (file != null && UserPermissions.IsAuthorized(User, "View", file.Folder.Permissions)) { - System.IO.File.Delete(file); - logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", file); + byte[] filebytes = System.IO.File.ReadAllBytes(GetFolderPath(file.Folder) + file.Name); + return File(filebytes, "application/octet-stream", file.Name); + } + else + { + return NotFound(); } } - private string GetFolder(string folder) + private string GetFolderPath(Folder folder) + { + return environment.ContentRootPath + "\\Content\\Tenants\\" + Tenants.GetTenant().TenantId.ToString() + "\\Sites\\" + folder.SiteId.ToString() + "\\" + folder.Path; + } + + private string GetFolderPath(string folder) { - folder = folder.Replace("/", "\\"); - if (folder.StartsWith("\\")) folder = folder.Substring(1); return Path.Combine(environment.WebRootPath, folder); } + + private void CreateDirectory(string folderpath) + { + if (!Directory.Exists(folderpath)) + { + string path = ""; + string[] folders = folderpath.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string folder in folders) + { + path += folder + "\\"; + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + } + } + } } } \ No newline at end of file diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index 46bae326..2bc4bc90 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -52,6 +52,12 @@ namespace Oqtane.Controllers { if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Edit", Folder.Permissions)) { + Folder.Path = ""; + if (string.IsNullOrEmpty(Folder.Path) && Folder.ParentId != null) + { + Folder parent = Folders.GetFolder(Folder.ParentId.Value); + Folder.Path = parent.Path + Folder.Name + "\\"; + } Folder = Folders.AddFolder(Folder); logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", Folder); } @@ -65,6 +71,12 @@ namespace Oqtane.Controllers { if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Folder", Folder.FolderId, "Edit")) { + Folder.Path = ""; + if (string.IsNullOrEmpty(Folder.Path) && Folder.ParentId != null) + { + Folder parent = Folders.GetFolder(Folder.ParentId.Value); + Folder.Path = parent.Path + Folder.Name + "\\"; + } Folder = Folders.UpdateFolder(Folder); logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", Folder); } diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index 6d220188..316a7ba3 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -62,11 +62,6 @@ namespace Oqtane.Controllers if (authorized) { Site = Sites.AddSite(Site); - string folder = environment.WebRootPath + "\\Tenants\\" + Tenants.GetTenant().TenantId.ToString() + "\\Sites\\" + Site.SiteId.ToString(); - if (!Directory.Exists(folder)) - { - Directory.CreateDirectory(folder); - } logger.Log(LogLevel.Information, this, LogFunction.Create, "Site Added {Site}", Site); } } diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 37038c9a..fb720264 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -26,9 +26,10 @@ namespace Oqtane.Controllers private readonly SignInManager IdentitySignInManager; private readonly ITenantResolver Tenants; private readonly INotificationRepository Notifications; + private readonly IFolderRepository Folders; private readonly ILogManager logger; - public UserController(IUserRepository Users, IRoleRepository Roles, IUserRoleRepository UserRoles, UserManager IdentityUserManager, SignInManager IdentitySignInManager, ITenantResolver Tenants, INotificationRepository Notifications, ILogManager logger) + public UserController(IUserRepository Users, IRoleRepository Roles, IUserRoleRepository UserRoles, UserManager IdentityUserManager, SignInManager IdentitySignInManager, ITenantResolver Tenants, INotificationRepository Notifications, IFolderRepository Folders, ILogManager logger) { this.Users = Users; this.Roles = Roles; @@ -36,12 +37,14 @@ namespace Oqtane.Controllers this.IdentityUserManager = IdentityUserManager; this.IdentitySignInManager = IdentitySignInManager; this.Tenants = Tenants; + this.Folders = Folders; this.Notifications = Notifications; this.logger = logger; } // GET: api/?siteid=x [HttpGet] + [Authorize(Roles = Constants.AdminRole)] public IEnumerable Get() { return Users.GetUsers(); @@ -98,6 +101,8 @@ namespace Oqtane.Controllers var result = await IdentityUserManager.CreateAsync(identityuser, User.Password); if (result.Succeeded) { + User.LastLoginOn = null; + User.LastIPAddress = ""; user = Users.AddUser(User); if (!verified) { @@ -128,6 +133,14 @@ namespace Oqtane.Controllers userrole.ExpiryDate = null; UserRoles.AddUserRole(userrole); } + + // add folder for user + Folder folder = Folders.GetFolder(User.SiteId, "Users\\"); + if (folder != null) + { + Folders.AddFolder(new Folder { SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", Path = folder.Path + user.UserId.ToString() + "\\", Order = 1, IsSystem = true, + Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + user.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + user.UserId.ToString() + "]\"}]" }); + } } } else @@ -222,6 +235,9 @@ namespace Oqtane.Controllers if (identityuser.EmailConfirmed) { user.IsAuthenticated = true; + user.LastLoginOn = DateTime.Now; + user.LastIPAddress = HttpContext.Connection.RemoteIpAddress.ToString(); + Users.UpdateUser(user); logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", User.Username); if (SetCookie) { diff --git a/Oqtane.Server/Repository/FileRepository.cs b/Oqtane.Server/Repository/FileRepository.cs index 1b85840f..3d51b17d 100644 --- a/Oqtane.Server/Repository/FileRepository.cs +++ b/Oqtane.Server/Repository/FileRepository.cs @@ -18,8 +18,8 @@ namespace Oqtane.Repository public IEnumerable GetFiles(int FolderId) { - IEnumerable permissions = Permissions.GetPermissions("Folder", FolderId); - IEnumerable files = db.File.Where(item => item.FolderId == FolderId); + IEnumerable permissions = Permissions.GetPermissions("Folder", FolderId).ToList(); + IEnumerable files = db.File.Where(item => item.FolderId == FolderId).Include(item => item.Folder); foreach (File file in files) { file.Folder.Permissions = Permissions.EncodePermissions(FolderId, permissions); @@ -43,10 +43,10 @@ namespace Oqtane.Repository public File GetFile(int FileId) { - File file = db.File.Find(FileId); + File file = db.File.Where(item => item.FileId == FileId).Include(item => item.Folder).FirstOrDefault(); if (file != null) { - IEnumerable permissions = Permissions.GetPermissions("Folder", file.FolderId); + IEnumerable permissions = Permissions.GetPermissions("Folder", file.FolderId).ToList(); file.Folder.Permissions = Permissions.EncodePermissions(file.FolderId, permissions); } return file; diff --git a/Oqtane.Server/Repository/FolderRepository.cs b/Oqtane.Server/Repository/FolderRepository.cs index 577639b6..3eb81471 100644 --- a/Oqtane.Server/Repository/FolderRepository.cs +++ b/Oqtane.Server/Repository/FolderRepository.cs @@ -53,7 +53,18 @@ namespace Oqtane.Repository Folder folder = db.Folder.Find(FolderId); if (folder != null) { - IEnumerable permissions = Permissions.GetPermissions("Folder", folder.FolderId); + IEnumerable permissions = Permissions.GetPermissions("Folder", folder.FolderId).ToList(); + folder.Permissions = Permissions.EncodePermissions(folder.FolderId, permissions); + } + return folder; + } + + public Folder GetFolder(int SiteId, string Path) + { + Folder folder = db.Folder.Where(item => item.SiteId == SiteId && item.Path == Path).FirstOrDefault(); + if (folder != null) + { + IEnumerable permissions = Permissions.GetPermissions("Folder", folder.FolderId).ToList(); folder.Permissions = Permissions.EncodePermissions(folder.FolderId, permissions); } return folder; diff --git a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs index d5619331..0d809ef1 100644 --- a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs @@ -10,6 +10,7 @@ namespace Oqtane.Repository Folder AddFolder(Folder Folder); Folder UpdateFolder(Folder Folder); Folder GetFolder(int FolderId); + Folder GetFolder(int SiteId, string Path); void DeleteFolder(int FolderId); } } diff --git a/Oqtane.Server/Repository/JobRepository.cs b/Oqtane.Server/Repository/JobRepository.cs index 46ffaba4..6c390355 100644 --- a/Oqtane.Server/Repository/JobRepository.cs +++ b/Oqtane.Server/Repository/JobRepository.cs @@ -3,21 +3,28 @@ using System.Linq; using Oqtane.Models; using Microsoft.EntityFrameworkCore; using System; +using Microsoft.Extensions.Caching.Memory; namespace Oqtane.Repository { public class JobRepository : IJobRepository { private MasterDBContext db; + private readonly IMemoryCache _cache; - public JobRepository(MasterDBContext context) + public JobRepository(MasterDBContext context, IMemoryCache cache) { db = context; + _cache = cache; } public IEnumerable GetJobs() { - return db.Job.ToList(); + return _cache.GetOrCreate("jobs", entry => + { + entry.SlidingExpiration = TimeSpan.FromMinutes(30); + return db.Job.ToList(); + }); } public Job AddJob(Job Job) diff --git a/Oqtane.Server/Repository/PageModuleRepository.cs b/Oqtane.Server/Repository/PageModuleRepository.cs index 92ebb732..730d3c4c 100644 --- a/Oqtane.Server/Repository/PageModuleRepository.cs +++ b/Oqtane.Server/Repository/PageModuleRepository.cs @@ -56,7 +56,7 @@ namespace Oqtane.Repository .SingleOrDefault(item => item.PageModuleId == PageModuleId); if (pagemodule != null) { - IEnumerable permissions = Permissions.GetPermissions("Module", pagemodule.ModuleId); + IEnumerable permissions = Permissions.GetPermissions("Module", pagemodule.ModuleId).ToList(); pagemodule.Module.Permissions = Permissions.EncodePermissions(pagemodule.ModuleId, permissions); } return pagemodule; @@ -68,7 +68,7 @@ namespace Oqtane.Repository .SingleOrDefault(item => item.PageId == PageId && item.ModuleId == ModuleId); if (pagemodule != null) { - IEnumerable permissions = Permissions.GetPermissions("Module", pagemodule.ModuleId); + IEnumerable permissions = Permissions.GetPermissions("Module", pagemodule.ModuleId).ToList(); pagemodule.Module.Permissions = Permissions.EncodePermissions(pagemodule.ModuleId, permissions); } return pagemodule; diff --git a/Oqtane.Server/Repository/PageRepository.cs b/Oqtane.Server/Repository/PageRepository.cs index 8ec9d1fd..6e4fc2ff 100644 --- a/Oqtane.Server/Repository/PageRepository.cs +++ b/Oqtane.Server/Repository/PageRepository.cs @@ -55,7 +55,7 @@ namespace Oqtane.Repository Page page = db.Page.Find(PageId); if (page != null) { - IEnumerable permissions = Permissions.GetPermissions("Page", page.PageId); + IEnumerable permissions = Permissions.GetPermissions("Page", page.PageId).ToList(); page.Permissions = Permissions.EncodePermissions(page.PageId, permissions); } return page; @@ -73,7 +73,7 @@ namespace Oqtane.Repository } if (page != null) { - IEnumerable permissions = Permissions.GetPermissions("Page", page.PageId); + IEnumerable permissions = Permissions.GetPermissions("Page", page.PageId).ToList(); page.Permissions = Permissions.EncodePermissions(page.PageId, permissions); } } diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index c1233b5d..d206c763 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -15,6 +15,8 @@ namespace Oqtane.Repository private readonly TenantDBContext db; private readonly IRoleRepository RoleRepository; private readonly IProfileRepository ProfileRepository; + private readonly IFolderRepository FolderRepository; + private readonly IFileRepository FileRepository; private readonly IPageRepository PageRepository; private readonly IModuleRepository ModuleRepository; private readonly IPageModuleRepository PageModuleRepository; @@ -22,11 +24,13 @@ namespace Oqtane.Repository private readonly IServiceProvider ServiceProvider; private readonly List SiteTemplate; - public SiteRepository(TenantDBContext context, IRoleRepository RoleRepository, IProfileRepository ProfileRepository, IPageRepository PageRepository, IModuleRepository ModuleRepository, IPageModuleRepository PageModuleRepository, IModuleDefinitionRepository ModuleDefinitionRepository, IServiceProvider ServiceProvider) + public SiteRepository(TenantDBContext context, IRoleRepository RoleRepository, IProfileRepository ProfileRepository, IFolderRepository FolderRepository, IFileRepository FileRepository, IPageRepository PageRepository, IModuleRepository ModuleRepository, IPageModuleRepository PageModuleRepository, IModuleDefinitionRepository ModuleDefinitionRepository, IServiceProvider ServiceProvider) { db = context; this.RoleRepository = RoleRepository; this.ProfileRepository = ProfileRepository; + this.FolderRepository = FolderRepository; + this.FileRepository = FileRepository; this.PageRepository = PageRepository; this.ModuleRepository = ModuleRepository; this.PageModuleRepository = PageModuleRepository; @@ -40,7 +44,7 @@ namespace Oqtane.Repository Content = "

Oqtane is an open source modular application framework built from the ground up using modern .NET Core technology. It leverages the revolutionary new Blazor component model to create a fully dynamic web development experience which can be executed on a client or server. Whether you are looking for a platform to accelerate your web development efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles.

" + "



Join Our Community  Clone Our Repo

" + "

Blazor is a single-page app framework that lets you build interactive web applications using C# instead of JavaScript. Client-side Blazor relies on WebAssembly, an open web standard that does not require plugins or code transpilation in order to run natively in a web browser. Server-side Blazor uses SignalR to host your application on a web server and provide a responsive and robust debugging experience. Blazor applications works in all modern web browsers, including mobile browsers.

" + - "

Blazor is a feature of ASP.NET Core 3.0, the popular cross platform web development framework from Microsoft that extends the .NET developer platform with tools and libraries for building web apps.

" + "

Blazor is a feature of ASP.NET Core 3, the popular cross platform web development framework from Microsoft that extends the .NET developer platform with tools and libraries for building web apps.

" }, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "

Copyright (c) 2019 .NET Foundation

" + @@ -168,6 +172,15 @@ namespace Oqtane.Repository ProfileRepository.AddProfile(new Profile { SiteId = site.SiteId, Name = "PostalCode", Title = "Postal Code", Description = "Postal Code Or Zip Code", Category = "Address", ViewOrder = 7, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false }); ProfileRepository.AddProfile(new Profile { SiteId = site.SiteId, Name = "Phone", Title = "Phone Number", Description = "Phone Number", Category = "Contact", ViewOrder = 8, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false }); + Folder folder = FolderRepository.AddFolder(new Folder { SiteId = site.SiteId, ParentId = null, Name = "Root", Path = "", Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" }); + FolderRepository.AddFolder(new Folder { SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Path = "Users\\", Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" }); + if (site.Name == "Default Site") + { + File file = FileRepository.AddFile(new File { FolderId = folder.FolderId, Name = "logo.png", Extension = "png", Size = 8192 }); + site.LogoFileId = file.FileId; + UpdateSite(site); + } + List moduledefinitions = ModuleDefinitionRepository.GetModuleDefinitions(site.SiteId).ToList(); foreach (PageTemplate pagetemplate in SiteTemplate) { diff --git a/Oqtane.Server/Scripts/00.00.00.sql b/Oqtane.Server/Scripts/00.00.00.sql index 93dc05f7..29637d45 100644 --- a/Oqtane.Server/Scripts/00.00.00.sql +++ b/Oqtane.Server/Scripts/00.00.00.sql @@ -8,7 +8,7 @@ CREATE TABLE [dbo].[Site]( [SiteId] [int] IDENTITY(1,1) NOT NULL, [TenantId] [int] NOT NULL, [Name] [nvarchar](200) NOT NULL, - [Logo] [nvarchar](50) NOT NULL, + [LogoFileId] [int] NULL, [DefaultThemeType] [nvarchar](200) NOT NULL, [DefaultLayoutType] [nvarchar](200) NOT NULL, [DefaultContainerType] [nvarchar](200) NOT NULL, @@ -96,6 +96,9 @@ CREATE TABLE [dbo].[User]( [Username] [nvarchar](256) NOT NULL, [DisplayName] [nvarchar](50) NOT NULL, [Email] [nvarchar](256) NOT NULL, + [PhotoFileId] [int] NULL, + [LastLoginOn] [datetime] NULL, + [LastIPAddress] [nvarchar](50) NOT NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, @@ -261,6 +264,7 @@ CREATE TABLE [dbo].[Folder]( [Name] [nvarchar](50) NOT NULL, [ParentId] [int] NULL, [Order] [int] NOT NULL, + [IsSystem] [bit] NOT NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, @@ -278,7 +282,9 @@ GO CREATE TABLE [dbo].[File]( [FileId] [int] IDENTITY(1,1) NOT NULL, [FolderId] [int] NOT NULL, - [Name] [nvarchar](50) NOT NULL, + [Name] [nvarchar](250) NOT NULL, + [Extension] [nvarchar](50) NOT NULL, + [Size] [int] NOT NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, diff --git a/Oqtane.Server/Scripts/Master.sql b/Oqtane.Server/Scripts/Master.sql index 0db09f12..45e474c7 100644 --- a/Oqtane.Server/Scripts/Master.sql +++ b/Oqtane.Server/Scripts/Master.sql @@ -139,7 +139,7 @@ GO SET IDENTITY_INSERT [dbo].[Job] ON GO INSERT [dbo].[Job] ([JobId], [Name], [JobType], [Frequency], [Interval], [StartDate], [EndDate], [IsEnabled], [IsStarted], [IsExecuting], [NextExecution], [RetentionHistory], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (1, N'Notification Job', N'Oqtane.Infrastructure.NotificationJob, Oqtane.Server', N'm', 1, null, null, 1, 0, 0, null, 10, '', getdate(), '', getdate()) +VALUES (1, N'Notification Job', N'Oqtane.Infrastructure.NotificationJob, Oqtane.Server', N'm', 1, null, null, 0, 0, 0, null, 10, '', getdate(), '', getdate()) GO SET IDENTITY_INSERT [dbo].[Job] OFF GO diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 3e65ec1c..2ea27dd4 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -82,6 +82,9 @@ namespace Oqtane.Server options.AddPolicy("EditPage", policy => policy.Requirements.Add(new PermissionRequirement("Page", "Edit"))); options.AddPolicy("ViewModule", policy => policy.Requirements.Add(new PermissionRequirement("Module", "View"))); options.AddPolicy("EditModule", policy => policy.Requirements.Add(new PermissionRequirement("Module", "Edit"))); + options.AddPolicy("ViewFolder", policy => policy.Requirements.Add(new PermissionRequirement("Folder", "View"))); + options.AddPolicy("EditFolder", policy => policy.Requirements.Add(new PermissionRequirement("Folder", "Edit"))); + options.AddPolicy("ListFolder", policy => policy.Requirements.Add(new PermissionRequirement("Folder", "List"))); }); // register scoped core services @@ -101,12 +104,13 @@ namespace Oqtane.Server services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddSingleton(); @@ -181,6 +185,8 @@ namespace Oqtane.Server services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddOqtaneModules(); services.AddOqtaneThemes(); @@ -329,6 +335,8 @@ namespace Oqtane.Server services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddOqtaneModules(); services.AddOqtaneThemes(); diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index 96ddf060..f2a2421f 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -62,9 +62,9 @@ window.interop = { document.body.appendChild(form); form.submit(); }, - getFiles: function (name) { + getFiles: function (id) { var files = []; - var fileinput = document.getElementById(name); + var fileinput = document.getElementById(id); if (fileinput !== null) { for (var i = 0; i < fileinput.files.length; i++) { files.push(fileinput.files[i].name); @@ -72,10 +72,10 @@ window.interop = { } return files; }, - uploadFiles: function (posturl, folder, name) { - var files = document.getElementById(name + 'FileInput').files; - var progressinfo = document.getElementById(name + 'ProgressInfo'); - var progressbar = document.getElementById(name + 'ProgressBar'); + uploadFiles: function (posturl, folder, id) { + var files = document.getElementById(id + 'FileInput').files; + var progressinfo = document.getElementById(id + 'ProgressInfo'); + var progressbar = document.getElementById(id + 'ProgressBar'); var filename = ''; for (var i = 0; i < files.length; i++) { diff --git a/Oqtane.Shared/Models/File.cs b/Oqtane.Shared/Models/File.cs index 55b8a637..44acbcbd 100644 --- a/Oqtane.Shared/Models/File.cs +++ b/Oqtane.Shared/Models/File.cs @@ -7,6 +7,8 @@ namespace Oqtane.Models public int FileId { get; set; } public int FolderId { get; set; } public string Name { get; set; } + public string Extension { get; set; } + public int Size { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } diff --git a/Oqtane.Shared/Models/Folder.cs b/Oqtane.Shared/Models/Folder.cs index c632eac4..5f04f4bb 100644 --- a/Oqtane.Shared/Models/Folder.cs +++ b/Oqtane.Shared/Models/Folder.cs @@ -11,6 +11,7 @@ namespace Oqtane.Models public string Name { get; set; } public string Path { get; set; } public int Order { get; set; } + public bool IsSystem { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } diff --git a/Oqtane.Shared/Models/Site.cs b/Oqtane.Shared/Models/Site.cs index dc7fbf35..439f5131 100644 --- a/Oqtane.Shared/Models/Site.cs +++ b/Oqtane.Shared/Models/Site.cs @@ -8,7 +8,7 @@ namespace Oqtane.Models public int SiteId { get; set; } public int TenantId { get; set; } public string Name { get; set; } - public string Logo { get; set; } + public int? LogoFileId { get; set; } public string DefaultThemeType { get; set; } public string DefaultLayoutType { get; set; } public string DefaultContainerType { get; set; } diff --git a/Oqtane.Shared/Models/User.cs b/Oqtane.Shared/Models/User.cs index bec8f3e2..e74deef2 100644 --- a/Oqtane.Shared/Models/User.cs +++ b/Oqtane.Shared/Models/User.cs @@ -9,6 +9,9 @@ namespace Oqtane.Models public string Username { get; set; } public string DisplayName { get; set; } public string Email { get; set; } + public int? PhotoFileId { get; set; } + public DateTime? LastLoginOn { get; set; } + public string LastIPAddress { get; set; } [NotMapped] public int SiteId { get; set; }