improved file upload, enhanced module installation from Nuget to support upgrades, added ability to upgrade the framework from Nuget, completed isolated multitenancy and site alias management, created IPortable interface for importing data into modules, added default content to initial installation

This commit is contained in:
Shaun Walker
2019-10-08 16:11:23 -04:00
parent dce53e10b0
commit 9971510b1e
48 changed files with 961 additions and 157 deletions

View File

@ -11,7 +11,7 @@
<label for="Name" class="control-label">Module: </label>
</td>
<td>
<FileUpload Filter=".nupkg"></FileUpload>
<FileUpload Filter=".nupkg" @ref="fileupload"></FileUpload>
</td>
</tr>
</table>
@ -50,18 +50,55 @@
bool uploaded = false;
List<Package> packages;
FileUpload fileupload;
protected override async Task OnInitializedAsync()
{
List<ModuleDefinition> moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
packages = await PackageService.GetPackagesAsync("module");
foreach(Package package in packages.ToArray())
{
if (moduledefinitions.Exists(item => Utilities.GetTypeName(item.ModuleDefinitionName) == package.PackageId))
{
packages.Remove(package);
}
}
}
private async Task UploadFile()
{
await FileService.UploadFilesAsync("Modules");
ModuleInstance.AddModuleMessage("Module Uploaded Successfully. Click Install To Complete Installation.", MessageType.Success);
uploaded = true;
StateHasChanged();
string[] files = await fileupload.GetFiles();
if (files.Length > 0)
{
if (files[0].Contains(".Module."))
{
try
{
if (await FileService.UploadFilesAsync("Modules", files, ""))
{
ModuleInstance.AddModuleMessage("Module Uploaded Successfully. Click Install To Complete Installation.", MessageType.Success);
uploaded = true;
StateHasChanged();
}
else
{
ModuleInstance.AddModuleMessage("Module Upload Failed.", MessageType.Error);
}
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage("Module Upload Failed. " + ex.Message, MessageType.Error);
}
}
else
{
ModuleInstance.AddModuleMessage("Invalid Module Package", MessageType.Error);
}
}
else
{
ModuleInstance.AddModuleMessage("You Must Select A Module To Upload", MessageType.Warning);
}
}
private async Task InstallModules()

View File

@ -1,6 +1,8 @@
@namespace Oqtane.Modules.Admin.ModuleDefinitions
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IModuleDefinitionService ModuleDefinitionService
@inject IPackageService PackageService
@if (moduledefinitions == null)
{
@ -16,12 +18,19 @@ else
<th>Version</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
</Header>
<Row>
<td>@context.Name</td>
<td>@context.Version</td>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" /></td>
<td><ActionLink Action="Delete" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" Class="btn btn-danger" /></td>
<td>
@if (UpgradeAvailable(context.ModuleDefinitionName, context.Version))
{
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.ModuleDefinitionName, context.Version))>Upgrade</button>
}
</td>
</Row>
</Pager>
}
@ -30,9 +39,29 @@ else
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
List<ModuleDefinition> moduledefinitions;
List<Package> packages;
protected override async Task OnInitializedAsync()
{
moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
packages = await PackageService.GetPackagesAsync("module");
}
private bool UpgradeAvailable(string moduledefinitionname, string version)
{
bool upgradeavailable = false;
Package package = packages.Where(item => item.PackageId == Utilities.GetTypeName(moduledefinitionname)).FirstOrDefault();
if (package != null)
{
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
}
return upgradeavailable;
}
private async Task DownloadModule(string moduledefinitionname, string version)
{
await PackageService.DownloadPackageAsync(moduledefinitionname, version, "Modules");
await ModuleDefinitionService.InstallModulesAsync();
NavigationManager.NavigateTo(NavigateUrl(Reload.Application));
}
}

View File

