Merge pull request #2828 from sbwalker/dev

ability to specify Resources in IModule and ITheme interfaces,, fixed module settings error for personalized pages
This commit is contained in:
Shaun Walker 2023-05-19 18:08:40 -04:00 committed by GitHub
commit 1ee9c2cf0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 506 additions and 446 deletions

View File

@ -44,12 +44,20 @@
<Label Class="col-sm-3" For="page" HelpText="The page that the module is located on" ResourceKey="Page">Page: </Label> <Label Class="col-sm-3" For="page" HelpText="The page that the module is located on" ResourceKey="Page">Page: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="page" class="form-select" @bind="@_pageId" required> <select id="page" class="form-select" @bind="@_pageId" required>
@foreach (Page p in PageState.Pages) @if (PageState.Page.UserId != null)
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList)) <option value="@PageState.Page.PageId">@(PageState.Page.Name)</option>
}
else
{
foreach (Page p in PageState.Pages)
{ {
<option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option> if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, p.PermissionList))
{
<option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option>
}
} }
} }
</select> </select>
</div> </div>

View File

@ -22,201 +22,201 @@
@DynamicComponent @DynamicComponent
@code { @code {
private string _absoluteUri; private string _absoluteUri;
private bool _navigationInterceptionEnabled; private bool _navigationInterceptionEnabled;
private PageState _pagestate; private PageState _pagestate;
private string _error = ""; private string _error = "";
[Parameter] [Parameter]
public string Runtime { get; set; } public string Runtime { get; set; }
[Parameter] [Parameter]
public string RenderMode { get; set; } public string RenderMode { get; set; }
[Parameter] [Parameter]
public int VisitorId { get; set; } public int VisitorId { get; set; }
[CascadingParameter] [CascadingParameter]
PageState PageState { get; set; } PageState PageState { get; set; }
[Parameter] [Parameter]
public Action<PageState> OnStateChange { get; set; } public Action<PageState> OnStateChange { get; set; }
private RenderFragment DynamicComponent { get; set; } private RenderFragment DynamicComponent { get; set; }
protected override void OnInitialized() protected override void OnInitialized()
{ {
_absoluteUri = NavigationManager.Uri; _absoluteUri = NavigationManager.Uri;
NavigationManager.LocationChanged += LocationChanged; NavigationManager.LocationChanged += LocationChanged;
DynamicComponent = builder => DynamicComponent = builder =>
{ {
if (PageState != null) if (PageState != null)
{ {
builder.OpenComponent(0, Type.GetType(Constants.PageComponent)); builder.OpenComponent(0, Type.GetType(Constants.PageComponent));
builder.CloseComponent(); builder.CloseComponent();
} }
}; };
} }
public void Dispose() public void Dispose()
{ {
NavigationManager.LocationChanged -= LocationChanged; NavigationManager.LocationChanged -= LocationChanged;
} }
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
if (PageState == null) if (PageState == null)
{ {
await Refresh(); await Refresh();
} }
} }
[SuppressMessage("ReSharper", "StringIndexOfIsCultureSpecific.1")] [SuppressMessage("ReSharper", "StringIndexOfIsCultureSpecific.1")]
private async Task Refresh() private async Task Refresh()
{ {
Site site; Site site;
Page page; Page page;
User user = null; User user = null;
var editmode = false; var editmode = false;
var refresh = false; var refresh = false;
var lastsyncdate = DateTime.UtcNow.AddHours(-1); var lastsyncdate = DateTime.UtcNow.AddHours(-1);
var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime); var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
_error = ""; _error = "";
Route route = new Route(_absoluteUri, SiteState.Alias.Path); Route route = new Route(_absoluteUri, SiteState.Alias.Path);
int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1; int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1;
var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction; var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction;
var querystring = ParseQueryString(route.Query); var querystring = ParseQueryString(route.Query);
var returnurl = ""; var returnurl = "";
if (querystring.ContainsKey("returnurl")) if (querystring.ContainsKey("returnurl"))
{ {
returnurl = WebUtility.UrlDecode(querystring["returnurl"]); returnurl = WebUtility.UrlDecode(querystring["returnurl"]);
} }
// reload the client application from the server if there is a forced reload or the user navigated to a site with a different alias // reload the client application from the server if there is a forced reload or the user navigated to a site with a different alias
if (querystring.ContainsKey("reload") || (!NavigationManager.ToBaseRelativePath(_absoluteUri).ToLower().StartsWith(SiteState.Alias.Path.ToLower()) && !string.IsNullOrEmpty(SiteState.Alias.Path))) if (querystring.ContainsKey("reload") || (!NavigationManager.ToBaseRelativePath(_absoluteUri).ToLower().StartsWith(SiteState.Alias.Path.ToLower()) && !string.IsNullOrEmpty(SiteState.Alias.Path)))
{ {
if (querystring.ContainsKey("reload") && querystring["reload"] == "post") if (querystring.ContainsKey("reload") && querystring["reload"] == "post")
{ {
// post back so that the cookies are set correctly - required on any change to the principal // post back so that the cookies are set correctly - required on any change to the principal
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
var fields = new { returnurl = "/" + NavigationManager.ToBaseRelativePath(_absoluteUri) }; var fields = new { returnurl = "/" + NavigationManager.ToBaseRelativePath(_absoluteUri) };
string url = Utilities.TenantUrl(SiteState.Alias, "/pages/external/"); string url = Utilities.TenantUrl(SiteState.Alias, "/pages/external/");
await interop.SubmitForm(url, fields); await interop.SubmitForm(url, fields);
return; return;
} }
else else
{ {
NavigationManager.NavigateTo(_absoluteUri.Replace("?reload", ""), true); NavigationManager.NavigateTo(_absoluteUri.Replace("?reload", ""), true);
return; return;
} }
} }
// the refresh parameter is used to refresh the client-side PageState // the refresh parameter is used to refresh the client-side PageState
if (querystring.ContainsKey("refresh")) if (querystring.ContainsKey("refresh"))
{ {
refresh = true; refresh = true;
} }
if (PageState != null) if (PageState != null)
{ {
editmode = PageState.EditMode; editmode = PageState.EditMode;
lastsyncdate = PageState.LastSyncDate; lastsyncdate = PageState.LastSyncDate;
} }
if (PageState?.Page.Path != route.PagePath) if (PageState?.Page.Path != route.PagePath)
{ {
editmode = false; // reset edit mode when navigating to different page editmode = false; // reset edit mode when navigating to different page
} }
// get user // get user
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId) if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
{ {
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
if (authState.User.Identity.IsAuthenticated) if (authState.User.Identity.IsAuthenticated)
{ {
user = await UserService.GetUserAsync(authState.User.Identity.Name, SiteState.Alias.SiteId); user = await UserService.GetUserAsync(authState.User.Identity.Name, SiteState.Alias.SiteId);
if (user != null) if (user != null)
{ {
user.IsAuthenticated = authState.User.Identity.IsAuthenticated; user.IsAuthenticated = authState.User.Identity.IsAuthenticated;
} }
} }
} }
else else
{ {
user = PageState.User; user = PageState.User;
} }
// process any sync events // process any sync events
var sync = await SyncService.GetSyncAsync(lastsyncdate); var sync = await SyncService.GetSyncAsync(lastsyncdate);
lastsyncdate = sync.SyncDate; lastsyncdate = sync.SyncDate;
if (sync.SyncEvents.Any()) if (sync.SyncEvents.Any())
{ {
// reload client application if server was restarted or site runtime/rendermode was modified // reload client application if server was restarted or site runtime/rendermode was modified
if (PageState != null && sync.SyncEvents.Exists(item => (item.Action == SyncEventActions.Reload))) if (PageState != null && sync.SyncEvents.Exists(item => (item.Action == SyncEventActions.Reload)))
{ {
NavigationManager.NavigateTo(_absoluteUri, true); NavigationManager.NavigateTo(_absoluteUri, true);
return; return;
} }
// when site information has changed the PageState needs to be refreshed // when site information has changed the PageState needs to be refreshed
if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId)) if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
{ {
refresh = true; refresh = true;
} }
// when user information has changed the PageState needs to be refreshed as the list of pages/modules may have changed // when user information has changed the PageState needs to be refreshed as the list of pages/modules may have changed
if (user != null && sync.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId)) if (user != null && sync.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId))
{ {
refresh = true; refresh = true;
} }
} }
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId) if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
{ {
site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId); site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId);
refresh = true; refresh = true;
} }
else else
{ {
site = PageState.Site; site = PageState.Site;
} }
if (site != null) if (site != null)
{ {
if (PageState == null || refresh || PageState.Page.Path != route.PagePath) if (PageState == null || refresh || PageState.Page.Path != route.PagePath)
{ {
page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase)); page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
} }
else else
{ {
page = PageState.Page; page = PageState.Page;
} }
if (page == null && route.PagePath == "") // naked path refers to site home page if (page == null && route.PagePath == "") // naked path refers to site home page
{ {
if (site.HomePageId != null) if (site.HomePageId != null)
{ {
page = site.Pages.FirstOrDefault(item => item.PageId == site.HomePageId); page = site.Pages.FirstOrDefault(item => item.PageId == site.HomePageId);
} }
if (page == null) if (page == null)
{ {
// fallback to use the first page in the collection // fallback to use the first page in the collection
page = site.Pages.FirstOrDefault(); page = site.Pages.FirstOrDefault();
} }
} }
if (page != null) if (page != null)
{ {
// check if user is authorized to view page // check if user is authorized to view page
if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.PermissionList)) if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.PermissionList))
{ {
// load additional metadata for current page // load additional metadata for current page
page = await ProcessPage(page, site, user); page = await ProcessPage(page, site, user, SiteState.Alias);
// load additional metadata for modules // load additional metadata for modules
(page, site.Modules) = ProcessModules(page, site.Modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType); (page, site.Modules) = ProcessModules(page, site.Modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType, SiteState.Alias);
// populate page state (which acts as a client-side cache for subsequent requests) // populate page state (which acts as a client-side cache for subsequent requests)
_pagestate = new PageState _pagestate = new PageState
{ {
Alias = SiteState.Alias, Alias = SiteState.Alias,
Site = site, Site = site,
@ -236,295 +236,311 @@
ReturnUrl = returnurl ReturnUrl = returnurl
}; };
OnStateChange?.Invoke(_pagestate); OnStateChange?.Invoke(_pagestate);
await ScrollToFragment(_pagestate.Uri); await ScrollToFragment(_pagestate.Uri);
} }
} }
else // page not found else // page not found
{ {
// look for url mapping // look for url mapping
var urlMapping = await UrlMappingService.GetUrlMappingAsync(site.SiteId, route.PagePath); var urlMapping = await UrlMappingService.GetUrlMappingAsync(site.SiteId, route.PagePath);
if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl)) if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl))
{ {
var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl; var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl;
NavigationManager.NavigateTo(url, false); NavigationManager.NavigateTo(url, false);
} }
else // not mapped else // not mapped
{ {
if (user == null) if (user == null)
{ {
// redirect to login page if user not logged in as they may need to be authenticated // redirect to login page if user not logged in as they may need to be authenticated
NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "login", "?returnurl=" + WebUtility.UrlEncode(route.PathAndQuery))); NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "login", "?returnurl=" + WebUtility.UrlEncode(route.PathAndQuery)));
} }
else else
{ {
if (route.PagePath != "404") if (route.PagePath != "404")
{ {
// redirect to 404 page // redirect to 404 page
NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "404", "")); NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "404", ""));
} }
else else
{ {
// redirect to home page as a fallback // redirect to home page as a fallback
NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "", "")); NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "", ""));
} }
} }
} }
} }
} }
else else
{ {
// site does not exist // site does not exist
} }
} }
private async void LocationChanged(object sender, LocationChangedEventArgs args) private async void LocationChanged(object sender, LocationChangedEventArgs args)
{ {
_absoluteUri = args.Location; _absoluteUri = args.Location;
await Refresh(); await Refresh();
} }
Task IHandleAfterRender.OnAfterRenderAsync() Task IHandleAfterRender.OnAfterRenderAsync()
{ {
if (!_navigationInterceptionEnabled) if (!_navigationInterceptionEnabled)
{ {
_navigationInterceptionEnabled = true; _navigationInterceptionEnabled = true;
return NavigationInterception.EnableNavigationInterceptionAsync(); return NavigationInterception.EnableNavigationInterceptionAsync();
} }
return Task.CompletedTask; return Task.CompletedTask;
} }
private Dictionary<string, string> ParseQueryString(string query) private Dictionary<string, string> ParseQueryString(string query)
{ {
Dictionary<string, string> querystring = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // case insensistive keys Dictionary<string, string> querystring = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // case insensistive keys
if (!string.IsNullOrEmpty(query)) if (!string.IsNullOrEmpty(query))
{ {
if (query.StartsWith("?")) if (query.StartsWith("?"))
{ {
query = query.Substring(1); // ignore "?" query = query.Substring(1); // ignore "?"
} }
foreach (string kvp in query.Split('&', StringSplitOptions.RemoveEmptyEntries)) foreach (string kvp in query.Split('&', StringSplitOptions.RemoveEmptyEntries))
{ {
if (kvp != "") if (kvp != "")
{ {
if (kvp.Contains("=")) if (kvp.Contains("="))
{ {
string[] pair = kvp.Split('='); string[] pair = kvp.Split('=');
if (!querystring.ContainsKey(pair[0])) if (!querystring.ContainsKey(pair[0]))
{ {
querystring.Add(pair[0], pair[1]); querystring.Add(pair[0], pair[1]);
} }
} }
else else
{ {
if (!querystring.ContainsKey(kvp)) if (!querystring.ContainsKey(kvp))
{ {
querystring.Add(kvp, "true"); // default parameter when no value is provided querystring.Add(kvp, "true"); // default parameter when no value is provided
} }
} }
} }
} }
} }
return querystring; return querystring;
} }
private async Task<Page> ProcessPage(Page page, Site site, User user) private async Task<Page> ProcessPage(Page page, Site site, User user, Alias alias)
{ {
try try
{ {
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);
} }
if (string.IsNullOrEmpty(page.ThemeType)) if (string.IsNullOrEmpty(page.ThemeType))
{ {
page.ThemeType = site.DefaultThemeType; page.ThemeType = site.DefaultThemeType;
} }
page.Panes = new List<string>(); page.Panes = new List<string>();
page.Resources = new List<Resource>(); page.Resources = new List<Resource>();
string panes = ""; // get theme resources
Type themetype = Type.GetType(page.ThemeType); var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType));
if (themetype == null) if (theme != null)
{ {
// fallback page.Resources = ManagePageResources(page.Resources, theme.Resources, ResourceLevel.Page, alias, "Themes", Utilities.GetTypeName(theme.ThemeName));
page.ThemeType = Constants.DefaultTheme; }
themetype = Type.GetType(Constants.DefaultTheme);
}
if (themetype != null)
{
var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
if (themeobject != null)
{
if (!string.IsNullOrEmpty(themeobject.Panes))
{
panes = themeobject.Panes;
}
page.Resources = ManagePageResources(page.Resources, themeobject.Resources, ResourceLevel.Page);
}
}
if (!string.IsNullOrEmpty(panes))
{
page.Panes = panes.Replace(";", ",").Split(',', StringSplitOptions.RemoveEmptyEntries).ToList();
if (!page.Panes.Contains(PaneNames.Default) && !page.Panes.Contains(PaneNames.Admin))
{
_error = "The Current Theme Does Not Contain A Default Or Admin Pane";
}
}
else
{
page.Panes.Add(PaneNames.Admin);
_error = "The Current Theme Does Not Contain Any Panes";
}
}
catch
{
// error loading theme or layout
}
return page; string panes = "";
} Type themetype = Type.GetType(page.ThemeType);
if (themetype == null)
{
// fallback
page.ThemeType = Constants.DefaultTheme;
themetype = Type.GetType(Constants.DefaultTheme);
}
if (themetype != null)
{
var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
if (themeobject != null)
{
if (!string.IsNullOrEmpty(themeobject.Panes))
{
panes = themeobject.Panes;
}
page.Resources = ManagePageResources(page.Resources, themeobject.Resources, ResourceLevel.Page, alias, "Themes", themetype.Namespace);
}
}
if (!string.IsNullOrEmpty(panes))
{
page.Panes = panes.Replace(";", ",").Split(',', StringSplitOptions.RemoveEmptyEntries).ToList();
if (!page.Panes.Contains(PaneNames.Default) && !page.Panes.Contains(PaneNames.Admin))
{
_error = "The Current Theme Does Not Contain A Default Or Admin Pane";
}
}
else
{
page.Panes.Add(PaneNames.Admin);
_error = "The Current Theme Does Not Contain Any Panes";
}
}
catch
{
// error loading theme or layout
}
private (Page Page, List<Module> Modules) ProcessModules(Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype) return page;
{ }
var paneindex = new Dictionary<string, int>();
foreach (Module module in modules)
{
// initialize module control properties
module.SecurityAccessLevel = SecurityAccessLevel.Host;
module.ControlTitle = "";
module.Actions = "";
module.UseAdminContainer = false;
module.PaneModuleIndex = -1;
module.PaneModuleCount = 0;
if ((module.PageId == page.PageId || module.ModuleId == moduleid)) private (Page Page, List<Module> Modules) ProcessModules(Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype, Alias alias)
{ {
var typename = Constants.ErrorModule; var paneindex = new Dictionary<string, int>();
if (module.ModuleDefinition != null && (module.ModuleDefinition.Runtimes == "" || module.ModuleDefinition.Runtimes.Contains(Runtime))) foreach (Module module in modules)
{ {
typename = module.ModuleDefinition.ControlTypeTemplate; // initialize module control properties
module.SecurityAccessLevel = SecurityAccessLevel.Host;
module.ControlTitle = "";
module.Actions = "";
module.UseAdminContainer = false;
module.PaneModuleIndex = -1;
module.PaneModuleCount = 0;
// handle default action if ((module.PageId == page.PageId || module.ModuleId == moduleid))
if (action == Constants.DefaultAction && !string.IsNullOrEmpty(module.ModuleDefinition.DefaultAction)) {
{ var typename = Constants.ErrorModule;
action = module.ModuleDefinition.DefaultAction; if (module.ModuleDefinition != null && (module.ModuleDefinition.Runtimes == "" || module.ModuleDefinition.Runtimes.Contains(Runtime)))
} {
typename = module.ModuleDefinition.ControlTypeTemplate;
// check if the module defines custom action routes // handle default action
if (module.ModuleDefinition.ControlTypeRoutes != "") if (action == Constants.DefaultAction && !string.IsNullOrEmpty(module.ModuleDefinition.DefaultAction))
{ {
foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(';', StringSplitOptions.RemoveEmptyEntries)) action = module.ModuleDefinition.DefaultAction;
{ }
if (route.StartsWith(action + "="))
{
typename = route.Replace(action + "=", "");
}
}
}
}
// ensure component exists and implements IModuleControl // check if the module defines custom action routes
module.ModuleType = ""; if (module.ModuleDefinition.ControlTypeRoutes != "")
if (Constants.DefaultModuleActions.Contains(action, StringComparer.OrdinalIgnoreCase)) {
{ foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(';', StringSplitOptions.RemoveEmptyEntries))
typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, action); {
} if (route.StartsWith(action + "="))
else {
{ typename = route.Replace(action + "=", "");
typename = typename.Replace(Constants.ActionToken, action); }
} }
Type moduletype = Type.GetType(typename, false, true); // case insensitive }
if (moduletype != null && moduletype.GetInterfaces().Contains(typeof(IModuleControl)))
{
module.ModuleType = Utilities.GetFullTypeName(moduletype.AssemblyQualifiedName); // get actual type name
}
// get additional metadata from IModuleControl interface // get module resources
if (moduletype != null && module.ModuleType != "") page.Resources = ManagePageResources(page.Resources, module.ModuleDefinition.Resources, ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName));
{ }
// retrieve module component resources
var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module);
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
{
// settings components are embedded within a framework settings module
moduletype = Type.GetType(module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action), false, true);
if (moduletype != null)
{
moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module);
}
}
// additional metadata needed for admin components // ensure component exists and implements IModuleControl
if (module.ModuleId == moduleid && action != "") module.ModuleType = "";
{ if (Constants.DefaultModuleActions.Contains(action, StringComparer.OrdinalIgnoreCase))
module.SecurityAccessLevel = moduleobject.SecurityAccessLevel; {
module.ControlTitle = moduleobject.Title; typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, action);
module.Actions = moduleobject.Actions; }
module.UseAdminContainer = moduleobject.UseAdminContainer; else
} {
} typename = typename.Replace(Constants.ActionToken, action);
}
Type moduletype = Type.GetType(typename, false, true); // case insensitive
if (moduletype != null && moduletype.GetInterfaces().Contains(typeof(IModuleControl)))
{
module.ModuleType = Utilities.GetFullTypeName(moduletype.AssemblyQualifiedName); // get actual type name
}
// validate that module's pane exists in current page // get additional metadata from IModuleControl interface
if (page.Panes.FindIndex(item => item.Equals(module.Pane, StringComparison.OrdinalIgnoreCase)) == -1) if (moduletype != null && module.ModuleType != "")
{ {
// fallback to default pane if it exists // retrieve module component resources
if (page.Panes.FindIndex(item => item.Equals(PaneNames.Default, StringComparison.OrdinalIgnoreCase)) != -1) var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
{ page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
module.Pane = PaneNames.Default; if (action.ToLower() == "settings" && module.ModuleDefinition != null)
} {
else // otherwise admin pane (legacy) // settings components are embedded within a framework settings module
{ moduletype = Type.GetType(module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action), false, true);
module.Pane = PaneNames.Admin; if (moduletype != null)
} {
} moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
}
}
// calculate module position within pane // additional metadata needed for admin components
if (paneindex.ContainsKey(module.Pane.ToLower())) if (module.ModuleId == moduleid && action != "")
{ {
paneindex[module.Pane.ToLower()] += 1; module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
} module.ControlTitle = moduleobject.Title;
else module.Actions = moduleobject.Actions;
{ module.UseAdminContainer = moduleobject.UseAdminContainer;
paneindex.Add(module.Pane.ToLower(), 0); }
} }
module.PaneModuleIndex = paneindex[module.Pane.ToLower()]; // validate that module's pane exists in current page
if (page.Panes.FindIndex(item => item.Equals(module.Pane, StringComparison.OrdinalIgnoreCase)) == -1)
{
// fallback to default pane if it exists
if (page.Panes.FindIndex(item => item.Equals(PaneNames.Default, StringComparison.OrdinalIgnoreCase)) != -1)
{
module.Pane = PaneNames.Default;
}
else // otherwise admin pane (legacy)
{
module.Pane = PaneNames.Admin;
}
}
// container fallback // calculate module position within pane
if (string.IsNullOrEmpty(module.ContainerType)) if (paneindex.ContainsKey(module.Pane.ToLower()))
{ {
module.ContainerType = defaultcontainertype; paneindex[module.Pane.ToLower()] += 1;
} }
} else
} {
paneindex.Add(module.Pane.ToLower(), 0);
}
foreach (Module module in modules.Where(item => item.PageId == page.PageId)) module.PaneModuleIndex = paneindex[module.Pane.ToLower()];
{
if (paneindex.ContainsKey(module.Pane.ToLower()))
{
module.PaneModuleCount = paneindex[module.Pane.ToLower()] + 1;
}
}
return (page, modules); // container fallback
} if (string.IsNullOrEmpty(module.ContainerType))
{
module.ContainerType = defaultcontainertype;
}
}
}
private List<Resource> ManagePageResources(List<Resource> pageresources, List<Resource> resources, ResourceLevel level) foreach (Module module in modules.Where(item => item.PageId == page.PageId))
{ {
if (resources != null) if (paneindex.ContainsKey(module.Pane.ToLower()))
{ {
foreach (var resource in resources) module.PaneModuleCount = paneindex[module.Pane.ToLower()] + 1;
{ }
// ensure resource does not exist already }
if (pageresources.Find(item => item.Url == resource.Url) == null)
{ return (page, modules);
resource.Level = level; }
private List<Resource> ManagePageResources(List<Resource> pageresources, List<Resource> resources, ResourceLevel level, Alias alias, string type, string name)
{
if (resources != null)
{
foreach (var resource in resources)
{
if (!resource.Url.Contains("://") && resource.Url.StartsWith("~/"))
{
// create local path
resource.Url = resource.Url.Replace("~", alias.BaseUrl + "/" + type + "/" + name);
}
// ensure resource does not exist already
if (pageresources.Find(item => item.Url == resource.Url) == null)
{
resource.Level = level;
pageresources.Add(resource); pageresources.Add(resource);
} }
} }

View File

@ -6,9 +6,9 @@
@DynamicComponent @DynamicComponent
@code { @code {
[CascadingParameter] PageState PageState { get; set; } [CascadingParameter] PageState PageState { get; set; }
RenderFragment DynamicComponent { get; set; } RenderFragment DynamicComponent { get; set; }
protected override void OnParametersSet() protected override void OnParametersSet()
{ {

View File

@ -21,6 +21,7 @@ namespace Oqtane.Controllers
{ {
private readonly ISiteRepository _sites; private readonly ISiteRepository _sites;
private readonly IPageRepository _pages; private readonly IPageRepository _pages;
private readonly IThemeRepository _themes;
private readonly IModuleRepository _modules; private readonly IModuleRepository _modules;
private readonly IPageModuleRepository _pageModules; private readonly IPageModuleRepository _pageModules;
private readonly IModuleDefinitionRepository _moduleDefinitions; private readonly IModuleDefinitionRepository _moduleDefinitions;
@ -32,10 +33,11 @@ namespace Oqtane.Controllers
private readonly IMemoryCache _cache; private readonly IMemoryCache _cache;
private readonly Alias _alias; private readonly Alias _alias;
public SiteController(ISiteRepository sites, IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger, IMemoryCache cache) public SiteController(ISiteRepository sites, IPageRepository pages, IThemeRepository themes, IModuleRepository modules, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger, IMemoryCache cache)
{ {
_sites = sites; _sites = sites;
_pages = pages; _pages = pages;
_themes = themes;
_modules = modules; _modules = modules;
_pageModules = pageModules; _pageModules = pageModules;
_moduleDefinitions = moduleDefinitions; _moduleDefinitions = moduleDefinitions;
@ -144,6 +146,9 @@ namespace Oqtane.Controllers
var defaultCulture = CultureInfo.GetCultureInfo(Constants.DefaultCulture); var defaultCulture = CultureInfo.GetCultureInfo(Constants.DefaultCulture);
site.Languages.Add(new Language { Code = defaultCulture.Name, Name = defaultCulture.DisplayName, Version = Constants.Version, IsDefault = !site.Languages.Any(l => l.IsDefault) }); site.Languages.Add(new Language { Code = defaultCulture.Name, Name = defaultCulture.DisplayName, Version = Constants.Version, IsDefault = !site.Languages.Any(l => l.IsDefault) });
// themes
site.Themes = _themes.FilterThemes(_themes.GetThemes().ToList());
return site; return site;
} }
else else

View File

@ -6,6 +6,7 @@ namespace Oqtane.Repository
public interface IThemeRepository public interface IThemeRepository
{ {
IEnumerable<Theme> GetThemes(); IEnumerable<Theme> GetThemes();
List<Theme> FilterThemes(List<Theme> themes);
void DeleteTheme(string ThemeName); void DeleteTheme(string ThemeName);
} }
} }

View File

@ -79,6 +79,7 @@ namespace Oqtane.Repository
ModuleDefinition.SettingsType = moduleDefinition.SettingsType; ModuleDefinition.SettingsType = moduleDefinition.SettingsType;
ModuleDefinition.ControlTypeTemplate = moduleDefinition.ControlTypeTemplate; ModuleDefinition.ControlTypeTemplate = moduleDefinition.ControlTypeTemplate;
ModuleDefinition.IsPortable = moduleDefinition.IsPortable; ModuleDefinition.IsPortable = moduleDefinition.IsPortable;
ModuleDefinition.Resources = moduleDefinition.Resources;
} }
return ModuleDefinition; return ModuleDefinition;

View File

@ -144,6 +144,24 @@ namespace Oqtane.Repository
return themes; return themes;
} }
public List<Theme> FilterThemes(List<Theme> themes)
{
var Themes = new List<Theme>();
foreach (Theme theme in themes)
{
var Theme = new Theme();
Theme.ThemeName = theme.ThemeName;
Theme.Name = theme.Name;
Theme.Resources = theme.Resources;
Theme.Themes = theme.Themes;
Theme.Containers = theme.Containers;
Themes.Add(Theme);
}
return Themes;
}
public void DeleteTheme(string ThemeName) public void DeleteTheme(string ThemeName)
{ {
_cache.Remove("themes"); _cache.Remove("themes");

View File

@ -34,6 +34,7 @@ namespace Oqtane.Models
PackageName = ""; PackageName = "";
Runtimes = ""; Runtimes = "";
Template = ""; Template = "";
Resources = null;
} }
/// <summary> /// <summary>
@ -106,6 +107,9 @@ namespace Oqtane.Models
[NotMapped] [NotMapped]
public string PackageName { get; set; } // added in 2.1.0 public string PackageName { get; set; } // added in 2.1.0
[NotMapped]
public List<Resource> Resources { get; set; } // added in 4.0.0
// internal properties // internal properties
[NotMapped] [NotMapped]
public int SiteId { get; set; } public int SiteId { get; set; }

View File

@ -23,7 +23,7 @@ namespace Oqtane.Models
get => _url; get => _url;
set set
{ {
_url = (value.Contains("://")) ? value : (!value.StartsWith("/") ? "/" : "") + value; _url = (value.Contains("://")) ? value : (!value.StartsWith("/") && !value.StartsWith("~") ? "/" : "") + value;
} }
} }

View File

@ -105,6 +105,9 @@ namespace Oqtane.Models
[NotMapped] [NotMapped]
public List<Language> Languages { get; set; } public List<Language> Languages { get; set; }
[NotMapped]
public List<Theme> Themes { get; set; }
#region IDeletable Properties #region IDeletable Properties
public string DeletedBy { get; set; } public string DeletedBy { get; set; }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace Oqtane.Models namespace Oqtane.Models
{ {
@ -21,6 +22,7 @@ namespace Oqtane.Models
ThemeSettingsType = ""; ThemeSettingsType = "";
ContainerSettingsType = ""; ContainerSettingsType = "";
PackageName = ""; PackageName = "";
Resources = null;
} }
/// <summary> /// <summary>
@ -67,6 +69,8 @@ namespace Oqtane.Models
public string ThemeSettingsType { get; set; } // added in 2.0.2 public string ThemeSettingsType { get; set; } // added in 2.0.2
public string ContainerSettingsType { get; set; } // added in 2.0.2 public string ContainerSettingsType { get; set; } // added in 2.0.2
public string PackageName { get; set; } // added in 2.1.0 public string PackageName { get; set; } // added in 2.1.0
public List<Resource> Resources { get; set; } // added in 4.0.0
// internal properties // internal properties
public string AssemblyName { get; set; } public string AssemblyName { get; set; }