From f60898dbc748ae2e07417f108b0c90fa3541f200 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sat, 7 Sep 2019 23:26:19 -0400 Subject: [PATCH] module installer --- .../Modules/Admin/ModuleDefinitions/Add.razor | 23 ++++++- .../Admin/ModuleDefinitions/Index.razor | 2 +- .../Interfaces/IModuleDefinitionService.cs | 3 +- .../Services/ModuleDefinitionService.cs | 5 ++ Oqtane.Client/Themes/Controls/Logo.razor | 2 +- Oqtane.Client/wwwroot/js/interop.js | 56 ++++++++++++++++++ .../Controllers/ModuleDefinitionController.cs | 50 +++++++++++++++- Oqtane.Server/wwwroot/Sites/1/oqtane.png | Bin 7963 -> 0 bytes Oqtane.Server/wwwroot/Sites/2/oqtane.png | Bin 7963 -> 0 bytes .../wwwroot/Tenants/1}/Sites/1/oqtane.png | Bin .../wwwroot/Tenants/1}/Sites/2/oqtane.png | Bin Oqtane.Shared/Models/Alias.cs | 35 +++++++++++ 12 files changed, 170 insertions(+), 6 deletions(-) delete mode 100644 Oqtane.Server/wwwroot/Sites/1/oqtane.png delete mode 100644 Oqtane.Server/wwwroot/Sites/2/oqtane.png rename {Oqtane.Client/wwwroot => Oqtane.Server/wwwroot/Tenants/1}/Sites/1/oqtane.png (100%) rename {Oqtane.Client/wwwroot => Oqtane.Server/wwwroot/Tenants/1}/Sites/2/oqtane.png (100%) diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index 5c8b86a4..9050489e 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -2,9 +2,11 @@ @using Oqtane.Client.Modules.Controls @using Oqtane.Modules @using Oqtane.Services +@using Oqtane.Shared @inherits ModuleBase @inject IUriHelper UriHelper @inject IFileService FileService +@inject IModuleDefinitionService ModuleDefinitionService @@ -16,15 +18,32 @@
- +@if (uploaded) +{ + +} +else +{ + +} Cancel @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + bool uploaded = false; + private async Task UploadFile() { - await FileService.UploadFilesAsync("/Sites/Modules"); + await FileService.UploadFilesAsync("Modules"); + uploaded = true; + StateHasChanged(); } + private async Task InstallFile() + { + await ModuleDefinitionService.InstallModulesAsync(); + PageState.Reload = Constants.ReloadApplication; + UriHelper.NavigateTo(NavigateUrl()); + } } diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor index 50fefe30..9b21ba5e 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor @@ -12,7 +12,7 @@ } else { - + diff --git a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs index b511799b..6d54bbc9 100644 --- a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs +++ b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs @@ -7,5 +7,6 @@ namespace Oqtane.Services public interface IModuleDefinitionService { Task> GetModuleDefinitionsAsync(); - } + Task InstallModulesAsync(); + } } diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index ffd28632..6ddde8af 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -63,5 +63,10 @@ namespace Oqtane.Services return moduledefinitions.OrderBy(item => item.Name).ToList(); } + + public async Task InstallModulesAsync() + { + await http.GetJsonAsync>(apiurl + "/install"); + } } } diff --git a/Oqtane.Client/Themes/Controls/Logo.razor b/Oqtane.Client/Themes/Controls/Logo.razor index 5970ce31..83459a32 100644 --- a/Oqtane.Client/Themes/Controls/Logo.razor +++ b/Oqtane.Client/Themes/Controls/Logo.razor @@ -10,7 +10,7 @@ { if (PageState.Site.Logo != "") { - logo = "\"""; + logo = "\"""; } return Task.CompletedTask; } diff --git a/Oqtane.Client/wwwroot/js/interop.js b/Oqtane.Client/wwwroot/js/interop.js index db61ecbe..7b23a474 100644 --- a/Oqtane.Client/wwwroot/js/interop.js +++ b/Oqtane.Client/wwwroot/js/interop.js @@ -55,5 +55,61 @@ window.interop = { document.body.appendChild(form); form.submit(); + }, + uploadFiles: function (posturl, folder, name) { + var files = document.getElementById(name + 'FileInput').files; + var progressinfo = document.getElementById(name + 'ProgressInfo'); + var progressbar = document.getElementById(name + 'ProgressBar'); + var filename = ''; + + for (var i = 0; i < files.length; i++) { + var FileChunk = []; + var file = files[i]; + var MaxFileSizeMB = 1; + var BufferChunkSize = MaxFileSizeMB * (1024 * 1024); + var FileStreamPos = 0; + var EndPos = BufferChunkSize; + var Size = file.size; + + progressbar.setAttribute("style", "visibility: visible;"); + + if (files.length > 1) { + filename = file.name; + } + + while (FileStreamPos < Size) { + FileChunk.push(file.slice(FileStreamPos, EndPos)); + FileStreamPos = EndPos; + EndPos = FileStreamPos + BufferChunkSize; + } + + var TotalParts = FileChunk.length; + var PartCount = 0; + + while (Chunk = FileChunk.shift()) { + PartCount++; + var FileName = file.name + ".part_" + PartCount + "_" + TotalParts; + + var data = new FormData(); + data.append('folder', folder); + data.append('file', Chunk, FileName); + var request = new XMLHttpRequest(); + request.open('POST', posturl, true); + request.upload.onloadstart = function (e) { + progressbar.value = 0; + progressinfo.innerHTML = filename + ' 0%'; + }; + request.upload.onprogress = function (e) { + var percent = Math.ceil((e.loaded / e.total) * 100); + progressbar.value = (percent / 100); + progressinfo.innerHTML = filename + '[' + PartCount + '] ' + percent + '%'; + }; + request.upload.onloadend = function (e) { + progressbar.value = 1; + progressinfo.innerHTML = filename + ' 100%'; + }; + request.send(data); + } + } } }; diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index d69275db..8844e9b6 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -2,6 +2,13 @@ using Microsoft.AspNetCore.Mvc; using Oqtane.Repository; using Oqtane.Models; +using Oqtane.Shared; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Hosting; +using System.IO.Compression; +using Microsoft.AspNetCore.Hosting; +using System.IO; +using System.Reflection; namespace Oqtane.Controllers { @@ -9,10 +16,14 @@ namespace Oqtane.Controllers public class ModuleDefinitionController : Controller { private readonly IModuleDefinitionRepository ModuleDefinitions; + private readonly IHostApplicationLifetime HostApplicationLifetime; + private readonly IWebHostEnvironment environment; - public ModuleDefinitionController(IModuleDefinitionRepository ModuleDefinitions) + public ModuleDefinitionController(IModuleDefinitionRepository ModuleDefinitions, IHostApplicationLifetime HostApplicationLifetime, IWebHostEnvironment environment) { this.ModuleDefinitions = ModuleDefinitions; + this.HostApplicationLifetime = HostApplicationLifetime; + this.environment = environment; } // GET: api/ @@ -21,5 +32,42 @@ namespace Oqtane.Controllers { return ModuleDefinitions.GetModuleDefinitions(); } + + [HttpGet("install")] + [Authorize(Roles = Constants.HostRole)] + public void InstallModules() + { + bool install = false; + string modulefolder = Path.Combine(environment.WebRootPath, "Modules"); + string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + + // iterate through module packages + foreach (string packagename in Directory.GetFiles(modulefolder, "*.nupkg")) + { + // iterate through files and deploy to appropriate locations + using (ZipArchive archive = ZipFile.OpenRead(packagename)) + { + foreach (ZipArchiveEntry entry in archive.Entries) + { + string filename = Path.GetFileName(entry.FullName); + switch (Path.GetExtension(filename)) + { + case ".dll": + entry.ExtractToFile(Path.Combine(binfolder, filename)); + break; + } + } + } + // remove module package + System.IO.File.Delete(packagename); + install = true; + } + + if (install) + { + // restart application + HostApplicationLifetime.StopApplication(); + } + } } } diff --git a/Oqtane.Server/wwwroot/Sites/1/oqtane.png b/Oqtane.Server/wwwroot/Sites/1/oqtane.png deleted file mode 100644 index 94454bf601612d061901f9d33121d19f5996d8bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7963 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 ZEyo$1+#R*=J79AfLqkPJxdCDq^FMa;ML_@n diff --git a/Oqtane.Server/wwwroot/Sites/2/oqtane.png b/Oqtane.Server/wwwroot/Sites/2/oqtane.png deleted file mode 100644 index 94454bf601612d061901f9d33121d19f5996d8bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7963 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 ZEyo$1+#R*=J79AfLqkPJxdCDq^FMa;ML_@n diff --git a/Oqtane.Client/wwwroot/Sites/1/oqtane.png b/Oqtane.Server/wwwroot/Tenants/1/Sites/1/oqtane.png similarity index 100% rename from Oqtane.Client/wwwroot/Sites/1/oqtane.png rename to Oqtane.Server/wwwroot/Tenants/1/Sites/1/oqtane.png diff --git a/Oqtane.Client/wwwroot/Sites/2/oqtane.png b/Oqtane.Server/wwwroot/Tenants/1/Sites/2/oqtane.png similarity index 100% rename from Oqtane.Client/wwwroot/Sites/2/oqtane.png rename to Oqtane.Server/wwwroot/Tenants/1/Sites/2/oqtane.png diff --git a/Oqtane.Shared/Models/Alias.cs b/Oqtane.Shared/Models/Alias.cs index a2caa95f..f22648b8 100644 --- a/Oqtane.Shared/Models/Alias.cs +++ b/Oqtane.Shared/Models/Alias.cs @@ -44,5 +44,40 @@ namespace Oqtane.Models } } + [NotMapped] + public string TenantRootPath + { + get + { + return "Tenants/" + TenantId.ToString() + "/"; + } + } + + [NotMapped] + public string TenantRootUrl + { + get + { + return Url + "/Tenants/" + TenantId.ToString() + "/"; + } + } + + [NotMapped] + public string SiteRootPath + { + get + { + return "Tenants/" + TenantId.ToString() + "/Sites/" + SiteId.ToString() + "/"; + } + } + + [NotMapped] + public string SiteRootUrl + { + get + { + return Url + "/Tenants/" + TenantId.ToString() + "/Sites/" + SiteId.ToString() + "/"; + } + } } }