add theme creator module to simplify the scaffolding of external themes

This commit is contained in:
Shaun Walker 2021-04-07 13:05:18 -04:00
parent d05747af1e
commit 1d3a79437c
20 changed files with 760 additions and 6 deletions

View File

@ -0,0 +1,186 @@
@namespace Oqtane.Modules.Admin.ThemeCreator
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IThemeService ThemeService
@inject IModuleService ModuleService
@inject IPageModuleService PageModuleService
@inject ISystemService SystemService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@using System.Text.RegularExpressions
@using System.IO;
@if (string.IsNullOrEmpty(_themename) && _systeminfo != null && _templates != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="owner" HelpText="Enter the name of the organization who is developing this theme. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" />
</td>
</tr>
<tr>
<td>
<Label For="module" HelpText="Enter a name for this theme. It should not contain spaces or punctuation." ResourceKey="ThemeName">Theme Name: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_theme" />
</td>
</tr>
<tr>
<td>
<Label For="template" HelpText="Select a theme template. Templates are located in the wwwroot/Themes/Templates folder on the server." ResourceKey="Template">Template: </Label>
</td>
<td>
<select id="template" class="form-control" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;@Localizer["Select Template"]&gt;</option>
@foreach (string template in _templates)
{
<option value="@template">@template</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
</td>
<td>
<select id="reference" class="form-control" @bind="@_reference">
@foreach (string version in Constants.ReleaseVersions.Split(','))
{
if (Version.Parse(version).CompareTo(Version.Parse("2.0.0")) >= 0)
{
<option value="@(version)">@(version)</option>
}
}
<option value="local">@Localizer["Local Version"]</option>
</select>
</td>
</tr>
@if (!string.IsNullOrEmpty(_location))
{
<tr>
<td>
<Label For="location" HelpText="Location where the theme will be created" ResourceKey="Location">Location: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_location" readonly />
</td>
</tr>
}
</table>
<button type="button" class="btn btn-success" @onclick="CreateTheme">@Localizer["Create Theme"]</button>
}
else
{
<button type="button" class="btn btn-success" @onclick="ActivateTheme">@Localizer["Activate Theme"]</button>
}
@code {
private string _themename = string.Empty;
private string _owner = string.Empty;
private string _theme = string.Empty;
private string _template = "-";
private string _reference = Constants.Version;
private string _location = string.Empty;
private Dictionary<string, string> _systeminfo;
private List<string> _templates;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
{
try
{
_themename = SettingService.GetSetting(ModuleState.Settings, "ThemeName", "");
_systeminfo = await SystemService.GetSystemInfoAsync();
_templates = await ThemeService.GetThemeTemplatesAsync();
if (string.IsNullOrEmpty(_themename))
{
AddModuleMessage(Localizer["Please Note That The Theme Creator Is Only Intended To Be Used In A Development Environment"], MessageType.Info);
}
else
{
AddModuleMessage(Localizer["Once You Have Compiled The Theme And Restarted The Application You Can Activate The Theme Below"], MessageType.Info);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Theme Creator");
}
}
private async Task CreateTheme()
{
try
{
if (IsValid(_owner) && IsValid(_theme) && _owner != _theme && _template != "-")
{
var theme = new Theme { Owner = _owner, Name = _theme, Template = _template, Version = _reference };
theme = await ThemeService.CreateThemeAsync(theme);
var settings = ModuleState.Settings;
SettingService.SetSetting(settings, "ThemeName", theme.ThemeName);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
GetLocation();
AddModuleMessage(Localizer["The Source Code For Your Theme Has Been Created At The Location Specified Below And Must Be Compiled In Order To Make It Functional. Once It Has Been Compiled You Must <a href=\"{0}\">Restart</a> Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success);
}
else
{
AddModuleMessage(Localizer["You Must Provide A Valid Owner Name And Theme Name ( ie. No Punctuation Or Spaces And The Values Cannot Be The Same ) And Choose A Template"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Creating Theme");
}
}
private async Task ActivateTheme()
{
try
{
if (!string.IsNullOrEmpty(_themename))
{
await PageModuleService.DeletePageModuleAsync(ModuleState.PageModuleId);
await ModuleService.DeleteModuleAsync(ModuleState.ModuleId);
NavigationManager.NavigateTo(NavigateUrl());
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Activating Theme");
}
}
private bool IsValid(string name)
{
// must contain letters, underscores and digits and first character must be letter or underscore
return !string.IsNullOrEmpty(name) && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
}
private void TemplateChanged(ChangeEventArgs e)
{
_template = (string)e.Value;
GetLocation();
}
private void GetLocation()
{
_location = string.Empty;
if (_template != "-" && _systeminfo != null && _systeminfo.ContainsKey("serverpath"))
{
string[] path = _systeminfo["serverpath"].Split(Path.DirectorySeparatorChar);
_location = string.Join(Path.DirectorySeparatorChar, path, 0, path.Length - 2) +
Path.DirectorySeparatorChar + _owner + "." + _theme;
}
StateHasChanged();
}
}

View File

@ -0,0 +1,15 @@
using Oqtane.Models;
namespace Oqtane.Modules.Admin.ThemeCreator
{
public class ModuleInfo : IModule
{
public ModuleDefinition ModuleDefinition => new ModuleDefinition
{
Name = "Theme Creator",
Description = "Enables software developers to quickly create themes by automating many of the initial theme creation tasks",
Version = "1.0.0",
Categories = "Developer"
};
}
}

View File

@ -1,4 +1,4 @@
using Oqtane.Models;
using Oqtane.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
@ -12,5 +12,7 @@ namespace Oqtane.Services
List<ThemeControl> GetContainerControls(List<Theme> themes, string themeName);
Task InstallThemesAsync();
Task DeleteThemeAsync(string themeName);
Task<Theme> CreateThemeAsync(Theme theme);
Task<List<string>> GetThemeTemplatesAsync();
}
}

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
@ -50,5 +50,16 @@ namespace Oqtane.Services
{
await DeleteAsync($"{ApiUrl}/{themeName}");
}
public async Task<Theme> CreateThemeAsync(Theme theme)
{
return await PostJsonAsync($"{ApiUrl}", theme);
}
public async Task<List<string>> GetThemeTemplatesAsync()
{
List<string> templates = await GetJsonAsync<List<string>>($"{ApiUrl}/templates");
return templates;
}
}
}

View File

@ -0,0 +1,83 @@
/* Oqtane Styles */
body {
padding-top: 7rem;
}
.controls {
z-index: 2000;
padding-top: 15px;
padding-bottom: 15px;
margin-right: 10px;
}
.app-menu .nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.app-menu .nav-item a {
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
}
.app-menu .nav-item a.active {
background-color: rgba(255,255,255,0.25);
color: white;
}
.app-menu .nav-item a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
.app-menu .nav-link .oi {
width: 1.5rem;
font-size: 1.1rem;
vertical-align: text-top;
top: -2px;
}
.navbar-toggler {
background-color: rgba(255, 255, 255, 0.1);
margin-left: auto;
}
div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu {
color:#ffffff;
}
@media (max-width: 767px) {
.app-menu {
width: 100%
}
.navbar {
position: fixed;
top: 60px;
width: 100%;
}
.controls {
height: 60px;
top: 15px;
position: fixed;
top: 0px;
width: 100%;
background-color: rgb(0, 0, 0);
}
.controls-group {
float: right;
margin-right: 25px;
}
.content {
position: relative;
top: 60px;
}
}

View File

@ -175,9 +175,9 @@ namespace Oqtane.Controllers
// GET: api/<controller>/templates
[HttpGet("templates")]
[Authorize(Roles = RoleNames.Host)]
public List<string> Get()
public List<string> GetTemplates()
{
var templates = new List<String>();
var templates = new List<string>();
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", Path.DirectorySeparatorChar.ToString());
foreach (string directory in Directory.GetDirectories(templatePath))
{
@ -186,7 +186,7 @@ namespace Oqtane.Controllers
return templates;
}
// POST api/<controller>?moduleid=x
// POST api/<controller>
[HttpPost]
[Authorize(Roles = RoleNames.Host)]
public ModuleDefinition Post([FromBody] ModuleDefinition moduleDefinition)

View File

@ -103,5 +103,91 @@ namespace Oqtane.Controllers
}
}
// GET: api/<controller>/templates
[HttpGet("templates")]
[Authorize(Roles = RoleNames.Host)]
public List<string> GetTemplates()
{
var templates = new List<string>();
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Themes", "Templates", Path.DirectorySeparatorChar.ToString());
foreach (string directory in Directory.GetDirectories(templatePath))
{
templates.Add(directory.Replace(templatePath, ""));
}
return templates;
}
// POST api/<controller>
[HttpPost]
[Authorize(Roles = RoleNames.Host)]
public Theme Post([FromBody] Theme theme)
{
if (ModelState.IsValid)
{
string rootPath;
DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Themes", "Templates", theme.Template, Path.DirectorySeparatorChar.ToString());
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName, theme.Owner + "." + theme.Name, Path.DirectorySeparatorChar.ToString());
theme.ThemeName = theme.Owner + "." + theme.Name + ", " + theme.Owner + "." + theme.Name + ".Client.Oqtane";
ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, theme);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Theme Created {Theme}", theme);
}
return theme;
}
private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, string rootFolder, string templatePath, Theme theme)
{
// process folder
string folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, ""));
folderPath = folderPath.Replace("[Owner]", theme.Owner);
folderPath = folderPath.Replace("[Theme]", theme.Name);
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
FileInfo[] files = current.GetFiles("*.*");
if (files != null)
{
foreach (FileInfo file in files)
{
// process file
string filePath = Path.Combine(folderPath, file.Name);
filePath = filePath.Replace("[Owner]", theme.Owner);
filePath = filePath.Replace("[Theme]", theme.Name);
string text = System.IO.File.ReadAllText(file.FullName);
text = text.Replace("[Owner]", theme.Owner);
text = text.Replace("[Theme]", theme.Name);
text = text.Replace("[RootPath]", rootPath);
text = text.Replace("[RootFolder]", rootFolder);
text = text.Replace("[Folder]", folderPath);
text = text.Replace("[File]", Path.GetFileName(filePath));
if (theme.Version == "local")
{
text = text.Replace("[FrameworkVersion]", Constants.Version);
text = text.Replace("[ClientReference]", "<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\oqtane.framework\\Oqtane.Server\\bin\\Debug\\net5.0\\Oqtane.Client.dll</HintPath></Reference>");
text = text.Replace("[SharedReference]", "<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\oqtane.framework\\Oqtane.Server\\bin\\Debug\\net5.0\\Oqtane.Shared.dll</HintPath></Reference>");
}
else
{
text = text.Replace("[FrameworkVersion]", theme.Version);
text = text.Replace("[ClientReference]", "<PackageReference Include=\"Oqtane.Client\" Version=\"" + theme.Version + "\" />");
text = text.Replace("[SharedReference]", "<PackageReference Include=\"Oqtane.Shared\" Version=\"" + theme.Version + "\" />");
}
System.IO.File.WriteAllText(filePath, text);
}
DirectoryInfo[] folders = current.GetDirectories();
foreach (DirectoryInfo folder in folders.Reverse())
{
ProcessTemplatesRecursively(folder, rootPath, rootFolder, templatePath, theme);
}
}
}
}
}

