448 lines
15 KiB
Plaintext
448 lines
15 KiB
Plaintext
@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<PageState> 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<ModuleDefinition> moduledefinitions;
|
|
List<Models.Theme> themes;
|
|
List<Alias> aliases;
|
|
Alias alias;
|
|
Site site;
|
|
List<Page> pages;
|
|
Page page;
|
|
User user;
|
|
List<Module> 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<string, string> querystring = new Dictionary<string, string>();
|
|
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<string, string> ParseQueryString(string path)
|
|
{
|
|
Dictionary<string, string> querystring = new Dictionary<string, string>();
|
|
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<Module> ProcessModules(List<Module> modules, List<ModuleDefinition> moduledefinitions, string control, string panes, Site site)
|
|
{
|
|
ModuleDefinition moduledefinition;
|
|
|
|
if (control == "")
|
|
{
|
|
control = Constants.DefaultControl;
|
|
}
|
|
|
|
Dictionary<string, int> paneindex = new Dictionary<string, int>();
|
|
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<Alias> 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;
|
|
}
|
|
|
|
} |