@ -0,0 +1,32 @@
@namespace Oqtane.Modules.Admin.Modules
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IModuleService ModuleService
<table class="table table-borderless">
<tbody>
<tr>
<td>
<label for="Title" class="control-label">Content: </label>
</td>
<td>
<textarea class="form-control" @bind="@content" rows="5" />
</td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-success" @onclick="ExportModule">Export</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code {
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } }
public override string Title { get { return "Export Module"; } }
string content = "";
private async Task ExportModule()
{
content = await ModuleService.ExportModuleAsync(ModuleState.ModuleId);
}
}

View File

@ -0,0 +1,41 @@
@namespace Oqtane.Modules.Admin.Modules
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IModuleService ModuleService
<table class="table table-borderless">
<tbody>
<tr>
<td>
<label for="Title" class="control-label">Content: </label>
</td>
<td>
<textarea class="form-control" @bind="@content" rows="5" />
</td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-success" @onclick="ImportModule">Import</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code {
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } }
public override string Title { get { return "Import Module"; } }
string content = "";
private async Task ImportModule()
{
if (content != "")
{
await ModuleService.ImportModuleAsync(ModuleState.ModuleId, content);
NavigationManager.NavigateTo(NavigateUrl(Reload.Page));
}
else
{
ModuleInstance.AddModuleMessage("You Must Enter Some Content To Import", MessageType.Warning);
}
}
}

View File

@ -1,4 +1,4 @@
@namespace Oqtane.Modules.Admin.ModuleSettings
@namespace Oqtane.Modules.Admin.Modules
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IThemeService ThemeService
@ -6,7 +6,7 @@
@inject IPageModuleService PageModuleService
<table class="table table-borderless">
<thead>
<tbody>
<tr>
<td>
<label for="Title" class="control-label">Title: </label>
@ -15,8 +15,6 @@
<input type="text" name="Title" class="form-control" @bind="@title" />
</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<label for="Container" class="control-label">Container: </label>

View File

@ -19,7 +19,7 @@ else
</td>
<td>
<select class="form-control" @bind="@tenantid">
<option value="">&lt;Select Tenant&gt;</option>
<option value="-1">&lt;Select Tenant&gt;</option>
@foreach (Tenant tenant in tenants)
{
<option value="@tenant.TenantId">@tenant.Name</option>
@ -37,10 +37,10 @@ else
</tr>
<tr>
<td>
<label for="Name" class="control-label">Alias: </label>
<label for="Name" class="control-label">Aliases: </label>
</td>
<td>
<input class="form-control" @bind="@url" />
<textarea class="form-control" @bind="@urls" rows="3" />
</td>
</tr>
<tr>
@ -91,9 +91,9 @@ else
Dictionary<string, string> panelayouts = new Dictionary<string, string>();
List<Tenant> tenants;
string tenantid = "";
string tenantid = "-1";
string name = "";
string url = "";
string urls = "";
string logo = "";
string themetype;
string layouttype;
@ -101,7 +101,7 @@ else
protected override async Task OnInitializedAsync()
{
tenants = await TenantService.GetTenantsAsync();
url = PageState.Alias.Name;
urls = PageState.Alias.Name;
themes = ThemeService.GetThemeTypes(PageState.Themes);
panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes);
}
@ -115,12 +115,16 @@ else
site.DefaultLayoutType = (layouttype == null ? "" : layouttype);
site = await SiteService.AddSiteAsync(site);
Alias alias = new Alias();
alias.Name = url;
alias.TenantId = int.Parse(tenantid);
alias.SiteId = site.SiteId;
await AliasService.AddAliasAsync(alias);
urls = urls.Replace("\n", ",");
foreach(string name in urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
Alias alias = new Alias();
alias.Name = name;
alias.TenantId = int.Parse(tenantid);
alias.SiteId = site.SiteId;
await AliasService.AddAliasAsync(alias);
}
NavigationManager.NavigateTo("http://" + url, true);
NavigationManager.NavigateTo("http://" + urls[0], true);
}
}

View File