View File

@ -0,0 +1,4 @@
using System.Resources;
using Microsoft.Extensions.Localization;
[assembly: RootNamespace("[Owner].[Theme].Client")]

View File

@ -0,0 +1,20 @@
@namespace [Owner].[Theme]
@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">
<div class="container">
<ModuleInstance />
</div>
</div>
</div>
@code {
public override string Name => "Container1";
}

View File

@ -0,0 +1,15 @@
using Oqtane.Models;
using Oqtane.Themes;
namespace [Owner].[Theme]
{
public class ThemeInfo : ITheme
{
public Theme Theme => new Theme
{
Name = "[Theme]",
Version = "1.0.0"
};
}
}

View File

@ -0,0 +1,107 @@
@namespace [Owner].[Theme]
@inherits ThemeBase
<main role="main">
<nav class="navbar navbar-expand-md navbar-dark bg-primary fixed-top">
<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="row">
<div class="col-md-12">
<Pane Name="Content" />
</div>
</div>
</div>
<Pane Name="Top Full Width" />
<div class="container">
<div class="row">
<div class="col-md-12">
<Pane Name="Top 100%" />
</div>
</div>
<div class="row">
<div class="col-md-6">
<Pane Name="Left 50%" />
</div>
<div class="col-md-6">
<Pane Name="Right 50%" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<Pane Name="Left 33%" />
</div>
<div class="col-md-4">
<Pane Name="Center 33%" />
</div>
<div class="col-md-4">
<Pane Name="Right 33%" />
</div>
</div>
<div class="row">
<div class="col-md-3">
<Pane Name="Left Outer 25%" />
</div>
<div class="col-md-3">
<Pane Name="Left Inner 25%" />
</div>
<div class="col-md-3">
<Pane Name="Right Inner 25%" />
</div>
<div class="col-md-3">
<Pane Name="Right Outer 25%" />
</div>
</div>
<div class="row">
<div class="col-md-3">
<Pane Name="Left 25%" />
</div>
<div class="col-md-6">
<Pane Name="Center 50%" />
</div>
<div class="col-md-3">
<Pane Name="Right 25%" />
</div>
</div>
<div class="row">
<div class="col-md-8">
<Pane Name="Left Sidebar 66%" />
</div>
<div class="col-md-4">
<Pane Name="Right Sidebar 33%" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<Pane Name="Left Sidebar 33%" />
</div>
<div class="col-md-8">
<Pane Name="Right Sidebar 66%" />
</div>
</div>
<div class="row">
<div class="col-md-12">
<Pane Name="Bottom 100%" />
</div>
</div>
</div>
<Pane Name="Bottom Full Width" />
</main>
@code {
public override string Name => "Theme1";
public override string Panes => "Content,Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width";
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css", Integrity = "sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://code.jquery.com/jquery-3.5.1.slim.min.js", Integrity = "sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js", Integrity = "sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js", Integrity = "sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI", CrossOrigin = "anonymous" }
};
}

