Merge pull request #2867 from sbwalker/dev

ability for non-administrators to edit page settings
This commit is contained in:
Shaun Walker 2023-06-05 14:33:17 -04:00 committed by GitHub
commit 9a1ea3f375
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 582 additions and 414 deletions

View File

@ -6,102 +6,109 @@
@inject IStringLocalizer<Add> Localizer @inject IStringLocalizer<Add> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@if (_initialized)
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate> {
<TabStrip Refresh="@_refresh"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<TabPanel Name="Settings" ResourceKey="Settings"> <TabStrip Refresh="@_refresh">
@if (PageState.Site.Themes != null) <TabPanel Name="Settings" ResourceKey="Settings">
{ @if (PageState.Site.Themes != 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">&lt;@Localizer["SiteRoot"]&gt;</option>
@foreach (Page page in PageState.Pages)
{
<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">&lt;@Localizer["Page.Select"]&gt;</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 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" Heading="Appearance" ResourceKey="Appearance">
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <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> <Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="title" class="form-control" @bind="@_title" /> <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" @onchange="(e => ParentChanged(e))" required>
<option value="-1">&lt;@Localizer["SiteRoot"]&gt;</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">&lt;@Localizer["Page.Select"]&gt;</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> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -111,112 +118,155 @@
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <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> <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"> <div class="col-sm-9">
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required> <select id="personalizable" class="form-select" @bind="@_ispersonalizable" required>
@foreach (var theme in _themes) <option value="True">@SharedLocalizer["Yes"]</option>
{ <option value="False">@SharedLocalizer["No"]</option>
<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="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select> </select>
</div> </div>
</div> </div>
</div> </div>
</Section>
<Section Name="PageContent" Heading="Page Content" ResourceKey="PageContent"> <Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <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> <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"> <div class="col-sm-9">
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea> <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>
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> </Section>
<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> <Section Name="PageContent" Heading="Page Content" ResourceKey="PageContent">
<div class="col-sm-9"> <div class="container">
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3"></textarea> <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>
</div> </div>
</div> </Section>
</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> </TabPanel>
} <TabPanel Name="Permissions" ResourceKey="Permissions">
</TabStrip> <div class="container">
<br /> <div class="row mb-1 align-items-center">
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button> <PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" />
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button> </div>
</form> </div>
</TabPanel>
@if (_themeSettingsType != null)
{
<TabPanel Name="ThemeSettings" Heading="Theme Settings" 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 { @code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
private List<ThemeControl> _themes = new List<ThemeControl>(); private bool _initialized = false;
private List<ThemeControl> _containers = new List<ThemeControl>(); private ElementReference form;
private string _name; private bool validated = false;
private string _parentid = "-1"; private List<ThemeControl> _themes = new List<ThemeControl>();
private string _insert = ">>"; private List<ThemeControl> _containers = new List<ThemeControl>();
private List<Page> _children; private int _pageId;
private int _childid = -1; private string _name;
private string _isnavigation = "True"; private string _parentid = "-1";
private string _isclickable = "True"; 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 _path = string.Empty;
private string _url; private string _url;
private string _ispersonalizable = "False"; private string _ispersonalizable = "False";
private string _title; private string _title;
private string _icon = string.Empty; private string _icon = string.Empty;
private string _themetype = string.Empty; private string _themetype = string.Empty;
private string _containertype = string.Empty; private string _containertype = string.Empty;
private string _headcontent; private string _headcontent;
private string _bodycontent; private string _bodycontent;
private string _permissions = null; private string _permissions = null;
private PermissionGrid _permissionGrid; private PermissionGrid _permissionGrid;
private Type _themeSettingsType; private Type _themeSettingsType;
private object _themeSettings; private object _themeSettings;
private RenderFragment ThemeSettingsComponent { get; set; } private RenderFragment ThemeSettingsComponent { get; set; }
private bool _refresh = false; private bool _refresh = false;
private ElementReference form; protected Page _parent = null;
private bool validated = false;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try
{ {
_themes = ThemeService.GetThemeControls(PageState.Site.Themes); if (PageState.QueryString.ContainsKey("id"))
_themetype = PageState.Site.DefaultThemeType; {
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype); _pageId = Int32.Parse(PageState.QueryString["id"]);
_containertype = PageState.Site.DefaultContainerType; _parent = await PageService.GetPageAsync(_pageId);
_children = PageState.Pages.Where(item => item.ParentId == null).ToList(); if (_parent != null)
ThemeSettings(); {
} _parentid = _parent.PageId.ToString();
}
}
// 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)))
{
_themes = ThemeService.GetThemeControls(PageState.Site.Themes);
_themetype = PageState.Site.DefaultThemeType;
_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) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Initializing Page {Error}", ex.Message); await logger.LogError(ex, "Error Loading Page {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Page.Initialize"], MessageType.Error); AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error);
} }
} }

View File

@ -8,113 +8,242 @@
@inject IStringLocalizer<Edit> Localizer @inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate> @if (_initialized)
<TabStrip Refresh="@_refresh"> {
<TabPanel Name="Settings" ResourceKey="Settings" Heading=@Localizer["Settings.Heading"]> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
@if (PageState.Site.Themes != null) @if (_page.UserId == null)
{ {
<div class="container"> <TabStrip Refresh="@_refresh">
<div class="row mb-1 align-items-center"> <TabPanel Name="Settings" ResourceKey="Settings" Heading=@Localizer["Settings.Heading"]>
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label> @if (PageState.Site.Themes != null)
<div class="col-sm-9"> {
<input id="name" class="form-control" @bind="@_name" maxlength="50" required /> <div class="container">
</div> <div class="row mb-1 align-items-center">
</div> <Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
<div class="row mb-1 align-items-center"> <div class="col-sm-9">
<Label Class="col-sm-3" For="parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label> <input id="name" class="form-control" @bind="@_name" maxlength="50" required />
<div class="col-sm-9"> </div>
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required> </div>
<option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option> @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
@foreach (Page page in PageState.Pages)
{
if (page.PageId != _pageId)
{
<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="move" HelpText="Select the location where you would like the page to be moved in relation to other pages" ResourceKey="Move">Move: </Label>
<div class="col-sm-9">
<select id="move" class="form-select" @bind="@_insert" required>
@if (_parentid == _currentparentid)
{
<option value="=">&lt;@Localizer["ThisLocation.Keep"]&gt;</option>
}
<option value="<<">@Localizer["ToBeginning"]</option>
@if (_children != null && _children.Count > 0)
{
<option value="<">@Localizer["Before"]</option>
<option value=">">@Localizer["After"]</option>
}
<option value=">>">@Localizer["ToEnd"]</option>
</select>
@if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">"))
{ {
<select class="form-select" @bind="@_childid"> <div class="row mb-1 align-items-center">
<option value="-1">&lt;@Localizer["Page.Select"]&gt;</option> <Label Class="col-sm-3" For="parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
@foreach (Page page in _children) <div class="col-sm-9">
{ <select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
<option value="@(page.PageId)">@(page.Name)</option> <option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option>
} @foreach (Page page in PageState.Pages)
</select> {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList) && page.PageId != _pageId)
{
<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="move" HelpText="Select the location where you would like the page to be moved in relation to other pages" ResourceKey="Move">Move: </Label>
<div class="col-sm-9">
<select id="move" class="form-select" @bind="@_insert" required>
@if (_parentid == _currentparentid)
{
<option value="=">&lt;@Localizer["ThisLocation.Keep"]&gt;</option>
}
<option value="<<">@Localizer["ToBeginning"]</option>
@if (_children != null && _children.Count > 0)
{
<option value="<">@Localizer["Before"]</option>
<option value=">">@Localizer["After"]</option>
}
<option value=">>">@Localizer["ToEnd"]</option>
</select>
@if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">"))
{
<select class="form-select" @bind="@_childid">
<option value="-1">&lt;@Localizer["Page.Select"]&gt;</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" value="@_parentid" @onchange="(e => ParentChanged(e))" disabled>
<option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option>
@if (_parent != null)
{
<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="move" HelpText="Select the location where you would like the page to be moved in relation to other pages" ResourceKey="Move">Move: </Label>
<div class="col-sm-9">
<select id="move" class="form-select" @bind="@_insert" disabled>
<option value="=">&lt;@Localizer["ThisLocation.Keep"]&gt;</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" maxlength="256" />
</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" maxlength="500" />
</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" maxlength="50" />
</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> </div>
</div> <Section Name="Appearance" ResourceKey="Appearance">
<div class="row mb-1 align-items-center"> <div class="container">
<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="row mb-1 align-items-center">
<div class="col-sm-9"> <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>
<select id="navigation" class="form-select" @bind="@_isnavigation" required> <div class="col-sm-9">
<option value="True">@SharedLocalizer["Yes"]</option> <input id="title" class="form-control" @bind="@_title" maxlength="200" />
<option value="False">@SharedLocalizer["No"]</option> </div>
</select> </div>
</div> <div class="row mb-1 align-items-center">
</div> <Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
<div class="row mb-1 align-items-center"> <div class="col-sm-9">
<Label Class="col-sm-3" For="clickable" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label> <select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
<div class="col-sm-9"> @foreach (var theme in _themes)
<select id="clickable" class="form-select" @bind="@_isclickable" required> {
<option value="True">@SharedLocalizer["Yes"]</option> <option value="@theme.TypeName">@theme.Name</option>
<option value="False">@SharedLocalizer["No"]</option> }
</select> </select>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <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> <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"> <div class="col-sm-9">
<input id="path" class="form-control" @bind="@_path" maxlength="256"/> <select id="container" class="form-select" @bind="@_containertype" required>
</div> <option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
</div> @foreach (var container in _containers)
<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> <option value="@container.TypeName">@container.Name</option>
<div class="col-sm-9"> }
<input id="url" class="form-control" @bind="@_url" maxlength="500"/> </select>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> </div>
<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> </Section>
<div class="col-sm-9"> <Section Name="PageContent" Heading="Page Content" ResourceKey="PageContent">
<select id="personalizable" class="form-select" @bind="@_ispersonalizable" required> <div class="container">
<option value="True">@SharedLocalizer["Yes"]</option> <div class="row mb-1 align-items-center">
<option value="False">@SharedLocalizer["No"]</option> <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>
</select> <div class="col-sm-9">
</div> <textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
</div> </div>
</div> </div>
<Section Name="Appearance" ResourceKey="Appearance"> <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>
<br />
<br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
}
</TabPanel>
@if (_page.UserId == null)
{
<TabPanel Name="Permissions" ResourceKey="Permissions">
@if (_permissions != null)
{
<div class="container">
<div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.Page" PermissionList="@_permissions" @ref="_permissionGrid" />
</div>
</div>
}
</TabPanel>
<TabPanel Name="PageModules" Heading="Modules" ResourceKey="PageModules">
@if (_pageModules != null)
{
<Pager Items="_pageModules">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["ModuleTitle"]</th>
<th>@Localizer["ModuleDefinition"]</th>
</Header>
<Row>
<td><ActionLink Action="Settings" Text="Edit" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" ResourceKey="ModuleSettings" /></td>
<td><ActionDialog Header="Delete Module" Message="Are You Sure You Wish To Delete This Module?" Action="Delete" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
<td>@context.Title</td>
<td>@context.ModuleDefinition?.Name</td>
</Row>
</Pager>
}
</TabPanel>
}
@if (_themeSettingsType != null)
{
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
@ThemeSettingsComponent
</TabPanel>
<br />
}
</TabStrip>
}
else
{
<TabStrip Refresh="@_refresh">
<TabPanel Name="Settings" ResourceKey="Settings" Heading=@Localizer["Settings.Heading"]>
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <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> <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"> <div class="col-sm-9">
<input id="title" class="form-control" @bind="@_title" maxlength="200"/> <input id="title" class="form-control" @bind="@_title" maxlength="200" />
</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" maxlength="50" />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -141,73 +270,26 @@
</div> </div>
</div> </div>
</div> </div>
</Section> </TabPanel>
<Section Name="PageContent" Heading="Page Content" ResourceKey="PageContent"> @if (_themeSettingsType != null)
<div class="container"> {
<div class="row mb-1 align-items-center"> <TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
<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> @ThemeSettingsComponent
<div class="col-sm-9"> </TabPanel>
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea> <br />
</div> }
</div> </TabStrip>
<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>
<br />
<br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
}
</TabPanel>
<TabPanel Name="Permissions" ResourceKey="Permissions">
@if (_permissions != null)
{
<div class="container">
<div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.Page" PermissionList="@_permissions" @ref="_permissionGrid" />
</div>
</div>
}
</TabPanel>
<TabPanel Name="PageModules" Heading="Modules" ResourceKey="PageModules">
@if(_pageModules != null)
{
<Pager Items="_pageModules">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["ModuleTitle"]</th>
<th>@Localizer["ModuleDefinition"]</th>
</Header>
<Row>
<td><ActionLink Action="Settings" Text="Edit" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" ResourceKey="ModuleSettings" /></td>
<td><ActionDialog Header="Delete Module" Message="Are You Sure You Wish To Delete This Module?" Action="Delete" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
<td>@context.Title</td>
<td>@context.ModuleDefinition?.Name</td>
</Row>
</Pager>
}
</TabPanel>
@if (_themeSettingsType != null)
{
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
@ThemeSettingsComponent
</TabPanel>
<br />
} }
</TabStrip> <br />
<br /> <button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button> </form>
</form> }
@code { @code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
private bool _initialized = false;
private ElementReference form; private ElementReference form;
private bool validated = false; private bool validated = false;
private List<ThemeControl> _themes = new List<ThemeControl>(); private List<ThemeControl> _themes = new List<ThemeControl>();
@ -243,7 +325,8 @@
private string _deletedby; private string _deletedby;
private DateTime? _deletedon; private DateTime? _deletedon;
private bool _refresh = false; private bool _refresh = false;
protected Page page; protected Page _page = null;
protected Page _parent = null;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@ -253,23 +336,24 @@
_themes = ThemeService.GetThemeControls(PageState.Site.Themes); _themes = ThemeService.GetThemeControls(PageState.Site.Themes);
_pageId = Int32.Parse(PageState.QueryString["id"]); _pageId = Int32.Parse(PageState.QueryString["id"]);
page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); _page = await PageService.GetPageAsync(_pageId);
if (page != null) if (_page != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _page.PermissionList))
{ {
_name = page.Name; _name = _page.Name;
if (page.ParentId == null) if (_page.ParentId == null)
{ {
_parentid = "-1"; _parentid = "-1";
} }
else else
{ {
_parentid = page.ParentId.ToString(); _parentid = _page.ParentId.ToString();
_parent = PageState.Pages.FirstOrDefault(item => item.PageId == _page.ParentId);
} }
_currentparentid = _parentid; _currentparentid = _parentid;
_isnavigation = page.IsNavigation.ToString(); _isnavigation = _page.IsNavigation.ToString();
_isclickable = page.IsClickable.ToString(); _isclickable = _page.IsClickable.ToString();
_path = page.Path; _path = _page.Path;
if (string.IsNullOrEmpty(_path)) if (string.IsNullOrEmpty(_path))
{ {
_path = "/"; _path = "/";
@ -281,43 +365,49 @@
_path = _path.Substring(_path.LastIndexOf("/") + 1); _path = _path.Substring(_path.LastIndexOf("/") + 1);
} }
} }
_url = page.Url; _url = _page.Url;
_ispersonalizable = page.IsPersonalizable.ToString(); _icon = _page.Icon;
_ispersonalizable = _page.IsPersonalizable.ToString();
// appearance // appearance
_title = page.Title; _title = _page.Title;
_icon = page.Icon; _themetype = _page.ThemeType;
_themetype = page.ThemeType;
if (string.IsNullOrEmpty(_themetype)) if (string.IsNullOrEmpty(_themetype))
{ {
_themetype = PageState.Site.DefaultThemeType; _themetype = PageState.Site.DefaultThemeType;
} }
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype); _containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
_containertype = page.DefaultContainerType; _containertype = _page.DefaultContainerType;
if (string.IsNullOrEmpty(_containertype)) if (string.IsNullOrEmpty(_containertype))
{ {
_containertype = PageState.Site.DefaultContainerType; _containertype = PageState.Site.DefaultContainerType;
} }
// page content // page content
_headcontent = page.HeadContent; _headcontent = _page.HeadContent;
_bodycontent = page.BodyContent; _bodycontent = _page.BodyContent;
// permissions // permissions
_permissions = page.PermissionList; _permissions = _page.PermissionList;
// page modules // page modules
_pageModules = PageState.Modules.Where(m => m.PageId == page.PageId).ToList(); _pageModules = PageState.Modules.Where(m => m.PageId == _page.PageId).ToList();
// audit // audit
_createdby = page.CreatedBy; _createdby = _page.CreatedBy;
_createdon = page.CreatedOn; _createdon = _page.CreatedOn;
_modifiedby = page.ModifiedBy; _modifiedby = _page.ModifiedBy;
_modifiedon = page.ModifiedOn; _modifiedon = _page.ModifiedOn;
_deletedby = page.DeletedBy; _deletedby = _page.DeletedBy;
_deletedon = page.DeletedOn; _deletedon = _page.DeletedOn;
ThemeSettings(); ThemeSettings();
_initialized = true;
}
else
{
await logger.LogWarning("Error Loading Page {PageId}", _pageId);
AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error);
} }
} }
catch (Exception ex) catch (Exception ex)
@ -331,7 +421,7 @@
{ {
try try
{ {
PageModule pagemodule = await PageModuleService.GetPageModuleAsync(page.PageId, module.ModuleId); PageModule pagemodule = await PageModuleService.GetPageModuleAsync(_page.PageId, module.ModuleId);
pagemodule.IsDeleted = true; pagemodule.IsDeleted = true;
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await logger.LogInformation(LogFunction.Update,"Module Deleted {Title}", module.Title); await logger.LogInformation(LogFunction.Update,"Module Deleted {Title}", module.Title);
@ -435,15 +525,13 @@
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
if (await interop.FormValid(form)) if (await interop.FormValid(form))
{ {
Page page = null;
try try
{ {
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-") if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
{ {
page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); string currentPath = _page.Path;
string currentPath = page.Path;
page.Name = _name; _page.Name = _name;
if (string.IsNullOrEmpty(_path)) if (string.IsNullOrEmpty(_path))
{ {
@ -460,33 +548,33 @@
if (_parentid == "-1") if (_parentid == "-1")
{ {
page.ParentId = null; _page.ParentId = null;
page.Path = Utilities.GetFriendlyUrl(_path); _page.Path = Utilities.GetFriendlyUrl(_path);
} }
else else
{ {
page.ParentId = Int32.Parse(_parentid); _page.ParentId = Int32.Parse(_parentid);
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == page.ParentId); Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == _page.ParentId);
if (parent.Path == string.Empty) if (parent.Path == string.Empty)
{ {
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path); _page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
} }
else else
{ {
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); _page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
} }
} }
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId); var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
if (_pages.Any(item => item.Path == page.Path && item.PageId != page.PageId)) if (_pages.Any(item => item.Path == _page.Path && item.PageId != _page.PageId))
{ {
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning); AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
return; return;
} }
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower())) if (_page.ParentId == null && Constants.ReservedRoutes.Contains(_page.Name.ToLower()))
{ {
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning); AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], _page.Name), MessageType.Warning);
return; return;
} }
@ -496,57 +584,59 @@
switch (_insert) switch (_insert)
{ {
case "<<": case "<<":
page.Order = 0; _page.Order = 0;
break; break;
case "<": case "<":
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid); child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
if (child != null) page.Order = child.Order - 1; if (child != null) _page.Order = child.Order - 1;
break; break;
case ">": case ">":
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid); child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
if (child != null) page.Order = child.Order + 1; if (child != null) _page.Order = child.Order + 1;
break; break;
case ">>": case ">>":
page.Order = int.MaxValue; _page.Order = int.MaxValue;
break; break;
} }
} }
page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation)); _page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation));
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable)); _page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
page.Url = _url; _page.Url = _url;
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable)); _page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
page.UserId = null;
// appearance // appearance
page.Title = _title; _page.Title = _title;
page.Icon = _icon ?? string.Empty; _page.Icon = _icon ?? string.Empty;
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty; _page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType) if (!string.IsNullOrEmpty(_page.ThemeType) && _page.ThemeType == PageState.Site.DefaultThemeType)
{ {
page.ThemeType = string.Empty; _page.ThemeType = string.Empty;
} }
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty; _page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType) if (!string.IsNullOrEmpty(_page.DefaultContainerType) && _page.DefaultContainerType == PageState.Site.DefaultContainerType)
{ {
page.DefaultContainerType = string.Empty; _page.DefaultContainerType = string.Empty;
} }
// page content // page content
page.HeadContent = _headcontent; _page.HeadContent = _headcontent;
page.BodyContent = _bodycontent; _page.BodyContent = _bodycontent;
// permissions // permissions
page.PermissionList = _permissionGrid.GetPermissionList(); if (_page.UserId == null)
{
_page.PermissionList = _permissionGrid.GetPermissionList();
}
page = await PageService.UpdatePageAsync(page); _page = await PageService.UpdatePageAsync(_page);
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId); await PageService.UpdatePageOrderAsync(_page.SiteId, _page.PageId, _page.ParentId);
if (_currentparentid == string.Empty) if (_currentparentid == string.Empty)
{ {
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, null); await PageService.UpdatePageOrderAsync(_page.SiteId, _page.PageId, null);
} }
else else
{ {
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, int.Parse(_currentparentid)); await PageService.UpdatePageOrderAsync(_page.SiteId, _page.PageId, int.Parse(_currentparentid));
} }
// update child paths // update child paths
@ -554,7 +644,7 @@
{ {
foreach (Page p in PageState.Pages.Where(item => item.Path.StartsWith(currentPath))) foreach (Page p in PageState.Pages.Where(item => item.Path.StartsWith(currentPath)))
{ {
p.Path = p.Path.Replace(currentPath, page.Path); p.Path = p.Path.Replace(currentPath, _page.Path);
await PageService.UpdatePageAsync(p); await PageService.UpdatePageAsync(p);
} }
} }
@ -564,14 +654,14 @@
await themeSettingsControl.UpdateSettings(); await themeSettingsControl.UpdateSettings();
} }
await logger.LogInformation("Page Saved {Page}", page); await logger.LogInformation("Page Saved {Page}", _page);
if (!string.IsNullOrEmpty(PageState.ReturnUrl)) if (!string.IsNullOrEmpty(PageState.ReturnUrl))
{ {
NavigationManager.NavigateTo(PageState.ReturnUrl); NavigationManager.NavigateTo(PageState.ReturnUrl);
} }
else else
{ {
NavigationManager.NavigateTo(NavigateUrl(page.Path)); NavigationManager.NavigateTo(NavigateUrl(_page.Path));
} }
} }
else else
@ -581,7 +671,7 @@
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message); await logger.LogError(ex, "Error Saving Page {Page} {Error}", _page, ex.Message);
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error); AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
} }
} }

