Merge branch 'oqtane:dev' into refs

This commit is contained in:
Tonći Vatavuk 2022-02-08 19:55:34 +01:00 committed by GitHub
commit 4eed2193f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 963 additions and 722 deletions

View File

@ -62,10 +62,6 @@
{ {
SiteState.Alias = _installation.Alias; SiteState.Alias = _installation.Alias;
} }
else
{
_installation.Message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name";
}
_initialized = true; _initialized = true;
} }

View File

@ -110,7 +110,7 @@
</div> </div>
</div> </div>
} }
<NavLink class="btn btn-secondary" href="@NavigateUrl(PageState.Page.Path, "level=" + PageState.QueryString["level"] + "&function=" + PageState.QueryString["function"] + "&rows=" + PageState.QueryString["rows"] + "&page=" + PageState.QueryString["page"])">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@CloseUrl()">@SharedLocalizer["Cancel"]</NavLink>
@code { @code {
private bool _initialized = false; private bool _initialized = false;
@ -188,4 +188,16 @@
AddModuleMessage(Localizer["Error.Log.Load"], MessageType.Error); AddModuleMessage(Localizer["Error.Log.Load"], MessageType.Error);
} }
} }
private string CloseUrl()
{
if (!PageState.QueryString.ContainsKey("level"))
{
return NavigateUrl();
}
else
{
return NavigateUrl(PageState.Page.Path, "level=" + PageState.QueryString["level"] + "&function=" + PageState.QueryString["function"] + "&rows=" + PageState.QueryString["rows"] + "&page=" + PageState.QueryString["page"]);
}
}
} }

View File

@ -95,6 +95,12 @@
<input id="title" class="form-control" @bind="@_title" /> <input id="title" class="form-control" @bind="@_title" />
</div> </div>
</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"> <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="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
@ -156,220 +162,222 @@
</form> </form>
@code { @code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private List<Theme> _themeList; private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>(); private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>(); private List<ThemeControl> _containers = new List<ThemeControl>();
private List<Page> _pageList; private List<Page> _pageList;
private string _name; private string _name;
private string _title; private string _title;
private string _path = string.Empty; private string _meta;
private string _parentid = "-1"; private string _path = string.Empty;
private string _insert = ">>"; private string _parentid = "-1";
private List<Page> _children; private string _insert = ">>";
private int _childid = -1; private List<Page> _children;
private string _isnavigation = "True"; private int _childid = -1;
private string _isclickable = "True"; private string _isnavigation = "True";
private string _url; private string _isclickable = "True";
private string _ispersonalizable = "False"; private string _url;
private string _themetype = string.Empty; private string _ispersonalizable = "False";
private string _containertype = string.Empty; private string _themetype = string.Empty;
private string _icon = string.Empty; private string _containertype = string.Empty;
private string _permissions = string.Empty; private string _icon = string.Empty;
private PermissionGrid _permissionGrid; private string _permissions = string.Empty;
private Type _themeSettingsType; private PermissionGrid _permissionGrid;
private object _themeSettings; private Type _themeSettingsType;
private RenderFragment ThemeSettingsComponent { get; set; } private object _themeSettings;
private bool _refresh = false; private RenderFragment ThemeSettingsComponent { get; set; }
private ElementReference form; private bool _refresh = false;
private bool validated = false; private ElementReference form;
private bool validated = false;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try
{ {
_themeList = await ThemeService.GetThemesAsync(); _themeList = await ThemeService.GetThemesAsync();
_themes = ThemeService.GetThemeControls(_themeList); _themes = ThemeService.GetThemeControls(_themeList);
_themetype = PageState.Site.DefaultThemeType; _themetype = PageState.Site.DefaultThemeType;
_containers = ThemeService.GetContainerControls(_themeList, _themetype); _containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = PageState.Site.DefaultContainerType; _containertype = PageState.Site.DefaultContainerType;
_pageList = PageState.Pages; _pageList = PageState.Pages;
_children = PageState.Pages.Where(item => item.ParentId == null).ToList(); _children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_permissions = string.Empty; _permissions = string.Empty;
ThemeSettings(); ThemeSettings();
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Initializing Page {Error}", ex.Message); await logger.LogError(ex, "Error Initializing Page {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Page.Initialize"], MessageType.Error); AddModuleMessage(Localizer["Error.Page.Initialize"], MessageType.Error);
} }
} }
private async void ParentChanged(ChangeEventArgs e) private async void ParentChanged(ChangeEventArgs e)
{ {
try try
{ {
_parentid = (string)e.Value; _parentid = (string)e.Value;
_children = new List<Page>(); _children = new List<Page>();
if (_parentid == "-1") if (_parentid == "-1")
{ {
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null)) foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{ {
_children.Add(p); _children.Add(p);
} }
} }
} }
else else
{ {
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid))) foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{ {
_children.Add(p); _children.Add(p);
} }
} }
} }
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message); await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error); AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
} }
} }
private async void ThemeChanged(ChangeEventArgs e) private async void ThemeChanged(ChangeEventArgs e)
{ {
try try
{ {
_themetype = (string)e.Value; _themetype = (string)e.Value;
_containers = ThemeService.GetContainerControls(_themeList, _themetype); _containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = "-"; _containertype = "-";
ThemeSettings(); ThemeSettings();
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message); await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error); AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error);
} }
} }
private void ThemeSettings() private void ThemeSettings()
{ {
_themeSettingsType = null; _themeSettingsType = null;
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype))); var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType)) if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
{ {
_themeSettingsType = Type.GetType(theme.ThemeSettingsType); _themeSettingsType = Type.GetType(theme.ThemeSettingsType);
if (_themeSettingsType != null) if (_themeSettingsType != null)
{ {
ThemeSettingsComponent = builder => ThemeSettingsComponent = builder =>
{ {
builder.OpenComponent(0, _themeSettingsType); builder.OpenComponent(0, _themeSettingsType);
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); }); builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
builder.CloseComponent(); builder.CloseComponent();
}; };
} }
_refresh = true; _refresh = true;
} }
} }
private async Task SavePage() private async Task SavePage()
{ {
validated = true; validated = true;
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
if (await interop.FormValid(form)) if (await interop.FormValid(form))
{ {
Page page = null; Page page = null;
try try
{ {
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-") if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
{ {
page = new Page(); page = new Page();
page.SiteId = PageState.Page.SiteId; page.SiteId = PageState.Page.SiteId;
page.Name = _name; page.Name = _name;
page.Title = _title; page.Title = _title;
if (string.IsNullOrEmpty(_path)) if (string.IsNullOrEmpty(_path))
{ {
_path = _name; _path = _name;
} }
if (_path.Contains("/")) if (_path.Contains("/"))
{ {
_path = _path.Substring(_path.LastIndexOf("/") + 1); _path = _path.Substring(_path.LastIndexOf("/") + 1);
} }
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);
var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault(); var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault();
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);
} }
} }
if(PagePathIsDeleted(page.Path, page.SiteId, _pageList)) if(PagePathIsDeleted(page.Path, page.SiteId, _pageList))
{ {
AddModuleMessage(string.Format(Localizer["Message.Page.Deleted"], _path), MessageType.Warning); AddModuleMessage(string.Format(Localizer["Message.Page.Deleted"], _path), MessageType.Warning);
return; return;
} }
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList)) if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
{ {
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning); AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
return; return;
} }
Page child; Page child;
switch (_insert) switch (_insert)
{ {
case "<<": case "<<":
page.Order = 0; page.Order = 0;
break; break;
case "<": case "<":
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault(); child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
page.Order = child.Order - 1; page.Order = child.Order - 1;
break; break;
case ">": case ">":
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault(); child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
page.Order = child.Order + 1; page.Order = child.Order + 1;
break; break;
case ">>": case ">>":
page.Order = int.MaxValue; page.Order = int.MaxValue;
break; break;
} }
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation)); page.IsNavigation = (_isnavigation == null ? true : 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.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.Icon = (_icon == null ? string.Empty : _icon); page.Icon = (_icon == null ? string.Empty : _icon);
page.Permissions = _permissionGrid.GetPermissions(); page.Permissions = _permissionGrid.GetPermissions();
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable)); page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
page.UserId = null; page.UserId = null;
page.Meta = _meta;
page = await PageService.AddPageAsync(page); page = await PageService.AddPageAsync(page);
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId); await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);