View File

@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RazorLangVersion>3.0</RazorLangVersion>
<Version>1.0.0</Version>
<Authors>[Owner]</Authors>
<Company>[Owner]</Company>
<Description>[Description]</Description>
<Product>[Owner].[Theme]</Product>
<Copyright>[Owner]</Copyright>
<AssemblyName>[Owner].[Theme].Client.Oqtane</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="5.0.0" />
<PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
[ClientReference]
[SharedReference]
</ItemGroup>
<PropertyGroup>
<!-- there may be other elements here -->
<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,21 @@
@using System
@using System.Linq
@using System.Collections.Generic
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
@using Oqtane.Models
@using Oqtane.Modules
@using Oqtane.Modules.Controls
@using Oqtane.Providers
@using Oqtane.Security
@using Oqtane.Services
@using Oqtane.Shared
@using Oqtane.Themes
@using Oqtane.Themes.Controls
@using Oqtane.UI
@using Oqtane.Enums

View File

@ -0,0 +1,83 @@
/* Oqtane Styles */
body {
padding-top: 7rem;
}
.controls {
z-index: 2000;
padding-top: 15px;
padding-bottom: 15px;
margin-right: 10px;
}
.app-menu .nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.app-menu .nav-item a {
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
}
.app-menu .nav-item a.active {
background-color: rgba(255,255,255,0.25);
color: white;
}
.app-menu .nav-item a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
.app-menu .nav-link .oi {
width: 1.5rem;
font-size: 1.1rem;
vertical-align: text-top;
top: -2px;
}
.navbar-toggler {
background-color: rgba(255, 255, 255, 0.1);
margin-left: auto;
}
div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu {
color:#ffffff;
}
@media (max-width: 767px) {
.app-menu {
width: 100%
}
.navbar {
position: fixed;
top: 60px;
width: 100%;
}
.controls {
height: 60px;
top: 15px;
position: fixed;
top: 0px;
width: 100%;
background-color: rgb(0, 0, 0);
}
.controls-group {
float: right;
margin-right: 25px;
}
.content {
position: relative;
top: 60px;
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Client\[Owner].[Theme].Client.csproj" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="IF $(ConfigurationName) == Debug (debug.cmd)" />
<Exec Command="IF $(ConfigurationName) == Release (release.cmd)" />
</Target>
</Project>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>[Owner].[Theme]</id>
<version>1.0.0</version>
<authors>[Owner]</authors>
<owners>[Owner]</owners>
<title>[Theme]</title>
<description>[Theme]</description>
<copyright>[Owner]</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane module</tags>
<releaseNotes></releaseNotes>
<summary></summary>
<dependencies>
<dependency id="Oqtane.Framework" version="[FrameworkVersion]" />
</dependencies>
</metadata>
<files>
<file src="..\Client\bin\Release\net5.0\[Owner].[Theme].Client.Oqtane.dll" target="lib\net5.0" />
<file src="..\Client\bin\Release\net5.0\[Owner].[Theme].Client.Oqtane.pdb" target="lib\net5.0" />
<file src="..\Client\wwwroot\**\*.*" target="wwwroot" />
</files>
</package>

View File

@ -0,0 +1,3 @@
XCOPY "..\Client\bin\Debug\net5.0\[Owner].[Theme].Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net5.0\" /Y
XCOPY "..\Client\bin\Debug\net5.0\[Owner].[Theme].Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net5.0\" /Y
XCOPY "..\Client\wwwroot\*" "..\..\[RootFolder]\Oqtane.Client\wwwroot\" /Y /S /I

View File

@ -0,0 +1,2 @@
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack [Owner].[Theme].nuspec
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\wwwroot\Themes\" /Y

View File

@ -0,0 +1,36 @@

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].[Theme].Client", "Client\[Owner].[Theme].Client.csproj", "{AA8E58A1-CD09-4208-BF66-A8BB341FD669}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "[Owner].[Theme].Package", "Package\[Owner].[Theme].Package.csproj", "{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
Wasm|Any CPU = Wasm|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.Build.0 = Release|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Wasm|Any CPU.ActiveCfg = Release|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Wasm|Any CPU.Build.0 = Release|Any CPU
{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Release|Any CPU.Build.0 = Release|Any CPU
{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Wasm|Any CPU.ActiveCfg = Debug|Any CPU
{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Wasm|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1D016F15-46FE-4726-8DFD-2E4FD4DC7668}
EndGlobalSection
EndGlobal

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
namespace Oqtane.Models
@ -14,6 +14,7 @@ namespace Oqtane.Models
Contact = "";
License = "";
Dependencies = "";
Template = "";
}
public string ThemeName { get; set; }
@ -28,6 +29,7 @@ namespace Oqtane.Models
public List<ThemeControl> Themes { get; set; }
public List<ThemeControl> Layouts { get; set; }
public List<ThemeControl> Containers { get; set; }
public string Template { get; set; }
//[Obsolete("This property is obsolete. Use Themes instead.", false)]
public string ThemeControls { get; set; }