Merge pull request #5 from oqtane/master

Sync master
This commit is contained in:
jimspillane 2020-05-09 14:04:36 -04:00 committed by GitHub
commit 2362faaee1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 783 additions and 11452 deletions

View File

@ -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 -->

View File

@ -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"
};

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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);

View File

@ -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>
}

View File

@ -1,6 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@if (_visible)
{
<div class="app-admin-modal">

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inject IUserService UserService
@if (_authorized)

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@if (_text != string.Empty)
{

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inject IFolderService FolderService
@inject IFileService FileService
@inject IJSRuntime JsRuntime

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@if (!string.IsNullOrEmpty(HelpText))
{

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@if (!string.IsNullOrEmpty(_message))
{

View File

@ -1,7 +1,9 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@typeparam TableItem
<p>
@if(Format == "Table")
{

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inject IRoleService RoleService
@inject IUserService UserService

View File

@ -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>&nbsp;&nbsp;
<button type="button" class="btn btn-primary" @onclick="InsertImage">Insert Image</button>
@if (_filemanagervisible)
{
@((MarkupString)"&nbsp;&nbsp;")
<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);
}
}

View File

@ -2,7 +2,7 @@
using Microsoft.JSInterop;
using System.Threading.Tasks;
namespace Oqtane.UI
namespace Oqtane.Modules.Controls
{
public static class RichTextEditorInterop
{

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
<div class="d-flex">
<div>

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@if (Name == Parent.ActiveTab)
{

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
<CascadingValue Value="this">
<div class="container-fluid">

View File

@ -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);
}
}
}

View File

@ -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)

View File

@ -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>

View File

@ -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}");
}

View File

@ -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">

View File

@ -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>

View File

@ -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;

View File

@ -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">&times;</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();

View File

@ -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>

View File

@ -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}";
}
}
}

View File

@ -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)

View File

@ -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>
}

View File

@ -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>
}

View File

@ -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 = "";

View File

@ -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 {

View File

@ -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">

View File

@ -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");
}
}

View File

@ -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);

View File

@ -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);

View File

@ -18,3 +18,4 @@
@using Oqtane.Themes
@using Oqtane.Themes.Controls
@using Oqtane.UI
@using Oqtane.Enums

View File

@ -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>

View File

@ -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
{

View File

@ -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);

View File

@ -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)

View File

@ -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());
}
}
}

View File

@ -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))

View File

@ -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}");
}
}
}

View 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));
}
}
}

View File

@ -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,

View File

@ -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>

View File

@ -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;
}
}
}

View File

@ -775,6 +775,7 @@ namespace Oqtane.Repository
{
SiteId = site.SiteId,
ModuleDefinitionName = pagetemplatemodule.ModuleDefinitionName,
AllPages = false,
Permissions = pagetemplatemodule.ModulePermissions,
};
module = _moduleRepository.AddModule(module);

View File

@ -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);

View File

@ -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;
}
}

View File

@ -0,0 +1,14 @@
/*
migration script
*/
ALTER TABLE [dbo].[Module] ADD
[AllPages] [bit] NULL
GO
UPDATE [dbo].[Module]
SET [AllPages] = 0
GO

View File

@ -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 =>
{

View File

@ -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

View File

@ -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 */

View File

@ -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);
}
}

View File

@ -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; }

View File

@ -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>

View File

@ -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";

View File

@ -0,0 +1,9 @@
using System;
namespace Oqtane.Shared
{
[AttributeUsage(AttributeTargets.Class)]
public class OqtaneIgnoreAttribute : Attribute
{
}
}

View File

@ -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>

View File

@ -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))
{

View File

@ -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 )