@namespace Oqtane.Shared @inject AuthenticationStateProvider AuthenticationStateProvider @inject SiteState SiteState @inject NavigationManager NavigationManager @inject INavigationInterception NavigationInterception @inject IAliasService AliasService @inject ITenantService TenantService @inject ISiteService SiteService @inject IPageService PageService @inject IUserService UserService @inject IModuleService ModuleService @inject IModuleDefinitionService ModuleDefinitionService @inject IThemeService ThemeService @implements IHandleAfterRender @DynamicComponent @code { [CascadingParameter] PageState PageState { get; set; } [Parameter] public Action OnStateChange { get; set; } PageState pagestate; RenderFragment DynamicComponent { get; set; } string _absoluteUri; bool _navigationInterceptionEnabled; protected override void OnInitialized() { _absoluteUri = NavigationManager.Uri; NavigationManager.LocationChanged += LocationChanged; DynamicComponent = builder => { if (PageState != null) { builder.OpenComponent(0, Type.GetType(Constants.PageComponent)); builder.CloseComponent(); } }; } public void Dispose() { NavigationManager.LocationChanged -= LocationChanged; } protected override async Task OnParametersSetAsync() { if (PageState == null) { // misconfigured api calls should not be processed through the router if (!_absoluteUri.Contains("~/api/")) { await Refresh(); } else { System.Diagnostics.Debug.WriteLine(this.GetType().FullName + ": Error: API call to " + _absoluteUri + " is not mapped to a Controller"); } } } private async Task Refresh() { List moduledefinitions; List themes; List aliases; Alias alias; Site site; List pages; Page page; User user; List modules; int moduleid = -1; string control = ""; bool editmode = false; bool designmode = false; Reload reload = Reload.None; // get Url path and querystring ( and remove anchors ) string path = new Uri(_absoluteUri).PathAndQuery.Substring(1); if (path.IndexOf("#") != -1) { path = path.Substring(0, path.IndexOf("#")); } // parse querystring and remove Dictionary querystring = new Dictionary(); if (path.IndexOf("?") != -1) { querystring = ParseQueryString(path); path = path.Substring(0, path.IndexOf("?")); } if (querystring.ContainsKey("reload")) { reload = (Reload)int.Parse(querystring["reload"]); } if (PageState != null) { editmode = PageState.EditMode; designmode = PageState.DesignMode; } if (PageState == null || reload == Reload.Application) { themes = await ThemeService.GetThemesAsync(); aliases = await AliasService.GetAliasesAsync(); alias = null; } else { themes = PageState.Themes; aliases = PageState.Aliases; alias = PageState.Alias; } // check if site has changed if (alias == null || GetAlias(_absoluteUri, aliases).Name != alias.Name) { alias = GetAlias(_absoluteUri, aliases); SiteState.Alias = alias; // set state for services reload = Reload.Site; } if (PageState == null || reload <= Reload.Site) { site = await SiteService.GetSiteAsync(alias.SiteId); } else { site = PageState.Site; } if (site != null) { if (PageState == null || reload >= Reload.Site) { moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(site.SiteId); pages = await PageService.GetPagesAsync(site.SiteId); } else { moduledefinitions = PageState.ModuleDefinitions; pages = PageState.Pages; } // format path and remove alias path = path.Replace("//", "/"); if (!path.EndsWith("/")) { path += "/"; } if (alias.Path != "") { path = path.Replace(alias.Path + "/", ""); } // extract admin route elements from path string[] segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); int result; if (segments.Length >= 2 && int.TryParse(segments[segments.Length - 2], out result)) { // path has moduleid and control specification ie. page/moduleid/control/ control = segments[segments.Length - 1]; moduleid = result; path = path.Replace(moduleid.ToString() + "/" + control + "/", ""); } else { if (segments.Length >= 2 && int.TryParse(segments[segments.Length - 2], out result)) { // path has only moduleid specification ie. page/moduleid/ moduleid = result; path = path.Replace(moduleid.ToString() + "/", ""); } } // remove trailing slash so it can be used as a key for Pages if (path.EndsWith("/")) path = path.Substring(0, path.Length - 1); if (PageState == null || reload >= Reload.Page) { page = pages.Where(item => item.Path == path).FirstOrDefault(); } else { page = PageState.Page; } // failsafe in case router cannot locate the home page for the site if (page == null && path == "") { page = pages.FirstOrDefault(); path = page.Path; } // check if page has changed if (page.Path != path) { page = pages.Where(item => item.Path == path).FirstOrDefault(); reload = Reload.Page; editmode = page.EditMode; designmode = false; } user = null; if (PageState == null || reload >= Reload.Page) { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); if (authState.User.Identity.IsAuthenticated) { user = await UserService.GetUserAsync(authState.User.Identity.Name, site.SiteId); } } else { user = PageState.User; } if (page != null) { page = ProcessPage(page, site); // check if user is authorized to view page if (UserSecurity.IsAuthorized(user, "View", page.Permissions)) { pagestate = new PageState(); pagestate.ModuleDefinitions = moduledefinitions; pagestate.Themes = themes; pagestate.Aliases = aliases; pagestate.Alias = alias; pagestate.Site = site; pagestate.Pages = pages; pagestate.Page = page; pagestate.User = user; pagestate.Uri = new Uri(_absoluteUri, UriKind.Absolute); pagestate.QueryString = querystring; pagestate.ModuleId = moduleid; pagestate.Control = control; if (PageState != null && (PageState.ModuleId != pagestate.ModuleId || PageState.Control != pagestate.Control)) { reload = Reload.Page; } if (PageState == null || reload >= Reload.Page) { modules = await ModuleService.GetModulesAsync(page.PageId); modules = ProcessModules(modules, moduledefinitions, pagestate.Control, page.Panes, site); } else { modules = PageState.Modules; } pagestate.Modules = modules; pagestate.EditMode = editmode; pagestate.DesignMode = designmode; OnStateChange?.Invoke(pagestate); } else { // user is not authorized to view page } } else { // page does not exist } } else { // site does not exist } } 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; } private Dictionary ParseQueryString(string path) { Dictionary querystring = new Dictionary(); if (path.IndexOf("?") != -1) { foreach (string kvp in path.Substring(path.IndexOf("?") + 1).Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries)) { if (kvp != "") { if (kvp.Contains("=")) { string[] pair = kvp.Split('='); querystring.Add(pair[0], pair[1]); } else { querystring.Add(kvp, "true"); // default querystring when no value is provided } } } } return querystring; } private Page ProcessPage(Page page, Site site) { try { if (string.IsNullOrEmpty(page.ThemeType)) { page.ThemeType = site.DefaultThemeType; page.LayoutType = site.DefaultLayoutType; } Type type; if (!string.IsNullOrEmpty(page.LayoutType)) { type = Type.GetType(page.LayoutType); } else { type = Type.GetType(page.ThemeType); } System.Reflection.PropertyInfo property = type.GetProperty("Panes"); page.Panes = (string)property.GetValue(Activator.CreateInstance(type), null); } catch { // error loading theme or layout } return page; } private List ProcessModules(List modules, List moduledefinitions, string control, string panes, Site site) { ModuleDefinition moduledefinition; if (control == "") { control = Constants.DefaultControl; } Dictionary paneindex = new Dictionary(); foreach (Module module in modules) { // set the type based on the template and action moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == module.ModuleDefinitionName).FirstOrDefault(); if (moduledefinition != null) { string typename = moduledefinition.ControlTypeTemplate; if (moduledefinition.ControlTypeRoutes != "") { foreach (string route in moduledefinition.ControlTypeRoutes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { if (route.StartsWith(control + "=")) { typename = route.Replace(control + "=", ""); } } } module.ModuleType = typename.Replace("{Control}", control); // get IModuleControl properties typename = module.ModuleType; // check for core module actions component if (Constants.DefaultModuleActions.Contains(control)) { typename = Constants.DefaultModuleActionsTemplate.Replace("{Control}", control); } Type moduletype = Type.GetType(typename); if (moduletype != null) { var moduleobject = Activator.CreateInstance(moduletype); module.SecurityAccessLevel = (SecurityAccessLevel)moduletype.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); module.ControlTitle = (string)moduletype.GetProperty("Title").GetValue(moduleobject); module.Actions = (string)moduletype.GetProperty("Actions").GetValue(moduleobject); module.UseAdminContainer = (bool)moduletype.GetProperty("UseAdminContainer").GetValue(moduleobject); } } // ensure module's pane exists in current page and if not, assign it to the Admin pane if (!panes.ToLower().Contains(module.Pane.ToLower())) { module.Pane = Constants.AdminPane; } // calculate module position within pane if (paneindex.ContainsKey(module.Pane)) { paneindex[module.Pane] += 1; } else { paneindex.Add(module.Pane, 0); } module.PaneModuleIndex = paneindex[module.Pane]; if (string.IsNullOrEmpty(module.ContainerType)) { module.ContainerType = site.DefaultContainerType; } } foreach (Module module in modules) { module.PaneModuleCount = paneindex[module.Pane] + 1; } return modules; } private Alias GetAlias(string absoluteUri, List aliases) { string aliasname; Alias alias = null; Uri uri = new Uri(absoluteUri); if (uri.Segments.Count() > 1) { // check if first path segment is an alias ( ie. a subfolder - www.domain.com/subfolder ) aliasname = uri.Authority + "/" + uri.Segments[1]; if (aliasname.EndsWith("/")) { aliasname = aliasname.Substring(0, aliasname.Length - 1); } alias = aliases.Where(item => item.Name == aliasname).FirstOrDefault(); } if (alias == null) { aliasname = uri.Authority; alias = aliases.Where(item => item.Name == aliasname).FirstOrDefault(); } if (alias == null && aliases.Count > 0) { // use first alias if Uri does not exist alias = aliases.FirstOrDefault(); } return alias; } }