filter deleted pages and modules in the router, provide support for cascading aspect of style sheets, replace ResourceDeclaration concept with ResourceLevel

This commit is contained in:
Shaun Walker 2022-03-31 21:05:58 -04:00
parent 06e25e04f8
commit 0fcf1c2732
18 changed files with 77 additions and 110 deletions

View File

@ -158,8 +158,8 @@
if (firstRender) if (firstRender)
{ {
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.IncludeLink("", "stylesheet", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css", "text/css", "sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==", "anonymous", ""); await interop.IncludeLink("", "stylesheet", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css", "text/css", "sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==", "anonymous");
await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", "anonymous", "", "head", ""); await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", "anonymous", "", "head");
} }
} }

View File

@ -27,6 +27,6 @@
protected override void OnInitialized() protected override void OnInitialized()
{ {
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin"); var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
_pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId && !item.IsDeleted).ToList(); _pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId).ToList();
} }
} }

View File

@ -252,7 +252,7 @@
_title = page.Title; _title = page.Title;
_meta = page.Meta; _meta = page.Meta;
_path = page.Path; _path = page.Path;
_pageModules = PageState.Modules.Where(m => m.PageId == page.PageId && m.IsDeleted == false).ToList(); _pageModules = PageState.Modules.Where(m => m.PageId == page.PageId).ToList();
if (string.IsNullOrEmpty(_path)) if (string.IsNullOrEmpty(_path))
{ {

View File

@ -13,6 +13,7 @@
<Header> <Header>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th> <th>@SharedLocalizer["Name"]</th>
</Header> </Header>
<Row> <Row>

View File

@ -53,7 +53,7 @@ namespace Oqtane.Modules
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script)) if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
{ {
var scripts = new List<object>(); var scripts = new List<object>();
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script && item.Declaration != ResourceDeclaration.Global)) foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
{ {
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module }); scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module });
} }

View File