View File

@ -5,7 +5,7 @@
@inject IStringLocalizer<Index> Localizer @inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@if (PageState.Pages != null) @if (PageState.Pages != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{ {
<ActionLink Action="Add" Text="Add Page" ResourceKey="AddPage" /> <ActionLink Action="Add" Text="Add Page" ResourceKey="AddPage" />

View File

@ -150,8 +150,8 @@
<data name="Container.Select" xml:space="preserve"> <data name="Container.Select" xml:space="preserve">
<value>Select Container</value> <value>Select Container</value>
</data> </data>
<data name="Error.Page.Initialize" xml:space="preserve"> <data name="Error.Page.Load" xml:space="preserve">
<value>Error Initializing Page</value> <value>Error Loading Page</value>
</data> </data>
<data name="Error.ChildPage.Load" xml:space="preserve"> <data name="Error.ChildPage.Load" xml:space="preserve">
<value>Error Loading Child Pages For Parent</value> <value>Error Loading Child Pages For Parent</value>

View File

@ -33,7 +33,7 @@
// obtained from https://cdnjs.com/libraries // obtained from https://cdnjs.com/libraries
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/css/bootstrap.min.css", Integrity = "sha512-XWTTruHZEYJsxV3W/lSXG1n3Q39YIWOstqvmFsdNEEQfHoZ6vm6E9GK2OrF6DSJSpIbRbi+Nn0WDPID9O7xB2Q==", CrossOrigin = "anonymous" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/css/bootstrap.min.css", Integrity = "sha512-XWTTruHZEYJsxV3W/lSXG1n3Q39YIWOstqvmFsdNEEQfHoZ6vm6E9GK2OrF6DSJSpIbRbi+Nn0WDPID9O7xB2Q==", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.bundle.min.js", Integrity = "sha512-9GacT4119eY3AcosfWtHMsT5JyZudrexyEVzTBWV3viP/YfB9e2pEy3N7WXL3SV6ASXpTU0vzzSxsbfsuUH4sQ==", CrossOrigin = "anonymous" } new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.bundle.min.js", Integrity = "sha512-9GacT4119eY3AcosfWtHMsT5JyZudrexyEVzTBWV3viP/YfB9e2pEy3N7WXL3SV6ASXpTU0vzzSxsbfsuUH4sQ==", CrossOrigin = "anonymous" }
}; };
} }

