Compare commits
97 Commits
v0.9.1-bet
...
v1.0.0
Author | SHA1 | Date | |
---|---|---|---|
19fb3120d3 | |||
4154bd0988 | |||
35b26c7525 | |||
52b2c876a4 | |||
b7eeda7562 | |||
b59e2533ea | |||
a8ec5e1265 | |||
61ff097f51 | |||
b82f69b8b0 | |||
263b045c75 | |||
a160597f6f | |||
f0fe6551dd | |||
09b17e142e | |||
b8ce634f85 | |||
1532eb7586 | |||
9b65cd0e07 | |||
b4596cb624 | |||
5e23448618 | |||
96c2a59551 | |||
6ae019336d | |||
0c236682b9 | |||
9b74262c76 | |||
a13208e65d | |||
f8ab886750 | |||
84b011224e | |||
c426302242 | |||
814e2100b2 | |||
54d4447d23 | |||
93942d7cdd | |||
0afd7d9ca4 | |||
3a19ced2d1 | |||
d5811a20e9 | |||
1b2600c6c4 | |||
f9cdc6d70c | |||
fbdf21320b | |||
96f5668a3b | |||
13adebb36c | |||
8a1e83ff7f | |||
e698ea4d36 | |||
f5ce00ae7d | |||
3cbb6e3e6e | |||
9394e77fd5 | |||
ac03afb146 | |||
51c27ae0e5 | |||
0ea4c4d723 | |||
b3dee737b4 | |||
24ca9f4ded | |||
9850e249fc | |||
5e04cb18a4 | |||
39641804f1 | |||
edc356292d | |||
78f4af6b70 | |||
e98b8801f3 | |||
caabac3e74 | |||
422f360807 | |||
6e28fa47a2 | |||
b4f3c4ae56 | |||
bafe2c6666 | |||
5eec442442 | |||
def12489e6 | |||
82429c2545 | |||
aa97dd4d0d | |||
cba5865e81 | |||
8afe8e7474 | |||
a9630e715b | |||
6824b3f1b5 | |||
1cca18c4d2 | |||
a886ae12cc | |||
560c995564 | |||
69a5077eda | |||
3efd39c74f | |||
bbcb054f7a | |||
88cf30f7c6 | |||
4c188b782d | |||
196e3d5865 | |||
6fd0efbb73 | |||
9eec0fd86b | |||
ad70128747 | |||
ee55b4e3cf | |||
53f454e370 | |||
f05c7d79e3 | |||
598b433cd2 | |||
6ac4ba4617 | |||
f4710f90c0 | |||
6f3fe8d933 | |||
da73d519d7 | |||
7f157582cc | |||
4d7ec16f36 | |||
7c814a67b3 | |||
c83496d814 | |||
83d47376cc | |||
a1449fb2dd | |||
2362faaee1 | |||
7ca1f92f52 | |||
c30ee60433 | |||
e627e14233 | |||
4a6b12b6f9 |
@ -1,4 +1,5 @@
|
||||
@namespace Oqtane.Modules.Admin.Files
|
||||
@using System.IO
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IFileService FileService
|
||||
@ -12,7 +13,7 @@
|
||||
<Label For="upload" HelpText="Upload the file you want">Upload: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager UploadMultiple="true" ShowFiles="false" FolderId="@_folderId.ToString()" />
|
||||
<FileManager UploadMultiple="true" ShowFiles="false" FolderId="@_folderId" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -70,18 +71,32 @@
|
||||
|
||||
private async Task Download()
|
||||
{
|
||||
if (url == string.Empty || _folderId == -1)
|
||||
{
|
||||
AddModuleMessage("You Must Enter A Url And Select A Folder", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
|
||||
|
||||
if (!Constants.UploadableFiles.Split(',')
|
||||
.Contains(Path.GetExtension(filename).ToLower().Replace(".", "")))
|
||||
{
|
||||
AddModuleMessage("File Could Not Be Downloaded From Url Due To Its File Extension", MessageType.Warning);
|
||||
return ;
|
||||
}
|
||||
|
||||
if (!filename.IsPathOrFileValid())
|
||||
{
|
||||
AddModuleMessage("You Must Enter A Url With A Valid File Name", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (url != string.Empty && _folderId != -1)
|
||||
{
|
||||
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);
|
||||
}
|
||||
await FileService.UploadFileAsync(url, _folderId);
|
||||
await logger.LogInformation("File Downloaded Successfully From Url {Url}", url);
|
||||
AddModuleMessage("File Downloaded Successfully From Url", MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -25,7 +25,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label for="name" HelpText="Enter the file name">Name: </Label>
|
||||
<Label for="name" HelpText="Enter the folder name">Name: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="name" class="form-control" @bind="@_name" />
|
||||
@ -112,51 +112,63 @@
|
||||
|
||||
private async Task SaveFolder()
|
||||
{
|
||||
if (_name == string.Empty || _parentId == -1)
|
||||
{
|
||||
AddModuleMessage("Folders Must Have A Parent And A Name", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_name.IsPathOrFileValid())
|
||||
{
|
||||
AddModuleMessage("Folder Name Not Valid.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (_name != string.Empty && _parentId != -1)
|
||||
Folder folder;
|
||||
if (_folderId != -1)
|
||||
{
|
||||
Folder folder;
|
||||
if (_folderId != -1)
|
||||
{
|
||||
folder = await FolderService.GetFolderAsync(_folderId);
|
||||
}
|
||||
else
|
||||
{
|
||||
folder = new Folder();
|
||||
}
|
||||
folder = await FolderService.GetFolderAsync(_folderId);
|
||||
}
|
||||
else
|
||||
{
|
||||
folder = new Folder();
|
||||
}
|
||||
|
||||
folder.SiteId = PageState.Site.SiteId;
|
||||
folder.SiteId = PageState.Site.SiteId;
|
||||
|
||||
if (_parentId == -1)
|
||||
{
|
||||
folder.ParentId = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
folder.ParentId = _parentId;
|
||||
}
|
||||
if (_parentId == -1)
|
||||
{
|
||||
folder.ParentId = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
folder.ParentId = _parentId;
|
||||
}
|
||||
|
||||
folder.Name = _name;
|
||||
folder.IsSystem = _isSystem;
|
||||
folder.Permissions = _permissionGrid.GetPermissions();
|
||||
folder.Name = _name;
|
||||
folder.IsSystem = _isSystem;
|
||||
folder.Permissions = _permissionGrid.GetPermissions();
|
||||
|
||||
if (_folderId != -1)
|
||||
{
|
||||
folder = await FolderService.UpdateFolderAsync(folder);
|
||||
}
|
||||
else
|
||||
{
|
||||
folder = await FolderService.AddFolderAsync(folder);
|
||||
}
|
||||
if (_folderId != -1)
|
||||
{
|
||||
folder = await FolderService.UpdateFolderAsync(folder);
|
||||
}
|
||||
else
|
||||
{
|
||||
folder = await FolderService.AddFolderAsync(folder);
|
||||
}
|
||||
|
||||
if (folder != null)
|
||||
{
|
||||
await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId);
|
||||
await logger.LogInformation("Folder Saved {Folder}", folder);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage("Folders Must Have A Parent And A Name", MessageType.Warning);
|
||||
AddModuleMessage("An Error Was Encountered Saving The Folder", MessageType.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -3,53 +3,66 @@
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IModuleDefinitionService ModuleDefinitionService
|
||||
@inject IModuleService ModuleService
|
||||
@inject ISystemService SystemService
|
||||
|
||||
<table class="table table-borderless">
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation.">Owner Name: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="owner" class="form-control" @bind="@_owner" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="module" HelpText="Enter a name for this module. It should be in singular form (ie. Car) and not contain spaces or punctuation.">Module Name: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="module" class="form-control" @bind="@_module" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="description" HelpText="Enter s short description for the module">Description: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<textarea id="description" class="form-control" @bind="@_description" rows="3"></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="template" HelpText="Select a module template. Internal modules are created inside of the Oqtane solution. External modules are created outside of the Oqtane solution.">Template: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="template" class="form-control" @onchange="(e => TemplateChanged(e))">
|
||||
<option value="-"><Select Template></option>
|
||||
<option value="internal">Internal</option>
|
||||
<option value="external">External</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
@if (!string.IsNullOrEmpty(_location))
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation.">Owner Name: </Label>
|
||||
<Label For="location" HelpText="Location where the module will be created">Location: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="owner" class="form-control" @bind="@_owner" />
|
||||
<input id="module" class="form-control" @bind="@_location" readonly />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="module" HelpText="Enter a name for this module. It should be in singular form (ie. Car) and not contain spaces or punctuation.">Module Name: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="module" class="form-control" @bind="@_module" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="description" HelpText="Enter s short description for the module">Description: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<textarea id="description" class="form-control" @bind="@_description" rows="3"></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="template" HelpText="Select a module template. Internal modules are created inside of the Oqtane solution. External modules are created outside of the Oqtane solution.">Template: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="template" class="form-control" @bind="@_template">
|
||||
<option value=""><Select Template></option>
|
||||
<option value="internal">Internal</option>
|
||||
<option value="external">External</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
}
|
||||
</table>
|
||||
|
||||
<button type="button" class="btn btn-success" @onclick="CreateModule">Create Module</button>
|
||||
|
||||
@code {
|
||||
@code {
|
||||
private string _owner = string.Empty;
|
||||
private string _module = string.Empty;
|
||||
private string _description = string.Empty;
|
||||
private string _template = string.Empty;
|
||||
private string _template = "-";
|
||||
private string _location = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
@ -62,9 +75,9 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_owner) && !string.IsNullOrEmpty(_module) && !string.IsNullOrEmpty(_template))
|
||||
if (!string.IsNullOrEmpty(_owner) && !string.IsNullOrEmpty(_module) && _template != "-")
|
||||
{
|
||||
var moduleDefinition = new ModuleDefinition { Owner = _owner.Replace(" ",""), Name = _module.Replace(" ", ""), Description = _description, Template = _template };
|
||||
var moduleDefinition = new ModuleDefinition { Owner = _owner.Replace(" ", ""), Name = _module.Replace(" ", ""), Description = _description, Template = _template };
|
||||
await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition, ModuleState.ModuleId);
|
||||
}
|
||||
else
|
||||
@ -77,4 +90,35 @@
|
||||
await logger.LogError(ex, "Error Creating Module");
|
||||
}
|
||||
}
|
||||
|
||||
private async void TemplateChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_location = string.Empty;
|
||||
_template = (string)e.Value;
|
||||
if (_template != "-")
|
||||
{
|
||||
Dictionary<string, string> systeminfo = await SystemService.GetSystemInfoAsync();
|
||||
if (systeminfo != null)
|
||||
{
|
||||
string[] path = systeminfo["serverpath"].Split('\\');
|
||||
if (_template == "internal")
|
||||
{
|
||||
_location = string.Join("\\", path, 0, path.Length - 1) + "\\Oqtane.Client\\Modules\\" + _owner + "." + _module + "s";
|
||||
}
|
||||
else
|
||||
{
|
||||
_location = string.Join("\\", path, 0, path.Length - 2) + "\\" + _owner + "." + _module + "s";
|
||||
}
|
||||
}
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Getting System Info {Error}", ex.Message);
|
||||
AddModuleMessage("Error Getting System Info", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,10 @@
|
||||
@using [Owner].[Module]s.Services
|
||||
@using [Owner].[Module]s.Models
|
||||
|
||||
@namespace [Owner].[Module]s.Modules
|
||||
@namespace [Owner].[Module]s
|
||||
@inherits ModuleBase
|
||||
@inject I[Module]Service [Module]Service
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject HttpClient http
|
||||
@inject SiteState sitestate
|
||||
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
@ -29,9 +28,14 @@
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
|
||||
public override string Actions => "Add,Edit";
|
||||
|
||||
I[Module]Service [Module]Service;
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
||||
};
|
||||
|
||||
int _id;
|
||||
string _name;
|
||||
string _createdby;
|
||||
@ -43,7 +47,6 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
[Module]Service = new [Module]Service(http, sitestate);
|
||||
if (PageState.Action == "Edit")
|
||||
{
|
||||
_id = Int32.Parse(PageState.QueryString["id"]);
|
||||
|
@ -1,11 +1,10 @@
|
||||
@using [Owner].[Module]s.Services
|
||||
@using [Owner].[Module]s.Models
|
||||
|
||||
@namespace [Owner].[Module]s.Modules
|
||||
@namespace [Owner].[Module]s
|
||||
@inherits ModuleBase
|
||||
@inject I[Module]Service [Module]Service
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject HttpClient http
|
||||
@inject SiteState sitestate
|
||||
|
||||
@if (_[Module]s == null)
|
||||
{
|
||||
@ -62,8 +61,9 @@ else
|
||||
- Repository\I[Module]Repository.cs - interface for defining repository methods<br />
|
||||
- Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core<br />
|
||||
- Repository\[Module]Context.cs - provides a DB Context for data access<br />
|
||||
- Scripts\[Owner].[Module].1.0.0.sql - database schema definition script<br /><br />
|
||||
- Scripts\[Owner].[Module].Uninstall.sql - database uninstall script<br /><br />
|
||||
- Scripts\[Owner].[Module]s.1.0.0.sql - database schema definition script<br />
|
||||
- Scripts\[Owner].[Module]s.Uninstall.sql - database uninstall script<br />
|
||||
- wwwroot\Module.css - module style sheet<br /><br />
|
||||
[RootPath]Shared\<br />
|
||||
- [Owner].[Module]s.csproj - shared project<br />
|
||||
- Models\[Module].cs - model definition<br /><br />
|
||||
@ -71,14 +71,17 @@ else
|
||||
<!-- The content above is for informational purposes only and can be safely removed -->
|
||||
|
||||
@code {
|
||||
I[Module]Service [Module]Service;
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
||||
};
|
||||
|
||||
List<[Module]> _[Module]s;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
[Module]Service = new [Module]Service(http, sitestate);
|
||||
_[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Modules;
|
||||
|
||||
namespace [Owner].[Module]s.Modules
|
||||
namespace [Owner].[Module]s
|
||||
{
|
||||
public class ModuleInfo : IModule
|
||||
{
|
||||
@ -10,7 +10,6 @@ namespace [Owner].[Module]s.Modules
|
||||
Name = "[Module]",
|
||||
Description = "[Module]",
|
||||
Version = "1.0.0",
|
||||
Dependencies = "[Owner].[Module]s.Shared.Oqtane",
|
||||
ServerManagerType = "[ServerManagerType]",
|
||||
ReleaseVersions = "1.0.0"
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
@namespace [Owner].[Module]s.Modules
|
||||
@namespace [Owner].[Module]s
|
||||
@inherits ModuleBase
|
||||
@inject ISettingService SettingService
|
||||
|
||||
|
@ -15,4 +15,6 @@
|
||||
@using Oqtane.Services
|
||||
@using Oqtane.Shared
|
||||
@using Oqtane.Themes
|
||||
@using Oqtane.Themes.Controls
|
||||
@using Oqtane.Themes.Controls
|
||||
@using Oqtane.UI
|
||||
@using Oqtane.Enums
|
@ -26,6 +26,7 @@
|
||||
<file src="..\Server\bin\Release\netcoreapp3.1\[Owner].[Module]s.Server.Oqtane.pdb" target="lib" />
|
||||
<file src="..\Shared\bin\Release\netstandard2.1\[Owner].[Module]s.Shared.Oqtane.dll" target="lib" />
|
||||
<file src="..\Shared\bin\Release\netstandard2.1\[Owner].[Module]s.Shared.Oqtane.pdb" target="lib" />
|
||||
<file src="..\wwwroot\**\*.*" target="wwwroot" />
|
||||
<file src="..\Server\wwwroot\**\*.*" target="wwwroot" />
|
||||
<file src="..\Server\content\**\*.*" target="content" />
|
||||
</files>
|
||||
</package>
|
@ -23,12 +23,12 @@ namespace [Owner].[Module]s.Manager
|
||||
|
||||
public bool Install(Tenant tenant, string version)
|
||||
{
|
||||
return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module]." + version + ".sql");
|
||||
return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module]s." + version + ".sql");
|
||||
}
|
||||
|
||||
public bool Uninstall(Tenant tenant)
|
||||
{
|
||||
return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module].Uninstall.sql");
|
||||
return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module]s.Uninstall.sql");
|
||||
}
|
||||
|
||||
public string ExportModule(Module module)
|
||||
|
@ -14,8 +14,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Scripts\[Owner].[Module].1.0.0.sql" />
|
||||
<EmbeddedResource Include="Scripts\[Owner].[Module].Uninstall.sql" />
|
||||
<EmbeddedResource Include="Scripts\[Owner].[Module]s.1.0.0.sql" />
|
||||
<EmbeddedResource Include="Scripts\[Owner].[Module]s.Uninstall.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
1
Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/content/resources.txt
vendored
Normal file
1
Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/content/resources.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
This is the location where static resources for third party libraries should be located ( the third party library assemblies will be included in the /lib folder ). They should be placed in subfolders which match the naming convention of the third party library. When the module package is deployed the static resource subfolders will be extracted under the web root.
|
1
Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/wwwroot/Module.css
vendored
Normal file
1
Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/wwwroot/Module.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
/* Module Custom Styles */
|
1
Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/wwwroot/resources.txt
vendored
Normal file
1
Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/wwwroot/resources.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
This is the location where static resources such as images, style sheets, or scripts for this module will be located. Static assets can be organized in subfolders. When the module package is deployed the assets will be extracted under the web root in a folder that matches the module namespace.
|
@ -2,11 +2,10 @@
|
||||
@using [Owner].[Module]s.Services
|
||||
@using [Owner].[Module]s.Models
|
||||
|
||||
@namespace [Owner].[Module]s.Modules
|
||||
@namespace [Owner].[Module]s
|
||||
@inherits ModuleBase
|
||||
@inject I[Module]Service [Module]Service
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject HttpClient http
|
||||
@inject SiteState sitestate
|
||||
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
@ -31,7 +30,6 @@
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
public override string Actions => "Add,Edit";
|
||||
|
||||
I[Module]Service [Module]Service;
|
||||
int _id;
|
||||
string _name;
|
||||
string _createdby;
|
||||
@ -43,7 +41,6 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
[Module]Service = new [Module]Service(http, sitestate);
|
||||
if (PageState.Action == "Edit")
|
||||
{
|
||||
_id = Int32.Parse(PageState.QueryString["id"]);
|
@ -1,11 +1,10 @@
|
||||
@using [Owner].[Module]s.Services
|
||||
@using [Owner].[Module]s.Models
|
||||
|
||||
@namespace [Owner].[Module]s.Modules
|
||||
@namespace [Owner].[Module]s
|
||||
@inherits ModuleBase
|
||||
@inject I[Module]Service [Module]Service
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject HttpClient http
|
||||
@inject SiteState sitestate
|
||||
|
||||
@if (_[Module]s == null)
|
||||
{
|
||||
@ -54,22 +53,20 @@ else
|
||||
- Repository\I[Module]Repository.cs - interface for defining repository methods<br />
|
||||
- Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core<br />
|
||||
- Repository\[Module]Context.cs - provides a DB Context for data access<br />
|
||||
- Scripts\[Owner].[Module].1.0.0.sql - database schema definition script<br /><br />
|
||||
- Scripts\[Owner].[Module].Uninstall.sql - database uninstall script<br /><br />
|
||||
- Scripts\[Owner].[Module]s.1.0.0.sql - database schema definition script<br />
|
||||
- Scripts\[Owner].[Module]s.Uninstall.sql - database uninstall script<br /><br />
|
||||
[RootPath]Oqtane.Shared\Modules\[Module]\<br />
|
||||
- Models\[Module].cs - model definition<br /><br />
|
||||
|
||||
<!-- The content above is for informational purposes only and can be safely removed -->
|
||||
|
||||
@code {
|
||||
I[Module]Service [Module]Service;
|
||||
List<[Module]> _[Module]s;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
[Module]Service = new [Module]Service(http, sitestate);
|
||||
_[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId);
|
||||
}
|
||||
catch (Exception ex)
|
@ -1,7 +1,7 @@
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Modules;
|
||||
|
||||
namespace [Owner].[Module]s.Modules
|
||||
namespace [Owner].[Module]s
|
||||
{
|
||||
public class ModuleInfo : IModule
|
||||
{
|
||||
@ -10,7 +10,6 @@ namespace [Owner].[Module]s.Modules
|
||||
Name = "[Module]",
|
||||
Description = "[Module]",
|
||||
Version = "1.0.0",
|
||||
Dependencies = "[Owner].[Module]s.Module.Shared",
|
||||
ServerManagerType = "[ServerManagerType]",
|
||||
ReleaseVersions = "1.0.0"
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
@namespace [Owner].[Module]s.Modules
|
||||
@namespace [Owner].[Module]s
|
||||
@inherits ModuleBase
|
||||
@inject ISettingService SettingService
|
||||
|
@ -10,25 +10,13 @@ using [Owner].[Module]s.Repository;
|
||||
|
||||
namespace [Owner].[Module]s.Manager
|
||||
{
|
||||
public class [Module]Manager : IInstallable, IPortable
|
||||
public class [Module]Manager : IPortable
|
||||
{
|
||||
private I[Module]Repository _[Module]s;
|
||||
private ISqlRepository _sql;
|
||||
|
||||
public [Module]Manager(I[Module]Repository [Module]s, ISqlRepository sql)
|
||||
public [Module]Manager(I[Module]Repository [Module]s)
|
||||
{
|
||||
_[Module]s = [Module]s;
|
||||
_sql = sql;
|
||||
}
|
||||
|
||||
public bool Install(Tenant tenant, string version)
|
||||
{
|
||||
return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module]." + version + ".sql");
|
||||
}
|
||||
|
||||
public bool Uninstall(Tenant tenant)
|
||||
{
|
||||
return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module].Uninstall.sql");
|
||||
}
|
||||
|
||||
public string ExportModule(Module module)
|
@ -35,7 +35,7 @@
|
||||
<Label HelpText="Upload one or more module packages. Once they are uploaded click Install to complete the installation.">Module: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager Filter="nupkg" ShowFiles="false" Folder="Modules" UploadMultiple="True" />
|
||||
<FileManager Filter="nupkg" ShowFiles="false" Folder="Modules" UploadMultiple="true" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -24,7 +24,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<select id="container" class="form-control" @bind="@_containerType">
|
||||
<option value=""><Select Container></option>
|
||||
<option value="-"><Inherit From Page Or Site></option>
|
||||
@foreach (KeyValuePair<string, string> container in _containers)
|
||||
{
|
||||
<option value="@container.Key">@container.Value</option>
|
||||
@ -107,6 +107,14 @@
|
||||
_title = ModuleState.Title;
|
||||
_containers = ThemeService.GetContainerTypes(await ThemeService.GetThemesAsync());
|
||||
_containerType = ModuleState.ContainerType;
|
||||
if (!string.IsNullOrEmpty(PageState.Page.DefaultContainerType) && _containerType == PageState.Page.DefaultContainerType)
|
||||
{
|
||||
_containerType = "-";
|
||||
}
|
||||
if (_containerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
_containerType = "-";
|
||||
}
|
||||
_allPages = ModuleState.AllPages.ToString();
|
||||
_permissions = ModuleState.Permissions;
|
||||
_permissionNames = ModuleState.ModuleDefinition.PermissionNames;
|
||||
@ -115,8 +123,8 @@
|
||||
_settingsModuleType = Type.GetType(ModuleState.ModuleType);
|
||||
if (_settingsModuleType != null)
|
||||
{
|
||||
var moduleobject = Activator.CreateInstance(_settingsModuleType);
|
||||
_settingstitle = (string)_settingsModuleType.GetProperty("Title").GetValue(moduleobject, null);
|
||||
var moduleobject = Activator.CreateInstance(_settingsModuleType) as IModuleControl;
|
||||
_settingstitle = moduleobject.Title;
|
||||
if (string.IsNullOrEmpty(_settingstitle))
|
||||
{
|
||||
_settingstitle = "Other Settings";
|
||||
@ -136,7 +144,15 @@
|
||||
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||
pagemodule.PageId = int.Parse(_pageId);
|
||||
pagemodule.Title = _title;
|
||||
pagemodule.ContainerType = _containerType;
|
||||
pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
|
||||
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
|
||||
{
|
||||
pagemodule.ContainerType = string.Empty;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
pagemodule.ContainerType = string.Empty;
|
||||
}
|
||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||
|
||||
|
@ -101,7 +101,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<select id="Theme" class="form-control" @onchange="(e => ThemeChanged(e))">
|
||||
<option value="-"><Select Theme></option>
|
||||
<option value="-"><Inherit From Site></option>
|
||||
@foreach (KeyValuePair<string, string> item in _themes)
|
||||
{
|
||||
if (item.Key == _themetype)
|
||||
@ -122,7 +122,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<select id="Layout" class="form-control" @bind="@_layouttype">
|
||||
<option value="-"><Select Layout></option>
|
||||
<option value="-"><Inherit From Site></option>
|
||||
@foreach (KeyValuePair<string, string> panelayout in _panelayouts)
|
||||
{
|
||||
if (panelayout.Key == _layouttype)
|
||||
@ -137,6 +137,20 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="defaultContainer" HelpText="Select the default container for the page">Default Container: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultContainer" class="form-control" @bind="@_containertype">
|
||||
<option value="-"><Inherit From Site></option>
|
||||
@foreach (KeyValuePair<string, string> container in _containers)
|
||||
{
|
||||
<option value="@container.Key">@container.Value</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="Icon" HelpText="Optionally provide an icon for this page which will be displayed in the site navigation">Icon: </Label>
|
||||
@ -187,6 +201,7 @@
|
||||
@code {
|
||||
private Dictionary<string, string> _themes;
|
||||
private Dictionary<string, string> _panelayouts;
|
||||
private Dictionary<string, string> _containers = new Dictionary<string, string>();
|
||||
private List<Theme> _themeList;
|
||||
private List<Page> _pageList;
|
||||
private string _name;
|
||||
@ -202,6 +217,7 @@
|
||||
private string _mode = "view";
|
||||
private string _themetype = "-";
|
||||
private string _layouttype = "-";
|
||||
private string _containertype = "-";
|
||||
private string _icon = string.Empty;
|
||||
private string _permissions = string.Empty;
|
||||
private PermissionGrid _permissionGrid;
|
||||
@ -216,11 +232,9 @@
|
||||
_pageList = PageState.Pages;
|
||||
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
||||
|
||||
_themetype = PageState.Site.DefaultThemeType;
|
||||
_layouttype = PageState.Site.DefaultLayoutType;
|
||||
|
||||
_themes = ThemeService.GetThemeTypes(_themeList);
|
||||
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
|
||||
_containers = ThemeService.GetContainerTypes(_themeList);
|
||||
|
||||
_permissions = string.Empty;
|
||||
}
|
||||
@ -351,16 +365,20 @@
|
||||
page.Url = _url;
|
||||
page.EditMode = (_mode == "edit" ? true : false);
|
||||
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
|
||||
page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty;
|
||||
if (page.ThemeType == PageState.Site.DefaultThemeType)
|
||||
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
||||
{
|
||||
page.ThemeType = string.Empty;
|
||||
}
|
||||
|
||||
if (page.LayoutType == PageState.Site.DefaultLayoutType)
|
||||
page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty;
|
||||
if (!string.IsNullOrEmpty(page.LayoutType) && page.LayoutType == PageState.Site.DefaultLayoutType)
|
||||
{
|
||||
page.LayoutType = string.Empty;
|
||||
}
|
||||
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
|
||||
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
page.DefaultContainerType = string.Empty;
|
||||
}
|
||||
page.Icon = (_icon == null ? string.Empty : _icon);
|
||||
page.Permissions = _permissionGrid.GetPermissions();
|
||||
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
|
||||
|
@ -112,7 +112,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<select id="Theme" class="form-control" @onchange="(e => ThemeChanged(e))">
|
||||
<option value="-"><Select Theme></option>
|
||||
<option value="-"><Inherit From Site></option>
|
||||
@foreach (KeyValuePair<string, string> item in _themes)
|
||||
{
|
||||
if (item.Key == _themetype)
|
||||
@ -133,7 +133,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<select id="Layout" class="form-control" @bind="@_layouttype">
|
||||
<option value="-"><Select Layout></option>
|
||||
<option value="-"><Inherit From Site></option>
|
||||
@foreach (KeyValuePair<string, string> panelayout in _panelayouts)
|
||||
{
|
||||
if (panelayout.Key == _layouttype)
|
||||
@ -148,6 +148,20 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="defaultContainer" HelpText="Select the default container for the page">Default Container: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultContainer" class="form-control" @bind="@_containertype">
|
||||
<option value="-"><Inherit From Site></option>
|
||||
@foreach (KeyValuePair<string, string> container in _containers)
|
||||
{
|
||||
<option value="@container.Key">@container.Value</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="Icon" HelpText="Optionally provide an icon for this page which will be displayed in the site navigation">Icon: </Label>
|
||||
@ -200,6 +214,7 @@
|
||||
@code {
|
||||
private Dictionary<string, string> _themes;
|
||||
private Dictionary<string, string> _panelayouts;
|
||||
private Dictionary<string, string> _containers = new Dictionary<string, string>();
|
||||
private List<Theme> _themeList;
|
||||
private List<Page> _pageList;
|
||||
private int _pageId;
|
||||
@ -217,6 +232,7 @@
|
||||
private string _mode;
|
||||
private string _themetype = "-";
|
||||
private string _layouttype = "-";
|
||||
private string _containertype = "-";
|
||||
private string _icon;
|
||||
private string _permissions;
|
||||
private string _createdby;
|
||||
@ -241,6 +257,7 @@
|
||||
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
||||
|
||||
_themes = ThemeService.GetThemeTypes(_themeList);
|
||||
_containers = ThemeService.GetContainerTypes(_themeList);
|
||||
|
||||
_pageId = Int32.Parse(PageState.QueryString["id"]);
|
||||
var page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
|
||||
@ -270,16 +287,21 @@
|
||||
_ispersonalizable = page.IsPersonalizable.ToString();
|
||||
_mode = (page.EditMode) ? "edit" : "view";
|
||||
_themetype = page.ThemeType;
|
||||
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
|
||||
_layouttype = page.LayoutType;
|
||||
if (_themetype == PageState.Site.DefaultThemeType)
|
||||
{
|
||||
_themetype = "-";
|
||||
}
|
||||
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
|
||||
_layouttype = page.LayoutType;
|
||||
if (_layouttype == PageState.Site.DefaultLayoutType)
|
||||
{
|
||||
_layouttype = "-";
|
||||
}
|
||||
_containertype = page.DefaultContainerType;
|
||||
if (string.IsNullOrEmpty(_containertype))
|
||||
{
|
||||
_containertype = "-";
|
||||
}
|
||||
_icon = page.Icon;
|
||||
_permissions = page.Permissions;
|
||||
_createdby = page.CreatedBy;
|
||||
@ -426,15 +448,20 @@
|
||||
page.Url = _url;
|
||||
page.EditMode = (_mode == "edit");
|
||||
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
|
||||
page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty;
|
||||
if (page.ThemeType == PageState.Site.DefaultThemeType)
|
||||
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
||||
{
|
||||
page.ThemeType = string.Empty;
|
||||
}
|
||||
if (page.LayoutType == PageState.Site.DefaultLayoutType)
|
||||
page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty;
|
||||
if (!string.IsNullOrEmpty(page.LayoutType) && page.LayoutType == PageState.Site.DefaultLayoutType)
|
||||
{
|
||||
page.LayoutType = string.Empty;
|
||||
}
|
||||
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
|
||||
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
page.DefaultContainerType = string.Empty;
|
||||
}
|
||||
page.Icon = _icon ?? string.Empty;
|
||||
page.Permissions = _permissionGrid.GetPermissions();
|
||||
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
|
||||
|
@ -39,7 +39,7 @@
|
||||
<Label For="logo" HelpText="Upload a logo for the site">Logo: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager FileId="@_logofileid.ToString()" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
|
||||
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -47,7 +47,7 @@
|
||||
<Label For="favicon" HelpText="Select Your default icon">Favicon: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager FileId="@_faviconfileid.ToString()" Filter="ico" @ref="_faviconfilemanager" />
|
||||
<FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -185,7 +185,7 @@
|
||||
<Label For="appIcon" HelpText="Include an application icon for your PWA. It should be a PNG which is 192 X 192 pixels in dimension.">App Icon: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager FileId="@_pwaappiconfileid.ToString()" Filter="png" @ref="_pwaappiconfilemanager" />
|
||||
<FileManager FileId="@_pwaappiconfileid" Filter="png" @ref="_pwaappiconfilemanager" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -193,7 +193,7 @@
|
||||
<Label For="splashIcon" HelpText="Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension.">Splash Icon: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager FileId="@_pwasplashiconfileid.ToString()" Filter="png" @ref="_pwasplashiconfilemanager" />
|
||||
<FileManager FileId="@_pwasplashiconfileid" Filter="png" @ref="_pwasplashiconfilemanager" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<Label For="version" HelpText="Framework Version">Framework Version: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="version" class="form-control" @bind="@_version" disabled />
|
||||
<input id="version" class="form-control" @bind="@_version" readonly />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -16,7 +16,7 @@
|
||||
<Label For="runtime" HelpText="Blazor Runtime (Server or WebAssembly)">Blazor Runtime: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="runtime" class="form-control" @bind="@_runtime" disabled />
|
||||
<input id="runtime" class="form-control" @bind="@_runtime" readonly />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -24,7 +24,7 @@
|
||||
<Label For="clrversion" HelpText="Common Language Runtime Version">CLR Version: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="clrversion" class="form-control" @bind="@_clrversion" disabled />
|
||||
<input id="clrversion" class="form-control" @bind="@_clrversion" readonly />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -32,7 +32,7 @@
|
||||
<Label For="osversion" HelpText="Operating System Version">OS Version: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="osversion" class="form-control" @bind="@_osversion" disabled />
|
||||
<input id="osversion" class="form-control" @bind="@_osversion" readonly />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -40,7 +40,7 @@
|
||||
<Label For="serverpath" HelpText="Server Path">Server Path: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="serverpath" class="form-control" @bind="@_serverpath" disabled />
|
||||
<input id="serverpath" class="form-control" @bind="@_serverpath" readonly />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -48,7 +48,7 @@
|
||||
<Label For="servertime" HelpText="Server Time">Server Time: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="servertime" class="form-control" @bind="@_servertime" disabled />
|
||||
<input id="servertime" class="form-control" @bind="@_servertime" readonly />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -35,7 +35,7 @@
|
||||
<Label HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation.">Theme: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager Filter="nupkg" ShowFiles="false" Folder="Themes" UploadMultiple="True" />
|
||||
<FileManager Filter="nupkg" ShowFiles="false" Folder="Themes" UploadMultiple="@true" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -64,7 +64,7 @@ else
|
||||
<label for="Name" class="control-label">Photo: </label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager FileId="@photofileid.ToString()" @ref="filemanager" />
|
||||
<FileManager FileId="@photofileid" @ref="filemanager" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -63,7 +63,7 @@ else
|
||||
<label class="control-label">Photo: </label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager FileId="@photofileid.ToString()" @ref="filemanager" />
|
||||
<FileManager FileId="@photofileid" @ref="filemanager" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -62,9 +62,9 @@
|
||||
[Parameter]
|
||||
public string Class { get; set; } // optional
|
||||
|
||||
[Parameter]
|
||||
public bool Disabled { get; set; } // optional
|
||||
|
||||
[Parameter]
|
||||
public bool Disabled { get; set; } // optional
|
||||
|
||||
[Parameter]
|
||||
public string EditMode { get; set; } // optional - specifies if a user must be in edit mode to see the action - default is true
|
||||
|
||||
@ -109,8 +109,8 @@
|
||||
Type moduleType = Type.GetType(typename);
|
||||
if (moduleType != null)
|
||||
{
|
||||
var moduleobject = Activator.CreateInstance(moduleType);
|
||||
security = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null);
|
||||
var moduleobject = Activator.CreateInstance(moduleType) as IModuleControl;
|
||||
security = moduleobject.SecurityAccessLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -101,8 +101,8 @@
|
||||
var moduleType = Type.GetType(typename);
|
||||
if (moduleType != null)
|
||||
{
|
||||
var moduleobject = Activator.CreateInstance(moduleType);
|
||||
security = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null);
|
||||
var moduleobject = Activator.CreateInstance(moduleType) as IModuleControl;
|
||||
security = moduleobject.SecurityAccessLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@inherits ModuleBase
|
||||
|
||||
@attribute [OqtaneIgnore]
|
||||
@inject IFolderService FolderService
|
||||
@inject IFileService FileService
|
||||
@ -10,33 +11,36 @@
|
||||
<div id="@Id" class="container-fluid px-0">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div>
|
||||
<select class="form-control" @onchange="(e => FolderChanged(e))">
|
||||
@if (string.IsNullOrEmpty(Folder))
|
||||
{
|
||||
<option value="-1"><Select Folder></option>
|
||||
}
|
||||
@foreach (Folder folder in _folders)
|
||||
{
|
||||
if (folder.FolderId == _folderid)
|
||||
@if (ShowFolders || FolderId <= 0)
|
||||
{
|
||||
<div>
|
||||
<select class="form-control" @onchange="(e => FolderChanged(e))">
|
||||
@if (string.IsNullOrEmpty(Folder))
|
||||
{
|
||||
<option value="@(folder.FolderId)" selected>@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
||||
<option value="-1"><Select Folder></option>
|
||||
}
|
||||
else
|
||||
@foreach (Folder folder in _folders)
|
||||
{
|
||||
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
||||
if (folder.FolderId == FolderId)
|
||||
{
|
||||
<option value="@(folder.FolderId)" selected>@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
||||
}
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
@if (_showfiles)
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
@if (ShowFiles)
|
||||
{
|
||||
<div>
|
||||
<select class="form-control" @onchange="(e => FileChanged(e))">
|
||||
<option value="-1"><Select File></option>
|
||||
@foreach (File file in _files)
|
||||
{
|
||||
if (file.FileId == _fileid)
|
||||
if (file.FileId == FileId)
|
||||
{
|
||||
<option value="@(file.FileId)" selected>@(file.Name)</option>
|
||||
}
|
||||
@ -48,33 +52,33 @@
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
@if (_haseditpermission)
|
||||
@if (ShowUpload && _haseditpermission)
|
||||
{
|
||||
<div>
|
||||
@if (_uploadmultiple)
|
||||
@if (UploadMultiple)
|
||||
{
|
||||
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple />
|
||||
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="file" id="@_fileinputid" name="file" accept="@_filter" />
|
||||
<input type="file" id="@_fileinputid" name="file" accept="@_filter"/>
|
||||
}
|
||||
<span id="@_progressinfoid"></span><progress id="@_progressbarid" style="width: 150px; visibility: hidden;"></progress>
|
||||
<span class="float-right">
|
||||
<button type="button" class="btn btn-success" @onclick="UploadFile">Upload</button>
|
||||
@if (_showfiles && GetFileId() != -1)
|
||||
{
|
||||
<button type="button" class="btn btn-danger" @onclick="DeleteFile">Delete</button>
|
||||
}
|
||||
<button type="button" class="btn btn-success" @onclick="UploadFile">Upload</button>
|
||||
@if (_showfiles && GetFileId() != -1)
|
||||
{
|
||||
<button type="button" class="btn btn-danger" @onclick="DeleteFile">Delete</button>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
@((MarkupString)_message)
|
||||
}
|
||||
@((MarkupString) _message)
|
||||
</div>
|
||||
@if (_image != string.Empty)
|
||||
{
|
||||
<div class="col-auto">
|
||||
@((MarkupString)_image)
|
||||
@((MarkupString) _image)
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@ -84,15 +88,12 @@
|
||||
@code {
|
||||
private string _id;
|
||||
private List<Folder> _folders;
|
||||
private int _folderid = -1;
|
||||
private List<File> _files = new List<File>();
|
||||
private int _fileid = -1;
|
||||
private bool _showfiles = true;
|
||||
private string _fileinputid = string.Empty;
|
||||
private string _progressinfoid = string.Empty;
|
||||
private string _progressbarid = string.Empty;
|
||||
private string _filter = "*";
|
||||
private bool _uploadmultiple = false;
|
||||
private bool _haseditpermission = false;
|
||||
private string _message = string.Empty;
|
||||
private string _image = string.Empty;
|
||||
@ -105,19 +106,25 @@
|
||||
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
|
||||
public int FolderId { get; set; } = -1; // 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
|
||||
public bool ShowFiles { get; set; } = true; // 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
|
||||
public bool ShowUpload { get; set; } = true; // optional - for indicating whether a Upload controls should be displayed - default is true
|
||||
|
||||
[Parameter]
|
||||
public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true
|
||||
|
||||
[Parameter]
|
||||
public int FileId { get; set; } = -1; // 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
|
||||
public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@ -129,56 +136,39 @@
|
||||
if (!string.IsNullOrEmpty(Folder))
|
||||
{
|
||||
_folders = new List<Folder> {new Folder {FolderId = -1, Name = Folder}};
|
||||
_folderid = -1;
|
||||
FolderId = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
|
||||
if (!string.IsNullOrEmpty(FolderId))
|
||||
{
|
||||
_folderid = int.Parse(FolderId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(FileId))
|
||||
if (FileId != -1)
|
||||
{
|
||||
_fileid = int.Parse(FileId);
|
||||
if (_fileid != -1)
|
||||
File file = await FileService.GetFileAsync(FileId);
|
||||
if (file != null)
|
||||
{
|
||||
File file = await FileService.GetFileAsync(int.Parse(FileId));
|
||||
if (file != null)
|
||||
{
|
||||
_folderid = file.FolderId;
|
||||
}
|
||||
else
|
||||
{
|
||||
_fileid = -1; // file does not exist
|
||||
}
|
||||
FolderId = file.FolderId;
|
||||
}
|
||||
else
|
||||
{
|
||||
FileId = -1; // file does not exist
|
||||
}
|
||||
await SetImage();
|
||||
}
|
||||
if (!string.IsNullOrEmpty(ShowFiles))
|
||||
{
|
||||
_showfiles = bool.Parse(ShowFiles);
|
||||
}
|
||||
await SetImage();
|
||||
|
||||
if (!string.IsNullOrEmpty(Filter))
|
||||
{
|
||||
_filter = "." + Filter.Replace(",",",.");
|
||||
_filter = "." + Filter.Replace(",", ",.");
|
||||
}
|
||||
|
||||
await GetFiles();
|
||||
|
||||
// create unique id for component
|
||||
// create unique id for component
|
||||
_guid = Guid.NewGuid().ToString("N");
|
||||
_fileinputid = _guid + "FileInput";
|
||||
_progressinfoid = _guid + "ProgressInfo";
|
||||
_progressbarid = _guid + "ProgressBar";
|
||||
|
||||
if (!string.IsNullOrEmpty(UploadMultiple))
|
||||
{
|
||||
_uploadmultiple = bool.Parse(UploadMultiple);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetFiles()
|
||||
@ -191,11 +181,11 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
Folder folder = _folders.FirstOrDefault(item => item.FolderId == _folderid);
|
||||
Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId);
|
||||
if (folder != null)
|
||||
{
|
||||
_haseditpermission = UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, folder.Permissions);
|
||||
_files = await FileService.GetFilesAsync(_folderid);
|
||||
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.Permissions);
|
||||
_files = await FileService.GetFilesAsync(FolderId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -222,9 +212,9 @@
|
||||
_message = string.Empty;
|
||||
try
|
||||
{
|
||||
_folderid = int.Parse((string)e.Value);
|
||||
FolderId = int.Parse((string) e.Value);
|
||||
await GetFiles();
|
||||
_fileid = -1;
|
||||
FileId = -1;
|
||||
_image = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
@ -238,7 +228,7 @@
|
||||
private async Task FileChanged(ChangeEventArgs e)
|
||||
{
|
||||
_message = string.Empty;
|
||||
_fileid = int.Parse((string)e.Value);
|
||||
FileId = int.Parse((string) e.Value);
|
||||
|
||||
await SetImage();
|
||||
StateHasChanged();
|
||||
@ -247,21 +237,21 @@
|
||||
private async Task SetImage()
|
||||
{
|
||||
_image = string.Empty;
|
||||
if (_fileid != -1)
|
||||
if (FileId != -1)
|
||||
{
|
||||
File file = await FileService.GetFileAsync(_fileid);
|
||||
File file = await FileService.GetFileAsync(FileId);
|
||||
if (file != null && file.ImageHeight != 0 && file.ImageWidth != 0)
|
||||
{
|
||||
var maxwidth = 200;
|
||||
var maxheight = 200;
|
||||
|
||||
var ratioX = (double)maxwidth / (double)file.ImageWidth;
|
||||
var ratioY = (double)maxheight / (double)file.ImageHeight;
|
||||
var ratioX = (double) maxwidth / (double) file.ImageWidth;
|
||||
var ratioY = (double) maxheight / (double) file.ImageHeight;
|
||||
var ratio = ratioX < ratioY ? ratioX : ratioY;
|
||||
|
||||
_image = "<img src=\"" + ContentUrl(_fileid) + "\" alt=\"" + file.Name +
|
||||
"\" width=\"" + Convert.ToInt32(file.ImageWidth * ratio).ToString() +
|
||||
"\" height=\"" + Convert.ToInt32(file.ImageHeight * ratio).ToString() + "\" />";
|
||||
_image = "<img src=\"" + ContentUrl(FileId) + "\" alt=\"" + file.Name +
|
||||
"\" width=\"" + Convert.ToInt32(file.ImageWidth * ratio).ToString() +
|
||||
"\" height=\"" + Convert.ToInt32(file.ImageHeight * ratio).ToString() + "\" />";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -281,7 +271,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await FileService.UploadFilesAsync(_folderid, upload, _guid);
|
||||
result = await FileService.UploadFilesAsync(FolderId, upload, _guid);
|
||||
}
|
||||
|
||||
if (result == string.Empty)
|
||||
@ -295,7 +285,7 @@
|
||||
var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault();
|
||||
if (file != null)
|
||||
{
|
||||
_fileid = file.FileId;
|
||||
FileId = file.FileId;
|
||||
await SetImage();
|
||||
}
|
||||
}
|
||||
@ -325,21 +315,21 @@
|
||||
|
||||
try
|
||||
{
|
||||
await FileService.DeleteFileAsync(_fileid);
|
||||
await logger.LogInformation("File Deleted {File}", _fileid);
|
||||
await FileService.DeleteFileAsync(FileId);
|
||||
await logger.LogInformation("File Deleted {File}", FileId);
|
||||
_message = "<br /><div class=\"alert alert-success\" role=\"alert\">File Deleted</div>";
|
||||
await GetFiles();
|
||||
_fileid = -1;
|
||||
FileId = -1;
|
||||
await SetImage();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting File {File} {Error}", _fileid, ex.Message);
|
||||
await logger.LogError(ex, "Error Deleting File {File} {Error}", FileId, ex.Message);
|
||||
_message = "<br /><div class=\"alert alert-danger\" role=\"alert\">Error Deleting File</div>";
|
||||
}
|
||||
}
|
||||
|
||||
public int GetFileId() => _fileid;
|
||||
public int GetFileId() => FileId;
|
||||
|
||||
}
|
||||
|
@ -3,9 +3,8 @@
|
||||
@using Oqtane.Modules.Controls
|
||||
@namespace Oqtane.Modules.HtmlText
|
||||
@inherits ModuleBase
|
||||
@inject IHtmlTextService HtmlTextService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject HttpClient http
|
||||
@inject SiteState sitestate
|
||||
|
||||
@if (_content != null)
|
||||
{
|
||||
@ -14,12 +13,28 @@
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
|
||||
@if (!string.IsNullOrEmpty(_content))
|
||||
{
|
||||
<br /><br />
|
||||
<br />
|
||||
<br />
|
||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
|
||||
public override string Title => "Edit Html/Text";
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
|
||||
// the following resources should be declared in the RichTextEditor component however the framework currently only supports resource management for modules and themes
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.bubble.css" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.snow.css" },
|
||||
new Resource { ResourceType = ResourceType.Script, Url = "js/quill1.3.6.min.js" },
|
||||
new Resource { ResourceType = ResourceType.Script, Url = "js/quill-blot-formatter.min.js" },
|
||||
new Resource { ResourceType = ResourceType.Script, Url = "js/quill-interop.js" }
|
||||
};
|
||||
|
||||
private RichTextEditor RichTextEditorHtml;
|
||||
private string _content = null;
|
||||
private string _createdby;
|
||||
@ -27,16 +42,11 @@
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
|
||||
public override string Title => "Edit Html/Text";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var htmltextservice = new HtmlTextService(http, sitestate);
|
||||
var htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||
if (htmltext != null)
|
||||
{
|
||||
_content = htmltext.Content;
|
||||
@ -65,19 +75,18 @@
|
||||
|
||||
try
|
||||
{
|
||||
var htmltextservice = new HtmlTextService(http, sitestate);
|
||||
var htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||
if (htmltext != null)
|
||||
{
|
||||
htmltext.Content = content;
|
||||
await htmltextservice.UpdateHtmlTextAsync(htmltext);
|
||||
await HtmlTextService.UpdateHtmlTextAsync(htmltext);
|
||||
}
|
||||
else
|
||||
{
|
||||
htmltext = new HtmlTextInfo();
|
||||
htmltext.ModuleId = ModuleState.ModuleId;
|
||||
htmltext.Content = content;
|
||||
await htmltextservice.AddHtmlTextAsync(htmltext);
|
||||
await HtmlTextService.AddHtmlTextAsync(htmltext);
|
||||
}
|
||||
|
||||
await logger.LogInformation("Html/Text Content Saved {HtmlText}", htmltext);
|
||||
|
@ -1,32 +1,28 @@
|
||||
@using Oqtane.Modules.HtmlText.Services
|
||||
@using Oqtane.Modules.HtmlText.Models
|
||||
@namespace Oqtane.Modules.HtmlText
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject HttpClient http
|
||||
@inject SiteState sitestate
|
||||
@inject IHtmlTextService HtmlTextService
|
||||
|
||||
@((MarkupString)content)
|
||||
|
||||
@if (PageState.EditMode)
|
||||
{
|
||||
<br />
|
||||
}
|
||||
<ActionLink Action="Edit" />
|
||||
@if (PageState.EditMode)
|
||||
{
|
||||
<br /><br />
|
||||
<br /><ActionLink Action="Edit" /><br /><br />
|
||||
}
|
||||
|
||||
@code {
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
||||
};
|
||||
|
||||
private string content = "";
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var htmltextservice = new HtmlTextService(http, sitestate);
|
||||
var htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||
if (htmltext != null)
|
||||
{
|
||||
content = htmltext.Content;
|
||||
|
@ -8,7 +8,7 @@ using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Modules.HtmlText.Services
|
||||
{
|
||||
public class HtmlTextService : ServiceBase, IHtmlTextService
|
||||
public class HtmlTextService : ServiceBase, IHtmlTextService, IService
|
||||
{
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
|
@ -6,10 +6,11 @@ using Oqtane.Services;
|
||||
using System;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.UI;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Oqtane.Modules
|
||||
{
|
||||
public class ModuleBase : ComponentBase, IModuleControl
|
||||
public abstract class ModuleBase : ComponentBase, IModuleControl
|
||||
{
|
||||
private Logger _logger;
|
||||
|
||||
@ -37,6 +38,9 @@ namespace Oqtane.Modules
|
||||
|
||||
public virtual bool UseAdminContainer { get { return true; } }
|
||||
|
||||
public virtual List<Resource> Resources { get; set; }
|
||||
|
||||
|
||||
// path method
|
||||
|
||||
public string ModulePath()
|
||||
|
@ -6,7 +6,7 @@
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>0.9.1</Version>
|
||||
<Version>1.0.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -27,10 +27,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.0-rc1.20223.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.0-rc1.20223.4" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="3.1.2" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="3.2.0-rc1.20217.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="3.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="3.1.4" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="3.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -4,12 +4,16 @@ using System.Threading.Tasks;
|
||||
using Oqtane.Services;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Providers;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using System.IO.Compression;
|
||||
using System.IO;
|
||||
|
||||
namespace Oqtane.Client
|
||||
{
|
||||
@ -19,10 +23,9 @@ namespace Oqtane.Client
|
||||
{
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
builder.RootComponents.Add<App>("app");
|
||||
HttpClient httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)};
|
||||
|
||||
builder.Services.AddSingleton(
|
||||
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }
|
||||
);
|
||||
builder.Services.AddSingleton(httpClient);
|
||||
builder.Services.AddOptions();
|
||||
|
||||
// register auth services
|
||||
@ -57,14 +60,16 @@ namespace Oqtane.Client
|
||||
builder.Services.AddScoped<ISqlService, SqlService>();
|
||||
builder.Services.AddScoped<ISystemService, SystemService>();
|
||||
|
||||
await LoadClientAssemblies(httpClient);
|
||||
|
||||
// dynamically register module contexts and repository services
|
||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
Type[] implementationtypes = assembly.GetTypes()
|
||||
.Where(item => item.GetInterfaces().Contains(typeof(IService)))
|
||||
.ToArray();
|
||||
foreach (Type implementationtype in implementationtypes)
|
||||
var implementationTypes = assembly.GetTypes()
|
||||
.Where(item => item.GetInterfaces().Contains(typeof(IService)));
|
||||
|
||||
foreach (Type implementationtype in implementationTypes)
|
||||
{
|
||||
Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name));
|
||||
if (servicetype != null)
|
||||
@ -76,9 +81,62 @@ namespace Oqtane.Client
|
||||
builder.Services.AddScoped(implementationtype, implementationtype); // no interface defined for service
|
||||
}
|
||||
}
|
||||
|
||||
assembly.GetInstances<IClientStartup>()
|
||||
.ToList()
|
||||
.ForEach(x => x.ConfigureServices(builder.Services));
|
||||
}
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
}
|
||||
|
||||
private static async Task LoadClientAssemblies(HttpClient http)
|
||||
{
|
||||
// get list of loaded assemblies on the client
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToList();
|
||||
|
||||
// get assemblies from server and load into client app domain
|
||||
var zip = await http.GetByteArrayAsync($"/~/api/Installation/load");
|
||||
|
||||
// asemblies and debug symbols are packaged in a zip file
|
||||
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
||||
{
|
||||
Dictionary<string, byte[]> dlls = new Dictionary<string, byte[]>();
|
||||
Dictionary<string, byte[]> pdbs = new Dictionary<string, byte[]>();
|
||||
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
{
|
||||
if (!assemblies.Contains(Path.GetFileNameWithoutExtension(entry.Name)))
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
entry.Open().CopyTo(memoryStream);
|
||||
byte[] file = memoryStream.ToArray();
|
||||
switch (Path.GetExtension(entry.Name))
|
||||
{
|
||||
case ".dll":
|
||||
dlls.Add(entry.Name, file);
|
||||
break;
|
||||
case ".pdb":
|
||||
pdbs.Add(entry.Name, file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in dlls)
|
||||
{
|
||||
if (pdbs.ContainsKey(item.Key))
|
||||
{
|
||||
Assembly.Load(item.Value, pdbs[item.Key]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assembly.Load(item.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,16 +5,21 @@ using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System;
|
||||
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
public class AliasService : ServiceBase, IAliasService
|
||||
{
|
||||
|
||||
public AliasService(HttpClient http) :base(http) { }
|
||||
|
||||
private string Apiurl => CreateApiUrl("Alias");
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public AliasService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl(_siteState.Alias, "Alias");
|
||||
|
||||
public async Task<List<Alias>> GetAliasesAsync()
|
||||
{
|
||||
|
@ -12,7 +12,6 @@ namespace Oqtane.Services
|
||||
Task UpdateModuleDefinitionAsync(ModuleDefinition moduleDefinition);
|
||||
Task InstallModuleDefinitionsAsync();
|
||||
Task DeleteModuleDefinitionAsync(int moduleDefinitionId, int siteId);
|
||||
Task LoadModuleDefinitionsAsync(int siteId, Runtime runtime);
|
||||
Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId);
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,20 @@ using System.Threading.Tasks;
|
||||
using System.Net.Http;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
public class JobLogService : ServiceBase, IJobLogService
|
||||
{
|
||||
public JobLogService(HttpClient http) :base(http) { }
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
private string Apiurl => CreateApiUrl("JobLog");
|
||||
public JobLogService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl(_siteState.Alias, "JobLog");
|
||||
|
||||
public async Task<List<JobLog>> GetJobLogsAsync()
|
||||
{
|
||||
|
@ -3,15 +3,21 @@ using System.Threading.Tasks;
|
||||
using System.Net.Http;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
public class JobService : ServiceBase, IJobService
|
||||
{
|
||||
public JobService(HttpClient http) : base(http) { }
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
private string Apiurl => CreateApiUrl("Job");
|
||||
public JobService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl(_siteState.Alias, "Job");
|
||||
|
||||
public async Task<List<Job>> GetJobsAsync()
|
||||
{
|
||||
List<Job> jobs = await GetJsonAsync<List<Job>>(Apiurl);
|
||||
|
@ -49,46 +49,9 @@ namespace Oqtane.Services
|
||||
await DeleteAsync($"{Apiurl}/{moduleDefinitionId}?siteid={siteId}");
|
||||
}
|
||||
|
||||
public async Task LoadModuleDefinitionsAsync(int siteId, Runtime runtime)
|
||||
{
|
||||
// get list of modules from the server
|
||||
List<ModuleDefinition> moduledefinitions = await GetModuleDefinitionsAsync(siteId);
|
||||
|
||||
// download assemblies to browser when running client-side Blazor
|
||||
if (runtime == Runtime.WebAssembly)
|
||||
{
|
||||
// get list of loaded assemblies on the client ( in the client-side hosting module the browser client has its own app domain )
|
||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
foreach (ModuleDefinition moduledefinition in moduledefinitions)
|
||||
{
|
||||
// if a module has dependencies, check if they are loaded
|
||||
if (moduledefinition.Dependencies != "")
|
||||
{
|
||||
foreach (string dependency in moduledefinition.Dependencies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
string assemblyname = dependency.Replace(".dll", "");
|
||||
if (assemblies.Where(item => item.FullName.StartsWith(assemblyname + ",")).FirstOrDefault() == null)
|
||||
{
|
||||
// download assembly from server and load
|
||||
var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{assemblyname}.dll");
|
||||
Assembly.Load(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if the module assembly is loaded
|
||||
if (assemblies.Where(item => item.FullName.StartsWith(moduledefinition.AssemblyName + ",")).FirstOrDefault() == null)
|
||||
{
|
||||
// download assembly from server and load
|
||||
var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{moduledefinition.AssemblyName}.dll");
|
||||
Assembly.Load(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public async Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId)
|
||||
{
|
||||
await PostJsonAsync($"{Apiurl}?moduleid={moduleId.ToString()}", moduleDefinition);
|
||||
await PostJsonAsync($"{Apiurl}?moduleid={moduleId}", moduleDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,13 +135,13 @@ namespace Oqtane.Services
|
||||
//TODO Missing content JSON validation
|
||||
}
|
||||
|
||||
// create an API Url which is tenant agnostic ( for use with entities in the MasterDB )
|
||||
// create an API Url which is tenant agnostic ( for use during installation )
|
||||
public string CreateApiUrl(string serviceName)
|
||||
{
|
||||
return CreateApiUrl(null, serviceName);
|
||||
}
|
||||
|
||||
// create an API Url which is tenant aware ( for use with entities in the TenantDB )
|
||||
// create an API Url which is tenant aware ( for use with repositories )
|
||||
public string CreateApiUrl(Alias alias, string serviceName)
|
||||
{
|
||||
string apiurl = "/";
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Oqtane.Models;
|
||||
using System;
|
||||
using Oqtane.Models;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net.Http;
|
||||
using System.Linq;
|
||||
@ -103,10 +104,10 @@ namespace Oqtane.Services
|
||||
public async Task UpdateSettingsAsync(Dictionary<string, string> settings, string entityName, int entityId)
|
||||
{
|
||||
var settingsList = await GetJsonAsync<List<Setting>>($"{Apiurl}?entityname={entityName}&entityid={entityId}");
|
||||
|
||||
|
||||
foreach (KeyValuePair<string, string> kvp in settings)
|
||||
{
|
||||
Setting setting = settingsList.FirstOrDefault(item => item.SettingName == kvp.Key);
|
||||
Setting setting = settingsList.FirstOrDefault(item => item.SettingName.Equals(kvp.Key,StringComparison.OrdinalIgnoreCase));
|
||||
if (setting == null)
|
||||
{
|
||||
setting = new Setting();
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -6,9 +7,14 @@ namespace Oqtane.Services
|
||||
{
|
||||
public class SqlService : ServiceBase, ISqlService
|
||||
{
|
||||
public SqlService(HttpClient http) : base(http) { }
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
private string Apiurl => CreateApiUrl("Sql");
|
||||
public SqlService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl(_siteState.Alias, "Sql");
|
||||
|
||||
public async Task<SqlQuery> ExecuteQueryAsync(SqlQuery sqlquery)
|
||||
{
|
||||
|
@ -3,14 +3,20 @@ using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
public class TenantService : ServiceBase, ITenantService
|
||||
{
|
||||
public TenantService(HttpClient http) : base(http) { }
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
private string Apiurl => CreateApiUrl("Tenant");
|
||||
public TenantService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl(_siteState.Alias, "Tenant");
|
||||
|
||||
public async Task<List<Tenant>> GetTenantsAsync()
|
||||
{
|
||||
|
@ -23,33 +23,6 @@ namespace Oqtane.Services
|
||||
public async Task<List<Theme>> GetThemesAsync()
|
||||
{
|
||||
List<Theme> themes = await GetJsonAsync<List<Theme>>(Apiurl);
|
||||
|
||||
// get list of loaded assemblies
|
||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
foreach (Theme theme in themes)
|
||||
{
|
||||
if (theme.Dependencies != "")
|
||||
{
|
||||
foreach (string dependency in theme.Dependencies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
string assemblyname = dependency.Replace(".dll", "");
|
||||
if (assemblies.Where(item => item.FullName.StartsWith(assemblyname + ",")).FirstOrDefault() == null)
|
||||
{
|
||||
// download assembly from server and load
|
||||
var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{assemblyname}.dll");
|
||||
Assembly.Load(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (assemblies.Where(item => item.FullName.StartsWith(theme.AssemblyName + ",")).FirstOrDefault() == null)
|
||||
{
|
||||
// download assembly from server and load
|
||||
var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{theme.AssemblyName}.dll");
|
||||
Assembly.Load(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
return themes.OrderBy(item => item.Name).ToList();
|
||||
}
|
||||
|
||||
|
@ -28,9 +28,8 @@
|
||||
@code {
|
||||
public override string Panes => "Content";
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
await IncludeCSS("Theme.css");
|
||||
}
|
||||
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" }
|
||||
};
|
||||
}
|
@ -17,7 +17,6 @@ namespace Oqtane.Themes
|
||||
[CascadingParameter]
|
||||
protected Module ModuleState { get; set; }
|
||||
|
||||
public virtual string Name { get; set; }
|
||||
|
||||
public string ThemePath()
|
||||
{
|
||||
|
@ -149,7 +149,7 @@
|
||||
<label for="Pane" class="control-label">Pane: </label>
|
||||
<select class="form-control" @bind="@_pane">
|
||||
<option value=""><Select Pane></option>
|
||||
@foreach (string pane in PageState.Page.Panes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
@foreach (string pane in PageState.Page.Panes)
|
||||
{
|
||||
<option value="@pane">@pane Pane</option>
|
||||
}
|
||||
@ -275,7 +275,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
var panes = PageState.Page.Panes.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
var panes = PageState.Page.Panes;
|
||||
_pane = panes.Count() == 1 ? panes.SingleOrDefault() : "";
|
||||
var themes = await ThemeService.GetThemesAsync();
|
||||
_containers = ThemeService.GetContainerTypes(themes);
|
||||
|
@ -60,7 +60,7 @@ namespace Oqtane.Themes.Controls
|
||||
actionList.Add(new ActionViewModel {Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m)});
|
||||
}
|
||||
|
||||
foreach (string pane in PageState.Page.Panes.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries))
|
||||
foreach (string pane in PageState.Page.Panes)
|
||||
{
|
||||
if (pane != ModuleState.Pane)
|
||||
{
|
||||
|
@ -2,6 +2,5 @@
|
||||
{
|
||||
public interface IContainerControl
|
||||
{
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,12 @@
|
||||
@code {
|
||||
public override string Panes => string.Empty;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
// go to https://www.bootstrapcdn.com/bootswatch/ and take your favorite theme
|
||||
//<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css" rel="stylesheet" integrity="sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM" crossorigin="anonymous">
|
||||
await LoadBootstrapTheme("https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css","sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM");
|
||||
await IncludeCSS("Theme.css");
|
||||
}
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "BootswatchCyborg.css" },
|
||||
// remote stylesheets can be linked using the format below, however we want the default theme to display properly in local development scenarios where an Internet connection is not available
|
||||
//new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css", Integrity = "sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM", CrossOrigin = "anonymous" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" }
|
||||
};
|
||||
|
||||
}
|
||||
|
9
Oqtane.Client/Themes/OqtaneTheme/NoTitle.razor
Normal file
9
Oqtane.Client/Themes/OqtaneTheme/NoTitle.razor
Normal file
@ -0,0 +1,9 @@
|
||||
@namespace Oqtane.Themes.OqtaneTheme
|
||||
@inherits ContainerBase
|
||||
<div class="container">
|
||||
@if (PageState.EditMode)
|
||||
{
|
||||
<ModuleActions />
|
||||
}
|
||||
<ModuleInstance />
|
||||
</div>
|
@ -1,7 +1,9 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.UI;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Themes
|
||||
@ -11,31 +13,21 @@ namespace Oqtane.Themes
|
||||
[Inject]
|
||||
protected IJSRuntime JSRuntime { get; set; }
|
||||
|
||||
// optional interface properties
|
||||
|
||||
[CascadingParameter]
|
||||
protected PageState PageState { get; set; }
|
||||
public virtual string Panes { get; set; }
|
||||
public virtual List<Resource> Resources { get; set; }
|
||||
|
||||
// path method
|
||||
|
||||
public string ThemePath()
|
||||
{
|
||||
return "Themes/" + GetType().Namespace + "/";
|
||||
}
|
||||
|
||||
public async Task IncludeCSS(string Url)
|
||||
{
|
||||
if (!Url.StartsWith("http"))
|
||||
{
|
||||
Url = ThemePath() + Url;
|
||||
}
|
||||
var interop = new Interop(JSRuntime);
|
||||
await interop.IncludeCSS("Theme", Url);
|
||||
}
|
||||
|
||||
public async Task LoadBootstrapTheme(string url, string integrity = null)
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
string crossorigin = string.IsNullOrEmpty(integrity) ? string.Empty : "anonymous";
|
||||
await interop.IncludeLink("bootstrap", "stylesheet", url, "text/css", integrity, crossorigin);
|
||||
}
|
||||
// url methods
|
||||
|
||||
public string NavigateUrl()
|
||||
{
|
||||
|
@ -7,7 +7,8 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="mx-auto text-center">
|
||||
<img src="oqtane.png" />
|
||||
<img src="oqtane-black.png" />
|
||||
<div style="font-weight: bold">Version: @Constants.Version</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="app-rule" />
|
||||
|
@ -118,6 +118,22 @@ namespace Oqtane.UI
|
||||
}
|
||||
}
|
||||
|
||||
public Task RemoveElementsById(string prefix, string first, string last)
|
||||
{
|
||||
try
|
||||
{
|
||||
_jsRuntime.InvokeAsync<string>(
|
||||
"interop.removeElementsById",
|
||||
prefix, first, last);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ValueTask<string> GetElementByName(string name)
|
||||
{
|
||||
try
|
||||
|
@ -32,7 +32,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
_paneadminborder = "";
|
||||
_paneadminborder = "container";
|
||||
_panetitle = "";
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
@inject IPageService PageService
|
||||
@inject IUserService UserService
|
||||
@inject IModuleService ModuleService
|
||||
@inject IModuleDefinitionService ModuleDefinitionService
|
||||
@inject ILogService LogService
|
||||
@implements IHandleAfterRender
|
||||
|
||||
@ -157,7 +156,6 @@
|
||||
|
||||
if (PageState == null || reload >= Reload.Site)
|
||||
{
|
||||
await ModuleDefinitionService.LoadModuleDefinitionsAsync(site.SiteId, runtime);
|
||||
pages = await PageService.GetPagesAsync(site.SiteId);
|
||||
}
|
||||
else
|
||||
@ -181,7 +179,7 @@
|
||||
// extract admin route elements from path
|
||||
var segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
int result;
|
||||
// check if path has moduleid and control specification ie. page/moduleid/control/
|
||||
// check if path has moduleid and action specification ie. pagename/moduleid/action/
|
||||
if (segments.Length >= 2 && int.TryParse(segments[segments.Length - 2], out result))
|
||||
{
|
||||
action = segments[segments.Length - 1];
|
||||
@ -190,7 +188,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
// check if path has only moduleid specification ie. page/moduleid/
|
||||
// check if path has moduleid specification ie. pagename/moduleid/
|
||||
if (segments.Length >= 1 && int.TryParse(segments[segments.Length - 1], out result))
|
||||
{
|
||||
moduleid = result;
|
||||
@ -240,21 +238,7 @@
|
||||
{
|
||||
page = await ProcessPage(page, site, user);
|
||||
|
||||
_pagestate = new PageState
|
||||
{
|
||||
Alias = alias,
|
||||
Site = site,
|
||||
Pages = pages,
|
||||
Page = page,
|
||||
User = user,
|
||||
Uri = new Uri(_absoluteUri, UriKind.Absolute),
|
||||
QueryString = querystring,
|
||||
ModuleId = moduleid,
|
||||
Action = action,
|
||||
Runtime = runtime
|
||||
};
|
||||
|
||||
if (PageState != null && (PageState.ModuleId != _pagestate.ModuleId || PageState.Action != _pagestate.Action))
|
||||
if (PageState != null && (PageState.ModuleId != moduleid || PageState.Action != action))
|
||||
{
|
||||
reload = Reload.Page;
|
||||
}
|
||||
@ -262,15 +246,29 @@
|
||||
if (PageState == null || reload >= Reload.Page)
|
||||
{
|
||||
modules = await ModuleService.GetModulesAsync(site.SiteId);
|
||||
modules = ProcessModules(modules, page.PageId, _pagestate.ModuleId, _pagestate.Action, page.Panes, site.DefaultContainerType);
|
||||
(page, modules) = ProcessModules(page, modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType);
|
||||
}
|
||||
else
|
||||
{
|
||||
modules = PageState.Modules;
|
||||
}
|
||||
_pagestate.Modules = modules;
|
||||
_pagestate.EditMode = editmode;
|
||||
_pagestate.LastSyncDate = lastsyncdate;
|
||||
|
||||
_pagestate = new PageState
|
||||
{
|
||||
Alias = alias,
|
||||
Site = site,
|
||||
Pages = pages,
|
||||
Page = page,
|
||||
User = user,
|
||||
Modules = modules,
|
||||
Uri = new Uri(_absoluteUri, UriKind.Absolute),
|
||||
QueryString = querystring,
|
||||
ModuleId = moduleid,
|
||||
Action = action,
|
||||
EditMode = editmode,
|
||||
LastSyncDate = lastsyncdate,
|
||||
Runtime = runtime
|
||||
};
|
||||
|
||||
OnStateChange?.Invoke(_pagestate);
|
||||
}
|
||||
@ -357,19 +355,30 @@
|
||||
page.ThemeType = site.DefaultThemeType;
|
||||
page.LayoutType = site.DefaultLayoutType;
|
||||
}
|
||||
Type type;
|
||||
|
||||
page.Panes = new List<string>();
|
||||
page.Resources = new List<Resource>();
|
||||
|
||||
string panes = "";
|
||||
Type themetype = Type.GetType(page.ThemeType);
|
||||
var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
|
||||
if (themeobject != null)
|
||||
{
|
||||
panes = themeobject.Panes;
|
||||
page.Resources = ManagePageResources(page.Resources, themeobject.Resources);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(page.LayoutType))
|
||||
{
|
||||
type = Type.GetType(page.LayoutType);
|
||||
}
|
||||
else
|
||||
{
|
||||
type = Type.GetType(page.ThemeType);
|
||||
Type layouttype = Type.GetType(page.LayoutType);
|
||||
var layoutobject = Activator.CreateInstance(layouttype) as ILayoutControl;
|
||||
if (layoutobject != null)
|
||||
{
|
||||
panes = layoutobject.Panes;
|
||||
}
|
||||
}
|
||||
|
||||
var property = type.GetProperty("Panes");
|
||||
page.Panes = (string)property.GetValue(Activator.CreateInstance(type), null);
|
||||
page.Panes = panes.Replace(";", ",").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -379,12 +388,12 @@
|
||||
return page;
|
||||
}
|
||||
|
||||
private List<Module> ProcessModules(List<Module> modules, int pageid, int moduleid, string control, string panes, string defaultcontainertype)
|
||||
private (Page Page, List<Module> Modules) ProcessModules(Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype)
|
||||
{
|
||||
var paneindex = new Dictionary<string, int>();
|
||||
foreach (Module module in modules)
|
||||
{
|
||||
if (module.PageId == pageid || module.ModuleId == moduleid)
|
||||
if (module.PageId == page.PageId || module.ModuleId == moduleid)
|
||||
{
|
||||
var typename = string.Empty;
|
||||
if (module.ModuleDefinition != null)
|
||||
@ -396,63 +405,72 @@
|
||||
typename = Constants.ErrorModule;
|
||||
}
|
||||
|
||||
if (module.ModuleId == moduleid && control != "")
|
||||
if (module.ModuleId == moduleid && action != "")
|
||||
{
|
||||
// check if the module defines custom routes
|
||||
if (module.ModuleDefinition.ControlTypeRoutes != "")
|
||||
{
|
||||
foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (route.StartsWith(control + "="))
|
||||
if (route.StartsWith(action + "="))
|
||||
{
|
||||
typename = route.Replace(control + "=", "");
|
||||
typename = route.Replace(action + "=", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
module.ModuleType = typename.Replace(Constants.ActionToken, control);
|
||||
|
||||
// admin controls need to load additional metadata from the IModuleControl interface
|
||||
if (moduleid == module.ModuleId)
|
||||
{
|
||||
typename = module.ModuleType;
|
||||
// check for core module actions component
|
||||
if (Constants.DefaultModuleActions.Contains(control))
|
||||
{
|
||||
typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, control);
|
||||
}
|
||||
Type moduletype = Type.GetType(typename);
|
||||
if (moduletype != null)
|
||||
{
|
||||
var moduleobject = Activator.CreateInstance(moduletype);
|
||||
module.SecurityAccessLevel = (SecurityAccessLevel)moduletype.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null);
|
||||
module.ControlTitle = (string)moduletype.GetProperty("Title").GetValue(moduleobject);
|
||||
module.Actions = (string)moduletype.GetProperty("Actions").GetValue(moduleobject);
|
||||
module.UseAdminContainer = (bool)moduletype.GetProperty("UseAdminContainer").GetValue(moduleobject);
|
||||
}
|
||||
}
|
||||
module.ModuleType = typename.Replace(Constants.ActionToken, action);
|
||||
}
|
||||
else
|
||||
{
|
||||
module.ModuleType = typename.Replace(Constants.ActionToken, Constants.DefaultAction);
|
||||
}
|
||||
|
||||
// get additional metadata from IModuleControl interface
|
||||
typename = module.ModuleType;
|
||||
if (Constants.DefaultModuleActions.Contains(action))
|
||||
{
|
||||
// core framework module action components
|
||||
typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, action);
|
||||
}
|
||||
Type moduletype = Type.GetType(typename);
|
||||
|
||||
// ensure component implements IModuleControl
|
||||
if (moduletype != null && !moduletype.GetInterfaces().Contains(typeof(IModuleControl)))
|
||||
{
|
||||
module.ModuleType = "";
|
||||
}
|
||||
if (moduletype != null && module.ModuleType != "")
|
||||
{
|
||||
var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources);
|
||||
|
||||
// additional metadata needed for admin components
|
||||
if (module.ModuleId == moduleid && action != "")
|
||||
{
|
||||
module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
|
||||
module.ControlTitle = moduleobject.Title;
|
||||
module.Actions = moduleobject.Actions;
|
||||
module.UseAdminContainer = moduleobject.UseAdminContainer;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure module's pane exists in current page and if not, assign it to the Admin pane
|
||||
if (panes == null || !panes.ToLower().Contains(module.Pane.ToLower()))
|
||||
if (page.Panes == null || page.Panes.FindIndex(item => item.Equals(module.Pane, StringComparison.OrdinalIgnoreCase)) == -1)
|
||||
{
|
||||
module.Pane = Constants.AdminPane;
|
||||
}
|
||||
|
||||
// calculate module position within pane
|
||||
if (paneindex.ContainsKey(module.Pane))
|
||||
if (paneindex.ContainsKey(module.Pane.ToLower()))
|
||||
{
|
||||
paneindex[module.Pane] += 1;
|
||||
paneindex[module.Pane.ToLower()] += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
paneindex.Add(module.Pane, 0);
|
||||
paneindex.Add(module.Pane.ToLower(), 0);
|
||||
}
|
||||
|
||||
module.PaneModuleIndex = paneindex[module.Pane];
|
||||
module.PaneModuleIndex = paneindex[module.Pane.ToLower()];
|
||||
|
||||
if (string.IsNullOrEmpty(module.ContainerType))
|
||||
{
|
||||
@ -461,16 +479,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Module module in modules.Where(item => item.PageId == pageid))
|
||||
foreach (Module module in modules.Where(item => item.PageId == page.PageId))
|
||||
{
|
||||
module.PaneModuleCount = paneindex[module.Pane] + 1;
|
||||
module.PaneModuleCount = paneindex[module.Pane.ToLower()] + 1;
|
||||
}
|
||||
|
||||
return modules;
|
||||
return (page, modules);
|
||||
}
|
||||
|
||||
private List<Resource> ManagePageResources(List<Resource> pageresources, List<Resource> resources)
|
||||
{
|
||||
if (resources != null)
|
||||
{
|
||||
foreach (var resource in resources)
|
||||
{
|
||||
// ensure resource does not exist already
|
||||
if (pageresources.Find(item => item.Url == resource.Url) == null)
|
||||
{
|
||||
pageresources.Add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pageresources;
|
||||
}
|
||||
|
||||
private Runtime GetRuntime()
|
||||
=> RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"))
|
||||
? Runtime.WebAssembly
|
||||
: Runtime.Server;
|
||||
=> RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"))
|
||||
? Runtime.WebAssembly
|
||||
: Runtime.Server;
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
var interop = new Interop(JsRuntime);
|
||||
|
||||
// set page title
|
||||
if (!string.IsNullOrEmpty(PageState.Page.Title))
|
||||
{
|
||||
await interop.UpdateTitle(PageState.Page.Title);
|
||||
@ -20,10 +22,34 @@
|
||||
{
|
||||
await interop.UpdateTitle(PageState.Site.Name + " - " + PageState.Page.Name);
|
||||
}
|
||||
|
||||
// update page resources
|
||||
int stylesheet = 0;
|
||||
int script = 0;
|
||||
foreach (Resource resource in PageState.Page.Resources)
|
||||
{
|
||||
switch (resource.ResourceType)
|
||||
{
|
||||
case ResourceType.Stylesheet:
|
||||
stylesheet += 1;
|
||||
await interop.IncludeLink("app-stylesheet" + stylesheet.ToString("00"), "stylesheet", resource.Url, "text/css", resource.Integrity ?? "", resource.CrossOrigin ?? "");
|
||||
break;
|
||||
case ResourceType.Script:
|
||||
script += 1;
|
||||
await interop.IncludeScript("app-script" + script.ToString("00"), resource.Url, "", "body", resource.Integrity ?? "", resource.CrossOrigin ?? "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// remove any page resources references which are no longer required for this page
|
||||
await interop.RemoveElementsById("app-stylesheet", "app-stylesheet" + (stylesheet + 1).ToString("00"), "");
|
||||
await interop.RemoveElementsById("app-script", "app-script" + (script + 1).ToString("00"), "");
|
||||
|
||||
// add favicon
|
||||
if (PageState.Site.FaviconFileId != null)
|
||||
{
|
||||
await interop.IncludeLink("fav-icon", "shortcut icon", Utilities.ContentUrl(PageState.Alias, PageState.Site.FaviconFileId.Value), "image/x-icon", "", "");
|
||||
}
|
||||
// add PWA support
|
||||
if (PageState.Site.PwaIsEnabled)
|
||||
{
|
||||
await InitializePwa(interop);
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>0.9.1</version>
|
||||
<version>1.0.0</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
@ -16,6 +16,7 @@ using System.Net;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Repository;
|
||||
using Microsoft.AspNetCore.Routing.Constraints;
|
||||
|
||||
// ReSharper disable StringIndexOfIsCultureSpecific.1
|
||||
|
||||
@ -64,7 +65,7 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
foreach (string file in Directory.GetFiles(folder))
|
||||
{
|
||||
files.Add(new Models.File {Name = Path.GetFileName(file), Extension = Path.GetExtension(file)?.Replace(".", "")});
|
||||
files.Add(new Models.File { Name = Path.GetFileName(file), Extension = Path.GetExtension(file)?.Replace(".", "") });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -188,41 +189,54 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
Models.File file = null;
|
||||
Folder folder = _folders.GetFolder(int.Parse(folderid));
|
||||
if (folder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions))
|
||||
{
|
||||
string folderPath = GetFolderPath(folder);
|
||||
CreateDirectory(folderPath);
|
||||
string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
|
||||
// check for allowable file extensions
|
||||
if (Constants.UploadableFiles.Contains(Path.GetExtension(filename).Replace(".", "")))
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = new WebClient();
|
||||
string targetPath = Path.Combine(folderPath, filename);
|
||||
// remove file if it already exists
|
||||
if (System.IO.File.Exists(targetPath))
|
||||
{
|
||||
System.IO.File.Delete(targetPath);
|
||||
}
|
||||
|
||||
client.DownloadFile(url, targetPath);
|
||||
_files.AddFile(CreateFile(filename, folder.FolderId, targetPath));
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url}", url);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (folder == null || !_userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions))
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Download File {Url} {FolderId}", url, folderid);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create,
|
||||
"User Not Authorized To Download File {Url} {FolderId}", url, folderid);
|
||||
HttpContext.Response.StatusCode = 401;
|
||||
return file;
|
||||
}
|
||||
|
||||
string folderPath = GetFolderPath(folder);
|
||||
CreateDirectory(folderPath);
|
||||
|
||||
string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
|
||||
// check for allowable file extensions
|
||||
if (!Constants.UploadableFiles.Split(',')
|
||||
.Contains(Path.GetExtension(filename).ToLower().Replace(".", "")))
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create,
|
||||
"File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
|
||||
return file;
|
||||
}
|
||||
|
||||
if (!filename.IsPathOrFileValid())
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create,
|
||||
$"File Could Not Be Downloaded From Url Due To Its File Name Not Allowed {url}");
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
|
||||
return file;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var client = new WebClient();
|
||||
string targetPath = Path.Combine(folderPath, filename);
|
||||
// remove file if it already exists
|
||||
if (System.IO.File.Exists(targetPath))
|
||||
{
|
||||
System.IO.File.Delete(targetPath);
|
||||
}
|
||||
|
||||
client.DownloadFile(url, targetPath);
|
||||
file = _files.AddFile(CreateFile(filename, folder.FolderId, targetPath));
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create,
|
||||
"File Could Not Be Downloaded From Url {Url}", url);
|
||||
}
|
||||
|
||||
return file;
|
||||
@ -232,46 +246,56 @@ namespace Oqtane.Controllers
|
||||
[HttpPost("upload")]
|
||||
public async Task UploadFile(string folder, IFormFile file)
|
||||
{
|
||||
if (file.Length > 0)
|
||||
if (file.Length <= 0)
|
||||
{
|
||||
string folderPath = "";
|
||||
return;
|
||||
}
|
||||
|
||||
if (int.TryParse(folder, out int folderId))
|
||||
if (!file.FileName.IsPathOrFileValid())
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
|
||||
return;
|
||||
}
|
||||
|
||||
string folderPath = "";
|
||||
|
||||
if (int.TryParse(folder, out int folderId))
|
||||
{
|
||||
Folder virtualFolder = _folders.GetFolder(folderId);
|
||||
if (virtualFolder != null &&
|
||||
_userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions))
|
||||
{
|
||||
Folder virtualFolder = _folders.GetFolder(folderId);
|
||||
if (virtualFolder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions))
|
||||
{
|
||||
folderPath = GetFolderPath(virtualFolder);
|
||||
}
|
||||
folderPath = GetFolderPath(virtualFolder);
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
if (User.IsInRole(Constants.HostRole))
|
||||
{
|
||||
if (User.IsInRole(Constants.HostRole))
|
||||
{
|
||||
folderPath = GetFolderPath(folder);
|
||||
}
|
||||
folderPath = GetFolderPath(folder);
|
||||
}
|
||||
}
|
||||
|
||||
if (folderPath != "")
|
||||
{
|
||||
CreateDirectory(folderPath);
|
||||
using (var stream = new FileStream(Path.Combine(folderPath, file.FileName), FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
if (folderPath != "")
|
||||
string upload = await MergeFile(folderPath, file.FileName);
|
||||
if (upload != "" && folderId != -1)
|
||||
{
|
||||
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)
|
||||
{
|
||||
_files.AddFile(CreateFile(upload, folderId, Path.Combine(folderPath, upload)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Upload File {Folder} {File}", folder, file);
|
||||
HttpContext.Response.StatusCode = 401;
|
||||
_files.AddFile(CreateFile(upload, folderId, Path.Combine(folderPath, upload)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create,
|
||||
"User Not Authorized To Upload File {Folder} {File}", folder, file);
|
||||
HttpContext.Response.StatusCode = 401;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> MergeFile(string folder, string filename)
|
||||
@ -282,7 +306,8 @@ namespace Oqtane.Controllers
|
||||
string token = ".part_";
|
||||
string parts = Path.GetExtension(filename)?.Replace(token, ""); // returns "x_y"
|
||||
int totalparts = int.Parse(parts?.Substring(parts.IndexOf("_") + 1));
|
||||
filename = filename?.Substring(0, filename.IndexOf(token)); // base filename
|
||||
|
||||
filename = Path.GetFileNameWithoutExtension(filename); // base filename
|
||||
string[] fileParts = Directory.GetFiles(folder, filename + token + "*"); // list of all file parts
|
||||
|
||||
// if all of the file parts exist ( note that file parts can arrive out of order )
|
||||
@ -317,7 +342,7 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
|
||||
// check for allowable file extensions
|
||||
if (!Constants.UploadableFiles.Contains(Path.GetExtension(filename)?.Replace(".", "")))
|
||||
if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(filename)?.ToLower().Replace(".", "")))
|
||||
{
|
||||
System.IO.File.Delete(Path.Combine(folder, filename + ".tmp"));
|
||||
}
|
||||
@ -339,13 +364,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
|
||||
// clean up file parts which are more than 2 hours old ( which can happen if a prior file upload failed )
|
||||
fileParts = Directory.GetFiles(folder, "*" + token + "*");
|
||||
foreach (string filepart in fileParts)
|
||||
var cleanupFiles = Directory.EnumerateFiles(folder, "*" + token + "*")
|
||||
.Where(f => Path.GetExtension(f).StartsWith(token));
|
||||
|
||||
foreach (var file in cleanupFiles)
|
||||
{
|
||||
DateTime createddate = System.IO.File.GetCreationTime(filepart).ToUniversalTime();
|
||||
if (createddate < DateTime.UtcNow.AddHours(-2))
|
||||
var createdDate = System.IO.File.GetCreationTime(file).ToUniversalTime();
|
||||
if (createdDate < DateTime.UtcNow.AddHours(-2))
|
||||
{
|
||||
System.IO.File.Delete(filepart);
|
||||
System.IO.File.Delete(file);
|
||||
}
|
||||
}
|
||||
|
||||
@ -396,12 +423,13 @@ namespace Oqtane.Controllers
|
||||
[HttpGet("download/{id}")]
|
||||
public IActionResult Download(int id)
|
||||
{
|
||||
string errorpath = Path.Combine(GetFolderPath("images"), "error.png");
|
||||
Models.File file = _files.GetFile(id);
|
||||
if (file != null)
|
||||
{
|
||||
if (_userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions))
|
||||
{
|
||||
string filepath = Path.Combine(GetFolderPath(file.Folder) , file.Name);
|
||||
string filepath = Path.Combine(GetFolderPath(file.Folder), file.Name);
|
||||
if (System.IO.File.Exists(filepath))
|
||||
{
|
||||
byte[] filebytes = System.IO.File.ReadAllBytes(filepath);
|
||||
@ -411,22 +439,29 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FileId} {FilePath}", id, filepath);
|
||||
HttpContext.Response.StatusCode = 404;
|
||||
return null;
|
||||
if (System.IO.File.Exists(errorpath))
|
||||
{
|
||||
byte[] filebytes = System.IO.File.ReadAllBytes(errorpath);
|
||||
return File(filebytes, "application/octet-stream", file.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access File {FileId}", id);
|
||||
HttpContext.Response.StatusCode = 401;
|
||||
return null;
|
||||
byte[] filebytes = System.IO.File.ReadAllBytes(errorpath);
|
||||
return File(filebytes, "application/octet-stream", file.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Read, "File Not Found {FileId}", id);
|
||||
HttpContext.Response.StatusCode = 404;
|
||||
return null;
|
||||
byte[] filebytes = System.IO.File.ReadAllBytes(errorpath);
|
||||
return File(filebytes, "application/octet-stream", "error.png");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetFolderPath(Folder folder)
|
||||
@ -448,7 +483,7 @@ namespace Oqtane.Controllers
|
||||
string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (string folder in folders)
|
||||
{
|
||||
path = Utilities.PathCombine(path, folder,"\\");
|
||||
path = Utilities.PathCombine(path, folder, "\\");
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
@ -465,11 +500,11 @@ namespace Oqtane.Controllers
|
||||
|
||||
FileInfo fileinfo = new FileInfo(filepath);
|
||||
file.Extension = fileinfo.Extension.ToLower().Replace(".", "");
|
||||
file.Size = (int) fileinfo.Length;
|
||||
file.Size = (int)fileinfo.Length;
|
||||
file.ImageHeight = 0;
|
||||
file.ImageWidth = 0;
|
||||
|
||||
if (Constants.ImageFiles.Contains(file.Extension))
|
||||
if (Constants.ImageFiles.Split(',').Contains(file.Extension.ToLower()))
|
||||
{
|
||||
FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read);
|
||||
using (var image = Image.FromStream(stream))
|
||||
|
@ -10,7 +10,6 @@ using Oqtane.Extensions;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Security;
|
||||
using System.IO;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
@ -33,7 +32,7 @@ namespace Oqtane.Controllers
|
||||
public IEnumerable<Folder> Get(string siteid)
|
||||
{
|
||||
List<Folder> folders = new List<Folder>();
|
||||
foreach(Folder folder in _folders.GetFolders(int.Parse(siteid)))
|
||||
foreach (Folder folder in _folders.GetFolders(int.Parse(siteid)))
|
||||
{
|
||||
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
|
||||
{
|
||||
@ -85,7 +84,7 @@ namespace Oqtane.Controllers
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// POST api/<controller>
|
||||
[HttpPost]
|
||||
[Authorize(Roles = Constants.RegisteredRole)]
|
||||
@ -104,15 +103,25 @@ namespace Oqtane.Controllers
|
||||
new Permission(PermissionNames.Edit, Constants.AdminRole, true),
|
||||
}.EncodePermissions();
|
||||
}
|
||||
if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions))
|
||||
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, permissions))
|
||||
{
|
||||
if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null)
|
||||
if (folder.IsPathValid())
|
||||
{
|
||||
Folder parent = _folders.GetFolder(folder.ParentId.Value);
|
||||
folder.Path = Utilities.PathCombine(parent.Path, folder.Name,"\\");
|
||||
if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null)
|
||||
{
|
||||
Folder parent = _folders.GetFolder(folder.ParentId.Value);
|
||||
folder.Path = Utilities.PathCombine(parent.Path, folder.Name);
|
||||
}
|
||||
folder.Path = Utilities.PathCombine(folder.Path, "\\");
|
||||
folder = _folders.AddFolder(folder);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Name Not Valid {Folder}", folder);
|
||||
HttpContext.Response.StatusCode = 401;
|
||||
folder = null;
|
||||
}
|
||||
folder = _folders.AddFolder(folder);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -131,13 +140,23 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, folder.FolderId, PermissionNames.Edit))
|
||||
{
|
||||
if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null)
|
||||
if (folder.IsPathValid())
|
||||
{
|
||||
Folder parent = _folders.GetFolder(folder.ParentId.Value);
|
||||
folder.Path = Utilities.PathCombine(parent.Path, folder.Name,"\\");
|
||||
if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null)
|
||||
{
|
||||
Folder parent = _folders.GetFolder(folder.ParentId.Value);
|
||||
folder.Path = Utilities.PathCombine(parent.Path, folder.Name);
|
||||
}
|
||||
folder.Path = Utilities.PathCombine(folder.Path, "\\");
|
||||
folder = _folders.UpdateFolder(folder);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Name Not Valid {Folder}", folder);
|
||||
HttpContext.Response.StatusCode = 401;
|
||||
folder = null;
|
||||
}
|
||||
folder = _folders.UpdateFolder(folder);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4,6 +4,11 @@ using Microsoft.Extensions.Configuration;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Infrastructure;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
@ -55,5 +60,56 @@ namespace Oqtane.Controllers
|
||||
_installationManager.UpgradeFramework();
|
||||
return installation;
|
||||
}
|
||||
|
||||
// GET api/<controller>/load
|
||||
[HttpGet("load")]
|
||||
public IActionResult Load()
|
||||
{
|
||||
if (_config.GetSection("Runtime").Value == "WebAssembly")
|
||||
{
|
||||
// get list of assemblies which should be downloaded to browser
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
|
||||
var list = assemblies.Select(a => a.GetName().Name).ToList();
|
||||
var deps = assemblies.SelectMany(a => a.GetReferencedAssemblies()).Distinct();
|
||||
list.AddRange(deps.Where(a => a.Name.EndsWith(".oqtane", StringComparison.OrdinalIgnoreCase)).Select(a => a.Name));
|
||||
|
||||
// create zip file containing assemblies and debug symbols
|
||||
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
byte[] zipfile;
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
|
||||
{
|
||||
ZipArchiveEntry entry;
|
||||
foreach (string file in list)
|
||||
{
|
||||
entry = archive.CreateEntry(file + ".dll");
|
||||
using (var filestream = new FileStream(Path.Combine(binfolder, file + ".dll"), FileMode.Open, FileAccess.Read))
|
||||
using (var entrystream = entry.Open())
|
||||
{
|
||||
filestream.CopyTo(entrystream);
|
||||
}
|
||||
|
||||
if (System.IO.File.Exists(Path.Combine(binfolder, file + ".pdb")))
|
||||
{
|
||||
entry = archive.CreateEntry(file + ".pdb");
|
||||
using (var filestream = new FileStream(Path.Combine(binfolder, file + ".pdb"), FileMode.Open, FileAccess.Read))
|
||||
using (var entrystream = entry.Open())
|
||||
{
|
||||
filestream.CopyTo(entrystream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
zipfile = memoryStream.ToArray();
|
||||
}
|
||||
return File(zipfile, "application/octet-stream", "oqtane.zip");
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = 401;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ using Oqtane.Repository;
|
||||
using Oqtane.Security;
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
// ReSharper disable StringIndexOfIsCultureSpecific.1
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
@ -23,20 +24,24 @@ namespace Oqtane.Controllers
|
||||
private readonly IModuleDefinitionRepository _moduleDefinitions;
|
||||
private readonly IModuleRepository _modules;
|
||||
private readonly ITenantRepository _tenants;
|
||||
private readonly ISqlRepository _sql;
|
||||
private readonly IUserPermissions _userPermissions;
|
||||
private readonly IInstallationManager _installationManager;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
private readonly IConfigurationRoot _config;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules,ITenantRepository tenants, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ILogManager logger)
|
||||
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules,ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IConfigurationRoot config, IServiceProvider serviceProvider, ILogManager logger)
|
||||
{
|
||||
_moduleDefinitions = moduleDefinitions;
|
||||
_modules = modules;
|
||||
_tenants = tenants;
|
||||
_sql = sql;
|
||||
_userPermissions = userPermissions;
|
||||
_installationManager = installationManager;
|
||||
_environment = environment;
|
||||
_config = config;
|
||||
_serviceProvider = serviceProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
@ -99,75 +104,60 @@ namespace Oqtane.Controllers
|
||||
public void Delete(int id, int siteid)
|
||||
{
|
||||
ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, siteid);
|
||||
if (moduledefinition != null)
|
||||
if (moduledefinition != null )
|
||||
{
|
||||
if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType))
|
||||
if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType) && Utilities.GetAssemblyName(moduledefinition.ServerManagerType) != "Oqtane.Server")
|
||||
{
|
||||
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
||||
if (moduletype != null && moduletype.GetInterface("IInstallable") != null)
|
||||
|
||||
foreach (Tenant tenant in _tenants.GetTenants())
|
||||
{
|
||||
foreach (Tenant tenant in _tenants.GetTenants())
|
||||
try
|
||||
{
|
||||
try
|
||||
if (moduletype.GetInterface("IInstallable") != null)
|
||||
{
|
||||
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
|
||||
((IInstallable)moduleobject).Uninstall(tenant);
|
||||
}
|
||||
catch
|
||||
else
|
||||
{
|
||||
// an error occurred executing the uninstall
|
||||
_sql.ExecuteScript(tenant, moduletype.Assembly, Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + ".Uninstall.sql");
|
||||
}
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "{ModuleDefinitionName} Uninstalled For Tenant {Tenant}", moduledefinition.ModuleDefinitionName, tenant.Name);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Uninstalling {ModuleDefinitionName} For Tenant {Tenant} {Error}", moduledefinition.ModuleDefinitionName, tenant.Name, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// format root assembly name
|
||||
string assemblyname = Utilities.GetAssemblyName(moduledefinition.ModuleDefinitionName);
|
||||
if (assemblyname != "Oqtane.Client")
|
||||
{
|
||||
assemblyname = assemblyname.Replace(".Client", "");
|
||||
|
||||
|
||||
// clean up module static resource folder
|
||||
string folder = Path.Combine(_environment.WebRootPath, Path.Combine("Modules",assemblyname));
|
||||
string folder = Path.Combine(_environment.WebRootPath, Path.Combine("Modules", Utilities.GetTypeName(moduledefinition.ModuleDefinitionName)));
|
||||
if (Directory.Exists(folder))
|
||||
{
|
||||
Directory.Delete(folder, true);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Removed For {AssemblynName}", assemblyname);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
|
||||
}
|
||||
|
||||
// remove module assembly from /bin
|
||||
// get root assembly name ( note that this only works if modules follow a specific naming convention for their assemblies )
|
||||
string assemblyname = Utilities.GetAssemblyName(moduledefinition.ModuleDefinitionName).ToLower();
|
||||
assemblyname = assemblyname.Replace(".client", "").Replace(".oqtane", "");
|
||||
|
||||
// remove module assemblies from /bin
|
||||
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
foreach (string file in Directory.EnumerateFiles(binfolder, assemblyname + "*.*"))
|
||||
{
|
||||
System.IO.File.Delete(file);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assembly Removed {Filename}", file);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assembly {Filename} Removed For {ModuleDefinitionName}", file, moduledefinition.ModuleDefinitionName);
|
||||
}
|
||||
|
||||
// remove module definition
|
||||
_moduleDefinitions.DeleteModuleDefinition(id, siteid);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition {ModuleDefinitionName} Deleted", moduledefinition.Name);
|
||||
|
||||
// restart application
|
||||
_installationManager.RestartApplication();
|
||||
}
|
||||
|
||||
// remove module definition
|
||||
_moduleDefinitions.DeleteModuleDefinition(id, siteid);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition Deleted {ModuleDefinitionName}", moduledefinition.Name);
|
||||
|
||||
// restart application
|
||||
_installationManager.RestartApplication();
|
||||
}
|
||||
}
|
||||
|
||||
// GET api/<controller>/load/assembyname
|
||||
[HttpGet("load/{assemblyname}")]
|
||||
public IActionResult Load(string assemblyname)
|
||||
{
|
||||
if (Path.GetExtension(assemblyname).ToLower() == ".dll")
|
||||
{
|
||||
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
byte[] file = System.IO.File.ReadAllBytes(Path.Combine(binfolder, assemblyname));
|
||||
return File(file, "application/octet-stream", assemblyname);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Download Assembly {Assembly}", assemblyname);
|
||||
HttpContext.Response.StatusCode = 401;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,13 +175,13 @@ namespace Oqtane.Controllers
|
||||
if (moduleDefinition.Template == "internal")
|
||||
{
|
||||
rootPath = Utilities.PathCombine(rootFolder.FullName,"\\");
|
||||
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, Oqtane.Client";
|
||||
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, Oqtane.Client";
|
||||
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s","\\");
|
||||
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Client.Oqtane";
|
||||
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Client.Oqtane";
|
||||
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Server.Oqtane";
|
||||
}
|
||||
|
||||
@ -204,7 +194,11 @@ namespace Oqtane.Controllers
|
||||
|
||||
if (moduleDefinition.Template == "internal")
|
||||
{
|
||||
// need logic to add embedded scripts to Oqtane.Server.csproj - also you need to remove them on uninstall
|
||||
// add embedded resources to project
|
||||
List<string> resources = new List<string>();
|
||||
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name + "s", "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.1.0.0.sql"));
|
||||
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name + "s", "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Uninstall.sql"));
|
||||
EmbedResourceFiles(Utilities.PathCombine(rootPath, "Oqtane.Server", "Oqtane.Server.csproj"), resources);
|
||||
}
|
||||
|
||||
_installationManager.RestartApplication();
|
||||
@ -253,5 +247,19 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EmbedResourceFiles(string projectfile, List<string> resources)
|
||||
{
|
||||
XDocument project = XDocument.Load(projectfile);
|
||||
var itemGroup = project.Descendants("ItemGroup").Descendants("EmbeddedResource").FirstOrDefault().Parent;
|
||||
if (itemGroup != null)
|
||||
{
|
||||
foreach (var resource in resources)
|
||||
{
|
||||
itemGroup.Add(new XElement("EmbeddedResource", new XAttribute("Include", resource)));
|
||||
}
|
||||
}
|
||||
project.Save(projectfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -166,6 +166,7 @@ namespace Oqtane.Controllers
|
||||
page.EditMode = false;
|
||||
page.ThemeType = parent.ThemeType;
|
||||
page.LayoutType = parent.LayoutType;
|
||||
page.DefaultContainerType = parent.DefaultContainerType;
|
||||
page.Icon = parent.Icon;
|
||||
page.Permissions = new List<Permission> {
|
||||
new Permission(PermissionNames.View, userid, true),
|
||||
|
@ -1,7 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
@ -17,6 +19,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
// GET: api/<controller>
|
||||
[HttpGet]
|
||||
[Authorize(Roles = Constants.HostRole)]
|
||||
public IEnumerable<SiteTemplate> Get()
|
||||
{
|
||||
return _siteTemplates.GetSiteTemplates();
|
||||
|
@ -54,22 +54,20 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
List<Theme> themes = _themes.GetThemes().ToList();
|
||||
Theme theme = themes.Where(item => item.ThemeName == themename).FirstOrDefault();
|
||||
if (theme != null)
|
||||
if (theme != null && Utilities.GetAssemblyName(theme.ThemeName) != "Oqtane.Client")
|
||||
{
|
||||
themename = theme.ThemeName.Substring(0, theme.ThemeName.IndexOf(","));
|
||||
|
||||
string folder = Path.Combine(_environment.WebRootPath, "Themes" , themename);
|
||||
// clean up theme static resource folder
|
||||
string folder = Path.Combine(_environment.WebRootPath, "Themes" , Utilities.GetTypeName(theme.ThemeName));
|
||||
if (Directory.Exists(folder))
|
||||
{
|
||||
Directory.Delete(folder, true);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Static Resources Removed For {ThemeName}", theme.ThemeName);
|
||||
}
|
||||
|
||||
// remove theme assembly from /bin
|
||||
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
foreach (string file in Directory.EnumerateFiles(binfolder, themename + "*.dll"))
|
||||
{
|
||||
System.IO.File.Delete(file);
|
||||
}
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Deleted {ThemeName}", themename);
|
||||
System.IO.File.Delete(Path.Combine(binfolder, Utilities.GetAssemblyName(theme.ThemeName) + ".dll"));
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Assembly {Filename} Removed For {ThemeName}", Utilities.GetAssemblyName(theme.ThemeName) + ".dll", themename);
|
||||
|
||||
_installationManager.RestartApplication();
|
||||
}
|
||||
|
26
Oqtane.Server/Extensions/ApplicationBuilderExtensions.cs
Normal file
26
Oqtane.Server/Extensions/ApplicationBuilderExtensions.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Oqtane.Infrastructure;
|
||||
|
||||
namespace Oqtane.Extensions
|
||||
{
|
||||
public static class ApplicationBuilderExtensions
|
||||
{
|
||||
public static IApplicationBuilder ConfigureOqtaneAssemblies(this IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
var startUps = AppDomain.CurrentDomain
|
||||
.GetOqtaneAssemblies()
|
||||
.SelectMany(x => x.GetInstances<IServerStartup>());
|
||||
|
||||
foreach (var startup in startUps)
|
||||
{
|
||||
startup.Configure(app, env);
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Oqtane.Infrastructure;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
@ -30,6 +31,22 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mvcBuilder;
|
||||
}
|
||||
|
||||
|
||||
public static IMvcBuilder ConfigureOqtaneMvc(this IMvcBuilder mvcBuilder)
|
||||
{
|
||||
var startUps = AppDomain.CurrentDomain
|
||||
.GetOqtaneAssemblies()
|
||||
.SelectMany(x => x.GetInstances<IServerStartup>());
|
||||
|
||||
foreach (var startup in startUps)
|
||||
{
|
||||
startup.ConfigureMvc(mvcBuilder);
|
||||
}
|
||||
|
||||
return mvcBuilder;
|
||||
}
|
||||
}
|
||||
|
@ -8,21 +8,23 @@ using Microsoft.Extensions.Hosting;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.UI;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
public static class OqtaneServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddOqtaneParts(this IServiceCollection services)
|
||||
public static IServiceCollection AddOqtaneParts(this IServiceCollection services, Runtime runtime)
|
||||
{
|
||||
LoadAssemblies();
|
||||
services.AddOqtaneServices();
|
||||
services.AddOqtaneServices(runtime);
|
||||
return services;
|
||||
}
|
||||
|
||||
private static IServiceCollection AddOqtaneServices(this IServiceCollection services)
|
||||
private static IServiceCollection AddOqtaneServices(this IServiceCollection services, Runtime runtime)
|
||||
{
|
||||
if (services is null)
|
||||
{
|
||||
@ -53,11 +55,24 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
services.AddSingleton(hostedServiceType, serviceType);
|
||||
}
|
||||
}
|
||||
|
||||
var startUps = assembly.GetInstances<IServerStartup>();
|
||||
foreach (var startup in startUps)
|
||||
{
|
||||
startup.ConfigureServices(services);
|
||||
}
|
||||
|
||||
if (runtime == Runtime.Server)
|
||||
{
|
||||
assembly.GetInstances<IClientStartup>()
|
||||
.ToList()
|
||||
.ForEach(x => x.ConfigureServices(services));
|
||||
}
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
|
||||
private static void LoadAssemblies()
|
||||
{
|
||||
var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
||||
|
@ -332,10 +332,13 @@ namespace Oqtane.Infrastructure
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
var moduledefinitions = scope.ServiceProvider.GetRequiredService<IModuleDefinitionRepository>();
|
||||
var sql = scope.ServiceProvider.GetRequiredService<ISqlRepository>();
|
||||
foreach (var moduledefinition in moduledefinitions.GetModuleDefinitions())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType) && !string.IsNullOrEmpty(moduledefinition.ReleaseVersions))
|
||||
if (!string.IsNullOrEmpty(moduledefinition.ReleaseVersions) && !string.IsNullOrEmpty(moduledefinition.ServerManagerType))
|
||||
{
|
||||
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
||||
|
||||
string[] versions = moduledefinition.ReleaseVersions.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
|
||||
{
|
||||
@ -351,19 +354,22 @@ namespace Oqtane.Infrastructure
|
||||
if (index == -1) index = 0;
|
||||
for (int i = index; i < versions.Length; i++)
|
||||
{
|
||||
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
||||
if (moduletype != null && moduletype.GetInterface("IInstallable") != null)
|
||||
try
|
||||
{
|
||||
try
|
||||
if (moduletype.GetInterface("IInstallable") != null)
|
||||
{
|
||||
var moduleobject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduletype);
|
||||
((IInstallable)moduleobject).Install(tenant, versions[i]);
|
||||
((IInstallable)moduleobject).Install(tenant, versions[i]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
else
|
||||
{
|
||||
result.Message = "An Error Occurred Installing " + moduledefinition.Name + " - " + ex.Message.ToString();
|
||||
sql.ExecuteScript(tenant, moduletype.Assembly, Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + "." + versions[i] + ".sql");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Message = "An Error Occurred Installing " + moduledefinition.Name + " Version " + versions[i] + " - " + ex.Message.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
var webRootPath = _environment.WebRootPath;
|
||||
|
||||
var install = UnpackPackages(folders, webRootPath);
|
||||
var install = InstallPackages(folders, webRootPath);
|
||||
|
||||
if (install && restart)
|
||||
{
|
||||
@ -36,7 +36,7 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
}
|
||||
|
||||
public static bool UnpackPackages(string folders, string webRootPath)
|
||||
public static bool InstallPackages(string folders, string webRootPath)
|
||||
{
|
||||
bool install = false;
|
||||
string binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
||||
@ -44,14 +44,12 @@ namespace Oqtane.Infrastructure
|
||||
foreach (string folder in folders.Split(','))
|
||||
{
|
||||
string sourceFolder = Path.Combine(webRootPath, folder);
|
||||
|
||||
// create folder if it does not exist
|
||||
if (!Directory.Exists(sourceFolder))
|
||||
{
|
||||
Directory.CreateDirectory(sourceFolder);
|
||||
}
|
||||
|
||||
// iterate through packages
|
||||
// iterate through Nuget packages in source folder
|
||||
foreach (string packagename in Directory.GetFiles(sourceFolder, "*.nupkg"))
|
||||
{
|
||||
string name = Path.GetFileNameWithoutExtension(packagename);
|
||||
@ -89,28 +87,25 @@ namespace Oqtane.Infrastructure
|
||||
// deploy to appropriate locations
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
{
|
||||
string foldername = Path.GetDirectoryName(entry.FullName).Split('\\')[0];
|
||||
string filename = Path.GetFileName(entry.FullName);
|
||||
switch (Path.GetExtension(filename).ToLower())
|
||||
{
|
||||
case ".pdb":
|
||||
case ".dll":
|
||||
if (binFolder != null) entry.ExtractToFile(Path.Combine(binFolder, filename), true);
|
||||
break;
|
||||
case ".png":
|
||||
case ".jpg":
|
||||
case ".jpeg":
|
||||
case ".gif":
|
||||
case ".svg":
|
||||
case ".js":
|
||||
case ".css":
|
||||
string entryPath = Utilities.PathCombine(entry.FullName.Replace("wwwroot", name).Split('/'));
|
||||
filename = Path.Combine(sourceFolder, entryPath);
|
||||
if (!Directory.Exists(Path.GetDirectoryName(filename)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filename));
|
||||
}
|
||||
|
||||
entry.ExtractToFile(filename, true);
|
||||
switch (foldername)
|
||||
{
|
||||
case "lib":
|
||||
filename = Path.Combine(binFolder, filename);
|
||||
ExtractFile(entry, filename);
|
||||
break;
|
||||
case "wwwroot":
|
||||
filename = Path.Combine(sourceFolder, Utilities.PathCombine(entry.FullName.Replace("wwwroot", name).Split('/')));
|
||||
ExtractFile(entry, filename);
|
||||
break;
|
||||
case "content":
|
||||
if (Path.GetDirectoryName(entry.FullName) != "content") // assets must be in subfolders
|
||||
{
|
||||
filename = Path.Combine(webRootPath, Utilities.PathCombine(entry.FullName.Replace("content", "").Split('/')));
|
||||
ExtractFile(entry, filename);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -126,6 +121,15 @@ namespace Oqtane.Infrastructure
|
||||
return install;
|
||||
}
|
||||
|
||||
private static void ExtractFile(ZipArchiveEntry entry, string filename)
|
||||
{
|
||||
if (!Directory.Exists(Path.GetDirectoryName(filename)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filename));
|
||||
}
|
||||
entry.ExtractToFile(filename, true);
|
||||
}
|
||||
|
||||
public void UpgradeFramework()
|
||||
{
|
||||
string folder = Path.Combine(_environment.WebRootPath, "Framework");
|
||||
|
17
Oqtane.Server/Infrastructure/Interfaces/IServerStartup.cs
Normal file
17
Oqtane.Server/Infrastructure/Interfaces/IServerStartup.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
public interface IServerStartup
|
||||
{
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||
void ConfigureServices(IServiceCollection services);
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
void Configure(IApplicationBuilder app, IWebHostEnvironment env);
|
||||
void ConfigureMvc(IMvcBuilder mvcBuilder);
|
||||
}
|
||||
}
|
||||
|
@ -55,9 +55,9 @@ namespace Oqtane.SiteTemplates
|
||||
new Permission(PermissionNames.View, Constants.AdminRole, true),
|
||||
new Permission(PermissionNames.Edit, Constants.AdminRole, true)
|
||||
}.EncodePermissions(),
|
||||
Content = "<p><a href=\"https://www.oqtane.org\" target=\"_new\">Oqtane</a> is an open source <b>modular application framework</b> built from the ground up using modern .NET Core technology. It leverages the revolutionary new Blazor component model to create a <b>fully dynamic</b> web development experience which can be executed on a client or server. Whether you are looking for a platform to <b>accelerate your web development</b> 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.</p>" +
|
||||
"<p align=\"center\"><a href=\"https://www.oqtane.org\" target=\"_new\"><img class=\"img-fluid\" src=\"oqtane.png\"></a></p><p align=\"center\"><a class=\"btn btn-primary\" href=\"https://www.oqtane.org/Community\" target=\"_new\">Join Our Community</a> <a class=\"btn btn-primary\" href=\"https://github.com/oqtane/oqtane.framework\" target=\"_new\">Clone Our Repo</a></p>" +
|
||||
"<p><a href=\"https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\" target=\"_new\">Blazor</a> 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.</p>" +
|
||||
Content = "<p><a href=\"https://www.oqtane.org\" target=\"_new\">Oqtane</a> is an open source <b>modular application framework</b> that provides advanced functionality for developing web and mobile applications on ASP.NET Core. It leverages the revolutionary new Blazor component model to compose a <b>fully dynamic</b> web development experience which can be hosted either client-side or server-side. Whether you are looking for a platform to <b>accelerate your web development</b> 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.</p>" +
|
||||
"<p align=\"center\"><a href=\"https://www.oqtane.org\" target=\"_new\"><img class=\"img-fluid\" src=\"oqtane-white.png\"></a></p><p align=\"center\"><a class=\"btn btn-primary\" href=\"https://www.oqtane.org/Community\" target=\"_new\">Join Our Community</a> <a class=\"btn btn-primary\" href=\"https://github.com/oqtane/oqtane.framework\" target=\"_new\">Clone Our Repo</a></p>" +
|
||||
"<p><a href=\"https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\" target=\"_new\">Blazor</a> is an open source and cross-platform web UI framework for building single-page apps using .NET and C# instead of JavaScript. Blazor WebAssembly relies on Wasm, an open web standard that does not require plugins or code transpilation in order to run natively in a web browser. Blazor Server uses SignalR to host your application on a web server and provide a responsive and robust development experience. Blazor applications work in all modern web browsers, including mobile browsers.</p>" +
|
||||
"<p>Blazor is a feature of <a href=\"https://dotnet.microsoft.com/apps/aspnet\" target=\"_new\">ASP.NET Core 3</a>, the popular cross platform web development framework from Microsoft that extends the <a href=\"https://dotnet.microsoft.com/learn/dotnet/what-is-dotnet\" target=\"_new\" >.NET developer platform</a> with tools and libraries for building web apps.</p>"
|
||||
},
|
||||
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = "Content",
|
||||
@ -132,16 +132,16 @@ namespace Oqtane.SiteTemplates
|
||||
}
|
||||
});
|
||||
|
||||
if (System.IO.File.Exists(Path.Combine(_environment.WebRootPath, "images", "logo.png")))
|
||||
if (System.IO.File.Exists(Path.Combine(_environment.WebRootPath, "images", "logo-white.png")))
|
||||
{
|
||||
string folderpath = Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", site.TenantId.ToString(), "Sites", site.SiteId.ToString(),"\\");
|
||||
System.IO.Directory.CreateDirectory(folderpath);
|
||||
if (!System.IO.File.Exists(Path.Combine(folderpath, "logo.png")))
|
||||
if (!System.IO.File.Exists(Path.Combine(folderpath, "logo-white.png")))
|
||||
{
|
||||
System.IO.File.Copy(Path.Combine(_environment.WebRootPath, "images", "logo.png"), Path.Combine(folderpath, "logo.png"));
|
||||
System.IO.File.Copy(Path.Combine(_environment.WebRootPath, "images", "logo-white.png"), Path.Combine(folderpath, "logo-white.png"));
|
||||
}
|
||||
Folder folder = _folderRepository.GetFolder(site.SiteId, "");
|
||||
Oqtane.Models.File file = _fileRepository.AddFile(new Oqtane.Models.File { FolderId = folder.FolderId, Name = "logo.png", Extension = "png", Size = 8192, ImageHeight = 80, ImageWidth = 250 });
|
||||
Oqtane.Models.File file = _fileRepository.AddFile(new Oqtane.Models.File { FolderId = folder.FolderId, Name = "logo-white.png", Extension = "png", Size = 8192, ImageHeight = 80, ImageWidth = 250 });
|
||||
site.LogoFileId = file.FileId;
|
||||
_siteRepository.UpdateSite(site);
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>0.9.1</Version>
|
||||
<Version>1.0.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -16,33 +16,26 @@
|
||||
<PackageReleaseNotes>Not for production use.</PackageReleaseNotes>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Scripts\Tenant.0.9.1.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Scripts\Master.0.9.0.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.0.9.0.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.0.9.1.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.0.9.2.sql" />
|
||||
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.1.0.0.sql" />
|
||||
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.Uninstall.sql" />
|
||||
<EmbeddedResource Include="Scripts\Master.0.9.0.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.0.9.1.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.0.9.0.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="dbup" Version="4.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="3.2.0-rc1.20223.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="3.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.4.1" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Oqtane.Client\Oqtane.Client.csproj" />
|
||||
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
@ -14,8 +14,6 @@
|
||||
<link id="fav-icon" rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
|
||||
<!-- stub the PWA manifest but defer the assignment of href -->
|
||||
<link id="pwa-manifest" rel="manifest" />
|
||||
<link href="css/quill/quill1.3.6.bubble.css" rel="stylesheet" />
|
||||
<link href="css/quill/quill1.3.6.snow.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
<link href="css/app.css" rel="stylesheet" />
|
||||
</head>
|
||||
@ -25,10 +23,7 @@
|
||||
<component type="typeof(Oqtane.App)" render-mode="Server" />
|
||||
</app>
|
||||
|
||||
<script src="js/site.js"></script>
|
||||
<script src="js/interop.js"></script>
|
||||
<script src="js/quill1.3.6.min.js"></script>
|
||||
<script src="js/quill-blot-formatter.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
|
||||
|
@ -213,8 +213,8 @@ namespace Oqtane.Repository
|
||||
if (moduletype != null)
|
||||
{
|
||||
// get property values from IModule
|
||||
var moduleobject = Activator.CreateInstance(moduletype);
|
||||
moduledefinition = (ModuleDefinition) moduletype.GetProperty("ModuleDefinition").GetValue(moduleobject);
|
||||
var moduleobject = Activator.CreateInstance(moduletype) as IModule;
|
||||
moduledefinition = moduleobject.ModuleDefinition;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -261,8 +261,8 @@ namespace Oqtane.Repository
|
||||
|
||||
moduledefinition = moduledefinitions[index];
|
||||
// actions
|
||||
var modulecontrolobject = Activator.CreateInstance(modulecontroltype);
|
||||
string actions = (string) modulecontroltype.GetProperty("Actions")?.GetValue(modulecontrolobject);
|
||||
var modulecontrolobject = Activator.CreateInstance(modulecontroltype) as IModuleControl;
|
||||
string actions = modulecontrolobject.Actions;
|
||||
if (!string.IsNullOrEmpty(actions))
|
||||
{
|
||||
foreach (string action in actions.Split(','))
|
||||
|
@ -757,6 +757,7 @@ namespace Oqtane.Repository
|
||||
EditMode = pagetemplate.EditMode,
|
||||
ThemeType = "",
|
||||
LayoutType = "",
|
||||
DefaultContainerType = "",
|
||||
Icon = pagetemplate.Icon,
|
||||
Permissions = pagetemplate.PagePermissions,
|
||||
IsPersonalizable = pagetemplate.IsPersonalizable,
|
||||
|
@ -66,8 +66,8 @@ namespace Oqtane.Repository
|
||||
.Where(item => item.GetInterfaces().Contains(typeof(ITheme))).FirstOrDefault();
|
||||
if (themetype != null)
|
||||
{
|
||||
var themeobject = Activator.CreateInstance(themetype);
|
||||
theme = (Theme)themetype.GetProperty("Theme").GetValue(themeobject);
|
||||
var themeobject = Activator.CreateInstance(themetype) as ITheme;
|
||||
theme = themeobject.Theme;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
migration script
|
||||
Version 0.9.1 migration script
|
||||
|
||||
*/
|
||||
|
||||
|
18
Oqtane.Server/Scripts/Tenant.0.9.2.sql
Normal file
18
Oqtane.Server/Scripts/Tenant.0.9.2.sql
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
|
||||
Version 0.9.2 migration script
|
||||
|
||||
*/
|
||||
|
||||
ALTER TABLE [dbo].[Role]
|
||||
ALTER COLUMN [Description] VARCHAR (256) NOT NULL
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[Page] ADD
|
||||
[DefaultContainerType] [nvarchar](200) NULL
|
||||
GO
|
||||
|
||||
UPDATE [dbo].[Page]
|
||||
SET [DefaultContainerType] = ''
|
||||
GO
|
||||
|
@ -14,11 +14,13 @@ using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Security;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.UI;
|
||||
|
||||
namespace Oqtane
|
||||
{
|
||||
@ -26,6 +28,7 @@ namespace Oqtane
|
||||
{
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
private string _webRoot;
|
||||
private Runtime _runtime;
|
||||
|
||||
public Startup(IWebHostEnvironment env)
|
||||
{
|
||||
@ -33,6 +36,9 @@ namespace Oqtane
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
|
||||
Configuration = builder.Build();
|
||||
|
||||
_runtime = (Configuration.GetSection("Runtime").Value == "WebAssembly") ? Runtime.WebAssembly : Runtime.Server;
|
||||
|
||||
_webRoot = env.WebRootPath;
|
||||
AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(env.ContentRootPath, "Data"));
|
||||
}
|
||||
@ -157,7 +163,7 @@ namespace Oqtane
|
||||
services.AddSingleton<IDatabaseManager, DatabaseManager>();
|
||||
|
||||
// install any modules or themes ( this needs to occur BEFORE the assemblies are loaded into the app domain )
|
||||
InstallationManager.UnpackPackages("Modules,Themes", _webRoot);
|
||||
InstallationManager.InstallPackages("Modules,Themes", _webRoot);
|
||||
|
||||
// register transient scoped core services
|
||||
services.AddTransient<IModuleDefinitionRepository, ModuleDefinitionRepository>();
|
||||
@ -187,14 +193,13 @@ namespace Oqtane
|
||||
services.AddTransient<ISqlRepository, SqlRepository>();
|
||||
services.AddTransient<IUpgradeManager, UpgradeManager>();
|
||||
|
||||
// load the external assemblies into the app domain
|
||||
services.AddOqtaneParts();
|
||||
// load the external assemblies into the app domain, install services
|
||||
services.AddOqtaneParts(_runtime);
|
||||
|
||||
services.AddMvc()
|
||||
.AddNewtonsoftJson()
|
||||
.AddOqtaneApplicationParts() // register any Controllers from custom modules
|
||||
.AddNewtonsoftJson();
|
||||
|
||||
|
||||
.ConfigureOqtaneMvc(); // any additional configuration from IStart classes.
|
||||
|
||||
services.AddSwaggerGen(c =>
|
||||
{
|
||||
@ -217,14 +222,12 @@ namespace Oqtane
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
app.UseBlazorFrameworkFiles();
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
@ -237,6 +240,7 @@ namespace Oqtane
|
||||
endpoints.MapControllers();
|
||||
endpoints.MapFallbackToPage("/_Host");
|
||||
});
|
||||
app.ConfigureOqtaneAssemblies(env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
<body>
|
||||
<div>
|
||||
<br /><br /><h1 align="center">Please Wait... Upgrade In Progress....</h1>
|
||||
<img src="https://www.oqtane.org/Portals/0/oqtane.png" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);" />
|
||||
<img src="https://www.oqtane.org/Portals/0/oqtane-black.png" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -0,0 +1 @@
|
||||
/* HtmlText Module Custom Styles */
|
@ -101,6 +101,12 @@
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.app-logo {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
position: fixed;
|
||||
left: 275px;
|
||||
@ -163,7 +169,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
@media (max-width: 767px) {
|
||||
.app-logo {
|
||||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
position: fixed;
|
||||
top: 150px;
|
||||
|
File diff suppressed because it is too large
Load Diff
BIN
Oqtane.Server/wwwroot/images/error.png
Normal file
BIN
Oqtane.Server/wwwroot/images/error.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user