View File

@ -102,6 +102,12 @@
<input id="title" class="form-control" @bind="@_title" maxlength="200"/> <input id="title" class="form-control" @bind="@_title" maxlength="200"/>
</div> </div>
</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"> <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="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
@ -200,6 +206,7 @@
private int _pageId; private int _pageId;
private string _name; private string _name;
private string _title; private string _title;
private string _meta;
private string _path; private string _path;
private string _currentparentid; private string _currentparentid;
private string _parentid = "-1"; private string _parentid = "-1";
@ -243,6 +250,7 @@
{ {
_name = page.Name; _name = page.Name;
_title = page.Title; _title = page.Title;
_meta = page.Meta;
_path = page.Path; _path = page.Path;
_pageModules = PageState.Modules.Where(m => m.PageId == page.PageId && m.IsDeleted == false).ToList(); _pageModules = PageState.Modules.Where(m => m.PageId == page.PageId && m.IsDeleted == false).ToList();
@ -313,77 +321,77 @@
_pageModules.RemoveAll(item => item.PageModuleId == pagemodule.PageModuleId); _pageModules.RemoveAll(item => item.PageModuleId == pagemodule.PageModuleId);
StateHasChanged(); StateHasChanged();
NavigationManager.NavigateTo(NavigationManager.Uri + "&tab=PageModules"); NavigationManager.NavigateTo(NavigationManager.Uri + "&tab=PageModules");
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Deleting Module {Title} {Error}", module.Title, ex.Message); await logger.LogError(ex, "Error Deleting Module {Title} {Error}", module.Title, ex.Message);
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error); AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
} }
} }
private async void ParentChanged(ChangeEventArgs e) private async void ParentChanged(ChangeEventArgs e)
{ {
try try
{ {
_parentid = (string)e.Value; _parentid = (string)e.Value;
_children = new List<Page>(); _children = new List<Page>();
if (_parentid == "-1") if (_parentid == "-1")
{ {
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null)) foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{ {
_children.Add(p); _children.Add(p);
} }
} }
} }
else else
{ {
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid))) foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{ {
_children.Add(p); _children.Add(p);
} }
} }
} }
if (_parentid == _currentparentid) if (_parentid == _currentparentid)
{ {
_insert = "="; _insert = "=";
} }
else else
{ {
_insert = ">>"; _insert = ">>";
} }
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message); await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error); AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
} }
} }
private async void ThemeChanged(ChangeEventArgs e) private async void ThemeChanged(ChangeEventArgs e)
{ {
try try
{ {
_themetype = (string)e.Value; _themetype = (string)e.Value;
_containers = ThemeService.GetContainerControls(_themeList, _themetype); _containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = "-"; _containertype = "-";
ThemeSettings(); ThemeSettings();
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message); await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error); AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error);
} }
} }
private void ThemeSettings() private void ThemeSettings()
{ {
_themeSettingsType = null; _themeSettingsType = null;
if (PageState.QueryString.ContainsKey("cp")) // can only be displayed if invoked from Control Panel if (PageState.QueryString.ContainsKey("cp")) // can only be displayed if invoked from Control Panel
{ {
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype))); var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
@ -402,97 +410,98 @@
_refresh = true; _refresh = true;
} }
} }
} }
private async Task SavePage() private async Task SavePage()
{ {
validated = true; validated = true;
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
if (await interop.FormValid(form)) if (await interop.FormValid(form))
{ {
Page page = null; Page page = null;
try try
{ {
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-") if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
{ {
page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
string currentPath = page.Path; string currentPath = page.Path;
page.Name = _name; page.Name = _name;
page.Title = _title; page.Title = _title;
if (string.IsNullOrEmpty(_path)) if (string.IsNullOrEmpty(_path))
{ {
_path = _name; _path = _name;
} }
if (_path.Contains("/")) if (_path.Contains("/"))
{ {
_path = _path.Substring(_path.LastIndexOf("/") + 1); _path = _path.Substring(_path.LastIndexOf("/") + 1);
} }
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);
} }
} }
if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList)) if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList))
{ {
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning); AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
return; return;
} }
if (_insert != "=") if (_insert != "=")
{ {
Page child; Page child;
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.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.Icon = _icon ?? string.Empty; page.Icon = _icon ?? string.Empty;
page.Permissions = _permissionGrid.GetPermissions(); page.Permissions = _permissionGrid.GetPermissions();
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable)); page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
page.UserId = null; page.UserId = null;
page.Meta = _meta;
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);

View File

@ -25,7 +25,7 @@
<th>@Localizer["DeletedOn"]</th> <th>@Localizer["DeletedOn"]</th>
</Header> </Header>
<Row> <Row>
<td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-info" title="Restore">Restore</button></td> <td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-success" title="Restore">Restore</button></td>
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td> <td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
<td>@context.Name</td> <td>@context.Name</td>
<td>@context.DeletedBy</td> <td>@context.DeletedBy</td>
@ -56,7 +56,7 @@
<th>@Localizer["DeletedOn"]</th> <th>@Localizer["DeletedOn"]</th>
</Header> </Header>
<Row> <Row>
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-info" title="Restore">@Localizer["Restore"]</button></td> <td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td> <td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
<td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td> <td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td>
<td>@context.Title</td> <td>@context.Title</td>

View File

@ -70,7 +70,7 @@
</div> </div>
} }
<NavLink class="btn btn-secondary" href="@NavigateUrl(PageState.Page.Path, "type=" + PageState.QueryString["type"] + "&days=" + PageState.QueryString["days"] + "&page=" + PageState.QueryString["page"])">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@CloseUrl()">@SharedLocalizer["Cancel"]</NavLink>
@code { @code {
private bool _initialized = false; private bool _initialized = false;
@ -125,4 +125,16 @@
AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error); AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error);
} }
} }
private string CloseUrl()
{
if (!PageState.QueryString.ContainsKey("type"))
{
return NavigateUrl();
}
else
{
return NavigateUrl(PageState.Page.Path, "type=" + PageState.QueryString["type"] + "&days=" + PageState.QueryString["days"] + "&page=" + PageState.QueryString["page"]);
}
}
} }

View File