@ -2,6 +2,7 @@
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject ISiteService SiteService
@inject IAliasService AliasService
@inject IThemeService ThemeService
@if (themes == null)
@ -19,6 +20,14 @@ else
<input class="form-control" @bind="@name" />
</td>
</tr>
<tr>
<td>
<label for="Name" class="control-label">Aliases: </label>
</td>
<td>
<textarea class="form-control" @bind="@urls" rows="3" />
</td>
</tr>
<tr>
<td>
<label for="Name" class="control-label">Logo: </label>
@ -80,7 +89,9 @@ else
Dictionary<string, string> themes = new Dictionary<string, string>();
Dictionary<string, string> panelayouts = new Dictionary<string, string>();
List<Alias> aliases;
string name = "";
string urls = "";
string logo = "";
string themetype;
string layouttype;
@ -95,9 +106,14 @@ else
protected override void OnInitialized()
{
aliases = PageState.Aliases.Where(item => item.TenantId == PageState.Alias.TenantId && item.SiteId == PageState.Site.SiteId).ToList();
themes = ThemeService.GetThemeTypes(PageState.Themes);
panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes);
name = PageState.Site.Name;
foreach (Alias alias in aliases)
{
urls += alias.Name + "\n";
}
logo = PageState.Site.Logo;
themetype = PageState.Site.DefaultThemeType;
layouttype = PageState.Site.DefaultLayoutType;
@ -122,6 +138,27 @@ else
site = await SiteService.UpdateSiteAsync(site);
urls = urls.Replace("\n", ",");
string[] names = urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (Alias alias in aliases)
{
if (!names.Contains(alias.Name))
{
await AliasService.DeleteAliasAsync(alias.AliasId);
}
}
foreach (string name in names)
{
if (!aliases.Exists(item => item.Name == name))
{
Alias alias = new Alias();
alias.Name = name;
alias.TenantId = PageState.Alias.TenantId;
alias.SiteId = site.SiteId;
await AliasService.AddAliasAsync(alias);
}
}
NavigationManager.NavigateTo(NavigateUrl());
}
}

View File

@ -2,6 +2,7 @@
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject ITenantService TenantService
@inject IInstallationService InstallationService
<table class="table table-borderless">
<tr>
@ -41,12 +42,20 @@
private async Task SaveTenant()
{
Tenant tenant = new Tenant();
tenant.Name = name;
tenant.DBConnectionString = connectionstring;
tenant.DBSchema = schema;
await TenantService.AddTenantAsync(tenant);
GenericResponse response = await InstallationService.Install(connectionstring);
if (response.Success)
{
Tenant tenant = new Tenant();
tenant.Name = name;
tenant.DBConnectionString = connectionstring;
tenant.DBSchema = schema;
await TenantService.AddTenantAsync(tenant);
NavigationManager.NavigateTo(NavigateUrl());
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
ModuleInstance.AddModuleMessage(response.Message, MessageType.Error);
}
}
}

View File

@ -11,7 +11,7 @@
<label for="Name" class="control-label">Theme: </label>
</td>
<td>
<FileUpload Filter=".nupkg"></FileUpload>
<FileUpload Filter=".nupkg" @ref="fileupload"></FileUpload>
</td>
</tr>
</table>
@ -49,18 +49,55 @@
bool uploaded = false;
List<Package> packages;
FileUpload fileupload;
protected override async Task OnInitializedAsync()
{
List<Theme> themes = await ThemeService.GetThemesAsync();
packages = await PackageService.GetPackagesAsync("theme");
foreach(Package package in packages.ToArray())
{
if (themes.Exists(item => Utilities.GetTypeName(item.ThemeName) == package.PackageId))
{
packages.Remove(package);
}
}
}
private async Task UploadTheme()
{
await FileService.UploadFilesAsync("Themes");
ModuleInstance.AddModuleMessage("Theme Uploaded Successfully. Click Install To Complete Installation.", MessageType.Success);
uploaded = true;
StateHasChanged();
string[] files = await fileupload.GetFiles();
if (files.Length > 0)
{
if (files[0].Contains(".Theme."))
{
try
{
if (await FileService.UploadFilesAsync("Themes", files, ""))
{
ModuleInstance.AddModuleMessage("Theme Uploaded Successfully. Click Install To Complete Installation.", MessageType.Success);
uploaded = true;
StateHasChanged();
}
else
{
ModuleInstance.AddModuleMessage("Theme Upload Failed.", MessageType.Error);
}
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage("Theme Upload Failed. " + ex.Message, MessageType.Error);
}
}
else
{
ModuleInstance.AddModuleMessage("Invalid Theme Package", MessageType.Error);
}
}
else
{
ModuleInstance.AddModuleMessage("You Must Select A Theme To Upload", MessageType.Warning);
}
}
private async Task InstallThemes()

