ability to specify Resources in IModule and ITheme interfaces,, fixed module settings for personalized pages
This commit is contained in:
		| @ -44,12 +44,20 @@ | ||||
|                         <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"> | ||||
|                             <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> | ||||
|                         </div> | ||||
|  | ||||
| @ -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<PageState> OnStateChange { get; set; } | ||||
|     [Parameter] | ||||
|     public Action<PageState> 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<string, string> ParseQueryString(string query) | ||||
| 	{ | ||||
| 		Dictionary<string, string> querystring = new Dictionary<string, string>(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<string, string> ParseQueryString(string query) | ||||
|     { | ||||
|         Dictionary<string, string> querystring = new Dictionary<string, string>(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<Page> 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<Page> 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<string>(); | ||||
| 			page.Resources = new List<Resource>(); | ||||
|             page.Panes = new List<string>(); | ||||
|             page.Resources = new List<Resource>(); | ||||
|  | ||||
| 			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<Module> Modules) ProcessModules(Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype) | ||||
| 	{ | ||||
| 		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; | ||||
|         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<Module> Modules) ProcessModules(Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype, Alias alias) | ||||
|     { | ||||
|         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; | ||||
|  | ||||
| 					// 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<Resource> ManagePageResources(List<Resource> pageresources, List<Resource> 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<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); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| @ -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() | ||||
|     { | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 sbwalker
					sbwalker