@ -71,11 +71,11 @@
</div> </div>
@if (ReadOnly) @if (ReadOnly)
{ {
<textarea class="form-control" placeholder="@Placeholder" @bind="@_content" rows="10" readonly></textarea> <textarea class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10" readonly></textarea>
} }
else else
{ {
<textarea class="form-control" placeholder="@Placeholder" @bind="@_content" rows="10"></textarea> <textarea class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
} }
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>
@ -83,110 +83,121 @@
</div> </div>
@code { @code {
private ElementReference _editorElement; private ElementReference _editorElement;
private ElementReference _toolBar; private ElementReference _toolBar;
private bool _filemanagervisible = false; private bool _filemanagervisible = false;
private FileManager _fileManager; private FileManager _fileManager;
private string _content = string.Empty; private string _richhtml = string.Empty;
private string _original = string.Empty; private string _originalrichhtml = string.Empty;
private string _message = string.Empty; private string _rawhtml = string.Empty;
private string _originalrawhtml = string.Empty;
private string _message = string.Empty;
[Parameter] [Parameter]
public string Content { get; set; } public string Content { get; set; }
[Parameter] [Parameter]
public bool ReadOnly { get; set; } = false; public bool ReadOnly { get; set; } = false;
[Parameter] [Parameter]
public string Placeholder { get; set; } = "Enter Your Content..."; public string Placeholder { get; set; } = "Enter Your Content...";
// parameters only applicable to rich text editor // parameters only applicable to rich text editor
[Parameter] [Parameter]
public RenderFragment ToolbarContent { get; set; } public RenderFragment ToolbarContent { get; set; }
[Parameter] [Parameter]
public string Theme { get; set; } = "snow"; public string Theme { get; set; } = "snow";
[Parameter] [Parameter]
public string DebugLevel { get; set; } = "info"; public string DebugLevel { get; set; } = "info";
[Parameter] [Parameter]
public bool AllowFileManagement { get; set; } = true; public bool AllowFileManagement { get; set; } = true;
public override List<Resource> Resources => new List<Resource>() public override List<Resource> Resources => new List<Resource>()
{ {
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" }, new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" }, new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" } new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
}; };
protected override async Task OnParametersSetAsync() protected override void OnParametersSet()
{ {
_content = Content; // raw HTML _richhtml = Content;
await RefreshRichText(); _rawhtml = Content;
} _originalrawhtml = _rawhtml; // preserve for comparison later
}
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
if (firstRender) await base.OnAfterRenderAsync(firstRender);
{
var interop = new RichTextEditorInterop(JSRuntime);
if (firstRender)
{
await interop.CreateEditor(
_editorElement,
_toolBar,
ReadOnly,
Placeholder,
Theme,
DebugLevel);
await interop.LoadEditorContent(_editorElement, _richhtml);
// preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor)
_originalrichhtml = await interop.GetHtml(_editorElement);
}
else
{
await interop.LoadEditorContent(_editorElement, _richhtml);
}
}
public void CloseFileManager()
{
_filemanagervisible = false;
_message = string.Empty;
StateHasChanged();
}
public void RefreshRichText()
{
_richhtml = _rawhtml;
StateHasChanged();
}
public async Task RefreshRawHtml()
{
var interop = new RichTextEditorInterop(JSRuntime);
_rawhtml = await interop.GetHtml(_editorElement);
StateHasChanged();
}
public async Task<string> GetHtml()
{
// evaluate raw html content as first priority
if (_rawhtml != _originalrawhtml)
{
return _rawhtml;
}
else
{
// return rich text content if it has changed
var interop = new RichTextEditorInterop(JSRuntime); var interop = new RichTextEditorInterop(JSRuntime);
var richhtml = await interop.GetHtml(_editorElement);
await base.OnAfterRenderAsync(firstRender); if (richhtml != _originalrichhtml)
{
await interop.CreateEditor( return richhtml;
_editorElement, }
_toolBar, else
ReadOnly, {
Placeholder, // return original raw html content
Theme, return _originalrawhtml;
DebugLevel); }
}
await interop.LoadEditorContent(_editorElement, Content);
_content = Content; // raw HTML
// preserve a copy of the rich text content ( Quill sanitizes content so we need to retrieve it from the editor )
_original = await interop.GetHtml(_editorElement);
}
}
public void CloseFileManager()
{
_filemanagervisible = false;
_message = string.Empty;
StateHasChanged();
}
public async Task RefreshRichText()
{
var interop = new RichTextEditorInterop(JSRuntime);
await interop.LoadEditorContent(_editorElement, _content);
}
public async Task RefreshRawHtml()
{
var interop = new RichTextEditorInterop(JSRuntime);
_content = await interop.GetHtml(_editorElement);
StateHasChanged();
}
public async Task<string> GetHtml()
{
// get rich text content
var interop = new RichTextEditorInterop(JSRuntime);
string content = await interop.GetHtml(_editorElement);
if (_original != content)
{
// rich text content has changed - return it
return content;
}
else
{
// return raw html content
return _content;
}
} }
public async Task InsertImage() public async Task InsertImage()
@ -212,23 +223,4 @@
} }
StateHasChanged(); StateHasChanged();
} }
// other rich text editor methods which can be used by developers
public async Task<string> GetText()
{
var interop = new RichTextEditorInterop(JSRuntime);
return await interop.GetText(_editorElement);
}
public async Task<string> GetContent()
{
var interop = new RichTextEditorInterop(JSRuntime);
return await interop.GetContent(_editorElement);
}
public async Task EnableEditor(bool mode)
{
var interop = new RichTextEditorInterop(JSRuntime);
await interop.EnableEditor(_editorElement, mode);
}
} }

View File

