441 lines
19 KiB
Plaintext
441 lines
19 KiB
Plaintext
@namespace Oqtane.Modules.Admin.Pages
|
|
@inherits ModuleBase
|
|
@inject NavigationManager NavigationManager
|
|
@inject IPageService PageService
|
|
@inject IThemeService ThemeService
|
|
@inject IStringLocalizer<Add> Localizer
|
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
|
|
|
|
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
|
<TabStrip Refresh="@_refresh">
|
|
<TabPanel Name="Settings" ResourceKey="Settings">
|
|
@if (_themeList != null)
|
|
{
|
|
<div class="container">
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
|
|
<div class="col-sm-9">
|
|
<input id="name" class="form-control" @bind="@_name" required />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
|
|
<div class="col-sm-9">
|
|
<select id="parent" class="form-select" @onchange="(e => ParentChanged(e))" required>
|
|
<option value="-1"><@Localizer["SiteRoot"]></option>
|
|
@foreach (Page page in _pageList)
|
|
{
|
|
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="insert" HelpText="Select the location where you would like the page to be inserted in relation to other pages" ResourceKey="Insert">Insert: </Label>
|
|
<div class="col-sm-9">
|
|
<select id="insert" class="form-select" @bind="@_insert" required>
|
|
<option value="<<">@Localizer["AtBeginning"]</option>
|
|
@if (_children != null && _children.Count > 0)
|
|
{
|
|
<option value="<">@Localizer["Before"]</option>
|
|
<option value=">">@Localizer["After"]</option>
|
|
}
|
|
<option value=">>">@Localizer["AtEnd"]</option>
|
|
</select>
|
|
@if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">"))
|
|
{
|
|
<select class="form-select" @bind="@_childid">
|
|
<option value="-1"><@Localizer["Page.Select"]></option>
|
|
@foreach (Page page in _children)
|
|
{
|
|
<option value="@(page.PageId)">@(page.Name)</option>
|
|
}
|
|
</select>
|
|
}
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label>
|
|
<div class="col-sm-9">
|
|
<select id="navigation" class="form-select" @bind="@_isnavigation" required>
|
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
|
<option value="False">@SharedLocalizer["No"]</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="clickable" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label>
|
|
<div class="col-sm-9">
|
|
<select id="clickable" class="form-select" @bind="@_isclickable" required>
|
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
|
<option value="False">@SharedLocalizer["No"]</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
|
|
<div class="col-sm-9">
|
|
<input id="path" class="form-control" @bind="@_path" />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
|
|
<div class="col-sm-9">
|
|
<input id="url" class="form-control" @bind="@_url" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<Section Name="Appearance" ResourceKey="Appearance">
|
|
<div class="container">
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
|
|
<div class="col-sm-9">
|
|
<input id="title" class="form-control" @bind="@_title" />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="meta" HelpText="Optionally enter meta tags (in exactly the form you want them to be included in the page output)." ResourceKey="Meta">Meta: </Label>
|
|
<div class="col-sm-9">
|
|
<textarea id="meta" class="form-control" @bind="@_meta" rows="3"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
|
<div class="col-sm-9">
|
|
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
|
@foreach (var theme in _themes)
|
|
{
|
|
<option value="@theme.TypeName">@theme.Name</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="container" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
|
|
<div class="col-sm-9">
|
|
<select id="container" class="form-select" @bind="@_containertype" required>
|
|
<option value="-"><@Localizer["Container.Select"]></option>
|
|
@foreach (var container in _containers)
|
|
{
|
|
<option value="@container.TypeName">@container.Name</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
|
|
<div class="col-sm-9">
|
|
<input id="icon" class="form-control" @bind="@_icon" />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content" ResourceKey="Personalizable">Personalizable? </Label>
|
|
<div class="col-sm-9">
|
|
<select id="personalizable" class="form-select" @bind="@_ispersonalizable" required>
|
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
|
<option value="False">@SharedLocalizer["No"]</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Section>
|
|
}
|
|
</TabPanel>
|
|
<TabPanel Name="Permissions" ResourceKey="Permissions">
|
|
<div class="container">
|
|
<div class="row mb-1 align-items-center">
|
|
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" />
|
|
</div>
|
|
</div>
|
|
</TabPanel>
|
|
@if (_themeSettingsType != null)
|
|
{
|
|
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
|
|
@ThemeSettingsComponent
|
|
</TabPanel>
|
|
}
|
|
</TabStrip>
|
|
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
|
</form>
|
|
|
|
@code {
|
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
|
|
|
private List<Theme> _themeList;
|
|
private List<ThemeControl> _themes = new List<ThemeControl>();
|
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
|
private List<Page> _pageList;
|
|
private string _name;
|
|
private string _title;
|
|
private string _meta;
|
|
private string _path = string.Empty;
|
|
private string _parentid = "-1";
|
|
private string _insert = ">>";
|
|
private List<Page> _children;
|
|
private int _childid = -1;
|
|
private string _isnavigation = "True";
|
|
private string _isclickable = "True";
|
|
private string _url;
|
|
private string _ispersonalizable = "False";
|
|
private string _themetype = string.Empty;
|
|
private string _containertype = string.Empty;
|
|
private string _icon = string.Empty;
|
|
private string _permissions = string.Empty;
|
|
private PermissionGrid _permissionGrid;
|
|
private Type _themeSettingsType;
|
|
private object _themeSettings;
|
|
private RenderFragment ThemeSettingsComponent { get; set; }
|
|
private bool _refresh = false;
|
|
private ElementReference form;
|
|
private bool validated = false;
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
try
|
|
{
|
|
_themeList = await ThemeService.GetThemesAsync();
|
|
_themes = ThemeService.GetThemeControls(_themeList);
|
|
_themetype = PageState.Site.DefaultThemeType;
|
|
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
|
_containertype = PageState.Site.DefaultContainerType;
|
|
_pageList = PageState.Pages;
|
|
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
|
_permissions = string.Empty;
|
|
ThemeSettings();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await logger.LogError(ex, "Error Initializing Page {Error}", ex.Message);
|
|
AddModuleMessage(Localizer["Error.Page.Initialize"], MessageType.Error);
|
|
}
|
|
}
|
|
|
|
private async void ParentChanged(ChangeEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
_parentid = (string)e.Value;
|
|
_children = new List<Page>();
|
|
if (_parentid == "-1")
|
|
{
|
|
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
|
|
{
|
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
|
{
|
|
_children.Add(p);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
|
|
{
|
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
|
{
|
|
_children.Add(p);
|
|
}
|
|
}
|
|
}
|
|
StateHasChanged();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
|
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
|
}
|
|
}
|
|
|
|
private async void ThemeChanged(ChangeEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
_themetype = (string)e.Value;
|
|
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
|
_containertype = "-";
|
|
ThemeSettings();
|
|
StateHasChanged();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
|
AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error);
|
|
}
|
|
}
|
|
|
|
private void ThemeSettings()
|
|
{
|
|
_themeSettingsType = null;
|
|
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
|
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
|
{
|
|
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
|
if (_themeSettingsType != null)
|
|
{
|
|
ThemeSettingsComponent = builder =>
|
|
{
|
|
builder.OpenComponent(0, _themeSettingsType);
|
|
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
|
builder.CloseComponent();
|
|
};
|
|
}
|
|
_refresh = true;
|
|
}
|
|
}
|
|
|
|
private async Task SavePage()
|
|
{
|
|
validated = true;
|
|
var interop = new Interop(JSRuntime);
|
|
if (await interop.FormValid(form))
|
|
{
|
|
Page page = null;
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
|
{
|
|
page = new Page();
|
|
page.SiteId = PageState.Page.SiteId;
|
|
page.Name = _name;
|
|
page.Title = _title;
|
|
|
|
if (string.IsNullOrEmpty(_path))
|
|
{
|
|
_path = _name;
|
|
}
|
|
if (_path.Contains("/"))
|
|
{
|
|
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
|
}
|
|
|
|
if (_parentid == "-1")
|
|
{
|
|
page.ParentId = null;
|
|
page.Path = Utilities.GetFriendlyUrl(_path);
|
|
}
|
|
else
|
|
{
|
|
page.ParentId = Int32.Parse(_parentid);
|
|
var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault();
|
|
if (parent.Path == string.Empty)
|
|
{
|
|
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
|
}
|
|
else
|
|
{
|
|
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
|
}
|
|
}
|
|
|
|
if (PagePathIsDeleted(page.Path, page.SiteId, _pageList))
|
|
{
|
|
AddModuleMessage(string.Format(Localizer["Message.Page.Deleted"], _path), MessageType.Warning);
|
|
return;
|
|
}
|
|
|
|
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
|
|
{
|
|
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
|
return;
|
|
}
|
|
|
|
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
|
{
|
|
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
|
return;
|
|
}
|
|
|
|
Page child;
|
|
switch (_insert)
|
|
{
|
|
case "<<":
|
|
page.Order = 0;
|
|
break;
|
|
case "<":
|
|
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
|
page.Order = child.Order - 1;
|
|
break;
|
|
case ">":
|
|
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
|
page.Order = child.Order + 1;
|
|
break;
|
|
case ">>":
|
|
page.Order = int.MaxValue;
|
|
break;
|
|
}
|
|
|
|
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
|
|
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
|
page.Url = _url;
|
|
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
|
|
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
|
{
|
|
page.ThemeType = string.Empty;
|
|
}
|
|
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
|
|
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
|
{
|
|
page.DefaultContainerType = string.Empty;
|
|
}
|
|
page.Icon = (_icon == null ? string.Empty : _icon);
|
|
page.Permissions = _permissionGrid.GetPermissions();
|
|
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
|
|
page.UserId = null;
|
|
page.Meta = _meta;
|
|
|
|
page = await PageService.AddPageAsync(page);
|
|
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
|
|
|
await logger.LogInformation("Page Added {Page}", page);
|
|
if (PageState.QueryString.ContainsKey("cp"))
|
|
{
|
|
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
|
|
}
|
|
else
|
|
{
|
|
NavigationManager.NavigateTo(NavigateUrl(page.Path));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
|
}
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
|
|
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
|
}
|
|
}
|
|
|
|
private void Cancel()
|
|
{
|
|
if (PageState.QueryString.ContainsKey("cp"))
|
|
{
|
|
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
|
|
}
|
|
else
|
|
{
|
|
NavigationManager.NavigateTo(NavigateUrl());
|
|
}
|
|
}
|
|
|
|
private static bool PagePathIsUnique(string pagePath, int siteId, List<Page> existingPages)
|
|
{
|
|
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath);
|
|
}
|
|
|
|
private static bool PagePathIsDeleted(string pagePath, int siteId, List<Page> existingPages)
|
|
{
|
|
return existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath && page.IsDeleted == true);
|
|
}
|
|
}
|