@ -333,9 +333,8 @@
if (PageId != "-") if (PageId != "-")
{ {
_modules = PageState.Modules _modules = PageState.Modules
.Where(module => module.PageId == int.Parse(PageId) .Where(module => module.PageId == int.Parse(PageId) &&
&& !module.IsDeleted UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
&& UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
.ToList(); .ToList();
} }
ModuleId = "-"; ModuleId = "-";

View File

@ -30,7 +30,7 @@ namespace Oqtane.Themes.Controls
private IEnumerable<Page> GetMenuPages() private IEnumerable<Page> GetMenuPages()
{ {
var securityLevel = int.MaxValue; var securityLevel = int.MaxValue;
foreach (Page p in PageState.Pages.Where(item => item.IsNavigation && !item.IsDeleted)) foreach (Page p in PageState.Pages.Where(item => item.IsNavigation))
{ {
if (p.Level <= securityLevel && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (p.Level <= securityLevel && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{ {

View File

@ -32,7 +32,7 @@ namespace Oqtane.Themes
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script)) if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
{ {
var scripts = new List<object>(); var scripts = new List<object>();
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script && item.Declaration != ResourceDeclaration.Global)) foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
{ {
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module }); scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module });
} }

View File

@ -72,13 +72,13 @@ namespace Oqtane.UI
} }
} }
public Task IncludeLink(string id, string rel, string href, string type, string integrity, string crossorigin, string key) public Task IncludeLink(string id, string rel, string href, string type, string integrity, string crossorigin)
{ {
try try
{ {
_jsRuntime.InvokeVoidAsync( _jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.includeLink", "Oqtane.Interop.includeLink",
id, rel, href, type, integrity, crossorigin, key); id, rel, href, type, integrity, crossorigin);
return Task.CompletedTask; return Task.CompletedTask;
} }
catch catch
@ -102,13 +102,13 @@ namespace Oqtane.UI
} }
} }
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string content, string location, string key) public Task IncludeScript(string id, string src, string integrity, string crossorigin, string content, string location)
{ {
try try
{ {
_jsRuntime.InvokeVoidAsync( _jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.includeScript", "Oqtane.Interop.includeScript",
id, src, integrity, crossorigin, content, location, key); id, src, integrity, crossorigin, content, location);
return Task.CompletedTask; return Task.CompletedTask;
} }
catch catch

View File

@ -48,7 +48,7 @@ else
if (Name.ToLower() == PaneNames.Admin.ToLower()) if (Name.ToLower() == PaneNames.Admin.ToLower())
{ {
Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId); Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId);
if (module != null && !module.IsDeleted) if (module != null)
{ {
var moduleType = Type.GetType(module.ModuleType); var moduleType = Type.GetType(module.ModuleType);
if (moduleType != null) if (moduleType != null)
@ -97,7 +97,7 @@ else
if (PageState.ModuleId != -1) if (PageState.ModuleId != -1)
{ {
Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId); Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId);
if (module != null && module.Pane.ToLower() == Name.ToLower() && !module.IsDeleted) if (module != null && module.Pane.ToLower() == Name.ToLower())
{ {
// check if user is authorized to view module // check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
@ -108,7 +108,7 @@ else
} }
else else
{ {
foreach (Module module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId && item.Pane.ToLower() == Name.ToLower() && !item.IsDeleted).OrderBy(x => x.Order).ToArray()) foreach (Module module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId && item.Pane.ToLower() == Name.ToLower()).OrderBy(x => x.Order).ToArray())
{ {
// check if user is authorized to view module // check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))

View File

@ -164,6 +164,7 @@
if (PageState == null || refresh == UI.Refresh.Site) if (PageState == null || refresh == UI.Refresh.Site)
{ {
pages = await PageService.GetPagesAsync(site.SiteId); pages = await PageService.GetPagesAsync(site.SiteId);
pages = pages.Where(item => !item.IsDeleted).ToList();
} }
else else
{ {
@ -206,6 +207,7 @@
if (PageState == null || refresh == UI.Refresh.Site) if (PageState == null || refresh == UI.Refresh.Site)
{ {
modules = await ModuleService.GetModulesAsync(site.SiteId); modules = await ModuleService.GetModulesAsync(site.SiteId);
modules = modules.Where(item => !item.IsDeleted).ToList();
} }
else else
{ {
@ -268,7 +270,7 @@
} }
else else
{ {
// site does not exist // site does not exist
} }
} }
@ -322,7 +324,7 @@
{ {
if (page.IsPersonalizable && user != null) if (page.IsPersonalizable && user != null)
{ {
// load the personalized page // load the personalized page
page = await PageService.GetPageAsync(page.PageId, user.UserId); page = await PageService.GetPageAsync(page.PageId, user.UserId);
} }
@ -338,7 +340,7 @@
Type themetype = Type.GetType(page.ThemeType); Type themetype = Type.GetType(page.ThemeType);
if (themetype == null) if (themetype == null)
{ {
// fallback // fallback
page.ThemeType = Constants.DefaultTheme; page.ThemeType = Constants.DefaultTheme;
themetype = Type.GetType(Constants.DefaultTheme); themetype = Type.GetType(Constants.DefaultTheme);
} }
@ -351,14 +353,14 @@
{ {
panes = themeobject.Panes; panes = themeobject.Panes;
} }
page.Resources = ManagePageResources(page.Resources, themeobject.Resources); page.Resources = ManagePageResources(page.Resources, themeobject.Resources, ResourceLevel.Page);
} }
} }
page.Panes = panes.Replace(";", ",").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); page.Panes = panes.Replace(";", ",").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
} }
catch catch
{ {
// error loading theme or layout // error loading theme or layout
} }
return page; return page;
@ -369,7 +371,7 @@
var paneindex = new Dictionary<string, int>(); var paneindex = new Dictionary<string, int>();
foreach (Module module in modules) foreach (Module module in modules)
{ {
// initialize module control properties // initialize module control properties
module.SecurityAccessLevel = SecurityAccessLevel.Host; module.SecurityAccessLevel = SecurityAccessLevel.Host;
module.ControlTitle = ""; module.ControlTitle = "";
module.Actions = ""; module.Actions = "";
@ -422,17 +424,17 @@
// get additional metadata from IModuleControl interface // get additional metadata from IModuleControl interface
if (moduletype != null && module.ModuleType != "") if (moduletype != null && module.ModuleType != "")
{ {
// retrieve module component resources // retrieve module component resources
var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl; var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources); page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module);
if (action.ToLower() == "settings" && module.ModuleDefinition != null) if (action.ToLower() == "settings" && module.ModuleDefinition != null)
{ {
// settings components are embedded within a framework settings module // settings components are embedded within a framework settings module
moduletype = Type.GetType(module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action), false, true); moduletype = Type.GetType(module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action), false, true);
if (moduletype != null) if (moduletype != null)
{ {
moduleobject = Activator.CreateInstance(moduletype) as IModuleControl; moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources); page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module);
} }
} }
@ -483,15 +485,16 @@
return (page, modules); return (page, modules);
} }
private List<Resource> ManagePageResources(List<Resource> pageresources, List<Resource> resources) private List<Resource> ManagePageResources(List<Resource> pageresources, List<Resource> resources, ResourceLevel level)
{ {
if (resources != null) if (resources != null)
{ {
foreach (var resource in resources) foreach (var resource in resources)
{ {
// ensure resource does not exist already // ensure resource does not exist already
if (pageresources.Find(item => item.Url == resource.Url) == null) if (pageresources.Find(item => item.Url == resource.Url) == null)
{ {
resource.Level = level;
pageresources.Add(resource); pageresources.Add(resource);
} }
} }

View File

@ -28,20 +28,22 @@
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
var interop = new Interop(JsRuntime); var interop = new Interop(JsRuntime);
// manage stylesheets for this page // manage stylesheets for this page
string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff"); string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff");
var links = new List<object>(); var links = new List<object>();
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet && item.Declaration != ResourceDeclaration.Global)) foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
{ {
links.Add(new { id = "app-stylesheet-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = resource.Url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", key = "" }); var prefix = "app-stylesheet-" + resource.Level.ToString().ToLower();
links.Add(new { id = prefix + "-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = resource.Url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", insertbefore = prefix });
} }
if (links.Any()) if (links.Any())
{ {
await interop.IncludeLinks(links.ToArray()); await interop.IncludeLinks(links.ToArray());
} }
await interop.RemoveElementsById("app-stylesheet", "", "app-stylesheet-" + batch + "-00"); await interop.RemoveElementsById("app-stylesheet-page-", "", "app-stylesheet-page-" + batch + "-00");
await interop.RemoveElementsById("app-stylesheet-module-", "", "app-stylesheet-module-" + batch + "-00");
// set page title // set page title
if (!string.IsNullOrEmpty(PageState.Page.Title)) if (!string.IsNullOrEmpty(PageState.Page.Title))

View File

@ -19,6 +19,8 @@
<script src="js/loadjs.min.js"></script> <script src="js/loadjs.min.js"></script>
<link rel="stylesheet" href="css/app.css" /> <link rel="stylesheet" href="css/app.css" />
@Html.Raw(Model.HeadResources) @Html.Raw(Model.HeadResources)
<link id="app-stylesheet-page" rel="stylesheet" href="disabled" disabled />
<link id="app-stylesheet-module" rel="stylesheet" href="disabled" disabled />
</head> </head>
<body> <body>
@if (string.IsNullOrEmpty(Model.Message)) @if (string.IsNullOrEmpty(Model.Message))

View File

@ -1,9 +1,7 @@
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.Modules;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Themes;
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -190,8 +188,6 @@ namespace Oqtane.Pages
foreach (Assembly assembly in assemblies) foreach (Assembly assembly in assemblies)
{ {
ProcessHostResources(assembly); ProcessHostResources(assembly);
ProcessModuleControls(assembly);
ProcessThemeControls(assembly);
} }
// set culture if not specified // set culture if not specified
@ -411,65 +407,19 @@ namespace Oqtane.Pages
var obj = Activator.CreateInstance(type) as IHostResources; var obj = Activator.CreateInstance(type) as IHostResources;
foreach (var resource in obj.Resources) foreach (var resource in obj.Resources)
{ {
resource.Declaration = ResourceDeclaration.Global; ProcessResource(resource);
ProcessResource(resource, 0);
} }
} }
} }
private void ProcessModuleControls(Assembly assembly) private void ProcessResource(Resource resource)
{
var types = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModuleControl)));
foreach (var type in types)
{
// Check if type should be ignored
if (type.IsOqtaneIgnore()) continue;
var obj = Activator.CreateInstance(type) as IModuleControl;
if (obj.Resources != null)
{
foreach (var resource in obj.Resources)
{
if (resource.Declaration == ResourceDeclaration.Global)
{
ProcessResource(resource, 0);
}
}
}
}
}
private void ProcessThemeControls(Assembly assembly)
{
var types = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IThemeControl)));
foreach (var type in types)
{
// Check if type should be ignored
if (type.IsOqtaneIgnore()) continue;
var obj = Activator.CreateInstance(type) as IThemeControl;
if (obj.Resources != null)
{
int count = 0; // required for local stylesheets for current theme
foreach (var resource in obj.Resources)
{
if (resource.Declaration == ResourceDeclaration.Global || (Utilities.GetFullTypeName(type.AssemblyQualifiedName) == ThemeType && resource.ResourceType == ResourceType.Stylesheet))
{
ProcessResource(resource, count++);
}
}
}
}
}
private void ProcessResource(Resource resource, int count)
{ {
switch (resource.ResourceType) switch (resource.ResourceType)
{ {
case ResourceType.Stylesheet: case ResourceType.Stylesheet:
if (!HeadResources.Contains(resource.Url, StringComparison.OrdinalIgnoreCase)) if (!HeadResources.Contains(resource.Url, StringComparison.OrdinalIgnoreCase))
{ {
var id = (resource.Declaration == ResourceDeclaration.Global) ? "" : "id=\"app-stylesheet-" + DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + "-" + count.ToString("00") + "\" "; HeadResources += "<link rel=\"stylesheet\" href=\"" + resource.Url + "\"" + CrossOrigin(resource.CrossOrigin) + Integrity(resource.Integrity) + " />" + Environment.NewLine;
HeadResources += "<link " + id + "rel=\"stylesheet\" href=\"" + resource.Url + "\"" + CrossOrigin(resource.CrossOrigin) + Integrity(resource.Integrity) + " />" + Environment.NewLine;
} }
break; break;
case ResourceType.Script: case ResourceType.Script:

View File

@ -53,14 +53,8 @@ Oqtane.Interop = {
} }
} }
}, },
includeLink: function (id, rel, href, type, integrity, crossorigin, key) { includeLink: function (id, rel, href, type, integrity, crossorigin, insertbefore) {
var link; var link = document.querySelector("link[href=\"" + CSS.escape(href) + "\"]");
if (id !== "" && key === "id") {
link = document.getElementById(id);
}
else {
link = document.querySelector("link[href=\"" + CSS.escape(href) + "\"]");
}
if (link === null) { if (link === null) {
link = document.createElement("link"); link = document.createElement("link");
if (id !== "") { if (id !== "") {
@ -77,7 +71,13 @@ Oqtane.Interop = {
if (crossorigin !== "") { if (crossorigin !== "") {
link.crossOrigin = crossorigin; link.crossOrigin = crossorigin;
} }
document.head.appendChild(link); if (insertbefore === "") {
document.head.appendChild(link);
}
else {
var sibling = document.getElementById(insertbefore);
sibling.parentNode.insertBefore(link, sibling);
}
} }
else { else {
if (link.id !== id) { if (link.id !== id) {
@ -116,17 +116,11 @@ Oqtane.Interop = {
}, },
includeLinks: function (links) { includeLinks: function (links) {
for (let i = 0; i < links.length; i++) { for (let i = 0; i < links.length; i++) {
this.includeLink(links[i].id, links[i].rel, links[i].href, links[i].type, links[i].integrity, links[i].crossorigin, links[i].key); this.includeLink(links[i].id, links[i].rel, links[i].href, links[i].type, links[i].integrity, links[i].crossorigin, links[i].insertbefore);
} }
}, },
includeScript: function (id, src, integrity, crossorigin, content, location, key) { includeScript: function (id, src, integrity, crossorigin, content, location) {
var script; var script = document.querySelector("script[src=\"" + CSS.escape(src) + "\"]");
if (id !== "" && key === "id") {
script = document.getElementById(id);
}
else {
script = document.querySelector("script[src=\"" + CSS.escape(src) + "\"]");
}
if (script === null) { if (script === null) {
script = document.createElement("script"); script = document.createElement("script");
if (id !== "") { if (id !== "") {

View File

@ -1,5 +1,8 @@
using System;
namespace Oqtane.Shared namespace Oqtane.Shared
{ {
[Obsolete("ResourceDeclaration is deprecated", false)]
public enum ResourceDeclaration public enum ResourceDeclaration
{ {
Local, Local,

View File

@ -0,0 +1,8 @@
namespace Oqtane.Shared
{
public enum ResourceLevel
{
Page,
Module
}
}

View File

@ -1,3 +1,4 @@
using System;
using Oqtane.Shared; using Oqtane.Shared;
namespace Oqtane.Models namespace Oqtane.Models
@ -33,12 +34,12 @@ namespace Oqtane.Models
public string Bundle { get; set; } public string Bundle { get; set; }
/// <summary> /// <summary>
/// Determines if the Resource is global or local, meaning that the entire solution uses it or just some modules. /// For Stylesheets this defines the relative position for cascading purposes
/// </summary> /// </summary>
public ResourceDeclaration Declaration { get; set; } public ResourceLevel Level { get; set; }
/// <summary> /// <summary>
/// If the Resource should be included in the `head` of the HTML document or the `body` /// For Scripts this defines if the resource should be included in the Head or Body
/// </summary> /// </summary>
public ResourceLocation Location { get; set; } public ResourceLocation Location { get; set; }
@ -46,5 +47,9 @@ namespace Oqtane.Models
/// For Scripts this allows type="module" registrations - not applicable to Stylesheets /// For Scripts this allows type="module" registrations - not applicable to Stylesheets
/// </summary> /// </summary>
public bool ES6Module { get; set; } public bool ES6Module { get; set; }
[Obsolete("ResourceDeclaration is deprecated", false)]
public ResourceDeclaration Declaration { get; set; }
} }
} }