@ -9,95 +9,186 @@
@inject IStringLocalizer<Edit> Localizer @inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@if (_content != null) <TabStrip>
{ <TabPanel Name="Edit" Heading="Edit" ResourceKey="Edit">
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" @ref="@RichTextEditorHtml"></RichTextEditor> @if (_content != null)
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button> {
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" @ref="@RichTextEditorHtml"></RichTextEditor>
@if (!string.IsNullOrEmpty(_content)) <br />
{ <button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
<br /> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br /> @if (!string.IsNullOrEmpty(_content))
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo> {
} <br />
} <br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
}
}
</TabPanel>
<TabPanel Name="Versions" Heading="Versions" ResourceKey="Versions">
<Pager Items="@_htmltexts">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["CreatedOn"]</th>
<th>@SharedLocalizer["CreatedBy"]</th>
</Header>
<Row>
<td><ActionLink Action="View" Security="SecurityAccessLevel.Edit" OnClick="@(async () => await View(context))" ResourceKey="View" /></td>
<td><ActionDialog Header="Restore Version" Message="@string.Format(Localizer["Confirm.Restore"], context.CreatedOn)" Action="Restore" Security="SecurityAccessLevel.Edit" Class="btn btn-success" OnClick="@(async () => await Restore(context))" ResourceKey="Restore" /></td>
<td><ActionDialog Header="Delete Version" Message="@string.Format(Localizer["Confirm.Delete"], context.CreatedOn)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" ResourceKey="Delete" /></td>
<td>@context.CreatedOn</td>
<td>@context.CreatedBy</td>
</Row>
</Pager>
@((MarkupString)_view)
</TabPanel>
</TabStrip>
@code { @code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Edit Html/Text"; public override string Title => "Edit Html/Text";
public override List<Resource> Resources => new List<Resource>() public override List<Resource> Resources => new List<Resource>()
{ {
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" } new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" }
}; };
private RichTextEditor RichTextEditorHtml; private RichTextEditor RichTextEditorHtml;
private bool _allowfilemanagement; private bool _allowfilemanagement;
private string _content = null; private string _content = null;
private string _createdby; private string _createdby;
private DateTime _createdon; private DateTime _createdon;
private string _modifiedby; private string _modifiedby;
private DateTime _modifiedon; private DateTime _modifiedon;
private List<Models.HtmlText> _htmltexts;
private string _view = "";
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try
{ {
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true")); _allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
await LoadContent();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Content {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error);
}
}
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId); private async Task LoadContent()
if (htmltext != null) {
{ var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
_content = htmltext.Content; if (htmltext != null)
_content = Utilities.FormatContent(_content, PageState.Alias, "render"); {
_createdby = htmltext.CreatedBy; _content = htmltext.Content;
_createdon = htmltext.CreatedOn; _content = Utilities.FormatContent(_content, PageState.Alias, "render");
_modifiedby = htmltext.ModifiedBy; _createdby = htmltext.CreatedBy;
_modifiedon = htmltext.ModifiedOn; _createdon = htmltext.CreatedOn;
} _modifiedby = htmltext.ModifiedBy;
else _modifiedon = htmltext.ModifiedOn;
{ }
_content = string.Empty; else
} {
} _content = string.Empty;
catch (Exception ex) }
{
await logger.LogError(ex, "Error Loading Content {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error);
}
}
private async Task SaveContent() _htmltexts = await HtmlTextService.GetHtmlTextsAsync(ModuleState.ModuleId);
{ _htmltexts = _htmltexts.OrderByDescending(item => item.CreatedOn).ToList();
string content = await RichTextEditorHtml.GetHtml();
content = Utilities.FormatContent(content, PageState.Alias, "save");
try _view = "";
{ }
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
if (htmltext != null)
{
htmltext.Content = content;
await HtmlTextService.UpdateHtmlTextAsync(htmltext);
}
else
{
htmltext = new HtmlText();
htmltext.ModuleId = ModuleState.ModuleId;
htmltext.Content = content;
await HtmlTextService.AddHtmlTextAsync(htmltext);
}
await logger.LogInformation("Content Saved {HtmlText}", htmltext); private async Task SaveContent()
NavigationManager.NavigateTo(NavigateUrl()); {
} string content = await RichTextEditorHtml.GetHtml();
catch (Exception ex) content = Utilities.FormatContent(content, PageState.Alias, "save");
{
await logger.LogError(ex, "Error Saving Content {Error}", ex.Message); try
AddModuleMessage(Localizer["Error.Content.Save"], MessageType.Error); {
} var htmltext = new HtmlText();
} htmltext.ModuleId = ModuleState.ModuleId;
htmltext.Content = content;
await HtmlTextService.AddHtmlTextAsync(htmltext);
await logger.LogInformation("Content Saved {HtmlText}", htmltext);
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Content {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Content.Save"], MessageType.Error);
}
}
private async Task View(Models.HtmlText htmltext)
{
try
{
htmltext = await HtmlTextService.GetHtmlTextAsync(htmltext.HtmlTextId, htmltext.ModuleId);
if (htmltext != null)
{
_view = htmltext.Content;
_view = Utilities.FormatContent(_view, PageState.Alias, "render");
StateHasChanged();
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Viewing Content {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Content.View"], MessageType.Error);
}
}
private async Task Restore(Models.HtmlText htmltext)
{
try
{
htmltext = await HtmlTextService.GetHtmlTextAsync(htmltext.HtmlTextId, ModuleState.ModuleId);
if (htmltext != null)
{
var content = htmltext.Content;
htmltext = new HtmlText();
htmltext.ModuleId = ModuleState.ModuleId;
htmltext.Content = content;
await HtmlTextService.AddHtmlTextAsync(htmltext);
await logger.LogInformation("Content Restored {HtmlText}", htmltext);
AddModuleMessage(Localizer["Message.Content.Restored"], MessageType.Success);
await LoadContent();
StateHasChanged();
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Restoring Content {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Content.Restore"], MessageType.Error);
}
}
private async Task Delete(Models.HtmlText htmltext)
{
try
{
htmltext = await HtmlTextService.GetHtmlTextAsync(htmltext.HtmlTextId, ModuleState.ModuleId);
if (htmltext != null)
{
await HtmlTextService.DeleteHtmlTextAsync(htmltext.HtmlTextId, htmltext.ModuleId);
await logger.LogInformation("Content Deleted {HtmlText}", htmltext);
AddModuleMessage(Localizer["Message.Content.Deleted"], MessageType.Success);
await LoadContent();
StateHasChanged();
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Content {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Content.Delete"], MessageType.Error);
}
}
} }

View File

@ -1,3 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Oqtane.Documentation; using Oqtane.Documentation;
@ -13,24 +15,29 @@ namespace Oqtane.Modules.HtmlText.Services
private string ApiUrl => CreateApiUrl("HtmlText"); private string ApiUrl => CreateApiUrl("HtmlText");
public async Task<List<Models.HtmlText>> GetHtmlTextsAsync(int moduleId)
{
return await GetJsonAsync<List<Models.HtmlText>>(CreateAuthorizationPolicyUrl($"{ApiUrl}?moduleid={moduleId}", EntityNames.Module, moduleId));
}
public async Task<Models.HtmlText> GetHtmlTextAsync(int moduleId) public async Task<Models.HtmlText> GetHtmlTextAsync(int moduleId)
{ {
return await GetJsonAsync<Models.HtmlText>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", EntityNames.Module, moduleId)); return await GetJsonAsync<Models.HtmlText>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", EntityNames.Module, moduleId));
} }
public async Task<Models.HtmlText> GetHtmlTextAsync(int htmlTextId, int moduleId)
{
return await GetJsonAsync<Models.HtmlText>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlTextId}/{moduleId}", EntityNames.Module, moduleId));
}
public async Task AddHtmlTextAsync(Models.HtmlText htmlText) public async Task AddHtmlTextAsync(Models.HtmlText htmlText)
{ {
await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", EntityNames.Module, htmlText.ModuleId), htmlText); await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", EntityNames.Module, htmlText.ModuleId), htmlText);
} }
public async Task UpdateHtmlTextAsync(Models.HtmlText htmlText) public async Task DeleteHtmlTextAsync(int htmlTextId, int moduleId)
{ {
await PutJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlText.HtmlTextId}", EntityNames.Module, htmlText.ModuleId), htmlText); await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlTextId}/{moduleId}", EntityNames.Module, moduleId));
}
public async Task DeleteHtmlTextAsync(int moduleId)
{
await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", EntityNames.Module, moduleId));
} }
} }
} }

View File

@ -1,19 +1,20 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Oqtane.Documentation; using Oqtane.Documentation;
using Oqtane.Modules.HtmlText.Models;
namespace Oqtane.Modules.HtmlText.Services namespace Oqtane.Modules.HtmlText.Services
{ {
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")] [PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
public interface IHtmlTextService public interface IHtmlTextService
{ {
Task<Models.HtmlText> GetHtmlTextAsync(int ModuleId); Task<List<Models.HtmlText>> GetHtmlTextsAsync(int moduleId);
Task<Models.HtmlText> GetHtmlTextAsync(int moduleId);
Task<Models.HtmlText> GetHtmlTextAsync(int htmlTextId, int moduleId);
Task AddHtmlTextAsync(Models.HtmlText htmltext); Task AddHtmlTextAsync(Models.HtmlText htmltext);
Task UpdateHtmlTextAsync(Models.HtmlText htmltext); Task DeleteHtmlTextAsync(int htmlTextId, int moduleId);
Task DeleteHtmlTextAsync(int ModuleId);
} }
} }

