commit
aff33c6a5d
|
@ -62,10 +62,6 @@
|
|||
{
|
||||
SiteState.Alias = _installation.Alias;
|
||||
}
|
||||
else
|
||||
{
|
||||
_installation.Message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name";
|
||||
}
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,86 +9,88 @@
|
|||
@inject IStringLocalizer<Detail> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="dateTime" class="form-control" @bind="@_logDate" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="level" class="form-control" @bind="@_level" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="feature" class="form-control" @bind="@_feature" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="function" class="form-control" @bind="@_function" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="category" class="form-control" @bind="@_category" readonly />
|
||||
</div>
|
||||
</div>
|
||||
@if (_pageName != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="page" class="form-control" @bind="@_pageName" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_moduleTitle != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="module" class="form-control" @bind="@_moduleTitle" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_username != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="user" class="form-control" @bind="@_username" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="template" class="form-control" @bind="@_template" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="message" HelpText="The message that the system generated" ResourceKey="Message">Message: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(_exception))
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label>
|
||||
@if (_initialized)
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="dateTime" class="form-control" @bind="@_logDate" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="level" class="form-control" @bind="@_level" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="feature" class="form-control" @bind="@_feature" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="function" class="form-control" @bind="@_function" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="category" class="form-control" @bind="@_category" readonly />
|
||||
</div>
|
||||
</div>
|
||||
@if (_pageName != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="page" class="form-control" @bind="@_pageName" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_moduleTitle != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="module" class="form-control" @bind="@_moduleTitle" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_username != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="user" class="form-control" @bind="@_username" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="template" class="form-control" @bind="@_template" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="message" HelpText="The message that the system generated" ResourceKey="Message">Message: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(_exception))
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea>
|
||||
</div>
|
||||
|
@ -107,81 +109,95 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<NavLink class="btn btn-secondary" href="@CloseUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private int _logId;
|
||||
private string _logDate = string.Empty;
|
||||
private string _level = string.Empty;
|
||||
private string _feature = string.Empty;
|
||||
private string _function = string.Empty;
|
||||
private string _category = string.Empty;
|
||||
private string _pageName = string.Empty;
|
||||
private string _moduleTitle = string.Empty;
|
||||
private string _username = string.Empty;
|
||||
private string _url = string.Empty;
|
||||
private string _template = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
private string _exception = string.Empty;
|
||||
private string _properties = string.Empty;
|
||||
private string _server = string.Empty;
|
||||
|
||||
@code {
|
||||
private int _logId;
|
||||
private string _logDate = string.Empty;
|
||||
private string _level = string.Empty;
|
||||
private string _feature = string.Empty;
|
||||
private string _function = string.Empty;
|
||||
private string _category = string.Empty;
|
||||
private string _pageName = string.Empty;
|
||||
private string _moduleTitle = string.Empty;
|
||||
private string _username = string.Empty;
|
||||
private string _url = string.Empty;
|
||||
private string _template = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
private string _exception = string.Empty;
|
||||
private string _properties = string.Empty;
|
||||
private string _server = string.Empty;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
_logId = Int32.Parse(PageState.QueryString["id"]);
|
||||
var log = await LogService.GetLogAsync(_logId);
|
||||
if (log != null)
|
||||
{
|
||||
_logId = Int32.Parse(PageState.QueryString["id"]);
|
||||
var log = await LogService.GetLogAsync(_logId);
|
||||
if (log != null)
|
||||
_logDate = log.LogDate.ToString(CultureInfo.CurrentCulture);
|
||||
_level = log.Level;
|
||||
_feature = log.Feature;
|
||||
_function = log.Function;
|
||||
_category = log.Category;
|
||||
|
||||
if (log.PageId != null)
|
||||
{
|
||||
_logDate = log.LogDate.ToString(CultureInfo.CurrentCulture);
|
||||
_level = log.Level;
|
||||
_feature = log.Feature;
|
||||
_function = log.Function;
|
||||
_category = log.Category;
|
||||
|
||||
if (log.PageId != null)
|
||||
var page = await PageService.GetPageAsync(log.PageId.Value);
|
||||
if (page != null)
|
||||
{
|
||||
var page = await PageService.GetPageAsync(log.PageId.Value);
|
||||
if (page != null)
|
||||
{
|
||||
_pageName = page.Name;
|
||||
}
|
||||
_pageName = page.Name;
|
||||
}
|
||||
|
||||
if (log.PageId != null && log.ModuleId != null)
|
||||
{
|
||||
var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value);
|
||||
if (pagemodule != null)
|
||||
{
|
||||
_moduleTitle = pagemodule.Title;
|
||||
}
|
||||
}
|
||||
|
||||
if (log.UserId != null)
|
||||
{
|
||||
var user = await UserService.GetUserAsync(log.UserId.Value, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
_username = user.Username;
|
||||
}
|
||||
}
|
||||
|
||||
_url = log.Url;
|
||||
_template = log.MessageTemplate;
|
||||
_message = log.Message;
|
||||
_exception = log.Exception;
|
||||
_properties = log.Properties;
|
||||
_server = log.Server;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Log {LogId} {Error}", _logId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Log.Load"], MessageType.Error);
|
||||
|
||||
if (log.PageId != null && log.ModuleId != null)
|
||||
{
|
||||
var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value);
|
||||
if (pagemodule != null)
|
||||
{
|
||||
_moduleTitle = pagemodule.Title;
|
||||
}
|
||||
}
|
||||
|
||||
if (log.UserId != null)
|
||||
{
|
||||
var user = await UserService.GetUserAsync(log.UserId.Value, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
_username = user.Username;
|
||||
}
|
||||
}
|
||||
|
||||
_url = log.Url;
|
||||
_template = log.MessageTemplate;
|
||||
_message = log.Message;
|
||||
_exception = log.Exception;
|
||||
_properties = log.Properties;
|
||||
_server = log.Server;
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Log {LogId} {Error}", _logId, ex.Message);
|
||||
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"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ else
|
|||
<div class="row mb-1 align-items-center">
|
||||
<div class="col-sm-4">
|
||||
<Label For="level" HelpText="Select the log level for event log items" ResourceKey="Level">Level: </Label><br /><br />
|
||||
<select id="level" class="form-select" @onchange="(e => LevelChanged(e))">
|
||||
<select id="level" class="form-select" value="@_level" @onchange="(e => LevelChanged(e))">
|
||||
<option value="-"><@Localizer["AllLevels"]></option>
|
||||
<option value="Trace">@Localizer["Trace"]</option>
|
||||
<option value="Debug">@Localizer["Debug"]</option>
|
||||
|
@ -29,7 +29,7 @@ else
|
|||
</div>
|
||||
<div class="col-sm-4">
|
||||
<Label For="function" HelpText="Select the function for event log items" ResourceKey="Function">Function: </Label><br /><br />
|
||||
<select id="function" class="form-select" @onchange="(e => FunctionChanged(e))">
|
||||
<select id="function" class="form-select" value="@_function" @onchange="(e => FunctionChanged(e))">
|
||||
<option value="-"><@Localizer["AllFunctions"]></option>
|
||||
<option value="Create">@Localizer["Create"]</option>
|
||||
<option value="Read">@Localizer["Read"]</option>
|
||||
|
@ -41,7 +41,7 @@ else
|
|||
</div>
|
||||
<div class="col-sm-4">
|
||||
<Label For="rows" HelpText="Select the maximum number of event log items to review. Please note that if you choose more than 10 items the information will be split into pages." ResourceKey="Rows">Maximum Items: </Label><br /><br />
|
||||
<select id="rows" class="form-select" @onchange="(e => RowsChanged(e))">
|
||||
<select id="rows" class="form-select" value="@_rows" @onchange="(e => RowsChanged(e))">
|
||||
<option value="10">10</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
|
@ -53,7 +53,7 @@ else
|
|||
|
||||
@if (_logs.Any())
|
||||
{
|
||||
<Pager Items="@_logs">
|
||||
<Pager Items="@_logs" CurrentPage="@_page.ToString()" OnPageChange="OnPageChange">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["Date"]</th>
|
||||
|
@ -62,7 +62,7 @@ else
|
|||
<th>@Localizer["Function"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString())" ResourceKey="LogDetails" /></td>
|
||||
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString() + "&level=" + _level + "&function=" + _function + "&rows=" + _rows + "&page=" + _page.ToString())" ResourceKey="LogDetails" /></td>
|
||||
<td class="@GetClass(context.Function)">@context.LogDate</td>
|
||||
<td class="@GetClass(context.Function)">@context.Level</td>
|
||||
<td class="@GetClass(context.Function)">@context.Feature</td>
|
||||
|
@ -94,6 +94,7 @@ else
|
|||
private string _level = "-";
|
||||
private string _function = "-";
|
||||
private string _rows = "10";
|
||||
private int _page = 1;
|
||||
private List<Log> _logs;
|
||||
private string _retention = "";
|
||||
|
||||
|
@ -103,8 +104,27 @@ else
|
|||
{
|
||||
try
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("level"))
|
||||
{
|
||||
_level = PageState.QueryString["level"];
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("function"))
|
||||
{
|
||||
_function = PageState.QueryString["function"];
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("rows"))
|
||||
{
|
||||
_rows = PageState.QueryString["rows"];
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("page") && int.TryParse(PageState.QueryString["page"], out int page))
|
||||
{
|
||||
_page = page;
|
||||
}
|
||||
|
||||
await GetLogs();
|
||||
_retention = SettingService.GetSetting(PageState.Site.Settings, "LogRetention", "30");
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_retention = SettingService.GetSetting(settings, "LogRetention", "30");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -208,4 +228,9 @@ else
|
|||
}
|
||||
}
|
||||
|
||||
private void OnPageChange(int page)
|
||||
{
|
||||
_page = page;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ else
|
|||
<td>@context.Name</td>
|
||||
<td>@context.Version</td>
|
||||
<td>
|
||||
@if(context.AssemblyName == "Oqtane.Client" || PageState.Modules.Where(m => m.ModuleDefinition.ModuleDefinitionId == context.ModuleDefinitionId).Count() > 0)
|
||||
@if(context.AssemblyName == "Oqtane.Client" || PageState.Modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
|
||||
{
|
||||
<span>@SharedLocalizer["Yes"]</span>
|
||||
}
|
||||
|
|
|
@ -95,6 +95,12 @@
|
|||
<input id="title" class="form-control" @bind="@_title" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="meta" HelpText="Optionally enter meta tags (in exactly the form you want them to be included in the page output)." ResourceKey="Meta">Meta: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="meta" class="form-control" @bind="@_meta" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||
<div class="col-sm-9">
|
||||
|
@ -156,220 +162,222 @@
|
|||
</form>
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
private List<Theme> _themeList;
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private List<Page> _pageList;
|
||||
private string _name;
|
||||
private string _title;
|
||||
private string _path = string.Empty;
|
||||
private string _parentid = "-1";
|
||||
private string _insert = ">>";
|
||||
private List<Page> _children;
|
||||
private int _childid = -1;
|
||||
private string _isnavigation = "True";
|
||||
private string _isclickable = "True";
|
||||
private string _url;
|
||||
private string _ispersonalizable = "False";
|
||||
private string _themetype = string.Empty;
|
||||
private string _containertype = string.Empty;
|
||||
private string _icon = string.Empty;
|
||||
private string _permissions = string.Empty;
|
||||
private PermissionGrid _permissionGrid;
|
||||
private Type _themeSettingsType;
|
||||
private object _themeSettings;
|
||||
private RenderFragment ThemeSettingsComponent { get; set; }
|
||||
private bool _refresh = false;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private List<Theme> _themeList;
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private List<Page> _pageList;
|
||||
private string _name;
|
||||
private string _title;
|
||||
private string _meta;
|
||||
private string _path = string.Empty;
|
||||
private string _parentid = "-1";
|
||||
private string _insert = ">>";
|
||||
private List<Page> _children;
|
||||
private int _childid = -1;
|
||||
private string _isnavigation = "True";
|
||||
private string _isclickable = "True";
|
||||
private string _url;
|
||||
private string _ispersonalizable = "False";
|
||||
private string _themetype = string.Empty;
|
||||
private string _containertype = string.Empty;
|
||||
private string _icon = string.Empty;
|
||||
private string _permissions = string.Empty;
|
||||
private PermissionGrid _permissionGrid;
|
||||
private Type _themeSettingsType;
|
||||
private object _themeSettings;
|
||||
private RenderFragment ThemeSettingsComponent { get; set; }
|
||||
private bool _refresh = false;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_themeList = await ThemeService.GetThemesAsync();
|
||||
_themes = ThemeService.GetThemeControls(_themeList);
|
||||
_themetype = PageState.Site.DefaultThemeType;
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
_containertype = PageState.Site.DefaultContainerType;
|
||||
_pageList = PageState.Pages;
|
||||
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
||||
_permissions = string.Empty;
|
||||
ThemeSettings();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Initializing Page {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Page.Initialize"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_themeList = await ThemeService.GetThemesAsync();
|
||||
_themes = ThemeService.GetThemeControls(_themeList);
|
||||
_themetype = PageState.Site.DefaultThemeType;
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
_containertype = PageState.Site.DefaultContainerType;
|
||||
_pageList = PageState.Pages;
|
||||
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
||||
_permissions = string.Empty;
|
||||
ThemeSettings();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Initializing Page {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Page.Initialize"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ParentChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_parentid = (string)e.Value;
|
||||
_children = new List<Page>();
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
||||
{
|
||||
_children.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
||||
{
|
||||
_children.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private async void ParentChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_parentid = (string)e.Value;
|
||||
_children = new List<Page>();
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
||||
{
|
||||
_children.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
||||
{
|
||||
_children.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
_containertype = "-";
|
||||
ThemeSettings();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private async void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
_containertype = "-";
|
||||
ThemeSettings();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void ThemeSettings()
|
||||
{
|
||||
_themeSettingsType = null;
|
||||
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
||||
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
||||
{
|
||||
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
||||
if (_themeSettingsType != null)
|
||||
{
|
||||
ThemeSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _themeSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
_refresh = true;
|
||||
}
|
||||
}
|
||||
private void ThemeSettings()
|
||||
{
|
||||
_themeSettingsType = null;
|
||||
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
||||
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
||||
{
|
||||
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
||||
if (_themeSettingsType != null)
|
||||
{
|
||||
ThemeSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _themeSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
_refresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SavePage()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
Page page = null;
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
||||
{
|
||||
page = new Page();
|
||||
page.SiteId = PageState.Page.SiteId;
|
||||
page.Name = _name;
|
||||
page.Title = _title;
|
||||
private async Task SavePage()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
Page page = null;
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
||||
{
|
||||
page = new Page();
|
||||
page.SiteId = PageState.Page.SiteId;
|
||||
page.Name = _name;
|
||||
page.Title = _title;
|
||||
|
||||
if (string.IsNullOrEmpty(_path))
|
||||
{
|
||||
_path = _name;
|
||||
}
|
||||
if (_path.Contains("/"))
|
||||
{
|
||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||
}
|
||||
if (string.IsNullOrEmpty(_path))
|
||||
{
|
||||
_path = _name;
|
||||
}
|
||||
if (_path.Contains("/"))
|
||||
{
|
||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
page.ParentId = null;
|
||||
page.Path = Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.ParentId = Int32.Parse(_parentid);
|
||||
var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault();
|
||||
if (parent.Path == string.Empty)
|
||||
{
|
||||
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
}
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
page.ParentId = null;
|
||||
page.Path = Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.ParentId = Int32.Parse(_parentid);
|
||||
var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault();
|
||||
if (parent.Path == string.Empty)
|
||||
{
|
||||
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
}
|
||||
|
||||
if(PagePathIsDeleted(page.Path, page.SiteId, _pageList))
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Message.Page.Deleted"], _path), MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
if(PagePathIsDeleted(page.Path, page.SiteId, _pageList))
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Message.Page.Deleted"], _path), MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
Page child;
|
||||
switch (_insert)
|
||||
{
|
||||
case "<<":
|
||||
page.Order = 0;
|
||||
break;
|
||||
case "<":
|
||||
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
||||
page.Order = child.Order - 1;
|
||||
break;
|
||||
case ">":
|
||||
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
||||
page.Order = child.Order + 1;
|
||||
break;
|
||||
case ">>":
|
||||
page.Order = int.MaxValue;
|
||||
break;
|
||||
}
|
||||
Page child;
|
||||
switch (_insert)
|
||||
{
|
||||
case "<<":
|
||||
page.Order = 0;
|
||||
break;
|
||||
case "<":
|
||||
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
||||
page.Order = child.Order - 1;
|
||||
break;
|
||||
case ">":
|
||||
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
||||
page.Order = child.Order + 1;
|
||||
break;
|
||||
case ">>":
|
||||
page.Order = int.MaxValue;
|
||||
break;
|
||||
}
|
||||
|
||||
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
|
||||
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
||||
page.Url = _url;
|
||||
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
|
||||
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
||||
{
|
||||
page.ThemeType = string.Empty;
|
||||
}
|
||||
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
|
||||
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
page.DefaultContainerType = string.Empty;
|
||||
}
|
||||
page.Icon = (_icon == null ? string.Empty : _icon);
|
||||
page.Permissions = _permissionGrid.GetPermissions();
|
||||
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
|
||||
page.UserId = null;
|
||||
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
|
||||
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
||||
page.Url = _url;
|
||||
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
|
||||
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
||||
{
|
||||
page.ThemeType = string.Empty;
|
||||
}
|
||||
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
|
||||
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
page.DefaultContainerType = string.Empty;
|
||||
}
|
||||
page.Icon = (_icon == null ? string.Empty : _icon);
|
||||
page.Permissions = _permissionGrid.GetPermissions();
|
||||
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
|
||||
page.UserId = null;
|
||||
page.Meta = _meta;
|
||||
|
||||
page = await PageService.AddPageAsync(page);
|
||||
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
||||
|
|
|
@ -102,6 +102,12 @@
|
|||
<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="meta" HelpText="Optionally enter meta tags (in exactly the form you want them to be included in the page output)." ResourceKey="Meta">Meta: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="meta" class="form-control" @bind="@_meta" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||
<div class="col-sm-9">
|
||||
|
@ -200,6 +206,7 @@
|
|||
private int _pageId;
|
||||
private string _name;
|
||||
private string _title;
|
||||
private string _meta;
|
||||
private string _path;
|
||||
private string _currentparentid;
|
||||
private string _parentid = "-1";
|
||||
|
@ -243,6 +250,7 @@
|
|||
{
|
||||
_name = page.Name;
|
||||
_title = page.Title;
|
||||
_meta = page.Meta;
|
||||
_path = page.Path;
|
||||
_pageModules = PageState.Modules.Where(m => m.PageId == page.PageId && m.IsDeleted == false).ToList();
|
||||
|
||||
|
@ -313,183 +321,187 @@
|
|||
_pageModules.RemoveAll(item => item.PageModuleId == pagemodule.PageModuleId);
|
||||
StateHasChanged();
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri + "&tab=PageModules");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Module {Title} {Error}", module.Title, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Module {Title} {Error}", module.Title, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ParentChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_parentid = (string)e.Value;
|
||||
_children = new List<Page>();
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
||||
{
|
||||
_children.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
||||
{
|
||||
_children.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_parentid == _currentparentid)
|
||||
{
|
||||
_insert = "=";
|
||||
}
|
||||
else
|
||||
{
|
||||
_insert = ">>";
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private async void ParentChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_parentid = (string)e.Value;
|
||||
_children = new List<Page>();
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
||||
{
|
||||
_children.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
||||
{
|
||||
_children.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_parentid == _currentparentid)
|
||||
{
|
||||
_insert = "=";
|
||||
}
|
||||
else
|
||||
{
|
||||
_insert = ">>";
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
_containertype = "-";
|
||||
ThemeSettings();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private async void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
_containertype = "-";
|
||||
ThemeSettings();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void ThemeSettings()
|
||||
{
|
||||
_themeSettingsType = null;
|
||||
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
||||
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
||||
{
|
||||
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
||||
if (_themeSettingsType != null)
|
||||
{
|
||||
ThemeSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _themeSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
_refresh = true;
|
||||
}
|
||||
}
|
||||
private void ThemeSettings()
|
||||
{
|
||||
_themeSettingsType = null;
|
||||
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)));
|
||||
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
||||
{
|
||||
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
||||
if (_themeSettingsType != null)
|
||||
{
|
||||
ThemeSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _themeSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
_refresh = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SavePage()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
Page page = null;
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
||||
{
|
||||
page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
|
||||
string currentPath = page.Path;
|
||||
private async Task SavePage()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
Page page = null;
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
||||
{
|
||||
page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
|
||||
string currentPath = page.Path;
|
||||
|
||||
page.Name = _name;
|
||||
page.Title = _title;
|
||||
page.Name = _name;
|
||||
page.Title = _title;
|
||||
|
||||
if (string.IsNullOrEmpty(_path))
|
||||
{
|
||||
_path = _name;
|
||||
}
|
||||
if (_path.Contains("/"))
|
||||
{
|
||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||
}
|
||||
if (string.IsNullOrEmpty(_path))
|
||||
{
|
||||
_path = _name;
|
||||
}
|
||||
if (_path.Contains("/"))
|
||||
{
|
||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
page.ParentId = null;
|
||||
page.Path = Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.ParentId = Int32.Parse(_parentid);
|
||||
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == page.ParentId);
|
||||
if (parent.Path == string.Empty)
|
||||
{
|
||||
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
}
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
page.ParentId = null;
|
||||
page.Path = Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.ParentId = Int32.Parse(_parentid);
|
||||
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == page.ParentId);
|
||||
if (parent.Path == string.Empty)
|
||||
{
|
||||
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList))
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList))
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_insert != "=")
|
||||
{
|
||||
Page child;
|
||||
switch (_insert)
|
||||
{
|
||||
case "<<":
|
||||
page.Order = 0;
|
||||
break;
|
||||
case "<":
|
||||
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
|
||||
if (child != null) page.Order = child.Order - 1;
|
||||
break;
|
||||
case ">":
|
||||
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
|
||||
if (child != null) page.Order = child.Order + 1;
|
||||
break;
|
||||
case ">>":
|
||||
page.Order = int.MaxValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation));
|
||||
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
||||
page.Url = _url;
|
||||
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
|
||||
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
||||
{
|
||||
page.ThemeType = string.Empty;
|
||||
}
|
||||
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
|
||||
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
page.DefaultContainerType = string.Empty;
|
||||
}
|
||||
page.Icon = _icon ?? string.Empty;
|
||||
page.Permissions = _permissionGrid.GetPermissions();
|
||||
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
|
||||
page.UserId = null;
|
||||
if (_insert != "=")
|
||||
{
|
||||
Page child;
|
||||
switch (_insert)
|
||||
{
|
||||
case "<<":
|
||||
page.Order = 0;
|
||||
break;
|
||||
case "<":
|
||||
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
|
||||
if (child != null) page.Order = child.Order - 1;
|
||||
break;
|
||||
case ">":
|
||||
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
|
||||
if (child != null) page.Order = child.Order + 1;
|
||||
break;
|
||||
case ">>":
|
||||
page.Order = int.MaxValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation));
|
||||
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
||||
page.Url = _url;
|
||||
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
|
||||
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
||||
{
|
||||
page.ThemeType = string.Empty;
|
||||
}
|
||||
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
|
||||
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
page.DefaultContainerType = string.Empty;
|
||||
}
|
||||
page.Icon = _icon ?? string.Empty;
|
||||
page.Permissions = _permissionGrid.GetPermissions();
|
||||
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
|
||||
page.UserId = null;
|
||||
page.Meta = _meta;
|
||||
|
||||
page = await PageService.UpdatePageAsync(page);
|
||||
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<th>@Localizer["DeletedOn"]</th>
|
||||
</Header>
|
||||
<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>@context.Name</td>
|
||||
<td>@context.DeletedBy</td>
|
||||
|
@ -56,7 +56,7 @@
|
|||
<th>@Localizer["DeletedOn"]</th>
|
||||
</Header>
|
||||
<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>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td>
|
||||
<td>@context.Title</td>
|
||||
|
|
|
@ -53,12 +53,14 @@ else
|
|||
<Pager Items="@userroles">
|
||||
<Header>
|
||||
<th>@Localizer["Users"]</th>
|
||||
<th>@SharedLocalizer["Username"]</th>
|
||||
<th>@Localizer["Effective"]</th>
|
||||
<th>@Localizer["Expiry"]</th>
|
||||
<th> </th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td>@context.User.DisplayName</td>
|
||||
<td>@context.User.Username</td>
|
||||
<td>@context.EffectiveDate</td>
|
||||
<td>@context.ExpiryDate</td>
|
||||
<td>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<Label Class="col-sm-3" For="to" HelpText="Enter the username you wish to send a message to" ResourceKey="To">To: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="to" class="form-control" @bind="@username" />
|
||||
</div> >
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="subject" HelpText="Enter the subject of the message" ResourceKey="Subject">Subject: </Label>
|
||||
|
|
|
@ -36,6 +36,7 @@ else
|
|||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
<th>@SharedLocalizer["Username"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td>
|
||||
|
@ -48,6 +49,7 @@ else
|
|||
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
|
||||
</td>
|
||||
<td>@context.User.DisplayName</td>
|
||||
<td>@context.User.Username</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
</TabPanel>
|
||||
|
|
|
@ -7,69 +7,73 @@
|
|||
@inject IStringLocalizer<Detail> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="ip" HelpText="The last recorded IP address for this visitor" ResourceKey="IP">IP Address: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="ip" class="form-control" @bind="@_ip" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="language" HelpText="The last recorded language for this visitor" ResourceKey="Language">Language: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="language" class="form-control" @bind="@_language" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="useragent" HelpText="The last recorded user agent for this visitor" ResourceKey="UserAgent">User Agent: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="useragent" class="form-control" @bind="@_useragent" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="url" HelpText="The last recorded url for this visitor" ResourceKey="Url">Url: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="referrer" HelpText="The last recorded referrer for this visitor" ResourceKey="Referrer">Referrer: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="referrer" class="form-control" @bind="@_referrer" readonly />
|
||||
</div>
|
||||
</div>
|
||||
@if (_user != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="user" HelpText="The last recorded user associated with this visitor" ResourceKey="User">User: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="user" class="form-control" @bind="@_user" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="visits" HelpText="The total number of visits by this visitor all time" ResourceKey="Visits">Visits: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="visits" class="form-control" @bind="@_visits" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="visited" HelpText="The last recorded date/time when the visitor visited the site" ResourceKey="Visited">Visited: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="visited" class="form-control" @bind="@_visited" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="created" HelpText="The first recorded date/time when this visitor visited the site" ResourceKey="Created">Created: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="created" class="form-control" @bind="@_created" readonly />
|
||||
</div>
|
||||
</div>
|
||||
@if (_initialized)
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="ip" HelpText="The last recorded IP address for this visitor" ResourceKey="IP">IP Address: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="ip" class="form-control" @bind="@_ip" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="language" HelpText="The last recorded language for this visitor" ResourceKey="Language">Language: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="language" class="form-control" @bind="@_language" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="useragent" HelpText="The last recorded user agent for this visitor" ResourceKey="UserAgent">User Agent: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="useragent" class="form-control" @bind="@_useragent" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="url" HelpText="The last recorded url for this visitor" ResourceKey="Url">Url: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="referrer" HelpText="The last recorded referrer for this visitor" ResourceKey="Referrer">Referrer: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="referrer" class="form-control" @bind="@_referrer" readonly />
|
||||
</div>
|
||||
</div>
|
||||
@if (_user != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="user" HelpText="The last recorded user associated with this visitor" ResourceKey="User">User: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="user" class="form-control" @bind="@_user" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="visits" HelpText="The total number of visits by this visitor all time" ResourceKey="Visits">Visits: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="visits" class="form-control" @bind="@_visits" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="visited" HelpText="The last recorded date/time when the visitor visited the site" ResourceKey="Visited">Visited: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="visited" class="form-control" @bind="@_visited" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="created" HelpText="The first recorded date/time when this visitor visited the site" ResourceKey="Created">Created: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="created" class="form-control" @bind="@_created" readonly />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
<NavLink class="btn btn-secondary" href="@CloseUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private int _visitorId;
|
||||
private string _ip = string.Empty;
|
||||
private string _language = string.Empty;
|
||||
|
@ -108,6 +112,7 @@
|
|||
_user = user.DisplayName;
|
||||
}
|
||||
}
|
||||
_initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -120,4 +125,16 @@
|
|||
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"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,13 @@ else
|
|||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="col-sm-6">
|
||||
<select id="type" class="form-select custom-select" @onchange="(e => TypeChanged(e))">
|
||||
<option value="false">@Localizer["AllVisitors"]</option>
|
||||
<option value="true">@Localizer["UsersOnly"]</option>
|
||||
<select id="type" class="form-select custom-select" value="@_type" @onchange="(e => TypeChanged(e))">
|
||||
<option value="visitors">@Localizer["AllVisitors"]</option>
|
||||
<option value="users">@Localizer["UsersOnly"]</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<select id="type" class="form-select custom-select" @onchange="(e => DateChanged(e))">
|
||||
<select id="days" class="form-select custom-select" value="@_days" @onchange="(e => DaysChanged(e))">
|
||||
<option value="1">@Localizer["PastDay"]</option>
|
||||
<option value="7">@Localizer["PastWeek"]</option>
|
||||
<option value="30">@Localizer["PastMonth"]</option>
|
||||
|
@ -32,7 +32,7 @@ else
|
|||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<Pager Items="@_visitors">
|
||||
<Pager Items="@_visitors" CurrentPage="@_page.ToString()" OnPageChange="OnPageChange">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["IP"]</th>
|
||||
|
@ -43,7 +43,7 @@ else
|
|||
<th>@Localizer["Created"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="Detail" Parameters="@($"id=" + context.VisitorId.ToString())" ResourceKey="Details" /></td>
|
||||
<td><ActionLink Action="Detail" Parameters="@($"id=" + context.VisitorId.ToString() + "&type=" + _type.ToString() + "&days=" + _days.ToString() + "&page=" + _page.ToString())" ResourceKey="Details" /></td>
|
||||
<td>@context.IPAddress</td>
|
||||
<td>
|
||||
@if (context.UserId != null)
|
||||
|
@ -70,7 +70,7 @@ else
|
|||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="filter" HelpText="Comma delimited list of terms which may exist in IP addresses, user agents, or languages which identify visitors which should not be tracked (ie. bots)" ResourceKey="Filter">Filter: </Label>
|
||||
<Label Class="col-sm-3" For="filter" HelpText="Comma delimited list of terms which may exist in IP addresses, user agents, or languages identifying visitors which should not be tracked" ResourceKey="Filter">Filter: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="filter" class="form-control" @bind="@_filter" rows="3"></textarea>
|
||||
</div>
|
||||
|
@ -81,6 +81,15 @@ else
|
|||
<input id="retention" class="form-control" @bind="@_retention" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="correlation" HelpText="Indicate if new visitors to this site should be correlated based on their IP Address" ResourceKey="Correlation">Correlate Visitors? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="correlation" class="form-select" @bind="@_correlation">
|
||||
<option value="true">@SharedLocalizer["True"]</option>
|
||||
<option value="false">@SharedLocalizer["False"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||
|
@ -89,28 +98,46 @@ else
|
|||
}
|
||||
|
||||
@code {
|
||||
private bool _users = false;
|
||||
private string _type = "visitors";
|
||||
private int _days = 1;
|
||||
private int _page = 1;
|
||||
private List<Visitor> _visitors;
|
||||
private string _tracking;
|
||||
private string _filter = "";
|
||||
private string _retention = "";
|
||||
private string _correlation = "true";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("type"))
|
||||
{
|
||||
_type = PageState.QueryString["type"];
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("days") && int.TryParse(PageState.QueryString["days"], out int days))
|
||||
{
|
||||
_days = days;
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("page") && int.TryParse(PageState.QueryString["page"], out int page))
|
||||
{
|
||||
_page = page;
|
||||
}
|
||||
|
||||
await GetVisitors();
|
||||
|
||||
_tracking = PageState.Site.VisitorTracking.ToString();
|
||||
_filter = SettingService.GetSetting(PageState.Site.Settings, "VisitorFilter", "");
|
||||
_retention = SettingService.GetSetting(PageState.Site.Settings, "VisitorRetention", "30");
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter);
|
||||
_retention = SettingService.GetSetting(settings, "VisitorRetention", "30");
|
||||
_correlation = SettingService.GetSetting(settings, "VisitorCorrelation", "true");
|
||||
}
|
||||
|
||||
private async void TypeChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_users = bool.Parse(e.Value.ToString());
|
||||
_type = e.Value.ToString();
|
||||
await GetVisitors();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
@ -120,7 +147,7 @@ else
|
|||
}
|
||||
}
|
||||
|
||||
private async void DateChanged(ChangeEventArgs e)
|
||||
private async void DaysChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -137,7 +164,7 @@ else
|
|||
private async Task GetVisitors()
|
||||
{
|
||||
_visitors = await VisitorService.GetVisitorsAsync(PageState.Site.SiteId, DateTime.UtcNow.AddDays(-_days));
|
||||
if (_users)
|
||||
if (_type == "users")
|
||||
{
|
||||
_visitors = _visitors.Where(item => item.UserId != null).ToList();
|
||||
}
|
||||
|
@ -154,6 +181,7 @@ else
|
|||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
|
||||
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention, true);
|
||||
settings = SettingService.SetSetting(settings, "VisitorCorrelation", _correlation, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
|
||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||
|
@ -164,4 +192,9 @@ else
|
|||
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPageChange(int page)
|
||||
{
|
||||
_page = page;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,12 +57,12 @@
|
|||
<div class="table-responsive">
|
||||
<table class="@Class">
|
||||
<thead>
|
||||
<tr>@Header</tr>
|
||||
<tr class="@RowClass">@Header</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in ItemList)
|
||||
{
|
||||
<tr>@Row(item)</tr>
|
||||
<tr class="@RowClass">@Row(item)</tr>
|
||||
@if (Detail != null)
|
||||
{
|
||||
<tr>@Detail(item)</tr>
|
||||
|
@ -75,28 +75,37 @@
|
|||
@if (Format == "Grid" && Row != null)
|
||||
{
|
||||
int count = 0;
|
||||
if (ItemList != null)
|
||||
int rows = 0;
|
||||
int cols = 0;
|
||||
if (ItemList != null)
|
||||
{
|
||||
count = (int)Math.Ceiling(ItemList.Count() / (decimal)_columns) * _columns;
|
||||
if (_columns == 0)
|
||||
{
|
||||
count = ItemList.Count();
|
||||
rows = 1;
|
||||
cols = count;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = (int)Math.Ceiling(ItemList.Count() / (decimal)_columns) * _columns;
|
||||
rows = count / _columns;
|
||||
cols = _columns;
|
||||
}
|
||||
}
|
||||
<div class="@Class">
|
||||
@if (Header != null)
|
||||
@for (int row = 0; row < rows; row++)
|
||||
{
|
||||
<div class="row"><div class="col">@Header</div></div>
|
||||
}
|
||||
@for (int row = 0; row < (count / _columns); row++)
|
||||
{
|
||||
<div class="row">
|
||||
@for (int col = 0; col < _columns; col++)
|
||||
<div class="@RowClass">
|
||||
@for (int col = 0; col < cols; col++)
|
||||
{
|
||||
int index = (row * _columns) + col;
|
||||
if (index < ItemList.Count())
|
||||
{
|
||||
<div class="col">@Row(ItemList.ElementAt(index))</div>
|
||||
<div class="@ColumnClass">@Row(ItemList.ElementAt(index))</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="col"> </div>
|
||||
<div> </div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
@ -160,7 +169,7 @@
|
|||
private int _displayPages = 5;
|
||||
private int _startPage = 0;
|
||||
private int _endPage = 0;
|
||||
private int _columns = 1;
|
||||
private int _columns = 0;
|
||||
|
||||
[Parameter]
|
||||
public string Format { get; set; } // Table or Grid
|
||||
|
@ -169,10 +178,10 @@
|
|||
public string Toolbar { get; set; } // Top, Bottom or Both
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment Header { get; set; } = null;
|
||||
public RenderFragment Header { get; set; } = null; // only applicable to Table layouts
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment<TableItem> Row { get; set; } = null;
|
||||
public RenderFragment<TableItem> Row { get; set; } = null; // required
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
|
||||
|
@ -184,16 +193,25 @@
|
|||
public string PageSize { get; set; } // number of items to display on a page
|
||||
|
||||
[Parameter]
|
||||
public string Columns { get; set; } // only applicable to Grid layouts
|
||||
public string Columns { get; set; } // only applicable to Grid layouts - default is zero indicating use responsive behavior
|
||||
|
||||
[Parameter]
|
||||
public string CurrentPage { get; set; } // optional property to set the initial page to display
|
||||
public string CurrentPage { get; set; } // sets the initial page to display
|
||||
|
||||
[Parameter]
|
||||
public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection
|
||||
|
||||
[Parameter]
|
||||
public string Class { get; set; }
|
||||
public string Class { get; set; } // class for the containing element - ie. <table> for Table or <div> for Grid
|
||||
|
||||
[Parameter]
|
||||
public string RowClass { get; set; } // class for row element - ie. <tr> for Table or <div> for Grid
|
||||
|
||||
[Parameter]
|
||||
public string ColumnClass { get; set; } // class for column element - only applicable to Grid format
|
||||
|
||||
[Parameter]
|
||||
public Action<int> OnPageChange { get; set; } // a method to be executed in the calling component when the page changes
|
||||
|
||||
private IEnumerable<TableItem> ItemList { get; set; }
|
||||
|
||||
|
@ -217,11 +235,35 @@
|
|||
}
|
||||
else
|
||||
{
|
||||
Class = "container-fluid px-0";
|
||||
Class = "container-fluid";
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(PageSize))
|
||||
if (string.IsNullOrEmpty(RowClass))
|
||||
{
|
||||
if (Format == "Table")
|
||||
{
|
||||
RowClass = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
RowClass = "row";
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(ColumnClass))
|
||||
{
|
||||
if (Format == "Table")
|
||||
{
|
||||
ColumnClass = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
ColumnClass = "col";
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(PageSize))
|
||||
{
|
||||
_maxItems = int.Parse(PageSize);
|
||||
}
|
||||
|
@ -268,6 +310,7 @@
|
|||
{
|
||||
_endPage = _pages;
|
||||
}
|
||||
OnPageChange?.Invoke(_page);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
|
|
|
@ -100,160 +100,172 @@
|
|||
}
|
||||
|
||||
@code {
|
||||
private string _permissionnames = string.Empty;
|
||||
private List<Role> _roles;
|
||||
private List<PermissionString> _permissions;
|
||||
private List<User> _users = new List<User>();
|
||||
private string _username = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
private string _permissionnames = string.Empty;
|
||||
private List<Role> _roles;
|
||||
private List<PermissionString> _permissions;
|
||||
private List<User> _users = new List<User>();
|
||||
private string _username = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string EntityName { get; set; }
|
||||
[Parameter]
|
||||
public string EntityName { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string PermissionNames { get; set; }
|
||||
[Parameter]
|
||||
public string PermissionNames { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Permissions { get; set; }
|
||||
[Parameter]
|
||||
public string Permissions { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (string.IsNullOrEmpty(PermissionNames))
|
||||
{
|
||||
_permissionnames = Shared.PermissionNames.View + "," + Shared.PermissionNames.Edit;
|
||||
}
|
||||
else
|
||||
{
|
||||
_permissionnames = PermissionNames;
|
||||
}
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (string.IsNullOrEmpty(PermissionNames))
|
||||
{
|
||||
_permissionnames = Shared.PermissionNames.View + "," + Shared.PermissionNames.Edit;
|
||||
}
|
||||
else
|
||||
{
|
||||
_permissionnames = PermissionNames;
|
||||
}
|
||||
|
||||
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId);
|
||||
_roles.Insert(0, new Role { Name = RoleNames.Everyone });
|
||||
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId);
|
||||
_roles.Insert(0, new Role { Name = RoleNames.Everyone });
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_roles.Add(new Role { Name = RoleNames.Host });
|
||||
}
|
||||
|
||||
_permissions = new List<PermissionString>();
|
||||
_permissions = new List<PermissionString>();
|
||||
|
||||
foreach (string permissionname in _permissionnames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
// initialize with admin role
|
||||
_permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = RoleNames.Admin });
|
||||
}
|
||||
foreach (string permissionname in _permissionnames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
// initialize with admin role
|
||||
_permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = RoleNames.Admin });
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Permissions))
|
||||
{
|
||||
// populate permissions
|
||||
foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions))
|
||||
{
|
||||
if (_permissions.Find(item => item.PermissionName == permissionstring.PermissionName) != null)
|
||||
{
|
||||
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(Permissions))
|
||||
{
|
||||
// populate permissions
|
||||
foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions))
|
||||
{
|
||||
if (_permissions.Find(item => item.PermissionName == permissionstring.PermissionName) != null)
|
||||
{
|
||||
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions;
|
||||
}
|
||||
|
||||
if (permissionstring.Permissions.Contains("["))
|
||||
{
|
||||
foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (user.Contains("]"))
|
||||
{
|
||||
var userid = int.Parse(user.Substring(0, user.IndexOf("]")));
|
||||
if (_users.Where(item => item.UserId == userid).FirstOrDefault() == null)
|
||||
{
|
||||
_users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (permissionstring.Permissions.Contains("["))
|
||||
{
|
||||
foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (user.Contains("]"))
|
||||
{
|
||||
var userid = int.Parse(user.Substring(0, user.IndexOf("]")));
|
||||
if (_users.Where(item => item.UserId == userid).FirstOrDefault() == null)
|
||||
{
|
||||
_users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool? GetPermissionValue(string permissions, string securityKey)
|
||||
{
|
||||
if ((";" + permissions + ";").Contains(";" + "!" + securityKey + ";"))
|
||||
{
|
||||
return false; // deny permission
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((";" + permissions + ";").Contains(";" + securityKey + ";"))
|
||||
{
|
||||
return true; // grant permission
|
||||
}
|
||||
else
|
||||
{
|
||||
return null; // not specified
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool? GetPermissionValue(string permissions, string securityKey)
|
||||
{
|
||||
if ((";" + permissions + ";").Contains(";" + "!" + securityKey + ";"))
|
||||
{
|
||||
return false; // deny permission
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((";" + permissions + ";").Contains(";" + securityKey + ";"))
|
||||
{
|
||||
return true; // grant permission
|
||||
}
|
||||
else
|
||||
{
|
||||
return null; // not specified
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetPermissionDisabled(string roleName)
|
||||
=> roleName == RoleNames.Admin
|
||||
? true
|
||||
: false;
|
||||
private bool GetPermissionDisabled(string roleName)
|
||||
=> (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) ? true : false;
|
||||
|
||||
private async Task AddUser()
|
||||
{
|
||||
if (_users.Where(item => item.Username == _username).FirstOrDefault() == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await UserService.GetUserAsync(_username, ModuleState.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
_users.Add(user);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
_message = Localizer["Message.Username.DontExist"];
|
||||
}
|
||||
}
|
||||
private async Task AddUser()
|
||||
{
|
||||
if (_users.Where(item => item.Username == _username).FirstOrDefault() == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await UserService.GetUserAsync(_username, ModuleState.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
_users.Add(user);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
_message = Localizer["Message.Username.DontExist"];
|
||||
}
|
||||
}
|
||||
|
||||
_username = string.Empty;
|
||||
}
|
||||
_username = string.Empty;
|
||||
}
|
||||
|
||||
private void PermissionChanged(bool? value, string permissionName, string securityId)
|
||||
{
|
||||
var selected = value;
|
||||
var permission = _permissions.Find(item => item.PermissionName == permissionName);
|
||||
if (permission != null)
|
||||
{
|
||||
var ids = permission.Permissions.Split(';').ToList();
|
||||
private void PermissionChanged(bool? value, string permissionName, string securityId)
|
||||
{
|
||||
var selected = value;
|
||||
var permission = _permissions.Find(item => item.PermissionName == permissionName);
|
||||
if (permission != null)
|
||||
{
|
||||
var ids = permission.Permissions.Split(';').ToList();
|
||||
|
||||
ids.Remove(securityId); // remove grant permission
|
||||
ids.Remove("!" + securityId); // remove deny permission
|
||||
ids.Remove(securityId); // remove grant permission
|
||||
ids.Remove("!" + securityId); // remove deny permission
|
||||
|
||||
switch (selected)
|
||||
{
|
||||
case true:
|
||||
ids.Add(securityId); // add grant permission
|
||||
break;
|
||||
case false:
|
||||
ids.Add("!" + securityId); // add deny permission
|
||||
break;
|
||||
case null:
|
||||
break; // permission not specified
|
||||
}
|
||||
switch (selected)
|
||||
{
|
||||
case true:
|
||||
ids.Add(securityId); // add grant permission
|
||||
break;
|
||||
case false:
|
||||
ids.Add("!" + securityId); // add deny permission
|
||||
break;
|
||||
case null:
|
||||
break; // permission not specified
|
||||
}
|
||||
|
||||
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionName)].Permissions = string.Join(";", ids.ToArray());
|
||||
}
|
||||
}
|
||||
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionName)].Permissions = string.Join(";", ids.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public string GetPermissions()
|
||||
{
|
||||
ValidatePermissions();
|
||||
return UserSecurity.SetPermissionStrings(_permissions);
|
||||
}
|
||||
public string GetPermissions()
|
||||
{
|
||||
ValidatePermissions();
|
||||
return UserSecurity.SetPermissionStrings(_permissions);
|
||||
}
|
||||
|
||||
private void ValidatePermissions()
|
||||
{
|
||||
PermissionString permission;
|
||||
for (int i = 0; i < _permissions.Count; i++)
|
||||
{
|
||||
permission = _permissions[i];
|
||||
List<string> ids = permission.Permissions.Split(';').ToList();
|
||||
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
||||
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
|
||||
permission.Permissions = string.Join(";", ids.ToArray());
|
||||
private void ValidatePermissions()
|
||||
{
|
||||
PermissionString permission;
|
||||
for (int i = 0; i < _permissions.Count; i++)
|
||||
{
|
||||
permission = _permissions[i];
|
||||
List<string> ids = permission.Permissions.Split(';').ToList();
|
||||
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
||||
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
ids.Remove("!" + RoleNames.Admin); // remove deny administrators
|
||||
ids.Remove("!" + RoleNames.Host); // remove deny host users
|
||||
if (!ids.Contains(RoleNames.Host) && !ids.Contains(RoleNames.Admin))
|
||||
{
|
||||
// add administrators role if host user role is not assigned
|
||||
ids.Add(RoleNames.Admin);
|
||||
}
|
||||
}
|
||||
permission.Permissions = string.Join(";", ids.ToArray());
|
||||
_permissions[i] = permission;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,11 +71,11 @@
|
|||
</div>
|
||||
@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
|
||||
{
|
||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_content" rows="10"></textarea>
|
||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
|
||||
}
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
|
@ -83,110 +83,121 @@
|
|||
</div>
|
||||
|
||||
@code {
|
||||
private ElementReference _editorElement;
|
||||
private ElementReference _toolBar;
|
||||
private bool _filemanagervisible = false;
|
||||
private FileManager _fileManager;
|
||||
private string _content = string.Empty;
|
||||
private string _original = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
private ElementReference _editorElement;
|
||||
private ElementReference _toolBar;
|
||||
private bool _filemanagervisible = false;
|
||||
private FileManager _fileManager;
|
||||
private string _richhtml = string.Empty;
|
||||
private string _originalrichhtml = string.Empty;
|
||||
private string _rawhtml = string.Empty;
|
||||
private string _originalrawhtml = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string Content { get; set; }
|
||||
[Parameter]
|
||||
public string Content { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; } = false;
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; } = "Enter Your Content...";
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; } = "Enter Your Content...";
|
||||
|
||||
// parameters only applicable to rich text editor
|
||||
[Parameter]
|
||||
public RenderFragment ToolbarContent { get; set; }
|
||||
// parameters only applicable to rich text editor
|
||||
[Parameter]
|
||||
public RenderFragment ToolbarContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Theme { get; set; } = "snow";
|
||||
[Parameter]
|
||||
public string Theme { get; set; } = "snow";
|
||||
|
||||
[Parameter]
|
||||
public string DebugLevel { get; set; } = "info";
|
||||
[Parameter]
|
||||
public string DebugLevel { get; set; } = "info";
|
||||
|
||||
[Parameter]
|
||||
public bool AllowFileManagement { get; set; } = true;
|
||||
[Parameter]
|
||||
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/quill1.3.7.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-interop.js" }
|
||||
};
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_content = Content; // raw HTML
|
||||
await RefreshRichText();
|
||||
}
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_richhtml = Content;
|
||||
_rawhtml = Content;
|
||||
_originalrawhtml = _rawhtml; // preserve for comparison later
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
if (firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
|
||||
await interop.CreateEditor(
|
||||
_editorElement,
|
||||
_toolBar,
|
||||
ReadOnly,
|
||||
Placeholder,
|
||||
Theme,
|
||||
DebugLevel);
|
||||
if (firstRender)
|
||||
{
|
||||
await interop.CreateEditor(
|
||||
_editorElement,
|
||||
_toolBar,
|
||||
ReadOnly,
|
||||
Placeholder,
|
||||
Theme,
|
||||
DebugLevel);
|
||||
|
||||
await interop.LoadEditorContent(_editorElement, Content);
|
||||
await interop.LoadEditorContent(_editorElement, _richhtml);
|
||||
|
||||
_content = Content; // raw HTML
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// 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 void CloseFileManager()
|
||||
{
|
||||
_filemanagervisible = false;
|
||||
_message = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
public void RefreshRichText()
|
||||
{
|
||||
_richhtml = _rawhtml;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task RefreshRichText()
|
||||
{
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
await interop.LoadEditorContent(_editorElement, _content);
|
||||
}
|
||||
public async Task RefreshRawHtml()
|
||||
{
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
_rawhtml = await interop.GetHtml(_editorElement);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
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<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 richhtml = await interop.GetHtml(_editorElement);
|
||||
if (richhtml != _originalrichhtml)
|
||||
{
|
||||
return richhtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
// return original raw html content
|
||||
return _originalrawhtml;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InsertImage()
|
||||
|
@ -212,23 +223,4 @@
|
|||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,95 +9,186 @@
|
|||
@inject IStringLocalizer<Edit> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@if (_content != null)
|
||||
{
|
||||
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
@if (!string.IsNullOrEmpty(_content))
|
||||
{
|
||||
<br />
|
||||
<br />
|
||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
||||
}
|
||||
}
|
||||
<TabStrip>
|
||||
<TabPanel Name="Edit" Heading="Edit" ResourceKey="Edit">
|
||||
@if (_content != null)
|
||||
{
|
||||
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
@if (!string.IsNullOrEmpty(_content))
|
||||
{
|
||||
<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;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </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 {
|
||||
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 = "css/quill/quill1.3.7.bubble.css" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.7.snow.css" }
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" }
|
||||
};
|
||||
|
||||
private RichTextEditor RichTextEditorHtml;
|
||||
private bool _allowfilemanagement;
|
||||
private string _content = null;
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
private RichTextEditor RichTextEditorHtml;
|
||||
private bool _allowfilemanagement;
|
||||
private string _content = null;
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
private List<Models.HtmlText> _htmltexts;
|
||||
private string _view = "";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_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);
|
||||
if (htmltext != null)
|
||||
{
|
||||
_content = htmltext.Content;
|
||||
_content = Utilities.FormatContent(_content, PageState.Alias, "render");
|
||||
_createdby = htmltext.CreatedBy;
|
||||
_createdon = htmltext.CreatedOn;
|
||||
_modifiedby = htmltext.ModifiedBy;
|
||||
_modifiedon = htmltext.ModifiedOn;
|
||||
}
|
||||
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 LoadContent()
|
||||
{
|
||||
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||
if (htmltext != null)
|
||||
{
|
||||
_content = htmltext.Content;
|
||||
_content = Utilities.FormatContent(_content, PageState.Alias, "render");
|
||||
_createdby = htmltext.CreatedBy;
|
||||
_createdon = htmltext.CreatedOn;
|
||||
_modifiedby = htmltext.ModifiedBy;
|
||||
_modifiedon = htmltext.ModifiedOn;
|
||||
}
|
||||
else
|
||||
{
|
||||
_content = string.Empty;
|
||||
}
|
||||
|
||||
private async Task SaveContent()
|
||||
{
|
||||
string content = await RichTextEditorHtml.GetHtml();
|
||||
content = Utilities.FormatContent(content, PageState.Alias, "save");
|
||||
_htmltexts = await HtmlTextService.GetHtmlTextsAsync(ModuleState.ModuleId);
|
||||
_htmltexts = _htmltexts.OrderByDescending(item => item.CreatedOn).ToList();
|
||||
|
||||
try
|
||||
{
|
||||
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);
|
||||
}
|
||||
_view = "";
|
||||
}
|
||||
|
||||
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 SaveContent()
|
||||
{
|
||||
string content = await RichTextEditorHtml.GetHtml();
|
||||
content = Utilities.FormatContent(content, PageState.Alias, "save");
|
||||
|
||||
try
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Documentation;
|
||||
|
@ -13,24 +15,29 @@ namespace Oqtane.Modules.HtmlText.Services
|
|||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
public async Task DeleteHtmlTextAsync(int moduleId)
|
||||
{
|
||||
await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", EntityNames.Module, moduleId));
|
||||
await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlTextId}/{moduleId}", EntityNames.Module, moduleId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Modules.HtmlText.Models;
|
||||
|
||||
namespace Oqtane.Modules.HtmlText.Services
|
||||
{
|
||||
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
|
||||
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 UpdateHtmlTextAsync(Models.HtmlText htmltext);
|
||||
|
||||
Task DeleteHtmlTextAsync(int ModuleId);
|
||||
Task DeleteHtmlTextAsync(int htmlTextId, int moduleId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<OutputType>Exe</OutputType>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>3.0.2</Version>
|
||||
<Version>3.0.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -13,7 +13,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
|
|
@ -231,4 +231,10 @@
|
|||
<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>
|
||||
</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>
|
|
@ -264,4 +264,10 @@
|
|||
<data name="Clickable.Text" xml:space="preserve">
|
||||
<value>Clickable?</value>
|
||||
</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>
|
|
@ -175,7 +175,7 @@
|
|||
<value>Details</value>
|
||||
</data>
|
||||
<data name="Filter.HelpText" xml:space="preserve">
|
||||
<value>Comma delimited list of terms which may exist in IP addresses, user agents, or languages which identify visitors which should not be tracked (ie. bots)</value>
|
||||
<value>Comma delimited list of terms which may exist in IP addresses, user agents, or languages identifying visitors which should not be tracked</value>
|
||||
</data>
|
||||
<data name="Filter.Text" xml:space="preserve">
|
||||
<value>Filter:</value>
|
||||
|
@ -186,4 +186,10 @@
|
|||
<data name="Retention.Text" xml:space="preserve">
|
||||
<value>Retention (Days):</value>
|
||||
</data>
|
||||
<data name="Correlation.HelpText" xml:space="preserve">
|
||||
<value>Indicate if new visitors to this site should be correlated based on their IP Address</value>
|
||||
</data>
|
||||
<data name="Correlation.Text" xml:space="preserve">
|
||||
<value>Correlate Visitors?</value>
|
||||
</data>
|
||||
</root>
|
|
@ -117,10 +117,52 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</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">
|
||||
<value>An Error Occurred Loading Content</value>
|
||||
</data>
|
||||
<data name="Error.Content.Restore" xml:space="preserve">
|
||||
<value>Error Restoring Version</value>
|
||||
</data>
|
||||
<data name="Error.Content.Save" xml:space="preserve">
|
||||
<value>An Error Occurred Saving Content</value>
|
||||
</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>
|
|
@ -259,7 +259,7 @@
|
|||
<value>Upgrade</value>
|
||||
</data>
|
||||
<data name="Username" xml:space="preserve">
|
||||
<value>Username:</value>
|
||||
<value>Username</value>
|
||||
</data>
|
||||
<data name="Version" xml:space="preserve">
|
||||
<value>Version</value>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
// obtained from https://cdnjs.com/libraries
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.0.2/css/bootstrap.min.css", Integrity = "sha512-usVBAd66/NpVNfBge19gws2j6JZinnca12rAe2l+d+QkLU9fiG02O1X8Q6hepIpr/EYKZvKx/I9WsnujJuOmBA==", CrossOrigin = "anonymous" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css", Integrity = "sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==", CrossOrigin = "anonymous" },
|
||||
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.1.3/js/bootstrap.bundle.min.js", Integrity = "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", CrossOrigin = "anonymous" }
|
||||
};
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
@attribute [OqtaneIgnore]
|
||||
|
||||
<span class="app-moduletitle">
|
||||
@((MarkupString)title)
|
||||
<a id="@ModuleState.PageModuleId.ToString()">
|
||||
@((MarkupString)title)
|
||||
</a>
|
||||
</span>
|
||||
|
||||
@code {
|
||||
|
|
|
@ -279,5 +279,19 @@ namespace Oqtane.UI
|
|||
}
|
||||
}
|
||||
|
||||
public Task ScrollToId(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
_jsRuntime.InvokeVoidAsync(
|
||||
"Oqtane.Interop.scrollToId",
|
||||
id);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
@inject IModuleService ModuleService
|
||||
@inject IUrlMappingService UrlMappingService
|
||||
@inject ILogService LogService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@implements IHandleAfterRender
|
||||
|
||||
@DynamicComponent
|
||||
|
@ -234,6 +235,7 @@
|
|||
};
|
||||
|
||||
OnStateChange?.Invoke(_pagestate);
|
||||
await ScrollToFragment(_pagestate.Uri);
|
||||
}
|
||||
}
|
||||
else // page not found
|
||||
|
@ -242,7 +244,7 @@
|
|||
var urlMapping = await UrlMappingService.GetUrlMappingAsync(site.SiteId, route.PagePath);
|
||||
if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl))
|
||||
{
|
||||
var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl;
|
||||
var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl;
|
||||
NavigationManager.NavigateTo(url, false);
|
||||
}
|
||||
else // not mapped
|
||||
|
@ -262,238 +264,259 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// site does not exist
|
||||
}
|
||||
}
|
||||
|
||||
private async void LocationChanged(object sender, LocationChangedEventArgs args)
|
||||
{
|
||||
_absoluteUri = args.Location;
|
||||
await Refresh();
|
||||
}
|
||||
|
||||
Task IHandleAfterRender.OnAfterRenderAsync()
|
||||
{
|
||||
if (!_navigationInterceptionEnabled)
|
||||
{
|
||||
_navigationInterceptionEnabled = true;
|
||||
return NavigationInterception.EnableNavigationInterceptionAsync();
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> ParseQueryString(string query)
|
||||
{
|
||||
Dictionary<string, string> querystring = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // case insensistive keys
|
||||
if (!string.IsNullOrEmpty(query))
|
||||
{
|
||||
if (query.StartsWith("?"))
|
||||
{
|
||||
query = query.Substring(1); // ignore "?"
|
||||
}
|
||||
foreach (string kvp in query.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (kvp != "")
|
||||
{
|
||||
if (kvp.Contains("="))
|
||||
{
|
||||
string[] pair = kvp.Split('=');
|
||||
querystring.Add(pair[0], pair[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
querystring.Add(kvp, "true"); // default parameter when no value is provided
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return querystring;
|
||||
}
|
||||
|
||||
private async Task<Page> ProcessPage(Page page, Site site, User user)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (page.IsPersonalizable && user != null)
|
||||
{
|
||||
// load the personalized page
|
||||
page = await PageService.GetPageAsync(page.PageId, user.UserId);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(page.ThemeType))
|
||||
{
|
||||
page.ThemeType = site.DefaultThemeType;
|
||||
}
|
||||
|
||||
page.Panes = new List<string>();
|
||||
page.Resources = new List<Resource>();
|
||||
|
||||
string panes = PaneNames.Admin;
|
||||
Type themetype = Type.GetType(page.ThemeType);
|
||||
if (themetype == null)
|
||||
{
|
||||
// fallback
|
||||
page.ThemeType = Constants.DefaultTheme;
|
||||
themetype = Type.GetType(Constants.DefaultTheme);
|
||||
}
|
||||
if (themetype != null)
|
||||
{
|
||||
var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
|
||||
if (themeobject != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(themeobject.Panes))
|
||||
{
|
||||
panes = themeobject.Panes;
|
||||
}
|
||||
page.Resources = ManagePageResources(page.Resources, themeobject.Resources);
|
||||
}
|
||||
}
|
||||
page.Panes = panes.Replace(";", ",").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// error loading theme or layout
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
private (Page Page, List<Module> Modules) ProcessModules(Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype)
|
||||
{
|
||||
var paneindex = new Dictionary<string, int>();
|
||||
foreach (Module module in modules)
|
||||
{
|
||||
// initialize module control properties
|
||||
module.SecurityAccessLevel = SecurityAccessLevel.Host;
|
||||
module.ControlTitle = "";
|
||||
module.Actions = "";
|
||||
module.UseAdminContainer = false;
|
||||
module.PaneModuleIndex = -1;
|
||||
module.PaneModuleCount = 0;
|
||||
|
||||
if ((module.PageId == page.PageId || module.ModuleId == moduleid))
|
||||
{
|
||||
var typename = Constants.ErrorModule;
|
||||
if (module.ModuleDefinition != null && (module.ModuleDefinition.Runtimes == "" || module.ModuleDefinition.Runtimes.Contains(Runtime)))
|
||||
{
|
||||
typename = module.ModuleDefinition.ControlTypeTemplate;
|
||||
|
||||
// handle default action
|
||||
if (action == Constants.DefaultAction && !string.IsNullOrEmpty(module.ModuleDefinition.DefaultAction))
|
||||
{
|
||||
action = module.ModuleDefinition.DefaultAction;
|
||||
}
|
||||
|
||||
// check if the module defines custom action routes
|
||||
if (module.ModuleDefinition.ControlTypeRoutes != "")
|
||||
{
|
||||
foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (route.StartsWith(action + "="))
|
||||
{
|
||||
typename = route.Replace(action + "=", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ensure component exists and implements IModuleControl
|
||||
module.ModuleType = "";
|
||||
if (Constants.DefaultModuleActions.Contains(action, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, action);
|
||||
}
|
||||
else
|
||||
{
|
||||
typename = typename.Replace(Constants.ActionToken, action);
|
||||
}
|
||||
Type moduletype = Type.GetType(typename, false, true); // case insensitive
|
||||
if (moduletype != null && moduletype.GetInterfaces().Contains(typeof(IModuleControl)))
|
||||
{
|
||||
module.ModuleType = Utilities.GetFullTypeName(moduletype.AssemblyQualifiedName); // get actual type name
|
||||
}
|
||||
|
||||
// get additional metadata from IModuleControl interface
|
||||
if (moduletype != null && module.ModuleType != "")
|
||||
{
|
||||
// retrieve module component resources
|
||||
var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources);
|
||||
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
||||
{
|
||||
// settings components are embedded within a framework settings module
|
||||
moduletype = Type.GetType(module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action), false, true);
|
||||
if (moduletype != null)
|
||||
{
|
||||
moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources);
|
||||
}
|
||||
}
|
||||
|
||||
// additional metadata needed for admin components
|
||||
if (module.ModuleId == moduleid && action != "")
|
||||
{
|
||||
module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
|
||||
module.ControlTitle = moduleobject.Title;
|
||||
module.Actions = moduleobject.Actions;
|
||||
module.UseAdminContainer = moduleobject.UseAdminContainer;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure module's pane exists in current page and if not, assign it to the Admin pane
|
||||
if (page.Panes == null || page.Panes.FindIndex(item => item.Equals(module.Pane, StringComparison.OrdinalIgnoreCase)) == -1)
|
||||
{
|
||||
module.Pane = PaneNames.Admin;
|
||||
}
|
||||
|
||||
// calculate module position within pane
|
||||
if (paneindex.ContainsKey(module.Pane.ToLower()))
|
||||
{
|
||||
paneindex[module.Pane.ToLower()] += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
paneindex.Add(module.Pane.ToLower(), 0);
|
||||
}
|
||||
|
||||
module.PaneModuleIndex = paneindex[module.Pane.ToLower()];
|
||||
|
||||
// container fallback
|
||||
if (string.IsNullOrEmpty(module.ContainerType))
|
||||
{
|
||||
module.ContainerType = defaultcontainertype;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Module module in modules.Where(item => item.PageId == page.PageId))
|
||||
{
|
||||
if (paneindex.ContainsKey(module.Pane.ToLower()))
|
||||
{
|
||||
module.PaneModuleCount = paneindex[module.Pane.ToLower()] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return (page, modules);
|
||||
}
|
||||
|
||||
private List<Resource> ManagePageResources(List<Resource> pageresources, List<Resource> resources)
|
||||
{
|
||||
if (resources != null)
|
||||
{
|
||||
foreach (var resource in resources)
|
||||
{
|
||||
// ensure resource does not exist already
|
||||
if (pageresources.Find(item => item.Url == resource.Url) == null)
|
||||
{
|
||||
pageresources.Add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pageresources;
|
||||
}
|
||||
|
||||
private async Task ScrollToFragment(Uri uri)
|
||||
{
|
||||
var fragment = uri.Fragment;
|
||||
if (fragment.StartsWith('#'))
|
||||
{
|
||||
// handle text fragment (https://example.org/#test:~:text=foo)
|
||||
var id = fragment.Substring(1);
|
||||
var index = id.IndexOf(":~:", StringComparison.Ordinal);
|
||||
if (index > 0)
|
||||
{
|
||||
id = id.Substring(0, index);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
await interop.ScrollToId(id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// site does not exist
|
||||
}
|
||||
}
|
||||
|
||||
private async void LocationChanged(object sender, LocationChangedEventArgs args)
|
||||
{
|
||||
_absoluteUri = args.Location;
|
||||
await Refresh();
|
||||
}
|
||||
|
||||
Task IHandleAfterRender.OnAfterRenderAsync()
|
||||
{
|
||||
if (!_navigationInterceptionEnabled)
|
||||
{
|
||||
_navigationInterceptionEnabled = true;
|
||||
return NavigationInterception.EnableNavigationInterceptionAsync();
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> ParseQueryString(string query)
|
||||
{
|
||||
Dictionary<string, string> querystring = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // case insensistive keys
|
||||
if (!string.IsNullOrEmpty(query))
|
||||
{
|
||||
if (query.StartsWith("?"))
|
||||
{
|
||||
query = query.Substring(1); // ignore "?"
|
||||
}
|
||||
foreach (string kvp in query.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (kvp != "")
|
||||
{
|
||||
if (kvp.Contains("="))
|
||||
{
|
||||
string[] pair = kvp.Split('=');
|
||||
querystring.Add(pair[0], pair[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
querystring.Add(kvp, "true"); // default parameter when no value is provided
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return querystring;
|
||||
}
|
||||
|
||||
private async Task<Page> ProcessPage(Page page, Site site, User user)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (page.IsPersonalizable && user != null)
|
||||
{
|
||||
// load the personalized page
|
||||
page = await PageService.GetPageAsync(page.PageId, user.UserId);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(page.ThemeType))
|
||||
{
|
||||
page.ThemeType = site.DefaultThemeType;
|
||||
}
|
||||
|
||||
page.Panes = new List<string>();
|
||||
page.Resources = new List<Resource>();
|
||||
|
||||
string panes = PaneNames.Admin;
|
||||
Type themetype = Type.GetType(page.ThemeType);
|
||||
if (themetype == null)
|
||||
{
|
||||
// fallback
|
||||
page.ThemeType = Constants.DefaultTheme;
|
||||
themetype = Type.GetType(Constants.DefaultTheme);
|
||||
}
|
||||
if (themetype != null)
|
||||
{
|
||||
var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
|
||||
if (themeobject != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(themeobject.Panes))
|
||||
{
|
||||
panes = themeobject.Panes;
|
||||
}
|
||||
page.Resources = ManagePageResources(page.Resources, themeobject.Resources);
|
||||
}
|
||||
}
|
||||
page.Panes = panes.Replace(";", ",").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// error loading theme or layout
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
private (Page Page, List<Module> Modules) ProcessModules(Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype)
|
||||
{
|
||||
var paneindex = new Dictionary<string, int>();
|
||||
foreach (Module module in modules)
|
||||
{
|
||||
// initialize module control properties
|
||||
module.SecurityAccessLevel = SecurityAccessLevel.Host;
|
||||
module.ControlTitle = "";
|
||||
module.Actions = "";
|
||||
module.UseAdminContainer = false;
|
||||
module.PaneModuleIndex = -1;
|
||||
module.PaneModuleCount = 0;
|
||||
|
||||
if ((module.PageId == page.PageId || module.ModuleId == moduleid))
|
||||
{
|
||||
var typename = Constants.ErrorModule;
|
||||
if (module.ModuleDefinition != null && (module.ModuleDefinition.Runtimes == "" || module.ModuleDefinition.Runtimes.Contains(Runtime)))
|
||||
{
|
||||
typename = module.ModuleDefinition.ControlTypeTemplate;
|
||||
|
||||
// handle default action
|
||||
if (action == Constants.DefaultAction && !string.IsNullOrEmpty(module.ModuleDefinition.DefaultAction))
|
||||
{
|
||||
action = module.ModuleDefinition.DefaultAction;
|
||||
}
|
||||
|
||||
// check if the module defines custom action routes
|
||||
if (module.ModuleDefinition.ControlTypeRoutes != "")
|
||||
{
|
||||
foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (route.StartsWith(action + "="))
|
||||
{
|
||||
typename = route.Replace(action + "=", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ensure component exists and implements IModuleControl
|
||||
module.ModuleType = "";
|
||||
if (Constants.DefaultModuleActions.Contains(action, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, action);
|
||||
}
|
||||
else
|
||||
{
|
||||
typename = typename.Replace(Constants.ActionToken, action);
|
||||
}
|
||||
Type moduletype = Type.GetType(typename, false, true); // case insensitive
|
||||
if (moduletype != null && moduletype.GetInterfaces().Contains(typeof(IModuleControl)))
|
||||
{
|
||||
module.ModuleType = Utilities.GetFullTypeName(moduletype.AssemblyQualifiedName); // get actual type name
|
||||
}
|
||||
|
||||
// get additional metadata from IModuleControl interface
|
||||
if (moduletype != null && module.ModuleType != "")
|
||||
{
|
||||
// retrieve module component resources
|
||||
var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources);
|
||||
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
||||
{
|
||||
// settings components are embedded within a framework settings module
|
||||
moduletype = Type.GetType(module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action), false, true);
|
||||
if (moduletype != null)
|
||||
{
|
||||
moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources);
|
||||
}
|
||||
}
|
||||
|
||||
// additional metadata needed for admin components
|
||||
if (module.ModuleId == moduleid && action != "")
|
||||
{
|
||||
module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
|
||||
module.ControlTitle = moduleobject.Title;
|
||||
module.Actions = moduleobject.Actions;
|
||||
module.UseAdminContainer = moduleobject.UseAdminContainer;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure module's pane exists in current page and if not, assign it to the Admin pane
|
||||
if (page.Panes == null || page.Panes.FindIndex(item => item.Equals(module.Pane, StringComparison.OrdinalIgnoreCase)) == -1)
|
||||
{
|
||||
module.Pane = PaneNames.Admin;
|
||||
}
|
||||
|
||||
// calculate module position within pane
|
||||
if (paneindex.ContainsKey(module.Pane.ToLower()))
|
||||
{
|
||||
paneindex[module.Pane.ToLower()] += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
paneindex.Add(module.Pane.ToLower(), 0);
|
||||
}
|
||||
|
||||
module.PaneModuleIndex = paneindex[module.Pane.ToLower()];
|
||||
|
||||
// container fallback
|
||||
if (string.IsNullOrEmpty(module.ContainerType))
|
||||
{
|
||||
module.ContainerType = defaultcontainertype;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Module module in modules.Where(item => item.PageId == page.PageId))
|
||||
{
|
||||
if (paneindex.ContainsKey(module.Pane.ToLower()))
|
||||
{
|
||||
module.PaneModuleCount = paneindex[module.Pane.ToLower()] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return (page, modules);
|
||||
}
|
||||
|
||||
private List<Resource> ManagePageResources(List<Resource> pageresources, List<Resource> resources)
|
||||
{
|
||||
if (resources != null)
|
||||
{
|
||||
foreach (var resource in resources)
|
||||
{
|
||||
// ensure resource does not exist already
|
||||
if (pageresources.Find(item => item.Url == resource.Url) == null)
|
||||
{
|
||||
pageresources.Add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pageresources;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.0.2</Version>
|
||||
<Version>3.0.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -10,7 +10,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.MySQL</id>
|
||||
<version>3.0.2</version>
|
||||
<version>3.0.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane MySQL Provider</title>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.0.2</Version>
|
||||
<Version>3.0.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -10,7 +10,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.PostgreSQL</id>
|
||||
<version>3.0.2</version>
|
||||
<version>3.0.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane PostgreSQL Provider</title>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.0.2</Version>
|
||||
<Version>3.0.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -10,7 +10,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.SqlServer</id>
|
||||
<version>3.0.2</version>
|
||||
<version>3.0.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQL Server Provider</title>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.0.2</Version>
|
||||
<Version>3.0.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -10,7 +10,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.Sqlite</id>
|
||||
<version>3.0.2</version>
|
||||
<version>3.0.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQLite Provider</title>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -30,6 +30,11 @@ namespace Oqtane.Database.Sqlite
|
|||
return table.Column<int>(name: name, nullable: false).Annotation("Sqlite:Autoincrement", true);
|
||||
}
|
||||
|
||||
public override void DropColumn(MigrationBuilder builder, string name, string table)
|
||||
{
|
||||
// not implemented as SQLite does not support dropping columns
|
||||
}
|
||||
|
||||
public override string ConcatenateSql(params string[] values)
|
||||
{
|
||||
var returnValue = String.Empty;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Client</id>
|
||||
<version>3.0.2</version>
|
||||
<version>3.0.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>3.0.2</version>
|
||||
<version>3.0.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -11,8 +11,8 @@
|
|||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v3.0.1/Oqtane.Framework.3.0.1.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v3.0.3/Oqtane.Framework.3.0.3.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane framework</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Server</id>
|
||||
<version>3.0.2</version>
|
||||
<version>3.0.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Shared</id>
|
||||
<version>3.0.2</version>
|
||||
<version>3.0.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Updater</id>
|
||||
<version>3.0.2</version>
|
||||
<version>3.0.3</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.0.2.Install.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.0.3.Install.zip" -Force
|
|
@ -1 +1 @@
|
|||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.0.2.Upgrade.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.0.3.Upgrade.zip" -Force
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
||||
using Oqtane.Databases.Interfaces;
|
||||
|
@ -75,6 +76,11 @@ namespace Oqtane.Databases
|
|||
|
||||
}
|
||||
|
||||
public virtual void DropColumn(MigrationBuilder builder, string name, string table)
|
||||
{
|
||||
builder.DropColumn(name, table);
|
||||
}
|
||||
|
||||
public abstract DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
||||
|
||||
|
@ -31,6 +32,8 @@ namespace Oqtane.Databases.Interfaces
|
|||
|
||||
public void UpdateIdentityStoreTableNames(ModelBuilder builder);
|
||||
|
||||
public void DropColumn(MigrationBuilder builder, string name, string table);
|
||||
|
||||
public DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,11 +24,11 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
{
|
||||
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();
|
||||
LoadSatelliteAssemblies(supportedCultures);
|
||||
services.AddOqtaneServices(runtime);
|
||||
services.AddOqtaneServices();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
return services;
|
||||
}
|
||||
|
||||
private static IServiceCollection AddOqtaneServices(this IServiceCollection services, Runtime runtime)
|
||||
private static IServiceCollection AddOqtaneServices(this IServiceCollection services)
|
||||
{
|
||||
if (services is null)
|
||||
{
|
||||
|
@ -223,20 +223,15 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
}
|
||||
|
||||
// register server startup services
|
||||
var startUps = assembly.GetInstances<IServerStartup>();
|
||||
foreach (var startup in startUps)
|
||||
{
|
||||
startup.ConfigureServices(services);
|
||||
}
|
||||
// dynamically register server startup services
|
||||
assembly.GetInstances<IServerStartup>()
|
||||
.ToList()
|
||||
.ForEach(x => x.ConfigureServices(services));
|
||||
|
||||
if (runtime == Runtime.Server)
|
||||
{
|
||||
// register client startup services if running on server
|
||||
assembly.GetInstances<IClientStartup>()
|
||||
.ToList()
|
||||
.ForEach(x => x.ConfigureServices(services));
|
||||
}
|
||||
// dynamically register client startup services (these services will only be used when running on Blazor Server)
|
||||
assembly.GetInstances<IClientStartup>()
|
||||
.ToList()
|
||||
.ForEach(x => x.ConfigureServices(services));
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
|
|
@ -28,14 +28,14 @@ namespace Oqtane.Infrastructure
|
|||
{
|
||||
public class DatabaseManager : IDatabaseManager
|
||||
{
|
||||
private readonly IConfigurationRoot _config;
|
||||
private readonly IConfigManager _config;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
private readonly IMemoryCache _cache;
|
||||
private readonly IConfigManager _configManager;
|
||||
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;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
|
|
|
@ -125,6 +125,12 @@ namespace Oqtane.Infrastructure
|
|||
case "ref": // ref/net*/...
|
||||
filename = ExtractFile(entry, Path.Combine(binPath, "ref"), 2);
|
||||
break;
|
||||
case "refs": // refs/net*/...
|
||||
filename = ExtractFile(entry, Path.Combine(binPath, "refs"), 2);
|
||||
break;
|
||||
case "content": // content/...
|
||||
filename = ExtractFile(entry, contentRootPath, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (filename != "")
|
||||
|
|
|
@ -14,18 +14,18 @@ namespace Oqtane.Infrastructure
|
|||
public class LogManager : ILogManager
|
||||
{
|
||||
private readonly ILogRepository _logs;
|
||||
private readonly IConfigurationRoot _config;
|
||||
private readonly ITenantManager _tenantManager;
|
||||
private readonly IConfigManager _config;
|
||||
private readonly IUserPermissions _userPermissions;
|
||||
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;
|
||||
_tenantManager = tenantManager;
|
||||
_config = config;
|
||||
_userPermissions = userPermissions;
|
||||
_accessor = accessor;
|
||||
_alias = tenantManager.GetAlias();
|
||||
}
|
||||
|
||||
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.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
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
|
@ -16,8 +15,8 @@ namespace Oqtane.Infrastructure
|
|||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
// check if framework is installed
|
||||
var config = context.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration;
|
||||
if (!string.IsNullOrEmpty(config.GetConnectionString("DefaultConnection")))
|
||||
var config = context.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
|
||||
if (config.IsInstalled())
|
||||
{
|
||||
// get alias
|
||||
var tenantManager = context.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager;
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
||||
using Oqtane.Databases.Interfaces;
|
||||
using Oqtane.Interfaces;
|
||||
// ReSharper disable BuiltInTypeReferenceStyleForMemberAccess
|
||||
|
||||
namespace Oqtane.Migrations.EntityBuilders
|
||||
|
@ -126,7 +124,7 @@ namespace Oqtane.Migrations.EntityBuilders
|
|||
|
||||
public void DropColumn(string name)
|
||||
{
|
||||
_migrationBuilder.DropColumn(RewriteName(name), RewriteName(EntityTableName));
|
||||
ActiveDatabase.DropColumn(_migrationBuilder, RewriteName(name), RewriteName(EntityTableName));
|
||||
}
|
||||
|
||||
|
||||
|
|
29
Oqtane.Server/Migrations/Tenant/03000301_AddMetaToPage.cs
Normal file
29
Oqtane.Server/Migrations/Tenant/03000301_AddMetaToPage.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,8 @@ using Oqtane.Infrastructure;
|
|||
using Oqtane.Controllers;
|
||||
using System.Net;
|
||||
using Oqtane.Documentation;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Oqtane.Modules.HtmlText.Controllers
|
||||
{
|
||||
|
@ -22,18 +24,60 @@ namespace Oqtane.Modules.HtmlText.Controllers
|
|||
_htmlText = htmlText;
|
||||
}
|
||||
|
||||
// GET api/<controller>/5
|
||||
[HttpGet("{id}")]
|
||||
[Authorize(Policy = PolicyNames.ViewModule)]
|
||||
public Models.HtmlText Get(int id)
|
||||
// GET: api/<controller>?moduleid=x
|
||||
[HttpGet]
|
||||
[Authorize(Roles = RoleNames.Registered)]
|
||||
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);
|
||||
}
|
||||
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;
|
||||
return null;
|
||||
}
|
||||
|
@ -53,27 +97,7 @@ namespace Oqtane.Modules.HtmlText.Controllers
|
|||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText 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);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Html/Text Post Attempt {HtmlText}", htmlText);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
|
@ -81,18 +105,18 @@ namespace Oqtane.Modules.HtmlText.Controllers
|
|||
|
||||
// DELETE api/<controller>/5
|
||||
[ValidateAntiForgeryToken]
|
||||
[HttpDelete("{id}")]
|
||||
[HttpDelete("{id}/{moduleid}")]
|
||||
[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);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Html/Text Deleted {HtmlTextId}", id);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,19 +43,10 @@ namespace Oqtane.Modules.HtmlText.Manager
|
|||
public void ImportModule(Module module, string content, string version)
|
||||
{
|
||||
content = WebUtility.HtmlDecode(content);
|
||||
var htmlText = _htmlText.GetHtmlText(module.ModuleId);
|
||||
if (htmlText != null)
|
||||
{
|
||||
htmlText.Content = content;
|
||||
_htmlText.UpdateHtmlText(htmlText);
|
||||
}
|
||||
else
|
||||
{
|
||||
htmlText = new Models.HtmlText();
|
||||
htmlText.ModuleId = module.ModuleId;
|
||||
htmlText.Content = content;
|
||||
_htmlText.AddHtmlText(htmlText);
|
||||
}
|
||||
var htmlText = new Models.HtmlText();
|
||||
htmlText.ModuleId = module.ModuleId;
|
||||
htmlText.Content = content;
|
||||
_htmlText.AddHtmlText(htmlText);
|
||||
}
|
||||
|
||||
public bool Install(Tenant tenant, string version)
|
||||
|
|
|
@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore;
|
|||
using System.Linq;
|
||||
using Oqtane.Modules.HtmlText.Models;
|
||||
using Oqtane.Documentation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Oqtane.Modules.HtmlText.Repository
|
||||
{
|
||||
|
@ -15,11 +16,15 @@ namespace Oqtane.Modules.HtmlText.Repository
|
|||
_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)
|
||||
{
|
||||
|
@ -28,16 +33,9 @@ namespace Oqtane.Modules.HtmlText.Repository
|
|||
return htmlText;
|
||||
}
|
||||
|
||||
public Models.HtmlText UpdateHtmlText(Models.HtmlText htmlText)
|
||||
public void DeleteHtmlText(int htmlTextId)
|
||||
{
|
||||
_db.Entry(htmlText).State = EntityState.Modified;
|
||||
_db.SaveChanges();
|
||||
return htmlText;
|
||||
}
|
||||
|
||||
public void DeleteHtmlText(int moduleId)
|
||||
{
|
||||
Models.HtmlText htmlText = _db.HtmlText.FirstOrDefault(item => item.ModuleId == moduleId);
|
||||
Models.HtmlText htmlText = _db.HtmlText.FirstOrDefault(item => item.HtmlTextId == htmlTextId);
|
||||
if (htmlText != null) _db.HtmlText.Remove(htmlText);
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using Oqtane.Documentation;
|
||||
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")]
|
||||
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 UpdateHtmlText(Models.HtmlText htmlText);
|
||||
void DeleteHtmlText(int moduleId);
|
||||
void DeleteHtmlText(int htmlTextId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>3.0.2</Version>
|
||||
<Version>3.0.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -11,11 +11,13 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
<IsPackable>true</IsPackable>
|
||||
<DefineConstants>$(DefineConstants);OQTANE;OQTANE3</DefineConstants>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="wwwroot\Modules\Templates\**" />
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@model Oqtane.Pages.HostModel
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="@Model.Language">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>@Model.Title</title>
|
||||
@Html.Raw(@Model.Meta)
|
||||
<base href="~/" />
|
||||
<link id="app-favicon" rel="shortcut icon" type="image/x-icon" href="@Model.FavIcon" />
|
||||
@if (!string.IsNullOrEmpty(Model.PWAScript))
|
||||
|
@ -20,34 +21,42 @@
|
|||
@Html.Raw(Model.HeadResources)
|
||||
</head>
|
||||
<body>
|
||||
@(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" />
|
||||
|
||||
<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))
|
||||
@if (string.IsNullOrEmpty(Model.Message))
|
||||
{
|
||||
@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>
|
||||
</html>
|
||||
|
|
|
@ -5,7 +5,6 @@ using Oqtane.Modules;
|
|||
using Oqtane.Models;
|
||||
using Oqtane.Themes;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Oqtane.Repository;
|
||||
|
@ -20,12 +19,13 @@ using Microsoft.AspNetCore.Http;
|
|||
using System.Security.Claims;
|
||||
using System.Net;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Oqtane.Enums;
|
||||
|
||||
namespace Oqtane.Pages
|
||||
{
|
||||
public class HostModel : PageModel
|
||||
{
|
||||
private IConfiguration _configuration;
|
||||
private IConfigManager _configuration;
|
||||
private readonly ITenantManager _tenantManager;
|
||||
private readonly ILocalizationManager _localizationManager;
|
||||
private readonly ILanguageRepository _languages;
|
||||
|
@ -36,8 +36,9 @@ namespace Oqtane.Pages
|
|||
private readonly IVisitorRepository _visitors;
|
||||
private readonly IAliasRepository _aliases;
|
||||
private readonly ISettingRepository _settings;
|
||||
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)
|
||||
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;
|
||||
_tenantManager = tenantManager;
|
||||
|
@ -50,8 +51,10 @@ namespace Oqtane.Pages
|
|||
_visitors = visitors;
|
||||
_aliases = aliases;
|
||||
_settings = settings;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string Language = "en";
|
||||
public string AntiForgeryToken = "";
|
||||
public string Runtime = "Server";
|
||||
public RenderMode RenderMode = RenderMode.Server;
|
||||
|
@ -60,9 +63,11 @@ namespace Oqtane.Pages
|
|||
public string HeadResources = "";
|
||||
public string BodyResources = "";
|
||||
public string Title = "";
|
||||
public string Meta = "";
|
||||
public string FavIcon = "favicon.ico";
|
||||
public string PWAScript = "";
|
||||
public string ThemeType = "";
|
||||
public string Message = "";
|
||||
|
||||
public IActionResult OnGet()
|
||||
{
|
||||
|
@ -80,15 +85,15 @@ namespace Oqtane.Pages
|
|||
}
|
||||
|
||||
// if framework is installed
|
||||
if (!string.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection")))
|
||||
if (_configuration.IsInstalled())
|
||||
{
|
||||
var alias = _tenantManager.GetAlias();
|
||||
if (alias != null)
|
||||
{
|
||||
var url = WebUtility.UrlDecode(HttpContext.Request.GetEncodedUrl());
|
||||
|
||||
// redirect non-default alias
|
||||
if (!alias.IsDefault)
|
||||
// redirect non-default alias unless you are trying to access site settings
|
||||
if (!alias.IsDefault && !url.Contains("admin/site"))
|
||||
{
|
||||
var aliases = _aliases.GetAliases().Where(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId);
|
||||
if (aliases.Where(item => item.IsDefault).FirstOrDefault() != null)
|
||||
|
@ -105,7 +110,7 @@ namespace Oqtane.Pages
|
|||
}
|
||||
|
||||
var site = _sites.GetSite(alias.SiteId);
|
||||
if (site != null)
|
||||
if (site != null && !site.IsDeleted)
|
||||
{
|
||||
Route route = new Route(url, alias.Path);
|
||||
|
||||
|
@ -145,6 +150,7 @@ namespace Oqtane.Pages
|
|||
{
|
||||
Title = Title + " - " + page.Name;
|
||||
}
|
||||
Meta = page.Meta;
|
||||
|
||||
// include theme resources
|
||||
if (!string.IsNullOrEmpty(page.ThemeType))
|
||||
|
@ -162,32 +168,50 @@ namespace Oqtane.Pages
|
|||
return RedirectPermanent(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// include global resources
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
ProcessHostResources(assembly);
|
||||
ProcessModuleControls(assembly);
|
||||
ProcessThemeControls(assembly);
|
||||
}
|
||||
|
||||
// set culture if not specified
|
||||
if (HttpContext.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName] == null)
|
||||
{
|
||||
// set default language for site if the culture is not supported
|
||||
var languages = _languages.GetLanguages(alias.SiteId);
|
||||
if (languages.Any() && languages.All(l => l.Code != CultureInfo.CurrentUICulture.Name))
|
||||
// include global resources
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
var defaultLanguage = languages.Where(l => l.IsDefault).SingleOrDefault() ?? languages.First();
|
||||
SetLocalizationCookie(defaultLanguage.Code);
|
||||
ProcessHostResources(assembly);
|
||||
ProcessModuleControls(assembly);
|
||||
ProcessThemeControls(assembly);
|
||||
}
|
||||
else
|
||||
|
||||
// set culture if not specified
|
||||
string culture = HttpContext.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName];
|
||||
if (culture == null)
|
||||
{
|
||||
SetLocalizationCookie(_localizationManager.GetDefaultCulture());
|
||||
// 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
|
||||
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();
|
||||
|
@ -195,85 +219,131 @@ namespace Oqtane.Pages
|
|||
|
||||
private void TrackVisitor(int SiteId)
|
||||
{
|
||||
// get request attributes
|
||||
string useragent = (Request.Headers[HeaderNames.UserAgent] != StringValues.Empty) ? Request.Headers[HeaderNames.UserAgent] : "";
|
||||
string language = (Request.Headers[HeaderNames.AcceptLanguage] != StringValues.Empty) ? Request.Headers[HeaderNames.AcceptLanguage] : "";
|
||||
language = (language.Contains(",")) ? language.Substring(0, language.IndexOf(",")) : language;
|
||||
language = (language.Contains(";")) ? language.Substring(0, language.IndexOf(";")) : language;
|
||||
language = (language.Trim().Length == 0) ? "??" : language;
|
||||
|
||||
// filter
|
||||
var filter = _settings.GetSetting(EntityNames.Site, SiteId, "VisitorFilter");
|
||||
if (filter != null && !string.IsNullOrEmpty(filter.SettingValue))
|
||||
try
|
||||
{
|
||||
foreach (string term in filter.SettingValue.ToLower().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
||||
// get request attributes
|
||||
string useragent = (Request.Headers[HeaderNames.UserAgent] != StringValues.Empty) ? Request.Headers[HeaderNames.UserAgent] : "(none)";
|
||||
string language = (Request.Headers[HeaderNames.AcceptLanguage] != StringValues.Empty) ? Request.Headers[HeaderNames.AcceptLanguage] : "";
|
||||
language = (language.Contains(",")) ? language.Substring(0, language.IndexOf(",")) : language;
|
||||
language = (language.Contains(";")) ? language.Substring(0, language.IndexOf(";")) : language;
|
||||
language = (language.Trim().Length == 0) ? "??" : language;
|
||||
|
||||
// filter
|
||||
string filter = Constants.DefaultVisitorFilter;
|
||||
var settings = _settings.GetSettings(EntityNames.Site, SiteId);
|
||||
if (settings.Any(item => item.SettingName == "VisitorFilter"))
|
||||
{
|
||||
filter = settings.First(item => item.SettingName == "VisitorFilter").SettingValue;
|
||||
}
|
||||
foreach (string term in filter.ToLower().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
||||
{
|
||||
if (RemoteIPAddress.ToLower().Contains(term) || useragent.ToLower().Contains(term) || language.ToLower().Contains(term))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string url = Request.GetEncodedUrl();
|
||||
string referrer = (Request.Headers[HeaderNames.Referer] != StringValues.Empty) ? Request.Headers[HeaderNames.Referer] : "";
|
||||
int? userid = null;
|
||||
if (User.HasClaim(item => item.Type == ClaimTypes.PrimarySid))
|
||||
{
|
||||
userid = int.Parse(User.Claims.First(item => item.Type == ClaimTypes.PrimarySid).Value);
|
||||
}
|
||||
|
||||
var VisitorCookie = "APP_VISITOR_" + SiteId.ToString();
|
||||
if (!int.TryParse(Request.Cookies[VisitorCookie], out VisitorId))
|
||||
{
|
||||
var visitor = new Visitor();
|
||||
visitor.SiteId = SiteId;
|
||||
visitor.IPAddress = RemoteIPAddress;
|
||||
visitor.UserAgent = useragent;
|
||||
visitor.Language = language;
|
||||
visitor.Url = url;
|
||||
visitor.Referrer = referrer;
|
||||
visitor.UserId = userid;
|
||||
visitor.Visits = 1;
|
||||
visitor.CreatedOn = DateTime.UtcNow;
|
||||
visitor.VisitedOn = DateTime.UtcNow;
|
||||
visitor = _visitors.AddVisitor(visitor);
|
||||
|
||||
Response.Cookies.Append(
|
||||
VisitorCookie,
|
||||
visitor.VisitorId.ToString(),
|
||||
new CookieOptions()
|
||||
{
|
||||
Expires = DateTimeOffset.UtcNow.AddYears(1),
|
||||
IsEssential = true
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
var visitor = _visitors.GetVisitor(VisitorId);
|
||||
if (visitor != null)
|
||||
// get other request attributes
|
||||
string url = Request.GetEncodedUrl();
|
||||
string referrer = (Request.Headers[HeaderNames.Referer] != StringValues.Empty) ? Request.Headers[HeaderNames.Referer] : "";
|
||||
int? userid = null;
|
||||
if (User.HasClaim(item => item.Type == ClaimTypes.PrimarySid))
|
||||
{
|
||||
userid = int.Parse(User.Claims.First(item => item.Type == ClaimTypes.PrimarySid).Value);
|
||||
}
|
||||
|
||||
// check if cookie already exists
|
||||
Visitor visitor = null;
|
||||
bool addcookie = false;
|
||||
var VisitorCookie = "APP_VISITOR_" + SiteId.ToString();
|
||||
if (!int.TryParse(Request.Cookies[VisitorCookie], out VisitorId))
|
||||
{
|
||||
// if enabled use IP Address correlation
|
||||
VisitorId = -1;
|
||||
bool correlate = true;
|
||||
if (settings.Any(item => item.SettingName == "VisitorCorrelation"))
|
||||
{
|
||||
correlate = bool.Parse(settings.First(item => item.SettingName == "VisitorCorrelation").SettingValue);
|
||||
}
|
||||
if (correlate)
|
||||
{
|
||||
visitor = _visitors.GetVisitor(SiteId, RemoteIPAddress);
|
||||
if (visitor != null)
|
||||
{
|
||||
VisitorId = visitor.VisitorId;
|
||||
addcookie = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (VisitorId == -1)
|
||||
{
|
||||
// create new visitor
|
||||
visitor = new Visitor();
|
||||
visitor.SiteId = SiteId;
|
||||
visitor.IPAddress = RemoteIPAddress;
|
||||
visitor.UserAgent = useragent;
|
||||
visitor.Language = language;
|
||||
visitor.Url = url;
|
||||
if (!string.IsNullOrEmpty(referrer))
|
||||
{
|
||||
visitor.Referrer = referrer;
|
||||
}
|
||||
if (userid != null)
|
||||
{
|
||||
visitor.UserId = userid;
|
||||
}
|
||||
visitor.Visits += 1;
|
||||
visitor.Referrer = referrer;
|
||||
visitor.UserId = userid;
|
||||
visitor.Visits = 1;
|
||||
visitor.CreatedOn = DateTime.UtcNow;
|
||||
visitor.VisitedOn = DateTime.UtcNow;
|
||||
_visitors.UpdateVisitor(visitor);
|
||||
visitor = _visitors.AddVisitor(visitor);
|
||||
VisitorId = visitor.VisitorId;
|
||||
addcookie = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Response.Cookies.Delete(VisitorCookie);
|
||||
if (visitor == null)
|
||||
{
|
||||
// get visitor if it was not previously loaded
|
||||
visitor = _visitors.GetVisitor(VisitorId);
|
||||
}
|
||||
if (visitor != null)
|
||||
{
|
||||
// update visitor
|
||||
visitor.IPAddress = RemoteIPAddress;
|
||||
visitor.UserAgent = useragent;
|
||||
visitor.Language = language;
|
||||
visitor.Url = url;
|
||||
if (!string.IsNullOrEmpty(referrer))
|
||||
{
|
||||
visitor.Referrer = referrer;
|
||||
}
|
||||
if (userid != null)
|
||||
{
|
||||
visitor.UserId = userid;
|
||||
}
|
||||
visitor.Visits += 1;
|
||||
visitor.VisitedOn = DateTime.UtcNow;
|
||||
_visitors.UpdateVisitor(visitor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove cookie if VisitorId does not exist
|
||||
Response.Cookies.Delete(VisitorCookie);
|
||||
}
|
||||
}
|
||||
|
||||
// append cookie
|
||||
if (addcookie)
|
||||
{
|
||||
Response.Cookies.Append(
|
||||
VisitorCookie,
|
||||
VisitorId.ToString(),
|
||||
new CookieOptions()
|
||||
{
|
||||
Expires = DateTimeOffset.UtcNow.AddYears(1),
|
||||
IsEssential = true
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Other, "Error Tracking Visitor {Error}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Oqtane.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Oqtane.Databases.Interfaces;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Interfaces;
|
||||
using Oqtane.Migrations.Framework;
|
||||
using Oqtane.Repository.Databases.Interfaces;
|
||||
using Oqtane.Shared;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using Oqtane.Infrastructure;
|
||||
|
||||
// ReSharper disable BuiltInTypeReferenceStyleForMemberAccess
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||
|
@ -24,14 +21,14 @@ namespace Oqtane.Repository
|
|||
public class MasterDBContext : DbContext, IMultiDatabase
|
||||
{
|
||||
private readonly IHttpContextAccessor _accessor;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IConfigManager _config;
|
||||
private string _connectionString;
|
||||
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;
|
||||
_configuration = configuration;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public IDatabase ActiveDatabase { get; private set; }
|
||||
|
@ -40,15 +37,15 @@ namespace Oqtane.Repository
|
|||
{
|
||||
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());
|
||||
}
|
||||
|
||||
_databaseType = _configuration.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey];
|
||||
_databaseType = _config.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey];
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(_databaseType))
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace Oqtane.Repository
|
|||
Visitor AddVisitor(Visitor visitor);
|
||||
Visitor UpdateVisitor(Visitor visitor);
|
||||
Visitor GetVisitor(int visitorId);
|
||||
Visitor GetVisitor(int siteId, string IPAddress);
|
||||
void DeleteVisitor(int visitorId);
|
||||
int DeleteVisitors(int age);
|
||||
}
|
||||
|
|
|
@ -125,15 +125,18 @@ namespace Oqtane.Repository
|
|||
foreach (Type containertype in containertypes)
|
||||
{
|
||||
var containerobject = Activator.CreateInstance(containertype) as IThemeControl;
|
||||
theme.Containers.Add(
|
||||
new ThemeControl
|
||||
{
|
||||
TypeName = containertype.FullName + ", " + themeControlType.Assembly.GetName().Name,
|
||||
Name = (string.IsNullOrEmpty(containerobject.Name)) ? Utilities.GetTypeNameLastSegment(containertype.FullName, 0) : containerobject.Name,
|
||||
Thumbnail = containerobject.Thumbnail,
|
||||
Panes = ""
|
||||
}
|
||||
);
|
||||
if (theme.Containers.FirstOrDefault(item => item.TypeName == containertype.FullName + ", " + themeControlType.Assembly.GetName().Name) == null)
|
||||
{
|
||||
theme.Containers.Add(
|
||||
new ThemeControl
|
||||
{
|
||||
TypeName = containertype.FullName + ", " + themeControlType.Assembly.GetName().Name,
|
||||
Name = (string.IsNullOrEmpty(containerobject.Name)) ? Utilities.GetTypeNameLastSegment(containertype.FullName, 0) : containerobject.Name,
|
||||
Thumbnail = containerobject.Thumbnail,
|
||||
Panes = ""
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
themes[index] = theme;
|
||||
|
|
|
@ -41,6 +41,11 @@ namespace Oqtane.Repository
|
|||
return _db.Visitor.Find(visitorId);
|
||||
}
|
||||
|
||||
public Visitor GetVisitor(int siteId, string IPAddress)
|
||||
{
|
||||
return _db.Visitor.FirstOrDefault(item => item.SiteId == siteId && item.IPAddress == IPAddress);
|
||||
}
|
||||
|
||||
public void DeleteVisitor(int visitorId)
|
||||
{
|
||||
Visitor visitor = _db.Visitor.Find(visitorId);
|
||||
|
|
|
@ -6,7 +6,6 @@ using Oqtane.Infrastructure;
|
|||
using Oqtane.Repository;
|
||||
using Oqtane.Models;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Oqtane.Security
|
||||
{
|
||||
|
@ -17,8 +16,8 @@ namespace Oqtane.Security
|
|||
if (context != null && context.Principal.Identity.IsAuthenticated)
|
||||
{
|
||||
// check if framework is installed
|
||||
var config = context.HttpContext.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration;
|
||||
if (!string.IsNullOrEmpty(config.GetConnectionString("DefaultConnection")))
|
||||
var config = context.HttpContext.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
|
||||
if (config.IsInstalled())
|
||||
{
|
||||
var tenantManager = context.HttpContext.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager;
|
||||
var alias = tenantManager.GetAlias();
|
||||
|
|
|
@ -21,7 +21,6 @@ namespace Oqtane
|
|||
{
|
||||
public class Startup
|
||||
{
|
||||
private readonly Runtime _runtime;
|
||||
private readonly bool _useSwagger;
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly string[] _supportedCultures;
|
||||
|
@ -36,7 +35,6 @@ namespace Oqtane
|
|||
Configuration = builder.Build();
|
||||
|
||||
_supportedCultures = localizationManager.GetSupportedCultures();
|
||||
_runtime = (Configuration.GetSection("Runtime").Value == "WebAssembly") ? Runtime.WebAssembly : Runtime.Server;
|
||||
|
||||
//add possibility to switch off swagger on production.
|
||||
_useSwagger = Configuration.GetSection("UseSwagger").Value != "false";
|
||||
|
@ -115,7 +113,7 @@ namespace Oqtane
|
|||
services.AddOqtaneTransientServices();
|
||||
|
||||
// load the external assemblies into the app domain, install services
|
||||
services.AddOqtane(_runtime, _supportedCultures);
|
||||
services.AddOqtane(_supportedCultures);
|
||||
services.AddOqtaneDbContext();
|
||||
|
||||
services.AddMvc()
|
||||
|
|
14
Oqtane.Server/web.Release.config
Normal file
14
Oqtane.Server/web.Release.config
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
|
||||
<location>
|
||||
<system.webServer>
|
||||
<modules xdt:Transform="InsertIfMissing">
|
||||
<remove name="WebDAVModule" xdt:Transform="InsertIfMissing"/>
|
||||
</modules>
|
||||
<handlers xdt:Transform="InsertIfMissing">
|
||||
<remove name="WebDAV" xdt:Transform="InsertIfMissing" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</location>
|
||||
</configuration>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -126,6 +126,10 @@ app {
|
|||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.app-moduletitle a {
|
||||
scroll-margin-top: 7rem;
|
||||
}
|
||||
|
||||
/* Tooltips */
|
||||
.app-tooltip {
|
||||
cursor: help;
|
||||
|
|
1173
Oqtane.Server/wwwroot/css/quill/quill.bubble.css
Normal file
1173
Oqtane.Server/wwwroot/css/quill/quill.bubble.css
Normal file
File diff suppressed because it is too large
Load Diff
1170
Oqtane.Server/wwwroot/css/quill/quill.snow.css
Normal file
1170
Oqtane.Server/wwwroot/css/quill/quill.snow.css
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -376,5 +376,14 @@ Oqtane.Interop = {
|
|||
left: left,
|
||||
behavior: behavior
|
||||
});
|
||||
},
|
||||
scrollToId: function (id) {
|
||||
var element = document.getElementById(id);
|
||||
if (element instanceof HTMLElement) {
|
||||
element.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "start",
|
||||
inline: "nearest"
|
||||
});
|
||||
}
|
||||
};
|
||||
}};
|
||||
|
|
8
Oqtane.Server/wwwroot/js/quill.min.js
vendored
Normal file
8
Oqtane.Server/wwwroot/js/quill.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -61,7 +61,12 @@ namespace Oqtane.Models
|
|||
/// Reference to a Container which will be used for modules on this page.
|
||||
/// </summary>
|
||||
public string DefaultContainerType { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Meta tags to be included in the head of the page
|
||||
/// </summary>
|
||||
public string Meta { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Icon file for this page.
|
||||
/// TODO: unclear what this is for, and what icon library is used. Probably FontAwesome?
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>3.0.2</Version>
|
||||
<Version>3.0.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -11,7 +11,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
|
|
@ -3,8 +3,8 @@ using System;
|
|||
namespace Oqtane.Shared {
|
||||
|
||||
public class Constants {
|
||||
public static readonly string Version = "3.0.2";
|
||||
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2";
|
||||
public static readonly string Version = "3.0.3";
|
||||
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3";
|
||||
public const string PackageId = "Oqtane.Framework";
|
||||
public const string UpdaterPackageId = "Oqtane.Updater";
|
||||
public const string PackageRegistryUrl = "https://www.oqtane.net";
|
||||
|
@ -62,7 +62,7 @@ namespace Oqtane.Shared {
|
|||
[Obsolete(RoleObsoleteMessage)]
|
||||
public const string RegisteredRole = RoleNames.Registered;
|
||||
|
||||
public const string ImageFiles = "jpg,jpeg,jpe,gif,bmp,png,ico";
|
||||
public const string ImageFiles = "jpg,jpeg,jpe,gif,bmp,png,ico,webp";
|
||||
public const string UploadableFiles = ImageFiles + ",mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg,csv";
|
||||
public const string ReservedDevices = "CON,NUL,PRN,COM0,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8,COM9,LPT0,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,CONIN$,CONOUT$";
|
||||
|
||||
|
@ -83,5 +83,7 @@ namespace Oqtane.Shared {
|
|||
public static readonly string RequestVerificationToken = "__RequestVerificationToken";
|
||||
public static readonly string AntiForgeryTokenHeaderName = "X-XSRF-TOKEN-HEADER";
|
||||
public static readonly string AntiForgeryTokenCookieName = "X-XSRF-TOKEN-COOKIE";
|
||||
|
||||
public static readonly string DefaultVisitorFilter = "bot,crawler,slurp,spider,(none),??";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>3.0.2</Version>
|
||||
<Version>3.0.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -11,7 +11,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Version>3.0.2</Version>
|
||||
<Version>3.0.3</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -11,7 +11,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
|
|
@ -43,7 +43,7 @@ Please note that this project is owned by the .NET Foundation and is governed by
|
|||
|
||||
**Additional Instructions**
|
||||
|
||||
- If you have already installed a previous version of Oqtane and you wish to do a clean database install, simply reset the DefaultConnection value in the Oqtane.Server\appsettings.json file to "". This will trigger a re-install when you run the application which will execute the database installation scripts.
|
||||
- If you have already installed a previous version of Oqtane and you wish to do a clean database install, simply reset the DefaultConnection value in the Oqtane.Server\appsettings.json file to "". This will trigger a re-install when you run the application which will execute the database installation.
|
||||
|
||||
- If you want to submit pull requests make sure you install the [Github Extension For Visual Studio](https://visualstudio.github.com/). It is recommended you ignore any local changes you have made to the appsettings.json file before you submit a pull request. To automate this activity, open a command prompt and navigate to the /Oqtane.Server/ folder and enter the command "git update-index --skip-worktree appsettings.json"
|
||||
|
||||
|
@ -55,7 +55,7 @@ Please note that this project is owned by the .NET Foundation and is governed by
|
|||
There is a separate [Documentation repository](https://github.com/oqtane/oqtane.docs) which contains a variety of types of documentation for Oqtane, including API documentation that is auto generated using Docfx. The contents of the repository is published to Githib Pages and is available at [https://docs.oqtane.org](https://docs.oqtane.org/)
|
||||
|
||||
# Roadmap
|
||||
This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist.
|
||||
This project is open source, and therefore is a work in progress...
|
||||
|
||||
Backlog (Not Yet Assigned)
|
||||
- [ ] Allow language specification in Url (#1731)
|
||||
|
@ -67,6 +67,11 @@ V.3.1.0 ( Q1 2022 )
|
|||
- [ ] Allow configuration of password complexity for local authentication
|
||||
- [ ] User account lockout support
|
||||
|
||||
V.3.0.3 ( Feb 15, 2022 )
|
||||
- [x] Url fragment and anchor navigation support
|
||||
- [x] Meta tag support in page head
|
||||
- [x] Html/Text content versioning support
|
||||
|
||||
V.3.0.2 ( Jan 16, 2022 )
|
||||
- [x] Default alias specification, auto alias registration, redirect logic
|
||||
- [x] Improvements to visitor tracking and url mapping
|
||||
|
|
Loading…
Reference in New Issue
Block a user