View File

@ -1,6 +1,8 @@
@namespace Oqtane.Modules.Admin.Themes
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IThemeService ThemeService
@inject IPackageService PackageService
@if (themes == null)
{
@ -14,10 +16,19 @@ else
<Header>
<th>Name</th>
<th>Version</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
</Header>
<Row>
<td>@context.Name</td>
<td>@context.Version</td>
<td><ActionLink Action="Delete" Parameters="@($"id=" + context.ThemeName)" Class="btn btn-danger" /></td>
<td>
@if (UpgradeAvailable(context.ThemeName, context.Version))
{
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.ThemeName, context.Version))>Upgrade</button>
}
</td>
</Row>
</Pager>
}
@ -26,9 +37,29 @@ else
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
List<Theme> themes;
List<Package> packages;
protected override async Task OnInitializedAsync()
{
themes = await ThemeService.GetThemesAsync();
packages = await PackageService.GetPackagesAsync("module");
}
private bool UpgradeAvailable(string themename, string version)
{
bool upgradeavailable = false;
Package package = packages.Where(item => item.PackageId == Utilities.GetTypeName(themename)).FirstOrDefault();
if (package != null)
{
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
}
return upgradeavailable;
}
private async Task DownloadTheme(string themename, string version)
{
await PackageService.DownloadPackageAsync(themename, version, "Themes");
await ThemeService.InstallThemesAsync();
NavigationManager.NavigateTo(NavigateUrl(Reload.Application));
}
}

View File

@ -0,0 +1,104 @@
@namespace Oqtane.Modules.Admin.Upgrade
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IFileService FileService
@inject IPackageService PackageService
@inject IInstallationService InstallationService
<table class="table table-borderless">
<tr>
<td>
<label for="Name" class="control-label">Framework: </label>
</td>
<td>
<FileUpload Filter=".nupkg" @ref="fileupload"></FileUpload>
</td>
</tr>
</table>
@if (uploaded)
{
<button type="button" class="btn btn-success" @onclick="Upgrade">Upgrade</button>
}
else
{
<button type="button" class="btn btn-primary" @onclick="UploadFile">Upload</button>
}
@if (upgradeavailable)
{
<hr />
<div class="mx-auto text-center"><h2>Upgrade Available</h2></div>
<button type="button" class="btn btn-success" @onclick=@(async () => await Download(Constants.PackageId, Constants.Version))>Upgrade Framework</button>
}
@code {
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
bool uploaded = false;
bool upgradeavailable = false;
FileUpload fileupload;
protected override async Task OnInitializedAsync()
{
List<Package> packages = await PackageService.GetPackagesAsync("framework");
Package package = packages.FirstOrDefault();
if (package != null)
{
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) > 0);
}
if (!upgradeavailable)
{
ModuleInstance.AddModuleMessage("Framework Up To Date", MessageType.Info);
}
}
private async Task UploadFile()
{
string[] files = await fileupload.GetFiles();
if (files.Length > 0)
{
if (files[0].Contains(".Framework."))
{
try
{
if (await FileService.UploadFilesAsync("Framework", files, ""))
{
ModuleInstance.AddModuleMessage("Framework Uploaded Successfully. Click Upgrade To Complete Installation.", MessageType.Success);
uploaded = true;
StateHasChanged();
}
else
{
ModuleInstance.AddModuleMessage("Framework Upload Failed.", MessageType.Error);
}
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage("Framework Upload Failed. " + ex.Message, MessageType.Error);
}
}
else
{
ModuleInstance.AddModuleMessage("Invalid Framework Package", MessageType.Error);
}
}
else
{
ModuleInstance.AddModuleMessage("You Must Select A Framework Package To Upload", MessageType.Warning);
}
}
private async Task Upgrade()
{
await InstallationService.Upgrade();
NavigationManager.NavigateTo(NavigateUrl(Reload.Application));
}
private async Task Download(string packageid, string version)
{
await PackageService.DownloadPackageAsync(packageid, version, "Framework");
await InstallationService.Upgrade();
NavigationManager.NavigateTo(NavigateUrl(Reload.Application));
}
}