View File

@ -231,4 +231,10 @@
<data name="Message.Page.Deleted" xml:space="preserve"> <data name="Message.Page.Deleted" xml:space="preserve">
<value>A page with path {0} already exists for the selected parent page in the Recycle Bin. Either recover the page or remove from the Recycle Bin and create it again.</value> <value>A page with path {0} already exists for the selected parent page in the Recycle Bin. Either recover the page or remove from the Recycle Bin and create it again.</value>
</data> </data>
<data name="Meta.HelpText" xml:space="preserve">
<value>Optionally enter meta tags (in exactly the form you want them to be included in the page output).</value>
</data>
<data name="Meta.Text" xml:space="preserve">
<value>Meta:</value>
</data>
</root> </root>

View File

@ -264,4 +264,10 @@
<data name="Clickable.Text" xml:space="preserve"> <data name="Clickable.Text" xml:space="preserve">
<value>Clickable?</value> <value>Clickable?</value>
</data> </data>
<data name="Meta.HelpText" xml:space="preserve">
<value>Optionally enter meta tags (in exactly the form you want them to be included in the page output).</value>
</data>
<data name="Meta.Text" xml:space="preserve">
<value>Meta:</value>
</data>
</root> </root>

View File

@ -117,10 +117,52 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Confirm.Delete" xml:space="preserve">
<value>Are You Sure You Wish To Delete The {0} Version?</value>
</data>
<data name="Confirm.Restore" xml:space="preserve">
<value>Are You Sure You Wish To Restore The {0} Version?</value>
</data>
<data name="CreatedBy" xml:space="preserve">
<value>Created By</value>
</data>
<data name="CreatedOn" xml:space="preserve">
<value>Created On</value>
</data>
<data name="Delete.Header" xml:space="preserve">
<value>Delete Version</value>
</data>
<data name="Delete.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="Error.Content.Delete" xml:space="preserve">
<value>Error Deleting Version</value>
</data>
<data name="Error.Content.Load" xml:space="preserve"> <data name="Error.Content.Load" xml:space="preserve">
<value>An Error Occurred Loading Content</value> <value>An Error Occurred Loading Content</value>
</data> </data>
<data name="Error.Content.Restore" xml:space="preserve">
<value>Error Restoring Version</value>
</data>
<data name="Error.Content.Save" xml:space="preserve"> <data name="Error.Content.Save" xml:space="preserve">
<value>An Error Occurred Saving Content</value> <value>An Error Occurred Saving Content</value>
</data> </data>
<data name="Error.Content.View" xml:space="preserve">
<value>Error Viewing Version</value>
</data>
<data name="Message.Content.Deleted" xml:space="preserve">
<value>Version Deleted</value>
</data>
<data name="Message.Content.Restored" xml:space="preserve">
<value>Version Restored</value>
</data>
<data name="Restore.Header" xml:space="preserve">
<value>Restore Version</value>
</data>
<data name="Restore.Text" xml:space="preserve">
<value>Restore</value>
</data>
<data name="View.Text" xml:space="preserve">
<value>View</value>
</data>
</root> </root>

View File

@ -17,18 +17,17 @@ using Oqtane.Infrastructure;
using Oqtane.Modules; using Oqtane.Modules;
using Oqtane.Repository; using Oqtane.Repository;
using Oqtane.Security; using Oqtane.Security;
using Oqtane.Services;
using Oqtane.Shared; using Oqtane.Shared;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
{ {
public static class OqtaneServiceCollectionExtensions public static class OqtaneServiceCollectionExtensions
{ {
public static IServiceCollection AddOqtane(this IServiceCollection services, Runtime runtime, string[] supportedCultures) public static IServiceCollection AddOqtane(this IServiceCollection services, string[] supportedCultures)
{ {
LoadAssemblies(); LoadAssemblies();
LoadSatelliteAssemblies(supportedCultures); LoadSatelliteAssemblies(supportedCultures);
services.AddOqtaneServices(runtime); services.AddOqtaneServices();
return services; return services;
} }
@ -190,7 +189,7 @@ namespace Microsoft.Extensions.DependencyInjection
return services; return services;
} }
private static IServiceCollection AddOqtaneServices(this IServiceCollection services, Runtime runtime) private static IServiceCollection AddOqtaneServices(this IServiceCollection services)
{ {
if (services is null) if (services is null)
{ {
@ -229,14 +228,6 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
startup.ConfigureServices(services); startup.ConfigureServices(services);
} }
if (runtime == Runtime.Server)
{
// register client startup services if running on server
assembly.GetInstances<IClientStartup>()
.ToList()
.ForEach(x => x.ConfigureServices(services));
}
} }
return services; return services;
} }

View File

