commit
2362faaee1
@ -42,7 +42,7 @@ else
|
||||
<hr />
|
||||
[Module] Module Created Successfully. Use Edit Mode To Add A [Module]. You Can Access The Files At The Following Locations:<br /><br />
|
||||
[RootPath]Client\<br />
|
||||
- [Owner].[Module]s.Module.Client.csproj - client project<br />
|
||||
- [Owner].[Module]s.Client.csproj - client project<br />
|
||||
- _Imports.razor - global imports for module components<br />
|
||||
- Edit.razor - component for adding or editing content<br />
|
||||
- Index.razor - main component for your module **the content you are reading is in this file**<br />
|
||||
@ -51,12 +51,12 @@ else
|
||||
- Services\I[Module]Service.cs - interface for defining service API methods<br />
|
||||
- Services\[Module]Service.cs - implements service API interface methods<br /><br />
|
||||
[RootPath]Package\<br />
|
||||
- [Owner].[Module]s.Module.nuspec - nuget manifest for packaging module<br />
|
||||
- [Owner].[Module]s.Module.Package.csproj - packaging project<br />
|
||||
- [Owner].[Module]s.nuspec - nuget manifest for packaging module<br />
|
||||
- [Owner].[Module]s.Package.csproj - packaging project<br />
|
||||
- debug.cmd - copies assemblies to Oqtane bin folder when in Debug mode<br />
|
||||
- release.cmd - creates nuget package and deploys to Oqtane wwwroot/modules folder when in Release mode<br /><br />
|
||||
[RootPath]Server\<br />
|
||||
- [Owner].[Module]s.Module.Server.csproj - server project<br />
|
||||
- [Owner].[Module]s.Server.csproj - server project<br />
|
||||
- Controllers\[Module]Controller.cs - API methods implemented using a REST pattern<br />
|
||||
- Manager\[Module]Manager.cs - implements optional module interfaces for features such as import/export of content<br />
|
||||
- Repository\I[Module]Repository.cs - interface for defining repository methods<br />
|
||||
@ -65,7 +65,7 @@ else
|
||||
- Scripts\[Owner].[Module].1.0.0.sql - database schema definition script<br /><br />
|
||||
- Scripts\[Owner].[Module].Uninstall.sql - database uninstall script<br /><br />
|
||||
[RootPath]Shared\<br />
|
||||
- [Owner].[Module]s.Module.Shared.csproj - shared project<br />
|
||||
- [Owner].[Module]s.csproj - shared project<br />
|
||||
- Models\[Module].cs - model definition<br /><br />
|
||||
|
||||
<!-- The content above is for informational purposes only and can be safely removed -->
|
||||
|
@ -10,7 +10,7 @@ namespace [Owner].[Module]s.Modules
|
||||
Name = "[Module]",
|
||||
Description = "[Module]",
|
||||
Version = "1.0.0",
|
||||
Dependencies = "[Owner].[Module]s.Module.Shared",
|
||||
Dependencies = "[Owner].[Module]s.Shared.Oqtane",
|
||||
ServerManagerType = "[ServerManagerType]",
|
||||
ReleaseVersions = "1.0.0"
|
||||
};
|
||||
|
@ -7,8 +7,9 @@
|
||||
<Authors>[Owner]</Authors>
|
||||
<Company>[Owner]</Company>
|
||||
<Description>[Description]</Description>
|
||||
<Product>[Owner].[Module]s.Module</Product>
|
||||
<Product>[Owner].[Module]s</Product>
|
||||
<Copyright>[Owner]</Copyright>
|
||||
<AssemblyName>[Owner].[Module]s.Client.Oqtane</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -18,7 +19,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shared\[Owner].[Module]s.Module.Shared.csproj" />
|
||||
<ProjectReference Include="..\Shared\[Owner].[Module]s.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
@ -6,9 +6,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Client\[Owner].[Module]s.Module.Client.csproj" />
|
||||
<ProjectReference Include="..\Server\[Owner].[Module]s.Module.Server.csproj" />
|
||||
<ProjectReference Include="..\Shared\[Owner].[Module]s.Module.Shared.csproj" />
|
||||
<ProjectReference Include="..\Client\[Owner].[Module]s.Client.csproj" />
|
||||
<ProjectReference Include="..\Server\[Owner].[Module]s.Server.csproj" />
|
||||
<ProjectReference Include="..\Shared\[Owner].[Module]s.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>[Owner].[Module]s.Module</id>
|
||||
<id>[Owner].[Module]s</id>
|
||||
<version>1.0.0</version>
|
||||
<authors>[Owner]</authors>
|
||||
<owners>[Owner]</owners>
|
||||
@ -20,12 +20,12 @@
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\Client\bin\Release\netstandard2.1\[Owner].[Module]s.Module.Client.dll" target="lib" />
|
||||
<file src="..\Client\bin\Release\netstandard2.1\[Owner].[Module]s.Module.Client.pdb" target="lib" />
|
||||
<file src="..\Server\bin\Release\netcoreapp3.1\[Owner].[Module]s.Module.Server.dll" target="lib" />
|
||||
<file src="..\Server\bin\Release\netcoreapp3.1\[Owner].[Module]s.Module.Server.pdb" target="lib" />
|
||||
<file src="..\Shared\bin\Release\netstandard2.1\[Owner].[Module]s.Module.Shared.dll" target="lib" />
|
||||
<file src="..\Shared\bin\Release\netstandard2.1\[Owner].[Module]s.Module.Shared.pdb" target="lib" />
|
||||
<file src="..\Client\bin\Release\netstandard2.1\[Owner].[Module]s.Client.Oqtane.dll" target="lib" />
|
||||
<file src="..\Client\bin\Release\netstandard2.1\[Owner].[Module]s.Client.Oqtane.pdb" target="lib" />
|
||||
<file src="..\Server\bin\Release\netcoreapp3.1\[Owner].[Module]s.Server.Oqtane.dll" target="lib" />
|
||||
<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" />
|
||||
</files>
|
||||
</package>
|
@ -1,6 +1,6 @@
|
||||
XCOPY "..\Client\bin\Debug\netstandard2.1\[Owner].[Module]s.Module.Client.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y
|
||||
XCOPY "..\Client\bin\Debug\netstandard2.1\[Owner].[Module]s.Module.Client.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y
|
||||
XCOPY "..\Server\bin\Debug\netcoreapp3.1\[Owner].[Module]s.Module.Server.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y
|
||||
XCOPY "..\Server\bin\Debug\netcoreapp3.1\[Owner].[Module]s.Module.Server.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y
|
||||
XCOPY "..\Shared\bin\Debug\netstandard2.1\[Owner].[Module]s.Module.Shared.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y
|
||||
XCOPY "..\Shared\bin\Debug\netstandard2.1\[Owner].[Module]s.Module.Shared.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y
|
||||
XCOPY "..\Client\bin\Debug\netstandard2.1\[Owner].[Module]s.Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y
|
||||
XCOPY "..\Client\bin\Debug\netstandard2.1\[Owner].[Module]s.Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y
|
||||
XCOPY "..\Server\bin\Debug\netcoreapp3.1\[Owner].[Module]s.Server.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y
|
||||
XCOPY "..\Server\bin\Debug\netcoreapp3.1\[Owner].[Module]s.Server.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y
|
||||
XCOPY "..\Shared\bin\Debug\netstandard2.1\[Owner].[Module]s.Shared.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y
|
||||
XCOPY "..\Shared\bin\Debug\netstandard2.1\[Owner].[Module]s.Shared.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y
|
||||
|
@ -1,2 +1,2 @@
|
||||
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack [Owner].[Module]s.Module.nuspec
|
||||
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack [Owner].[Module]s.nuspec
|
||||
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\wwwroot\Modules\" /Y
|
||||
|
@ -5,11 +5,12 @@
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||
<Version>1.0.0</Version>
|
||||
<Product>[Owner].[Module]s.Module</Product>
|
||||
<Product>[Owner].[Module]s</Product>
|
||||
<Authors>[Owner]</Authors>
|
||||
<Company>[Owner]</Company>
|
||||
<Description>[Description]</Description>
|
||||
<Copyright>[Owner]</Copyright>
|
||||
<AssemblyName>[Owner].[Module]s.Server.Oqtane</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -26,7 +27,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shared\[Owner].[Module]s.Module.Shared.csproj" />
|
||||
<ProjectReference Include="..\Shared\[Owner].[Module]s.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
@ -4,11 +4,12 @@
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Version>1.0.0</Version>
|
||||
<Product>[Owner].[Module].Module</Product>
|
||||
<Product>[Owner].[Module]s</Product>
|
||||
<Authors>[Owner]</Authors>
|
||||
<Company>[Owner]</Company>
|
||||
<Description>[Description]</Description>
|
||||
<Copyright>[Owner]</Copyright>
|
||||
<AssemblyName>[Owner].[Module]s.Shared.Oqtane</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.28621.142
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Module.Client", "Client\[Owner].[Module]s.Module.Client.csproj", "{AA8E58A1-CD09-4208-BF66-A8BB341FD669}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Client", "Client\[Owner].[Module]s.Client.csproj", "{AA8E58A1-CD09-4208-BF66-A8BB341FD669}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Module.Server", "Server\[Owner].[Module]s.Module.Server.csproj", "{04B05448-788F-433D-92C0-FED35122D45A}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Server", "Server\[Owner].[Module]s.Server.csproj", "{04B05448-788F-433D-92C0-FED35122D45A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Module.Shared", "Shared\[Owner].[Module]s.Module.Shared.csproj", "{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Shared", "Shared\[Owner].[Module]s.Shared.csproj", "{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "[Owner].[Module]s.Module.Package", "Package\[Owner].[Module]s.Module.Package.csproj", "{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "[Owner].[Module]s.Package", "Package\[Owner].[Module]s.Package.csproj", "{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
@ -32,6 +32,17 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="allpages" HelpText="Indicate if this module should be displayed on all pages">Display On All Pages? </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="allpages" class="form-control" @bind="@_allPages">
|
||||
<option value="True">Yes</option>
|
||||
<option value="False">No</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="page" HelpText="The page that the module is on">Page: </Label>
|
||||
@ -77,6 +88,7 @@
|
||||
private Dictionary<string, string> _containers;
|
||||
private string _title;
|
||||
private string _containerType;
|
||||
private string _allPages = "false";
|
||||
private string _permissionNames = "";
|
||||
private string _permissions;
|
||||
private string _pageId;
|
||||
@ -95,6 +107,7 @@
|
||||
_title = ModuleState.Title;
|
||||
_containers = ThemeService.GetContainerTypes(await ThemeService.GetThemesAsync());
|
||||
_containerType = ModuleState.ContainerType;
|
||||
_allPages = ModuleState.AllPages.ToString();
|
||||
_permissions = ModuleState.Permissions;
|
||||
_permissionNames = ModuleState.ModuleDefinition.PermissionNames;
|
||||
_pageId = ModuleState.PageId.ToString();
|
||||
@ -120,18 +133,18 @@
|
||||
|
||||
private async Task SaveModule()
|
||||
{
|
||||
var module = ModuleState;
|
||||
module.Permissions = _permissionGrid.GetPermissions();
|
||||
await ModuleService.UpdateModuleAsync(module);
|
||||
|
||||
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||
pagemodule.PageId = int.Parse(_pageId);
|
||||
pagemodule.Title = _title;
|
||||
pagemodule.ContainerType = _containerType;
|
||||
|
||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||
|
||||
var module = ModuleState;
|
||||
module.AllPages = bool.Parse(_allPages);
|
||||
module.Permissions = _permissionGrid.GetPermissions();
|
||||
await ModuleService.UpdateModuleAsync(module);
|
||||
|
||||
if (_settingsModuleType != null)
|
||||
{
|
||||
var moduleType = Type.GetType(ModuleState.ModuleType);
|
||||
|
@ -11,29 +11,27 @@
|
||||
<TabPanel Name="Download">
|
||||
@if (_upgradeavailable)
|
||||
{
|
||||
<ModuleMessage Type="MessageType.Info" Message="Download a new version of the framework. Once you are ready click Install to complete the installation."></ModuleMessage>
|
||||
@("Framework") @_package.Version <button type="button" class="btn btn-success" @onclick=@(async () => await Download(Constants.PackageId, Constants.Version))>Download</button>
|
||||
<ModuleMessage Type="MessageType.Info" Message="Select The Upgrade Button To Install a New Framework Version"></ModuleMessage>
|
||||
@("Framework") @_package.Version <button type="button" class="btn btn-success" @onclick=@(async () => await Download(Constants.PackageId, Constants.Version))>Upgrade</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ModuleMessage Type="MessageType.Info" Message="Framework Is Already Up To Date"></ModuleMessage>
|
||||
}
|
||||
</TabPanel>
|
||||
@if (_upgradeavailable)
|
||||
{
|
||||
<TabPanel Name="Upload">
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label HelpText="Upload a new framework package. Once it is uploaded click Install to complete the installation.">Framework: </Label>
|
||||
<Label HelpText="Upload a framework package and select Install to complete the installation">Framework: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager Filter="nupkg" ShowFiles="false" Folder="Framework" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button type="button" class="btn btn-success" @onclick="Upgrade">Install</button>
|
||||
</TabPanel>
|
||||
}
|
||||
</TabStrip>
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
|
||||
@attribute [OqtaneIgnore]
|
||||
@if (_visible)
|
||||
{
|
||||
<div class="app-admin-modal">
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@attribute [OqtaneIgnore]
|
||||
@inject IUserService UserService
|
||||
|
||||
@if (_authorized)
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
@if (_text != string.Empty)
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@attribute [OqtaneIgnore]
|
||||
@inject IFolderService FolderService
|
||||
@inject IFileService FileService
|
||||
@inject IJSRuntime JsRuntime
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
@if (!string.IsNullOrEmpty(HelpText))
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
@if (!string.IsNullOrEmpty(_message))
|
||||
{
|
||||
|
@ -1,7 +1,9 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@attribute [OqtaneIgnore]
|
||||
@typeparam TableItem
|
||||
|
||||
|
||||
<p>
|
||||
@if(Format == "Table")
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@attribute [OqtaneIgnore]
|
||||
@inject IRoleService RoleService
|
||||
@inject IUserService UserService
|
||||
|
||||
|
@ -1,29 +1,83 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@attribute [OqtaneIgnore]
|
||||
@inject IJSRuntime JsRuntime
|
||||
|
||||
@if (_filemanagervisible)
|
||||
{
|
||||
<div class="row" style="margin-bottom: 50px;">
|
||||
<div class="col">
|
||||
<TabStrip>
|
||||
<TabPanel Name="Rich" Heading="Rich Text Editor">
|
||||
@if (_filemanagervisible)
|
||||
{
|
||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||
@((MarkupString)_message)
|
||||
<br />
|
||||
}
|
||||
<div class="row justify-content-center">
|
||||
<button type="button" class="btn btn-success" @onclick="InsertImage">Insert Image</button>
|
||||
}
|
||||
<div class="row justify-content-center" style="margin-bottom: 20px;">
|
||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">Synchronize Content</button>
|
||||
<button type="button" class="btn btn-primary" @onclick="InsertImage">Insert Image</button>
|
||||
@if (_filemanagervisible)
|
||||
{
|
||||
@((MarkupString)" ")
|
||||
<button type="button" class="btn btn-secondary" @onclick="CloseFileManager">Close</button>
|
||||
}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class ="col">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div @ref="@_toolBar">
|
||||
@if (ToolbarContent != null)
|
||||
{
|
||||
@ToolbarContent
|
||||
}
|
||||
else
|
||||
{
|
||||
<select class="ql-header">
|
||||
<option selected=""></option>
|
||||
<option value="1"></option>
|
||||
<option value="2"></option>
|
||||
<option value="3"></option>
|
||||
<option value="4"></option>
|
||||
<option value="5"></option>
|
||||
</select>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-bold"></button>
|
||||
<button class="ql-italic"></button>
|
||||
<button class="ql-underline"></button>
|
||||
<button class="ql-strike"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-color"></select>
|
||||
<select class="ql-background"></select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-list" value="ordered"></button>
|
||||
<button class="ql-list" value="bullet"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-link"></button>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<div @ref="@_editorElement">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Raw" Heading="Raw HTML Editor">
|
||||
<div class="row justify-content-center" style="margin-bottom: 20px;">
|
||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRawHtml">Synchronize Content</button>
|
||||
</div>
|
||||
@if (ReadOnly)
|
||||
{
|
||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_content" rows="10" readonly></textarea>
|
||||
}
|
||||
else
|
||||
{
|
||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_content" rows="10"></textarea>
|
||||
}
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
@ -31,10 +85,12 @@
|
||||
private ElementReference _toolBar;
|
||||
private bool _filemanagervisible = false;
|
||||
private FileManager _fileManager;
|
||||
private string _content = string.Empty;
|
||||
private string _original = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ToolbarContent { get; set; }
|
||||
public string Content { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; } = false;
|
||||
@ -42,12 +98,21 @@
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; } = "Enter Your Content...";
|
||||
|
||||
// parameters only applicable to rich text editor
|
||||
[Parameter]
|
||||
public RenderFragment ToolbarContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Theme { get; set; } = "snow";
|
||||
|
||||
[Parameter]
|
||||
public string DebugLevel { get; set; } = "info";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_content = Content; // raw HTML
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
@ -60,42 +125,57 @@
|
||||
Placeholder,
|
||||
Theme,
|
||||
DebugLevel);
|
||||
|
||||
await RichTextEditorInterop.LoadEditorContent(
|
||||
JsRuntime,
|
||||
_editorElement, Content);
|
||||
|
||||
// preserve a copy of the rich text content ( Quill sanitizes content so we need to retrieve it from the editor )
|
||||
_original = await RichTextEditorInterop.GetHtml(
|
||||
JsRuntime,
|
||||
_editorElement);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> GetText()
|
||||
public void CloseFileManager()
|
||||
{
|
||||
return await RichTextEditorInterop.GetText(
|
||||
_filemanagervisible = false;
|
||||
_message = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task RefreshRichText()
|
||||
{
|
||||
await RichTextEditorInterop.LoadEditorContent(
|
||||
JsRuntime,
|
||||
_editorElement, _content);
|
||||
}
|
||||
|
||||
public async Task RefreshRawHtml()
|
||||
{
|
||||
_content = await RichTextEditorInterop.GetHtml(
|
||||
JsRuntime,
|
||||
_editorElement);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task<string> GetHtml()
|
||||
{
|
||||
return await RichTextEditorInterop.GetHtml(
|
||||
// get rich text content
|
||||
string content = await RichTextEditorInterop.GetHtml(
|
||||
JsRuntime,
|
||||
_editorElement);
|
||||
}
|
||||
|
||||
public async Task<string> GetContent()
|
||||
if (_original != content)
|
||||
{
|
||||
return await RichTextEditorInterop.GetContent(
|
||||
JsRuntime,
|
||||
_editorElement);
|
||||
// rich text content has changed - return it
|
||||
return content;
|
||||
}
|
||||
|
||||
public async Task LoadContent(string content)
|
||||
else
|
||||
{
|
||||
await RichTextEditorInterop.LoadEditorContent(
|
||||
JsRuntime,
|
||||
_editorElement, content);
|
||||
// return raw html content
|
||||
return _content;
|
||||
}
|
||||
|
||||
public async Task EnableEditor(bool mode)
|
||||
{
|
||||
await RichTextEditorInterop.EnableEditor(
|
||||
JsRuntime,
|
||||
_editorElement, mode);
|
||||
}
|
||||
|
||||
public async Task InsertImage()
|
||||
@ -121,16 +201,28 @@
|
||||
_filemanagervisible = true;
|
||||
_message = string.Empty;
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void CloseFileManager()
|
||||
// other rich text editor methods which can be used by developers
|
||||
public async Task<string> GetText()
|
||||
{
|
||||
_filemanagervisible = false;
|
||||
_message = string.Empty;
|
||||
|
||||
StateHasChanged();
|
||||
return await RichTextEditorInterop.GetText(
|
||||
JsRuntime,
|
||||
_editorElement);
|
||||
}
|
||||
|
||||
public async Task<string> GetContent()
|
||||
{
|
||||
return await RichTextEditorInterop.GetContent(
|
||||
JsRuntime,
|
||||
_editorElement);
|
||||
}
|
||||
|
||||
public async Task EnableEditor(bool mode)
|
||||
{
|
||||
await RichTextEditorInterop.EnableEditor(
|
||||
JsRuntime,
|
||||
_editorElement, mode);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
using Microsoft.JSInterop;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.UI
|
||||
namespace Oqtane.Modules.Controls
|
||||
{
|
||||
public static class RichTextEditorInterop
|
||||
{
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
<div class="d-flex">
|
||||
<div>
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
@if (Name == Parent.ActiveTab)
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
<CascadingValue Value="this">
|
||||
<div class="container-fluid">
|
||||
|
@ -7,111 +7,48 @@
|
||||
@inject HttpClient http
|
||||
@inject SiteState sitestate
|
||||
|
||||
<div class="row" style="margin-bottom: 50px;">
|
||||
<div class="col @_visibleText">
|
||||
<textarea class="form-control" @bind="@content" rows="10"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="col @_visibleRich">
|
||||
<RichTextEditor @ref="@RichTextEditorHtml">
|
||||
<ToolbarContent>
|
||||
<select class="ql-header">
|
||||
<option selected=""></option>
|
||||
<option value="1"></option>
|
||||
<option value="2"></option>
|
||||
<option value="3"></option>
|
||||
<option value="4"></option>
|
||||
<option value="5"></option>
|
||||
</select>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-bold"></button>
|
||||
<button class="ql-italic"></button>
|
||||
<button class="ql-underline"></button>
|
||||
<button class="ql-strike"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-color"></select>
|
||||
<select class="ql-background"></select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-list" value="ordered"></button>
|
||||
<button class="ql-list" value="bullet"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-link"></button>
|
||||
</span>
|
||||
</ToolbarContent>
|
||||
</RichTextEditor>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
@if (!RichTextEditorMode)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="RichTextEditor">Rich Text Editor</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="RawHtmlEditor">Raw HTML Editor</button>
|
||||
}
|
||||
@if (_content != null)
|
||||
{
|
||||
<RichTextEditor Content="@_content" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||
<button type="button" class="btn btn-success" @onclick="SaveContent">Save</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
|
||||
</div>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(_content))
|
||||
{
|
||||
<br /><br />
|
||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
private string _visibleText = "d-none";
|
||||
private string _visibleRich;
|
||||
private bool _richTextEditorMode;
|
||||
private RichTextEditor RichTextEditorHtml;
|
||||
private string content;
|
||||
private string createdby;
|
||||
private DateTime createdon;
|
||||
private string modifiedby;
|
||||
private DateTime modifiedon;
|
||||
private string _content = null;
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
|
||||
public override string Title => "Edit Html/Text";
|
||||
|
||||
public bool RichTextEditorMode
|
||||
{
|
||||
get => _richTextEditorMode;
|
||||
set
|
||||
{
|
||||
_richTextEditorMode = value;
|
||||
|
||||
if (_richTextEditorMode)
|
||||
{
|
||||
_visibleText = "d-none";
|
||||
_visibleRich = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
_visibleText = string.Empty;
|
||||
_visibleRich = "d-none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (firstRender)
|
||||
var htmltextservice = new HtmlTextService(http, sitestate);
|
||||
var htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||
if (htmltext != null)
|
||||
{
|
||||
if (content == null)
|
||||
{
|
||||
RichTextEditorMode = true;
|
||||
await LoadText();
|
||||
_content = htmltext.Content;
|
||||
_content = _content.Replace(Constants.ContentUrl, "/" + PageState.Alias.AliasId.ToString() + Constants.ContentUrl);
|
||||
_createdby = htmltext.CreatedBy;
|
||||
_createdon = htmltext.CreatedOn;
|
||||
_modifiedby = htmltext.ModifiedBy;
|
||||
_modifiedon = htmltext.ModifiedOn;
|
||||
}
|
||||
else
|
||||
{
|
||||
_content = string.Empty;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -121,48 +58,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadText()
|
||||
{
|
||||
var htmltextservice = new HtmlTextService(http, sitestate);
|
||||
var htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||
if (htmltext != null)
|
||||
{
|
||||
content = htmltext.Content;
|
||||
createdby = htmltext.CreatedBy;
|
||||
createdon = htmltext.CreatedOn;
|
||||
modifiedby = htmltext.ModifiedBy;
|
||||
modifiedon = htmltext.ModifiedOn;
|
||||
|
||||
if (RichTextEditorMode)
|
||||
{
|
||||
await RichTextEditorHtml.LoadContent(content);
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RichTextEditor()
|
||||
{
|
||||
RichTextEditorMode = true;
|
||||
await RichTextEditorHtml.LoadContent(content);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task RawHtmlEditor()
|
||||
{
|
||||
content = await this.RichTextEditorHtml.GetHtml();
|
||||
RichTextEditorMode = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task SaveContent()
|
||||
{
|
||||
if (RichTextEditorMode)
|
||||
{
|
||||
content = await RichTextEditorHtml.GetHtml();
|
||||
}
|
||||
|
||||
content = content.Replace(((PageState.Alias.Path == string.Empty) ? "/~" : PageState.Alias.Path) + Constants.ContentUrl, Constants.ContentUrl);
|
||||
string content = await RichTextEditorHtml.GetHtml();
|
||||
content = content.Replace("/" + PageState.Alias.AliasId.ToString() + Constants.ContentUrl, Constants.ContentUrl);
|
||||
|
||||
try
|
||||
{
|
||||
@ -190,5 +89,4 @@
|
||||
AddModuleMessage("Error Saving Content", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
if (htmltext != null)
|
||||
{
|
||||
content = htmltext.Content;
|
||||
content = content.Replace(Constants.ContentUrl, ((PageState.Alias.Path == "") ? "/~" : PageState.Alias.Path) + Constants.ContentUrl);
|
||||
content = content.Replace(Constants.ContentUrl, "/" + PageState.Alias.AliasId.ToString() + Constants.ContentUrl);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -6,7 +6,7 @@
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>0.9.0</Version>
|
||||
<Version>0.9.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -82,7 +82,6 @@ namespace Oqtane.Services
|
||||
var result = await response.Content.ReadFromJsonAsync<TResult>();
|
||||
return result;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
@ -121,6 +120,8 @@ namespace Oqtane.Services
|
||||
if (response.StatusCode != HttpStatusCode.NoContent && response.StatusCode != HttpStatusCode.NotFound)
|
||||
{
|
||||
//TODO: Log errors here
|
||||
|
||||
Console.WriteLine($"Request: {response.RequestMessage.RequestUri}");
|
||||
Console.WriteLine($"Response status: {response.StatusCode} {response.ReasonPhrase}");
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,9 @@
|
||||
@inherits ContainerBase
|
||||
<div class="container">
|
||||
<div class="row px-4">
|
||||
<div class="d-flex flex-nowrap">
|
||||
<ModuleActions /><h2><ModuleTitle /></h2>
|
||||
</div>
|
||||
<hr class="app-rule" />
|
||||
</div>
|
||||
<div class="row px-4">
|
||||
|
@ -7,15 +7,7 @@
|
||||
|
||||
<div class="sidebar">
|
||||
<nav class="navbar">
|
||||
<Logo />
|
||||
<button class="navbar-toggler" aria-expanded="false" aria-controls="navbarSupportedContent"
|
||||
aria-label="Toggle navigation" type="button" data-toggle="collapse"
|
||||
data-target="#navbarSupportedContent">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<Menu Orientation="Vertical" />
|
||||
</div>
|
||||
<Logo /><Menu Orientation="Vertical" />
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
@ -1,27 +1,34 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ThemeControlBase
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
@if (BreadCrumbPages.Any())
|
||||
{
|
||||
<span class="app-breadcrumbs">
|
||||
<ol class="breadcrumb">
|
||||
@foreach (var p in BreadCrumbPages)
|
||||
{
|
||||
<li class="breadcrumb-item @ActiveClass(p)">
|
||||
if (p.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<li class="breadcrumb-item active">
|
||||
<a href="@NavigateUrl(p.Path)">@p.Name</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="breadcrumb-item">
|
||||
<a href="@NavigateUrl(p.Path)">@p.Name</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
</ol>
|
||||
</span>
|
||||
}
|
||||
|
||||
@code {
|
||||
|
||||
protected IEnumerable<Page> BreadCrumbPages => GetBreadCrumbPages().Reverse().ToList();
|
||||
|
||||
protected string ActiveClass(Page page)
|
||||
{
|
||||
return (page.PageId == PageState.Page.PageId) ? " active" : string.Empty;
|
||||
}
|
||||
|
||||
private IEnumerable<Page> GetBreadCrumbPages()
|
||||
{
|
||||
var page = PageState.Page;
|
||||
|
@ -1,6 +1,6 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@using Oqtane.Enums
|
||||
@inherits ThemeControlBase
|
||||
@attribute [OqtaneIgnore]
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IUserService UserService
|
||||
@inject IModuleDefinitionService ModuleDefinitionService
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
<div class="@CardClass">
|
||||
<div class="@HeaderClass">
|
||||
Control Panel
|
||||
<span class="font-weight-bold">Control Panel</span>
|
||||
<button type="button" class="close" @onclick="HideControlPanel" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
@ -245,17 +245,17 @@
|
||||
{
|
||||
if (string.IsNullOrEmpty(ButtonClass))
|
||||
{
|
||||
ButtonClass = "btn-outline-primary";
|
||||
ButtonClass = "btn-outline-secondary";
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(CardClass))
|
||||
{
|
||||
CardClass = "card bg-secondary mb-3";
|
||||
CardClass = "card border-secondary mb-3";
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(HeaderClass))
|
||||
{
|
||||
HeaderClass = "card-header text-white";
|
||||
HeaderClass = "card-header";
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(BodyClass))
|
||||
@ -361,6 +361,7 @@
|
||||
module.SiteId = PageState.Site.SiteId;
|
||||
module.PageId = PageState.Page.PageId;
|
||||
module.ModuleDefinitionName = _moduleDefinitionName;
|
||||
module.AllPages = false;
|
||||
module.Permissions = PageState.Page.Permissions;
|
||||
module = await ModuleService.AddModuleAsync(module);
|
||||
_moduleId = module.ModuleId.ToString();
|
||||
|
@ -1,7 +1,9 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits LoginBase
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
<AuthorizeView>
|
||||
<span class="app-login">
|
||||
<AuthorizeView>
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
@ -11,4 +13,5 @@
|
||||
<NotAuthorized>
|
||||
<button type="button" class="btn btn-primary" @onclick="LoginUser">Login</button>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
</AuthorizeView>
|
||||
</span>
|
@ -1,21 +1,13 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ThemeControlBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
@if (PageState.Site.LogoFileId != null)
|
||||
{
|
||||
<a href="@Href">
|
||||
<img class="img-fluid" src="@ContentUrl(PageState.Site.LogoFileId.Value)" alt="@PageState.Site.Name"/>
|
||||
<span class="app-logo">
|
||||
<a href="@PageState.Alias.Path">
|
||||
<img class="img-fluid" src="@ContentUrl(PageState.Site.LogoFileId.Value)" alt="@PageState.Site.Name" />
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
|
||||
@code {
|
||||
string Href
|
||||
{
|
||||
get
|
||||
{
|
||||
var uri = new Uri(NavigationManager.Uri);
|
||||
return $"{uri.Scheme}://{uri.Authority}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits MenuBase
|
||||
@attribute [OqtaneIgnore]
|
||||
@if (MenuPages.Any())
|
||||
{
|
||||
<div class="app-menu">
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#Menu" aria-controls="Menu" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="app-menu-toggler">
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#Menu" aria-controls="Menu" aria-expanded="false" aria-label="Toggle Navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
</span>
|
||||
<div class="app-menu">
|
||||
<div class="collapse navbar-collapse" id="Menu">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
@foreach (var p in MenuPages)
|
||||
|
@ -1,8 +1,15 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits MenuBase
|
||||
@attribute [OqtaneIgnore]
|
||||
@if (MenuPages.Any())
|
||||
{
|
||||
<span class="app-menu-toggler">
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#Menu" aria-controls="Menu" aria-expanded="false" aria-label="Toggle Navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
</span>
|
||||
<div class="app-menu">
|
||||
<div class="collapse navbar-collapse" id="Menu">
|
||||
<ul class="nav flex-column">
|
||||
@foreach (var p in MenuPages)
|
||||
{
|
||||
@ -18,10 +25,10 @@
|
||||
<span class="oi oi-@p.Icon" aria-hidden="true"></span>
|
||||
}
|
||||
@p.Name
|
||||
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ModuleActionsBase
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
@if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions))
|
||||
{
|
||||
<div class="app-moduleactions">
|
||||
<a class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>
|
||||
<div class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 37px, 0px);">
|
||||
@foreach (var action in Actions)
|
||||
@ -17,4 +19,5 @@
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ContainerBase
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
@((MarkupString)title)
|
||||
<span class="app-moduletitle">
|
||||
@((MarkupString)title)
|
||||
</span>
|
||||
|
||||
@code {
|
||||
private string title = "";
|
||||
|
@ -1,8 +1,10 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ThemeControlBase
|
||||
@attribute [OqtaneIgnore]
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<AuthorizeView>
|
||||
<span class="app-profile">
|
||||
<AuthorizeView>
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
@ -15,8 +17,8 @@
|
||||
<button type="button" class="btn btn-primary" @onclick="RegisterUser">Register</button>
|
||||
}
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
</AuthorizeView>
|
||||
</span>
|
||||
|
||||
@code {
|
||||
|
||||
|
@ -2,7 +2,9 @@
|
||||
@inherits ContainerBase
|
||||
<div class="container">
|
||||
<div class="row px-4">
|
||||
<div class="d-flex flex-nowrap">
|
||||
<ModuleActions /><h2><ModuleTitle /></h2>
|
||||
</div>
|
||||
<hr class="app-rule" />
|
||||
</div>
|
||||
<div class="row px-4">
|
||||
|
@ -3,9 +3,12 @@
|
||||
|
||||
<main role="main">
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-primary fixed-top">
|
||||
<Logo /><Menu Orientation="Horizontal" /><div class="ml-md-auto"><UserProfile /> <Login /> <ControlPanel ButtonClass="btn-outline-secondary" CardClass="bg-light" /></div>
|
||||
<Logo /><Menu Orientation="Horizontal" />
|
||||
<div class="controls ml-md-auto">
|
||||
<div class="controls-group"><UserProfile /> <Login /> <ControlPanel /></div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container">
|
||||
<div class="content container">
|
||||
<PaneLayout />
|
||||
<div class="row px-4">
|
||||
<Pane Name="Admin" />
|
||||
@ -18,6 +21,9 @@
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.UI;
|
||||
@ -30,6 +30,13 @@ namespace Oqtane.Themes
|
||||
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);
|
||||
}
|
||||
|
||||
public string NavigateUrl()
|
||||
{
|
||||
return NavigateUrl(PageState.Page.Path);
|
||||
|
@ -1,4 +1,6 @@
|
||||
@namespace Oqtane.UI
|
||||
@using System.Diagnostics.CodeAnalysis
|
||||
@using System.Runtime.InteropServices
|
||||
@namespace Oqtane.UI
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject SiteState SiteState
|
||||
@inject NavigationManager NavigationManager
|
||||
@ -11,9 +13,6 @@
|
||||
@inject IModuleService ModuleService
|
||||
@inject IModuleDefinitionService ModuleDefinitionService
|
||||
@inject ILogService LogService
|
||||
@using System.Diagnostics.CodeAnalysis
|
||||
@using Oqtane.Enums
|
||||
@using System.Runtime.InteropServices
|
||||
@implements IHandleAfterRender
|
||||
|
||||
@DynamicComponent
|
||||
@ -237,7 +236,7 @@
|
||||
}
|
||||
|
||||
// check if user is authorized to view page
|
||||
if (UserSecurity.IsAuthorized(user,PermissionNames.View, page.Permissions))
|
||||
if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.Permissions))
|
||||
{
|
||||
page = await ProcessPage(page, site, user);
|
||||
|
||||
|
@ -18,3 +18,4 @@
|
||||
@using Oqtane.Themes
|
||||
@using Oqtane.Themes.Controls
|
||||
@using Oqtane.UI
|
||||
@using Oqtane.Enums
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>0.9.0</version>
|
||||
<version>0.9.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -16,14 +16,16 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
private readonly IModuleRepository _modules;
|
||||
private readonly IPageModuleRepository _pageModules;
|
||||
private readonly IPageRepository _pages;
|
||||
private readonly IModuleDefinitionRepository _moduleDefinitions;
|
||||
private readonly IUserPermissions _userPermissions;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public ModuleController(IModuleRepository modules, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, IUserPermissions userPermissions, ILogManager logger)
|
||||
public ModuleController(IModuleRepository modules, IPageModuleRepository pageModules, IPageRepository pages, IModuleDefinitionRepository moduleDefinitions, IUserPermissions userPermissions, ILogManager logger)
|
||||
{
|
||||
_modules = modules;
|
||||
_pageModules = pageModules;
|
||||
_pages = pages;
|
||||
_moduleDefinitions = moduleDefinitions;
|
||||
_userPermissions = userPermissions;
|
||||
_logger = logger;
|
||||
@ -42,6 +44,7 @@ namespace Oqtane.Controllers
|
||||
Module module = new Module();
|
||||
module.SiteId = pagemodule.Module.SiteId;
|
||||
module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName;
|
||||
module.AllPages = pagemodule.Module.AllPages;
|
||||
module.Permissions = pagemodule.Module.Permissions;
|
||||
module.CreatedBy = pagemodule.Module.CreatedBy;
|
||||
module.CreatedOn = pagemodule.Module.CreatedOn;
|
||||
@ -111,7 +114,20 @@ namespace Oqtane.Controllers
|
||||
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
||||
{
|
||||
module = _modules.UpdateModule(module);
|
||||
if (module.AllPages)
|
||||
{
|
||||
var pageModule = _pageModules.GetPageModules(module.SiteId).FirstOrDefault(item => item.ModuleId == module.ModuleId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module);
|
||||
|
||||
var pages = _pages.GetPages(module.SiteId).ToList();
|
||||
foreach (Page page in pages)
|
||||
{
|
||||
if (page.PageId != pageModule.PageId && !page.EditMode)
|
||||
{
|
||||
_pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -190,9 +190,9 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module","\\");
|
||||
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Client";
|
||||
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Server";
|
||||
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.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Server.Oqtane";
|
||||
}
|
||||
|
||||
ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, moduleDefinition);
|
||||
|
@ -124,6 +124,16 @@ namespace Oqtane.Controllers
|
||||
page = _pages.AddPage(page);
|
||||
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", page);
|
||||
|
||||
if (!page.EditMode)
|
||||
{
|
||||
var modules = _modules.GetModules(page.SiteId).Where(item => item.AllPages).ToList();
|
||||
foreach (Module module in modules)
|
||||
{
|
||||
var pageModule = _pageModules.GetPageModules(page.SiteId).FirstOrDefault(item => item.ModuleId == module.ModuleId);
|
||||
_pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType });
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -174,6 +184,7 @@ namespace Oqtane.Controllers
|
||||
module.SiteId = page.SiteId;
|
||||
module.PageId = page.PageId;
|
||||
module.ModuleDefinitionName = pm.Module.ModuleDefinitionName;
|
||||
module.AllPages = false;
|
||||
module.Permissions = new List<Permission> {
|
||||
new Permission(PermissionNames.View, userid, true),
|
||||
new Permission(PermissionNames.Edit, userid, true)
|
||||
|
@ -1,5 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Oqtane.Shared;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System.Reflection
|
||||
@ -31,5 +33,20 @@ namespace System.Reflection
|
||||
return assembly.GetTypes()
|
||||
.Where(t => t.GetInterfaces().Contains(interfaceType));
|
||||
}
|
||||
|
||||
public static bool IsOqtaneAssembly(this Assembly assembly)
|
||||
{
|
||||
return assembly.FullName != null && (assembly.FullName.Contains("oqtane", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public static bool IsOqtaneAssembly(this FileInfo fileInfo)
|
||||
{
|
||||
return (fileInfo.Name.Contains("oqtane", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public static IEnumerable<Assembly> GetOqtaneAssemblies(this AppDomain appDomain)
|
||||
{
|
||||
return appDomain.GetAssemblies().Where(a => a.IsOqtaneAssembly());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
|
||||
@ -16,10 +17,11 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
}
|
||||
|
||||
// load MVC application parts from module assemblies
|
||||
foreach (var assembly in OqtaneServiceCollectionExtensions.GetOqtaneModuleAssemblies())
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
// check if assembly contains MVC Controllers
|
||||
if (assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Controller))).ToArray().Length > 0)
|
||||
if (assembly.GetTypes().Any(t => t.IsSubclassOf(typeof(Controller))))
|
||||
{
|
||||
var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
|
||||
foreach (var part in partFactory.GetApplicationParts(assembly))
|
||||
|
@ -5,67 +5,35 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Shared;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
public static class OqtaneServiceCollectionExtensions
|
||||
{
|
||||
private static readonly IList<Assembly> OqtaneModuleAssemblies = new List<Assembly>();
|
||||
|
||||
private static Assembly[] Assemblies => AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
internal static IEnumerable<Assembly> GetOqtaneModuleAssemblies() => OqtaneModuleAssemblies;
|
||||
|
||||
public static IServiceCollection AddOqtaneModules(this IServiceCollection services)
|
||||
public static IServiceCollection AddOqtaneParts(this IServiceCollection services)
|
||||
{
|
||||
if (services is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
LoadAssemblies("Module");
|
||||
|
||||
LoadAssemblies();
|
||||
services.AddOqtaneServices();
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddOqtaneThemes(this IServiceCollection services)
|
||||
private static IServiceCollection AddOqtaneServices(this IServiceCollection services)
|
||||
{
|
||||
if (services is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
LoadAssemblies("Theme");
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddOqtaneSiteTemplates(this IServiceCollection services)
|
||||
{
|
||||
if (services is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
LoadAssemblies("SiteTemplate");
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddOqtaneServices(this IServiceCollection services)
|
||||
{
|
||||
if (services is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
// dynamically register module services, contexts, and repository classes
|
||||
var assemblies = Assemblies.Where(item => item.FullName != null && (item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module."))).ToArray();
|
||||
var hostedServiceType = typeof(IHostedService);
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
// dynamically register module services, contexts, and repository classes
|
||||
var implementationTypes = assembly.GetInterfaces<IService>();
|
||||
foreach (var implementationType in implementationTypes)
|
||||
{
|
||||
@ -75,22 +43,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
services.AddScoped(serviceType ?? implementationType, implementationType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddOqtaneHostedServices(this IServiceCollection services)
|
||||
{
|
||||
if (services is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
// dynamically register hosted services
|
||||
var hostedServiceType = typeof(IHostedService);
|
||||
foreach (var assembly in Assemblies)
|
||||
{
|
||||
var serviceTypes = assembly.GetTypes(hostedServiceType);
|
||||
foreach (var serviceType in serviceTypes)
|
||||
{
|
||||
@ -104,22 +58,37 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
return services;
|
||||
}
|
||||
|
||||
private static void LoadAssemblies(string pattern)
|
||||
private static void LoadAssemblies()
|
||||
{
|
||||
var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
||||
if (assemblyPath == null) return;
|
||||
|
||||
var assembliesFolder = new DirectoryInfo(assemblyPath);
|
||||
|
||||
|
||||
// iterate through Oqtane assemblies in /bin ( filter is narrow to optimize loading process )
|
||||
foreach (var dll in assembliesFolder.EnumerateFiles($"*.{pattern}.*.dll"))
|
||||
foreach (var dll in assembliesFolder.EnumerateFiles($"*.dll", SearchOption.TopDirectoryOnly).Where(f => f.IsOqtaneAssembly()))
|
||||
{
|
||||
// check if assembly is already loaded
|
||||
var assembly = Assemblies.FirstOrDefault(a =>!a.IsDynamic && a.Location == dll.FullName);
|
||||
if (assembly == null)
|
||||
AssemblyName assemblyName;
|
||||
try
|
||||
{
|
||||
assemblyName = AssemblyName.GetAssemblyName(dll.FullName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"Not Assembly : {dll.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
if (!assemblies.Any(a => AssemblyName.ReferenceMatchesDefinition(assemblyName, a.GetName())))
|
||||
{
|
||||
try
|
||||
{
|
||||
var pdb = Path.ChangeExtension(dll.FullName, ".pdb");
|
||||
Assembly assembly = null;
|
||||
|
||||
// load assembly ( and symbols ) from stream to prevent locking files ( as long as dependencies are in /bin they will load as well )
|
||||
string pdb = dll.FullName.Replace(".dll", ".pdb");
|
||||
if (File.Exists(pdb))
|
||||
{
|
||||
assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(dll.FullName)), new MemoryStream(File.ReadAllBytes(pdb)));
|
||||
@ -128,10 +97,11 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(dll.FullName)));
|
||||
}
|
||||
if (pattern == "Module")
|
||||
Console.WriteLine($"Loaded : {assemblyName}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// build a list of module assemblies
|
||||
OqtaneModuleAssemblies.Add(assembly);
|
||||
Console.WriteLine($"Failed : {assemblyName}\n{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
Oqtane.Server/Extensions/StringExtensions.cs
Normal file
17
Oqtane.Server/Extensions/StringExtensions.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Oqtane.Extensions
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static bool StartWithAnyOf(this string s, IEnumerable<string> list)
|
||||
{
|
||||
if (s == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return list.Any(f => s.StartsWith(f));
|
||||
}
|
||||
}
|
||||
}
|
@ -180,17 +180,17 @@ namespace Oqtane.Infrastructure
|
||||
|
||||
private void FinishUpgrade()
|
||||
{
|
||||
string folder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
||||
|
||||
// check if upgrade application exists
|
||||
string folder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
||||
if (folder == null || !File.Exists(Path.Combine(folder, "Oqtane.Upgrade.exe"))) return;
|
||||
|
||||
// run upgrade application
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
FileName = Path.Combine(folder, "Oqtane.Upgrade.exe"),
|
||||
Arguments = "",
|
||||
Arguments = "\"" + _environment.ContentRootPath + "\" \"" + _environment.WebRootPath + "\"",
|
||||
ErrorDialog = false,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
|
@ -4,7 +4,7 @@
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>0.9.0</Version>
|
||||
<Version>0.9.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -17,10 +17,15 @@
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Scripts\Tenant.0.9.1.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<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>
|
||||
|
||||
|
@ -75,6 +75,7 @@ namespace Oqtane.Repository
|
||||
// get module assemblies
|
||||
_moduleDefinitions = LoadModuleDefinitionsFromAssemblies();
|
||||
}
|
||||
|
||||
List<ModuleDefinition> moduleDefinitions = _moduleDefinitions;
|
||||
|
||||
List<Permission> permissions = new List<Permission>();
|
||||
@ -94,7 +95,7 @@ namespace Oqtane.Repository
|
||||
if (moduledef == null)
|
||||
{
|
||||
// new module definition
|
||||
moduledef = new ModuleDefinition { ModuleDefinitionName = moduledefinition.ModuleDefinitionName };
|
||||
moduledef = new ModuleDefinition {ModuleDefinitionName = moduledefinition.ModuleDefinitionName};
|
||||
_db.ModuleDefinition.Add(moduledef);
|
||||
_db.SaveChanges();
|
||||
if (siteId != -1)
|
||||
@ -109,18 +110,22 @@ namespace Oqtane.Repository
|
||||
{
|
||||
moduledefinition.Name = moduledef.Name;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(moduledef.Description))
|
||||
{
|
||||
moduledefinition.Description = moduledef.Description;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(moduledef.Categories))
|
||||
{
|
||||
moduledefinition.Categories = moduledef.Categories;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(moduledef.Version))
|
||||
{
|
||||
moduledefinition.Version = moduledef.Version;
|
||||
}
|
||||
|
||||
if (siteId != -1)
|
||||
{
|
||||
if (permissions.Count == 0)
|
||||
@ -139,9 +144,11 @@ namespace Oqtane.Repository
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove module definition from list as it is already synced
|
||||
moduledefs.Remove(moduledef);
|
||||
}
|
||||
|
||||
moduledefinition.ModuleDefinitionId = moduledef.ModuleDefinitionId;
|
||||
moduledefinition.SiteId = siteId;
|
||||
moduledefinition.CreatedBy = moduledef.CreatedBy;
|
||||
@ -157,6 +164,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
_permissions.DeletePermissions(siteId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId);
|
||||
}
|
||||
|
||||
_db.ModuleDefinition.Remove(moduledefinition); // delete
|
||||
_db.SaveChanges();
|
||||
}
|
||||
@ -168,12 +176,12 @@ namespace Oqtane.Repository
|
||||
{
|
||||
List<ModuleDefinition> moduleDefinitions = new List<ModuleDefinition>();
|
||||
// iterate through Oqtane module assemblies
|
||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray();
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
moduleDefinitions = LoadModuleDefinitionsFromAssembly(moduleDefinitions, assembly);
|
||||
}
|
||||
|
||||
return moduleDefinitions;
|
||||
}
|
||||
|
||||
@ -183,13 +191,15 @@ namespace Oqtane.Repository
|
||||
Type[] modulecontroltypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModuleControl))).ToArray();
|
||||
foreach (Type modulecontroltype in modulecontroltypes)
|
||||
{
|
||||
if (modulecontroltype.Name != "ModuleBase" && !modulecontroltype.Namespace.EndsWith(".Controls"))
|
||||
{
|
||||
string[] typename = modulecontroltype.AssemblyQualifiedName?.Split(',').Select(item => item.Trim()).ToArray();
|
||||
string[] segments = typename[0].Split('.');
|
||||
Array.Resize(ref segments, segments.Length - 1);
|
||||
string moduleType = string.Join(".", segments);
|
||||
string qualifiedModuleType = moduleType + ", " + typename[1];
|
||||
// Check if type should be ignored
|
||||
if (modulecontroltype.Name == "ModuleBase"
|
||||
|| modulecontroltype.IsGenericType
|
||||
|| modulecontroltype.IsAbstract
|
||||
|| Attribute.IsDefined(modulecontroltype, typeof(OqtaneIgnoreAttribute))
|
||||
) continue;
|
||||
|
||||
string moduleNamespace = modulecontroltype.Namespace;
|
||||
string qualifiedModuleType = moduleNamespace + ", " + modulecontroltype.Assembly.GetName().Name;
|
||||
|
||||
int index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType);
|
||||
if (index == -1)
|
||||
@ -198,34 +208,36 @@ namespace Oqtane.Repository
|
||||
Type moduletype = assembly
|
||||
.GetTypes()
|
||||
.Where(item => item.Namespace != null)
|
||||
.Where(item => item.Namespace.StartsWith(moduleType))
|
||||
.Where(item => item.Namespace.StartsWith(moduleNamespace))
|
||||
.FirstOrDefault(item => item.GetInterfaces().Contains(typeof(IModule)));
|
||||
if (moduletype != null)
|
||||
{
|
||||
// get property values from IModule
|
||||
var moduleobject = Activator.CreateInstance(moduletype);
|
||||
moduledefinition = (ModuleDefinition)moduletype.GetProperty("ModuleDefinition").GetValue(moduleobject);
|
||||
moduledefinition = (ModuleDefinition) moduletype.GetProperty("ModuleDefinition").GetValue(moduleobject);
|
||||
}
|
||||
else
|
||||
{
|
||||
// set default property values
|
||||
moduledefinition = new ModuleDefinition
|
||||
{
|
||||
Name = moduleType.Substring(moduleType.LastIndexOf(".") + 1),
|
||||
Description = "Manage " + moduleType.Substring(moduleType.LastIndexOf(".") + 1),
|
||||
Name = moduleNamespace.Substring(moduleNamespace.LastIndexOf(".") + 1),
|
||||
Description = "Manage " + moduleNamespace.Substring(moduleNamespace.LastIndexOf(".") + 1),
|
||||
Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : "")
|
||||
};
|
||||
}
|
||||
|
||||
// set internal properties
|
||||
moduledefinition.ModuleDefinitionName = qualifiedModuleType;
|
||||
moduledefinition.Version = ""; // will be populated from database
|
||||
moduledefinition.ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1];
|
||||
moduledefinition.ControlTypeTemplate = moduleNamespace + "." + Constants.ActionToken + ", " + modulecontroltype.Assembly.GetName().Name;
|
||||
moduledefinition.AssemblyName = assembly.GetName().Name;
|
||||
|
||||
if (string.IsNullOrEmpty(moduledefinition.Categories))
|
||||
{
|
||||
moduledefinition.Categories = "Common";
|
||||
}
|
||||
|
||||
if (moduledefinition.Categories == "Admin")
|
||||
{
|
||||
moduledefinition.Permissions = new List<Permission>
|
||||
@ -241,26 +253,28 @@ namespace Oqtane.Repository
|
||||
new Permission(PermissionNames.Utilize, Constants.RegisteredRole, true)
|
||||
}.EncodePermissions();
|
||||
}
|
||||
|
||||
Console.WriteLine($"Registering module: {moduledefinition.ModuleDefinitionName}");
|
||||
moduledefinitions.Add(moduledefinition);
|
||||
index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType);
|
||||
}
|
||||
|
||||
moduledefinition = moduledefinitions[index];
|
||||
// actions
|
||||
var modulecontrolobject = Activator.CreateInstance(modulecontroltype);
|
||||
string actions = (string)modulecontroltype.GetProperty("Actions")?.GetValue(modulecontrolobject);
|
||||
string actions = (string) modulecontroltype.GetProperty("Actions")?.GetValue(modulecontrolobject);
|
||||
if (!string.IsNullOrEmpty(actions))
|
||||
{
|
||||
foreach (string action in actions.Split(','))
|
||||
{
|
||||
moduledefinition.ControlTypeRoutes += (action + "=" + modulecontroltype.FullName + ", " + typename[1] + ";");
|
||||
moduledefinition.ControlTypeRoutes += (action + "=" + modulecontroltype.FullName + ", " + modulecontroltype.Assembly.GetName().Name + ";");
|
||||
}
|
||||
}
|
||||
|
||||
moduledefinitions[index] = moduledefinition;
|
||||
}
|
||||
}
|
||||
|
||||
return moduledefinitions;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -775,6 +775,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
SiteId = site.SiteId,
|
||||
ModuleDefinitionName = pagetemplatemodule.ModuleDefinitionName,
|
||||
AllPages = false,
|
||||
Permissions = pagetemplatemodule.ModulePermissions,
|
||||
};
|
||||
module = _moduleRepository.AddModule(module);
|
||||
|
@ -22,8 +22,8 @@ namespace Oqtane.Repository
|
||||
List<SiteTemplate> siteTemplates = new List<SiteTemplate>();
|
||||
|
||||
// iterate through Oqtane site template assemblies
|
||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".SiteTemplate.")).ToArray();
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
siteTemplates = LoadSiteTemplatesFromAssembly(siteTemplates, assembly);
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Themes;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
@ -31,8 +32,7 @@ namespace Oqtane.Repository
|
||||
List<Theme> themes = new List<Theme>();
|
||||
|
||||
// iterate through Oqtane theme assemblies
|
||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Theme.")).ToArray();
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
themes = LoadThemesFromAssembly(themes, assembly);
|
||||
@ -47,20 +47,22 @@ namespace Oqtane.Repository
|
||||
Type[] themeControlTypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IThemeControl))).ToArray();
|
||||
foreach (Type themeControlType in themeControlTypes)
|
||||
{
|
||||
if (themeControlType.Name != "ThemeBase")
|
||||
{
|
||||
string[] typename = themeControlType.AssemblyQualifiedName.Split(',').Select(item => item.Trim()).ToList().ToArray();
|
||||
string[] segments = typename[0].Split('.');
|
||||
Array.Resize(ref segments, segments.Length - 1);
|
||||
string @namespace = string.Join(".", segments);
|
||||
// Check if type should be ignored
|
||||
if (themeControlType.Name == "ThemeBase"
|
||||
|| themeControlType.IsGenericType
|
||||
|| Attribute.IsDefined(themeControlType, typeof(OqtaneIgnoreAttribute))
|
||||
) continue;
|
||||
|
||||
int index = themes.FindIndex(item => item.ThemeName == @namespace);
|
||||
string themeNamespace = themeControlType.Namespace;
|
||||
string qualifiedModuleType = themeNamespace + ", " + themeControlType.Assembly.GetName().Name;
|
||||
|
||||
int index = themes.FindIndex(item => item.ThemeName == themeNamespace);
|
||||
if (index == -1)
|
||||
{
|
||||
// determine if this theme implements ITheme
|
||||
Type themetype = assembly.GetTypes()
|
||||
.Where(item => item.Namespace != null)
|
||||
.Where(item => item.Namespace.StartsWith(@namespace))
|
||||
.Where(item => item.Namespace.StartsWith(themeNamespace))
|
||||
.Where(item => item.GetInterfaces().Contains(typeof(ITheme))).FirstOrDefault();
|
||||
if (themetype != null)
|
||||
{
|
||||
@ -76,25 +78,25 @@ namespace Oqtane.Repository
|
||||
};
|
||||
}
|
||||
// set internal properties
|
||||
theme.ThemeName = @namespace;
|
||||
theme.ThemeName = themeNamespace;
|
||||
theme.ThemeControls = "";
|
||||
theme.PaneLayouts = "";
|
||||
theme.ContainerControls = "";
|
||||
theme.AssemblyName = assembly.FullName.Split(",")[0];
|
||||
themes.Add(theme);
|
||||
index = themes.FindIndex(item => item.ThemeName == @namespace);
|
||||
index = themes.FindIndex(item => item.ThemeName == themeNamespace);
|
||||
}
|
||||
theme = themes[index];
|
||||
theme.ThemeControls += (themeControlType.FullName + ", " + typename[1] + ";");
|
||||
theme.ThemeControls += (themeControlType.FullName + ", " + themeControlType.Assembly.GetName().Name + ";");
|
||||
|
||||
// layouts
|
||||
Type[] layouttypes = assembly.GetTypes()
|
||||
.Where(item => item.Namespace != null)
|
||||
.Where(item => item.Namespace.StartsWith(@namespace))
|
||||
.Where(item => item.Namespace.StartsWith(themeNamespace))
|
||||
.Where(item => item.GetInterfaces().Contains(typeof(ILayoutControl))).ToArray();
|
||||
foreach (Type layouttype in layouttypes)
|
||||
{
|
||||
string panelayout = layouttype.FullName + ", " + typename[1] + ";";
|
||||
string panelayout = layouttype.FullName + ", " + themeControlType.Assembly.GetName().Name + ";";
|
||||
if (!theme.PaneLayouts.Contains(panelayout))
|
||||
{
|
||||
theme.PaneLayouts += panelayout;
|
||||
@ -104,11 +106,11 @@ namespace Oqtane.Repository
|
||||
// containers
|
||||
Type[] containertypes = assembly.GetTypes()
|
||||
.Where(item => item.Namespace != null)
|
||||
.Where(item => item.Namespace.StartsWith(@namespace))
|
||||
.Where(item => item.Namespace.StartsWith(themeNamespace))
|
||||
.Where(item => item.GetInterfaces().Contains(typeof(IContainerControl))).ToArray();
|
||||
foreach (Type containertype in containertypes)
|
||||
{
|
||||
string container = containertype.FullName + ", " + typename[1] + ";";
|
||||
string container = containertype.FullName + ", " + themeControlType.Assembly.GetName().Name + ";";
|
||||
if (!theme.ContainerControls.Contains(container))
|
||||
{
|
||||
theme.ContainerControls += container;
|
||||
@ -117,7 +119,6 @@ namespace Oqtane.Repository
|
||||
|
||||
themes[index] = theme;
|
||||
}
|
||||
}
|
||||
return themes;
|
||||
}
|
||||
}
|
||||
|
14
Oqtane.Server/Scripts/Tenant.0.9.1.sql
Normal file
14
Oqtane.Server/Scripts/Tenant.0.9.1.sql
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
|
||||
migration script
|
||||
|
||||
*/
|
||||
|
||||
ALTER TABLE [dbo].[Module] ADD
|
||||
[AllPages] [bit] NULL
|
||||
GO
|
||||
|
||||
UPDATE [dbo].[Module]
|
||||
SET [AllPages] = 0
|
||||
GO
|
||||
|
@ -41,7 +41,7 @@ namespace Oqtane
|
||||
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc().AddNewtonsoftJson();
|
||||
|
||||
services.AddServerSideBlazor();
|
||||
|
||||
// setup HttpClient for server side in a client side compatible fashion ( with auth cookie )
|
||||
@ -188,16 +188,13 @@ namespace Oqtane
|
||||
services.AddTransient<IUpgradeManager, UpgradeManager>();
|
||||
|
||||
// load the external assemblies into the app domain
|
||||
services.AddOqtaneModules();
|
||||
services.AddOqtaneThemes();
|
||||
services.AddOqtaneSiteTemplates();
|
||||
services.AddOqtaneParts();
|
||||
|
||||
services.AddMvc()
|
||||
.AddOqtaneApplicationParts() // register any Controllers from custom modules
|
||||
.AddNewtonsoftJson();
|
||||
|
||||
services.AddOqtaneServices();
|
||||
services.AddOqtaneHostedServices();
|
||||
|
||||
|
||||
services.AddSwaggerGen(c =>
|
||||
{
|
||||
|
@ -37,6 +37,10 @@
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
.app-menu {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@ -83,6 +87,7 @@
|
||||
|
||||
.navbar-toggler {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,16 +28,6 @@ app {
|
||||
width: 100%; /* 100% width */
|
||||
}
|
||||
|
||||
/* Pad the navigation links */
|
||||
.app-controlpanel .nav-item {
|
||||
font-size: 0.9rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.app-controlpanel .card-body .control-label {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Admin Modal */
|
||||
.app-admin-modal .modal {
|
||||
position: fixed; /* Stay in place */
|
||||
|
@ -70,7 +70,7 @@ window.interop = {
|
||||
link.integrity = integrity;
|
||||
}
|
||||
if (crossorigin !== "") {
|
||||
link.crossorigin = crossorigin;
|
||||
link.crossOrigin = crossorigin;
|
||||
}
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
@ -87,7 +87,7 @@ window.interop = {
|
||||
if (integrity !== "" && link.integrity !== integrity) {
|
||||
link.setAttribute('integrity', integrity);
|
||||
}
|
||||
if (crossorigin !== "" && link.crossorigin !== crossorigin) {
|
||||
if (crossorigin !== "" && link.crossOrigin !== crossorigin) {
|
||||
link.setAttribute('crossorigin', crossorigin);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ namespace Oqtane.Models
|
||||
public int ModuleId { get; set; }
|
||||
public int SiteId { get; set; }
|
||||
public string ModuleDefinitionName { get; set; }
|
||||
public bool AllPages { get; set; }
|
||||
|
||||
public string CreatedBy { get; set; }
|
||||
public DateTime CreatedOn { get; set; }
|
||||
|
@ -4,7 +4,7 @@
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>0.9.0</Version>
|
||||
<Version>0.9.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -3,8 +3,8 @@
|
||||
public class Constants
|
||||
{
|
||||
public const string PackageId = "Oqtane.Framework";
|
||||
public const string Version = "0.9.0";
|
||||
public const string ReleaseVersions = "0.9.0";
|
||||
public const string Version = "0.9.1";
|
||||
public const string ReleaseVersions = "0.9.0,0.9.1";
|
||||
|
||||
public const string PageComponent = "Oqtane.UI.ThemeBuilder, Oqtane.Client";
|
||||
public const string ContainerComponent = "Oqtane.UI.ContainerBuilder, Oqtane.Client";
|
||||
|
9
Oqtane.Shared/Shared/OqtaneIgnoreAttribute.cs
Normal file
9
Oqtane.Shared/Shared/OqtaneIgnoreAttribute.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Oqtane.Shared
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class OqtaneIgnoreAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Version>0.9.0</Version>
|
||||
<Version>0.9.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -10,14 +10,12 @@ namespace Oqtane.Upgrade
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
|
||||
// assumes that the application executable must be deployed to the /bin of the Oqtane.Server project
|
||||
if (binfolder.Contains(Path.Combine("Oqtane.Server", "bin")))
|
||||
// requires 2 arguments - the contentrootpath and the webrootpath of the site
|
||||
if (args.Length == 2)
|
||||
{
|
||||
// ie. binfolder = Oqtane.Server\bin\Debug\netcoreapp3.0\
|
||||
string rootfolder = Directory.GetParent(binfolder).Parent.Parent.FullName;
|
||||
string deployfolder = Path.Combine(rootfolder, Path.Combine("wwwroot","Framework"));
|
||||
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
string rootfolder = args[0];
|
||||
string deployfolder = Path.Combine(args[1], "Framework");
|
||||
|
||||
if (Directory.Exists(deployfolder))
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ Please note that this project is governed by the **[.NET Foundation Contributor
|
||||
# Roadmap
|
||||
This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist.
|
||||
|
||||
Note: We are planning to release V1 at the same time that Blazor WebAssembly ships on May 21, 2020
|
||||
Note: We are planning to release V1 at the same time that Blazor WebAssembly ships on May 19, 2020
|
||||
|
||||
V1 (MVP)
|
||||
- [x] Multi-Tenant ( Shared Database & Isolated Database )
|
||||
|
Reference in New Issue
Block a user