View File

@ -1,12 +1,13 @@
@namespace Oqtane.Modules.Controls
@inject IJSRuntime jsRuntime
@if (multiple)
{
<input type="file" id="@fileid" name="file" accept="@filter" multiple />
<input type="file" id="@fileid" name="file" accept="@filter" value="@files" multiple />
}
else
{
<input type="file" id="@fileid" name="file" accept="@filter" />
<input type="file" id="@fileid" name="file" accept="@filter" value="@files" />
}
<span id="@progressinfoid"></span> <progress id="@progressbarid" style="visibility: hidden;"></progress>
@ -24,6 +25,7 @@ else
string progressinfoid = "";
string progressbarid = "";
string filter = "*";
string files = "";
bool multiple = false;
protected override void OnInitialized()
@ -42,4 +44,11 @@ else
multiple = bool.Parse(Multiple);
}
}
public async Task<string[]> GetFiles()
{
var interop = new Interop(jsRuntime);
string[] files = await interop.GetFiles(fileid);
return files;
}
}

View File

@ -2,7 +2,7 @@
namespace Oqtane.Modules.HtmlText
{
public class Module : IModule
public class ModuleInfo : IModule
{
public Dictionary<string, string> Properties
{
@ -12,7 +12,8 @@ namespace Oqtane.Modules.HtmlText
{
{ "Name", "HtmlText" },
{ "Description", "Renders HTML or Text" },
{ "Version", "1.0.0" }
{ "Version", "1.0.0" },
{ "ServerAssemblyName", "Oqtane.Server" }
};
return properties;
}

View File

@ -1,12 +1,10 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Http;
using Microsoft.AspNetCore.Components;
using Oqtane.Services;
using Oqtane.Modules.HtmlText.Models;
using Oqtane.Shared;
using System.Text.Json;
using Oqtane.Models;
namespace Oqtane.Modules.HtmlText.Services
{
@ -57,5 +55,6 @@ namespace Oqtane.Modules.HtmlText.Services
{
await http.DeleteAsync(apiurl + "/" + ModuleId.ToString() + "?entityid=" + ModuleId.ToString());
}
}
}

View File

@ -3,6 +3,7 @@ using Microsoft.JSInterop;
using Oqtane.Shared;
using Oqtane.Models;
using System.Threading.Tasks;
using System.Linq;
namespace Oqtane.Modules
{
@ -20,6 +21,14 @@ namespace Oqtane.Modules
[CascadingParameter]
protected ModuleInstance ModuleInstance { get; set; }
protected ModuleDefinition ModuleDefinition
{
get
{
return PageState.ModuleDefinitions.Where(item => item.ModuleDefinitionName == ModuleState.ModuleDefinitionName).FirstOrDefault();
}
}
public virtual SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } set { } } // default security
public virtual string Title { get { return ""; } }

View File

