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 94454bf6..00000000 Binary files a/Oqtane.Server/wwwroot/Sites/1/oqtane.png and /dev/null differ diff --git a/Oqtane.Server/wwwroot/Sites/2/oqtane.png b/Oqtane.Server/wwwroot/Sites/2/oqtane.png deleted file mode 100644 index 94454bf6..00000000 Binary files a/Oqtane.Server/wwwroot/Sites/2/oqtane.png and /dev/null differ 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() + "/"; + } + } } }