diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor
index 68df3167..6767f011 100644
--- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor
+++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor
@@ -44,12 +44,20 @@
diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor
index a68bd1c1..8465faa8 100644
--- a/Oqtane.Client/UI/SiteRouter.razor
+++ b/Oqtane.Client/UI/SiteRouter.razor
@@ -22,201 +22,201 @@
@DynamicComponent
@code {
- private string _absoluteUri;
- private bool _navigationInterceptionEnabled;
- private PageState _pagestate;
- private string _error = "";
+ private string _absoluteUri;
+ private bool _navigationInterceptionEnabled;
+ private PageState _pagestate;
+ private string _error = "";
- [Parameter]
- public string Runtime { get; set; }
+ [Parameter]
+ public string Runtime { get; set; }
- [Parameter]
- public string RenderMode { get; set; }
+ [Parameter]
+ public string RenderMode { get; set; }
- [Parameter]
- public int VisitorId { get; set; }
+ [Parameter]
+ public int VisitorId { get; set; }
- [CascadingParameter]
- PageState PageState { get; set; }
+ [CascadingParameter]
+ PageState PageState { get; set; }
- [Parameter]
- public Action OnStateChange { get; set; }
+ [Parameter]
+ public Action OnStateChange { get; set; }
- private RenderFragment DynamicComponent { get; set; }
+ private RenderFragment DynamicComponent { get; set; }
- protected override void OnInitialized()
- {
- _absoluteUri = NavigationManager.Uri;
- NavigationManager.LocationChanged += LocationChanged;
+ protected override void OnInitialized()
+ {
+ _absoluteUri = NavigationManager.Uri;
+ NavigationManager.LocationChanged += LocationChanged;
- DynamicComponent = builder =>
- {
- if (PageState != null)
- {
- builder.OpenComponent(0, Type.GetType(Constants.PageComponent));
- builder.CloseComponent();
- }
- };
- }
+ DynamicComponent = builder =>
+ {
+ if (PageState != null)
+ {
+ builder.OpenComponent(0, Type.GetType(Constants.PageComponent));
+ builder.CloseComponent();
+ }
+ };
+ }
- public void Dispose()
- {
- NavigationManager.LocationChanged -= LocationChanged;
- }
+ public void Dispose()
+ {
+ NavigationManager.LocationChanged -= LocationChanged;
+ }
- protected override async Task OnParametersSetAsync()
- {
- if (PageState == null)
- {
- await Refresh();
- }
- }
+ protected override async Task OnParametersSetAsync()
+ {
+ if (PageState == null)
+ {
+ await Refresh();
+ }
+ }
- [SuppressMessage("ReSharper", "StringIndexOfIsCultureSpecific.1")]
- private async Task Refresh()
- {
- Site site;
- Page page;
- User user = null;
- var editmode = false;
- var refresh = false;
- var lastsyncdate = DateTime.UtcNow.AddHours(-1);
- var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
- _error = "";
+ [SuppressMessage("ReSharper", "StringIndexOfIsCultureSpecific.1")]
+ private async Task Refresh()
+ {
+ Site site;
+ Page page;
+ User user = null;
+ var editmode = false;
+ var refresh = false;
+ var lastsyncdate = DateTime.UtcNow.AddHours(-1);
+ var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
+ _error = "";
- Route route = new Route(_absoluteUri, SiteState.Alias.Path);
- int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1;
- var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction;
- var querystring = ParseQueryString(route.Query);
- var returnurl = "";
- if (querystring.ContainsKey("returnurl"))
- {
- returnurl = WebUtility.UrlDecode(querystring["returnurl"]);
- }
+ Route route = new Route(_absoluteUri, SiteState.Alias.Path);
+ int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1;
+ var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction;
+ var querystring = ParseQueryString(route.Query);
+ var returnurl = "";
+ if (querystring.ContainsKey("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
- 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")
- {
- // post back so that the cookies are set correctly - required on any change to the principal
- var interop = new Interop(JSRuntime);
- var fields = new { returnurl = "/" + NavigationManager.ToBaseRelativePath(_absoluteUri) };
- string url = Utilities.TenantUrl(SiteState.Alias, "/pages/external/");
- await interop.SubmitForm(url, fields);
- return;
- }
- else
- {
- NavigationManager.NavigateTo(_absoluteUri.Replace("?reload", ""), true);
- return;
- }
- }
+ // 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") && querystring["reload"] == "post")
+ {
+ // post back so that the cookies are set correctly - required on any change to the principal
+ var interop = new Interop(JSRuntime);
+ var fields = new { returnurl = "/" + NavigationManager.ToBaseRelativePath(_absoluteUri) };
+ string url = Utilities.TenantUrl(SiteState.Alias, "/pages/external/");
+ await interop.SubmitForm(url, fields);
+ return;
+ }
+ else
+ {
+ NavigationManager.NavigateTo(_absoluteUri.Replace("?reload", ""), true);
+ return;
+ }
+ }
- // the refresh parameter is used to refresh the client-side PageState
- if (querystring.ContainsKey("refresh"))
- {
- refresh = true;
- }
+ // the refresh parameter is used to refresh the client-side PageState
+ if (querystring.ContainsKey("refresh"))
+ {
+ refresh = true;
+ }
- if (PageState != null)
- {
- editmode = PageState.EditMode;
- lastsyncdate = PageState.LastSyncDate;
- }
- if (PageState?.Page.Path != route.PagePath)
- {
- editmode = false; // reset edit mode when navigating to different page
- }
+ if (PageState != null)
+ {
+ editmode = PageState.EditMode;
+ lastsyncdate = PageState.LastSyncDate;
+ }
+ if (PageState?.Page.Path != route.PagePath)
+ {
+ editmode = false; // reset edit mode when navigating to different page
+ }
- // get user
- if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
- {
- var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
- if (authState.User.Identity.IsAuthenticated)
- {
- user = await UserService.GetUserAsync(authState.User.Identity.Name, SiteState.Alias.SiteId);
- if (user != null)
- {
- user.IsAuthenticated = authState.User.Identity.IsAuthenticated;
- }
- }
- }
- else
- {
- user = PageState.User;
- }
+ // get user
+ if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
+ {
+ var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
+ if (authState.User.Identity.IsAuthenticated)
+ {
+ user = await UserService.GetUserAsync(authState.User.Identity.Name, SiteState.Alias.SiteId);
+ if (user != null)
+ {
+ user.IsAuthenticated = authState.User.Identity.IsAuthenticated;
+ }
+ }
+ }
+ else
+ {
+ user = PageState.User;
+ }
- // process any sync events
- var sync = await SyncService.GetSyncAsync(lastsyncdate);
- lastsyncdate = sync.SyncDate;
- if (sync.SyncEvents.Any())
- {
- // reload client application if server was restarted or site runtime/rendermode was modified
- if (PageState != null && sync.SyncEvents.Exists(item => (item.Action == SyncEventActions.Reload)))
- {
- NavigationManager.NavigateTo(_absoluteUri, true);
- return;
- }
- // 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))
- {
- refresh = true;
- }
- // 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))
- {
- refresh = true;
- }
- }
+ // process any sync events
+ var sync = await SyncService.GetSyncAsync(lastsyncdate);
+ lastsyncdate = sync.SyncDate;
+ if (sync.SyncEvents.Any())
+ {
+ // reload client application if server was restarted or site runtime/rendermode was modified
+ if (PageState != null && sync.SyncEvents.Exists(item => (item.Action == SyncEventActions.Reload)))
+ {
+ NavigationManager.NavigateTo(_absoluteUri, true);
+ return;
+ }
+ // 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))
+ {
+ refresh = true;
+ }
+ // 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))
+ {
+ refresh = true;
+ }
+ }
- if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
- {
- site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId);
- refresh = true;
- }
- else
- {
- site = PageState.Site;
- }
+ if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
+ {
+ site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId);
+ refresh = true;
+ }
+ else
+ {
+ site = PageState.Site;
+ }
- if (site != null)
- {
- if (PageState == null || refresh || PageState.Page.Path != route.PagePath)
- {
- page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
- }
- else
- {
- page = PageState.Page;
- }
+ if (site != null)
+ {
+ if (PageState == null || refresh || PageState.Page.Path != route.PagePath)
+ {
+ page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
+ }
+ else
+ {
+ page = PageState.Page;
+ }
- if (page == null && route.PagePath == "") // naked path refers to site home page
- {
- if (site.HomePageId != null)
- {
- page = site.Pages.FirstOrDefault(item => item.PageId == site.HomePageId);
- }
- if (page == null)
- {
- // fallback to use the first page in the collection
- page = site.Pages.FirstOrDefault();
- }
- }
+ if (page == null && route.PagePath == "") // naked path refers to site home page
+ {
+ if (site.HomePageId != null)
+ {
+ page = site.Pages.FirstOrDefault(item => item.PageId == site.HomePageId);
+ }
+ if (page == null)
+ {
+ // fallback to use the first page in the collection
+ page = site.Pages.FirstOrDefault();
+ }
+ }
- if (page != null)
- {
- // check if user is authorized to view page
- if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.PermissionList))
- {
- // load additional metadata for current page
- page = await ProcessPage(page, site, user);
+ if (page != null)
+ {
+ // check if user is authorized to view page
+ if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.PermissionList))
+ {
+ // load additional metadata for current page
+ page = await ProcessPage(page, site, user, SiteState.Alias);
- // load additional metadata for modules
- (page, site.Modules) = ProcessModules(page, site.Modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType);
+ // load additional metadata for modules
+ (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)
- _pagestate = new PageState
+ // populate page state (which acts as a client-side cache for subsequent requests)
+ _pagestate = new PageState
{
Alias = SiteState.Alias,
Site = site,
@@ -236,295 +236,311 @@
ReturnUrl = returnurl
};
- OnStateChange?.Invoke(_pagestate);
- await ScrollToFragment(_pagestate.Uri);
- }
- }
- else // page not found
- {
- // look for url mapping
- var urlMapping = await UrlMappingService.GetUrlMappingAsync(site.SiteId, route.PagePath);
- if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl))
- {
- var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl;
- NavigationManager.NavigateTo(url, false);
- }
- else // not mapped
- {
- if (user == null)
- {
- // 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)));
- }
- else
- {
- if (route.PagePath != "404")
- {
- // redirect to 404 page
- NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "404", ""));
- }
- else
- {
- // redirect to home page as a fallback
- NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "", ""));
- }
- }
- }
- }
- }
- else
- {
- // site does not exist
- }
- }
+ OnStateChange?.Invoke(_pagestate);
+ await ScrollToFragment(_pagestate.Uri);
+ }
+ }
+ else // page not found
+ {
+ // look for url mapping
+ var urlMapping = await UrlMappingService.GetUrlMappingAsync(site.SiteId, route.PagePath);
+ if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl))
+ {
+ var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl;
+ NavigationManager.NavigateTo(url, false);
+ }
+ else // not mapped
+ {
+ if (user == null)
+ {
+ // 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)));
+ }
+ else
+ {
+ if (route.PagePath != "404")
+ {
+ // redirect to 404 page
+ NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "404", ""));
+ }
+ else
+ {
+ // redirect to home page as a fallback
+ NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "", ""));
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // site does not exist
+ }
+ }
- private async void LocationChanged(object sender, LocationChangedEventArgs args)
- {
- _absoluteUri = args.Location;
- await Refresh();
- }
+ private async void LocationChanged(object sender, LocationChangedEventArgs args)
+ {
+ _absoluteUri = args.Location;
+ await Refresh();
+ }
- Task IHandleAfterRender.OnAfterRenderAsync()
- {
- if (!_navigationInterceptionEnabled)
- {
- _navigationInterceptionEnabled = true;
- return NavigationInterception.EnableNavigationInterceptionAsync();
- }
- return Task.CompletedTask;
- }
+ Task IHandleAfterRender.OnAfterRenderAsync()
+ {
+ if (!_navigationInterceptionEnabled)
+ {
+ _navigationInterceptionEnabled = true;
+ return NavigationInterception.EnableNavigationInterceptionAsync();
+ }
+ return Task.CompletedTask;
+ }
- private Dictionary ParseQueryString(string query)
- {
- Dictionary querystring = new Dictionary(StringComparer.OrdinalIgnoreCase); // case insensistive keys
- if (!string.IsNullOrEmpty(query))
- {
- if (query.StartsWith("?"))
- {
- query = query.Substring(1); // ignore "?"
- }
- foreach (string kvp in query.Split('&', StringSplitOptions.RemoveEmptyEntries))
- {
- if (kvp != "")
- {
- if (kvp.Contains("="))
- {
- string[] pair = kvp.Split('=');
- if (!querystring.ContainsKey(pair[0]))
- {
- querystring.Add(pair[0], pair[1]);
- }
- }
- else
- {
- if (!querystring.ContainsKey(kvp))
- {
- querystring.Add(kvp, "true"); // default parameter when no value is provided
- }
- }
- }
- }
- }
- return querystring;
- }
+ private Dictionary ParseQueryString(string query)
+ {
+ Dictionary querystring = new Dictionary(StringComparer.OrdinalIgnoreCase); // case insensistive keys
+ if (!string.IsNullOrEmpty(query))
+ {
+ if (query.StartsWith("?"))
+ {
+ query = query.Substring(1); // ignore "?"
+ }
+ foreach (string kvp in query.Split('&', StringSplitOptions.RemoveEmptyEntries))
+ {
+ if (kvp != "")
+ {
+ if (kvp.Contains("="))
+ {
+ string[] pair = kvp.Split('=');
+ if (!querystring.ContainsKey(pair[0]))
+ {
+ querystring.Add(pair[0], pair[1]);
+ }
+ }
+ else
+ {
+ if (!querystring.ContainsKey(kvp))
+ {
+ querystring.Add(kvp, "true"); // default parameter when no value is provided
+ }
+ }
+ }
+ }
+ }
+ return querystring;
+ }
- private async Task ProcessPage(Page page, Site site, User user)
- {
- try
- {
- if (page.IsPersonalizable && user != null)
- {
- // load the personalized page
- page = await PageService.GetPageAsync(page.PageId, user.UserId);
- }
+ private async Task ProcessPage(Page page, Site site, User user, Alias alias)
+ {
+ try
+ {
+ if (page.IsPersonalizable && user != null)
+ {
+ // load the personalized page
+ page = await PageService.GetPageAsync(page.PageId, user.UserId);
+ }
- if (string.IsNullOrEmpty(page.ThemeType))
- {
- page.ThemeType = site.DefaultThemeType;
- }
+ if (string.IsNullOrEmpty(page.ThemeType))
+ {
+ page.ThemeType = site.DefaultThemeType;
+ }
- page.Panes = new List();
- page.Resources = new List();
+ page.Panes = new List();
+ page.Resources = new List();
- 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);
- }
- }
- 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
- }
+ // get theme resources
+ var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType));
+ if (theme != null)
+ {
+ page.Resources = ManagePageResources(page.Resources, theme.Resources, ResourceLevel.Page, alias, "Themes", Utilities.GetTypeName(theme.ThemeName));
+ }
- 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 Modules) ProcessModules(Page page, List modules, int moduleid, string action, string defaultcontainertype)
- {
- var paneindex = new Dictionary();
- 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;
+ return page;
+ }
- if ((module.PageId == page.PageId || module.ModuleId == moduleid))
- {
- var typename = Constants.ErrorModule;
- if (module.ModuleDefinition != null && (module.ModuleDefinition.Runtimes == "" || module.ModuleDefinition.Runtimes.Contains(Runtime)))
- {
- typename = module.ModuleDefinition.ControlTypeTemplate;
+ private (Page Page, List Modules) ProcessModules(Page page, List modules, int moduleid, string action, string defaultcontainertype, Alias alias)
+ {
+ var paneindex = new Dictionary();
+ 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;
- // handle default action
- if (action == Constants.DefaultAction && !string.IsNullOrEmpty(module.ModuleDefinition.DefaultAction))
- {
- action = module.ModuleDefinition.DefaultAction;
- }
+ if ((module.PageId == page.PageId || module.ModuleId == moduleid))
+ {
+ var typename = Constants.ErrorModule;
+ if (module.ModuleDefinition != null && (module.ModuleDefinition.Runtimes == "" || module.ModuleDefinition.Runtimes.Contains(Runtime)))
+ {
+ typename = module.ModuleDefinition.ControlTypeTemplate;
- // check if the module defines custom action routes
- if (module.ModuleDefinition.ControlTypeRoutes != "")
- {
- foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(';', StringSplitOptions.RemoveEmptyEntries))
- {
- if (route.StartsWith(action + "="))
- {
- typename = route.Replace(action + "=", "");
- }
- }
- }
- }
+ // handle default action
+ if (action == Constants.DefaultAction && !string.IsNullOrEmpty(module.ModuleDefinition.DefaultAction))
+ {
+ action = module.ModuleDefinition.DefaultAction;
+ }
- // ensure component exists and implements IModuleControl
- module.ModuleType = "";
- if (Constants.DefaultModuleActions.Contains(action, StringComparer.OrdinalIgnoreCase))
- {
- typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, action);
- }
- 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
- }
+ // check if the module defines custom action routes
+ if (module.ModuleDefinition.ControlTypeRoutes != "")
+ {
+ foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(';', StringSplitOptions.RemoveEmptyEntries))
+ {
+ if (route.StartsWith(action + "="))
+ {
+ typename = route.Replace(action + "=", "");
+ }
+ }
+ }
- // get additional metadata from IModuleControl interface
- if (moduletype != null && module.ModuleType != "")
- {
- // 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);
- }
- }
+ // get module resources
+ page.Resources = ManagePageResources(page.Resources, module.ModuleDefinition.Resources, ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName));
+ }
- // additional metadata needed for admin components
- if (module.ModuleId == moduleid && action != "")
- {
- module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
- module.ControlTitle = moduleobject.Title;
- module.Actions = moduleobject.Actions;
- module.UseAdminContainer = moduleobject.UseAdminContainer;
- }
- }
+ // ensure component exists and implements IModuleControl
+ module.ModuleType = "";
+ if (Constants.DefaultModuleActions.Contains(action, StringComparer.OrdinalIgnoreCase))
+ {
+ typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, action);
+ }
+ 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
- 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;
- }
- }
+ // get additional metadata from IModuleControl interface
+ if (moduletype != null && module.ModuleType != "")
+ {
+ // retrieve module component resources
+ var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
+ page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
+ 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, alias, "Modules", moduletype.Namespace);
+ }
+ }
- // calculate module position within pane
- if (paneindex.ContainsKey(module.Pane.ToLower()))
- {
- paneindex[module.Pane.ToLower()] += 1;
- }
- else
- {
- paneindex.Add(module.Pane.ToLower(), 0);
- }
+ // additional metadata needed for admin components
+ if (module.ModuleId == moduleid && action != "")
+ {
+ module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
+ module.ControlTitle = moduleobject.Title;
+ module.Actions = moduleobject.Actions;
+ module.UseAdminContainer = moduleobject.UseAdminContainer;
+ }
+ }
- 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
- if (string.IsNullOrEmpty(module.ContainerType))
- {
- module.ContainerType = defaultcontainertype;
- }
- }
- }
+ // calculate module position within pane
+ if (paneindex.ContainsKey(module.Pane.ToLower()))
+ {
+ paneindex[module.Pane.ToLower()] += 1;
+ }
+ else
+ {
+ paneindex.Add(module.Pane.ToLower(), 0);
+ }
- foreach (Module module in modules.Where(item => item.PageId == page.PageId))
- {
- if (paneindex.ContainsKey(module.Pane.ToLower()))
- {
- module.PaneModuleCount = paneindex[module.Pane.ToLower()] + 1;
- }
- }
+ module.PaneModuleIndex = paneindex[module.Pane.ToLower()];
- return (page, modules);
- }
+ // container fallback
+ if (string.IsNullOrEmpty(module.ContainerType))
+ {
+ module.ContainerType = defaultcontainertype;
+ }
+ }
+ }
- private List ManagePageResources(List pageresources, List resources, ResourceLevel level)
- {
- if (resources != null)
- {
- foreach (var resource in resources)
- {
- // ensure resource does not exist already
- if (pageresources.Find(item => item.Url == resource.Url) == null)
- {
- resource.Level = level;
+ foreach (Module module in modules.Where(item => item.PageId == page.PageId))
+ {
+ if (paneindex.ContainsKey(module.Pane.ToLower()))
+ {
+ module.PaneModuleCount = paneindex[module.Pane.ToLower()] + 1;
+ }
+ }
+
+ return (page, modules);
+ }
+
+ private List ManagePageResources(List pageresources, List 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);
}
}
diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor
index 64d4f82b..49017206 100644
--- a/Oqtane.Client/UI/ThemeBuilder.razor
+++ b/Oqtane.Client/UI/ThemeBuilder.razor
@@ -6,9 +6,9 @@
@DynamicComponent
@code {
- [CascadingParameter] PageState PageState { get; set; }
+ [CascadingParameter] PageState PageState { get; set; }
- RenderFragment DynamicComponent { get; set; }
+ RenderFragment DynamicComponent { get; set; }
protected override void OnParametersSet()
{
diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs
index 895744a1..95de3a92 100644
--- a/Oqtane.Server/Controllers/SiteController.cs
+++ b/Oqtane.Server/Controllers/SiteController.cs
@@ -21,6 +21,7 @@ namespace Oqtane.Controllers
{
private readonly ISiteRepository _sites;
private readonly IPageRepository _pages;
+ private readonly IThemeRepository _themes;
private readonly IModuleRepository _modules;
private readonly IPageModuleRepository _pageModules;
private readonly IModuleDefinitionRepository _moduleDefinitions;
@@ -32,10 +33,11 @@ namespace Oqtane.Controllers
private readonly IMemoryCache _cache;
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;
_pages = pages;
+ _themes = themes;
_modules = modules;
_pageModules = pageModules;
_moduleDefinitions = moduleDefinitions;
@@ -144,6 +146,9 @@ namespace Oqtane.Controllers
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) });
+ // themes
+ site.Themes = _themes.FilterThemes(_themes.GetThemes().ToList());
+
return site;
}
else
diff --git a/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs b/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs
index 61dfc677..a7a81eee 100644
--- a/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs
+++ b/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs
@@ -6,6 +6,7 @@ namespace Oqtane.Repository
public interface IThemeRepository
{
IEnumerable GetThemes();
+ List FilterThemes(List themes);
void DeleteTheme(string ThemeName);
}
}
diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs
index 5f2efd9e..53b9c663 100644
--- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs
+++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs
@@ -79,6 +79,7 @@ namespace Oqtane.Repository
ModuleDefinition.SettingsType = moduleDefinition.SettingsType;
ModuleDefinition.ControlTypeTemplate = moduleDefinition.ControlTypeTemplate;
ModuleDefinition.IsPortable = moduleDefinition.IsPortable;
+ ModuleDefinition.Resources = moduleDefinition.Resources;
}
return ModuleDefinition;
diff --git a/Oqtane.Server/Repository/ThemeRepository.cs b/Oqtane.Server/Repository/ThemeRepository.cs
index 3794f4ab..4ca2c40e 100644
--- a/Oqtane.Server/Repository/ThemeRepository.cs
+++ b/Oqtane.Server/Repository/ThemeRepository.cs
@@ -144,6 +144,24 @@ namespace Oqtane.Repository
return themes;
}
+ public List FilterThemes(List themes)
+ {
+ var Themes = new List();
+
+ 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)
{
_cache.Remove("themes");
diff --git a/Oqtane.Shared/Models/ModuleDefinition.cs b/Oqtane.Shared/Models/ModuleDefinition.cs
index 7912165f..f287428e 100644
--- a/Oqtane.Shared/Models/ModuleDefinition.cs
+++ b/Oqtane.Shared/Models/ModuleDefinition.cs
@@ -34,6 +34,7 @@ namespace Oqtane.Models
PackageName = "";
Runtimes = "";
Template = "";
+ Resources = null;
}
///
@@ -106,6 +107,9 @@ namespace Oqtane.Models
[NotMapped]
public string PackageName { get; set; } // added in 2.1.0
+ [NotMapped]
+ public List Resources { get; set; } // added in 4.0.0
+
// internal properties
[NotMapped]
public int SiteId { get; set; }
diff --git a/Oqtane.Shared/Models/Resource.cs b/Oqtane.Shared/Models/Resource.cs
index 7afe1474..254af250 100644
--- a/Oqtane.Shared/Models/Resource.cs
+++ b/Oqtane.Shared/Models/Resource.cs
@@ -23,7 +23,7 @@ namespace Oqtane.Models
get => _url;
set
{
- _url = (value.Contains("://")) ? value : (!value.StartsWith("/") ? "/" : "") + value;
+ _url = (value.Contains("://")) ? value : (!value.StartsWith("/") && !value.StartsWith("~") ? "/" : "") + value;
}
}
diff --git a/Oqtane.Shared/Models/Site.cs b/Oqtane.Shared/Models/Site.cs
index 383b5668..02712c50 100644
--- a/Oqtane.Shared/Models/Site.cs
+++ b/Oqtane.Shared/Models/Site.cs
@@ -105,6 +105,9 @@ namespace Oqtane.Models
[NotMapped]
public List Languages { get; set; }
+ [NotMapped]
+ public List Themes { get; set; }
+
#region IDeletable Properties
public string DeletedBy { get; set; }
diff --git a/Oqtane.Shared/Models/Theme.cs b/Oqtane.Shared/Models/Theme.cs
index 0f0be718..1ff07b34 100644
--- a/Oqtane.Shared/Models/Theme.cs
+++ b/Oqtane.Shared/Models/Theme.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations.Schema;
namespace Oqtane.Models
{
@@ -21,6 +22,7 @@ namespace Oqtane.Models
ThemeSettingsType = "";
ContainerSettingsType = "";
PackageName = "";
+ Resources = null;
}
///
@@ -67,6 +69,8 @@ namespace Oqtane.Models
public string ThemeSettingsType { 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 List Resources { get; set; } // added in 4.0.0
+
// internal properties
public string AssemblyName { get; set; }