
The control accepts a dictionary of string that can be searched using an input control. Pages Add and Edit have implemented the control to render the list of Icons found in the Oqtane Shared namespace.
505 lines
23 KiB
Plaintext
505 lines
23 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
|
|
|
|
@if (_initialized)
|
|
{
|
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
|
<TabStrip Refresh="@_refresh">
|
|
<TabPanel Name="Settings" ResourceKey="Settings" Heading="Settings">
|
|
<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>
|
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
|
{
|
|
<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" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
|
<option value="-1"><@Localizer["SiteRoot"]></option>
|
|
@foreach (Page page in PageState.Pages)
|
|
{
|
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList))
|
|
{
|
|
<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>
|
|
}
|
|
else
|
|
{
|
|
<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="@(_parent.PageId)">@(new string('-', _parent.Level * 2))@(_parent.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["AtEnd"]</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 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">
|
|
<InputList Value="@_icon" ValueChanged="IconChanged" InputValues="@IconList" />
|
|
</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 Name="Appearance" ResourceKey="Appearance" Heading=@Localizer["Appearance.Name"]>
|
|
<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="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>
|
|
@foreach (var container in _containers)
|
|
{
|
|
<option value="@container.TypeName">@container.Name</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Section>
|
|
<Section Name="PageContent" ResourceKey="PageContent" Heading=@Localizer["PageContent.Heading"]>
|
|
<div class="container">
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
|
<div class="col-sm-9">
|
|
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
|
<div class="col-sm-9">
|
|
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Section>
|
|
</TabPanel>
|
|
<TabPanel Name="Permissions" ResourceKey="Permissions" Heading=@Localizer["Permissions.Heading"]>
|
|
<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=@Localizer["Theme.Heading"] ResourceKey="ThemeSettings">
|
|
@ThemeSettingsComponent
|
|
</TabPanel>
|
|
}
|
|
</TabStrip>
|
|
<br />
|
|
<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.View;
|
|
|
|
private bool _initialized = false;
|
|
private ElementReference form;
|
|
private bool validated = false;
|
|
private List<ThemeControl> _themes = new List<ThemeControl>();
|
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
|
private int _pageId;
|
|
private string _name;
|
|
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 _path = string.Empty;
|
|
private string _url;
|
|
private string _ispersonalizable = "False";
|
|
private string _title;
|
|
private string _icon { get; set; } = string.Empty;
|
|
private string _themetype = string.Empty;
|
|
private string _containertype = string.Empty;
|
|
private string _headcontent;
|
|
private string _bodycontent;
|
|
private string _permissions = null;
|
|
private PermissionGrid _permissionGrid;
|
|
private Type _themeSettingsType;
|
|
private object _themeSettings;
|
|
private RenderFragment ThemeSettingsComponent { get; set; }
|
|
private bool _refresh = false;
|
|
protected Page _parent = null;
|
|
protected Dictionary<string, string> IconList = new();
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
try
|
|
{
|
|
if (PageState.QueryString.ContainsKey("id"))
|
|
{
|
|
_pageId = Int32.Parse(PageState.QueryString["id"]);
|
|
_parent = await PageService.GetPageAsync(_pageId);
|
|
if (_parent != null)
|
|
{
|
|
_parentid = _parent.PageId.ToString();
|
|
}
|
|
}
|
|
|
|
Type iconsType = typeof(Icons);
|
|
System.Reflection.FieldInfo[] fields = iconsType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.GetField);
|
|
|
|
foreach (System.Reflection.FieldInfo field in fields)
|
|
{
|
|
if (field.FieldType == typeof(string))
|
|
{
|
|
string fieldName = field.Name;
|
|
string fieldValue = (string)field.GetValue(null);
|
|
|
|
IconList.Add(fieldName, fieldValue);
|
|
}
|
|
}
|
|
|
|
// if admin or user has edit access to parent page
|
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) || (_parent != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _parent.PermissionList)))
|
|
{
|
|
_themetype = PageState.Site.DefaultThemeType;
|
|
_themes = ThemeService.GetThemeControls(PageState.Site.Themes);
|
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
|
_containertype = PageState.Site.DefaultContainerType;
|
|
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
|
ThemeSettings();
|
|
_initialized = true;
|
|
}
|
|
else
|
|
{
|
|
await logger.LogWarning("Error Loading Page {ParentId}", _parentid);
|
|
AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await logger.LogError(ex, "Error Loading Page {Error}", ex.Message);
|
|
AddModuleMessage(Localizer["Error.Page.Load"], 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.PermissionList))
|
|
{
|
|
_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.PermissionList))
|
|
{
|
|
_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 void ThemeChanged(ChangeEventArgs e)
|
|
{
|
|
_themetype = (string)e.Value;
|
|
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
|
{
|
|
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
|
_containertype = _containers.First().TypeName;
|
|
ThemeSettings();
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
private void ThemeSettings()
|
|
{
|
|
_themeSettingsType = null;
|
|
var theme = PageState.Site.Themes.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) && !string.IsNullOrEmpty(_containertype))
|
|
{
|
|
page = new Page();
|
|
page.SiteId = PageState.Page.SiteId;
|
|
page.Name = _name;
|
|
|
|
if (string.IsNullOrEmpty(_path))
|
|
{
|
|
_path = _name;
|
|
}
|
|
if (_path.Contains("/"))
|
|
{
|
|
if (_path.EndsWith("/") && _path != "/")
|
|
{
|
|
_path = _path.Substring(0, _path.Length - 1);
|
|
}
|
|
_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);
|
|
}
|
|
}
|
|
|
|
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
|
if (_pages.Any(item => item.Path == page.Path))
|
|
{
|
|
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.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
|
|
page.UserId = null;
|
|
|
|
// appearance
|
|
page.Title = _title;
|
|
page.Icon = (_icon == null ? string.Empty : _icon);
|
|
page.ThemeType = _themetype;
|
|
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
|
{
|
|
page.ThemeType = string.Empty;
|
|
}
|
|
page.DefaultContainerType = _containertype;
|
|
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
|
{
|
|
page.DefaultContainerType = string.Empty;
|
|
}
|
|
|
|
// page content
|
|
page.HeadContent = _headcontent;
|
|
page.BodyContent = _bodycontent;
|
|
|
|
// permissions
|
|
page.PermissionList = _permissionGrid.GetPermissionList();
|
|
|
|
page = await PageService.AddPageAsync(page);
|
|
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
|
|
|
await logger.LogInformation("Page Added {Page}", page);
|
|
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
|
{
|
|
NavigationManager.NavigateTo(page.Path); // redirect to new page
|
|
}
|
|
else
|
|
{
|
|
NavigationManager.NavigateTo(NavigateUrl()); // redirect to page management
|
|
}
|
|
}
|
|
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 (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
|
{
|
|
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
|
}
|
|
else
|
|
{
|
|
NavigationManager.NavigateTo(NavigateUrl());
|
|
}
|
|
}
|
|
private void IconChanged(string NewIcon)
|
|
{
|
|
_icon = NewIcon;
|
|
}
|
|
}
|