@ -28,14 +28,14 @@ namespace Oqtane.Infrastructure
{ {
public class DatabaseManager : IDatabaseManager public class DatabaseManager : IDatabaseManager
{ {
private readonly IConfigurationRoot _config; private readonly IConfigManager _config;
private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly IWebHostEnvironment _environment; private readonly IWebHostEnvironment _environment;
private readonly IMemoryCache _cache; private readonly IMemoryCache _cache;
private readonly IConfigManager _configManager; private readonly IConfigManager _configManager;
private readonly ILogger<DatabaseManager> _filelogger; private readonly ILogger<DatabaseManager> _filelogger;
public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment, IMemoryCache cache, IConfigManager configManager, ILogger<DatabaseManager> filelogger) public DatabaseManager(IConfigManager config, IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment, IMemoryCache cache, IConfigManager configManager, ILogger<DatabaseManager> filelogger)
{ {
_config = config; _config = config;
_serviceScopeFactory = serviceScopeFactory; _serviceScopeFactory = serviceScopeFactory;

View File

@ -125,6 +125,9 @@ namespace Oqtane.Infrastructure
case "ref": // ref/net*/... case "ref": // ref/net*/...
filename = ExtractFile(entry, Path.Combine(binPath, "ref"), 2); filename = ExtractFile(entry, Path.Combine(binPath, "ref"), 2);
break; break;
case "refs": // refs/net*/...
filename = ExtractFile(entry, Path.Combine(binPath, "refs"), 2);
break;
} }
if (filename != "") if (filename != "")

View File

@ -14,18 +14,18 @@ namespace Oqtane.Infrastructure
public class LogManager : ILogManager public class LogManager : ILogManager
{ {
private readonly ILogRepository _logs; private readonly ILogRepository _logs;
private readonly IConfigurationRoot _config; private readonly ITenantManager _tenantManager;
private readonly IConfigManager _config;
private readonly IUserPermissions _userPermissions; private readonly IUserPermissions _userPermissions;
private readonly IHttpContextAccessor _accessor; private readonly IHttpContextAccessor _accessor;
private readonly Alias _alias;
public LogManager(ILogRepository logs, ITenantManager tenantManager, IConfigurationRoot config, IUserPermissions userPermissions, IHttpContextAccessor accessor) public LogManager(ILogRepository logs, ITenantManager tenantManager, IConfigManager config, IUserPermissions userPermissions, IHttpContextAccessor accessor)
{ {
_logs = logs; _logs = logs;
_tenantManager = tenantManager;
_config = config; _config = config;
_userPermissions = userPermissions; _userPermissions = userPermissions;
_accessor = accessor; _accessor = accessor;
_alias = tenantManager.GetAlias();
} }
public void Log(LogLevel level, object @class, LogFunction function, string message, params object[] args) public void Log(LogLevel level, object @class, LogFunction function, string message, params object[] args)
@ -48,9 +48,13 @@ namespace Oqtane.Infrastructure
Log log = new Log(); Log log = new Log();
log.SiteId = siteId; log.SiteId = siteId;
if (log.SiteId == -1 && _alias != null) if (log.SiteId == -1)
{ {
log.SiteId = _alias.SiteId; var alias = _tenantManager.GetAlias();
if (alias != null)
{
log.SiteId = alias.SiteId;
}
} }
if (log.SiteId == -1) return; // logs must be site specific if (log.SiteId == -1) return; // logs must be site specific

View File

@ -1,6 +1,5 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
namespace Oqtane.Infrastructure namespace Oqtane.Infrastructure
{ {
@ -16,8 +15,8 @@ namespace Oqtane.Infrastructure
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
// check if framework is installed // check if framework is installed
var config = context.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration; var config = context.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
if (!string.IsNullOrEmpty(config.GetConnectionString("DefaultConnection"))) if (config.IsInstalled())
{ {
// get alias // get alias
var tenantManager = context.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager; var tenantManager = context.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager;

View File

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.03.00.03.01")]
public class AddPageMeta : MultiDatabaseMigration
{
public AddPageMeta(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
var pageEntityBuilder = new PageEntityBuilder(migrationBuilder, ActiveDatabase);
pageEntityBuilder.AddStringColumn("Meta", 2000, true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
var pageEntityBuilder = new PageEntityBuilder(migrationBuilder, ActiveDatabase);
pageEntityBuilder.DropColumn("Meta");
}
}
}

View File

@ -8,6 +8,8 @@ using Oqtane.Infrastructure;
using Oqtane.Controllers; using Oqtane.Controllers;
using System.Net; using System.Net;
using Oqtane.Documentation; using Oqtane.Documentation;
using System.Collections.Generic;
using System.Linq;
namespace Oqtane.Modules.HtmlText.Controllers namespace Oqtane.Modules.HtmlText.Controllers
{ {
@ -22,18 +24,60 @@ namespace Oqtane.Modules.HtmlText.Controllers
_htmlText = htmlText; _htmlText = htmlText;
} }
// GET api/<controller>/5 // GET: api/<controller>?moduleid=x
[HttpGet("{id}")] [HttpGet]
[Authorize(Policy = PolicyNames.ViewModule)] [Authorize(Roles = RoleNames.Registered)]
public Models.HtmlText Get(int id) public IEnumerable<Models.HtmlText> Get(string moduleId)
{ {
if (AuthEntityId(EntityNames.Module) == id) if (int.TryParse(moduleId, out int ModuleId) && AuthEntityId(EntityNames.Module) == ModuleId)
{
return _htmlText.GetHtmlTexts(ModuleId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Html/Text Get Attempt {ModuleId}", moduleId);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// GET api/<controller>/5
[HttpGet("{moduleid}")]
[Authorize(Policy = PolicyNames.ViewModule)]
public Models.HtmlText Get(int moduleId)
{
if (AuthEntityId(EntityNames.Module) == moduleId)
{
var htmltexts = _htmlText.GetHtmlTexts(moduleId);
if (htmltexts != null && htmltexts.Any())
{
return htmltexts.OrderByDescending(item => item.CreatedOn).First();
}
else
{
return null;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Html/Text Get Attempt {ModuleId}", moduleId);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// GET api/<controller>/5/6
[HttpGet("{id}/{moduleid}")]
[Authorize(Policy = PolicyNames.ViewModule)]
public Models.HtmlText Get(int id, int moduleId)
{
if (AuthEntityId(EntityNames.Module) == moduleId)
{ {
return _htmlText.GetHtmlText(id); return _htmlText.GetHtmlText(id);
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText Get Attempt {ModuleId}", id); _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Html/Text Get Attempt {HtmlTextId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null; return null;
} }
@ -53,27 +97,7 @@ namespace Oqtane.Modules.HtmlText.Controllers
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText Post Attempt {HtmlText}", htmlText); _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Html/Text Post Attempt {HtmlText}", htmlText);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// PUT api/<controller>/5
[ValidateAntiForgeryToken]
[HttpPut("{id}")]
[Authorize(Policy = PolicyNames.EditModule)]
public Models.HtmlText Put(int id, [FromBody] Models.HtmlText htmlText)
{
if (ModelState.IsValid && AuthEntityId(EntityNames.Module) == htmlText.ModuleId)
{
htmlText = _htmlText.UpdateHtmlText(htmlText);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Html/Text Updated {HtmlText}", htmlText);
return htmlText;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText Put Attempt {HtmlText}", htmlText);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null; return null;
} }
@ -81,18 +105,18 @@ namespace Oqtane.Modules.HtmlText.Controllers
// DELETE api/<controller>/5 // DELETE api/<controller>/5
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
[HttpDelete("{id}")] [HttpDelete("{id}/{moduleid}")]
[Authorize(Policy = PolicyNames.EditModule)] [Authorize(Policy = PolicyNames.EditModule)]
public void Delete(int id) public void Delete(int id, int moduleId)
{ {
if (AuthEntityId(EntityNames.Module) == id) if (AuthEntityId(EntityNames.Module) == moduleId)
{ {
_htmlText.DeleteHtmlText(id); _htmlText.DeleteHtmlText(id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Html/Text Deleted {HtmlTextId}", id); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Html/Text Deleted {HtmlTextId}", id);
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText Delete Attempt {ModuleId}", id); _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Html/Text Delete Attempt {HtmlTextId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
} }
} }

View File

@ -43,19 +43,10 @@ namespace Oqtane.Modules.HtmlText.Manager
public void ImportModule(Module module, string content, string version) public void ImportModule(Module module, string content, string version)
{ {
content = WebUtility.HtmlDecode(content); content = WebUtility.HtmlDecode(content);
var htmlText = _htmlText.GetHtmlText(module.ModuleId); var htmlText = new Models.HtmlText();
if (htmlText != null) htmlText.ModuleId = module.ModuleId;
{ htmlText.Content = content;
htmlText.Content = content; _htmlText.AddHtmlText(htmlText);
_htmlText.UpdateHtmlText(htmlText);
}
else
{
htmlText = new Models.HtmlText();
htmlText.ModuleId = module.ModuleId;
htmlText.Content = content;
_htmlText.AddHtmlText(htmlText);
}
} }
public bool Install(Tenant tenant, string version) public bool Install(Tenant tenant, string version)

View File

@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore;
using System.Linq; using System.Linq;
using Oqtane.Modules.HtmlText.Models; using Oqtane.Modules.HtmlText.Models;
using Oqtane.Documentation; using Oqtane.Documentation;
using System.Collections.Generic;
namespace Oqtane.Modules.HtmlText.Repository namespace Oqtane.Modules.HtmlText.Repository
{ {
@ -15,11 +16,15 @@ namespace Oqtane.Modules.HtmlText.Repository
_db = context; _db = context;
} }
public Models.HtmlText GetHtmlText(int moduleId) public IEnumerable<Models.HtmlText> GetHtmlTexts(int moduleId)
{ {
return _db.HtmlText.FirstOrDefault(item => item.ModuleId == moduleId); return _db.HtmlText.Where(item => item.ModuleId == moduleId);
} }
public Models.HtmlText GetHtmlText(int htmlTextId)
{
return _db.HtmlText.Find(htmlTextId);
}
public Models.HtmlText AddHtmlText(Models.HtmlText htmlText) public Models.HtmlText AddHtmlText(Models.HtmlText htmlText)
{ {
@ -28,16 +33,9 @@ namespace Oqtane.Modules.HtmlText.Repository
return htmlText; return htmlText;
} }
public Models.HtmlText UpdateHtmlText(Models.HtmlText htmlText) public void DeleteHtmlText(int htmlTextId)
{ {
_db.Entry(htmlText).State = EntityState.Modified; Models.HtmlText htmlText = _db.HtmlText.FirstOrDefault(item => item.HtmlTextId == htmlTextId);
_db.SaveChanges();
return htmlText;
}
public void DeleteHtmlText(int moduleId)
{
Models.HtmlText htmlText = _db.HtmlText.FirstOrDefault(item => item.ModuleId == moduleId);
if (htmlText != null) _db.HtmlText.Remove(htmlText); if (htmlText != null) _db.HtmlText.Remove(htmlText);
_db.SaveChanges(); _db.SaveChanges();
} }

View File

@ -1,3 +1,4 @@
using System.Collections.Generic;
using Oqtane.Documentation; using Oqtane.Documentation;
using Oqtane.Modules.HtmlText.Models; using Oqtane.Modules.HtmlText.Models;
@ -6,9 +7,9 @@ namespace Oqtane.Modules.HtmlText.Repository
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")] [PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
public interface IHtmlTextRepository public interface IHtmlTextRepository
{ {
Models.HtmlText GetHtmlText(int moduleId); IEnumerable<Models.HtmlText> GetHtmlTexts(int moduleId);
Models.HtmlText GetHtmlText(int htmlTextId);
Models.HtmlText AddHtmlText(Models.HtmlText htmlText); Models.HtmlText AddHtmlText(Models.HtmlText htmlText);
Models.HtmlText UpdateHtmlText(Models.HtmlText htmlText); void DeleteHtmlText(int htmlTextId);
void DeleteHtmlText(int moduleId);
} }
} }

View File

@ -8,6 +8,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@Model.Title</title> <title>@Model.Title</title>
@Html.Raw(@Model.Meta)
<base href="~/" /> <base href="~/" />
<link id="app-favicon" rel="shortcut icon" type="image/x-icon" href="@Model.FavIcon" /> <link id="app-favicon" rel="shortcut icon" type="image/x-icon" href="@Model.FavIcon" />
@if (!string.IsNullOrEmpty(Model.PWAScript)) @if (!string.IsNullOrEmpty(Model.PWAScript))
@ -20,34 +21,42 @@
@Html.Raw(Model.HeadResources) @Html.Raw(Model.HeadResources)
</head> </head>
<body> <body>
@(Html.AntiForgeryToken()) @if (string.IsNullOrEmpty(Model.Message))
<component type="typeof(Oqtane.App)" render-mode="@Model.RenderMode" param-AntiForgeryToken="@Model.AntiForgeryToken" param-Runtime="@Model.Runtime" param-RenderMode="@Model.RenderMode.ToString()" param-VisitorId="@Model.VisitorId" param-RemoteIPAddress="@Model.RemoteIPAddress" />
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="js/interop.js"></script>
@if (Model.Runtime == "WebAssembly")
{
<script src="_framework/blazor.webassembly.js"></script>
}
else
{
<script src="_framework/blazor.server.js"></script>
}
@if (!string.IsNullOrEmpty(Model.PWAScript))
{ {
@Html.Raw(Model.PWAScript) @(Html.AntiForgeryToken())
<component type="typeof(Oqtane.App)" render-mode="@Model.RenderMode" param-AntiForgeryToken="@Model.AntiForgeryToken" param-Runtime="@Model.Runtime" param-RenderMode="@Model.RenderMode.ToString()" param-VisitorId="@Model.VisitorId" param-RemoteIPAddress="@Model.RemoteIPAddress" />
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="js/interop.js"></script>
@if (Model.Runtime == "WebAssembly")
{
<script src="_framework/blazor.webassembly.js"></script>
}
else
{
<script src="_framework/blazor.server.js"></script>
}
@if (!string.IsNullOrEmpty(Model.PWAScript))
{
@Html.Raw(Model.PWAScript)
}
@Html.Raw(Model.BodyResources)
}
else
{
<div class="app-alert">@Model.Message</div>
} }
@Html.Raw(Model.BodyResources)
</body> </body>
</html> </html>

View File

@ -25,7 +25,7 @@ namespace Oqtane.Pages
{ {
public class HostModel : PageModel public class HostModel : PageModel
{ {
private IConfiguration _configuration; private IConfigManager _configuration;
private readonly ITenantManager _tenantManager; private readonly ITenantManager _tenantManager;
private readonly ILocalizationManager _localizationManager; private readonly ILocalizationManager _localizationManager;
private readonly ILanguageRepository _languages; private readonly ILanguageRepository _languages;
@ -38,7 +38,7 @@ namespace Oqtane.Pages
private readonly ISettingRepository _settings; private readonly ISettingRepository _settings;
private readonly ILogManager _logger; private readonly ILogManager _logger;
public HostModel(IConfiguration configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ILogManager logger) public HostModel(IConfigManager configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ILogManager logger)
{ {
_configuration = configuration; _configuration = configuration;
_tenantManager = tenantManager; _tenantManager = tenantManager;
@ -63,9 +63,11 @@ namespace Oqtane.Pages
public string HeadResources = ""; public string HeadResources = "";
public string BodyResources = ""; public string BodyResources = "";
public string Title = ""; public string Title = "";
public string Meta = "";
public string FavIcon = "favicon.ico"; public string FavIcon = "favicon.ico";
public string PWAScript = ""; public string PWAScript = "";
public string ThemeType = ""; public string ThemeType = "";
public string Message = "";
public IActionResult OnGet() public IActionResult OnGet()
{ {
@ -83,7 +85,7 @@ namespace Oqtane.Pages
} }
// if framework is installed // if framework is installed
if (!string.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection"))) if (_configuration.IsInstalled())
{ {
var alias = _tenantManager.GetAlias(); var alias = _tenantManager.GetAlias();
if (alias != null) if (alias != null)
@ -108,7 +110,7 @@ namespace Oqtane.Pages
} }
var site = _sites.GetSite(alias.SiteId); var site = _sites.GetSite(alias.SiteId);
if (site != null) if (site != null && !site.IsDeleted)
{ {
Route route = new Route(url, alias.Path); Route route = new Route(url, alias.Path);
@ -148,6 +150,7 @@ namespace Oqtane.Pages
{ {
Title = Title + " - " + page.Name; Title = Title + " - " + page.Name;
} }
Meta = page.Meta;
// include theme resources // include theme resources
if (!string.IsNullOrEmpty(page.ThemeType)) if (!string.IsNullOrEmpty(page.ThemeType))
@ -165,42 +168,50 @@ namespace Oqtane.Pages
return RedirectPermanent(url); return RedirectPermanent(url);
} }
} }
}
// include global resources // include global resources
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies(); var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
foreach (Assembly assembly in assemblies) foreach (Assembly assembly in assemblies)
{
ProcessHostResources(assembly);
ProcessModuleControls(assembly);
ProcessThemeControls(assembly);
}
// set culture if not specified
string culture = HttpContext.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName];
if (culture == null)
{
// get default language for site
var languages = _languages.GetLanguages(alias.SiteId);
if (languages.Any())
{ {
// use default language if specified otherwise use first language in collection ProcessHostResources(assembly);
culture = (languages.Where(l => l.IsDefault).SingleOrDefault() ?? languages.First()).Code; ProcessModuleControls(assembly);
ProcessThemeControls(assembly);
} }
else
{
culture = _localizationManager.GetDefaultCulture();
}
SetLocalizationCookie(culture);
}
// set language for page // set culture if not specified
if (!string.IsNullOrEmpty(culture)) string culture = HttpContext.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName];
{ if (culture == null)
// localization cookie value in form of c=en|uic=en {
Language = culture.Split('|')[0]; // get default language for site
Language = Language.Replace("c=", ""); var languages = _languages.GetLanguages(alias.SiteId);
if (languages.Any())
{
// use default language if specified otherwise use first language in collection
culture = (languages.Where(l => l.IsDefault).SingleOrDefault() ?? languages.First()).Code;
}
else
{
culture = _localizationManager.GetDefaultCulture();
}
SetLocalizationCookie(culture);
}
// set language for page
if (!string.IsNullOrEmpty(culture))
{
// localization cookie value in form of c=en|uic=en
Language = culture.Split('|')[0];
Language = Language.Replace("c=", "");
}
} }
else
{
Message = "Site Is Either Disabled Or Not Configured Correctly";
}
}
else
{
Message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name";
} }
} }
return Page(); return Page();

View File

@ -1,19 +1,16 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Models; using Oqtane.Models;
using Microsoft.Extensions.Configuration;
using Oqtane.Databases.Interfaces; using Oqtane.Databases.Interfaces;
using Oqtane.Extensions; using Oqtane.Extensions;
using Oqtane.Interfaces;
using Oqtane.Migrations.Framework; using Oqtane.Migrations.Framework;
using Oqtane.Repository.Databases.Interfaces; using Oqtane.Repository.Databases.Interfaces;
using Oqtane.Shared; using Oqtane.Shared;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading; using System.Threading;
using Oqtane.Infrastructure;
// ReSharper disable BuiltInTypeReferenceStyleForMemberAccess // ReSharper disable BuiltInTypeReferenceStyleForMemberAccess
// ReSharper disable UnusedAutoPropertyAccessor.Global // ReSharper disable UnusedAutoPropertyAccessor.Global
@ -24,14 +21,14 @@ namespace Oqtane.Repository
public class MasterDBContext : DbContext, IMultiDatabase public class MasterDBContext : DbContext, IMultiDatabase
{ {
private readonly IHttpContextAccessor _accessor; private readonly IHttpContextAccessor _accessor;
private readonly IConfiguration _configuration; private readonly IConfigManager _config;
private string _connectionString; private string _connectionString;
private string _databaseType; private string _databaseType;
public MasterDBContext(DbContextOptions<MasterDBContext> options, IHttpContextAccessor accessor, IConfiguration configuration) : base(options) public MasterDBContext(DbContextOptions<MasterDBContext> options, IHttpContextAccessor accessor, IConfigManager config) : base(options)
{ {
_accessor = accessor; _accessor = accessor;
_configuration = configuration; _config = config;
} }
public IDatabase ActiveDatabase { get; private set; } public IDatabase ActiveDatabase { get; private set; }
@ -40,15 +37,15 @@ namespace Oqtane.Repository
{ {
optionsBuilder.ReplaceService<IMigrationsAssembly, MultiDatabaseMigrationsAssembly>(); optionsBuilder.ReplaceService<IMigrationsAssembly, MultiDatabaseMigrationsAssembly>();
if(_configuration != null) if(_config != null)
{ {
if (!String.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection"))) if (_config.IsInstalled())
{ {
_connectionString = _configuration.GetConnectionString("DefaultConnection") _connectionString = _config.GetConnectionString("DefaultConnection")
.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString());
} }
_databaseType = _configuration.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey]; _databaseType = _config.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey];
} }
if (!String.IsNullOrEmpty(_databaseType)) if (!String.IsNullOrEmpty(_databaseType))

View File

@ -6,7 +6,6 @@ using Oqtane.Infrastructure;
using Oqtane.Repository; using Oqtane.Repository;
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
namespace Oqtane.Security namespace Oqtane.Security
{ {
@ -17,8 +16,8 @@ namespace Oqtane.Security
if (context != null && context.Principal.Identity.IsAuthenticated) if (context != null && context.Principal.Identity.IsAuthenticated)
{ {
// check if framework is installed // check if framework is installed
var config = context.HttpContext.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration; var config = context.HttpContext.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
if (!string.IsNullOrEmpty(config.GetConnectionString("DefaultConnection"))) if (config.IsInstalled())
{ {
var tenantManager = context.HttpContext.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager; var tenantManager = context.HttpContext.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager;
var alias = tenantManager.GetAlias(); var alias = tenantManager.GetAlias();

View File

@ -21,7 +21,6 @@ namespace Oqtane
{ {
public class Startup public class Startup
{ {
private readonly Runtime _runtime;
private readonly bool _useSwagger; private readonly bool _useSwagger;
private readonly IWebHostEnvironment _env; private readonly IWebHostEnvironment _env;
private readonly string[] _supportedCultures; private readonly string[] _supportedCultures;
@ -36,7 +35,6 @@ namespace Oqtane
Configuration = builder.Build(); Configuration = builder.Build();
_supportedCultures = localizationManager.GetSupportedCultures(); _supportedCultures = localizationManager.GetSupportedCultures();
_runtime = (Configuration.GetSection("Runtime").Value == "WebAssembly") ? Runtime.WebAssembly : Runtime.Server;
//add possibility to switch off swagger on production. //add possibility to switch off swagger on production.
_useSwagger = Configuration.GetSection("UseSwagger").Value != "false"; _useSwagger = Configuration.GetSection("UseSwagger").Value != "false";
@ -115,7 +113,7 @@ namespace Oqtane
services.AddOqtaneTransientServices(); services.AddOqtaneTransientServices();
// load the external assemblies into the app domain, install services // load the external assemblies into the app domain, install services
services.AddOqtane(_runtime, _supportedCultures); services.AddOqtane(_supportedCultures);
services.AddOqtaneDbContext(); services.AddOqtaneDbContext();
services.AddMvc() services.AddMvc()

View File

@ -61,7 +61,12 @@ namespace Oqtane.Models
/// Reference to a Container which will be used for modules on this page. /// Reference to a Container which will be used for modules on this page.
/// </summary> /// </summary>
public string DefaultContainerType { get; set; } public string DefaultContainerType { get; set; }
/// <summary>
/// Meta tags to be included in the head of the page
/// </summary>
public string Meta { get; set; }
/// <summary> /// <summary>
/// Icon file for this page. /// Icon file for this page.
/// TODO: unclear what this is for, and what icon library is used. Probably FontAwesome? /// TODO: unclear what this is for, and what icon library is used. Probably FontAwesome?