@ -1,4 +1,6 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Oqtane.Shared;
@ -7,12 +9,14 @@ namespace Oqtane.Services
{
public class FileService : ServiceBase, IFileService
{
private readonly HttpClient http;
private readonly SiteState sitestate;
private readonly NavigationManager NavigationManager;
private readonly IJSRuntime jsRuntime;
public FileService(SiteState sitestate, NavigationManager NavigationManager, IJSRuntime jsRuntime)
public FileService(HttpClient http, SiteState sitestate, NavigationManager NavigationManager, IJSRuntime jsRuntime)
{
this.http = http;
this.sitestate = sitestate;
this.NavigationManager = NavigationManager;
this.jsRuntime = jsRuntime;
@ -23,15 +27,29 @@ namespace Oqtane.Services
get { return CreateApiUrl(sitestate.Alias, NavigationManager.Uri, "File"); }
}
public async Task UploadFilesAsync(string Folder)
public async Task<List<string>> GetFilesAsync(string Folder)
{
await UploadFilesAsync(Folder, "");
return await http.GetJsonAsync<List<string>>(apiurl + "?folder=" + Folder);
}
public async Task UploadFilesAsync(string Folder, string FileUploadName)
public async Task<bool> UploadFilesAsync(string Folder, string[] Files, string FileUploadName)
{
bool success = false;
var interop = new Interop(jsRuntime);
await interop.UploadFiles(apiurl + "/upload", Folder, FileUploadName);
List<string> files = await GetFilesAsync(Folder);
if (files.Count > 0)
{
success = true;
foreach (string file in Files)
{
if (!files.Contains(file))
{
success = false;
}
}
}
return success;
}
}
}

View File

@ -35,5 +35,10 @@ namespace Oqtane.Services
{
return await http.PostJsonAsync<GenericResponse>(apiurl, connectionstring);
}
public async Task<GenericResponse> Upgrade()
{
return await http.GetJsonAsync<GenericResponse>(apiurl + "/upgrade");
}
}
}

View File

@ -6,7 +6,7 @@ namespace Oqtane.Services
{
public interface IFileService
{
Task UploadFilesAsync(string Folder);
Task UploadFilesAsync(string Folder, string FileUploadName);
Task<List<string>> GetFilesAsync(string Folder);
Task<bool> UploadFilesAsync(string Folder, string[] Files, string FileUploadName);
}
}

View File

@ -1,5 +1,4 @@
using Oqtane.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Oqtane.Services
@ -8,5 +7,6 @@ namespace Oqtane.Services
{
Task<GenericResponse> IsInstalled();
Task<GenericResponse> Install(string connectionstring);
Task<GenericResponse> Upgrade();
}
}

View File

@ -12,5 +12,7 @@ namespace Oqtane.Services
Task<Module> AddModuleAsync(Module Module);
Task<Module> UpdateModuleAsync(Module Module);
Task DeleteModuleAsync(int ModuleId);
Task<bool> ImportModuleAsync(int ModuleId, string Content);
Task<string> ExportModuleAsync(int ModuleId);
}
}

View File

@ -60,5 +60,15 @@ namespace Oqtane.Services
{
await http.DeleteAsync(apiurl + "/" + ModuleId.ToString());
}
public async Task<bool> ImportModuleAsync(int ModuleId, string Content)
{
return await http.PostJsonAsync<bool>(apiurl + "/import?moduleid=" + ModuleId, Content);
}
public async Task<string> ExportModuleAsync(int ModuleId)
{
return await http.GetStringAsync(apiurl + "/export?moduleid=" + ModuleId.ToString());
}
}
}

View File