View File

@ -56,7 +56,7 @@
</div> </div>
<hr class="app-rule" /> <hr class="app-rule" />
} }
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) @if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{ {
<div class="row"> <div class="row">
<div class="col text-center"> <div class="col text-center">
@ -65,7 +65,10 @@
</div> </div>
<div class="row d-flex mb-2"> <div class="row d-flex mb-2">
<div class="col d-flex justify-content-between"> <div class="col d-flex justify-content-between">
<button type="button" class="btn btn-secondary col me-1" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Add"))>@SharedLocalizer["Add"]</button> @if (PageState.Page.UserId == null)
{
<button type="button" class="btn btn-secondary col me-1" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Add"))>@SharedLocalizer["Add"]</button>
}
<button type="button" class="btn btn-secondary col" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Edit"))>@SharedLocalizer["Edit"]</button> <button type="button" class="btn btn-secondary col" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Edit"))>@SharedLocalizer["Edit"]</button>
<button type="button" class="btn btn-danger col ms-1" @onclick="ConfirmDelete">@SharedLocalizer["Delete"]</button> <button type="button" class="btn btn-danger col ms-1" @onclick="ConfirmDelete">@SharedLocalizer["Delete"]</button>
</div> </div>
@ -520,7 +523,7 @@
switch (location) switch (location)
{ {
case "Add": case "Add":
url = EditUrl("admin/pages", module.ModuleId, location, $"returnurl={WebUtility.UrlEncode(PageState.Route.PathAndQuery)}"); url = EditUrl("admin/pages", module.ModuleId, location, $"id={PageState.Page.PageId}&returnurl={WebUtility.UrlEncode(PageState.Route.PathAndQuery)}");
break; break;
case "Edit": case "Edit":
url = EditUrl("admin/pages", module.ModuleId, location, $"id={PageState.Page.PageId}&returnurl={WebUtility.UrlEncode(PageState.Route.PathAndQuery)}"); url = EditUrl("admin/pages", module.ModuleId, location, $"id={PageState.Page.PageId}&returnurl={WebUtility.UrlEncode(PageState.Route.PathAndQuery)}");

View File

@ -10,6 +10,8 @@ using Oqtane.Enums;
using Oqtane.Extensions; using Oqtane.Extensions;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
using Oqtane.Repository; using Oqtane.Repository;
using Oqtane.Modules.Admin.Users;
using System.IO;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
@ -73,6 +75,26 @@ namespace Oqtane.Controllers
return pages; return pages;
} }
// GET api/<controller>/5
[HttpGet("{id}")]
public Page Get(int id)
{
var page = _pages.GetPage(id);
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, page.PermissionList))
{
page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return page;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {PageId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// GET api/<controller>/path/x?path=y // GET api/<controller>/path/x?path=y
[HttpGet("path/{siteid}")] [HttpGet("path/{siteid}")]
public Page Get(string path, int siteid) public Page Get(string path, int siteid)

View File

@ -141,7 +141,7 @@ namespace Oqtane.SiteTemplates
Path = "develop", Path = "develop",
Icon = "oi oi-wrench", Icon = "oi oi-wrench",
IsNavigation = true, IsNavigation = true,
IsPersonalizable = true, IsPersonalizable = false,
PermissionList = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Host, true), new Permission(PermissionNames.View, RoleNames.Host, true),
new Permission(PermissionNames.Edit, RoleNames.Host, true) new Permission(PermissionNames.Edit, RoleNames.Host, true)

View File

@ -694,6 +694,7 @@ namespace Oqtane.Repository
PermissionList = new List<Permission> PermissionList = new List<Permission>
{ {
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.View, RoleNames.Registered, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}, },
PageTemplateModules = new List<PageTemplateModule> PageTemplateModules = new List<PageTemplateModule>

View File

@ -87,6 +87,8 @@ namespace Oqtane.Repository
Theme.Resources = theme.Resources; Theme.Resources = theme.Resources;
Theme.Themes = theme.Themes; Theme.Themes = theme.Themes;
Theme.Containers = theme.Containers; Theme.Containers = theme.Containers;
Theme.ThemeSettingsType = theme.ThemeSettingsType;
Theme.ContainerSettingsType = theme.ContainerSettingsType;
Themes.Add(Theme); Themes.Add(Theme);
} }