commit
5fbd64da71
|
@ -170,6 +170,7 @@
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Folder folder;
|
Folder folder;
|
||||||
|
|
||||||
if (_folderId != -1)
|
if (_folderId != -1)
|
||||||
{
|
{
|
||||||
folder = await FolderService.GetFolderAsync(_folderId);
|
folder = await FolderService.GetFolderAsync(_folderId);
|
||||||
|
@ -179,8 +180,6 @@
|
||||||
folder = new Folder();
|
folder = new Folder();
|
||||||
}
|
}
|
||||||
|
|
||||||
folder.SiteId = PageState.Site.SiteId;
|
|
||||||
|
|
||||||
if (_parentId == -1)
|
if (_parentId == -1)
|
||||||
{
|
{
|
||||||
folder.ParentId = null;
|
folder.ParentId = null;
|
||||||
|
@ -190,6 +189,14 @@
|
||||||
folder.ParentId = _parentId;
|
folder.ParentId = _parentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for duplicate folder names
|
||||||
|
if (_folders.Any(item => item.ParentId == folder.ParentId && item.Name == _name && item.FolderId != _folderId))
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Folder.Duplicate"], MessageType.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
folder.SiteId = PageState.Site.SiteId;
|
||||||
folder.Name = _name;
|
folder.Name = _name;
|
||||||
folder.Type = _type;
|
folder.Type = _type;
|
||||||
folder.ImageSizes = _imagesizes;
|
folder.ImageSizes = _imagesizes;
|
||||||
|
|
|
@ -26,8 +26,8 @@ else
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="EditJob" /></td>
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="EditJob" /></td>
|
||||||
<td><ActionLink Action="Log" Class="btn btn-secondary" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="JobLog" /></td>
|
<td><ActionLink Action="Log" Text="Log" Class="btn btn-secondary" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="JobLog" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
|
<td>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
|
||||||
<td>@DisplayFrequency(context.Interval, context.Frequency)</td>
|
<td>@DisplayFrequency(context.Interval, context.Frequency)</td>
|
||||||
|
|
|
@ -63,7 +63,7 @@ else
|
||||||
<th>@Localizer["Function"]</th>
|
<th>@Localizer["Function"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"/{context.LogId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, AddUrlParameters(_level, _function, _rows, _page)))" ResourceKey="LogDetails" /></td>
|
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Text="Details" Parameters="@($"/{context.LogId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, AddUrlParameters(_level, _function, _rows, _page)))" ResourceKey="LogDetails" /></td>
|
||||||
<td class="@GetClass(context.Function)">@context.LogDate</td>
|
<td class="@GetClass(context.Function)">@context.LogDate</td>
|
||||||
<td class="@GetClass(context.Function)">@context.Level</td>
|
<td class="@GetClass(context.Function)">@context.Level</td>
|
||||||
<td class="@GetClass(context.Function)">@context.Feature</td>
|
<td class="@GetClass(context.Function)">@context.Feature</td>
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="categories" HelpText="Comma delimited list of module categories" ResourceKey="Categories">Categories: </Label>
|
<Label Class="col-sm-3" For="categories" HelpText="Comma delimited list of module categories" ResourceKey="Categories">Categories: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="categories" class="form-control" @bind="@_categories" maxlength="200" required />
|
<input id="categories" class="form-control" @bind="@_categories" maxlength="200" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
|
@ -306,10 +306,9 @@
|
||||||
_languages = _languages.OrderBy(item => item.Name).ToList();
|
_languages = _languages.OrderBy(item => item.Name).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group modules by PageId
|
// get distinct pages where module exists
|
||||||
// Get distinct PageIds where modules are present
|
|
||||||
var distinctPageIds = PageState.Modules
|
var distinctPageIds = PageState.Modules
|
||||||
.Where(md => md.ModuleDefinition.ModuleDefinitionId == _moduleDefinitionId && md.IsDeleted == false)
|
.Where(md => md.ModuleDefinition?.ModuleDefinitionId == _moduleDefinitionId && md.IsDeleted == false)
|
||||||
.Select(md => md.PageId)
|
.Select(md => md.PageId)
|
||||||
.Distinct();
|
.Distinct();
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ else
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" ResourceKey="EditModule" /></td>
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" ResourceKey="EditModule" /></td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.AssemblyName != Constants.ClientId)
|
@if (context.AssemblyName != Constants.ClientId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -94,7 +94,7 @@
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Permissions" ResourceKey="Permissions">
|
<TabPanel Name="Permissions" Heading="Permissions" ResourceKey="Permissions">
|
||||||
@if (_permissions != null)
|
@if (_permissions != null)
|
||||||
{
|
{
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@ -126,9 +126,8 @@
|
||||||
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
|
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
public override string Title => "Module Settings";
|
|
||||||
|
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
|
@ -144,7 +143,7 @@
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
private Type _moduleSettingsType;
|
private Type _moduleSettingsType;
|
||||||
private object _moduleSettings;
|
private object _moduleSettings;
|
||||||
private string _moduleSettingsTitle = "Module Settings";
|
private string _moduleSettingsTitle;
|
||||||
private RenderFragment ModuleSettingsComponent { get; set; }
|
private RenderFragment ModuleSettingsComponent { get; set; }
|
||||||
private Type _containerSettingsType;
|
private Type _containerSettingsType;
|
||||||
private object _containerSettings;
|
private object _containerSettings;
|
||||||
|
@ -158,8 +157,10 @@
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
|
SetModuleTitle(Localizer["ModuleSettings.Title"]);
|
||||||
_module = ModuleState.ModuleDefinition.Name;
|
_module = ModuleState.ModuleDefinition.Name;
|
||||||
_title = ModuleState.Title;
|
_title = ModuleState.Title;
|
||||||
|
_moduleSettingsTitle = Localizer["ModuleSettings.Heading"];
|
||||||
_pane = ModuleState.Pane;
|
_pane = ModuleState.Pane;
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, PageState.Page.ThemeType);
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, PageState.Page.ThemeType);
|
||||||
_containerType = ModuleState.ContainerType;
|
_containerType = ModuleState.ContainerType;
|
||||||
|
@ -173,6 +174,7 @@
|
||||||
_effectivedate = Utilities.UtcAsLocalDate(ModuleState.EffectiveDate);
|
_effectivedate = Utilities.UtcAsLocalDate(ModuleState.EffectiveDate);
|
||||||
_expirydate = Utilities.UtcAsLocalDate(ModuleState.ExpiryDate);
|
_expirydate = Utilities.UtcAsLocalDate(ModuleState.ExpiryDate);
|
||||||
|
|
||||||
|
|
||||||
if (ModuleState.ModuleDefinition != null)
|
if (ModuleState.ModuleDefinition != null)
|
||||||
{
|
{
|
||||||
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
||||||
|
|
|
@ -198,12 +198,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
@if (_themeSettingsType != null)
|
|
||||||
{
|
|
||||||
<TabPanel Name="ThemeSettings" Heading=@Localizer["Theme.Heading"] ResourceKey="ThemeSettings">
|
|
||||||
@_themeSettingsComponent
|
|
||||||
</TabPanel>
|
|
||||||
}
|
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
<br />
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||||
|
@ -238,9 +232,6 @@
|
||||||
private string _bodycontent;
|
private string _bodycontent;
|
||||||
private string _permissions = null;
|
private string _permissions = null;
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
private Type _themeSettingsType;
|
|
||||||
private object _themeSettings;
|
|
||||||
private RenderFragment _themeSettingsComponent { get; set; }
|
|
||||||
private bool _refresh = false;
|
private bool _refresh = false;
|
||||||
protected Page _parent = null;
|
protected Page _parent = null;
|
||||||
protected Dictionary<string, string> _icons;
|
protected Dictionary<string, string> _icons;
|
||||||
|
@ -281,7 +272,6 @@
|
||||||
}
|
}
|
||||||
_effectivedate = Utilities.UtcAsLocalDate(PageState.Page.EffectiveDate);
|
_effectivedate = Utilities.UtcAsLocalDate(PageState.Page.EffectiveDate);
|
||||||
_expirydate = Utilities.UtcAsLocalDate(PageState.Page.ExpiryDate);
|
_expirydate = Utilities.UtcAsLocalDate(PageState.Page.ExpiryDate);
|
||||||
ThemeSettings();
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -324,7 +314,6 @@
|
||||||
_themetype = (string)e.Value;
|
_themetype = (string)e.Value;
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||||
_containertype = _containers.First().TypeName;
|
_containertype = _containers.First().TypeName;
|
||||||
ThemeSettings();
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|
||||||
// if theme chosen is different than default site theme, display warning message to user
|
// if theme chosen is different than default site theme, display warning message to user
|
||||||
|
@ -334,28 +323,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThemeSettings()
|
|
||||||
{
|
|
||||||
_themeSettingsType = null;
|
|
||||||
_themeSettingsComponent = null;
|
|
||||||
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
|
||||||
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
|
||||||
{
|
|
||||||
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
|
||||||
if (_themeSettingsType != null)
|
|
||||||
{
|
|
||||||
_themeSettingsComponent = builder =>
|
|
||||||
{
|
|
||||||
builder.OpenComponent(0, _themeSettingsType);
|
|
||||||
builder.AddAttribute(1, "RenderModeBoundary", RenderModeBoundary);
|
|
||||||
builder.AddComponentReferenceCapture(2, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
|
||||||
builder.CloseComponent();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
_refresh = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SavePage()
|
private async Task SavePage()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
|
@ -482,11 +449,11 @@
|
||||||
await logger.LogInformation("Page Added {Page}", page);
|
await logger.LogInformation("Page Added {Page}", page);
|
||||||
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(PageState.ReturnUrl, true);
|
NavigationManager.NavigateTo(page.Path, true); // redirect to page added and reload
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(page.Path); // redirect to new page created
|
NavigationManager.NavigateTo(NavigateUrl()); // redirect to page management
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
@namespace Oqtane.Modules.Admin.Pages
|
@namespace Oqtane.Modules.Admin.Pages
|
||||||
@using Oqtane.Interfaces
|
@using Oqtane.Interfaces
|
||||||
|
@using System.Globalization
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IPageService PageService
|
@inject IPageService PageService
|
||||||
|
@ -362,7 +363,7 @@
|
||||||
_parent = PageState.Pages.FirstOrDefault(item => item.PageId == _page.ParentId);
|
_parent = PageState.Pages.FirstOrDefault(item => item.PageId == _page.ParentId);
|
||||||
}
|
}
|
||||||
_children = new List<Page>();
|
_children = new List<Page>();
|
||||||
foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
|
foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid, CultureInfo.InvariantCulture))))
|
||||||
{
|
{
|
||||||
if (p.PageId != _pageId && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
if (p.PageId != _pageId && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||||
{
|
{
|
||||||
|
@ -643,11 +644,11 @@
|
||||||
await logger.LogInformation("Page Saved {Page}", _page);
|
await logger.LogInformation("Page Saved {Page}", _page);
|
||||||
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(PageState.ReturnUrl, true);
|
NavigationManager.NavigateTo(PageState.ReturnUrl, true); // redirect to page being edited and reload
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl(), true); // redirect to page being edited
|
NavigationManager.NavigateTo(NavigateUrl()); // redirect to page management
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.PageId.ToString())" ResourceKey="EditPage" /></td>
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.PageId.ToString())" ResourceKey="EditPage" /></td>
|
||||||
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
||||||
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => NavigationManager.NavigateTo(Browse(context)))">@Localizer["Browse"]</button></td>
|
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => NavigationManager.NavigateTo(Browse(context)))">@Localizer["Browse"]</button></td>
|
||||||
<td>@(new string('-', context.Level * 2))@(context.Name)</td>
|
<td>@(new string('-', context.Level * 2))@(context.Name)</td>
|
||||||
|
|
|
@ -22,7 +22,7 @@ else
|
||||||
<th>@Localizer["Order"]</th>
|
<th>@Localizer["Order"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditProfile" /></td>
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditProfile" /></td>
|
||||||
<td><ActionDialog Header="Delete Profile" Message="@string.Format(Localizer["Confirm.Profile.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td>
|
<td><ActionDialog Header="Delete Profile" Message="@string.Format(Localizer["Confirm.Profile.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.Title</td>
|
<td>@context.Title</td>
|
||||||
|
|
|
@ -20,9 +20,9 @@ else
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.RoleId.ToString())" Security="SecurityAccessLevel.Edit" Disabled="@(context.IsSystem)" ResourceKey="Edit" /></td>
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.RoleId.ToString())" Security="SecurityAccessLevel.Edit" Disabled="@(context.IsSystem)" ResourceKey="Edit" /></td>
|
||||||
<td><ActionDialog Header="Delete Role" Message="@string.Format(Localizer["Confirm.DeleteUser"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteRole(context))" Disabled="@(context.IsSystem)" ResourceKey="DeleteRole" /></td>
|
<td><ActionDialog Header="Delete Role" Message="@string.Format(Localizer["Confirm.DeleteUser"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteRole(context))" Disabled="@(context.IsSystem)" ResourceKey="DeleteRole" /></td>
|
||||||
<td><ActionLink Action="Users" Parameters="@($"id=" + context.RoleId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Users" /></td>
|
<td><ActionLink Action="Users" Text="Users" Parameters="@($"id=" + context.RoleId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Users" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
|
|
|
@ -319,7 +319,7 @@
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="rendermode" HelpText="The default render mode for the site" ResourceKey="Rendermode">Render Mode: </Label>
|
<Label Class="col-sm-3" For="rendermode" HelpText="The default render mode for the site" ResourceKey="Rendermode">Render Mode: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="rendermode" class="form-select" @bind="@_rendermode" required>
|
<select id="rendermode" class="form-select" value="@_rendermode" @onchange="(e => RenderModeChanged(e))" required>
|
||||||
<option value="@RenderModes.Interactive">@(SharedLocalizer["RenderMode" + @RenderModes.Interactive])</option>
|
<option value="@RenderModes.Interactive">@(SharedLocalizer["RenderMode" + @RenderModes.Interactive])</option>
|
||||||
<option value="@RenderModes.Static">@(SharedLocalizer["RenderMode" + @RenderModes.Static])</option>
|
<option value="@RenderModes.Static">@(SharedLocalizer["RenderMode" + @RenderModes.Static])</option>
|
||||||
<option value="@RenderModes.Headless">@(SharedLocalizer["RenderMode" + @RenderModes.Headless])</option>
|
<option value="@RenderModes.Headless">@(SharedLocalizer["RenderMode" + @RenderModes.Headless])</option>
|
||||||
|
@ -337,7 +337,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="prerender" HelpText="Specifies if interactive components should prerender their output" ResourceKey="Prerender">Prerender? </Label>
|
<Label Class="col-sm-3" For="prerender" HelpText="Specifies if interactive components should prerender their output on the server" ResourceKey="Prerender">Prerender: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="prerender" class="form-select" @bind="@_prerender" required>
|
<select id="prerender" class="form-select" @bind="@_prerender" required>
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
@ -572,6 +572,23 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RenderModeChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
_rendermode = (string)e.Value;
|
||||||
|
switch (_rendermode)
|
||||||
|
{
|
||||||
|
case RenderModes.Interactive:
|
||||||
|
_prerender = "True";
|
||||||
|
break;
|
||||||
|
case RenderModes.Static:
|
||||||
|
_prerender = "False";
|
||||||
|
break;
|
||||||
|
case RenderModes.Headless:
|
||||||
|
_prerender = "False";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveSite()
|
private async Task SaveSite()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
|
|
|
@ -29,7 +29,7 @@ else
|
||||||
<th> </th>
|
<th> </th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ThemeId.ToString())" ResourceKey="EditTheme" /></td>
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.ThemeId.ToString())" ResourceKey="EditTheme" /></td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.AssemblyName != Constants.ClientId)
|
@if (context.AssemblyName != Constants.ClientId)
|
||||||
{
|
{
|
||||||
|
@ -63,7 +63,6 @@ else
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.PackageName, version))>@SharedLocalizer["Upgrade"]</button>
|
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.PackageName, version))>@SharedLocalizer["Upgrade"]</button>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ else
|
||||||
<th>@Localizer["Requested"]</th>
|
<th>@Localizer["Requested"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.UrlMappingId.ToString())" ResourceKey="Edit" /></td>
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.UrlMappingId.ToString())" ResourceKey="Edit" /></td>
|
||||||
<td><ActionDialog Header="Delete Url Mapping" Message="@string.Format(Localizer["Confirm.DeleteUrlMapping"], context.Url)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUrlMapping(context))" ResourceKey="DeleteUrlMapping" /></td>
|
<td><ActionDialog Header="Delete Url Mapping" Message="@string.Format(Localizer["Confirm.DeleteUrlMapping"], context.Url)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUrlMapping(context))" ResourceKey="DeleteUrlMapping" /></td>
|
||||||
<td>
|
<td>
|
||||||
<a href="@Utilities.TenantUrl(PageState.Alias, context.Url)">@context.Url</a>
|
<a href="@Utilities.TenantUrl(PageState.Alias, context.Url)">@context.Url</a>
|
||||||
|
|
|
@ -226,11 +226,6 @@
|
||||||
user.Password = _password;
|
user.Password = _password;
|
||||||
user.Email = email;
|
user.Email = email;
|
||||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||||
user.PhotoFileId = null;
|
|
||||||
if (user.PhotoFileId == -1)
|
|
||||||
{
|
|
||||||
user.PhotoFileId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
|
user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
|
||||||
|
|
||||||
|
|
|
@ -32,13 +32,13 @@ else
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td>
|
<td>
|
||||||
<ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditUser" />
|
<ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditUser" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.UserId == PageState.User.UserId)" ResourceKey="DeleteUser" />
|
<ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.UserId == PageState.User.UserId)" ResourceKey="DeleteUser" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Roles" />
|
<ActionLink Action="Roles" Text="Roles" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Roles" />
|
||||||
</td>
|
</td>
|
||||||
<td>@context.User.Username</td>
|
<td>@context.User.Username</td>
|
||||||
<td>@context.User.DisplayName</td>
|
<td>@context.User.DisplayName</td>
|
||||||
|
|
|
@ -43,7 +43,7 @@ else
|
||||||
<th>@Localizer["Created"]</th>
|
<th>@Localizer["Created"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Detail" Parameters="@($"id={context.VisitorId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, $"type={_type}&days={_days}&page={_page}"))" ResourceKey="Details" /></td>
|
<td><ActionLink Action="Detail" Text="Detail" Parameters="@($"id={context.VisitorId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, $"type={_type}&days={_days}&page={_page}"))" ResourceKey="Details" /></td>
|
||||||
<td>@context.IPAddress</td>
|
<td>@context.IPAddress</td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.UserId != null)
|
@if (context.UserId != null)
|
||||||
|
@ -69,14 +69,20 @@ else
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="duration" HelpText="The duration of a browsing session considered to be a distinct visit (in minutes)" ResourceKey="Duration">Session Duration: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="duration" class="form-control" type="number" min="0" step="1" @bind="@_duration" />
|
||||||
|
</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 identifying visitors which should not be tracked" 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">
|
<div class="col-sm-9">
|
||||||
<textarea id="filter" class="form-control" @bind="@_filter" rows="3"></textarea>
|
<textarea id="filter" class="form-control" @bind="@_filter" rows="3"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of visitor activity to retain" ResourceKey="Retention">Retention (Days): </Label>
|
<Label Class="col-sm-3" For="retention" HelpText="Number of days of visitor activity to retain" ResourceKey="Retention">Retention: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="retention" class="form-control" type="number" min="0" step="1" @bind="@_retention" />
|
<input id="retention" class="form-control" type="number" min="0" step="1" @bind="@_retention" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -103,7 +109,8 @@ else
|
||||||
private int _page = 1;
|
private int _page = 1;
|
||||||
private List<Visitor> _visitors;
|
private List<Visitor> _visitors;
|
||||||
private string _tracking;
|
private string _tracking;
|
||||||
private string _filter = "";
|
private int _duration = 5;
|
||||||
|
private string _filter = "";
|
||||||
private int _retention = 30;
|
private int _retention = 30;
|
||||||
private string _correlation = "true";
|
private string _correlation = "true";
|
||||||
|
|
||||||
|
@ -128,7 +135,8 @@ else
|
||||||
|
|
||||||
_tracking = PageState.Site.VisitorTracking.ToString();
|
_tracking = PageState.Site.VisitorTracking.ToString();
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
_filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter);
|
_duration = int.Parse(SettingService.GetSetting(settings, "VisitorDuration", "5"));
|
||||||
|
_filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter);
|
||||||
_retention = int.Parse(SettingService.GetSetting(settings, "VisitorRetention", "30"));
|
_retention = int.Parse(SettingService.GetSetting(settings, "VisitorRetention", "30"));
|
||||||
_correlation = SettingService.GetSetting(settings, "VisitorCorrelation", "true");
|
_correlation = SettingService.GetSetting(settings, "VisitorCorrelation", "true");
|
||||||
}
|
}
|
||||||
|
@ -179,7 +187,8 @@ else
|
||||||
await SiteService.UpdateSiteAsync(site);
|
await SiteService.UpdateSiteAsync(site);
|
||||||
|
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
|
settings = SettingService.SetSetting(settings, "VisitorDuration", _duration.ToString(), true);
|
||||||
|
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
|
||||||
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention.ToString(), true);
|
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention.ToString(), true);
|
||||||
settings = SettingService.SetSetting(settings, "VisitorCorrelation", _correlation, true);
|
settings = SettingService.SetSetting(settings, "VisitorCorrelation", _correlation, true);
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
|
|
|
@ -35,11 +35,11 @@
|
||||||
{
|
{
|
||||||
if (Disabled)
|
if (Disabled)
|
||||||
{
|
{
|
||||||
<button type="button" class="@Class" disabled>@((MarkupString)_iconSpan) @Text</button>
|
<button type="button" class="@Class" disabled>@((MarkupString)_openIconSpan) @_openText</button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<button type="button" class="@Class" @onclick="DisplayModal">@((MarkupString)_iconSpan) @Text</button>
|
<button type="button" class="@Class" @onclick="DisplayModal">@((MarkupString)_openIconSpan) @_openText</button>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,13 +83,13 @@ else
|
||||||
{
|
{
|
||||||
if (Disabled)
|
if (Disabled)
|
||||||
{
|
{
|
||||||
<button type="button" class="@Class" disabled>@((MarkupString)_iconSpan) @Text</button>
|
<button type="button" class="@Class" disabled>@((MarkupString)_openIconSpan) @_openText</button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<form method="post" @formname="@($"ActionDialogActionForm{Id}")" @onsubmit="DisplayModal" data-enhance>
|
<form method="post" @formname="@($"ActionDialogActionForm{Id}")" @onsubmit="DisplayModal" data-enhance>
|
||||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||||
<button type="submit" class="@Class">@((MarkupString)_iconSpan) @Text</button>
|
<button type="submit" class="@Class">@((MarkupString)_openIconSpan) @_openText</button>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,8 @@ else
|
||||||
private bool _editmode = false;
|
private bool _editmode = false;
|
||||||
private bool _authorized = false;
|
private bool _authorized = false;
|
||||||
private string _iconSpan = string.Empty;
|
private string _iconSpan = string.Empty;
|
||||||
|
private string _openIconSpan = string.Empty;
|
||||||
|
private string _openText = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Header { get; set; } // required
|
public string Header { get; set; } // required
|
||||||
|
@ -138,6 +140,9 @@ else
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
|
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool IconOnly { get; set; } // optional - specifies only icon in opening link
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Id { get; set; } // optional - specifies a unique id for the compoment - required when there are multiple component instances on a page in static rendering
|
public string Id { get; set; } // optional - specifies a unique id for the compoment - required when there are multiple component instances on a page in static rendering
|
||||||
|
|
||||||
|
@ -157,6 +162,8 @@ else
|
||||||
{
|
{
|
||||||
Text = Action;
|
Text = Action;
|
||||||
}
|
}
|
||||||
|
_openText = Text;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Class))
|
if (string.IsNullOrEmpty(Class))
|
||||||
{
|
{
|
||||||
Class = "btn btn-success";
|
Class = "btn btn-success";
|
||||||
|
@ -169,11 +176,17 @@ else
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(IconName))
|
if (!string.IsNullOrEmpty(IconName))
|
||||||
{
|
{
|
||||||
|
if (IconOnly)
|
||||||
|
{
|
||||||
|
_openText = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
if (!IconName.Contains(" "))
|
if (!IconName.Contains(" "))
|
||||||
{
|
{
|
||||||
IconName = "oi oi-" + IconName;
|
IconName = "oi oi-" + IconName;
|
||||||
}
|
}
|
||||||
_iconSpan = $"<span class=\"{IconName}\"></span> ";
|
_openIconSpan = $"<span class=\"{IconName}\"></span>{(IconOnly ? "" : " ")}";
|
||||||
|
_iconSpan = $"<span class=\"{IconName}\"></span> ";
|
||||||
}
|
}
|
||||||
|
|
||||||
Text = Localize(nameof(Text), Text);
|
Text = Localize(nameof(Text), Text);
|
||||||
|
|
|
@ -6,23 +6,24 @@
|
||||||
{
|
{
|
||||||
<div class="@_classname alert-dismissible fade show mb-3" role="alert">
|
<div class="@_classname alert-dismissible fade show mb-3" role="alert">
|
||||||
@((MarkupString)Message)
|
@((MarkupString)Message)
|
||||||
@if (PageState != null)
|
@if (Type == MessageType.Error && PageState != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
@if (Type == MessageType.Error && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
<NavLink class="ms-2" href="@NavigateUrl("admin/log")">View Details</NavLink>
|
||||||
{
|
}
|
||||||
<NavLink class="ms-2" href="@NavigateUrl("admin/log")">View Details</NavLink>
|
@if (ModuleState.RenderMode == RenderModes.Static)
|
||||||
}
|
{
|
||||||
<form method="post" @onsubmit="DismissModal" @formname="@_formname" data-enhance>
|
<a href="@NavigationManager.Uri" class="btn-close" data-dismiss="alert" aria-label="close"></a>
|
||||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
}
|
||||||
<button type="submit" class="btn-close" aria-label="Close"></button>
|
else
|
||||||
</form>
|
{
|
||||||
|
<button type="button" class="btn-close" data-dismiss="alert" aria-label="close" @onclick="CloseMessage"></button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private string _message = string.Empty;
|
||||||
private string _classname = string.Empty;
|
private string _classname = string.Empty;
|
||||||
private string _formname = "ModuleMessageForm";
|
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
|
@ -30,32 +31,13 @@
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public MessageType Type { get; set; }
|
public MessageType Type { get; set; }
|
||||||
|
|
||||||
public void RefreshMessage(string message, MessageType type)
|
[Parameter]
|
||||||
{
|
public RenderModeBoundary Parent { get; set; }
|
||||||
Message = message;
|
|
||||||
Type = type;
|
|
||||||
|
|
||||||
UpdateClassName();
|
|
||||||
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
|
||||||
{
|
|
||||||
if (ModuleState != null)
|
|
||||||
{
|
|
||||||
_formname += ModuleState.PageModuleId.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
UpdateClassName();
|
_message = Message;
|
||||||
}
|
if (!string.IsNullOrEmpty(_message))
|
||||||
|
|
||||||
private void UpdateClassName()
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(Message))
|
|
||||||
{
|
{
|
||||||
_classname = GetMessageType(Type);
|
_classname = GetMessageType(Type);
|
||||||
}
|
}
|
||||||
|
@ -82,9 +64,15 @@
|
||||||
|
|
||||||
return classname;
|
return classname;
|
||||||
}
|
}
|
||||||
|
private void CloseMessage(MouseEventArgs e)
|
||||||
private void DismissModal()
|
|
||||||
{
|
{
|
||||||
Message = "";
|
if(Parent != null)
|
||||||
|
{
|
||||||
|
Parent.DismissMessage();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(NavigationManager.Uri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,16 +23,16 @@
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-center my-2">
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@for (int i = _startPage; i <= _endPage; i++)
|
@for (int i = _startPage; i <= _endPage; i++)
|
||||||
{
|
{
|
||||||
|
@ -40,30 +40,30 @@
|
||||||
if (pager == _page)
|
if (pager == _page)
|
||||||
{
|
{
|
||||||
<li class="page-item app-pager-pointer active">
|
<li class="page-item app-pager-pointer active">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<li class="page-item app-pager-pointer">
|
<li class="page-item app-pager-pointer">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item disabled">
|
<li class="page-item disabled">
|
||||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
<a class="page-link shadow-none" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
|
@ -86,16 +86,16 @@
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-center my-2">
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" href="@PageUrl(_startPage - 1, _search)"><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(_startPage - 1, _search)"><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" href="@PageUrl(((_page > 1) ? _page - 1 : _page), _search)"><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(((_page > 1) ? _page - 1 : _page), _search)"><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@for (int i = _startPage; i <= _endPage; i++)
|
@for (int i = _startPage; i <= _endPage; i++)
|
||||||
{
|
{
|
||||||
|
@ -103,30 +103,30 @@
|
||||||
if (pager == _page)
|
if (pager == _page)
|
||||||
{
|
{
|
||||||
<li class="page-item app-pager-pointer active">
|
<li class="page-item app-pager-pointer active">
|
||||||
<a class="page-link" href="@PageUrl(pager, _search)">@pager</a>
|
<a class="page-link shadow-none" href="@PageUrl(pager, _search)">@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<li class="page-item app-pager-pointer">
|
<li class="page-item app-pager-pointer">
|
||||||
<a class="page-link" href="@PageUrl(pager, _search)">@pager</a>
|
<a class="page-link shadow-none" href="@PageUrl(pager, _search)">@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" href="@PageUrl(((_page < _pages) ? _page + 1 : _page), _search)"><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(((_page < _pages) ? _page + 1 : _page), _search)"><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" href="@PageUrl(_endPage + 1, _search)"><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(_endPage + 1, _search)"><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" href="@PageUrl(_pages, _search)"><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(_pages, _search)"><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item disabled">
|
<li class="page-item disabled">
|
||||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
<a class="page-link shadow-none" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
|
@ -202,16 +202,16 @@
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-center my-2">
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@for (int i = _startPage; i <= _endPage; i++)
|
@for (int i = _startPage; i <= _endPage; i++)
|
||||||
{
|
{
|
||||||
|
@ -219,30 +219,30 @@
|
||||||
if (pager == _page)
|
if (pager == _page)
|
||||||
{
|
{
|
||||||
<li class="page-item app-pager-pointer active">
|
<li class="page-item app-pager-pointer active">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<li class="page-item app-pager-pointer">
|
<li class="page-item app-pager-pointer">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item disabled">
|
<li class="page-item disabled">
|
||||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
<a class="page-link shadow-none" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
|
@ -250,16 +250,16 @@
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-center my-2">
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" href="@PageUrl(_startPage - 1, _search)"><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(_startPage - 1, _search)"><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" href="@PageUrl(((_page > 1) ? _page - 1 : _page), _search)"><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(((_page > 1) ? _page - 1 : _page), _search)"><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@for (int i = _startPage; i <= _endPage; i++)
|
@for (int i = _startPage; i <= _endPage; i++)
|
||||||
{
|
{
|
||||||
|
@ -267,30 +267,30 @@
|
||||||
if (pager == _page)
|
if (pager == _page)
|
||||||
{
|
{
|
||||||
<li class="page-item app-pager-pointer active">
|
<li class="page-item app-pager-pointer active">
|
||||||
<a class="page-link" href="@PageUrl(pager, _search)">@pager</a>
|
<a class="page-link shadow-none" href="@PageUrl(pager, _search)">@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<li class="page-item app-pager-pointer">
|
<li class="page-item app-pager-pointer">
|
||||||
<a class="page-link" href="@PageUrl(pager, _search)">@pager</a>
|
<a class="page-link shadow-none" href="@PageUrl(pager, _search)">@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" href="@PageUrl(((_page < _pages) ? _page + 1 : _page), _search)"><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(((_page < _pages) ? _page + 1 : _page), _search)"><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" href="@PageUrl(_endPage + 1, _search)"><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(_endPage + 1, _search)"><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" href="@PageUrl(_pages, _search)"><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(_pages, _search)"><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item disabled">
|
<li class="page-item disabled">
|
||||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
<a class="page-link shadow-none" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,6 +122,7 @@
|
||||||
|
|
||||||
private string _message = string.Empty;
|
private string _message = string.Empty;
|
||||||
private bool _contentchanged = false;
|
private bool _contentchanged = false;
|
||||||
|
private int _editorIndex;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
|
@ -173,7 +174,11 @@
|
||||||
_rawhtml = Content;
|
_rawhtml = Content;
|
||||||
_originalrawhtml = _rawhtml; // preserve for comparison later
|
_originalrawhtml = _rawhtml; // preserve for comparison later
|
||||||
_originalrichhtml = "";
|
_originalrichhtml = "";
|
||||||
_contentchanged = true; // identifies when Content parameter has changed
|
|
||||||
|
if (Content != _originalrawhtml)
|
||||||
|
{
|
||||||
|
_contentchanged = true; // identifies when Content parameter has changed
|
||||||
|
}
|
||||||
|
|
||||||
if (!AllowRichText)
|
if (!AllowRichText)
|
||||||
{
|
{
|
||||||
|
@ -275,18 +280,18 @@
|
||||||
// return original raw html content
|
// return original raw html content
|
||||||
return _originalrawhtml;
|
return _originalrawhtml;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InsertRichImage()
|
public async Task InsertRichImage()
|
||||||
{
|
{
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
if (_richfilemanager)
|
if (_richfilemanager)
|
||||||
{
|
{
|
||||||
var file = _fileManager.GetFile();
|
var file = _fileManager.GetFile();
|
||||||
if (file != null)
|
if (file != null)
|
||||||
{
|
{
|
||||||
await interop.InsertImage(_editorElement, file.Url, ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name));
|
await interop.InsertImage(_editorElement, file.Url, ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name), _editorIndex);
|
||||||
_richhtml = await interop.GetHtml(_editorElement);
|
_richhtml = await interop.GetHtml(_editorElement);
|
||||||
_richfilemanager = false;
|
_richfilemanager = false;
|
||||||
}
|
}
|
||||||
|
@ -297,6 +302,7 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_editorIndex = await interop.GetCurrentCursor(_editorElement);
|
||||||
_richfilemanager = true;
|
_richfilemanager = true;
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|
|
@ -105,13 +105,25 @@ namespace Oqtane.Modules.Controls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task InsertImage(ElementReference quillElement, string imageUrl, string altText)
|
public ValueTask<int> GetCurrentCursor(ElementReference quillElement)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _jsRuntime.InvokeAsync<int>("Oqtane.RichTextEditor.getCurrentCursor", quillElement);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new ValueTask<int>(Task.FromResult(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task InsertImage(ElementReference quillElement, string imageUrl, string altText, int editorIndex)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_jsRuntime.InvokeAsync<object>(
|
_jsRuntime.InvokeAsync<object>(
|
||||||
"Oqtane.RichTextEditor.insertQuillImage",
|
"Oqtane.RichTextEditor.insertQuillImage",
|
||||||
quillElement, imageUrl, altText);
|
quillElement, imageUrl, altText, editorIndex);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|
|
@ -37,6 +37,10 @@
|
||||||
content = htmltext.Content;
|
content = htmltext.Content;
|
||||||
content = Utilities.FormatContent(content, PageState.Alias, "render");
|
content = Utilities.FormatContent(content, PageState.Alias, "render");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
content = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
@ -52,6 +52,8 @@ namespace Oqtane.Modules
|
||||||
|
|
||||||
public virtual string RenderMode { get { return RenderModes.Interactive; } } // interactive by default
|
public virtual string RenderMode { get { return RenderModes.Interactive; } } // interactive by default
|
||||||
|
|
||||||
|
public virtual bool? Prerender { get { return null; } } // allows the Site Prerender property to be overridden
|
||||||
|
|
||||||
// url parameters
|
// url parameters
|
||||||
public virtual string UrlParametersTemplate { get; set; }
|
public virtual string UrlParametersTemplate { get; set; }
|
||||||
|
|
||||||
|
@ -276,7 +278,6 @@ namespace Oqtane.Modules
|
||||||
|
|
||||||
public void AddModuleMessage(string message, MessageType type, string position)
|
public void AddModuleMessage(string message, MessageType type, string position)
|
||||||
{
|
{
|
||||||
ClearModuleMessage();
|
|
||||||
RenderModeBoundary.AddModuleMessage(message, type, position);
|
RenderModeBoundary.AddModuleMessage(message, type, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>5.1.1</Version>
|
<Version>5.1.2</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
@ -22,9 +22,9 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -195,4 +195,7 @@
|
||||||
<data name="Folder Management" xml:space="preserve">
|
<data name="Folder Management" xml:space="preserve">
|
||||||
<value>Folder Management</value>
|
<value>Folder Management</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.Folder.Duplicate" xml:space="preserve">
|
||||||
|
<value>Folder Name Specified Already Exists In Parent</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -156,7 +156,7 @@
|
||||||
<data name="Module.Text" xml:space="preserve">
|
<data name="Module.Text" xml:space="preserve">
|
||||||
<value>Module:</value>
|
<value>Module:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Module Settings" xml:space="preserve">
|
<data name="ModuleSettings.Heading" xml:space="preserve">
|
||||||
<value>Module Settings</value>
|
<value>Module Settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Pane.HelpText" xml:space="preserve">
|
<data name="Pane.HelpText" xml:space="preserve">
|
||||||
|
@ -177,4 +177,16 @@
|
||||||
<data name="ExpiryDate.Text" xml:space="preserve">
|
<data name="ExpiryDate.Text" xml:space="preserve">
|
||||||
<value>Expiry Date: </value>
|
<value>Expiry Date: </value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Permissions.Text" xml:space="preserve">
|
||||||
|
<value>Permissions</value>
|
||||||
|
</data>
|
||||||
|
<data name="Permissions.Heading" xml:space="preserve">
|
||||||
|
<value>Permissions</value>
|
||||||
|
</data>
|
||||||
|
<data name="ContainerSettings.Heading" xml:space="preserve">
|
||||||
|
<value>Container Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="ModuleSettings.Title" xml:space="preserve">
|
||||||
|
<value>Module Settings</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -147,4 +147,7 @@
|
||||||
<data name="Title" xml:space="preserve">
|
<data name="Title" xml:space="preserve">
|
||||||
<value>Title</value>
|
<value>Title</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Detail.Text" xml:space="preserve">
|
||||||
|
<value>Detail</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -277,10 +277,10 @@
|
||||||
<value>UI Component Settings</value>
|
<value>UI Component Settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Prerender.HelpText" xml:space="preserve">
|
<data name="Prerender.HelpText" xml:space="preserve">
|
||||||
<value>Specifies if interactive components should prerender their output</value>
|
<value>Specifies if interactive components should prerender their output on the server</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Prerender.Text" xml:space="preserve">
|
<data name="Prerender.Text" xml:space="preserve">
|
||||||
<value>Prerender? </value>
|
<value>Prerender: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RenderMode.HelpText" xml:space="preserve">
|
<data name="RenderMode.HelpText" xml:space="preserve">
|
||||||
<value>The default render mode for the site</value>
|
<value>The default render mode for the site</value>
|
||||||
|
|
|
@ -159,4 +159,7 @@
|
||||||
<data name="Url" xml:space="preserve">
|
<data name="Url" xml:space="preserve">
|
||||||
<value>Url</value>
|
<value>Url</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Edit.Text" xml:space="preserve">
|
||||||
|
<value>Edit</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -184,7 +184,7 @@
|
||||||
<value>Number of days of visitor activity to retain</value>
|
<value>Number of days of visitor activity to retain</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Retention.Text" xml:space="preserve">
|
<data name="Retention.Text" xml:space="preserve">
|
||||||
<value>Retention (Days):</value>
|
<value>Retention:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Correlation.HelpText" xml:space="preserve">
|
<data name="Correlation.HelpText" xml:space="preserve">
|
||||||
<value>Indicate if new visitors to this site should be correlated based on their IP Address</value>
|
<value>Indicate if new visitors to this site should be correlated based on their IP Address</value>
|
||||||
|
@ -192,4 +192,10 @@
|
||||||
<data name="Correlation.Text" xml:space="preserve">
|
<data name="Correlation.Text" xml:space="preserve">
|
||||||
<value>Correlate Visitors?</value>
|
<value>Correlate Visitors?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Duration.HelpText" xml:space="preserve">
|
||||||
|
<value>The duration of a browsing session considered to be a distinct visit (in minutes)</value>
|
||||||
|
</data>
|
||||||
|
<data name="Duration.Text" xml:space="preserve">
|
||||||
|
<value>Session Duration:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -453,4 +453,10 @@
|
||||||
<data name="RenderModeStatic" xml:space="preserve">
|
<data name="RenderModeStatic" xml:space="preserve">
|
||||||
<value>Static</value>
|
<value>Static</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Disabled" xml:space="preserve">
|
||||||
|
<value>Disabled</value>
|
||||||
|
</data>
|
||||||
|
<data name="Enabled" xml:space="preserve">
|
||||||
|
<value>Enabled</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -198,4 +198,7 @@
|
||||||
<data name="LocationTop" xml:space="preserve">
|
<data name="LocationTop" xml:space="preserve">
|
||||||
<value>Top</value>
|
<value>Top</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Module.CopyExisting" xml:space="preserve">
|
||||||
|
<value>Copy Existing Module</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
150
Oqtane.Client/Resources/Themes/Controls/ModuleActionsBase.resx
Normal file
150
Oqtane.Client/Resources/Themes/Controls/ModuleActionsBase.resx
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="DeleteModule" xml:space="preserve">
|
||||||
|
<value>Delete Module</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExportContent" xml:space="preserve">
|
||||||
|
<value>Export Content</value>
|
||||||
|
</data>
|
||||||
|
<data name="ImportContent" xml:space="preserve">
|
||||||
|
<value>Import Content</value>
|
||||||
|
</data>
|
||||||
|
<data name="ManageSettings" xml:space="preserve">
|
||||||
|
<value>Manage Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="MoveDown" xml:space="preserve">
|
||||||
|
<value>Move Down</value>
|
||||||
|
</data>
|
||||||
|
<data name="MoveToBottom" xml:space="preserve">
|
||||||
|
<value>Move To Bottom</value>
|
||||||
|
</data>
|
||||||
|
<data name="MoveToTop" xml:space="preserve">
|
||||||
|
<value>Move To Top</value>
|
||||||
|
</data>
|
||||||
|
<data name="MoveUp" xml:space="preserve">
|
||||||
|
<value>MoveUp</value>
|
||||||
|
</data>
|
||||||
|
<data name="PublishModule" xml:space="preserve">
|
||||||
|
<value>Publish Module</value>
|
||||||
|
</data>
|
||||||
|
<data name="UnpublishModule" xml:space="preserve">
|
||||||
|
<value>Unpublish Module</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
namespace Oqtane.Services
|
namespace Oqtane.Services
|
||||||
{
|
{
|
||||||
|
@ -18,7 +19,7 @@ namespace Oqtane.Services
|
||||||
|
|
||||||
public async Task<List<Visitor>> GetVisitorsAsync(int siteId, DateTime fromDate)
|
public async Task<List<Visitor>> GetVisitorsAsync(int siteId, DateTime fromDate)
|
||||||
{
|
{
|
||||||
List<Visitor> visitors = await GetJsonAsync<List<Visitor>>($"{Apiurl}?siteid={siteId}&fromdate={fromDate.ToString("dd-MMM-yyyy")}");
|
List<Visitor> visitors = await GetJsonAsync<List<Visitor>>($"{Apiurl}?siteid={siteId}&fromdate={fromDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)}");
|
||||||
return visitors.OrderByDescending(item => item.VisitedOn).ToList();
|
return visitors.OrderByDescending(item => item.VisitedOn).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
@namespace Oqtane.Themes
|
@namespace Oqtane.Themes
|
||||||
@inherits ContainerBase
|
@inherits ContainerBase
|
||||||
@inject NavigationManager NavigationManager
|
|
||||||
|
|
||||||
<div class="app-admin-modal">
|
<div class="app-admin-modal">
|
||||||
<div class="modal" tabindex="-1" role="dialog">
|
<div class="modal" tabindex="-1" role="dialog">
|
||||||
|
@ -8,10 +7,7 @@
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title"><ModuleTitle /></h5>
|
<h5 class="modal-title"><ModuleTitle /></h5>
|
||||||
<form method="post" class="app-form-inline" @formname="AdminContainerForm" @onsubmit="@CloseModal" data-enhance>
|
<a href="@_url" class="btn-close" aria-label="Close"></a>
|
||||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
|
||||||
<button type="submit" class="btn-close" aria-label="Close"></button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<ModuleInstance />
|
<ModuleInstance />
|
||||||
|
@ -22,9 +18,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private void CloseModal()
|
private string _url;
|
||||||
{
|
|
||||||
NavigationManager.NavigateTo((!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : NavigateUrl());
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
_url = (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : NavigateUrl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,6 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ModuleActionsInteractive PageState="@PageState" ModuleState="@ModuleState" @rendermode="@InteractiveRenderMode.GetInteractiveRenderMode(PageState.Site.Runtime, PageState.Site.Prerender)" />
|
<ModuleActionsInteractive PageState="@PageState" ModuleState="@ModuleState" @rendermode="@InteractiveRenderMode.GetInteractiveRenderMode(PageState.Site.Runtime, false)" />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ using Oqtane.Services;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using Oqtane.UI;
|
using Oqtane.UI;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
// ReSharper disable UnassignedGetOnlyAutoProperty
|
// ReSharper disable UnassignedGetOnlyAutoProperty
|
||||||
// ReSharper disable MemberCanBePrivate.Global
|
// ReSharper disable MemberCanBePrivate.Global
|
||||||
|
@ -20,6 +22,7 @@ namespace Oqtane.Themes.Controls
|
||||||
[Inject] public NavigationManager NavigationManager { get; set; }
|
[Inject] public NavigationManager NavigationManager { get; set; }
|
||||||
[Inject] public IPageModuleService PageModuleService { get; set; }
|
[Inject] public IPageModuleService PageModuleService { get; set; }
|
||||||
[Inject] public IModuleService ModuleService { get; set; }
|
[Inject] public IModuleService ModuleService { get; set; }
|
||||||
|
[Inject] public IStringLocalizer<ModuleActionsBase> Localizer { get; set; }
|
||||||
|
|
||||||
[Parameter] public PageState PageState { get; set; }
|
[Parameter] public PageState PageState { get; set; }
|
||||||
[Parameter] public Module ModuleState { get; set; }
|
[Parameter] public Module ModuleState { get; set; }
|
||||||
|
@ -37,30 +40,30 @@ namespace Oqtane.Themes.Controls
|
||||||
|
|
||||||
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||||
{
|
{
|
||||||
actionList.Add(new ActionViewModel { Icon = Icons.Cog, Name = "Manage Settings", Action = async (u, m) => await Settings(u, m) });
|
actionList.Add(new ActionViewModel { Icon = Icons.Cog, Name = Localizer["ManageSettings"], Action = async (u, m) => await Settings(u, m) });
|
||||||
|
|
||||||
if (UserSecurity.ContainsRole(ModuleState.PermissionList, PermissionNames.View, RoleNames.Everyone))
|
if (UserSecurity.ContainsRole(ModuleState.PermissionList, PermissionNames.View, RoleNames.Everyone))
|
||||||
{
|
{
|
||||||
actionList.Add(new ActionViewModel { Icon = Icons.CircleX, Name = "Unpublish Module", Action = async (s, m) => await Unpublish(s, m) });
|
actionList.Add(new ActionViewModel { Icon = Icons.CircleX, Name = Localizer["UnpublishModule"], Action = async (s, m) => await Unpublish(s, m) });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
actionList.Add(new ActionViewModel { Icon = Icons.CircleCheck, Name = "Publish Module", Action = async (s, m) => await Publish(s, m) });
|
actionList.Add(new ActionViewModel { Icon = Icons.CircleCheck, Name = Localizer["PublishModule"], Action = async (s, m) => await Publish(s, m) });
|
||||||
}
|
}
|
||||||
actionList.Add(new ActionViewModel { Icon = Icons.Trash, Name = "Delete Module", Action = async (u, m) => await DeleteModule(u, m) });
|
actionList.Add(new ActionViewModel { Icon = Icons.Trash, Name = Localizer["DeleteModule"], Action = async (u, m) => await DeleteModule(u, m) });
|
||||||
|
|
||||||
if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.IsPortable)
|
if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.IsPortable)
|
||||||
{
|
{
|
||||||
actionList.Add(new ActionViewModel { Name = "" });
|
actionList.Add(new ActionViewModel { Name = "" });
|
||||||
actionList.Add(new ActionViewModel { Icon = Icons.CloudUpload, Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import") });
|
actionList.Add(new ActionViewModel { Icon = Icons.CloudUpload, Name = Localizer["ImportContent"], Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import") });
|
||||||
actionList.Add(new ActionViewModel { Icon = Icons.CloudDownload, Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export") });
|
actionList.Add(new ActionViewModel { Icon = Icons.CloudDownload, Name = Localizer["ExportContent"], Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export") });
|
||||||
}
|
}
|
||||||
|
|
||||||
actionList.Add(new ActionViewModel { Name = "" });
|
actionList.Add(new ActionViewModel { Name = "" });
|
||||||
|
|
||||||
if (ModuleState.PaneModuleIndex > 0)
|
if (ModuleState.PaneModuleIndex > 0)
|
||||||
{
|
{
|
||||||
actionList.Add(new ActionViewModel { Icon = Icons.DataTransferUpload, Name = "Move To Top", Action = async (s, m) => await MoveTop(s, m) });
|
actionList.Add(new ActionViewModel { Icon = Icons.DataTransferUpload, Name = Localizer["MoveToTop"], Action = async (s, m) => await MoveTop(s, m) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ModuleState.PaneModuleIndex > 0)
|
if (ModuleState.PaneModuleIndex > 0)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
@if (ShowLanguageSwitcher)
|
@if (ShowLanguageSwitcher)
|
||||||
{
|
{
|
||||||
<LanguageSwitcher DropdownAlignment="@LanguageDropdownAlignment" />
|
<LanguageSwitcher ButtonClass="@ButtonClass" DropdownAlignment="@LanguageDropdownAlignment" />
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (_showEditMode || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)))
|
@if (_showEditMode || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)))
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ControlPanelInteractive PageState="@PageState" SiteState="@SiteState" ButtonClass="@ButtonClass" ContainerClass="@ContainerClass" HeaderClass="@HeaderClass" BodyClass="@BodyClass" ShowLanguageSwitcher="@ShowLanguageSwitcher" LanguageDropdownAlignment="@LanguageDropdownAlignment" @rendermode="@InteractiveRenderMode.GetInteractiveRenderMode(PageState.Site.Runtime, PageState.Site.Prerender)" />
|
<ControlPanelInteractive PageState="@PageState" SiteState="@SiteState" ButtonClass="@ButtonClass" ContainerClass="@ContainerClass" HeaderClass="@HeaderClass" BodyClass="@BodyClass" ShowLanguageSwitcher="@ShowLanguageSwitcher" LanguageDropdownAlignment="@LanguageDropdownAlignment" @rendermode="@InteractiveRenderMode.GetInteractiveRenderMode(PageState.Site.Runtime, false)" />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
@inject ILogService LoggingService
|
@inject ILogService LoggingService
|
||||||
@inject IStringLocalizer<ControlPanelInteractive> Localizer
|
@inject IStringLocalizer<ControlPanelInteractive> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
@inject IServiceProvider ServiceProvider
|
||||||
|
|
||||||
<button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel" @onclick="ClearMessage">
|
<button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel" @onclick="ClearMessage">
|
||||||
<span class="oi oi-cog"></span>
|
<span class="oi oi-cog"></span>
|
||||||
|
@ -93,9 +94,13 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col text-center">
|
<div class="col text-center">
|
||||||
<label for="Module" class="control-label">@Localizer["Module.Manage"]</label>
|
<label for="Module" class="control-label">@Localizer["Module.Manage"]</label>
|
||||||
<select class="form-select" @bind="@_moduleType">
|
<select class="form-select" @onchange="(e => ModuleTypeChanged(e))">
|
||||||
<option value="new">@Localizer["Module.AddNew"]</option>
|
<option value="new">@Localizer["Module.AddNew"]</option>
|
||||||
<option value="existing">@Localizer["Module.AddExisting"]</option>
|
@if (PageState.Page.UserId == null)
|
||||||
|
{
|
||||||
|
<option value="add">@Localizer["Module.AddExisting"]</option>
|
||||||
|
<option value="copy">@Localizer["Module.CopyExisting"]</option>
|
||||||
|
}
|
||||||
</select>
|
</select>
|
||||||
@if (_moduleType == "new")
|
@if (_moduleType == "new")
|
||||||
{
|
{
|
||||||
|
@ -138,7 +143,7 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<select class="form-select mt-1" @onchange="(e => PageChanged(e))">
|
<select class="form-select mt-1" value="@_pageId" @onchange="(e => PageChanged(e))">
|
||||||
<option value="-"><@Localizer["Page.Select"]></option>
|
<option value="-"><@Localizer["Page.Select"]></option>
|
||||||
@foreach (Page p in _pages)
|
@foreach (Page p in _pages)
|
||||||
{
|
{
|
||||||
|
@ -211,7 +216,7 @@
|
||||||
|
|
||||||
<div class="row d-flex">
|
<div class="row d-flex">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<button type="button" data-bs-dismiss="offcanvas" class="btn btn-secondary col-12" @onclick=@(async () => await LogoutUser())>@Localizer["Logout"]</button>
|
<button type="button" data-bs-dismiss="offcanvas" class="btn btn-secondary col-12 mt-2" @onclick=@(async () => await LogoutUser())>@Localizer["Logout"]</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -291,7 +296,7 @@
|
||||||
_containerType = PageState.Site.DefaultContainerType;
|
_containerType = PageState.Site.DefaultContainerType;
|
||||||
_allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
_allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||||
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList();
|
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList();
|
||||||
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',', StringSplitOptions.RemoveEmptyEntries)).Distinct().Where(item => item != "Headless").ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,6 +339,13 @@
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ModuleTypeChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
_moduleType = (string)e.Value;
|
||||||
|
_pageId = "-";
|
||||||
|
_moduleId = "-";
|
||||||
|
}
|
||||||
|
|
||||||
private void PageChanged(ChangeEventArgs e)
|
private void PageChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_pageId = (string)e.Value;
|
_pageId = (string)e.Value;
|
||||||
|
@ -341,7 +353,8 @@
|
||||||
{
|
{
|
||||||
_modules = PageState.Modules
|
_modules = PageState.Modules
|
||||||
.Where(module => module.PageId == int.Parse(_pageId) &&
|
.Where(module => module.PageId == int.Parse(_pageId) &&
|
||||||
UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList))
|
UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList) &&
|
||||||
|
(_moduleType == "add" || module.ModuleDefinition.IsPortable))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
_moduleId = "-";
|
_moduleId = "-";
|
||||||
|
@ -354,6 +367,7 @@
|
||||||
{
|
{
|
||||||
if ((_moduleType == "new" && _moduleDefinitionName != "-") || (_moduleType != "new" && _moduleId != "-"))
|
if ((_moduleType == "new" && _moduleDefinitionName != "-") || (_moduleType != "new" && _moduleId != "-"))
|
||||||
{
|
{
|
||||||
|
var newModuleId = _moduleId != "-" ? int.Parse(_moduleId) : 0;
|
||||||
if (_moduleType == "new")
|
if (_moduleType == "new")
|
||||||
{
|
{
|
||||||
Module module = new Module();
|
Module module = new Module();
|
||||||
|
@ -361,33 +375,37 @@
|
||||||
module.PageId = PageState.Page.PageId;
|
module.PageId = PageState.Page.PageId;
|
||||||
module.ModuleDefinitionName = _moduleDefinitionName;
|
module.ModuleDefinitionName = _moduleDefinitionName;
|
||||||
module.AllPages = false;
|
module.AllPages = false;
|
||||||
|
module.PermissionList = GenerateDefaultPermissions(module.SiteId);
|
||||||
var permissions = new List<Permission>();
|
|
||||||
if (_visibility == "view")
|
|
||||||
{
|
|
||||||
// set module view permissions to page view permissions
|
|
||||||
permissions = SetPermissions(permissions, module.SiteId, PermissionNames.View, PermissionNames.View);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// set module view permissions to page edit permissions
|
|
||||||
permissions = SetPermissions(permissions, module.SiteId, PermissionNames.View, PermissionNames.Edit);
|
|
||||||
}
|
|
||||||
// set module edit permissions to page edit permissions
|
|
||||||
permissions = SetPermissions(permissions, module.SiteId, PermissionNames.Edit, PermissionNames.Edit);
|
|
||||||
module.PermissionList = permissions;
|
|
||||||
|
|
||||||
module = await ModuleService.AddModuleAsync(module);
|
module = await ModuleService.AddModuleAsync(module);
|
||||||
_moduleId = module.ModuleId.ToString();
|
newModuleId = module.ModuleId;
|
||||||
|
}
|
||||||
|
else if (_moduleType == "copy")
|
||||||
|
{
|
||||||
|
var module = await ModuleService.GetModuleAsync(int.Parse(_moduleId));
|
||||||
|
module.ModuleId = 0;
|
||||||
|
module.SiteId = PageState.Site.SiteId;
|
||||||
|
module.PageId = PageState.Page.PageId;
|
||||||
|
module.AllPages = false;
|
||||||
|
module.PermissionList = GenerateDefaultPermissions(module.SiteId);
|
||||||
|
|
||||||
|
module = await ModuleService.AddModuleAsync(module);
|
||||||
|
var moduleContent = await ModuleService.ExportModuleAsync(int.Parse(_moduleId), PageState.Page.PageId);
|
||||||
|
if (!string.IsNullOrEmpty(moduleContent))
|
||||||
|
{
|
||||||
|
await ModuleService.ImportModuleAsync(module.ModuleId, PageState.Page.PageId, moduleContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
newModuleId = module.ModuleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pageModule = new PageModule
|
var pageModule = new PageModule
|
||||||
{
|
{
|
||||||
PageId = PageState.Page.PageId,
|
PageId = PageState.Page.PageId,
|
||||||
ModuleId = int.Parse(_moduleId),
|
ModuleId = newModuleId,
|
||||||
Title = _title
|
Title = _title
|
||||||
};
|
};
|
||||||
if (pageModule.Title == "")
|
if (string.IsNullOrEmpty(pageModule.Title))
|
||||||
{
|
{
|
||||||
if (_moduleType == "new")
|
if (_moduleType == "new")
|
||||||
{
|
{
|
||||||
|
@ -412,9 +430,16 @@
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane);
|
await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane);
|
||||||
await UpdateSettingsAsync();
|
await UpdateSettingsAsync();
|
||||||
|
|
||||||
_message = $"<div class=\"alert alert-success mt-2 text-center\" role=\"alert\">{Localizer["Success.Page.ModuleAdd"]}</div>";
|
if (PageState.RenderMode == RenderModes.Interactive)
|
||||||
_title = "";
|
{
|
||||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, PageState.Page.Path, ""));
|
_message = $"<div class=\"alert alert-success mt-2 text-center\" role=\"alert\">{Localizer["Success.Page.ModuleAdd"]}</div>";
|
||||||
|
_title = "";
|
||||||
|
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, PageState.Page.Path, ""));
|
||||||
|
}
|
||||||
|
else // reload page in static rendering
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, PageState.Page.Path, ""), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -427,6 +452,25 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Permission> GenerateDefaultPermissions(int siteId)
|
||||||
|
{
|
||||||
|
var permissions = new List<Permission>();
|
||||||
|
if (_visibility == "view")
|
||||||
|
{
|
||||||
|
// set module view permissions to page view permissions
|
||||||
|
permissions = SetPermissions(permissions, siteId, PermissionNames.View, PermissionNames.View);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// set module view permissions to page edit permissions
|
||||||
|
permissions = SetPermissions(permissions, siteId, PermissionNames.View, PermissionNames.Edit);
|
||||||
|
}
|
||||||
|
// set module edit permissions to page edit permissions
|
||||||
|
permissions = SetPermissions(permissions, siteId, PermissionNames.Edit, PermissionNames.Edit);
|
||||||
|
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
private List<Permission> SetPermissions(List<Permission> permissions, int siteId, string modulePermission, string pagePermission)
|
private List<Permission> SetPermissions(List<Permission> permissions, int siteId, string modulePermission, string pagePermission)
|
||||||
{
|
{
|
||||||
foreach (var permission in PageState.Page.PermissionList.Where(item => item.PermissionName == pagePermission))
|
foreach (var permission in PageState.Page.PermissionList.Where(item => item.PermissionName == pagePermission))
|
||||||
|
|
|
@ -1,21 +1,29 @@
|
||||||
@namespace Oqtane.Themes.Controls
|
|
||||||
@inherits ThemeControlBase
|
|
||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@using Microsoft.AspNetCore.Localization
|
@using Microsoft.AspNetCore.Localization
|
||||||
|
@using Microsoft.AspNetCore.Http
|
||||||
@using Oqtane.Models
|
@using Oqtane.Models
|
||||||
|
@namespace Oqtane.Themes.Controls
|
||||||
|
@inherits ThemeControlBase
|
||||||
@inject ILanguageService LanguageService
|
@inject ILanguageService LanguageService
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
@if (_supportedCultures?.Count() > 1)
|
@if (_supportedCultures?.Count() > 1)
|
||||||
{
|
{
|
||||||
<div class="btn-group pe-1" role="group">
|
<div class="btn-group pe-1" role="group">
|
||||||
<button id="btnCultures" type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button id="btnCultures" type="button" class="btn @ButtonClass dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
<span class="oi oi-globe"></span>
|
<span class="oi oi-globe"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu @MenuAlignment" aria-labelledby="btnCultures">
|
<div class="dropdown-menu @MenuAlignment" aria-labelledby="btnCultures">
|
||||||
@foreach (var culture in _supportedCultures)
|
@foreach (var culture in _supportedCultures)
|
||||||
{
|
{
|
||||||
<a class="dropdown-item @(CultureInfo.CurrentUICulture.Name == culture.Name ? "active" : String.Empty)" href="#" @onclick="@(async e => await SetCultureAsync(culture.Name))">@culture.DisplayName</a>
|
@if (PageState.RenderMode == RenderModes.Interactive)
|
||||||
|
{
|
||||||
|
<a class="dropdown-item @(CultureInfo.CurrentUICulture.Name == culture.Name ? "active" : String.Empty)" href="#" @onclick="@(async e => await SetCultureAsync(culture.Name))" @onclick:preventDefault="true">@culture.DisplayName</a>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<a class="dropdown-item @(CultureInfo.CurrentUICulture.Name == culture.Name ? "active" : String.Empty)" href="@NavigateUrl(PageState.Page.Path, "culture=" + culture.Name)">@culture.DisplayName</a>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,9 +31,15 @@
|
||||||
|
|
||||||
@code{
|
@code{
|
||||||
private IEnumerable<Culture> _supportedCultures;
|
private IEnumerable<Culture> _supportedCultures;
|
||||||
|
private string MenuAlignment = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string DropdownAlignment { get; set; } = string.Empty; // Empty or Left or Right
|
public string DropdownAlignment { get; set; } = string.Empty; // Empty or Left or Right
|
||||||
private string MenuAlignment = string.Empty;
|
[Parameter]
|
||||||
|
public string ButtonClass { get; set; } = "btn-outline-secondary";
|
||||||
|
|
||||||
|
[CascadingParameter]
|
||||||
|
HttpContext HttpContext { get; set; }
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
|
@ -33,16 +47,26 @@
|
||||||
|
|
||||||
var languages = PageState.Languages;
|
var languages = PageState.Languages;
|
||||||
_supportedCultures = languages.Select(l => new Culture { Name = l.Code, DisplayName = l.Name });
|
_supportedCultures = languages.Select(l => new Culture { Name = l.Code, DisplayName = l.Name });
|
||||||
|
|
||||||
|
if (PageState.QueryString.ContainsKey("culture"))
|
||||||
|
{
|
||||||
|
var culture = PageState.QueryString["culture"];
|
||||||
|
if (_supportedCultures.Any(item => item.Name == culture))
|
||||||
|
{
|
||||||
|
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||||
|
HttpContext.Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, new CookieOptions { Path = "/", Expires = DateTimeOffset.UtcNow.AddYears(365) });
|
||||||
|
}
|
||||||
|
NavigationManager.NavigateTo(NavigationManager.Uri.Replace($"?culture={culture}", ""), forceLoad: true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetCultureAsync(string culture)
|
private async Task SetCultureAsync(string culture)
|
||||||
{
|
{
|
||||||
if (culture != CultureInfo.CurrentUICulture.Name)
|
if (culture != CultureInfo.CurrentUICulture.Name)
|
||||||
{
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
|
||||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true);
|
NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
@namespace Oqtane.UI
|
@namespace Oqtane.UI
|
||||||
@inject SiteState SiteState
|
@inject SiteState SiteState
|
||||||
|
|
||||||
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Static)
|
@if (_comment != null)
|
||||||
{
|
{
|
||||||
<RenderModeBoundary ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" />
|
@((MarkupString)_comment)
|
||||||
}
|
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Static)
|
||||||
else
|
{
|
||||||
{
|
<RenderModeBoundary ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" />
|
||||||
<RenderModeBoundary ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" @rendermode="@InteractiveRenderMode.GetInteractiveRenderMode(PageState.Site.Runtime, PageState.Site.Prerender)" />
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<RenderModeBoundary ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" @rendermode="InteractiveRenderMode.GetInteractiveRenderMode(PageState.Site.Runtime, _prerender)" />
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
@ -20,6 +24,24 @@ else
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
private Module ModuleState { get; set; }
|
private Module ModuleState { get; set; }
|
||||||
|
|
||||||
|
private bool _prerender;
|
||||||
|
private string _comment;
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
_prerender = ModuleState.Prerender ?? PageState.Site.Prerender;
|
||||||
|
_comment = "<!-- rendermode: ";
|
||||||
|
if (PageState.RenderMode == RenderModes.Static && ModuleState.RenderMode == RenderModes.Static)
|
||||||
|
{
|
||||||
|
_comment += RenderModes.Static;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_comment += $"{RenderModes.Interactive}:{PageState.Runtime} - prerender: {_prerender}";
|
||||||
|
}
|
||||||
|
_comment += " -->";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[Obsolete("AddModuleMessage is deprecated. Use AddModuleMessage in ModuleBase instead.", false)]
|
[Obsolete("AddModuleMessage is deprecated. Use AddModuleMessage in ModuleBase instead.", false)]
|
||||||
public void AddModuleMessage(string message, MessageType type)
|
public void AddModuleMessage(string message, MessageType type)
|
||||||
|
|
|
@ -10,14 +10,19 @@
|
||||||
{
|
{
|
||||||
@if (ModuleType != null)
|
@if (ModuleType != null)
|
||||||
{
|
{
|
||||||
@((MarkupString)$"<!-- rendermode: {ModuleState.RenderMode} -->")
|
@if (!string.IsNullOrEmpty(_messageContent) && _messagePosition == "top")
|
||||||
<ModuleMessage @ref="moduleMessageTop" Message="@_messageContent" Type="@_messageType" />
|
{
|
||||||
|
<ModuleMessage Message="@_messageContent" Type="@_messageType" Parent="@this" />
|
||||||
|
}
|
||||||
@DynamicComponent
|
@DynamicComponent
|
||||||
@if (_progressIndicator)
|
@if (_progressIndicator)
|
||||||
{
|
{
|
||||||
<div class="app-progress-indicator"></div>
|
<div class="app-progress-indicator"></div>
|
||||||
}
|
}
|
||||||
<ModuleMessage @ref="moduleMessageBottom" Message="@_messageContent" Type="@_messageType" />
|
@if (!string.IsNullOrEmpty(_messageContent) && _messagePosition == "bottom")
|
||||||
|
{
|
||||||
|
<ModuleMessage Message="@_messageContent" Type="@_messageType" Parent="@this" />
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -42,8 +47,6 @@
|
||||||
private string _messagePosition;
|
private string _messagePosition;
|
||||||
private bool _progressIndicator = false;
|
private bool _progressIndicator = false;
|
||||||
private string _error;
|
private string _error;
|
||||||
private ModuleMessage moduleMessageTop;
|
|
||||||
private ModuleMessage moduleMessageBottom;
|
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public SiteState SiteState { get; set; }
|
public SiteState SiteState { get; set; }
|
||||||
|
@ -104,12 +107,17 @@
|
||||||
|
|
||||||
public void AddModuleMessage(string message, MessageType type, string position)
|
public void AddModuleMessage(string message, MessageType type, string position)
|
||||||
{
|
{
|
||||||
_messageContent = message;
|
if(message != _messageContent
|
||||||
_messageType = type;
|
|| type != _messageType
|
||||||
_messagePosition = position;
|
|| position != _messagePosition)
|
||||||
_progressIndicator = false;
|
{
|
||||||
|
_messageContent = message;
|
||||||
|
_messageType = type;
|
||||||
|
_messagePosition = position;
|
||||||
|
_progressIndicator = false;
|
||||||
|
|
||||||
Refresh();
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowProgressIndicator()
|
public void ShowProgressIndicator()
|
||||||
|
@ -124,25 +132,10 @@
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DismissMessage()
|
public void DismissMessage()
|
||||||
{
|
{
|
||||||
_messageContent = "";
|
_messageContent = "";
|
||||||
}
|
StateHasChanged();
|
||||||
|
|
||||||
private void Refresh()
|
|
||||||
{
|
|
||||||
var updateTop = string.IsNullOrEmpty(_messageContent) || _messagePosition == "top";
|
|
||||||
var updateBottom = string.IsNullOrEmpty(_messageContent) || _messagePosition == "bottom";
|
|
||||||
|
|
||||||
if (updateTop && moduleMessageTop != null)
|
|
||||||
{
|
|
||||||
moduleMessageTop.RefreshMessage(_messageContent, _messageType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updateBottom && moduleMessageBottom != null)
|
|
||||||
{
|
|
||||||
moduleMessageBottom.RefreshMessage(_messageContent, _messageType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnErrorAsync(Exception exception)
|
protected override async Task OnErrorAsync(Exception exception)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
@using System.Diagnostics.CodeAnalysis
|
@using System.Diagnostics.CodeAnalysis
|
||||||
@using System.Net
|
@using System.Net
|
||||||
@using Microsoft.AspNetCore.Http
|
@using Microsoft.AspNetCore.Http
|
||||||
|
@using System.Globalization
|
||||||
@namespace Oqtane.UI
|
@namespace Oqtane.UI
|
||||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||||
@inject SiteState SiteState
|
@inject SiteState SiteState
|
||||||
|
@ -103,7 +104,7 @@
|
||||||
_error = "";
|
_error = "";
|
||||||
|
|
||||||
Route route = new Route(_absoluteUri, SiteState.Alias.Path);
|
Route route = new Route(_absoluteUri, SiteState.Alias.Path);
|
||||||
int moduleid = int.Parse(route.ModuleId);
|
int moduleid = int.Parse(route.ModuleId, CultureInfo.InvariantCulture);
|
||||||
var action = route.Action;
|
var action = route.Action;
|
||||||
|
|
||||||
var querystring = Utilities.ParseQueryString(route.Query);
|
var querystring = Utilities.ParseQueryString(route.Query);
|
||||||
|
@ -263,7 +264,7 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
editmode = (page.PageId == ((user.Settings.ContainsKey("CP-editmode")) ? int.Parse(user.Settings["CP-editmode"]) : -1));
|
editmode = (page.PageId == ((user.Settings.ContainsKey("CP-editmode")) ? int.Parse(user.Settings["CP-editmode"], CultureInfo.InvariantCulture) : -1));
|
||||||
if (!editmode)
|
if (!editmode)
|
||||||
{
|
{
|
||||||
var userSettings = new Dictionary<string, string> { { "CP-editmode", "-1" } };
|
var userSettings = new Dictionary<string, string> { { "CP-editmode", "-1" } };
|
||||||
|
@ -476,6 +477,7 @@
|
||||||
// retrieve module component resources
|
// retrieve module component resources
|
||||||
var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||||
module.RenderMode = moduleobject.RenderMode;
|
module.RenderMode = moduleobject.RenderMode;
|
||||||
|
module.Prerender = moduleobject.Prerender;
|
||||||
|
|
||||||
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
||||||
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
||||||
|
@ -549,7 +551,7 @@
|
||||||
{
|
{
|
||||||
foreach (var resource in resources)
|
foreach (var resource in resources)
|
||||||
{
|
{
|
||||||
if (resource.Level != ResourceLevel.Site)
|
if (resource.ResourceType == ResourceType.Stylesheet || resource.Level != ResourceLevel.Site)
|
||||||
{
|
{
|
||||||
if (resource.Url.StartsWith("~"))
|
if (resource.Url.StartsWith("~"))
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Version>5.1.1</Version>
|
<Version>5.1.2</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Version>5.1.1</Version>
|
<Version>5.1.2</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -34,8 +34,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
|
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.5" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Version>5.1.1</Version>
|
<Version>5.1.2</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Version>5.1.1</Version>
|
<Version>5.1.2</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<!-- <TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks> -->
|
<!-- <TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks> -->
|
||||||
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
|
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Version>5.1.1</Version>
|
<Version>5.1.2</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane.Maui</RootNamespace>
|
<RootNamespace>Oqtane.Maui</RootNamespace>
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
|
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
|
||||||
|
|
||||||
<!-- Versions -->
|
<!-- Versions -->
|
||||||
<ApplicationDisplayVersion>5.1.1</ApplicationDisplayVersion>
|
<ApplicationDisplayVersion>5.1.2</ApplicationDisplayVersion>
|
||||||
<ApplicationVersion>1</ApplicationVersion>
|
<ApplicationVersion>1</ApplicationVersion>
|
||||||
|
|
||||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
|
||||||
|
@ -65,15 +65,15 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.5" />
|
||||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.20" />
|
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.40" />
|
||||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.20" />
|
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.40" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.20" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.40" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Client</id>
|
<id>Oqtane.Client</id>
|
||||||
<version>5.1.1</version>
|
<version>5.1.2</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Framework</id>
|
<id>Oqtane.Framework</id>
|
||||||
<version>5.1.1</version>
|
<version>5.1.2</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
|
@ -11,8 +11,8 @@
|
||||||
<copyright>.NET Foundation</copyright>
|
<copyright>.NET Foundation</copyright>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v5.1.1/Oqtane.Framework.5.1.1.Upgrade.zip</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v5.1.2/Oqtane.Framework.5.1.2.Upgrade.zip</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane framework</tags>
|
<tags>oqtane framework</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Server</id>
|
<id>Oqtane.Server</id>
|
||||||
<version>5.1.1</version>
|
<version>5.1.2</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Shared</id>
|
<id>Oqtane.Shared</id>
|
||||||
<version>5.1.1</version>
|
<version>5.1.2</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Updater</id>
|
<id>Oqtane.Updater</id>
|
||||||
<version>5.1.1</version>
|
<version>5.1.2</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.1.1.Install.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.1.2.Install.zip" -Force
|
|
@ -1 +1 @@
|
||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.1.1.Upgrade.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.1.2.Upgrade.zip" -Force
|
|
@ -16,6 +16,7 @@
|
||||||
@using Oqtane.Shared
|
@using Oqtane.Shared
|
||||||
@using Oqtane.Themes
|
@using Oqtane.Themes
|
||||||
@using Oqtane.Extensions
|
@using Oqtane.Extensions
|
||||||
|
@using System.Globalization
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IAntiforgery Antiforgery
|
@inject IAntiforgery Antiforgery
|
||||||
@inject IConfigManager ConfigManager
|
@inject IConfigManager ConfigManager
|
||||||
|
@ -39,7 +40,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<base href="/" />
|
<base href="/" />
|
||||||
<link rel="stylesheet" href="css/app.css" />
|
<link rel="stylesheet" href="css/app.css" />
|
||||||
@if (!string.IsNullOrEmpty(_PWAScript))
|
@if (_scripts.Contains("PWA Manifest"))
|
||||||
{
|
{
|
||||||
<link id="app-manifest" rel="manifest" />
|
<link id="app-manifest" rel="manifest" />
|
||||||
}
|
}
|
||||||
|
@ -68,20 +69,13 @@
|
||||||
<Routes PageState="@_pageState" RenderMode="@_renderMode" Runtime="@_runtime" AntiForgeryToken="@_antiForgeryToken" AuthorizationToken="@_authorizationToken" Platform="@_platform" @rendermode="InteractiveRenderMode.GetInteractiveRenderMode(_runtime, _prerender)" />
|
<Routes PageState="@_pageState" RenderMode="@_renderMode" Runtime="@_runtime" AntiForgeryToken="@_antiForgeryToken" AuthorizationToken="@_authorizationToken" Platform="@_platform" @rendermode="InteractiveRenderMode.GetInteractiveRenderMode(_runtime, _prerender)" />
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (!string.IsNullOrEmpty(_reconnectScript))
|
<script src="_framework/blazor.web.js"></script>
|
||||||
{
|
|
||||||
@((MarkupString)_reconnectScript)
|
|
||||||
}
|
|
||||||
@if (!string.IsNullOrEmpty(_PWAScript))
|
|
||||||
{
|
|
||||||
@((MarkupString)_PWAScript)
|
|
||||||
}
|
|
||||||
@((MarkupString)_bodyResources)
|
|
||||||
|
|
||||||
<script src="js/app.js"></script>
|
<script src="js/app.js"></script>
|
||||||
<script src="js/loadjs.min.js"></script>
|
<script src="js/loadjs.min.js"></script>
|
||||||
<script src="js/interop.js"></script>
|
<script src="js/interop.js"></script>
|
||||||
<script src="_framework/blazor.web.js"></script>
|
|
||||||
|
@((MarkupString)_scripts)
|
||||||
|
@((MarkupString)_bodyResources)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -105,8 +99,7 @@
|
||||||
private string _headResources = "";
|
private string _headResources = "";
|
||||||
private string _bodyResources = "";
|
private string _bodyResources = "";
|
||||||
private string _styleSheets = "";
|
private string _styleSheets = "";
|
||||||
private string _PWAScript = "";
|
private string _scripts = "";
|
||||||
private string _reconnectScript = "";
|
|
||||||
private string _message = "";
|
private string _message = "";
|
||||||
private PageState _pageState;
|
private PageState _pageState;
|
||||||
|
|
||||||
|
@ -175,23 +168,25 @@
|
||||||
CreateJwtToken(alias);
|
CreateJwtToken(alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
// include stylesheets to prevent FOUC
|
// includes resources
|
||||||
var resources = GetPageResources(alias, site, page, int.Parse(route.ModuleId), route.Action);
|
var resources = GetPageResources(alias, site, page, int.Parse(route.ModuleId, CultureInfo.InvariantCulture), route.Action);
|
||||||
ManageStyleSheets(resources);
|
ManageStyleSheets(resources);
|
||||||
|
ManageScripts(resources, alias);
|
||||||
|
|
||||||
// scripts
|
// generate scripts
|
||||||
if (_renderMode == RenderModes.Static)
|
|
||||||
{
|
|
||||||
ManageScripts(resources, alias);
|
|
||||||
}
|
|
||||||
if (_renderMode == RenderModes.Interactive && _runtime == Runtimes.Server)
|
if (_renderMode == RenderModes.Interactive && _runtime == Runtimes.Server)
|
||||||
{
|
{
|
||||||
_reconnectScript = CreateReconnectScript();
|
_scripts += CreateReconnectScript();
|
||||||
}
|
}
|
||||||
if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null)
|
if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null)
|
||||||
{
|
{
|
||||||
_PWAScript = CreatePWAScript(alias, site, route);
|
_scripts += CreatePWAScript(alias, site, route);
|
||||||
}
|
}
|
||||||
|
@if (_renderMode == RenderModes.Static)
|
||||||
|
{
|
||||||
|
_scripts += CreateScrollPositionScript();
|
||||||
|
}
|
||||||
|
|
||||||
_headResources += ParseScripts(site.HeadContent);
|
_headResources += ParseScripts(site.HeadContent);
|
||||||
_bodyResources += ParseScripts(site.BodyContent);
|
_bodyResources += ParseScripts(site.BodyContent);
|
||||||
|
|
||||||
|
@ -329,14 +324,26 @@
|
||||||
int? userid = Context.User.UserId();
|
int? userid = Context.User.UserId();
|
||||||
userid = (userid == -1) ? null : userid;
|
userid = (userid == -1) ? null : userid;
|
||||||
|
|
||||||
// check if cookie already exists
|
// get cookie value
|
||||||
|
var visitorCookieName = Constants.VisitorCookiePrefix + SiteId.ToString();
|
||||||
|
var visitorCookieValue = Context.Request.Cookies[visitorCookieName];
|
||||||
|
DateTime expiry = DateTime.MinValue;
|
||||||
|
if (visitorCookieValue != null && visitorCookieValue.Contains("|"))
|
||||||
|
{
|
||||||
|
var values = visitorCookieValue.Split('|');
|
||||||
|
int.TryParse(values[0], out _visitorId);
|
||||||
|
DateTime.TryParse(values[1], out expiry);
|
||||||
|
}
|
||||||
|
else // legacy cookie format
|
||||||
|
{
|
||||||
|
int.TryParse(visitorCookieValue, out _visitorId);
|
||||||
|
}
|
||||||
|
bool setcookie = false;
|
||||||
Visitor visitor = null;
|
Visitor visitor = null;
|
||||||
bool addcookie = false;
|
|
||||||
var VisitorCookie = Constants.VisitorCookiePrefix + SiteId.ToString();
|
if (_visitorId <= 0)
|
||||||
if (!int.TryParse(Context.Request.Cookies[VisitorCookie], out _visitorId))
|
|
||||||
{
|
{
|
||||||
// if enabled use IP Address correlation
|
// if enabled use IP Address correlation
|
||||||
_visitorId = -1;
|
|
||||||
var correlate = bool.Parse(settings.GetValue("VisitorCorrelation", "true"));
|
var correlate = bool.Parse(settings.GetValue("VisitorCorrelation", "true"));
|
||||||
if (correlate)
|
if (correlate)
|
||||||
{
|
{
|
||||||
|
@ -344,12 +351,12 @@
|
||||||
if (visitor != null)
|
if (visitor != null)
|
||||||
{
|
{
|
||||||
_visitorId = visitor.VisitorId;
|
_visitorId = visitor.VisitorId;
|
||||||
addcookie = true;
|
setcookie = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_visitorId == -1)
|
if (_visitorId <= 0)
|
||||||
{
|
{
|
||||||
// create new visitor
|
// create new visitor
|
||||||
visitor = new Visitor();
|
visitor = new Visitor();
|
||||||
|
@ -365,52 +372,59 @@
|
||||||
visitor.VisitedOn = DateTime.UtcNow;
|
visitor.VisitedOn = DateTime.UtcNow;
|
||||||
visitor = VisitorRepository.AddVisitor(visitor);
|
visitor = VisitorRepository.AddVisitor(visitor);
|
||||||
_visitorId = visitor.VisitorId;
|
_visitorId = visitor.VisitorId;
|
||||||
addcookie = true;
|
setcookie = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (visitor == null)
|
// check expiry
|
||||||
|
if (DateTime.UtcNow > expiry)
|
||||||
{
|
{
|
||||||
// get visitor if it was not previously loaded
|
if (visitor == null)
|
||||||
visitor = VisitorRepository.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;
|
// get visitor if not previously loaded
|
||||||
|
visitor = VisitorRepository.GetVisitor(_visitorId);
|
||||||
}
|
}
|
||||||
if (userid != null)
|
if (visitor != null)
|
||||||
{
|
{
|
||||||
visitor.UserId = userid;
|
// 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;
|
||||||
|
VisitorRepository.UpdateVisitor(visitor);
|
||||||
|
setcookie = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// remove cookie if visitor does not exist
|
||||||
|
Context.Response.Cookies.Delete(visitorCookieName);
|
||||||
}
|
}
|
||||||
visitor.Visits += 1;
|
|
||||||
visitor.VisitedOn = DateTime.UtcNow;
|
|
||||||
VisitorRepository.UpdateVisitor(visitor);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// remove cookie if VisitorId does not exist
|
|
||||||
Context.Response.Cookies.Delete(VisitorCookie);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// append cookie
|
// set cookie
|
||||||
if (addcookie)
|
if (setcookie)
|
||||||
{
|
{
|
||||||
|
expiry = DateTime.UtcNow.AddMinutes(int.Parse(settings.GetValue("VisitorDuration", "5")));
|
||||||
|
|
||||||
Context.Response.Cookies.Append(
|
Context.Response.Cookies.Append(
|
||||||
VisitorCookie,
|
visitorCookieName,
|
||||||
_visitorId.ToString(),
|
$"{_visitorId}|{expiry}",
|
||||||
new CookieOptions()
|
new CookieOptions()
|
||||||
{
|
{
|
||||||
Expires = DateTimeOffset.UtcNow.AddYears(1),
|
Expires = DateTimeOffset.UtcNow.AddYears(10),
|
||||||
IsEssential = true
|
IsEssential = true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -432,7 +446,7 @@
|
||||||
|
|
||||||
private string CreatePWAScript(Alias alias, Site site, Route route)
|
private string CreatePWAScript(Alias alias, Site site, Route route)
|
||||||
{
|
{
|
||||||
return
|
return Environment.NewLine +
|
||||||
"<script>" + Environment.NewLine +
|
"<script>" + Environment.NewLine +
|
||||||
" // PWA Manifest" + Environment.NewLine +
|
" // PWA Manifest" + Environment.NewLine +
|
||||||
" setTimeout(() => {" + Environment.NewLine +
|
" setTimeout(() => {" + Environment.NewLine +
|
||||||
|
@ -468,14 +482,14 @@
|
||||||
" console.log('ServiceWorker Registration Failed ', err);" + Environment.NewLine +
|
" console.log('ServiceWorker Registration Failed ', err);" + Environment.NewLine +
|
||||||
" });" + Environment.NewLine +
|
" });" + Environment.NewLine +
|
||||||
" };" + Environment.NewLine +
|
" };" + Environment.NewLine +
|
||||||
"</script>";
|
"</script>" + Environment.NewLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CreateReconnectScript()
|
private string CreateReconnectScript()
|
||||||
{
|
{
|
||||||
return
|
return Environment.NewLine +
|
||||||
"<script>" + Environment.NewLine +
|
"<script>" + Environment.NewLine +
|
||||||
" // Blazor Server Reconnect" + Environment.NewLine +
|
" // Interactive Blazor Server Reconnect" + Environment.NewLine +
|
||||||
" new MutationObserver((mutations, observer) => {" + Environment.NewLine +
|
" new MutationObserver((mutations, observer) => {" + Environment.NewLine +
|
||||||
" if (document.querySelector('#components-reconnect-modal h5 a')) {" + Environment.NewLine +
|
" if (document.querySelector('#components-reconnect-modal h5 a')) {" + Environment.NewLine +
|
||||||
" async function attemptReload() {" + Environment.NewLine +
|
" async function attemptReload() {" + Environment.NewLine +
|
||||||
|
@ -487,7 +501,26 @@
|
||||||
" setInterval(attemptReload, 5000);" + Environment.NewLine +
|
" setInterval(attemptReload, 5000);" + Environment.NewLine +
|
||||||
" }" + Environment.NewLine +
|
" }" + Environment.NewLine +
|
||||||
" }).observe(document.body, { childList: true, subtree: true });" + Environment.NewLine +
|
" }).observe(document.body, { childList: true, subtree: true });" + Environment.NewLine +
|
||||||
"</script>";
|
"</script>" + Environment.NewLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string CreateScrollPositionScript()
|
||||||
|
{
|
||||||
|
return Environment.NewLine +
|
||||||
|
"<script>" + Environment.NewLine +
|
||||||
|
" // Blazor Static Rendering Scroll Position" + Environment.NewLine +
|
||||||
|
" window.interceptNavigation = () => {" + Environment.NewLine +
|
||||||
|
" let currentUrl = window.location.href;" + Environment.NewLine +
|
||||||
|
" Blazor.addEventListener('enhancedload', () => {" + Environment.NewLine +
|
||||||
|
" let newUrl = window.location.href;" + Environment.NewLine +
|
||||||
|
" if (currentUrl != newUrl) {" + Environment.NewLine +
|
||||||
|
" window.scrollTo({ top: 0, left: 0, behavior: 'instant' });" + Environment.NewLine +
|
||||||
|
" }" + Environment.NewLine +
|
||||||
|
" currentUrl = newUrl;" + Environment.NewLine +
|
||||||
|
" });" + Environment.NewLine +
|
||||||
|
" };" + Environment.NewLine +
|
||||||
|
" document.onload += window.interceptNavigation();" + Environment.NewLine +
|
||||||
|
"</script>" + Environment.NewLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ParseScripts(string content)
|
private string ParseScripts(string content)
|
||||||
|
@ -536,7 +569,7 @@
|
||||||
((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
|
((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
|
||||||
((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
|
((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
|
||||||
((resource.ES6Module) ? " type=\"module\"" : "") +
|
((resource.ES6Module) ? " type=\"module\"" : "") +
|
||||||
" src =\"" + url + "\"></script>"; // src at end of element due to enhanced navigation patch algorithm
|
" src=\"" + url + "\"></script>"; // src at end of element due to enhanced navigation patch algorithm
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -566,13 +599,13 @@
|
||||||
var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == themeType));
|
var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == themeType));
|
||||||
if (theme != null)
|
if (theme != null)
|
||||||
{
|
{
|
||||||
resources = AddResources(resources, theme.Resources, ResourceLevel.Page, alias, "Themes", Utilities.GetTypeName(theme.ThemeName));
|
resources = AddResources(resources, theme.Resources, ResourceLevel.Page, alias, "Themes", Utilities.GetTypeName(theme.ThemeName), site.RenderMode);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// fallback to default Oqtane theme
|
// fallback to default Oqtane theme
|
||||||
theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == Constants.DefaultTheme));
|
theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == Constants.DefaultTheme));
|
||||||
resources = AddResources(resources, theme.Resources, ResourceLevel.Page, alias, "Themes", Utilities.GetTypeName(theme.ThemeName));
|
resources = AddResources(resources, theme.Resources, ResourceLevel.Page, alias, "Themes", Utilities.GetTypeName(theme.ThemeName), site.RenderMode);
|
||||||
}
|
}
|
||||||
var type = Type.GetType(themeType);
|
var type = Type.GetType(themeType);
|
||||||
if (type != null)
|
if (type != null)
|
||||||
|
@ -580,7 +613,7 @@
|
||||||
var obj = Activator.CreateInstance(type) as IThemeControl;
|
var obj = Activator.CreateInstance(type) as IThemeControl;
|
||||||
if (obj != null)
|
if (obj != null)
|
||||||
{
|
{
|
||||||
resources = AddResources(resources, obj.Resources, ResourceLevel.Page, alias, "Themes", type.Namespace);
|
resources = AddResources(resources, obj.Resources, ResourceLevel.Page, alias, "Themes", type.Namespace, site.RenderMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,7 +622,7 @@
|
||||||
var typename = "";
|
var typename = "";
|
||||||
if (module.ModuleDefinition != null)
|
if (module.ModuleDefinition != null)
|
||||||
{
|
{
|
||||||
resources = AddResources(resources, module.ModuleDefinition.Resources, ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName));
|
resources = AddResources(resources, module.ModuleDefinition.Resources, ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName), site.RenderMode);
|
||||||
|
|
||||||
// handle default action
|
// handle default action
|
||||||
if (action == Constants.DefaultAction && !string.IsNullOrEmpty(module.ModuleDefinition.DefaultAction))
|
if (action == Constants.DefaultAction && !string.IsNullOrEmpty(module.ModuleDefinition.DefaultAction))
|
||||||
|
@ -635,7 +668,7 @@
|
||||||
var obj = Activator.CreateInstance(moduletype) as IModuleControl;
|
var obj = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||||
if (obj != null)
|
if (obj != null)
|
||||||
{
|
{
|
||||||
resources = AddResources(resources, obj.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
resources = AddResources(resources, obj.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace, site.RenderMode);
|
||||||
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
||||||
{
|
{
|
||||||
// settings components are embedded within a framework settings module
|
// settings components are embedded within a framework settings module
|
||||||
|
@ -643,7 +676,7 @@
|
||||||
if (moduletype != null)
|
if (moduletype != null)
|
||||||
{
|
{
|
||||||
obj = Activator.CreateInstance(moduletype) as IModuleControl;
|
obj = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||||
resources = AddResources(resources, obj.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
resources = AddResources(resources, obj.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace, site.RenderMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -656,32 +689,35 @@
|
||||||
{
|
{
|
||||||
if (module.ModuleDefinition?.Resources != null)
|
if (module.ModuleDefinition?.Resources != null)
|
||||||
{
|
{
|
||||||
resources = AddResources(resources, module.ModuleDefinition.Resources.Where(item => item.Level == ResourceLevel.Site).ToList(), ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName));
|
resources = AddResources(resources, module.ModuleDefinition.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level == ResourceLevel.Site).ToList(), ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName), site.RenderMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Resource> AddResources(List<Resource> pageresources, List<Resource> resources, ResourceLevel level, Alias alias, string type, string name)
|
private List<Resource> AddResources(List<Resource> pageresources, List<Resource> resources, ResourceLevel level, Alias alias, string type, string name, string rendermode)
|
||||||
{
|
{
|
||||||
if (resources != null)
|
if (resources != null)
|
||||||
{
|
{
|
||||||
foreach (var resource in resources)
|
foreach (var resource in resources)
|
||||||
{
|
{
|
||||||
if (resource.Url.StartsWith("~"))
|
if (rendermode == RenderModes.Static || resource.ResourceType == ResourceType.Stylesheet || resource.Level == ResourceLevel.Site)
|
||||||
{
|
{
|
||||||
resource.Url = resource.Url.Replace("~", "/" + type + "/" + name + "/").Replace("//", "/");
|
if (resource.Url.StartsWith("~"))
|
||||||
}
|
{
|
||||||
if (!resource.Url.Contains("://") && alias.BaseUrl != "" && !resource.Url.StartsWith(alias.BaseUrl))
|
resource.Url = resource.Url.Replace("~", "/" + type + "/" + name + "/").Replace("//", "/");
|
||||||
{
|
}
|
||||||
resource.Url = alias.BaseUrl + resource.Url;
|
if (!resource.Url.Contains("://") && alias.BaseUrl != "" && !resource.Url.StartsWith(alias.BaseUrl))
|
||||||
}
|
{
|
||||||
|
resource.Url = alias.BaseUrl + resource.Url;
|
||||||
|
}
|
||||||
|
|
||||||
// ensure resource does not exist already
|
// ensure resource does not exist already
|
||||||
if (!pageresources.Exists(item => item.Url.ToLower() == resource.Url.ToLower()))
|
if (!pageresources.Exists(item => item.Url.ToLower() == resource.Url.ToLower()))
|
||||||
{
|
{
|
||||||
pageresources.Add(resource.Clone(level, name));
|
pageresources.Add(resource.Clone(level, name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -692,6 +728,7 @@
|
||||||
{
|
{
|
||||||
if (resources != null)
|
if (resources != null)
|
||||||
{
|
{
|
||||||
|
// include stylesheets to prevent FOUC
|
||||||
string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff");
|
string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff");
|
||||||
int count = 0;
|
int count = 0;
|
||||||
foreach (var resource in resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
|
foreach (var resource in resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
|
||||||
|
|
|
@ -760,7 +760,7 @@ namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(folderpath))
|
if (!Directory.Exists(folderpath))
|
||||||
{
|
{
|
||||||
string path = "";
|
string path = folderpath.StartsWith(Path.DirectorySeparatorChar) ? Path.DirectorySeparatorChar.ToString() : string.Empty;
|
||||||
var separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
|
var separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
|
||||||
string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries);
|
string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries);
|
||||||
foreach (string folder in folders)
|
foreach (string folder in folders)
|
||||||
|
|
|
@ -8,6 +8,7 @@ using Oqtane.Infrastructure;
|
||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
|
@ -33,7 +34,7 @@ namespace Oqtane.Controllers
|
||||||
int SiteId;
|
int SiteId;
|
||||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
||||||
{
|
{
|
||||||
return _visitors.GetVisitors(SiteId, DateTime.Parse(fromdate));
|
return _visitors.GetVisitors(SiteId, DateTime.ParseExact(fromdate, "yyyy-MM-dd", CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Routing;
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.AspNetCore.Antiforgery;
|
||||||
|
|
||||||
namespace OqtaneSSR.Extensions
|
namespace OqtaneSSR.Extensions
|
||||||
{
|
{
|
||||||
|
@ -23,6 +24,7 @@ namespace OqtaneSSR.Extensions
|
||||||
{
|
{
|
||||||
routeEndpointBuilder.Metadata.Add(new RootComponentMetadata(typeof(App)));
|
routeEndpointBuilder.Metadata.Add(new RootComponentMetadata(typeof(App)));
|
||||||
routeEndpointBuilder.Metadata.Add(new ComponentTypeMetadata(typeof(App)));
|
routeEndpointBuilder.Metadata.Add(new ComponentTypeMetadata(typeof(App)));
|
||||||
|
routeEndpointBuilder.Metadata.Add(new RequireAntiforgeryTokenAttribute());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -539,6 +539,8 @@ namespace Oqtane.Infrastructure
|
||||||
var identityUserManager = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
|
var identityUserManager = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
|
||||||
|
|
||||||
var tenant = tenants.GetTenants().FirstOrDefault(item => item.Name == install.TenantName);
|
var tenant = tenants.GetTenants().FirstOrDefault(item => item.Name == install.TenantName);
|
||||||
|
var rendermode = (!string.IsNullOrEmpty(install.RenderMode)) ? install.RenderMode : _configManager.GetSection("RenderMode").Value;
|
||||||
|
var runtime = (!string.IsNullOrEmpty(install.Runtime)) ? install.Runtime : _configManager.GetSection("Runtime").Value;
|
||||||
|
|
||||||
site = new Site
|
site = new Site
|
||||||
{
|
{
|
||||||
|
@ -556,9 +558,9 @@ namespace Oqtane.Infrastructure
|
||||||
DefaultContainerType = (!string.IsNullOrEmpty(install.DefaultContainer)) ? install.DefaultContainer : Constants.DefaultContainer,
|
DefaultContainerType = (!string.IsNullOrEmpty(install.DefaultContainer)) ? install.DefaultContainer : Constants.DefaultContainer,
|
||||||
AdminContainerType = (!string.IsNullOrEmpty(install.DefaultAdminContainer)) ? install.DefaultAdminContainer : Constants.DefaultAdminContainer,
|
AdminContainerType = (!string.IsNullOrEmpty(install.DefaultAdminContainer)) ? install.DefaultAdminContainer : Constants.DefaultAdminContainer,
|
||||||
SiteTemplateType = install.SiteTemplate,
|
SiteTemplateType = install.SiteTemplate,
|
||||||
RenderMode = (!string.IsNullOrEmpty(install.RenderMode)) ? install.RenderMode : _configManager.GetSection("RenderMode").Value,
|
RenderMode = rendermode,
|
||||||
Runtime = (!string.IsNullOrEmpty(install.Runtime)) ? install.Runtime : _configManager.GetSection("Runtime").Value,
|
Runtime = runtime,
|
||||||
Prerender = true,
|
Prerender = (rendermode == RenderModes.Interactive),
|
||||||
Hybrid = false
|
Hybrid = false
|
||||||
};
|
};
|
||||||
site = sites.AddSite(site);
|
site = sites.AddSite(site);
|
||||||
|
|
|
@ -197,6 +197,12 @@ namespace Oqtane.Infrastructure
|
||||||
string[] segments = entry.FullName.Split('/'); // ZipArchiveEntries always use unix path separator
|
string[] segments = entry.FullName.Split('/'); // ZipArchiveEntries always use unix path separator
|
||||||
string filename = Path.Combine(folder, string.Join(Path.DirectorySeparatorChar, segments, ignoreLeadingSegments, segments.Length - ignoreLeadingSegments));
|
string filename = Path.Combine(folder, string.Join(Path.DirectorySeparatorChar, segments, ignoreLeadingSegments, segments.Length - ignoreLeadingSegments));
|
||||||
|
|
||||||
|
// validate path to prevent path traversal
|
||||||
|
if (!Path.GetFullPath(filename).StartsWith(folder + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(Path.GetDirectoryName(filename)))
|
if (!Directory.Exists(Path.GetDirectoryName(filename)))
|
||||||
|
@ -227,6 +233,7 @@ namespace Oqtane.Infrastructure
|
||||||
// an error occurred extracting the file
|
// an error occurred extracting the file
|
||||||
filename = "";
|
filename = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,56 +201,54 @@ namespace Oqtane.Managers
|
||||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||||
if (identityuser != null)
|
if (identityuser != null)
|
||||||
{
|
{
|
||||||
var valid = true;
|
|
||||||
if (!string.IsNullOrEmpty(user.Password))
|
if (!string.IsNullOrEmpty(user.Password))
|
||||||
{
|
{
|
||||||
var validator = new PasswordValidator<IdentityUser>();
|
var validator = new PasswordValidator<IdentityUser>();
|
||||||
var result = await validator.ValidateAsync(_identityUserManager, null, user.Password);
|
var result = await validator.ValidateAsync(_identityUserManager, null, user.Password);
|
||||||
valid = result.Succeeded;
|
if (result.Succeeded)
|
||||||
if (valid)
|
|
||||||
{
|
{
|
||||||
identityuser.PasswordHash = _identityUserManager.PasswordHasher.HashPassword(identityuser, user.Password);
|
identityuser.PasswordHash = _identityUserManager.PasswordHasher.HashPassword(identityuser, user.Password);
|
||||||
|
await _identityUserManager.UpdateAsync(identityuser);
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
if (valid)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(user.Password))
|
|
||||||
{
|
{
|
||||||
await _identityUserManager.UpdateAsync(identityuser); // requires password to be provided
|
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Update, "Unable To Update User {Username}. Password Does Not Meet Complexity Requirements.", user.Username);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.Email != identityuser.Email)
|
|
||||||
{
|
|
||||||
await _identityUserManager.SetEmailAsync(identityuser, user.Email);
|
|
||||||
|
|
||||||
// if email address changed and user is not administrator, email verification is required for new email address
|
|
||||||
if (!user.EmailConfirmed)
|
|
||||||
{
|
|
||||||
var alias = _tenantManager.GetAlias();
|
|
||||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
|
||||||
string url = alias.Protocol + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
|
||||||
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Verify The Email Address Associated To Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
|
||||||
var notification = new Notification(user.SiteId, user, "User Account Verification", body);
|
|
||||||
_notifications.AddNotification(notification);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var emailConfirmationToken = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
|
||||||
await _identityUserManager.ConfirmEmailAsync(identityuser, emailConfirmationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
user = _users.UpdateUser(user);
|
|
||||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias(), EntityNames.User, user.UserId, SyncEventActions.Update);
|
|
||||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias(), EntityNames.User, user.UserId, SyncEventActions.Reload);
|
|
||||||
user.Password = ""; // remove sensitive information
|
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (user.Email != identityuser.Email)
|
||||||
{
|
{
|
||||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Update, "Unable To Update User {Username}. Password Does Not Meet Complexity Requirements.", user.Username);
|
await _identityUserManager.SetEmailAsync(identityuser, user.Email);
|
||||||
user = null;
|
|
||||||
|
// if email address changed and it is not confirmed, verification is required for new email address
|
||||||
|
if (!user.EmailConfirmed)
|
||||||
|
{
|
||||||
|
var alias = _tenantManager.GetAlias();
|
||||||
|
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||||
|
string url = alias.Protocol + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||||
|
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Verify The Email Address Associated To Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||||
|
var notification = new Notification(user.SiteId, user, "User Account Verification", body);
|
||||||
|
_notifications.AddNotification(notification);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.EmailConfirmed)
|
||||||
|
{
|
||||||
|
var emailConfirmationToken = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||||
|
await _identityUserManager.ConfirmEmailAsync(identityuser, emailConfirmationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
user = _users.UpdateUser(user);
|
||||||
|
_syncManager.AddSyncEvent(_tenantManager.GetAlias(), EntityNames.User, user.UserId, SyncEventActions.Update);
|
||||||
|
_syncManager.AddSyncEvent(_tenantManager.GetAlias(), EntityNames.User, user.UserId, SyncEventActions.Reload);
|
||||||
|
user.Password = ""; // remove sensitive information
|
||||||
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Update, "Unable To Update User {Username}. User Does Not Exist.", user.Username);
|
||||||
|
user = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
|
|
|
@ -7,6 +7,7 @@ using Oqtane.Repository;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using Oqtane.Migrations.Framework;
|
using Oqtane.Migrations.Framework;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
// ReSharper disable ConvertToUsingDeclaration
|
// ReSharper disable ConvertToUsingDeclaration
|
||||||
|
|
||||||
|
@ -29,10 +30,11 @@ namespace Oqtane.Modules.HtmlText.Manager
|
||||||
public string ExportModule(Module module)
|
public string ExportModule(Module module)
|
||||||
{
|
{
|
||||||
string content = "";
|
string content = "";
|
||||||
var htmlText = _htmlText.GetHtmlText(module.ModuleId);
|
var htmltexts = _htmlText.GetHtmlTexts(module.ModuleId);
|
||||||
if (htmlText != null)
|
if (htmltexts != null && htmltexts.Any())
|
||||||
{
|
{
|
||||||
content = WebUtility.HtmlEncode(htmlText.Content);
|
var htmltext = htmltexts.OrderByDescending(item => item.CreatedOn).First();
|
||||||
|
content = WebUtility.HtmlEncode(htmltext.Content);
|
||||||
}
|
}
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>5.1.1</Version>
|
<Version>5.1.2</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
@ -33,19 +33,19 @@
|
||||||
<EmbeddedResource Include="Scripts\MigrateTenant.sql" />
|
<EmbeddedResource Include="Scripts\MigrateTenant.sql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.0" />
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.5">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.5" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.4" />
|
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.5" />
|
||||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.8" />
|
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.8" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -46,13 +46,16 @@ namespace Oqtane.Pages
|
||||||
{
|
{
|
||||||
var sitemap = new List<Sitemap>();
|
var sitemap = new List<Sitemap>();
|
||||||
|
|
||||||
|
// internal pages which should not be indexed
|
||||||
|
string[] internalPaths = { "login", "register", "reset", "404" };
|
||||||
|
|
||||||
// build site map
|
// build site map
|
||||||
var rooturl = _alias.Protocol + (string.IsNullOrEmpty(_alias.Path) ? _alias.Name : _alias.Name.Substring(0, _alias.Name.IndexOf("/")));
|
var rooturl = _alias.Protocol + (string.IsNullOrEmpty(_alias.Path) ? _alias.Name : _alias.Name.Substring(0, _alias.Name.IndexOf("/")));
|
||||||
var moduleDefinitions = _moduleDefinitions.GetModuleDefinitions(_alias.SiteId).ToList();
|
var moduleDefinitions = _moduleDefinitions.GetModuleDefinitions(_alias.SiteId).ToList();
|
||||||
var pageModules = _pageModules.GetPageModules(_alias.SiteId);
|
var pageModules = _pageModules.GetPageModules(_alias.SiteId);
|
||||||
foreach (var page in _pages.GetPages(_alias.SiteId))
|
foreach (var page in _pages.GetPages(_alias.SiteId))
|
||||||
{
|
{
|
||||||
if (_userPermissions.IsAuthorized(null, PermissionNames.View, page.PermissionList) && page.IsNavigation)
|
if (_userPermissions.IsAuthorized(null, PermissionNames.View, page.PermissionList) && !internalPaths.Contains(page.Path))
|
||||||
{
|
{
|
||||||
var pageurl = rooturl;
|
var pageurl = rooturl;
|
||||||
if (string.IsNullOrEmpty(page.Url))
|
if (string.IsNullOrEmpty(page.Url))
|
||||||
|
|
|
@ -287,7 +287,9 @@ namespace Oqtane.Repository
|
||||||
{
|
{
|
||||||
ModuleDefinition moduledefinition;
|
ModuleDefinition moduledefinition;
|
||||||
|
|
||||||
|
Type[] moduletypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModule))).ToArray();
|
||||||
Type[] modulecontroltypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModuleControl))).ToArray();
|
Type[] modulecontroltypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModuleControl))).ToArray();
|
||||||
|
|
||||||
foreach (Type modulecontroltype in modulecontroltypes)
|
foreach (Type modulecontroltype in modulecontroltypes)
|
||||||
{
|
{
|
||||||
// Check if type should be ignored
|
// Check if type should be ignored
|
||||||
|
@ -299,12 +301,9 @@ namespace Oqtane.Repository
|
||||||
int index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType);
|
int index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
// determine if this module implements IModule
|
// determine if this component is part of a module which implements IModule
|
||||||
Type moduletype = assembly
|
Type moduletype = moduletypes.FirstOrDefault(item => item.Namespace == modulecontroltype.Namespace);
|
||||||
.GetTypes()
|
|
||||||
.Where(item => item.Namespace != null)
|
|
||||||
.Where(item => item.Namespace == modulecontroltype.Namespace || item.Namespace.StartsWith(modulecontroltype.Namespace + "."))
|
|
||||||
.FirstOrDefault(item => item.GetInterfaces().Contains(typeof(IModule)));
|
|
||||||
if (moduletype != null)
|
if (moduletype != null)
|
||||||
{
|
{
|
||||||
// get property values from IModule
|
// get property values from IModule
|
||||||
|
@ -399,6 +398,22 @@ namespace Oqtane.Repository
|
||||||
moduledefinitions[index] = moduledefinition;
|
moduledefinitions[index] = moduledefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// process modules without UI components
|
||||||
|
foreach (var moduletype in moduletypes.Where(m1 => !modulecontroltypes.Any(m2 => m1.Namespace == m2.Namespace)))
|
||||||
|
{
|
||||||
|
// get property values from IModule
|
||||||
|
var moduleobject = Activator.CreateInstance(moduletype) as IModule;
|
||||||
|
moduledefinition = moduleobject.ModuleDefinition;
|
||||||
|
moduledefinition.ModuleDefinitionName = moduletype.Namespace + ", " + moduletype.Assembly.GetName().Name;
|
||||||
|
moduledefinition.AssemblyName = assembly.GetName().Name;
|
||||||
|
moduledefinition.Categories = "Headless";
|
||||||
|
moduledefinition.PermissionList = new List<Permission>
|
||||||
|
{
|
||||||
|
new Permission(PermissionNames.Utilize, RoleNames.Host, true)
|
||||||
|
};
|
||||||
|
moduledefinitions.Add(moduledefinition);
|
||||||
|
}
|
||||||
|
|
||||||
return moduledefinitions;
|
return moduledefinitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -165,22 +165,23 @@ namespace Oqtane.Repository
|
||||||
if (!serverstate.IsInitialized)
|
if (!serverstate.IsInitialized)
|
||||||
{
|
{
|
||||||
var site = GetSite(alias.SiteId);
|
var site = GetSite(alias.SiteId);
|
||||||
|
if (site != null)
|
||||||
// initialize theme Assemblies
|
|
||||||
site.Themes = _themeRepository.GetThemes().ToList();
|
|
||||||
|
|
||||||
// initialize module Assemblies
|
|
||||||
var moduleDefinitions = _moduleDefinitionRepository.GetModuleDefinitions(alias.SiteId);
|
|
||||||
|
|
||||||
// execute migrations
|
|
||||||
var version = ProcessSiteMigrations(alias, site);
|
|
||||||
version = ProcessPageTemplates(alias, site, moduleDefinitions, version);
|
|
||||||
if (site.Version != version)
|
|
||||||
{
|
{
|
||||||
site.Version = version;
|
// initialize theme Assemblies
|
||||||
UpdateSite(site);
|
site.Themes = _themeRepository.GetThemes().ToList();
|
||||||
}
|
|
||||||
|
|
||||||
|
// initialize module Assemblies
|
||||||
|
var moduleDefinitions = _moduleDefinitionRepository.GetModuleDefinitions(alias.SiteId);
|
||||||
|
|
||||||
|
// execute migrations
|
||||||
|
var version = ProcessSiteMigrations(alias, site);
|
||||||
|
version = ProcessPageTemplates(alias, site, moduleDefinitions, version);
|
||||||
|
if (site.Version != version)
|
||||||
|
{
|
||||||
|
site.Version = version;
|
||||||
|
UpdateSite(site);
|
||||||
|
}
|
||||||
|
}
|
||||||
serverstate.IsInitialized = true;
|
serverstate.IsInitialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -411,7 +412,7 @@ namespace Oqtane.Repository
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parent = pages.FirstOrDefault(item => item.Path.ToLower() == pageTemplate.Parent.ToLower());
|
parent = pages.FirstOrDefault(item => item.Path.ToLower() == ((pageTemplate.Parent == "/") ? "" : pageTemplate.Parent.ToLower()));
|
||||||
}
|
}
|
||||||
page.ParentId = (parent != null) ? parent.PageId : null;
|
page.ParentId = (parent != null) ? parent.PageId : null;
|
||||||
page.Path = page.Path.ToLower();
|
page.Path = page.Path.ToLower();
|
||||||
|
@ -487,7 +488,11 @@ namespace Oqtane.Repository
|
||||||
pageModule.Order = (pageTemplateModule.Order == 0) ? 1 : pageTemplateModule.Order;
|
pageModule.Order = (pageTemplateModule.Order == 0) ? 1 : pageTemplateModule.Order;
|
||||||
pageModule.ContainerType = pageTemplateModule.ContainerType;
|
pageModule.ContainerType = pageTemplateModule.ContainerType;
|
||||||
pageModule.IsDeleted = pageTemplateModule.IsDeleted;
|
pageModule.IsDeleted = pageTemplateModule.IsDeleted;
|
||||||
pageModule.Module.PermissionList = pageTemplateModule.PermissionList;
|
pageModule.Module.PermissionList = new List<Permission>();
|
||||||
|
foreach (var permission in pageTemplateModule.PermissionList)
|
||||||
|
{
|
||||||
|
pageModule.Module.PermissionList.Add(permission.Clone(permission));
|
||||||
|
}
|
||||||
pageModule.Module.AllPages = false;
|
pageModule.Module.AllPages = false;
|
||||||
pageModule.Module.IsDeleted = false;
|
pageModule.Module.IsDeleted = false;
|
||||||
try
|
try
|
||||||
|
@ -539,8 +544,11 @@ namespace Oqtane.Repository
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var module = _moduleRepository.GetModule(pageModule.ModuleId);
|
var module = _moduleRepository.GetModule(pageModule.ModuleId);
|
||||||
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
|
if (module != null)
|
||||||
((IPortable)moduleobject).ImportModule(module, pageTemplateModule.Content, moduleDefinition.Version);
|
{
|
||||||
|
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
|
||||||
|
((IPortable)moduleobject).ImportModule(module, pageTemplateModule.Content, moduleDefinition.Version);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,6 +13,7 @@ using Oqtane.Shared;
|
||||||
using Oqtane.Themes;
|
using Oqtane.Themes;
|
||||||
using System.Reflection.Metadata;
|
using System.Reflection.Metadata;
|
||||||
using Oqtane.Migrations.Master;
|
using Oqtane.Migrations.Master;
|
||||||
|
using Oqtane.Modules;
|
||||||
|
|
||||||
namespace Oqtane.Repository
|
namespace Oqtane.Repository
|
||||||
{
|
{
|
||||||
|
@ -224,9 +225,11 @@ namespace Oqtane.Repository
|
||||||
private List<Theme> LoadThemesFromAssembly(List<Theme> themes, Assembly assembly)
|
private List<Theme> LoadThemesFromAssembly(List<Theme> themes, Assembly assembly)
|
||||||
{
|
{
|
||||||
Theme theme;
|
Theme theme;
|
||||||
List<Type> themeTypes = new List<Type>();
|
|
||||||
|
|
||||||
|
Type[] themeTypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ITheme))).ToArray();
|
||||||
Type[] themeControlTypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IThemeControl))).ToArray();
|
Type[] themeControlTypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IThemeControl))).ToArray();
|
||||||
|
Type[] containerControlTypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IContainerControl))).ToArray();
|
||||||
|
|
||||||
foreach (Type themeControlType in themeControlTypes)
|
foreach (Type themeControlType in themeControlTypes)
|
||||||
{
|
{
|
||||||
// Check if type should be ignored
|
// Check if type should be ignored
|
||||||
|
@ -240,16 +243,9 @@ namespace Oqtane.Repository
|
||||||
int index = themes.FindIndex(item => item.ThemeName == qualifiedThemeType);
|
int index = themes.FindIndex(item => item.ThemeName == qualifiedThemeType);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
// Find all types in the assembly with the same namespace root
|
// determine if this component is part of a theme which implements ITheme
|
||||||
themeTypes = assembly.GetTypes()
|
Type themetype = themeTypes.FirstOrDefault(item => item.Namespace == themeControlType.Namespace);
|
||||||
.Where(item => !item.IsOqtaneIgnore())
|
|
||||||
.Where(item => item.Namespace != null)
|
|
||||||
.Where(item => item.Namespace == themeControlType.Namespace || item.Namespace.StartsWith(themeControlType.Namespace + "."))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// determine if this theme implements ITheme
|
|
||||||
Type themetype = themeTypes
|
|
||||||
.FirstOrDefault(item => item.GetInterfaces().Contains(typeof(ITheme)));
|
|
||||||
if (themetype != null)
|
if (themetype != null)
|
||||||
{
|
{
|
||||||
var themeobject = Activator.CreateInstance(themetype) as ITheme;
|
var themeobject = Activator.CreateInstance(themetype) as ITheme;
|
||||||
|
@ -285,6 +281,7 @@ namespace Oqtane.Repository
|
||||||
}
|
}
|
||||||
theme = themes[index];
|
theme = themes[index];
|
||||||
|
|
||||||
|
// add theme control
|
||||||
var themecontrolobject = Activator.CreateInstance(themeControlType) as IThemeControl;
|
var themecontrolobject = Activator.CreateInstance(themeControlType) as IThemeControl;
|
||||||
theme.Themes.Add(
|
theme.Themes.Add(
|
||||||
new ThemeControl
|
new ThemeControl
|
||||||
|
@ -296,14 +293,12 @@ namespace Oqtane.Repository
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// containers
|
if (!theme.Containers.Any())
|
||||||
Type[] containertypes = themeTypes
|
|
||||||
.Where(item => item.GetInterfaces().Contains(typeof(IContainerControl))).ToArray();
|
|
||||||
foreach (Type containertype in containertypes)
|
|
||||||
{
|
{
|
||||||
var containerobject = Activator.CreateInstance(containertype) as IThemeControl;
|
// add container controls
|
||||||
if (theme.Containers.FirstOrDefault(item => item.TypeName == containertype.FullName + ", " + themeControlType.Assembly.GetName().Name) == null)
|
foreach (Type containertype in containerControlTypes.Where(item => item.Namespace == themeControlType.Namespace))
|
||||||
{
|
{
|
||||||
|
var containerobject = Activator.CreateInstance(containertype) as IThemeControl;
|
||||||
theme.Containers.Add(
|
theme.Containers.Add(
|
||||||
new ThemeControl
|
new ThemeControl
|
||||||
{
|
{
|
||||||
|
|
|
@ -216,6 +216,7 @@ namespace Oqtane
|
||||||
app.UseCors();
|
app.UseCors();
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
app.UseAntiforgery();
|
||||||
|
|
||||||
if (_useSwagger)
|
if (_useSwagger)
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,9 +13,9 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.5" />
|
||||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||||
|
|
|
@ -19,10 +19,10 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -12,9 +12,9 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -35,13 +35,15 @@ Oqtane.RichTextEditor = {
|
||||||
enableQuillEditor: function (editorElement, mode) {
|
enableQuillEditor: function (editorElement, mode) {
|
||||||
editorElement.__quill.enable(mode);
|
editorElement.__quill.enable(mode);
|
||||||
},
|
},
|
||||||
insertQuillImage: function (quillElement, imageURL, altText) {
|
getCurrentCursor: function (quillElement) {
|
||||||
var Delta = Quill.import('delta');
|
var editorIndex = 0;
|
||||||
editorIndex = 0;
|
|
||||||
|
|
||||||
if (quillElement.__quill.getSelection() !== null) {
|
if (quillElement.__quill.getSelection() !== null) {
|
||||||
editorIndex = quillElement.__quill.getSelection().index;
|
editorIndex = quillElement.__quill.getSelection().index;
|
||||||
}
|
}
|
||||||
|
return editorIndex;
|
||||||
|
},
|
||||||
|
insertQuillImage: function (quillElement, imageURL, altText, editorIndex) {
|
||||||
|
var Delta = Quill.import('delta');
|
||||||
|
|
||||||
return quillElement.__quill.updateContents(
|
return quillElement.__quill.updateContents(
|
||||||
new Delta()
|
new Delta()
|
||||||
|
|
|
@ -35,5 +35,10 @@ namespace Oqtane.Modules
|
||||||
/// Specifies the required render mode for the module control ie. Static,Interactive
|
/// Specifies the required render mode for the module control ie. Static,Interactive
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string RenderMode { get; }
|
string RenderMode { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the prerender mode for the moudle control ie: true or false
|
||||||
|
/// </summary>
|
||||||
|
bool? Prerender { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,6 +117,8 @@ namespace Oqtane.Models
|
||||||
public bool UseAdminContainer { get; set; }
|
public bool UseAdminContainer { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public string RenderMode{ get; set; }
|
public string RenderMode{ get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public bool? Prerender { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,20 @@ namespace Oqtane.Models
|
||||||
IsAuthorized = isAuthorized;
|
IsAuthorized = isAuthorized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Permission Clone(Permission permission)
|
||||||
|
{
|
||||||
|
return new Permission
|
||||||
|
{
|
||||||
|
SiteId = permission.SiteId,
|
||||||
|
EntityName = permission.EntityName,
|
||||||
|
EntityId = permission.EntityId,
|
||||||
|
PermissionName = permission.PermissionName,
|
||||||
|
RoleName = permission.RoleName,
|
||||||
|
UserId = permission.UserId,
|
||||||
|
IsAuthorized = permission.IsAuthorized
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
[Obsolete("The Role property is deprecated", false)]
|
[Obsolete("The Role property is deprecated", false)]
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
[JsonIgnore] // exclude from API payload
|
[JsonIgnore] // exclude from API payload
|
||||||
|
|
|
@ -43,13 +43,19 @@ namespace Oqtane.Models
|
||||||
int pos = PagePath.IndexOf("/" + Constants.UrlParametersDelimiter + "/");
|
int pos = PagePath.IndexOf("/" + Constants.UrlParametersDelimiter + "/");
|
||||||
if (pos != -1)
|
if (pos != -1)
|
||||||
{
|
{
|
||||||
UrlParameters = PagePath.Substring(pos + 3);
|
if (pos + 3 < PagePath.Length)
|
||||||
|
{
|
||||||
|
UrlParameters = PagePath.Substring(pos + 3);
|
||||||
|
}
|
||||||
PagePath = PagePath.Substring(0, pos);
|
PagePath = PagePath.Substring(0, pos);
|
||||||
}
|
}
|
||||||
pos = PagePath.IndexOf("/" + Constants.ModuleDelimiter + "/");
|
pos = PagePath.IndexOf("/" + Constants.ModuleDelimiter + "/");
|
||||||
if (pos != -1)
|
if (pos != -1)
|
||||||
{
|
{
|
||||||
ModuleId = PagePath.Substring(pos + 3);
|
if (pos + 3 < PagePath.Length)
|
||||||
|
{
|
||||||
|
ModuleId = PagePath.Substring(pos + 3);
|
||||||
|
}
|
||||||
PagePath = PagePath.Substring(0, pos);
|
PagePath = PagePath.Substring(0, pos);
|
||||||
}
|
}
|
||||||
if (ModuleId.Length != 0)
|
if (ModuleId.Length != 0)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>5.1.1</Version>
|
<Version>5.1.2</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
@ -19,8 +19,8 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
|
||||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.3" />
|
<PackageReference Include="System.Text.Json" Version="8.0.3" />
|
||||||
|
|
|
@ -4,8 +4,8 @@ namespace Oqtane.Shared
|
||||||
{
|
{
|
||||||
public class Constants
|
public class Constants
|
||||||
{
|
{
|
||||||
public static readonly string Version = "5.1.1";
|
public static readonly string Version = "5.1.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,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.1.1";
|
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,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.1.1,5.1.2";
|
||||||
public const string PackageId = "Oqtane.Framework";
|
public const string PackageId = "Oqtane.Framework";
|
||||||
public const string ClientId = "Oqtane.Client";
|
public const string ClientId = "Oqtane.Client";
|
||||||
public const string UpdaterPackageId = "Oqtane.Updater";
|
public const string UpdaterPackageId = "Oqtane.Updater";
|
||||||
|
|
|
@ -44,9 +44,10 @@ namespace Oqtane.Shared
|
||||||
string querystring = "";
|
string querystring = "";
|
||||||
string fragment = "";
|
string fragment = "";
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(path) && !path.StartsWith("/")) path = "/" + path;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(parameters))
|
if (!string.IsNullOrEmpty(parameters))
|
||||||
{
|
{
|
||||||
// parse parameters
|
|
||||||
(string urlparameters, querystring, fragment) = ParseParameters(parameters);
|
(string urlparameters, querystring, fragment) = ParseParameters(parameters);
|
||||||
if (!string.IsNullOrEmpty(urlparameters))
|
if (!string.IsNullOrEmpty(urlparameters))
|
||||||
{
|
{
|
||||||
|
@ -138,6 +139,9 @@ namespace Oqtane.Shared
|
||||||
|
|
||||||
public static string FormatContent(string content, Alias alias, string operation)
|
public static string FormatContent(string content, Alias alias, string operation)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(content) || alias == null)
|
||||||
|
return content;
|
||||||
|
|
||||||
var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : "";
|
var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : "";
|
||||||
switch (operation)
|
switch (operation)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Version>5.1.1</Version>
|
<Version>5.1.2</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
|
205
README.md
205
README.md
|
@ -1,6 +1,6 @@
|
||||||
# Latest Release
|
# Latest Release
|
||||||
|
|
||||||
[5.1.0](https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0) was released on Mar 27, 2024 and is a major release providing Static Server Rendering support for Blazor in .NET 8. This release includes 263 pull requests by 6 different contributors, pushing the total number of project commits all-time to over 5100. The Oqtane framework continues to evolve at a rapid pace to meet the needs of .NET developers.
|
[5.1.1](https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1) was released on Apr 16, 2024 and is primarily a stabilization release, including a variety of improvements to the Static Server-Side Rendering support for Blazor in .NET 8. This release includes 40 pull requests by 6 different contributors, pushing the total number of project commits all-time to over 5200. The Oqtane framework continues to evolve at a rapid pace to meet the needs of .NET developers.
|
||||||
|
|
||||||
[](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Foqtane%2Foqtane.framework%2Fmaster%2Fazuredeploy.json)
|
[](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Foqtane%2Foqtane.framework%2Fmaster%2Fazuredeploy.json)
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Oqtane is an open source CMS and Application Framework that provides advanced functionality for developing web, mobile, and desktop applications on .NET. It leverages Blazor to compose a fully dynamic digital experience which can be hosted on Blazor Server, Blazor WebAssembly, or Blazor Hybrid (via .NET MAUI).
|
Oqtane is an open source CMS and Application Framework that provides advanced functionality for developing web, mobile, and desktop applications on .NET. It leverages Blazor to compose a fully dynamic digital experience which can be hosted on Static Blazor, Blazor Server, Blazor WebAssembly, or Blazor Hybrid (via .NET MAUI).
|
||||||
|
|
||||||
Oqtane is being developed based on some fundamental principles which are outlined in the [Oqtane Philosophy](https://www.oqtane.org/blog/!/20/oqtane-philosophy).
|
Oqtane is being developed based on some fundamental principles which are outlined in the [Oqtane Philosophy](https://www.oqtane.org/blog/!/20/oqtane-philosophy).
|
||||||
|
|
||||||
|
@ -18,15 +18,15 @@ Please note that this project is owned by the .NET Foundation and is governed by
|
||||||
|
|
||||||
**Using Version 5:**
|
**Using Version 5:**
|
||||||
|
|
||||||
- Install **[.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)**.
|
- Install **[.NET 8.0.4 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)**.
|
||||||
|
|
||||||
- Install the latest edition (v17.8 or higher) of [Visual Studio 2022](https://visualstudio.microsoft.com/downloads) with the **ASP.NET and web development** workload enabled. Oqtane works with ALL editions of Visual Studio from Community to Enterprise. If you wish to use LocalDB for development ( not a requirement as Oqtane supports SQLite, mySQL, and PostgreSQL ) you must also install the **Data storage and processing**.
|
- Install the latest edition (v17.9 or higher) of [Visual Studio 2022](https://visualstudio.microsoft.com/downloads) with the **ASP.NET and web development** workload enabled. Oqtane works with ALL editions of Visual Studio from Community to Enterprise. If you wish to use LocalDB for development ( not a requirement as Oqtane supports SQLite, mySQL, and PostgreSQL ) you must also install the **Data storage and processing**.
|
||||||
|
|
||||||
- Clone the Oqtane dev branch source code to your local system.
|
- Clone the Oqtane dev branch source code to your local system.
|
||||||
|
|
||||||
- Open the **Oqtane.sln** solution file.
|
- Open the **Oqtane.sln** solution file.
|
||||||
|
|
||||||
- **Important:** Build the solution.
|
- **Important:** Rebuild the entire solution before running it.
|
||||||
|
|
||||||
- Make sure you specify Oqtane.Server as the Startup Project
|
- Make sure you specify Oqtane.Server as the Startup Project
|
||||||
|
|
||||||
|
@ -63,8 +63,8 @@ Backlog (TBD)
|
||||||
- [ ] Folder Providers
|
- [ ] Folder Providers
|
||||||
- [ ] Generative AI Integration
|
- [ ] Generative AI Integration
|
||||||
|
|
||||||
5.1.1 (Apr 2024)
|
[5.1.1](https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.1) (Apr 16, 2024)
|
||||||
- [ ] Stabilization improvements
|
- [x] Stabilization improvements
|
||||||
|
|
||||||
[5.1.0](https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0) (Mar 27, 2024)
|
[5.1.0](https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0) (Mar 27, 2024)
|
||||||
- [x] Migration to the new unified Blazor approach in .NET 8 (ie. blazor.web.js)
|
- [x] Migration to the new unified Blazor approach in .NET 8 (ie. blazor.web.js)
|
||||||
|
@ -79,200 +79,11 @@ Backlog (TBD)
|
||||||
[5.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0) (Nov 16, 2023)
|
[5.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0) (Nov 16, 2023)
|
||||||
- [x] Migration to .NET 8
|
- [x] Migration to .NET 8
|
||||||
|
|
||||||
[4.0.6](https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.6) ( Oct 16, 2023 )
|
➡️ Full list and older versions can be found in the [docs roadmap](https://docs.oqtane.org/guides/roadmap/index.html)
|
||||||
- [x] Stabilization improvements
|
|
||||||
|
|
||||||
[4.0.5](https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.5) ( Sep 26, 2023 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
|
|
||||||
[4.0.4](https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4) ( Sep 25, 2023 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
- [x] User Import
|
|
||||||
|
|
||||||
[4.0.3](https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3) ( Aug 29, 2023 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
|
|
||||||
[4.0.2](https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2) ( Aug 9, 2023 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
|
|
||||||
[4.0.1](https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1) ( Jul 18, 2023 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
|
|
||||||
[4.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0) ( Jun 26, 2023 )
|
|
||||||
- [x] Migration to .NET 7
|
|
||||||
- [x] Improved JavaScript, CSS, and Meta support
|
|
||||||
- [x] Optimized Client Assembly Loading
|
|
||||||
- [x] Routable Modules (ie. declarative configuration)
|
|
||||||
- [x] Site Template improvements
|
|
||||||
- [x] IEventSubscriber interface
|
|
||||||
|
|
||||||
[3.4.3](https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.3) ( May 3, 2023 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
|
|
||||||
[3.4.2](https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.2) ( Mar 29, 2023 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
|
|
||||||
[3.4.1](https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.1) ( Mar 13, 2023 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
|
|
||||||
[3.4.0](https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0) ( Mar 12, 2023 )
|
|
||||||
- [x] Permissions performance optimization
|
|
||||||
- [x] Connection string management improvements
|
|
||||||
- [x] XML site map generator
|
|
||||||
- [x] OIDC integration with User Profiles
|
|
||||||
|
|
||||||
[3.3.1](https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1) ( Jan 14, 2023 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
|
|
||||||
[3.3.0](https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.0) ( Jan 12, 2023 )
|
|
||||||
- [x] Dynamic Authorization Policies
|
|
||||||
- [x] Entity-Level Permissions
|
|
||||||
- [x] Extended Module Permissions
|
|
||||||
|
|
||||||
[3.2.1](https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.1) ( Oct 17, 2022 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
- [x] Server Event System
|
|
||||||
|
|
||||||
[3.2.0](https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0) ( Sep 13, 2022 )
|
|
||||||
- [x] .NET MAUI / Blazor Hybrid support
|
|
||||||
- [x] Upgrade to Bootstrap 5.2
|
|
||||||
|
|
||||||
[3.1.3](https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.3) ( Jun 27, 2022 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
|
|
||||||
[3.1.2](https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.2) ( May 14, 2022 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
|
|
||||||
[3.1.1](https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1) ( May 3, 2022 )
|
|
||||||
- [x] Stabilization improvements
|
|
||||||
|
|
||||||
[3.1.0](https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.0) ( Apr 5, 2022 )
|
|
||||||
- [x] User account lockout support
|
|
||||||
- [x] Two factor authentication support
|
|
||||||
- [x] Per-site configuration of password complexity, lockout criteria
|
|
||||||
- [x] External login support via OAuth2 / OpenID Connect
|
|
||||||
- [x] Support for Single Sign On (SSO) via OpenID Connect
|
|
||||||
- [x] External client support via Jwt tokens
|
|
||||||
- [x] Downstream API support via Jwt tokens
|
|
||||||
- [x] CSS resource hierarchy support
|
|
||||||
- [x] Site structure/content migration
|
|
||||||
- [x] Event log notifications
|
|
||||||
- [x] 404 page handling
|
|
||||||
- [x] Property change component notifications
|
|
||||||
- [x] Support for ES6 JavaScript modules
|
|
||||||
|
|
||||||
[3.0.3](https://github.com/oqtane/oqtane.framework/releases/tag/v3.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
|
|
||||||
|
|
||||||
[3.0.2](https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2) ( Jan 16, 2022 )
|
|
||||||
- [x] Default alias specification, auto alias registration, redirect logic
|
|
||||||
- [x] Improvements to visitor tracking and url mapping
|
|
||||||
- [x] Scheduler enhancements for stop/start, weekly and one-time jobs
|
|
||||||
- [x] Purge job for daily housekeeping of event log and visitors
|
|
||||||
- [x] Granular security filtering for Settings
|
|
||||||
|
|
||||||
[3.0.1](https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1) ( Dec 12, 2021 )
|
|
||||||
- [x] Url mapping for broken links, content migration
|
|
||||||
- [x] Visitor tracking for usage insights, personalization
|
|
||||||
- [x] User experience improvements in Page and Module management
|
|
||||||
|
|
||||||
[3.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.0) ( Nov 11, 2021 )
|
|
||||||
- [x] Migration to .NET 6
|
|
||||||
- [x] Blazor hosting model flexibility per site
|
|
||||||
- [x] Blazor WebAssembly prerendering support
|
|
||||||
|
|
||||||
[2.3.1](https://github.com/oqtane/oqtane.framework/releases/tag/v2.3.1) ( Sep 27, 2021 )
|
|
||||||
- [x] Complete UI migration to Bootstrap 5 and HTML5 form validation
|
|
||||||
- [x] Improve module/theme installation and add support for commercial extensions
|
|
||||||
- [x] Replace System.Drawing with ImageSharp
|
|
||||||
- [x] Image resizing service
|
|
||||||
|
|
||||||
[2.2.0](https://github.com/oqtane/oqtane.framework/releases/tag/v2.2.0) ( Jul 6, 2021 )
|
|
||||||
- [x] Bootstrap 5 Upgrade
|
|
||||||
- [x] Package Service integration
|
|
||||||
- [x] Default and Shared Resource File inclusion
|
|
||||||
- [x] Startup Error logging
|
|
||||||
- [x] API Controller Validation and Logging
|
|
||||||
|
|
||||||
[2.1.0](https://github.com/oqtane/oqtane.framework/releases/tag/v2.1.0) ( Jun 4, 2021 )
|
|
||||||
- [x] Cross Platform Database Support ( ie. LocalDB, SQL Server, SQLite, MySQL, PostgreSQL ) - see [#964](https://github.com/oqtane/oqtane.framework/discussions/964)
|
|
||||||
- [x] Utilize EF Core Migrations - see [#964](https://github.com/oqtane/oqtane.framework/discussions/964)
|
|
||||||
- [x] Public Content Folder support
|
|
||||||
- [x] Multi-tenant Infrastructure improvements
|
|
||||||
- [x] User Authorization optimization
|
|
||||||
- [x] Consolidation of Package Management
|
|
||||||
- [x] Blazor Server Pre-rendering
|
|
||||||
- [x] Translation Package installation support
|
|
||||||
|
|
||||||
[2.0.2](https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.2) ( Apr 19, 2021 )
|
|
||||||
- [x] Assorted fixes and user experience improvements
|
|
||||||
|
|
||||||
[2.0.1](https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1) ( Feb 27, 2021 )
|
|
||||||
- [x] Complete Static Localization of Admin UI
|
|
||||||
|
|
||||||
[2.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0) ( Nov 11, 2020 )
|
|
||||||
- [x] Migration to .NET 5
|
|
||||||
- [x] Static Localization ( ie. labels, help text, etc.. )
|
|
||||||
- [x] Improved JavaScript Reference Support
|
|
||||||
- [x] Performance Optimizations
|
|
||||||
- [x] Developer Productivity Enhancements
|
|
||||||
|
|
||||||
[1.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.0) ( May 19, 2020 )
|
|
||||||
- [x] Migration to .NET Core 3.2
|
|
||||||
- [x] Multi-Tenant ( Shared Database & Isolated Database )
|
|
||||||
- [x] Modular Architecture
|
|
||||||
- [x] Headless API with Swagger Support
|
|
||||||
- [x] Dynamic Page Compositing Model / Site & Page Management
|
|
||||||
- [x] Authentication / User Management / Profile Management
|
|
||||||
- [x] Authorization / Roles Management / Granular Permissions
|
|
||||||
- [x] Dynamic Routing
|
|
||||||
- [x] Extensibility via Custom Modules
|
|
||||||
- [x] Extensibility via Custom Themes
|
|
||||||
- [x] Event Logging / Audit Trail
|
|
||||||
- [x] Folder / File Management
|
|
||||||
- [x] Recycle Bin
|
|
||||||
- [x] Scheduled Jobs ( Background Processing )
|
|
||||||
- [x] Notifications / Email Delivery
|
|
||||||
- [x] Seamless Upgrade Experience
|
|
||||||
- [x] Progressive Web Application Support
|
|
||||||
- [x] JavaScript Lazy Loading
|
|
||||||
- [x] Dynamic CSS/Lazy Loading
|
|
||||||
|
|
||||||
[POC](https://www.oqtane.org/blog/!/7/announcing-oqtane-a-modular-application-framework-for-blazor) ( May 9, 2019 )
|
|
||||||
- [x] Initial public release on GitHub
|
|
||||||
- [x] .NET Core 3.0
|
|
||||||
|
|
||||||
# Background
|
# Background
|
||||||
Oqtane was created by [Shaun Walker](https://www.linkedin.com/in/shaunbrucewalker/) and is inspired by the DotNetNuke web application framework. Initially created as a proof of concept, Oqtane is a native Blazor application written from the ground up using modern .NET Core technology and a Single Page Application (SPA) architecture. It is a modular application framework offering a fully dynamic page compositing model, multi-site support, designer friendly themes, and extensibility via third party modules.
|
Oqtane was created by [Shaun Walker](https://www.linkedin.com/in/shaunbrucewalker/) and is inspired by the DotNetNuke web application framework. Initially created as a proof of concept, Oqtane is a native Blazor application written from the ground up using modern .NET Core technology and a Single Page Application (SPA) architecture. It is a modular application framework offering a fully dynamic page compositing model, multi-site support, designer friendly themes, and extensibility via third party modules.
|
||||||
|
|
||||||
# Release Announcements
|
|
||||||
|
|
||||||
[Oqtane 5.0](https://www.oqtane.org/blog/!/75/announcing-oqtane-5-0-for-net-8)
|
|
||||||
|
|
||||||
[Oqtane 4.0](https://www.oqtane.org/blog/!/63/announcing-oqtane-4-0-for-net-7)
|
|
||||||
|
|
||||||
[Oqtane 3.4](https://www.oqtane.org/blog/!/56/oqtane-3-4-0-released)
|
|
||||||
|
|
||||||
[Oqtane 3.3](https://www.oqtane.org/blog/!/54/oqtane-3-3-0-released)
|
|
||||||
|
|
||||||
[Oqtane 3.2](https://www.oqtane.org/blog/!/50/oqtane-3-2-for-net-maui-blazor-hybrid)
|
|
||||||
|
|
||||||
[Oqtane 3.1](https://www.oqtane.org/blog/!/41/oqtane-3-1-released)
|
|
||||||
|
|
||||||
[Oqtane 3.0](https://www.oqtane.org/Resources/Blog/PostId/551/announcing-oqtane-30-for-net-6)
|
|
||||||
|
|
||||||
[Oqtane 2.2](https://www.oqtane.org/Resources/Blog/PostId/549/oqtane-22-upgrades-to-bootstrap-5)
|
|
||||||
|
|
||||||
[Oqtane 2.1](https://www.oqtane.org/Resources/Blog/PostId/548/oqtane-21-now-supports-multiple-databases)
|
|
||||||
|
|
||||||
[Oqtane 2.0](https://www.oqtane.org/Resources/Blog/PostId/544/announcing-oqtane-20-for-net-5)
|
|
||||||
|
|
||||||
[Oqtane 1.0](https://www.oqtane.org/Resources/Blog/PostId/540/announcing-oqtane-10-a-modular-application-framework-for-blazor)
|
|
||||||
|
|
||||||
[Oqtane POC](https://www.oqtane.org/Resources/Blog/PostId/520/announcing-oqtane-a-modular-application-framework-for-blazor)
|
|
||||||
|
|
||||||
# Reference Implementations
|
# Reference Implementations
|
||||||
|
|
||||||
[Built On Blazor!](https://builtonblazor.net) - a showcase of sites built on Blazor
|
[Built On Blazor!](https://builtonblazor.net) - a showcase of sites built on Blazor
|
||||||
|
|
Loading…
Reference in New Issue
Block a user