@ -122,7 +122,7 @@
private string DatabaseType = "LocalDB";
private string ServerName = "(LocalDb)\\MSSQLLocalDB";
private string DatabaseName = "Oqtane-" + DateTime.Now.ToString("yyyyMMddHHmm");
private string Username = "";
private string Username = "host";
private string Password = "";
private string HostUsername = "";
private string HostPassword = "";
@ -146,7 +146,7 @@
private async Task Install()
{
if (HostPassword.Length >= 6)
if (HostUsername != "" & HostPassword.Length >= 6 & HostEmail != "")
{
LoadingDisplay = "";
StateHasChanged();
@ -197,7 +197,7 @@
}
else
{
Message = "<div class=\"alert alert-danger\" role=\"alert\">Password Must Be 6 Characters Or Greater</div>";
Message = "<div class=\"alert alert-danger\" role=\"alert\">Username And Email Must Be Provided And Password Must Be Greater Than 5 Characters</div>";
}
}
}

View File

@ -100,6 +100,20 @@ namespace Oqtane.Shared
}
}
public ValueTask<string[]> GetFiles(string name)
{
try
{
return jsRuntime.InvokeAsync<string[]>(
"interop.getFiles",
name);
}
catch
{
return new ValueTask<string[]>(Task.FromResult(new string[0]));
}
}
public Task UploadFiles(string posturl, string folder, string name)
{
try

View File

@ -21,10 +21,12 @@
DynamicComponent = builder =>
{
string typename = ModuleState.ModuleType;
if (PageState.Control == "Settings") // module settings are a core component
{
typename = Constants.DefaultSettingsControl;
// check for core module actions component
if (Constants.DefaultModuleActions.Contains(PageState.Control))
{
typename = Constants.DefaultModuleActionsTemplate.Replace("{Control}", PageState.Control);
}
Type moduleType = null;
if (typename != null)
{

View File

@ -40,21 +40,22 @@
{
if (PageState.ModuleId != -1 && PageState.Control != "")
{
if (Name == Constants.AdminPane)
if (Name.ToLower() == Constants.AdminPane.ToLower())
{
Module module = PageState.Modules.Where(item => item.ModuleId == PageState.ModuleId).FirstOrDefault();
if (module != null)
{
string typename = module.ModuleType;
if (PageState.Control == "Settings")
// check for core module actions component
if (Constants.DefaultModuleActions.Contains(PageState.Control))
{
typename = Constants.DefaultSettingsControl;
typename = Constants.DefaultModuleActionsTemplate.Replace("{Control}", PageState.Control);
}
Type moduleType = Type.GetType(typename);
if (moduleType != null)
{
bool authorized = false;
if (PageState.Control == "Settings")
if (Constants.DefaultModuleActions.Contains(PageState.Control))
{
authorized = UserSecurity.IsAuthorized(PageState.User, "Edit", PageState.Page.Permissions);
}
@ -82,7 +83,7 @@
}
if (authorized)
{
if (PageState.Control != "Settings" && module.ControlTitle != "")
if (!Constants.DefaultModuleActions.Contains(PageState.Control) && module.ControlTitle != "")
{
module.Title = module.ControlTitle;
}
@ -93,8 +94,8 @@
}
else
{
// module control does not exist with name specified
}
// module control does not exist with name specified
}
}
}
}
@ -103,10 +104,10 @@
if (PageState.ModuleId != -1)
{
Module module = PageState.Modules.Where(item => item.ModuleId == PageState.ModuleId).FirstOrDefault();
if (module != null && module.Pane == Name)
if (module != null && module.Pane.ToLower() == Name.ToLower())
{
// check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, "View", module.Permissions))
// check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, "View", module.Permissions))
{
builder.OpenComponent(0, Type.GetType(Constants.DefaultContainer));
builder.AddAttribute(1, "Module", module);
@ -116,10 +117,10 @@
}
else
{
foreach (Module module in PageState.Modules.Where(item => item.Pane == Name).OrderBy(x => x.Order).ToArray())
foreach (Module module in PageState.Modules.Where(item => item.Pane.ToLower() == Name.ToLower()).OrderBy(x => x.Order).ToArray())
{
// check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, "View", module.Permissions))
// check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, "View", module.Permissions))
{
builder.OpenComponent(0, Type.GetType(Constants.DefaultContainer));
builder.AddAttribute(1, "Module", module);

View File

@ -342,9 +342,10 @@
// get IModuleControl properties
typename = module.ModuleType;
if (control == "Settings")
// check for core module actions component
if (Constants.DefaultModuleActions.Contains(control))
{
typename = Constants.DefaultSettingsControl;
typename = Constants.DefaultModuleActionsTemplate.Replace("{Control}", control);
}
Type moduletype = Type.GetType(typename);
if (moduletype != null)
@ -358,7 +359,7 @@
}
// ensure module's pane exists in current page and if not, assign it to the Admin pane
if (!panes.Contains(module.Pane))
if (!panes.ToLower().Contains(module.Pane.ToLower()))
{
module.Pane = Constants.AdminPane;
}

View File

@ -59,6 +59,18 @@ namespace Oqtane.Shared
return url;
}
public static string GetTypeName(string fullyqualifiedtypename)
{
if (fullyqualifiedtypename.Contains(","))
{
return fullyqualifiedtypename.Substring(0, fullyqualifiedtypename.IndexOf(","));
}
else
{
return fullyqualifiedtypename;
}
}
public static string GetTypeNameClass(string typename)
{
if (typename.Contains(","))

View File

@ -3,6 +3,7 @@ using Microsoft.JSInterop;
using Oqtane.Shared;
using Oqtane.Models;
using System.Threading.Tasks;
using System.Linq;
namespace Oqtane.Themes
{
@ -17,6 +18,14 @@ namespace Oqtane.Themes
[CascadingParameter]
protected Module ModuleState { get; set; }
protected ModuleDefinition ModuleDefinition
{
get
{
return PageState.ModuleDefinitions.Where(item => item.ModuleDefinitionName == ModuleState.ModuleDefinitionName).FirstOrDefault();
}
}
public virtual string Name { get; set; }
public string ThemePath()

View File

@ -48,8 +48,13 @@
actions.Add(new ActionViewModel { Action = pane, Name = "Move To " + pane + " Pane" });
}
}
actions.Add(new ActionViewModel { Action = "settings", Name = "Settings" });
actions.Add(new ActionViewModel { Action = "delete", Name = "Delete" });
actions.Add(new ActionViewModel { Action = "settings", Name = "Manage Settings" });
if (ModuleDefinition.ServerAssemblyName != "")
{
actions.Add(new ActionViewModel { Action = "import", Name = "Import Content" });
actions.Add(new ActionViewModel { Action = "export", Name = "Export Content" });
}
actions.Add(new ActionViewModel { Action = "delete", Name = "Delete Module" });
}
}
@ -85,6 +90,12 @@
case "settings":
url = EditUrl(pagemodule.ModuleId, "Settings");
break;
case "import":
url = EditUrl(pagemodule.ModuleId, "Import");
break;
case "export":
url = EditUrl(pagemodule.ModuleId, "Export");
break;
case "delete":
await PageModuleService.DeletePageModuleAsync(pagemodule.PageModuleId);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);

View File

@ -9,7 +9,8 @@
protected override Task OnParametersSetAsync()
{
title = ModuleState.Title;
if (PageState.Control == "Settings")
// check for core module actions component
if (Constants.DefaultModuleActions.Contains(PageState.Control))
{
title = PageState.Control;
}

View File

@ -64,6 +64,16 @@ window.interop = {
document.body.appendChild(form);
form.submit();
},
getFiles: function (name) {
var files = [];
var fileinput = document.getElementById(name);
if (fileinput !== null) {
for (var i = 0; i < fileinput.files.length; i++) {
files.push(fileinput.files[i].name);
}
}
return files;
},
uploadFiles: function (posturl, folder, name) {
var files = document.getElementById(name + 'FileInput').files;
var progressinfo = document.getElementById(name + 'ProgressInfo');