6.1.1 Release
6.1.1 Release
This commit is contained in:
@ -52,6 +52,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
services.AddScoped<IVisitorService, VisitorService>();
|
services.AddScoped<IVisitorService, VisitorService>();
|
||||||
services.AddScoped<ISyncService, SyncService>();
|
services.AddScoped<ISyncService, SyncService>();
|
||||||
services.AddScoped<ILocalizationCookieService, LocalizationCookieService>();
|
services.AddScoped<ILocalizationCookieService, LocalizationCookieService>();
|
||||||
|
services.AddScoped<ICookieConsentService, CookieConsentService>();
|
||||||
|
services.AddScoped<IOutputCacheService, OutputCacheService>();
|
||||||
|
|
||||||
// providers
|
// providers
|
||||||
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
||||||
|
@ -55,10 +55,6 @@ else
|
|||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
await GetJobs();
|
await GetJobs();
|
||||||
if (_jobs.Count == 0)
|
|
||||||
{
|
|
||||||
AddModuleMessage(string.Format(Localizer["Message.NoJobs"], NavigateUrl("admin/system")), MessageType.Warning);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GetJobs()
|
private async Task GetJobs()
|
||||||
|
@ -29,12 +29,12 @@ else
|
|||||||
{
|
{
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<Label Class="control-label" For="username" HelpText="Please enter your Username" ResourceKey="Username">Username:</Label>
|
<Label Class="control-label" For="username" HelpText="Please enter your Username" ResourceKey="Username">Username:</Label>
|
||||||
<input id="username" type="text" @ref="username" class="form-control" placeholder="@Localizer["Username.Placeholder"]" @bind="@_username" required />
|
<input id="username" type="text" @ref="username" class="form-control" placeholder="@Localizer["Username.Placeholder"]" @bind="@_username" @bind:event="oninput" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mt-2">
|
<div class="form-group mt-2">
|
||||||
<Label Class="control-label" For="password" HelpText="Please enter your Password" ResourceKey="Password">Password:</Label>
|
<Label Class="control-label" For="password" HelpText="Please enter your Password" ResourceKey="Password">Password:</Label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="password" type="@_passwordtype" name="Password" class="form-control" placeholder="@Localizer["Password.Placeholder"]" @bind="@_password" required />
|
<input id="password" type="@_passwordtype" name="Password" class="form-control" placeholder="@Localizer["Password.Placeholder"]" @bind="@_password" @bind:event="oninput" required />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -63,24 +63,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this module was installed. This value must be specified within the module's IModule interface specification." ResourceKey="PackageName">Package Name: </Label>
|
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this module was installed. This value must be specified within the module's IModule interface specification." ResourceKey="PackageName">Package Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@if (!string.IsNullOrEmpty(_packagename))
|
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||||
{
|
|
||||||
<div class="input-group">
|
|
||||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
|
||||||
@if (string.IsNullOrEmpty(_packageurl))
|
|
||||||
{
|
|
||||||
<button type="button" class="btn btn-secondary" @onclick="ValidatePackage">@Localizer["Validate"]</button>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<a href="@_packageurl" target="_blank" class="btn btn-primary">@SharedLocalizer["Download"]</a>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -244,7 +227,6 @@
|
|||||||
private string _moduledefinitionname = "";
|
private string _moduledefinitionname = "";
|
||||||
private string _version;
|
private string _version;
|
||||||
private string _packagename = "";
|
private string _packagename = "";
|
||||||
private string _packageurl = "";
|
|
||||||
private string _owner = "";
|
private string _owner = "";
|
||||||
private string _url = "";
|
private string _url = "";
|
||||||
private string _contact = "";
|
private string _contact = "";
|
||||||
@ -445,27 +427,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ValidatePackage()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var package = await PackageService.GetPackageAsync(_packagename, _version, true);
|
|
||||||
if (package == null || string.IsNullOrEmpty(package.PackageUrl))
|
|
||||||
{
|
|
||||||
AddModuleMessage(Localizer["Message.Validate"], MessageType.Warning);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_packageurl = package.PackageUrl;
|
|
||||||
AddModuleMessage(Localizer["Message.Download"], MessageType.Info);
|
|
||||||
}
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packagename, _version);
|
|
||||||
AddModuleMessage(Localizer["Error.Validate"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private string Browse(Page page) => string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
|
private string Browse(Page page) => string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
|
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="name" class="form-control" @bind="@_name" required />
|
<input id="name" class="form-control" @bind="@_name" maxlength="50" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
@ -101,13 +101,13 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
|
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="path" class="form-control" @bind="@_path" />
|
<input id="path" class="form-control" @bind="@_path" maxlength="256" />
|
||||||
</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="url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
|
<Label Class="col-sm-3" For="url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="url" class="form-control" @bind="@_url" />
|
<input id="url" class="form-control" @bind="@_url" maxlength="500" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -147,7 +147,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
|
<Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="title" class="form-control" @bind="@_title" />
|
<input id="title" class="form-control" @bind="@_title" maxlength="200" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -186,13 +186,13 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
|
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3" maxlength="4000"></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="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3"></textarea>
|
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3" maxlength="4000"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -205,13 +205,13 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
|
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3" maxlength="4000"></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="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3"></textarea>
|
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3" maxlength="4000"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject INotificationService NotificationService
|
@inject INotificationService NotificationService
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
@inject IOutputCacheService CacheService
|
||||||
|
|
||||||
@if (_initialized)
|
@if (_initialized)
|
||||||
{
|
{
|
||||||
@ -50,11 +51,12 @@
|
|||||||
</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="sitemap" HelpText="The site map url for this site which can be submitted to search engines for indexing" ResourceKey="SiteMap">Site Map: </Label>
|
<Label Class="col-sm-3" For="sitemap" HelpText="The site map url for this site which can be submitted to search engines for indexing. The sitemap is cached for 5 minutes and the cache can be manually cleared." ResourceKey="SiteMap">Site Map: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="sitemap" class="form-control" @bind="@_sitemap" disabled />
|
<input id="sitemap" class="form-control" @bind="@_sitemap" disabled />
|
||||||
<a href="@_sitemap" class="btn btn-secondary" target="_new">@Localizer["Browse"]</a>
|
<a href="@_sitemap" class="btn btn-secondary" target="_new">@Localizer["Browse"]</a>
|
||||||
|
<button type="button" class="btn btn-danger" @onclick="EvictSitemapOutputCache">@Localizer["SiteMap.EvictCache"]</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -72,20 +74,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
|
<Section Name="Theme" Heading="Theme" ResourceKey="Theme">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<FileManager FileId="@_logofileid" Filter="@_imageFiles" @ref="_logofilemanager" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<FileManager FileId="@_faviconfileid" Filter="ico,png,gif" @ref="_faviconfilemanager" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
|
<Label Class="col-sm-3" For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -126,6 +116,32 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="cookieconsent" HelpText="Specify if cookie consent is enabled on this site. Please note this option must be used in conjunction with a Theme which supports cookie consent." ResourceKey="CookieConsent">Cookie Consent: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="cookieconsent" class="form-select" @bind="@_cookieconsent">
|
||||||
|
<option value="">@SharedLocalizer["Disabled"]</option>
|
||||||
|
<option value="optin">@Localizer["OptIn"]</option>
|
||||||
|
<option value="optout">@Localizer["OptOut"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<FileManager FileId="@_logofileid" Filter="@_imageFiles" @ref="_logofilemanager" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<FileManager FileId="@_faviconfileid" Filter="ico,png,gif" @ref="_faviconfilemanager" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
<Section Name="Functionality" Heading="Functionality" ResourceKey="Functionality">
|
<Section Name="Functionality" Heading="Functionality" ResourceKey="Functionality">
|
||||||
@ -415,6 +431,7 @@
|
|||||||
private string _themetype = "";
|
private string _themetype = "";
|
||||||
private string _containertype = "";
|
private string _containertype = "";
|
||||||
private string _admincontainertype = "";
|
private string _admincontainertype = "";
|
||||||
|
private string _cookieconsent = "";
|
||||||
|
|
||||||
private Dictionary<string, string> _textEditors = new Dictionary<string, string>();
|
private Dictionary<string, string> _textEditors = new Dictionary<string, string>();
|
||||||
private string _textEditor = "";
|
private string _textEditor = "";
|
||||||
@ -505,6 +522,7 @@
|
|||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||||
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
|
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
|
||||||
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
|
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
|
||||||
|
_cookieconsent = SettingService.GetSetting(settings, "CookieConsent", string.Empty);
|
||||||
|
|
||||||
// functionality
|
// functionality
|
||||||
var textEditors = ServiceProvider.GetServices<ITextEditor>();
|
var textEditors = ServiceProvider.GetServices<ITextEditor>();
|
||||||
@ -717,6 +735,9 @@
|
|||||||
settings = SettingService.SetSetting(settings, "SiteGuid", _siteguid, true);
|
settings = SettingService.SetSetting(settings, "SiteGuid", _siteguid, true);
|
||||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention.ToString(), true);
|
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention.ToString(), true);
|
||||||
|
|
||||||
|
//cookie consent
|
||||||
|
settings = SettingService.SetSetting(settings, "CookieConsent", _cookieconsent);
|
||||||
|
|
||||||
// functionality
|
// functionality
|
||||||
settings = SettingService.SetSetting(settings, "TextEditor", _textEditor);
|
settings = SettingService.SetSetting(settings, "TextEditor", _textEditor);
|
||||||
|
|
||||||
@ -913,4 +934,9 @@
|
|||||||
_aliasname = "";
|
_aliasname = "";
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task EvictSitemapOutputCache() {
|
||||||
|
await CacheService.EvictByTag(Constants.SitemapOutputCacheTag);
|
||||||
|
AddModuleMessage(Localizer["Success.SiteMap.CacheEvicted"], MessageType.Success);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,8 +153,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveConfig">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveConfig">@SharedLocalizer["Save"]</button>
|
||||||
<a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Swagger"]</a>
|
|
||||||
<ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" />
|
<ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" />
|
||||||
|
<br /><br />
|
||||||
|
<a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Swagger"]</a>
|
||||||
|
<a class="btn btn-secondary" href="api/endpoint" target="_new">@Localizer["Endpoints"]</a>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Log" Heading="Log" ResourceKey="Log">
|
<TabPanel Name="Log" Heading="Log" ResourceKey="Log">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@ -45,24 +45,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this theme was installed. This value must be specified within the theme's ITheme interface specification." ResourceKey="PackageName">Package Name: </Label>
|
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this theme was installed. This value must be specified within the theme's ITheme interface specification." ResourceKey="PackageName">Package Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@if (!string.IsNullOrEmpty(_packagename))
|
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||||
{
|
|
||||||
<div class="input-group">
|
|
||||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
|
||||||
@if (string.IsNullOrEmpty(_packageurl))
|
|
||||||
{
|
|
||||||
<button type="button" class="btn btn-secondary" @onclick="ValidatePackage">@Localizer["Validate"]</button>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<a href="@_packageurl" target="_blank" class="btn btn-primary">@SharedLocalizer["Download"]</a>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -116,7 +99,6 @@
|
|||||||
private string _name;
|
private string _name;
|
||||||
private string _version;
|
private string _version;
|
||||||
private string _packagename = "";
|
private string _packagename = "";
|
||||||
private string _packageurl = "";
|
|
||||||
private string _owner = "";
|
private string _owner = "";
|
||||||
private string _url = "";
|
private string _url = "";
|
||||||
private string _contact = "";
|
private string _contact = "";
|
||||||
@ -185,27 +167,4 @@
|
|||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ValidatePackage()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var package = await PackageService.GetPackageAsync(_packagename, _version, true);
|
|
||||||
if (package == null || string.IsNullOrEmpty(package.PackageUrl))
|
|
||||||
{
|
|
||||||
AddModuleMessage(Localizer["Message.Validate"], MessageType.Warning);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_packageurl = package.PackageUrl;
|
|
||||||
AddModuleMessage(Localizer["Message.Download"], MessageType.Info);
|
|
||||||
}
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packagename, _version);
|
|
||||||
AddModuleMessage(Localizer["Error.Validate"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IThemeService ThemeService
|
@inject IThemeService ThemeService
|
||||||
@inject IPackageService PackageService
|
@inject IPackageService PackageService
|
||||||
|
@inject ISiteService SiteService
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ else
|
|||||||
|
|
||||||
<Pager Items="@_themes">
|
<Pager Items="@_themes">
|
||||||
<Header>
|
<Header>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
@ -32,10 +34,11 @@ else
|
|||||||
<td><ActionLink Action="Edit" Text="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)
|
||||||
{
|
{
|
||||||
<ActionDialog Header="Delete Theme" Message="@string.Format(Localizer["Confirm.Theme.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" ResourceKey="DeleteTheme" />
|
<ActionDialog Header="Delete Theme" Message="@string.Format(Localizer["Confirm.Theme.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" ResourceKey="DeleteTheme" />
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
|
<td><NavLink class="btn btn-secondary" href="@NavigateUrl("admin/site")">@Localizer["Assign"]</NavLink></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.Version</td>
|
<td>@context.Version</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -17,171 +17,180 @@ else
|
|||||||
{
|
{
|
||||||
<TabStrip>
|
<TabStrip>
|
||||||
<TabPanel Name="Users" Heading="Users" ResourceKey="Users">
|
<TabPanel Name="Users" Heading="Users" ResourceKey="Users">
|
||||||
<ActionLink Action="Add" Text="Add User" Security="SecurityAccessLevel.Edit" ResourceKey="AddUser" />
|
<ActionLink Action="Add" Text="Add User" Security="SecurityAccessLevel.Edit" ResourceKey="AddUser" />
|
||||||
<ActionLink Text="Import Users" Class="btn btn-secondary ms-2" Action="Users" Security="SecurityAccessLevel.Admin" ResourceKey="ImportUsers"/>
|
<ActionLink Text="Import Users" Class="btn btn-secondary ms-2" Action="Users" Security="SecurityAccessLevel.Admin" ResourceKey="ImportUsers"/>
|
||||||
|
|
||||||
<Pager Items="@users" RowClass="align-middle" SearchProperties="User.Username,User.Email,User.DisplayName">
|
<Pager Items="@users" RowClass="align-middle" SearchProperties="User.Username,User.Email,User.DisplayName">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("Username"))">@Localizer["Username"]<i class="@(SetSortIcon("Username"))"></i></th>
|
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("Username"))">@Localizer["Username"]<i class="@(SetSortIcon("Username"))"></i></th>
|
||||||
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("DisplayName"))">@Localizer["Name"]<i class="@(SetSortIcon("DisplayName"))"></i></th>
|
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("DisplayName"))">@Localizer["Name"]<i class="@(SetSortIcon("DisplayName"))"></i></th>
|
||||||
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("Email"))">@Localizer["Email"]<i class="@(SetSortIcon("Email"))"></i></th>
|
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("Email"))">@Localizer["Email"]<i class="@(SetSortIcon("Email"))"></i></th>
|
||||||
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("LastLoginOn"))">@Localizer["LastLoginOn"]<i class="@(SetSortIcon("LastLoginOn"))"></i></th>
|
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("LastLoginOn"))">@Localizer["LastLoginOn"]<i class="@(SetSortIcon("LastLoginOn"))"></i></th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td>
|
<td>
|
||||||
<ActionLink Action="Edit" Text="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 || context.User.IsDeleted)" 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 || context.User.IsDeleted)" ResourceKey="DeleteUser" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ActionLink Action="Roles" Text="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>
|
||||||
<td>@((MarkupString)string.Format("<a href=\"mailto:{0}\">{1}</a>", @context.User.Email, @context.User.Email))</td>
|
<td>@((MarkupString)string.Format("<a href=\"mailto:{0}\">{1}</a>", @context.User.Email, @context.User.Email))</td>
|
||||||
<td>@((context.User.LastLoginOn != DateTime.MinValue) ? string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn) : "")</td>
|
<td>@((context.User.LastLoginOn != DateTime.MinValue) ? string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn) : "")</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings" Security="SecurityAccessLevel.Admin">
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings" Security="SecurityAccessLevel.Admin">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Section Name="User" Heading="User Settings" ResourceKey="UserSettings">
|
<Section Name="User" Heading="User Settings" ResourceKey="UserSettings">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="allowregistration" HelpText="Do you want anonymous visitors to be able to register for an account on the site" ResourceKey="AllowRegistration">Allow User Registration?</Label>
|
<Label Class="col-sm-3" For="allowregistration" HelpText="Do you want anonymous visitors to be able to register for an account on the site" ResourceKey="AllowRegistration">Allow User Registration?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="allowregistration" class="form-select" @bind="@_allowregistration">
|
<select id="allowregistration" class="form-select" @bind="@_allowregistration">
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
<option value="False">@SharedLocalizer["No"]</option>
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (_providertype != "")
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
@if (_providertype != "")
|
||||||
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
|
{
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<select id="allowsitelogin" class="form-select" @bind="@_allowsitelogin">
|
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
|
||||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
<div class="col-sm-9">
|
||||||
<option value="false">@SharedLocalizer["No"]</option>
|
<select id="allowsitelogin" class="form-select" @bind="@_allowsitelogin">
|
||||||
</select>
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
</div>
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
</div>
|
</select>
|
||||||
}
|
</div>
|
||||||
else
|
</div>
|
||||||
{
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
else
|
||||||
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
|
{
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="allowsitelogin" class="form-control" value="@SharedLocalizer["Yes"]" readonly />
|
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="allowsitelogin" class="form-control" value="@SharedLocalizer["Yes"]" readonly />
|
||||||
}
|
</div>
|
||||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
</div>
|
||||||
{
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="twofactor" HelpText="Do you want users to use two factor authentication? Note that you should use the Disabled option until you have successfully verified that the Notification Job in Scheduled Jobs is enabled and your SMTP options in Site Settings are configured or else you will lock yourself out." ResourceKey="TwoFactor">Two Factor?</Label>
|
<Label Class="col-sm-3" For="twofactor" HelpText="Do you want users to use two factor authentication? Note that you should use the Disabled option until you have successfully verified that the Notification Job in Scheduled Jobs is enabled and your SMTP options in Site Settings are configured or else you will lock yourself out." ResourceKey="TwoFactor">Two Factor?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="twofactor" class="form-select" @bind="@_twofactor">
|
<select id="twofactor" class="form-select" @bind="@_twofactor">
|
||||||
<option value="false">@Localizer["Disabled"]</option>
|
<option value="false">@Localizer["Disabled"]</option>
|
||||||
<option value="true">@Localizer["Optional"]</option>
|
<option value="true">@Localizer["Optional"]</option>
|
||||||
<option value="required">@Localizer["Required"]</option>
|
<option value="required">@Localizer["Required"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="cookiename" HelpText="You can choose to use a custom authentication cookie name for each site. However please be aware that if you want to share an authentication cookie between sites on the same domain they need to use a consistent cookie name. Also be aware that changing the authentication cookie name will logout all current users." ResourceKey="CookieName">Cookie Name:</Label>
|
<Label Class="col-sm-3" For="cookiename" HelpText="You can choose to use a custom authentication cookie name for each site. However please be aware that if you want to share an authentication cookie between sites on the same domain they need to use a consistent cookie name. Also be aware that changing the authentication cookie name will logout all current users." ResourceKey="CookieName">Cookie Name:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="cookiename" class="form-control" @bind="@_cookiename" />
|
<input id="cookiename" class="form-control" @bind="@_cookiename" />
|
||||||
</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="cookieexpiration" HelpText="You can choose to use a custom authentication cookie expiration timespan for each site (e.g. '08:00:00' for 8 hours). The default is 14 days if not specified." ResourceKey="CookieExpiration">Cookie Expiration Timespan:</Label>
|
<Label Class="col-sm-3" For="cookieexpiration" HelpText="You can choose to use a custom authentication cookie expiration timespan for each site (e.g. '08:00:00' for 8 hours). The default is 14 days if not specified." ResourceKey="CookieExpiration">Cookie Expiration Timespan:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="cookieexpiration" class="form-control" @bind="@_cookieexpiration" />
|
<input id="cookieexpiration" class="form-control" @bind="@_cookieexpiration" />
|
||||||
</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="alwaysremember" HelpText="Enabling this option will set a permanent cookie in conjunction with the Cookie Expiration Timespan, which will automatically sign in users the next time they visit the site. By default the site will use session cookies." ResourceKey="AlwaysRemember">Always Remember User?</Label>
|
<Label Class="col-sm-3" For="alwaysremember" HelpText="Enabling this option will set a permanent cookie in conjunction with the Cookie Expiration Timespan, which will automatically sign in users the next time they visit the site. By default the site will use session cookies." ResourceKey="AlwaysRemember">Always Remember User?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="alwaysremember" class="form-select" @bind="@_alwaysremember">
|
<select id="alwaysremember" class="form-select" @bind="@_alwaysremember">
|
||||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
<option value="false">@SharedLocalizer["No"]</option>
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
<div class="row mb-1 align-items-center">
|
||||||
</Section>
|
<Label Class="col-sm-3" For="logouteverywhere" HelpText="Do you want users to be logged out of every active session on any device, or only their current session?" ResourceKey="LogoutEverywhere">Logout Everywhere?</Label>
|
||||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
<div class="col-sm-9">
|
||||||
{
|
<select id="logouteverywhere" class="form-select" @bind="@_logouteverywhere">
|
||||||
<Section Name="Password" Heading="Password Settings" ResourceKey="PasswordSettings">
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
<div class="row mb-1 align-items-center">
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
<Label Class="col-sm-3" For="minimumlength" HelpText="The Minimum Length For A Password" ResourceKey="RequiredLength">Minimum Length:</Label>
|
</select>
|
||||||
<div class="col-sm-9">
|
</div>
|
||||||
<input id="minimumlength" class="form-control" @bind="@_minimumlength" required />
|
</div>
|
||||||
</div>
|
}
|
||||||
</div>
|
</Section>
|
||||||
<div class="row mb-1 align-items-center">
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
<Label Class="col-sm-3" For="uniquecharacters" HelpText="The Minimum Number Of Unique Characters Which A Password Must Contain" ResourceKey="UniqueCharacters">Unique Characters:</Label>
|
{
|
||||||
<div class="col-sm-9">
|
<Section Name="Password" Heading="Password Settings" ResourceKey="PasswordSettings">
|
||||||
<input id="uniquecharacters" class="form-control" @bind="@_uniquecharacters" required />
|
<div class="row mb-1 align-items-center">
|
||||||
</div>
|
<Label Class="col-sm-3" For="minimumlength" HelpText="The Minimum Length For A Password" ResourceKey="RequiredLength">Minimum Length:</Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
<div class="row mb-1 align-items-center">
|
<input id="minimumlength" class="form-control" @bind="@_minimumlength" required />
|
||||||
<Label Class="col-sm-3" For="requiredigit" HelpText="Indicate If Passwords Must Contain A Digit" ResourceKey="RequireDigit">Require Digit?</Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
</div>
|
||||||
<select id="requiredigit" class="form-select" @bind="@_requiredigit" required>
|
<div class="row mb-1 align-items-center">
|
||||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
<Label Class="col-sm-3" For="uniquecharacters" HelpText="The Minimum Number Of Unique Characters Which A Password Must Contain" ResourceKey="UniqueCharacters">Unique Characters:</Label>
|
||||||
<option value="false">@SharedLocalizer["No"]</option>
|
<div class="col-sm-9">
|
||||||
</select>
|
<input id="uniquecharacters" class="form-control" @bind="@_uniquecharacters" required />
|
||||||
</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="requireupper" HelpText="Indicate If Passwords Must Contain An Upper Case Character" ResourceKey="RequireUpper">Require Uppercase?</Label>
|
<Label Class="col-sm-3" For="requiredigit" HelpText="Indicate If Passwords Must Contain A Digit" ResourceKey="RequireDigit">Require Digit?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="requireupper" class="form-select" @bind="@_requireupper" required>
|
<select id="requiredigit" class="form-select" @bind="@_requiredigit" required>
|
||||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
<option value="false">@SharedLocalizer["No"]</option>
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="requirelower" HelpText="Indicate If Passwords Must Contain A Lower Case Character" ResourceKey="RequireLower">Require Lowercase?</Label>
|
<Label Class="col-sm-3" For="requireupper" HelpText="Indicate If Passwords Must Contain An Upper Case Character" ResourceKey="RequireUpper">Require Uppercase?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="requirelower" class="form-select" @bind="@_requirelower" required>
|
<select id="requireupper" class="form-select" @bind="@_requireupper" required>
|
||||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
<option value="false">@SharedLocalizer["No"]</option>
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="requirepunctuation" HelpText="Indicate if Passwords Must Contain A Non-alphanumeric Character (ie. Punctuation)" ResourceKey="RequirePunctuation">Require Punctuation?</Label>
|
<Label Class="col-sm-3" For="requirelower" HelpText="Indicate If Passwords Must Contain A Lower Case Character" ResourceKey="RequireLower">Require Lowercase?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="requirepunctuation" class="form-select" @bind="@_requirepunctuation" required>
|
<select id="requirelower" class="form-select" @bind="@_requirelower" required>
|
||||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
<option value="false">@SharedLocalizer["No"]</option>
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
<div class="row mb-1 align-items-center">
|
||||||
<Section Name="Lockout" Heading="Lockout Settings" ResourceKey="LockoutSettings">
|
<Label Class="col-sm-3" For="requirepunctuation" HelpText="Indicate if Passwords Must Contain A Non-alphanumeric Character (ie. Punctuation)" ResourceKey="RequirePunctuation">Require Punctuation?</Label>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="col-sm-9">
|
||||||
<Label Class="col-sm-3" For="maximum" HelpText="The Maximum Number Of Sign In Attempts Before A User Is Locked Out" ResourceKey="MaximumFailures">Maximum Failures:</Label>
|
<select id="requirepunctuation" class="form-select" @bind="@_requirepunctuation" required>
|
||||||
<div class="col-sm-9">
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
<input id="maximum" class="form-control" @bind="@_maximumfailures" required />
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
</div>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="lockoutduration" HelpText="The Number Of Minutes A User Should Be Locked Out" ResourceKey="LockoutDuration">Lockout Duration:</Label>
|
</Section>
|
||||||
<div class="col-sm-9">
|
<Section Name="Lockout" Heading="Lockout Settings" ResourceKey="LockoutSettings">
|
||||||
<input id="lockoutduration" class="form-control" @bind="@_lockoutduration" required />
|
<div class="row mb-1 align-items-center">
|
||||||
</div>
|
<Label Class="col-sm-3" For="maximum" HelpText="The Maximum Number Of Sign In Attempts Before A User Is Locked Out" ResourceKey="MaximumFailures">Maximum Failures:</Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
</Section>
|
<input id="maximum" class="form-control" @bind="@_maximumfailures" required />
|
||||||
<Section Name="ExternalLogin" Heading="External Login Settings" ResourceKey="ExternalLoginSettings">
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="lockoutduration" HelpText="The Number Of Minutes A User Should Be Locked Out" ResourceKey="LockoutDuration">Lockout Duration:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="lockoutduration" class="form-control" @bind="@_lockoutduration" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
<Section Name="ExternalLogin" Heading="External Login Settings" ResourceKey="ExternalLoginSettings">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="provider" HelpText="Select the external login provider" ResourceKey="Provider">Provider:</Label>
|
<Label Class="col-sm-3" For="provider" HelpText="Select the external login provider" ResourceKey="Provider">Provider:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -201,77 +210,77 @@ else
|
|||||||
</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="providertype" HelpText="Select the external login provider type" ResourceKey="ProviderType">Provider Type:</Label>
|
<Label Class="col-sm-3" For="providertype" HelpText="Select the external login provider type" ResourceKey="ProviderType">Provider Type:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="providertype" class="form-select" value="@_providertype" @onchange="(e => ProviderTypeChanged(e))">
|
<select id="providertype" class="form-select" value="@_providertype" @onchange="(e => ProviderTypeChanged(e))">
|
||||||
<option value="" selected><@Localizer["Not Specified"]></option>
|
<option value="" selected><@Localizer["Not Specified"]></option>
|
||||||
<option value="@AuthenticationProviderTypes.OpenIDConnect">@Localizer["OIDC"]</option>
|
<option value="@AuthenticationProviderTypes.OpenIDConnect">@Localizer["OIDC"]</option>
|
||||||
<option value="@AuthenticationProviderTypes.OAuth2">@Localizer["OAuth2"]</option>
|
<option value="@AuthenticationProviderTypes.OAuth2">@Localizer["OAuth2"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (_providertype != "")
|
@if (_providertype != "")
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="providername" HelpText="Specify a friendly name for the external login provider which will be displayed on the Login page" ResourceKey="ProviderName">Provider Name:</Label>
|
<Label Class="col-sm-3" For="providername" HelpText="Specify a friendly name for the external login provider which will be displayed on the Login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="providername" class="form-control" @bind="@_providername" />
|
<input id="providername" class="form-control" @bind="@_providername" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="authority" HelpText="The Authority Url or Issuer Url associated with the OpenID Connect provider" ResourceKey="Authority">Authority:</Label>
|
<Label Class="col-sm-3" For="authority" HelpText="The Authority Url or Issuer Url associated with the OpenID Connect provider" ResourceKey="Authority">Authority:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="authority" class="form-control" @bind="@_authority" />
|
<input id="authority" class="form-control" @bind="@_authority" />
|
||||||
</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="metadataurl" HelpText="The discovery endpoint for obtaining metadata for this provider. Only specify if the OpenID Connect provider does not use the standard approach (ie. /.well-known/openid-configuration)" ResourceKey="MetadataUrl">Metadata Url:</Label>
|
<Label Class="col-sm-3" For="metadataurl" HelpText="The discovery endpoint for obtaining metadata for this provider. Only specify if the OpenID Connect provider does not use the standard approach (ie. /.well-known/openid-configuration)" ResourceKey="MetadataUrl">Metadata Url:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="metadataurl" class="form-control" @bind="@_metadataurl" />
|
<input id="metadataurl" class="form-control" @bind="@_metadataurl" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if (_providertype == AuthenticationProviderTypes.OAuth2)
|
@if (_providertype == AuthenticationProviderTypes.OAuth2)
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="authorizationurl" HelpText="The endpoint for obtaining an Authorization Code" ResourceKey="AuthorizationUrl">Authorization Url:</Label>
|
<Label Class="col-sm-3" For="authorizationurl" HelpText="The endpoint for obtaining an Authorization Code" ResourceKey="AuthorizationUrl">Authorization Url:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="authorizationurl" class="form-control" @bind="@_authorizationurl" />
|
<input id="authorizationurl" class="form-control" @bind="@_authorizationurl" />
|
||||||
</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="tokenurl" HelpText="The endpoint for obtaining an Auth Token" ResourceKey="TokenUrl">Token Url:</Label>
|
<Label Class="col-sm-3" For="tokenurl" HelpText="The endpoint for obtaining an Auth Token" ResourceKey="TokenUrl">Token Url:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="tokenurl" class="form-control" @bind="@_tokenurl" />
|
<input id="tokenurl" class="form-control" @bind="@_tokenurl" />
|
||||||
</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="userinfourl" HelpText="The endpoint for obtaining user information. This should be an API or Page Url which contains the users email address." ResourceKey="UserInfoUrl">User Info Url:</Label>
|
<Label Class="col-sm-3" For="userinfourl" HelpText="The endpoint for obtaining user information. This should be an API or Page Url which contains the users email address." ResourceKey="UserInfoUrl">User Info Url:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="userinfourl" class="form-control" @bind="@_userinfourl" />
|
<input id="userinfourl" class="form-control" @bind="@_userinfourl" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if (_providertype != "")
|
@if (_providertype != "")
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="clientid" HelpText="The Client ID from the provider" ResourceKey="ClientID">Client ID:</Label>
|
<Label Class="col-sm-3" For="clientid" HelpText="The Client ID from the provider" ResourceKey="ClientID">Client ID:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="clientid" class="form-control" @bind="@_clientid" />
|
<input id="clientid" class="form-control" @bind="@_clientid" />
|
||||||
</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="clientsecret" HelpText="The Client Secret from the provider" ResourceKey="ClientSecret">Client Secret:</Label>
|
<Label Class="col-sm-3" For="clientsecret" HelpText="The Client Secret from the provider" ResourceKey="ClientSecret">Client Secret:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="@_clientsecrettype" id="clientsecret" class="form-control" @bind="@_clientsecret" />
|
<input type="@_clientsecrettype" id="clientsecret" class="form-control" @bind="@_clientsecret" />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleClientSecret">@_toggleclientsecret</button>
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleClientSecret">@_toggleclientsecret</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -291,32 +300,32 @@ else
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="scopes" class="form-control" @bind="@_scopes" />
|
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="parameters" HelpText="Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple)." ResourceKey="Parameters">Parameters:</Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="parameters" class="form-control" @bind="@_parameters" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
<Label Class="col-sm-3" For="parameters" HelpText="Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple)." ResourceKey="Parameters">Parameters:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="pkce" class="form-select" @bind="@_pkce" required>
|
<input id="parameters" class="form-control" @bind="@_parameters" />
|
||||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
</div>
|
||||||
<option value="false">@SharedLocalizer["No"]</option>
|
</div>
|
||||||
</select>
|
<div class="row mb-1 align-items-center">
|
||||||
</div>
|
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
||||||
</div>
|
<div class="col-sm-9">
|
||||||
<div class="row mb-1 align-items-center">
|
<select id="pkce" class="form-select" @bind="@_pkce" required>
|
||||||
<Label Class="col-sm-3" For="redirecturl" HelpText="The Redirect Url (or Callback Url) which usually needs to be registered with the provider" ResourceKey="RedirectUrl">Redirect Url:</Label>
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
<div class="col-sm-9">
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="redirecturl" HelpText="The Redirect Url (or Callback Url) which usually needs to be registered with the provider" ResourceKey="RedirectUrl">Redirect Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="reviewclaims" HelpText="This option will record the full list of Claims returned by the Provider in the Event Log. It should only be used for testing purposes. External Login will be restricted when this option is enabled." ResourceKey="ReviewClaims">Review Claims?</Label>
|
<Label Class="col-sm-3" For="reviewclaims" HelpText="This option will record the full list of Claims returned by the Provider in the Event Log. It should only be used for testing purposes. External Login will be restricted when this option is enabled." ResourceKey="ReviewClaims">Review Claims?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -334,10 +343,10 @@ else
|
|||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="Specify the type name of the unique user identifier claim provided by the provider. The default value is 'sub'." ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="Specify the type name of the unique user identifier claim provided by the provider. The default value is 'sub'." ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="identifierclaimtype" class="form-control" @bind="@_identifierclaimtype" />
|
<input id="identifierclaimtype" class="form-control" @bind="@_identifierclaimtype" />
|
||||||
</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="nameclaimtype" HelpText="Optionally specify the type name of the user's name claim provided by the provider. The typical value is 'name'." ResourceKey="NameClaimType">Name Claim:</Label>
|
<Label Class="col-sm-3" For="nameclaimtype" HelpText="Optionally specify the type name of the user's name claim provided by the provider. The typical value is 'name'." ResourceKey="NameClaimType">Name Claim:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -346,16 +355,16 @@ else
|
|||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="emailclaimtype" HelpText="Optionally specify the type name of the email address claim provided by the provider. The typical value is 'email'," ResourceKey="EmailClaimType">Email Claim:</Label>
|
<Label Class="col-sm-3" For="emailclaimtype" HelpText="Optionally specify the type name of the email address claim provided by the provider. The typical value is 'email'," ResourceKey="EmailClaimType">Email Claim:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
||||||
</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="roleclaimtype" HelpText="The name of the roles claim provided by the provider" ResourceKey="RoleClaimType">Roles Claim:</Label>
|
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the roles claim provided by the provider" ResourceKey="RoleClaimType">Roles Claim:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
||||||
</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="roleclaimmappings" HelpText="Optionally provide a comma delimited list of role names provided by the identity provider, as well as mappings to your site roles." ResourceKey="RoleClaimMappings">Role Claim Mappings:</Label>
|
<Label Class="col-sm-3" For="roleclaimmappings" HelpText="Optionally provide a comma delimited list of role names provided by the identity provider, as well as mappings to your site roles." ResourceKey="RoleClaimMappings">Role Claim Mappings:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -374,11 +383,11 @@ else
|
|||||||
</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="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
|
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
|
||||||
</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="savetokens" HelpText="Specify whether access and refresh tokens should be saved after a successful login. The default is false to reduce the size of the authentication cookie." ResourceKey="SaveTokens">Save Tokens?</Label>
|
<Label Class="col-sm-3" For="savetokens" HelpText="Specify whether access and refresh tokens should be saved after a successful login. The default is false to reduce the size of the authentication cookie." ResourceKey="SaveTokens">Save Tokens?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -389,20 +398,20 @@ else
|
|||||||
</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="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="domainfilter" class="form-control" @bind="@_domainfilter" />
|
<input id="domainfilter" class="form-control" @bind="@_domainfilter" />
|
||||||
</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="createusers" HelpText="Do you want new users to be created automatically? If you disable this option, users must already be registered on the site in order to sign in with their external login." ResourceKey="CreateUsers">Create New Users?</Label>
|
<Label Class="col-sm-3" For="createusers" HelpText="Do you want new users to be created automatically? If you disable this option, users must already be registered on the site in order to sign in with their external login." ResourceKey="CreateUsers">Create New Users?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="createusers" class="form-select" @bind="@_createusers">
|
<select id="createusers" class="form-select" @bind="@_createusers">
|
||||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
<option value="false">@SharedLocalizer["No"]</option>
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="verifyusers" HelpText="Do you want existing users to perform an additional email verification step to link their external login? If you disable this option, existing users will be linked automatically." ResourceKey="VerifyUsers">Verify Existing Users?</Label>
|
<Label Class="col-sm-3" For="verifyusers" HelpText="Do you want existing users to perform an additional email verification step to link their external login? If you disable this option, existing users will be linked automatically." ResourceKey="VerifyUsers">Verify Existing Users?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -413,51 +422,51 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</Section>
|
</Section>
|
||||||
<Section Name="Token" Heading="Token Settings" ResourceKey="TokenSettings">
|
<Section Name="Token" Heading="Token Settings" ResourceKey="TokenSettings">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="jwtsecret" HelpText="If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated." ResourceKey="Secret">Secret:</Label>
|
<Label Class="col-sm-3" For="jwtsecret" HelpText="If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated." ResourceKey="Secret">Secret:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="@_secrettype" id="jwtsecret" class="form-control" @bind="@_secret" />
|
<input type="@_secrettype" id="jwtsecret" class="form-control" @bind="@_secret" />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleSecret">@_togglesecret</button>
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleSecret">@_togglesecret</button>
|
||||||
</div>
|
</div>
|
||||||
</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="issuer" HelpText="Optionally provide the issuer of the token" ResourceKey="Issuer">Issuer:</Label>
|
<Label Class="col-sm-3" For="issuer" HelpText="Optionally provide the issuer of the token" ResourceKey="Issuer">Issuer:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="issuer" class="form-control" @bind="@_issuer" />
|
<input id="issuer" class="form-control" @bind="@_issuer" />
|
||||||
</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="audience" HelpText="Optionally provide the audience for the token" ResourceKey="Audience">Audience:</Label>
|
<Label Class="col-sm-3" For="audience" HelpText="Optionally provide the audience for the token" ResourceKey="Audience">Audience:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="audience" class="form-control" @bind="@_audience" />
|
<input id="audience" class="form-control" @bind="@_audience" />
|
||||||
</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="lifetime" HelpText="The number of minutes for which a token should be valid" ResourceKey="Lifetime">Lifetime:</Label>
|
<Label Class="col-sm-3" For="lifetime" HelpText="The number of minutes for which a token should be valid" ResourceKey="Lifetime">Lifetime:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="lifetime" class="form-control" @bind="@_lifetime" />
|
<input id="lifetime" class="form-control" @bind="@_lifetime" />
|
||||||
</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="token" HelpText="Select the Create Token button to generate a long-lived access token (valid for 1 year). Be sure to store this token in a safe location as you will not be able to access it in the future." ResourceKey="Token">Access Token:</Label>
|
<Label Class="col-sm-3" For="token" HelpText="Select the Create Token button to generate a long-lived access token (valid for 1 year). Be sure to store this token in a safe location as you will not be able to access it in the future." ResourceKey="Token">Access Token:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="token" class="form-control" @bind="@_token" />
|
<input id="token" class="form-control" @bind="@_token" />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@CreateToken">@Localizer["CreateToken"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="@CreateToken">@Localizer["CreateToken"]</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
@ -469,6 +478,7 @@ else
|
|||||||
private string _cookiename;
|
private string _cookiename;
|
||||||
private string _cookieexpiration;
|
private string _cookieexpiration;
|
||||||
private string _alwaysremember;
|
private string _alwaysremember;
|
||||||
|
private string _logouteverywhere;
|
||||||
|
|
||||||
private string _minimumlength;
|
private string _minimumlength;
|
||||||
private string _uniquecharacters;
|
private string _uniquecharacters;
|
||||||
@ -529,7 +539,7 @@ else
|
|||||||
await LoadUsersAsync(true);
|
await LoadUsersAsync(true);
|
||||||
|
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
_allowregistration = PageState.Site.AllowRegistration.ToString();
|
_allowregistration = PageState.Site.AllowRegistration.ToString().ToLower();
|
||||||
_allowsitelogin = SettingService.GetSetting(settings, "LoginOptions:AllowSiteLogin", "true");
|
_allowsitelogin = SettingService.GetSetting(settings, "LoginOptions:AllowSiteLogin", "true");
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
@ -538,6 +548,7 @@ else
|
|||||||
_cookiename = SettingService.GetSetting(settings, "LoginOptions:CookieName", ".AspNetCore.Identity.Application");
|
_cookiename = SettingService.GetSetting(settings, "LoginOptions:CookieName", ".AspNetCore.Identity.Application");
|
||||||
_cookieexpiration = SettingService.GetSetting(settings, "LoginOptions:CookieExpiration", "");
|
_cookieexpiration = SettingService.GetSetting(settings, "LoginOptions:CookieExpiration", "");
|
||||||
_alwaysremember = SettingService.GetSetting(settings, "LoginOptions:AlwaysRemember", "false");
|
_alwaysremember = SettingService.GetSetting(settings, "LoginOptions:AlwaysRemember", "false");
|
||||||
|
_logouteverywhere = SettingService.GetSetting(settings, "LoginOptions:LogoutEverywhere", "false");
|
||||||
|
|
||||||
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
|
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
|
||||||
_uniquecharacters = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", "1");
|
_uniquecharacters = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", "1");
|
||||||
@ -656,6 +667,7 @@ else
|
|||||||
settings = SettingService.SetSetting(settings, "LoginOptions:CookieName", _cookiename, true);
|
settings = SettingService.SetSetting(settings, "LoginOptions:CookieName", _cookiename, true);
|
||||||
settings = SettingService.SetSetting(settings, "LoginOptions:CookieExpiration", _cookieexpiration, true);
|
settings = SettingService.SetSetting(settings, "LoginOptions:CookieExpiration", _cookieexpiration, true);
|
||||||
settings = SettingService.SetSetting(settings, "LoginOptions:AlwaysRemember", _alwaysremember, false);
|
settings = SettingService.SetSetting(settings, "LoginOptions:AlwaysRemember", _alwaysremember, false);
|
||||||
|
settings = SettingService.SetSetting(settings, "LoginOptions:LogoutEverywhere", _logouteverywhere, false);
|
||||||
|
|
||||||
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredLength", _minimumlength, true);
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredLength", _minimumlength, true);
|
||||||
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", _uniquecharacters, true);
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", _uniquecharacters, true);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
@namespace Oqtane.Modules.HtmlText
|
@namespace Oqtane.Modules.HtmlText
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject IHtmlTextService HtmlTextService
|
@inject IHtmlTextService HtmlTextService
|
||||||
|
@inject ISettingService SettingService
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
|
|
||||||
@if (PageState.EditMode)
|
@if (PageState.EditMode)
|
||||||
@ -36,6 +37,10 @@
|
|||||||
{
|
{
|
||||||
content = htmltext.Content;
|
content = htmltext.Content;
|
||||||
content = Utilities.FormatContent(content, PageState.Alias, "render");
|
content = Utilities.FormatContent(content, PageState.Alias, "render");
|
||||||
|
if (bool.Parse(SettingService.GetSetting(ModuleState.Settings, "DynamicTokens", "false")))
|
||||||
|
{
|
||||||
|
content = ReplaceTokens(content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,7 @@ namespace Oqtane.Modules.HtmlText
|
|||||||
Version = "1.0.1",
|
Version = "1.0.1",
|
||||||
ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server",
|
ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server",
|
||||||
ReleaseVersions = "1.0.0,1.0.1",
|
ReleaseVersions = "1.0.0,1.0.1",
|
||||||
SettingsType = string.Empty,
|
SettingsType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client",
|
||||||
Resources = new List<Resource>()
|
Resources = new List<Resource>()
|
||||||
{
|
{
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Module.css" }
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Module.css" }
|
||||||
|
55
Oqtane.Client/Modules/HtmlText/Settings.razor
Normal file
55
Oqtane.Client/Modules/HtmlText/Settings.razor
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
@namespace Oqtane.Modules.HtmlText
|
||||||
|
@inherits ModuleBase
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
@implements Oqtane.Interfaces.ISettingsControl
|
||||||
|
@inject IStringLocalizer<Settings> Localizer
|
||||||
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="dynamictokens" ResourceKey="DynamicTokens" ResourceType="@resourceType" HelpText="Do you wish to allow tokens to be dynamically replaced? Please note that this will affect the performance of your site.">Dynamic Tokens? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="dynamictokens" class="form-select" @bind="@_dynamictokens">
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
|
||||||
|
|
||||||
|
private ElementReference form;
|
||||||
|
private bool validated = false;
|
||||||
|
|
||||||
|
private string _dynamictokens;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_dynamictokens = SettingService.GetSetting(ModuleState.Settings, "DynamicTokens", "false");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||||
|
settings = SettingService.SetSetting(settings, "DynamicTokens", _dynamictokens);
|
||||||
|
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ using System.Collections.Generic;
|
|||||||
using Microsoft.JSInterop;
|
using Microsoft.JSInterop;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Dynamic;
|
using System.Dynamic;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Oqtane.Modules
|
namespace Oqtane.Modules
|
||||||
{
|
{
|
||||||
@ -35,7 +36,7 @@ namespace Oqtane.Modules
|
|||||||
protected PageState PageState { get; set; }
|
protected PageState PageState { get; set; }
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
protected Module ModuleState { get; set; }
|
protected Models.Module ModuleState { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderModeBoundary RenderModeBoundary { get; set; }
|
public RenderModeBoundary RenderModeBoundary { get; set; }
|
||||||
@ -413,6 +414,79 @@ namespace Oqtane.Modules
|
|||||||
await interop.ScrollTo(0, 0, "smooth");
|
await interop.ScrollTo(0, 0, "smooth");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string ReplaceTokens(string content)
|
||||||
|
{
|
||||||
|
return ReplaceTokens(content, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ReplaceTokens(string content, object obj)
|
||||||
|
{
|
||||||
|
var tokens = new List<string>();
|
||||||
|
var pos = content.IndexOf("[");
|
||||||
|
if (pos != -1)
|
||||||
|
{
|
||||||
|
if (content.IndexOf("]", pos) != -1)
|
||||||
|
{
|
||||||
|
var token = content.Substring(pos, content.IndexOf("]", pos) - pos + 1);
|
||||||
|
if (token.Contains(":"))
|
||||||
|
{
|
||||||
|
tokens.Add(token.Substring(1, token.Length - 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos = content.IndexOf("[", pos + 1);
|
||||||
|
}
|
||||||
|
if (tokens.Count != 0)
|
||||||
|
{
|
||||||
|
foreach (string token in tokens)
|
||||||
|
{
|
||||||
|
var segments = token.Split(":");
|
||||||
|
if (segments.Length >= 2 && segments.Length <= 3)
|
||||||
|
{
|
||||||
|
var objectName = string.Join(":", segments, 0, segments.Length - 1);
|
||||||
|
var propertyName = segments[segments.Length - 1];
|
||||||
|
var propertyValue = "";
|
||||||
|
|
||||||
|
switch (objectName)
|
||||||
|
{
|
||||||
|
case "ModuleState":
|
||||||
|
propertyValue = ModuleState.GetType().GetProperty(propertyName)?.GetValue(ModuleState, null).ToString();
|
||||||
|
break;
|
||||||
|
case "PageState":
|
||||||
|
propertyValue = PageState.GetType().GetProperty(propertyName)?.GetValue(PageState, null).ToString();
|
||||||
|
break;
|
||||||
|
case "PageState:Alias":
|
||||||
|
propertyValue = PageState.Alias.GetType().GetProperty(propertyName)?.GetValue(PageState.Alias, null).ToString();
|
||||||
|
break;
|
||||||
|
case "PageState:Site":
|
||||||
|
propertyValue = PageState.Site.GetType().GetProperty(propertyName)?.GetValue(PageState.Site, null).ToString();
|
||||||
|
break;
|
||||||
|
case "PageState:Page":
|
||||||
|
propertyValue = PageState.Page.GetType().GetProperty(propertyName)?.GetValue(PageState.Page, null).ToString();
|
||||||
|
break;
|
||||||
|
case "PageState:User":
|
||||||
|
propertyValue = PageState.User?.GetType().GetProperty(propertyName)?.GetValue(PageState.User, null).ToString();
|
||||||
|
break;
|
||||||
|
case "PageState:Route":
|
||||||
|
propertyValue = PageState.Route.GetType().GetProperty(propertyName)?.GetValue(PageState.Route, null).ToString();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (obj != null && obj.GetType().Name == objectName)
|
||||||
|
{
|
||||||
|
propertyValue = obj.GetType().GetProperty(propertyName)?.GetValue(obj, null).ToString();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (propertyValue != null)
|
||||||
|
{
|
||||||
|
content = content.Replace("[" + token + "]", propertyValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
// logging methods
|
// logging methods
|
||||||
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
|
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>6.1.0</Version>
|
<Version>6.1.1</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/v6.1.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</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,10 +22,10 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -180,9 +180,6 @@
|
|||||||
<data name="Once" xml:space="preserve">
|
<data name="Once" xml:space="preserve">
|
||||||
<value>Execute Once</value>
|
<value>Execute Once</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.NoJobs" xml:space="preserve">
|
|
||||||
<value>Please Note That After An Initial Installation You Must <a href={0}>Restart</a> The Application In Order To Activate The Default Scheduled Jobs.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Refresh.Text" xml:space="preserve">
|
<data name="Refresh.Text" xml:space="preserve">
|
||||||
<value>Refresh</value>
|
<value>Refresh</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -228,18 +228,6 @@
|
|||||||
<data name="View License" xml:space="preserve">
|
<data name="View License" xml:space="preserve">
|
||||||
<value>View License</value>
|
<value>View License</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.Validate" xml:space="preserve">
|
|
||||||
<value>Error Validating Package</value>
|
|
||||||
</data>
|
|
||||||
<data name="Message.Download" xml:space="preserve">
|
|
||||||
<value>Package Version Has Been Verified. Please Select The Download Button To Obtain The Package.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Message.Validate" xml:space="preserve">
|
|
||||||
<value>This Package Version Has Not Been Registered In The Oqtane Marketplace Or You Do Not Have The Right To Use It From This Installation</value>
|
|
||||||
</data>
|
|
||||||
<data name="Validate" xml:space="preserve">
|
|
||||||
<value>Validate</value>
|
|
||||||
</data>
|
|
||||||
<data name="Browse" xml:space="preserve">
|
<data name="Browse" xml:space="preserve">
|
||||||
<value>Browse</value>
|
<value>Browse</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -118,7 +118,7 @@
|
|||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="NoCriteria" xml:space="preserve">
|
<data name="NoCriteria" xml:space="preserve">
|
||||||
<value>You Must Provide Some Search Criteria</value>
|
<value>Please Enter Some Search Criteria</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoResult" xml:space="preserve">
|
<data name="NoResult" xml:space="preserve">
|
||||||
<value>No Content Matches The Criteria Provided</value>
|
<value>No Content Matches The Criteria Provided</value>
|
||||||
|
@ -349,7 +349,7 @@
|
|||||||
<value>Relay Configured?</value>
|
<value>Relay Configured?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SiteMap.HelpText" xml:space="preserve">
|
<data name="SiteMap.HelpText" xml:space="preserve">
|
||||||
<value>The site map url for this site which can be submitted to search engines for indexing</value>
|
<value>The site map url for this site which can be submitted to search engines for indexing. The sitemap is cached for 5 minutes and the cache can be manually cleared.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SiteMap.Text" xml:space="preserve">
|
<data name="SiteMap.Text" xml:space="preserve">
|
||||||
<value>Site Map:</value>
|
<value>Site Map:</value>
|
||||||
@ -426,4 +426,25 @@
|
|||||||
<data name="System" xml:space="preserve">
|
<data name="System" xml:space="preserve">
|
||||||
<value>System</value>
|
<value>System</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CookieConsent.HelpText" xml:space="preserve">
|
||||||
|
<value>Specify if cookie consent is enabled on this site. Please note this option must be used in conjunction with a Theme which supports cookie consent.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CookieConsent.Text" xml:space="preserve">
|
||||||
|
<value>Cookie Consent:</value>
|
||||||
|
</data>
|
||||||
|
<data name="OptIn" xml:space="preserve">
|
||||||
|
<value>Opt-In (GDPR)</value>
|
||||||
|
</data>
|
||||||
|
<data name="OptOut" xml:space="preserve">
|
||||||
|
<value>Opt-Out (CCPA)</value>
|
||||||
|
</data>
|
||||||
|
<data name="Theme.Heading" xml:space="preserve">
|
||||||
|
<value>Theme</value>
|
||||||
|
</data>
|
||||||
|
<data name="SiteMap.EvictCache" xml:space="preserve">
|
||||||
|
<value>Clear Cache</value>
|
||||||
|
</data>
|
||||||
|
<data name="Success.SiteMap.CacheEvicted" xml:space="preserve">
|
||||||
|
<value>Site Map Cache Cleared</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -118,7 +118,7 @@
|
|||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="Swagger" xml:space="preserve">
|
<data name="Swagger" xml:space="preserve">
|
||||||
<value>Access Swagger UI</value>
|
<value>Swagger UI</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FrameworkVersion.HelpText" xml:space="preserve">
|
<data name="FrameworkVersion.HelpText" xml:space="preserve">
|
||||||
<value>Framework Version</value>
|
<value>Framework Version</value>
|
||||||
@ -306,4 +306,7 @@
|
|||||||
<data name="CacheControl.HelpText" xml:space="preserve">
|
<data name="CacheControl.HelpText" xml:space="preserve">
|
||||||
<value>Provide a Cache-Control directive for static assets. For example 'public, max-age=60' indicates that static assets should be cached for 60 seconds. A blank value indicates caching is not enabled.</value>
|
<value>Provide a Cache-Control directive for static assets. For example 'public, max-age=60' indicates that static assets should be cached for 60 seconds. A blank value indicates caching is not enabled.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Endpoints" xml:space="preserve">
|
||||||
|
<value>API Endpoints</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -180,16 +180,4 @@
|
|||||||
<data name="View License" xml:space="preserve">
|
<data name="View License" xml:space="preserve">
|
||||||
<value>View License</value>
|
<value>View License</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.Validate" xml:space="preserve">
|
|
||||||
<value>Error Validating Package</value>
|
|
||||||
</data>
|
|
||||||
<data name="Message.Download" xml:space="preserve">
|
|
||||||
<value>Package Version Has Been Verified. Please Select The Download Button To Obtain The Package.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Message.Validate" xml:space="preserve">
|
|
||||||
<value>This Package Version Has Not Been Registered In The Oqtane Marketplace Or You Do Not Have The Right To Use It From This Installation</value>
|
|
||||||
</data>
|
|
||||||
<data name="Validate" xml:space="preserve">
|
|
||||||
<value>Validate</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
</root>
|
@ -146,7 +146,7 @@
|
|||||||
</data>
|
</data>
|
||||||
<data name="InstallTheme.Text" xml:space="preserve">
|
<data name="InstallTheme.Text" xml:space="preserve">
|
||||||
<value>Install Theme</value>
|
<value>Install Theme</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewTheme.Text" xml:space="preserve">
|
<data name="ViewTheme.Text" xml:space="preserve">
|
||||||
<value>View</value>
|
<value>View</value>
|
||||||
</data>
|
</data>
|
||||||
@ -156,4 +156,7 @@
|
|||||||
<data name="Enabled" xml:space="preserve">
|
<data name="Enabled" xml:space="preserve">
|
||||||
<value>Enabled?</value>
|
<value>Enabled?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Assign" xml:space="preserve">
|
||||||
|
<value>Assign</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -507,4 +507,10 @@
|
|||||||
<data name="Error.DeleteUser" xml:space="preserve">
|
<data name="Error.DeleteUser" xml:space="preserve">
|
||||||
<value>Error Deleting User</value>
|
<value>Error Deleting User</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="LogoutEverywhere.Text" xml:space="preserve">
|
||||||
|
<value>Logout Everywhere?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LogoutEverywhere.HelpText" xml:space="preserve">
|
||||||
|
<value>Do you want users to be logged out of every active session on any device, or only their current session?</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
126
Oqtane.Client/Resources/Modules/HtmlText/Settings.resx
Normal file
126
Oqtane.Client/Resources/Modules/HtmlText/Settings.resx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?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="DynamicTokens.HelpText" xml:space="preserve">
|
||||||
|
<value>Do you wish to allow tokens to be dynamically replaced? Please note that this will affect the performance of your site.</value>
|
||||||
|
</data>
|
||||||
|
<data name="DynamicTokens.Text" xml:space="preserve">
|
||||||
|
<value>Dynamic Tokens?</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
129
Oqtane.Client/Resources/Themes/Controls/CookieConsent.resx
Normal file
129
Oqtane.Client/Resources/Themes/Controls/CookieConsent.resx
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<?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="Confirm" xml:space="preserve">
|
||||||
|
<value>Confirm</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConsentNotice" xml:space="preserve">
|
||||||
|
<value>I agree to use cookies to provide the best possible user experience for this site. I understand that I can change these preferences at any time.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Privacy" xml:space="preserve">
|
||||||
|
<value>Privacy</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
47
Oqtane.Client/Services/CookieConsentService.cs
Normal file
47
Oqtane.Client/Services/CookieConsentService.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using Oqtane.Models;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System;
|
||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Oqtane.Services
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="ICookieConsentService" />
|
||||||
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
|
public class CookieConsentService : ServiceBase, ICookieConsentService
|
||||||
|
{
|
||||||
|
public CookieConsentService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
|
private string ApiUrl => CreateApiUrl("CookieConsent");
|
||||||
|
|
||||||
|
public async Task<bool> IsActionedAsync()
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<bool>($"{ApiUrl}/IsActioned");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> CanTrackAsync(bool optOut)
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<bool>($"{ApiUrl}/CanTrack?optout=" + optOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> CreateActionedCookieAsync()
|
||||||
|
{
|
||||||
|
var cookie = await GetStringAsync($"{ApiUrl}/CreateActionedCookie");
|
||||||
|
return cookie ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> CreateConsentCookieAsync()
|
||||||
|
{
|
||||||
|
var cookie = await GetStringAsync($"{ApiUrl}/CreateConsentCookie");
|
||||||
|
return cookie ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> WithdrawConsentCookieAsync()
|
||||||
|
{
|
||||||
|
var cookie = await GetStringAsync($"{ApiUrl}/WithdrawConsentCookie");
|
||||||
|
return cookie ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
Oqtane.Client/Services/Interfaces/ICookieConsentService.cs
Normal file
42
Oqtane.Client/Services/Interfaces/ICookieConsentService.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using Oqtane.Models;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Oqtane.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Service to retrieve cookie consent information.
|
||||||
|
/// </summary>
|
||||||
|
public interface ICookieConsentService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get cookie consent bar actioned status
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<bool> IsActionedAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get cookie consent status
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<bool> CanTrackAsync(bool optOut);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// create actioned cookie
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<string> CreateActionedCookieAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// create consent cookie
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<string> CreateConsentCookieAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// widhdraw consent cookie
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<string> WithdrawConsentCookieAsync();
|
||||||
|
}
|
||||||
|
}
|
18
Oqtane.Client/Services/Interfaces/IOutputCacheService.cs
Normal file
18
Oqtane.Client/Services/Interfaces/IOutputCacheService.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Oqtane.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Service to manage cache
|
||||||
|
/// </summary>
|
||||||
|
public interface IOutputCacheService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Evicts the output cache for a specific tag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task EvictByTag(string tag);
|
||||||
|
}
|
||||||
|
}
|
23
Oqtane.Client/Services/OutputCacheService.cs
Normal file
23
Oqtane.Client/Services/OutputCacheService.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Services
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="IOutputCacheService" />
|
||||||
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
|
public class OutputCacheService : ServiceBase, IOutputCacheService
|
||||||
|
{
|
||||||
|
public OutputCacheService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
|
private string ApiUrl => CreateApiUrl("OutputCache");
|
||||||
|
|
||||||
|
public async Task EvictByTag(string tag)
|
||||||
|
{
|
||||||
|
await DeleteAsync($"{ApiUrl}/{tag}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,6 @@
|
|||||||
@namespace Oqtane.Themes.BlazorTheme
|
@namespace Oqtane.Themes.BlazorTheme
|
||||||
@inherits ThemeBase
|
@inherits ThemeBase
|
||||||
|
|
||||||
|
|
||||||
<div class="breadcrumbs">
|
|
||||||
<Breadcrumbs />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row flex-xl-nowrap gx-0">
|
<div class="row flex-xl-nowrap gx-0">
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<nav class="navbar">
|
<nav class="navbar">
|
||||||
@ -22,13 +17,18 @@
|
|||||||
<Login />
|
<Login />
|
||||||
<ControlPanel LanguageDropdownAlignment="right" />
|
<ControlPanel LanguageDropdownAlignment="right" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
<Breadcrumbs />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row px-4">
|
<div class="row px-4">
|
||||||
<Pane Name="@PaneNames.Admin" />
|
<Pane Name="@PaneNames.Admin" />
|
||||||
|
<CookieConsent />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
@ -573,7 +573,7 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// post to the Logout page to complete the logout process
|
// post to the Logout page to complete the logout process
|
||||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url };
|
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url, everywhere = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:LogoutEverywhere", "false")) };
|
||||||
var interop = new Interop(jsRuntime);
|
var interop = new Interop(jsRuntime);
|
||||||
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
|
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
|
||||||
}
|
}
|
||||||
|
168
Oqtane.Client/Themes/Controls/Theme/CookieConsent.razor
Normal file
168
Oqtane.Client/Themes/Controls/Theme/CookieConsent.razor
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
@namespace Oqtane.Themes.Controls
|
||||||
|
@inherits ThemeControlBase
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
@inject ICookieConsentService CookieConsentService
|
||||||
|
@inject IStringLocalizer<CookieConsent> Localizer
|
||||||
|
|
||||||
|
@if (_enabled && !Hidden)
|
||||||
|
{
|
||||||
|
|
||||||
|
<div class="gdpr-consent-bar bg-light text-dark @(_showBanner ? "p-3" : "p-0") pe-5 fixed-bottom">
|
||||||
|
<form method="post" @formname="CookieConsentForm" @onsubmit="async () => await AcceptPolicy()" data-enhance>
|
||||||
|
@if (_showBanner)
|
||||||
|
{
|
||||||
|
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-9 col-xl-10">
|
||||||
|
@if (PageState.RenderMode == RenderModes.Static)
|
||||||
|
{
|
||||||
|
<input type="checkbox" name="cantrack" checked="@_canTrack" value="1" class="form-check-input me-2" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<input type="checkbox" name="cantrack" @bind="@_canTrack" value="1" class="form-check-input me-2" />
|
||||||
|
}
|
||||||
|
@((MarkupString)Convert.ToString(Localizer["ConsentNotice"]))
|
||||||
|
</div>
|
||||||
|
<div class="col-3 col-xl-2">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 col-xs-6 text-center">
|
||||||
|
<button class="btn btn-primary mb-1 px-0 w-100" type="submit">@((MarkupString)Convert.ToString(Localizer["Confirm"]))</button>
|
||||||
|
</div>
|
||||||
|
@if (ShowPrivacyLink)
|
||||||
|
{
|
||||||
|
<div class="col-md-6 col-xs-6 text-center">
|
||||||
|
<a class="btn btn-secondary mb-1 px-0 w-100" href="/privacy" target="_blank">@((MarkupString)Convert.ToString(Localizer["Privacy"]))</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</form>
|
||||||
|
<form method="post" @formname="CookieConsentToggleForm" @onsubmit="async () => await ToggleBanner()" data-enhance>
|
||||||
|
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||||
|
@if (_showBanner)
|
||||||
|
{
|
||||||
|
<input type="hidden" name="showbanner" value="false" />
|
||||||
|
<button type="submit" class="btn btn-light text-dark btn-sm position-absolute btn-hide">
|
||||||
|
<i class="oi oi-chevron-bottom"></i>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<input type="hidden" name="showbanner" value="true" />
|
||||||
|
<button type="submit" class="btn btn-light text-dark btn-sm position-absolute btn-show">
|
||||||
|
<i class="oi oi-chevron-top"></i>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@code {
|
||||||
|
private bool _showBanner;
|
||||||
|
private bool _enabled;
|
||||||
|
private bool _optout;
|
||||||
|
private bool _actioned;
|
||||||
|
private bool _canTrack;
|
||||||
|
private bool _consentPostback;
|
||||||
|
private bool _togglePostback;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool Hidden { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowPrivacyLink { get; set; } = true;
|
||||||
|
|
||||||
|
[SupplyParameterFromForm(FormName = "CookieConsentToggleForm")]
|
||||||
|
public string ShowBanner
|
||||||
|
{
|
||||||
|
get => "";
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_showBanner = bool.Parse(value);
|
||||||
|
_togglePostback = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupplyParameterFromForm(FormName = "CookieConsentForm")]
|
||||||
|
public string CanTrack
|
||||||
|
{
|
||||||
|
get => "";
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_canTrack = !string.IsNullOrEmpty(value);
|
||||||
|
_consentPostback = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
var cookieConsentSetting = SettingService.GetSetting(PageState.Site.Settings, "CookieConsent", string.Empty);
|
||||||
|
_enabled = !string.IsNullOrEmpty(cookieConsentSetting);
|
||||||
|
_optout = cookieConsentSetting == "optout";
|
||||||
|
_actioned = await CookieConsentService.IsActionedAsync();
|
||||||
|
|
||||||
|
if (!_consentPostback)
|
||||||
|
{
|
||||||
|
_canTrack = await CookieConsentService.CanTrackAsync(_optout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_togglePostback)
|
||||||
|
{
|
||||||
|
_showBanner = !_actioned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AcceptPolicy()
|
||||||
|
{
|
||||||
|
var cookieString = string.Empty;
|
||||||
|
if (_optout)
|
||||||
|
{
|
||||||
|
cookieString = _canTrack ? await CookieConsentService.WithdrawConsentCookieAsync() : await CookieConsentService.CreateConsentCookieAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cookieString = _canTrack ? await CookieConsentService.CreateConsentCookieAsync() : await CookieConsentService.WithdrawConsentCookieAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
//update the page state
|
||||||
|
PageState.AllowCookies = _canTrack;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(cookieString))
|
||||||
|
{
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
await interop.SetCookieString(cookieString);
|
||||||
|
|
||||||
|
_actioned = true;
|
||||||
|
_showBanner = false;
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ToggleBanner()
|
||||||
|
{
|
||||||
|
if (!_actioned)
|
||||||
|
{
|
||||||
|
var cookieString = await CookieConsentService.CreateActionedCookieAsync();
|
||||||
|
if (!string.IsNullOrEmpty(cookieString))
|
||||||
|
{
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
await interop.SetCookieString(cookieString);
|
||||||
|
|
||||||
|
_actioned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PageState.RenderMode == RenderModes.Interactive)
|
||||||
|
{
|
||||||
|
_showBanner = !_showBanner;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@
|
|||||||
<form method="post" class="app-form-inline" action="@logouturl" @formname="LogoutForm">
|
<form method="post" class="app-form-inline" action="@logouturl" @formname="LogoutForm">
|
||||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||||
<input type="hidden" name="returnurl" value="@returnurl" />
|
<input type="hidden" name="returnurl" value="@returnurl" />
|
||||||
|
<input type="hidden" name="everywhere" value="@everywhere" />
|
||||||
<button type="submit" class="@CssClass">@Localizer["Logout"]</button>
|
<button type="submit" class="@CssClass">@Localizer["Logout"]</button>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.JSInterop;
|
using Microsoft.JSInterop;
|
||||||
using Oqtane.Enums;
|
using Oqtane.Enums;
|
||||||
using Oqtane.Models;
|
|
||||||
using Oqtane.Providers;
|
using Oqtane.Providers;
|
||||||
using Oqtane.Security;
|
using Oqtane.Security;
|
||||||
using Oqtane.Services;
|
using Oqtane.Services;
|
||||||
@ -26,6 +25,7 @@ namespace Oqtane.Themes.Controls
|
|||||||
protected string loginurl;
|
protected string loginurl;
|
||||||
protected string logouturl;
|
protected string logouturl;
|
||||||
protected string returnurl;
|
protected string returnurl;
|
||||||
|
protected string everywhere;
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
@ -57,6 +57,7 @@ namespace Oqtane.Themes.Controls
|
|||||||
|
|
||||||
// set logout url
|
// set logout url
|
||||||
logouturl = Utilities.TenantUrl(PageState.Alias, "/pages/logout/");
|
logouturl = Utilities.TenantUrl(PageState.Alias, "/pages/logout/");
|
||||||
|
everywhere = SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:LogoutEverywhere", "false");
|
||||||
|
|
||||||
// verify anonymous users can access current page
|
// verify anonymous users can access current page
|
||||||
if (UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.PermissionList) && Utilities.IsEffectiveAndNotExpired(PageState.Page.EffectiveDate, PageState.Page.ExpiryDate))
|
if (UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.PermissionList) && Utilities.IsEffectiveAndNotExpired(PageState.Page.EffectiveDate, PageState.Page.ExpiryDate))
|
||||||
@ -98,7 +99,7 @@ namespace Oqtane.Themes.Controls
|
|||||||
else // this condition is only valid for legacy Login button inheriting from LoginBase
|
else // this condition is only valid for legacy Login button inheriting from LoginBase
|
||||||
{
|
{
|
||||||
// post to the Logout page to complete the logout process
|
// post to the Logout page to complete the logout process
|
||||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = returnurl };
|
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = returnurl, everywhere = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:LogoutEverywhere", "false")) };
|
||||||
var interop = new Interop(jsRuntime);
|
var interop = new Interop(jsRuntime);
|
||||||
await interop.SubmitForm(logouturl, fields);
|
await interop.SubmitForm(logouturl, fields);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Pane Name="Top Full Width" />
|
<Pane Name="Top Full Width" />
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
@ -107,13 +107,14 @@
|
|||||||
{
|
{
|
||||||
<Pane Name="Footer" />
|
<Pane Name="Footer" />
|
||||||
}
|
}
|
||||||
</div>
|
<CookieConsent />
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public override string Name => "Default Theme";
|
public override string Name => "Default Theme";
|
||||||
|
|
||||||
public override string Panes => PaneNames.Default + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width,Footer";
|
public override string Panes => PaneNames.Default + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width,Footer";
|
||||||
|
|
||||||
private bool _login = true;
|
private bool _login = true;
|
||||||
private bool _register = true;
|
private bool _register = true;
|
||||||
|
@ -13,9 +13,9 @@
|
|||||||
<select id="scope" class="form-select" value="@_scope" @onchange="(e => ScopeChanged(e))">
|
<select id="scope" class="form-select" value="@_scope" @onchange="(e => ScopeChanged(e))">
|
||||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
{
|
{
|
||||||
<option value="site">@Localizer["Site"]</option>
|
<option value="site">@Localizer["Site"]</option>
|
||||||
}
|
}
|
||||||
<option value="page">@Localizer["Page"]</option>
|
<option value="page">@Localizer["Page"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -92,7 +92,7 @@
|
|||||||
settings = SettingService.MergeSettings(PageState.Site.Settings, settings);
|
settings = SettingService.MergeSettings(PageState.Site.Settings, settings);
|
||||||
_login = SettingService.GetSetting(settings, GetType().Namespace + ":Login", "-");
|
_login = SettingService.GetSetting(settings, GetType().Namespace + ":Login", "-");
|
||||||
_register = SettingService.GetSetting(settings, GetType().Namespace + ":Register", "-");
|
_register = SettingService.GetSetting(settings, GetType().Namespace + ":Register", "-");
|
||||||
_footer = SettingService.GetSetting(settings, GetType().Namespace + ":Footer", "-");
|
_footer = SettingService.GetSetting(settings, GetType().Namespace + ":Footer", "-");
|
||||||
}
|
}
|
||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,19 @@ namespace Oqtane.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task SetCookieString(string cookieString)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_jsRuntime.InvokeVoidAsync("Oqtane.Interop.setCookieString", cookieString);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ValueTask<string> GetCookie(string name)
|
public ValueTask<string> GetCookie(string name)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -27,6 +27,7 @@ namespace Oqtane.UI
|
|||||||
public bool IsInternalNavigation { get; set; }
|
public bool IsInternalNavigation { get; set; }
|
||||||
public Guid RenderId { get; set; }
|
public Guid RenderId { get; set; }
|
||||||
public bool Refresh { get; set; }
|
public bool Refresh { get; set; }
|
||||||
|
public bool AllowCookies { get; set; }
|
||||||
|
|
||||||
public List<Page> Pages
|
public List<Page> Pages
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
@inject IUrlMappingService UrlMappingService
|
@inject IUrlMappingService UrlMappingService
|
||||||
@inject ILogService LogService
|
@inject ILogService LogService
|
||||||
@inject ISettingService SettingService
|
@inject ISettingService SettingService
|
||||||
|
@inject ICookieConsentService CookieConsentService
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
@implements IHandleAfterRender
|
@implements IHandleAfterRender
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
@ -293,6 +294,14 @@
|
|||||||
// load additional metadata for modules
|
// load additional metadata for modules
|
||||||
(page, modules) = ProcessModules(site, page, modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType, SiteState.Alias);
|
(page, modules) = ProcessModules(site, page, modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType, SiteState.Alias);
|
||||||
|
|
||||||
|
//cookie consent
|
||||||
|
var _allowCookies = PageState?.AllowCookies;
|
||||||
|
if(!_allowCookies.HasValue)
|
||||||
|
{
|
||||||
|
var cookieConsentSettings = SettingService.GetSetting(site.Settings, "CookieConsent", string.Empty);
|
||||||
|
_allowCookies = string.IsNullOrEmpty(cookieConsentSettings) || await CookieConsentService.CanTrackAsync(cookieConsentSettings == "optout");
|
||||||
|
}
|
||||||
|
|
||||||
// populate page state (which acts as a client-side cache for subsequent requests)
|
// populate page state (which acts as a client-side cache for subsequent requests)
|
||||||
_pagestate = new PageState
|
_pagestate = new PageState
|
||||||
{
|
{
|
||||||
@ -316,7 +325,8 @@
|
|||||||
ReturnUrl = returnurl,
|
ReturnUrl = returnurl,
|
||||||
IsInternalNavigation = _isInternalNavigation,
|
IsInternalNavigation = _isInternalNavigation,
|
||||||
RenderId = Guid.NewGuid(),
|
RenderId = Guid.NewGuid(),
|
||||||
Refresh = false
|
Refresh = false,
|
||||||
|
AllowCookies = _allowCookies.GetValueOrDefault(true)
|
||||||
};
|
};
|
||||||
OnStateChange?.Invoke(_pagestate);
|
OnStateChange?.Invoke(_pagestate);
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Version>6.1.0</Version>
|
<Version>6.1.1</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/v6.1.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</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,7 +34,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MySql.Data" Version="9.2.0" />
|
<PackageReference Include="MySql.Data" Version="9.2.0" />
|
||||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-preview.2.efcore.9.0.0" />
|
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-preview.3.efcore.9.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Version>6.1.0</Version>
|
<Version>6.1.1</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/v6.1.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</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="9.0.0" />
|
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.3" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Version>6.1.0</Version>
|
<Version>6.1.1</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/v6.1.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</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="9.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Version>6.1.0</Version>
|
<Version>6.1.1</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/v6.1.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</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="9.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<!-- <TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks> -->
|
<!-- <TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks> -->
|
||||||
<!-- <TargetFrameworks>$(TargetFrameworks);net9.0-tizen</TargetFrameworks> -->
|
<!-- <TargetFrameworks>$(TargetFrameworks);net9.0-tizen</TargetFrameworks> -->
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Version>6.1.0</Version>
|
<Version>6.1.1</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/v6.1.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</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>
|
||||||
@ -30,7 +30,7 @@
|
|||||||
<ApplicationId>com.oqtane.maui</ApplicationId>
|
<ApplicationId>com.oqtane.maui</ApplicationId>
|
||||||
|
|
||||||
<!-- Versions -->
|
<!-- Versions -->
|
||||||
<ApplicationDisplayVersion>6.1.0</ApplicationDisplayVersion>
|
<ApplicationDisplayVersion>6.1.1</ApplicationDisplayVersion>
|
||||||
<ApplicationVersion>1</ApplicationVersion>
|
<ApplicationVersion>1</ApplicationVersion>
|
||||||
|
|
||||||
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
|
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
|
||||||
@ -67,14 +67,14 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
|
||||||
<PackageReference Include="System.Net.Http.Json" Version="9.0.1" />
|
<PackageReference Include="System.Net.Http.Json" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.30" />
|
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.40" />
|
||||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.30" />
|
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.40" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.30" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.40" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -14,6 +14,9 @@ Oqtane.Interop = {
|
|||||||
}
|
}
|
||||||
document.cookie = cookieString;
|
document.cookie = cookieString;
|
||||||
},
|
},
|
||||||
|
setCookieString: function (cookieString) {
|
||||||
|
document.cookie = cookieString;
|
||||||
|
},
|
||||||
getCookie: function (name) {
|
getCookie: function (name) {
|
||||||
name = name + "=";
|
name = name + "=";
|
||||||
var decodedCookie = decodeURIComponent(document.cookie);
|
var decodedCookie = decodeURIComponent(document.cookie);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Client</id>
|
<id>Oqtane.Client</id>
|
||||||
<version>6.1.0</version>
|
<version>6.1.1</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/v6.1.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
|
||||||
<readme>readme.md</readme>
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Framework</id>
|
<id>Oqtane.Framework</id>
|
||||||
<version>6.1.0</version>
|
<version>6.1.1</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/v6.1.0/Oqtane.Framework.6.1.0.Upgrade.zip</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v6.1.1/Oqtane.Framework.6.1.1.Upgrade.zip</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
|
||||||
<readme>readme.md</readme>
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane framework</tags>
|
<tags>oqtane framework</tags>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Server</id>
|
<id>Oqtane.Server</id>
|
||||||
<version>6.1.0</version>
|
<version>6.1.1</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/v6.1.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
|
||||||
<readme>readme.md</readme>
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Shared</id>
|
<id>Oqtane.Shared</id>
|
||||||
<version>6.1.0</version>
|
<version>6.1.1</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/v6.1.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
|
||||||
<readme>readme.md</readme>
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Updater</id>
|
<id>Oqtane.Updater</id>
|
||||||
<version>6.1.0</version>
|
<version>6.1.1</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/v6.1.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
|
||||||
<readme>readme.md</readme>
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
|
@ -1 +1 @@
|
|||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.0.Install.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.1.Install.zip" -Force
|
||||||
|
@ -1 +1 @@
|
|||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.0.Upgrade.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.1.Upgrade.zip" -Force
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
@inject IUrlMappingRepository UrlMappingRepository
|
@inject IUrlMappingRepository UrlMappingRepository
|
||||||
@inject IVisitorRepository VisitorRepository
|
@inject IVisitorRepository VisitorRepository
|
||||||
@inject IJwtManager JwtManager
|
@inject IJwtManager JwtManager
|
||||||
|
@inject ICookieConsentService CookieConsentService
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
|
||||||
@if (_initialized)
|
@if (_initialized)
|
||||||
{
|
{
|
||||||
@ -107,6 +109,7 @@
|
|||||||
private string _styleSheets = "";
|
private string _styleSheets = "";
|
||||||
private string _scripts = "";
|
private string _scripts = "";
|
||||||
private string _message = "";
|
private string _message = "";
|
||||||
|
private bool _allowCookies;
|
||||||
private PageState _pageState;
|
private PageState _pageState;
|
||||||
|
|
||||||
// CascadingParameter is required to access HttpContext
|
// CascadingParameter is required to access HttpContext
|
||||||
@ -140,6 +143,9 @@
|
|||||||
_prerender = site.Prerender;
|
_prerender = site.Prerender;
|
||||||
_fingerprint = site.Fingerprint;
|
_fingerprint = site.Fingerprint;
|
||||||
|
|
||||||
|
var cookieConsentSettings = SettingService.GetSetting(site.Settings, "CookieConsent", string.Empty);
|
||||||
|
_allowCookies = string.IsNullOrEmpty(cookieConsentSettings) || await CookieConsentService.CanTrackAsync(cookieConsentSettings == "optout");
|
||||||
|
|
||||||
var modules = new List<Module>();
|
var modules = new List<Module>();
|
||||||
|
|
||||||
Route route = new Route(url, alias.Path);
|
Route route = new Route(url, alias.Path);
|
||||||
@ -170,7 +176,7 @@
|
|||||||
modules = await SiteService.GetModulesAsync(site.SiteId, page.PageId);
|
modules = await SiteService.GetModulesAsync(site.SiteId, page.PageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (site.VisitorTracking)
|
if (site.VisitorTracking && _allowCookies)
|
||||||
{
|
{
|
||||||
TrackVisitor(site.SiteId);
|
TrackVisitor(site.SiteId);
|
||||||
}
|
}
|
||||||
@ -245,7 +251,8 @@
|
|||||||
ReturnUrl = "",
|
ReturnUrl = "",
|
||||||
IsInternalNavigation = false,
|
IsInternalNavigation = false,
|
||||||
RenderId = Guid.NewGuid(),
|
RenderId = Guid.NewGuid(),
|
||||||
Refresh = true
|
Refresh = true,
|
||||||
|
AllowCookies = _allowCookies
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
52
Oqtane.Server/Controllers/CookieConsentController.cs
Normal file
52
Oqtane.Server/Controllers/CookieConsentController.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Oqtane.Infrastructure;
|
||||||
|
using Oqtane.Services;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Oqtane.Controllers
|
||||||
|
{
|
||||||
|
[Route(ControllerRoutes.ApiRoute)]
|
||||||
|
public class CookieConsentController : Controller
|
||||||
|
{
|
||||||
|
private readonly ICookieConsentService _cookieConsentService;
|
||||||
|
|
||||||
|
public CookieConsentController(ICookieConsentService cookieConsentService)
|
||||||
|
{
|
||||||
|
_cookieConsentService = cookieConsentService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("IsActioned")]
|
||||||
|
public async Task<bool> IsActioned()
|
||||||
|
{
|
||||||
|
return await _cookieConsentService.IsActionedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("CanTrack")]
|
||||||
|
public async Task<bool> CanTrack(string optout)
|
||||||
|
{
|
||||||
|
return await _cookieConsentService.CanTrackAsync(bool.Parse(optout));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("CreateActionedCookie")]
|
||||||
|
public async Task<string> CreateActionedCookie()
|
||||||
|
{
|
||||||
|
return await _cookieConsentService.CreateActionedCookieAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("CreateConsentCookie")]
|
||||||
|
public async Task<string> CreateConsentCookie()
|
||||||
|
{
|
||||||
|
return await _cookieConsentService.CreateConsentCookieAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("WithdrawConsentCookie")]
|
||||||
|
public async Task<string> WithdrawConsentCookie()
|
||||||
|
{
|
||||||
|
return await _cookieConsentService.WithdrawConsentCookieAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
Oqtane.Server/Controllers/EndpointController.cs
Normal file
56
Oqtane.Server/Controllers/EndpointController.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
||||||
|
namespace Oqtane.Controllers
|
||||||
|
{
|
||||||
|
[Route(ControllerRoutes.ApiRoute)]
|
||||||
|
public class EndpointController : Controller
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<EndpointDataSource> _endpointSources;
|
||||||
|
|
||||||
|
public EndpointController(IEnumerable<EndpointDataSource> endpointSources)
|
||||||
|
{
|
||||||
|
_endpointSources = endpointSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET api/<controller>
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize(Roles = RoleNames.Host)]
|
||||||
|
public ActionResult Get()
|
||||||
|
{
|
||||||
|
var endpoints = _endpointSources
|
||||||
|
.SelectMany(item => item.Endpoints)
|
||||||
|
.OfType<RouteEndpoint>();
|
||||||
|
|
||||||
|
var output = endpoints.Select(
|
||||||
|
item =>
|
||||||
|
{
|
||||||
|
var controller = item.Metadata
|
||||||
|
.OfType<ControllerActionDescriptor>()
|
||||||
|
.FirstOrDefault();
|
||||||
|
var action = controller != null
|
||||||
|
? $"{controller.ControllerName}.{controller.ActionName}"
|
||||||
|
: null;
|
||||||
|
var controllerMethod = controller != null
|
||||||
|
? $"{controller.ControllerTypeInfo.FullName}:{controller.MethodInfo.Name}"
|
||||||
|
: null;
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
Method = item.Metadata.OfType<HttpMethodMetadata>().FirstOrDefault()?.HttpMethods?[0],
|
||||||
|
Route = $"/{item.RoutePattern.RawText.TrimStart('/')}",
|
||||||
|
Action = action,
|
||||||
|
ControllerMethod = controllerMethod
|
||||||
|
};
|
||||||
|
}
|
||||||
|
).OrderBy(item => item.Route);
|
||||||
|
|
||||||
|
return Json(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -171,7 +171,8 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return assemblyList;
|
return assemblyList;
|
||||||
});
|
}).ToList();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET api/<controller>/load?list=x,y
|
// GET api/<controller>/load?list=x,y
|
||||||
|
30
Oqtane.Server/Controllers/OutputCacheController.cs
Normal file
30
Oqtane.Server/Controllers/OutputCacheController.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Services;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Controllers
|
||||||
|
{
|
||||||
|
[Route(ControllerRoutes.ApiRoute)]
|
||||||
|
public class OutputCacheController : Controller
|
||||||
|
{
|
||||||
|
private readonly IOutputCacheService _cacheService;
|
||||||
|
|
||||||
|
public OutputCacheController(IOutputCacheService cacheService)
|
||||||
|
{
|
||||||
|
_cacheService = cacheService;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE api/<controller>/{tag}
|
||||||
|
[HttpDelete("{tag}")]
|
||||||
|
[Authorize(Roles = RoleNames.Admin)]
|
||||||
|
public async Task EvictByTag(string tag)
|
||||||
|
{
|
||||||
|
await _cacheService.EvictByTag(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -34,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.ParseExact(fromdate, "yyyy-MM-dd", CultureInfo.InvariantCulture));
|
return _visitors.GetVisitors(SiteId, DateTime.ParseExact(fromdate, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -34,6 +34,14 @@ namespace Oqtane.Extensions
|
|||||||
options.SetDefaultCulture(defaultCulture)
|
options.SetDefaultCulture(defaultCulture)
|
||||||
.AddSupportedCultures(supportedCultures)
|
.AddSupportedCultures(supportedCultures)
|
||||||
.AddSupportedUICultures(supportedCultures);
|
.AddSupportedUICultures(supportedCultures);
|
||||||
|
|
||||||
|
foreach(var culture in options.SupportedCultures)
|
||||||
|
{
|
||||||
|
if (culture.TextInfo.IsRightToLeft)
|
||||||
|
{
|
||||||
|
RightToLeftCulture.ResolveFormat(culture);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
@ -47,6 +55,5 @@ namespace Oqtane.Extensions
|
|||||||
|
|
||||||
public static IApplicationBuilder UseExceptionMiddleWare(this IApplicationBuilder builder)
|
public static IApplicationBuilder UseExceptionMiddleWare(this IApplicationBuilder builder)
|
||||||
=> builder.UseMiddleware<ExceptionMiddleware>();
|
=> builder.UseMiddleware<ExceptionMiddleware>();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
services.AddScoped<ISearchService, SearchService>();
|
services.AddScoped<ISearchService, SearchService>();
|
||||||
services.AddScoped<ISearchProvider, DatabaseSearchProvider>();
|
services.AddScoped<ISearchProvider, DatabaseSearchProvider>();
|
||||||
services.AddScoped<IImageService, ImageService>();
|
services.AddScoped<IImageService, ImageService>();
|
||||||
|
services.AddScoped<ICookieConsentService, ServerCookieConsentService>();
|
||||||
|
|
||||||
// providers
|
// providers
|
||||||
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
||||||
@ -116,6 +117,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
// services
|
// services
|
||||||
services.AddTransient<ISiteService, ServerSiteService>();
|
services.AddTransient<ISiteService, ServerSiteService>();
|
||||||
services.AddTransient<ILocalizationCookieService, ServerLocalizationCookieService>();
|
services.AddTransient<ILocalizationCookieService, ServerLocalizationCookieService>();
|
||||||
|
services.AddTransient<IOutputCacheService, ServerOutputCacheService>();
|
||||||
|
|
||||||
// repositories
|
// repositories
|
||||||
services.AddTransient<IModuleDefinitionRepository, ModuleDefinitionRepository>();
|
services.AddTransient<IModuleDefinitionRepository, ModuleDefinitionRepository>();
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@ -45,110 +46,140 @@ namespace Oqtane.Infrastructure
|
|||||||
|
|
||||||
protected async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
await Task.Yield(); // required so that this method does not block startup
|
|
||||||
|
|
||||||
while (!stoppingToken.IsCancellationRequested)
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
using (var scope = _serviceScopeFactory.CreateScope())
|
using (var scope = _serviceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
|
IConfigurationRoot _config = scope.ServiceProvider.GetRequiredService<IConfigurationRoot>();
|
||||||
ILogger<HostedServiceBase> _filelogger = scope.ServiceProvider.GetRequiredService<ILogger<HostedServiceBase>>();
|
ILogger<HostedServiceBase> _filelogger = scope.ServiceProvider.GetRequiredService<ILogger<HostedServiceBase>>();
|
||||||
|
|
||||||
try
|
// if framework is installed
|
||||||
|
if (IsInstalled(_config))
|
||||||
{
|
{
|
||||||
var jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
try
|
||||||
var jobLogs = scope.ServiceProvider.GetRequiredService<IJobLogRepository>();
|
|
||||||
var tenantRepository = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
|
|
||||||
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
|
||||||
|
|
||||||
// get name of job
|
|
||||||
string jobType = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
|
||||||
|
|
||||||
// load jobs and find current job
|
|
||||||
Job job = jobs.GetJobs().Where(item => item.JobType == jobType).FirstOrDefault();
|
|
||||||
if (job != null && job.IsEnabled && !job.IsExecuting)
|
|
||||||
{
|
{
|
||||||
// get next execution date
|
var jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||||
DateTime NextExecution;
|
|
||||||
if (job.NextExecution == null)
|
// get name of job
|
||||||
|
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||||
|
|
||||||
|
// load jobs and find current job
|
||||||
|
Job job = jobs.GetJobs().Where(item => item.JobType == jobTypeName).FirstOrDefault();
|
||||||
|
|
||||||
|
if (job == null)
|
||||||
{
|
{
|
||||||
if (job.StartDate != null)
|
// auto registration
|
||||||
|
job = new Job { JobType = jobTypeName };
|
||||||
|
|
||||||
|
// optional HostedServiceBase properties
|
||||||
|
var jobType = Type.GetType(jobTypeName);
|
||||||
|
var jobObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, jobType) as HostedServiceBase;
|
||||||
|
if (jobObject.Name != "")
|
||||||
{
|
{
|
||||||
NextExecution = job.StartDate.Value;
|
job.Name = jobObject.Name;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NextExecution = DateTime.UtcNow;
|
job.Name = Utilities.GetTypeName(job.JobType);
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NextExecution = job.NextExecution.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine if the job should be run
|
|
||||||
if (NextExecution <= DateTime.UtcNow && (job.EndDate == null || job.EndDate >= DateTime.UtcNow))
|
|
||||||
{
|
|
||||||
// update the job to indicate it is running
|
|
||||||
job.IsExecuting = true;
|
|
||||||
jobs.UpdateJob(job);
|
|
||||||
|
|
||||||
// create a job log entry
|
|
||||||
JobLog log = new JobLog();
|
|
||||||
log.JobId = job.JobId;
|
|
||||||
log.StartDate = DateTime.UtcNow;
|
|
||||||
log.FinishDate = null;
|
|
||||||
log.Succeeded = false;
|
|
||||||
log.Notes = "";
|
|
||||||
log = jobLogs.AddJobLog(log);
|
|
||||||
|
|
||||||
// execute the job
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var notes = "";
|
|
||||||
foreach (var tenant in tenantRepository.GetTenants())
|
|
||||||
{
|
|
||||||
// set tenant and execute job
|
|
||||||
tenantManager.SetTenant(tenant.TenantId);
|
|
||||||
notes += ExecuteJob(scope.ServiceProvider);
|
|
||||||
notes += await ExecuteJobAsync(scope.ServiceProvider);
|
|
||||||
}
|
|
||||||
log.Notes = notes;
|
|
||||||
log.Succeeded = true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
log.Notes = ex.Message;
|
|
||||||
log.Succeeded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the job log
|
|
||||||
log.FinishDate = DateTime.UtcNow;
|
|
||||||
jobLogs.UpdateJobLog(log);
|
|
||||||
|
|
||||||
// update the job
|
|
||||||
job.NextExecution = CalculateNextExecution(NextExecution, job);
|
|
||||||
if (job.Frequency == "O") // one time
|
|
||||||
{
|
|
||||||
job.EndDate = DateTime.UtcNow;
|
|
||||||
job.NextExecution = null;
|
|
||||||
}
|
}
|
||||||
|
job.Frequency = jobObject.Frequency;
|
||||||
|
job.Interval = jobObject.Interval;
|
||||||
|
job.StartDate = jobObject.StartDate;
|
||||||
|
job.EndDate = jobObject.EndDate;
|
||||||
|
job.RetentionHistory = jobObject.RetentionHistory;
|
||||||
|
job.IsEnabled = jobObject.IsEnabled;
|
||||||
|
job.IsStarted = true;
|
||||||
job.IsExecuting = false;
|
job.IsExecuting = false;
|
||||||
jobs.UpdateJob(job);
|
job.NextExecution = null;
|
||||||
|
|
||||||
// trim the job log
|
job = jobs.AddJob(job);
|
||||||
List<JobLog> logs = jobLogs.GetJobLogs().Where(item => item.JobId == job.JobId)
|
}
|
||||||
.OrderByDescending(item => item.JobLogId).ToList();
|
|
||||||
for (int i = logs.Count; i > job.RetentionHistory; i--)
|
if (job != null && job.IsEnabled && !job.IsExecuting)
|
||||||
|
{
|
||||||
|
var jobLogs = scope.ServiceProvider.GetRequiredService<IJobLogRepository>();
|
||||||
|
var tenantRepository = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
|
||||||
|
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
||||||
|
|
||||||
|
// get next execution date
|
||||||
|
DateTime NextExecution;
|
||||||
|
if (job.NextExecution == null)
|
||||||
{
|
{
|
||||||
jobLogs.DeleteJobLog(logs[i - 1].JobLogId);
|
if (job.StartDate != null)
|
||||||
|
{
|
||||||
|
NextExecution = job.StartDate.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NextExecution = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NextExecution = job.NextExecution.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine if the job should be run
|
||||||
|
if (NextExecution <= DateTime.UtcNow && (job.EndDate == null || job.EndDate >= DateTime.UtcNow))
|
||||||
|
{
|
||||||
|
// update the job to indicate it is running
|
||||||
|
job.IsExecuting = true;
|
||||||
|
jobs.UpdateJob(job);
|
||||||
|
|
||||||
|
// create a job log entry
|
||||||
|
JobLog log = new JobLog();
|
||||||
|
log.JobId = job.JobId;
|
||||||
|
log.StartDate = DateTime.UtcNow;
|
||||||
|
log.FinishDate = null;
|
||||||
|
log.Succeeded = false;
|
||||||
|
log.Notes = "";
|
||||||
|
log = jobLogs.AddJobLog(log);
|
||||||
|
|
||||||
|
// execute the job
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var notes = "";
|
||||||
|
foreach (var tenant in tenantRepository.GetTenants())
|
||||||
|
{
|
||||||
|
// set tenant and execute job
|
||||||
|
tenantManager.SetTenant(tenant.TenantId);
|
||||||
|
notes += ExecuteJob(scope.ServiceProvider);
|
||||||
|
notes += await ExecuteJobAsync(scope.ServiceProvider);
|
||||||
|
}
|
||||||
|
log.Notes = notes;
|
||||||
|
log.Succeeded = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.Notes = ex.Message;
|
||||||
|
log.Succeeded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the job log
|
||||||
|
log.FinishDate = DateTime.UtcNow;
|
||||||
|
jobLogs.UpdateJobLog(log);
|
||||||
|
|
||||||
|
// update the job
|
||||||
|
job.NextExecution = CalculateNextExecution(NextExecution, job);
|
||||||
|
if (job.Frequency == "O") // one time
|
||||||
|
{
|
||||||
|
job.EndDate = DateTime.UtcNow;
|
||||||
|
job.NextExecution = null;
|
||||||
|
}
|
||||||
|
job.IsExecuting = false;
|
||||||
|
jobs.UpdateJob(job);
|
||||||
|
|
||||||
|
// trim the job log
|
||||||
|
List<JobLog> logs = jobLogs.GetJobLogs().Where(item => item.JobId == job.JobId)
|
||||||
|
.OrderByDescending(item => item.JobLogId).ToList();
|
||||||
|
for (int i = logs.Count; i > job.RetentionHistory; i--)
|
||||||
|
{
|
||||||
|
jobLogs.DeleteJobLog(logs[i - 1].JobLogId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// can occur during the initial installation because the database has not yet been created
|
|
||||||
if (!ex.Message.Contains("No database provider has been configured for this DbContext"))
|
|
||||||
{
|
{
|
||||||
_filelogger.LogError(Utilities.LogMessage(this, $"An Error Occurred Executing Scheduled Job: {Name} - {ex}"));
|
_filelogger.LogError(Utilities.LogMessage(this, $"An Error Occurred Executing Scheduled Job: {Name} - {ex}"));
|
||||||
}
|
}
|
||||||
@ -208,55 +239,28 @@ namespace Oqtane.Infrastructure
|
|||||||
{
|
{
|
||||||
using (var scope = _serviceScopeFactory.CreateScope())
|
using (var scope = _serviceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
|
IConfigurationRoot _config = scope.ServiceProvider.GetRequiredService<IConfigurationRoot>();
|
||||||
ILogger<HostedServiceBase> _filelogger = scope.ServiceProvider.GetRequiredService<ILogger<HostedServiceBase>>();
|
ILogger<HostedServiceBase> _filelogger = scope.ServiceProvider.GetRequiredService<ILogger<HostedServiceBase>>();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
if (IsInstalled(_config))
|
||||||
IJobRepository jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
|
||||||
Job job = jobs.GetJobs().Where(item => item.JobType == jobTypeName).FirstOrDefault();
|
|
||||||
if (job != null)
|
|
||||||
{
|
{
|
||||||
// reset in case this job was forcefully terminated previously
|
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||||
job.IsStarted = true;
|
IJobRepository jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||||
job.IsExecuting = false;
|
Job job = jobs.GetJobs().Where(item => item.JobType == jobTypeName).FirstOrDefault();
|
||||||
jobs.UpdateJob(job);
|
if (job != null)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// auto registration - job will not run on initial installation due to no DBContext but will run after restart
|
|
||||||
job = new Job { JobType = jobTypeName };
|
|
||||||
|
|
||||||
// optional HostedServiceBase properties
|
|
||||||
var jobType = Type.GetType(jobTypeName);
|
|
||||||
var jobObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, jobType) as HostedServiceBase;
|
|
||||||
if (jobObject.Name != "")
|
|
||||||
{
|
{
|
||||||
job.Name = jobObject.Name;
|
// reset in case this job was forcefully terminated previously
|
||||||
|
job.IsStarted = true;
|
||||||
|
job.IsExecuting = false;
|
||||||
|
jobs.UpdateJob(job);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
job.Name = Utilities.GetTypeName(job.JobType);
|
|
||||||
}
|
|
||||||
job.Frequency = jobObject.Frequency;
|
|
||||||
job.Interval = jobObject.Interval;
|
|
||||||
job.StartDate = jobObject.StartDate;
|
|
||||||
job.EndDate = jobObject.EndDate;
|
|
||||||
job.RetentionHistory = jobObject.RetentionHistory;
|
|
||||||
job.IsEnabled = jobObject.IsEnabled;
|
|
||||||
job.IsStarted = true;
|
|
||||||
job.IsExecuting = false;
|
|
||||||
job.NextExecution = null;
|
|
||||||
jobs.AddJob(job);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// can occur during the initial installation because the database has not yet been created
|
_filelogger.LogError(Utilities.LogMessage(this, $"An Error Occurred Starting Scheduled Job: {Name} - {ex}"));
|
||||||
if (!ex.Message.Contains("No database provider has been configured for this DbContext"))
|
|
||||||
{
|
|
||||||
_filelogger.LogError(Utilities.LogMessage(this, $"An Error Occurred Starting Scheduled Job: {Name} - {ex}"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,6 +318,11 @@ namespace Oqtane.Infrastructure
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsInstalled(IConfigurationRoot config)
|
||||||
|
{
|
||||||
|
return !string.IsNullOrEmpty(config.GetConnectionString(SettingKeys.ConnectionStringKey));
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_cancellationTokenSource.Cancel();
|
_cancellationTokenSource.Cancel();
|
||||||
|
@ -39,7 +39,7 @@ namespace Oqtane.Infrastructure
|
|||||||
List<Site> sites = siteRepository.GetSites().ToList();
|
List<Site> sites = siteRepository.GetSites().ToList();
|
||||||
foreach (Site site in sites)
|
foreach (Site site in sites)
|
||||||
{
|
{
|
||||||
log += "Processing Site: " + site.Name + "<br />";
|
log += "<br />Processing Site: " + site.Name + "<br />";
|
||||||
int retention;
|
int retention;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
@ -118,11 +118,11 @@ namespace Oqtane.Infrastructure
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var assemblies = installationManager.RegisterAssemblies();
|
var assemblies = installationManager.RegisterAssemblies();
|
||||||
log += assemblies.ToString() + " Assemblies Registered<br />";
|
log += "<br />" + assemblies.ToString() + " Assemblies Registered<br />";
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
log += $"Error Registering Assemblies - {ex.Message}<br />";
|
log += $"<br />Error Registering Assemblies - {ex.Message}<br />";
|
||||||
}
|
}
|
||||||
|
|
||||||
return log;
|
return log;
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Oqtane.Infrastructure
|
||||||
|
{
|
||||||
|
public class RightToLeftCulture
|
||||||
|
{
|
||||||
|
public static CultureInfo ResolveFormat(CultureInfo cultureInfo)
|
||||||
|
{
|
||||||
|
SetNumberFormatInfo(cultureInfo.NumberFormat);
|
||||||
|
SetCalenar(cultureInfo);
|
||||||
|
|
||||||
|
return cultureInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetCalenar(CultureInfo cultureInfo)
|
||||||
|
{
|
||||||
|
var calendar = new RightToLeftCultureCalendar();
|
||||||
|
|
||||||
|
var fieldInfo = cultureInfo.GetType().GetField("_calendar", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
if (fieldInfo != null)
|
||||||
|
{
|
||||||
|
fieldInfo.SetValue(cultureInfo, calendar);
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = cultureInfo.DateTimeFormat.GetType().GetField("calendar", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
if (info != null)
|
||||||
|
{
|
||||||
|
info.SetValue(cultureInfo.DateTimeFormat, calendar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetNumberFormatInfo(NumberFormatInfo persianNumberFormatInfo)
|
||||||
|
{
|
||||||
|
persianNumberFormatInfo.NumberDecimalSeparator = ".";
|
||||||
|
persianNumberFormatInfo.DigitSubstitution = DigitShapes.NativeNational;
|
||||||
|
persianNumberFormatInfo.NumberNegativePattern = 0;
|
||||||
|
persianNumberFormatInfo.NegativeSign = "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Oqtane.Infrastructure
|
||||||
|
{
|
||||||
|
public class RightToLeftCultureCalendar : System.Globalization.PersianCalendar
|
||||||
|
{
|
||||||
|
public override int GetYear(DateTime time)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return base.GetYear(time);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Year;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetMonth(DateTime time)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return base.GetMonth(time);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Month;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetDayOfMonth(DateTime time)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return base.GetDayOfMonth(time);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Day;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetDayOfYear(DateTime time)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return base.GetDayOfYear(time);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.DayOfYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DayOfWeek GetDayOfWeek(DateTime time)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return base.GetDayOfWeek(time);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.DayOfWeek;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,22 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
|
||||||
namespace Oqtane.SiteTemplates
|
namespace Oqtane.Infrastructure.SiteTemplates
|
||||||
{
|
{
|
||||||
[PrivateApi("Mark Site-Template classes as private, since it's not very useful in the public docs")]
|
[PrivateApi("Mark Site-Template classes as private, since it's not very useful in the public docs")]
|
||||||
public class AdminSiteTemplate : ISiteTemplate
|
public class AdminSiteTemplate : ISiteTemplate
|
||||||
{
|
{
|
||||||
|
private readonly IStringLocalizer<AdminSiteTemplate> _localizer;
|
||||||
|
|
||||||
|
public AdminSiteTemplate(IStringLocalizer<AdminSiteTemplate> localizer)
|
||||||
|
{
|
||||||
|
_localizer = localizer;
|
||||||
|
}
|
||||||
|
|
||||||
public string Name
|
public string Name
|
||||||
{
|
{
|
||||||
get { return "Admin Site Template"; }
|
get { return "Admin Site Template"; }
|
||||||
@ -169,6 +177,66 @@ namespace Oqtane.SiteTemplates
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pageTemplates.Add(new PageTemplate
|
||||||
|
{
|
||||||
|
Name = "Privacy",
|
||||||
|
Parent = "",
|
||||||
|
Path = "privacy",
|
||||||
|
Icon = Icons.Eye,
|
||||||
|
IsNavigation = false,
|
||||||
|
IsPersonalizable = false,
|
||||||
|
PermissionList = new List<Permission>
|
||||||
|
{
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||||
|
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||||
|
},
|
||||||
|
PageTemplateModules = new List<PageTemplateModule>
|
||||||
|
{
|
||||||
|
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Privacy Policy", Pane = PaneNames.Default,
|
||||||
|
PermissionList = new List<Permission> {
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||||
|
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||||
|
},
|
||||||
|
Settings = new List<Setting> {
|
||||||
|
new Setting { SettingName = "DynamicTokens", SettingValue = "true" }
|
||||||
|
},
|
||||||
|
Content = _localizer["Privacy"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pageTemplates.Add(new PageTemplate
|
||||||
|
{
|
||||||
|
Name = "Terms",
|
||||||
|
Parent = "",
|
||||||
|
Path = "terms",
|
||||||
|
Icon = Icons.List,
|
||||||
|
IsNavigation = false,
|
||||||
|
IsPersonalizable = false,
|
||||||
|
PermissionList = new List<Permission>
|
||||||
|
{
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||||
|
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||||
|
},
|
||||||
|
PageTemplateModules = new List<PageTemplateModule>
|
||||||
|
{
|
||||||
|
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Terms of Use", Pane = PaneNames.Default,
|
||||||
|
PermissionList = new List<Permission> {
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||||
|
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||||
|
},
|
||||||
|
Settings = new List<Setting> {
|
||||||
|
new Setting { SettingName = "DynamicTokens", SettingValue = "true" }
|
||||||
|
},
|
||||||
|
Content = _localizer["Terms"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
pageTemplates.Add(new PageTemplate
|
pageTemplates.Add(new PageTemplate
|
||||||
{
|
{
|
||||||
Name = "Not Found",
|
Name = "Not Found",
|
||||||
|
@ -7,7 +7,7 @@ using Oqtane.Models;
|
|||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
|
||||||
namespace Oqtane.SiteTemplates
|
namespace Oqtane.Infrastructure.SiteTemplates
|
||||||
{
|
{
|
||||||
[PrivateApi("Mark Site-Template classes as private, since it's not very useful in the public docs")]
|
[PrivateApi("Mark Site-Template classes as private, since it's not very useful in the public docs")]
|
||||||
public class DefaultSiteTemplate : ISiteTemplate
|
public class DefaultSiteTemplate : ISiteTemplate
|
||||||
|
@ -4,7 +4,7 @@ using Oqtane.Infrastructure;
|
|||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
|
||||||
namespace Oqtane.SiteTemplates
|
namespace Oqtane.Infrastructure.SiteTemplates
|
||||||
{
|
{
|
||||||
[PrivateApi("Mark Site-Template classes as private, since it's not very useful in the public docs")]
|
[PrivateApi("Mark Site-Template classes as private, since it's not very useful in the public docs")]
|
||||||
public class EmptySiteTemplate : ISiteTemplate
|
public class EmptySiteTemplate : ISiteTemplate
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Oqtane.Infrastructure.SiteTemplates;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
@ -75,6 +77,9 @@ namespace Oqtane.Infrastructure
|
|||||||
case "6.1.0":
|
case "6.1.0":
|
||||||
Upgrade_6_1_0(tenant, scope);
|
Upgrade_6_1_0(tenant, scope);
|
||||||
break;
|
break;
|
||||||
|
case "6.1.1":
|
||||||
|
Upgrade_6_1_1(tenant, scope);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -457,6 +462,75 @@ namespace Oqtane.Infrastructure
|
|||||||
RemoveAssemblies(tenant, assemblies, "6.1.0");
|
RemoveAssemblies(tenant, assemblies, "6.1.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Upgrade_6_1_1(Tenant tenant, IServiceScope scope)
|
||||||
|
{
|
||||||
|
var localizer = scope.ServiceProvider.GetRequiredService<IStringLocalizer<AdminSiteTemplate>>();
|
||||||
|
|
||||||
|
var pageTemplates = new List<PageTemplate>
|
||||||
|
{
|
||||||
|
new PageTemplate
|
||||||
|
{
|
||||||
|
Name = "Privacy",
|
||||||
|
Parent = "",
|
||||||
|
Path = "privacy",
|
||||||
|
Icon = Icons.Eye,
|
||||||
|
IsNavigation = false,
|
||||||
|
IsPersonalizable = false,
|
||||||
|
PermissionList = new List<Permission>
|
||||||
|
{
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||||
|
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||||
|
},
|
||||||
|
PageTemplateModules = new List<PageTemplateModule>
|
||||||
|
{
|
||||||
|
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Privacy Policy", Pane = PaneNames.Default,
|
||||||
|
PermissionList = new List<Permission> {
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||||
|
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||||
|
},
|
||||||
|
Settings = new List<Setting> {
|
||||||
|
new Setting { SettingName = "DynamicTokens", SettingValue = "true" }
|
||||||
|
},
|
||||||
|
Content = localizer["Privacy"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new PageTemplate
|
||||||
|
{
|
||||||
|
Name = "Terms",
|
||||||
|
Parent = "",
|
||||||
|
Path = "terms",
|
||||||
|
Icon = Icons.List,
|
||||||
|
IsNavigation = false,
|
||||||
|
IsPersonalizable = false,
|
||||||
|
PermissionList = new List<Permission>
|
||||||
|
{
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||||
|
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||||
|
},
|
||||||
|
PageTemplateModules = new List<PageTemplateModule>
|
||||||
|
{
|
||||||
|
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Terms of Use", Pane = PaneNames.Default,
|
||||||
|
PermissionList = new List<Permission> {
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||||
|
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||||
|
},
|
||||||
|
Settings = new List<Setting> {
|
||||||
|
new Setting { SettingName = "DynamicTokens", SettingValue = "true" }
|
||||||
|
},
|
||||||
|
Content = localizer["Terms"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AddPagesToSites(scope, tenant, pageTemplates);
|
||||||
|
}
|
||||||
|
|
||||||
private void AddPagesToSites(IServiceScope scope, Tenant tenant, List<PageTemplate> pageTemplates)
|
private void AddPagesToSites(IServiceScope scope, Tenant tenant, List<PageTemplate> pageTemplates)
|
||||||
{
|
{
|
||||||
var tenants = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
var tenants = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>6.1.0</Version>
|
<Version>6.1.1</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/v6.1.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</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>
|
||||||
@ -34,21 +34,21 @@
|
|||||||
<EmbeddedResource Include="Scripts\MigrateTenant.sql" />
|
<EmbeddedResource Include="Scripts\MigrateTenant.sql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.1" />
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.3">
|
||||||
<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="9.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.1" />
|
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.3" />
|
||||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.11-pre20241216174303" />
|
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.11" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.72" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.12.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Oqtane.Client\Oqtane.Client.csproj" />
|
<ProjectReference Include="..\Oqtane.Client\Oqtane.Client.csproj" />
|
||||||
|
@ -50,7 +50,6 @@ namespace Oqtane.Pages
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Access Attempt - Path Not Specified For Site {SiteId}", _alias.SiteId);
|
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
return BrokenFile();
|
return BrokenFile();
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using System.Xml;
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.AspNetCore.OutputCaching;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Oqtane.Enums;
|
using Oqtane.Enums;
|
||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
@ -19,6 +20,7 @@ using Oqtane.Shared;
|
|||||||
namespace Oqtane.Pages
|
namespace Oqtane.Pages
|
||||||
{
|
{
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
|
[OutputCache(Duration = 300, Tags = [Constants.SitemapOutputCacheTag])]
|
||||||
public class SitemapModel : PageModel
|
public class SitemapModel : PageModel
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
@ -22,6 +22,14 @@ namespace Oqtane.Repository
|
|||||||
{
|
{
|
||||||
return _cache.GetOrCreate("jobs", entry =>
|
return _cache.GetOrCreate("jobs", entry =>
|
||||||
{
|
{
|
||||||
|
// remove any jobs which have been uninstalled
|
||||||
|
foreach (var job in _db.Job.ToList())
|
||||||
|
{
|
||||||
|
if (Type.GetType(job.JobType) == null)
|
||||||
|
{
|
||||||
|
DeleteJob(job.JobId);
|
||||||
|
}
|
||||||
|
}
|
||||||
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
|
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
|
||||||
return _db.Job.ToList();
|
return _db.Job.ToList();
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
@ -27,7 +28,7 @@ namespace Oqtane.Repository
|
|||||||
{
|
{
|
||||||
if (IsMaster(entityName))
|
if (IsMaster(entityName))
|
||||||
{
|
{
|
||||||
return _master.Setting.Where(item => item.EntityName == entityName);
|
return _master.Setting.Where(item => item.EntityName == entityName).ToList();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -38,13 +39,28 @@ namespace Oqtane.Repository
|
|||||||
|
|
||||||
public IEnumerable<Setting> GetSettings(string entityName, int entityId)
|
public IEnumerable<Setting> GetSettings(string entityName, int entityId)
|
||||||
{
|
{
|
||||||
var settings = GetSettings(entityName);
|
var settings = GetSettings(entityName).ToList();
|
||||||
|
if (entityName == EntityNames.Site)
|
||||||
|
{
|
||||||
|
// site settings can be overridden by host settings
|
||||||
|
var hostsettings = GetSettings(EntityNames.Host);
|
||||||
|
foreach (var hostsetting in hostsettings)
|
||||||
|
{
|
||||||
|
if (settings.Any(item => item.SettingName == hostsetting.SettingName))
|
||||||
|
{
|
||||||
|
settings.First(item => item.SettingName == hostsetting.SettingName).SettingValue = hostsetting.SettingValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
settings.Add(new Setting { SettingId = -1, EntityName = entityName, EntityId = entityId, SettingName = hostsetting.SettingName, SettingValue = hostsetting.SettingValue, IsPrivate = hostsetting.IsPrivate });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return settings.Where(item => item.EntityId == entityId);
|
return settings.Where(item => item.EntityId == entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Setting AddSetting(Setting setting)
|
public Setting AddSetting(Setting setting)
|
||||||
{
|
{
|
||||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
|
||||||
if (IsMaster(setting.EntityName))
|
if (IsMaster(setting.EntityName))
|
||||||
{
|
{
|
||||||
_master.Setting.Add(setting);
|
_master.Setting.Add(setting);
|
||||||
@ -52,6 +68,7 @@ namespace Oqtane.Repository
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||||
tenant.Setting.Add(setting);
|
tenant.Setting.Add(setting);
|
||||||
tenant.SaveChanges();
|
tenant.SaveChanges();
|
||||||
}
|
}
|
||||||
@ -61,7 +78,6 @@ namespace Oqtane.Repository
|
|||||||
|
|
||||||
public Setting UpdateSetting(Setting setting)
|
public Setting UpdateSetting(Setting setting)
|
||||||
{
|
{
|
||||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
|
||||||
if (IsMaster(setting.EntityName))
|
if (IsMaster(setting.EntityName))
|
||||||
{
|
{
|
||||||
_master.Entry(setting).State = EntityState.Modified;
|
_master.Entry(setting).State = EntityState.Modified;
|
||||||
@ -69,6 +85,7 @@ namespace Oqtane.Repository
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||||
tenant.Entry(setting).State = EntityState.Modified;
|
tenant.Entry(setting).State = EntityState.Modified;
|
||||||
tenant.SaveChanges();
|
tenant.SaveChanges();
|
||||||
}
|
}
|
||||||
@ -78,33 +95,32 @@ namespace Oqtane.Repository
|
|||||||
|
|
||||||
public Setting GetSetting(string entityName, int settingId)
|
public Setting GetSetting(string entityName, int settingId)
|
||||||
{
|
{
|
||||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
|
||||||
if (IsMaster(entityName))
|
if (IsMaster(entityName))
|
||||||
{
|
{
|
||||||
return _master.Setting.Find(settingId);
|
return _master.Setting.Find(settingId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||||
return tenant.Setting.Find(settingId);
|
return tenant.Setting.Find(settingId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Setting GetSetting(string entityName, int entityId, string settingName)
|
public Setting GetSetting(string entityName, int entityId, string settingName)
|
||||||
{
|
{
|
||||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
|
||||||
if (IsMaster(entityName))
|
if (IsMaster(entityName))
|
||||||
{
|
{
|
||||||
return _master.Setting.Where(item => item.EntityName == entityName && item.EntityId == entityId && item.SettingName == settingName).FirstOrDefault();
|
return _master.Setting.Where(item => item.EntityName == entityName && item.EntityId == entityId && item.SettingName == settingName).FirstOrDefault();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||||
return tenant.Setting.Where(item => item.EntityName == entityName && item.EntityId == entityId && item.SettingName == settingName).FirstOrDefault();
|
return tenant.Setting.Where(item => item.EntityName == entityName && item.EntityId == entityId && item.SettingName == settingName).FirstOrDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteSetting(string entityName, int settingId)
|
public void DeleteSetting(string entityName, int settingId)
|
||||||
{
|
{
|
||||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
|
||||||
if (IsMaster(entityName))
|
if (IsMaster(entityName))
|
||||||
{
|
{
|
||||||
Setting setting = _master.Setting.Find(settingId);
|
Setting setting = _master.Setting.Find(settingId);
|
||||||
@ -113,6 +129,7 @@ namespace Oqtane.Repository
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||||
Setting setting = tenant.Setting.Find(settingId);
|
Setting setting = tenant.Setting.Find(settingId);
|
||||||
tenant.Setting.Remove(setting);
|
tenant.Setting.Remove(setting);
|
||||||
tenant.SaveChanges();
|
tenant.SaveChanges();
|
||||||
@ -122,7 +139,6 @@ namespace Oqtane.Repository
|
|||||||
|
|
||||||
public void DeleteSettings(string entityName, int entityId)
|
public void DeleteSettings(string entityName, int entityId)
|
||||||
{
|
{
|
||||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
|
||||||
if (IsMaster(entityName))
|
if (IsMaster(entityName))
|
||||||
{
|
{
|
||||||
IEnumerable<Setting> settings = _master.Setting
|
IEnumerable<Setting> settings = _master.Setting
|
||||||
@ -136,6 +152,7 @@ namespace Oqtane.Repository
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||||
IEnumerable<Setting> settings = tenant.Setting
|
IEnumerable<Setting> settings = tenant.Setting
|
||||||
.Where(item => item.EntityName == entityName)
|
.Where(item => item.EntityName == entityName)
|
||||||
.Where(item => item.EntityId == entityId);
|
.Where(item => item.EntityId == entityId);
|
||||||
|
@ -9,6 +9,7 @@ using Oqtane.Enums;
|
|||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Modules;
|
using Oqtane.Modules;
|
||||||
|
using Oqtane.Modules.Admin.Modules;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using Module = Oqtane.Models.Module;
|
using Module = Oqtane.Models.Module;
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ namespace Oqtane.Repository
|
|||||||
private readonly IPageModuleRepository _pageModuleRepository;
|
private readonly IPageModuleRepository _pageModuleRepository;
|
||||||
private readonly IModuleDefinitionRepository _moduleDefinitionRepository;
|
private readonly IModuleDefinitionRepository _moduleDefinitionRepository;
|
||||||
private readonly IThemeRepository _themeRepository;
|
private readonly IThemeRepository _themeRepository;
|
||||||
|
private readonly ISettingRepository _settingRepository;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly IConfigurationRoot _config;
|
private readonly IConfigurationRoot _config;
|
||||||
private readonly IServerStateManager _serverState;
|
private readonly IServerStateManager _serverState;
|
||||||
@ -32,8 +34,8 @@ namespace Oqtane.Repository
|
|||||||
private static readonly object _lock = new object();
|
private static readonly object _lock = new object();
|
||||||
|
|
||||||
public SiteRepository(IDbContextFactory<TenantDBContext> factory, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IPageRepository pageRepository,
|
public SiteRepository(IDbContextFactory<TenantDBContext> factory, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IPageRepository pageRepository,
|
||||||
IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IThemeRepository themeRepository, IServiceProvider serviceProvider,
|
IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IThemeRepository themeRepository, ISettingRepository settingRepository,
|
||||||
IConfigurationRoot config, IServerStateManager serverState, ILogManager logger)
|
IServiceProvider serviceProvider, IConfigurationRoot config, IServerStateManager serverState, ILogManager logger)
|
||||||
{
|
{
|
||||||
_factory = factory;
|
_factory = factory;
|
||||||
_roleRepository = roleRepository;
|
_roleRepository = roleRepository;
|
||||||
@ -44,6 +46,7 @@ namespace Oqtane.Repository
|
|||||||
_pageModuleRepository = pageModuleRepository;
|
_pageModuleRepository = pageModuleRepository;
|
||||||
_moduleDefinitionRepository = moduleDefinitionRepository;
|
_moduleDefinitionRepository = moduleDefinitionRepository;
|
||||||
_themeRepository = themeRepository;
|
_themeRepository = themeRepository;
|
||||||
|
_settingRepository = settingRepository;
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
_config = config;
|
_config = config;
|
||||||
_serverState = serverState;
|
_serverState = serverState;
|
||||||
@ -391,6 +394,7 @@ namespace Oqtane.Repository
|
|||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Information, "Site Template", LogFunction.Update, "Page Updated {Page}", page);
|
_logger.Log(LogLevel.Information, "Site Template", LogFunction.Update, "Page Updated {Page}", page);
|
||||||
}
|
}
|
||||||
|
UpdateSettings(EntityNames.Page, page.PageId, pageTemplate.Settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -401,6 +405,7 @@ namespace Oqtane.Repository
|
|||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Information, "Site Template", LogFunction.Create, "Page Added {Page}", page);
|
_logger.Log(LogLevel.Information, "Site Template", LogFunction.Create, "Page Added {Page}", page);
|
||||||
}
|
}
|
||||||
|
UpdateSettings(EntityNames.Page, page.PageId, pageTemplate.Settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -457,6 +462,7 @@ namespace Oqtane.Repository
|
|||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Information, "Site Template", LogFunction.Update, "Page Module Updated {PageModule}", pageModule);
|
_logger.Log(LogLevel.Information, "Site Template", LogFunction.Update, "Page Module Updated {PageModule}", pageModule);
|
||||||
}
|
}
|
||||||
|
UpdateSettings(EntityNames.Module, pageModule.Module.ModuleId, pageTemplateModule.Settings);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -475,6 +481,7 @@ namespace Oqtane.Repository
|
|||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Information, "Site Template", LogFunction.Create, "Page Module Added {PageModule}", pageModule);
|
_logger.Log(LogLevel.Information, "Site Template", LogFunction.Create, "Page Module Added {PageModule}", pageModule);
|
||||||
}
|
}
|
||||||
|
UpdateSettings(EntityNames.Module, pageModule.Module.ModuleId, pageTemplateModule.Settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -522,5 +529,25 @@ namespace Oqtane.Repository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateSettings(string entityName, int entityId, List<Setting> templateSettings)
|
||||||
|
{
|
||||||
|
foreach (var templateSetting in templateSettings)
|
||||||
|
{
|
||||||
|
var setting = _settingRepository.GetSetting(entityName, entityId, templateSetting.SettingName);
|
||||||
|
if (setting == null)
|
||||||
|
{
|
||||||
|
templateSetting.EntityName = entityName;
|
||||||
|
templateSetting.EntityId = entityId;
|
||||||
|
_settingRepository.AddSetting(templateSetting);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setting.SettingValue = templateSetting.SettingValue;
|
||||||
|
setting.IsPrivate = templateSetting.IsPrivate;
|
||||||
|
_settingRepository.UpdateSetting(setting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,14 +65,14 @@ namespace Oqtane.Repository
|
|||||||
// delete visitors in batches of 100 records
|
// delete visitors in batches of 100 records
|
||||||
var count = 0;
|
var count = 0;
|
||||||
var purgedate = DateTime.UtcNow.AddDays(-age);
|
var purgedate = DateTime.UtcNow.AddDays(-age);
|
||||||
var visitors = db.Visitor.Where(item => item.SiteId == siteId && item.Visits < 2 && item.VisitedOn < purgedate)
|
var visitors = db.Visitor.Where(item => item.SiteId == siteId && item.VisitedOn < purgedate)
|
||||||
.OrderBy(item => item.VisitedOn).Take(100).ToList();
|
.OrderBy(item => item.VisitedOn).Take(100).ToList();
|
||||||
while (visitors.Count > 0)
|
while (visitors.Count > 0)
|
||||||
{
|
{
|
||||||
count += visitors.Count;
|
count += visitors.Count;
|
||||||
db.Visitor.RemoveRange(visitors);
|
db.Visitor.RemoveRange(visitors);
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
visitors = db.Visitor.Where(item => item.SiteId == siteId && item.Visits < 2 && item.VisitedOn < purgedate)
|
visitors = db.Visitor.Where(item => item.SiteId == siteId && item.VisitedOn < purgedate)
|
||||||
.OrderBy(item => item.VisitedOn).Take(100).ToList();
|
.OrderBy(item => item.VisitedOn).Take(100).ToList();
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
|
@ -0,0 +1,199 @@
|
|||||||
|
<?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="Privacy" xml:space="preserve">
|
||||||
|
<value><p>This privacy policy ("policy") will help you understand how [PageState:Site:Name] ("us", "we", "our") uses and protects the data you provide to us when you visit and use this website.</p>
|
||||||
|
|
||||||
|
<p>We reserve the right to change this policy at any time. If you want to make sure that you are up to date with the latest changes, we advise you to frequently visit this page.</p>
|
||||||
|
|
||||||
|
<h2>What User Data We Collect</h2>
|
||||||
|
|
||||||
|
<p>When you visit this website, we may collect the following data: your IP address, your contact information and email address, other information such as interests and preferences.</p>
|
||||||
|
|
||||||
|
<h2>Why We Collect Your Data</h2>
|
||||||
|
|
||||||
|
<p>We are collecting your data for several reasons: to better understand your needs, to improve our products and services, to send you promotional emails containing the information we think you will find interesting, to customize our website according to your online behavior and personal preferences.</p>
|
||||||
|
|
||||||
|
<h2>Safeguarding and Securing the Data</h2>
|
||||||
|
|
||||||
|
<p>[PageState:Site:Name] is committed to securing your data and keeping it confidential. [PageState:Site:Name] has done everything in its power to prevent data theft, unauthorized access, and disclosure by implementing the latest technologies and software, which help us safeguard all the information we collect online.</p>
|
||||||
|
|
||||||
|
<h2>Our Cookie Policy</h2>
|
||||||
|
|
||||||
|
<p>Once you agree to allow our website to use cookies, you also agree to allow us to use the data it collects regarding your online behavior (analyze web traffic, web pages you visit and spend the most time on, etc...).</p>
|
||||||
|
|
||||||
|
<p>The data we collect by using cookies is used to customize our website to your needs.</p>
|
||||||
|
|
||||||
|
<p>Please note that cookies don't allow us to gain access to your computer in any way. They are strictly used to monitor which pages you find useful and which you do not so that we can provide a better experience for you.</p>
|
||||||
|
|
||||||
|
<p>If you want to disable or remove cookies, you can do so by accessing the settings of your internet browser.</p>
|
||||||
|
|
||||||
|
<h2>Links to Other Websites</h2>
|
||||||
|
|
||||||
|
<p>Our website contains links that lead to other websites. If you click on these links [PageState:Site:Name] is not held responsible for your data and privacy protection. Visiting those websites is not governed by this privacy policy agreement. Make sure to read the privacy policy documentation of any website you navigate to from our website.</p>
|
||||||
|
|
||||||
|
<h2>Restricting the Collection of your Personal Data</h2>
|
||||||
|
|
||||||
|
<p>At some point, you might wish to restrict the use and collection of your personal data. If you previously agreed to share your information with us, feel free to contact us via email and we will be more than happy to change this for you.</p>
|
||||||
|
|
||||||
|
<p>[PageState:Site:Name] will not lease, sell or distribute your personal information to any third parties, unless we have your permission. Your personal information will only be used when we need to send you promotional materials if you agree to this privacy policy.</p></value>
|
||||||
|
</data>
|
||||||
|
<data name="Terms" xml:space="preserve">
|
||||||
|
<value><p>Please read these terms and conditions carefully before using this website operated by [PageState:Site:Name] ("us", "we", "our").</p>
|
||||||
|
|
||||||
|
<h2>Conditions of Use</h2>
|
||||||
|
|
||||||
|
<p>By using this website, you certify that you have read and reviewed this Agreement and that you agree to comply with its terms. If you do not want to be bound by the terms of this Agreement, you are advised to stop using the website accordingly. [PageState:Site:Name] only grants use and access of this website, its products, and its services to those who have accepted its terms.</p>
|
||||||
|
|
||||||
|
<h2>Privacy Policy</h2>
|
||||||
|
|
||||||
|
<p>Before you continue using our website, we advise you to read our <a href="/privacy">privacy policy</a> regarding our user data collection. It will help you better understand our practices.</p>
|
||||||
|
|
||||||
|
<h2>Intellectual Property</h2>
|
||||||
|
|
||||||
|
<p>You agree that all materials, products, and services provided on this website are the property of [PageState:Site:Name], its affiliates, directors, officers, employees, agents, suppliers, or licensors including all copyrights, trade secrets, trademarks, patents, and other intellectual property. You also agree that you will not reproduce or redistribute the [PageState:Site:Name]’s intellectual property in any way, including electronic, digital, or new trademark registrations.</p>
|
||||||
|
|
||||||
|
<p>You grant [PageState:Site:Name] a royalty-free and non-exclusive license to display, use, copy, transmit, and broadcast the content you upload and publish. For issues regarding intellectual property claims, you should contact us in order to come to an agreement.</p>
|
||||||
|
|
||||||
|
<h2>User Accounts</h2>
|
||||||
|
|
||||||
|
<p>As a user of this website, you may be asked to register with us and provide private information. You are responsible for ensuring the accuracy of this information, and you are responsible for maintaining the safety and security of your identifying information.</p>
|
||||||
|
|
||||||
|
<p>You are also responsible for all activities that occur under your account or password. If you think there are any possible issues regarding the security of your account on the website, inform us immediately so we may address them accordingly.</p>
|
||||||
|
|
||||||
|
<p>We reserve all rights to terminate accounts, edit or remove content and cancel orders at our sole discretion.</p>
|
||||||
|
|
||||||
|
<h2>Applicable Law</h2>
|
||||||
|
|
||||||
|
<p>By using this website, you agree that the laws of the jurisdiction associated to [PageState:Site:Name], without regard to principles of conflict laws, will govern these terms and conditions, or any dispute of any sort that might come between [PageState:Site:Name] and you, or its business partners and associates.</p>
|
||||||
|
|
||||||
|
<h2>Disputes</h2>
|
||||||
|
|
||||||
|
<p>Any dispute related in any way to your use of this website or to products you purchase from us shall be arbitrated by a court of law and you consent to exclusive jurisdiction and venue of such courts.</p>
|
||||||
|
|
||||||
|
<h2>Indemnification</h2>
|
||||||
|
|
||||||
|
<p>You agree to indemnify [PageState:Site:Name] and its affiliates and hold [PageState:Site:Name] harmless against legal claims and demands that may arise from your use or misuse of our services. We reserve the right to select our own legal counsel.</p>
|
||||||
|
|
||||||
|
<h2>Limitation on Liability</h2>
|
||||||
|
|
||||||
|
<p>[PageState:Site:Name] is not liable for any damages that may occur to you as a result of your misuse of our website. [PageState:Site:Name] reserves the right to edit, modify, and change this Agreement at any time. We shall let our users know of these changes through electronic mail. This Agreement is an understanding between [PageState:Site:Name] and the user, and this supersedes and replaces all prior agreements regarding the use of this website.</p>
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
120
Oqtane.Server/Services/CookieConsentService.cs
Normal file
120
Oqtane.Server/Services/CookieConsentService.cs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.AspNetCore.Localization;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Services
|
||||||
|
{
|
||||||
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
|
public class ServerCookieConsentService : ICookieConsentService
|
||||||
|
{
|
||||||
|
private readonly IHttpContextAccessor _accessor;
|
||||||
|
private readonly CookiePolicyOptions _cookiePolicyOptions;
|
||||||
|
|
||||||
|
public ServerCookieConsentService(IHttpContextAccessor accessor, IOptions<CookiePolicyOptions> cookiePolicyOptions)
|
||||||
|
{
|
||||||
|
_accessor = accessor;
|
||||||
|
_cookiePolicyOptions = cookiePolicyOptions.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<bool> IsActionedAsync()
|
||||||
|
{
|
||||||
|
var actioned = false;
|
||||||
|
if (_accessor.HttpContext != null)
|
||||||
|
{
|
||||||
|
var cookieValue = GetCookieValue("actioned");
|
||||||
|
actioned = cookieValue == Constants.CookieConsentActionCookieValue;
|
||||||
|
}
|
||||||
|
return Task.FromResult(actioned);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<bool> CanTrackAsync(bool optOut)
|
||||||
|
{
|
||||||
|
var canTrack = true;
|
||||||
|
if (_accessor.HttpContext != null)
|
||||||
|
{
|
||||||
|
var cookieValue = GetCookieValue("consent");
|
||||||
|
var saved = cookieValue == Constants.CookieConsentCookieValue;
|
||||||
|
if (optOut)
|
||||||
|
{
|
||||||
|
canTrack = string.IsNullOrEmpty(cookieValue) || !saved;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
canTrack = cookieValue == Constants.CookieConsentCookieValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(canTrack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string> CreateActionedCookieAsync()
|
||||||
|
{
|
||||||
|
var cookieString = CreateCookieString(false, string.Empty);
|
||||||
|
return Task.FromResult(cookieString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string> CreateConsentCookieAsync()
|
||||||
|
{
|
||||||
|
var cookieString = CreateCookieString(true, Constants.CookieConsentCookieValue);
|
||||||
|
return Task.FromResult(cookieString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string> WithdrawConsentCookieAsync()
|
||||||
|
{
|
||||||
|
var cookieString = CreateCookieString(true, string.Empty);
|
||||||
|
return Task.FromResult(cookieString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetCookieValue(string type)
|
||||||
|
{
|
||||||
|
var cookieValue = string.Empty;
|
||||||
|
if (_accessor.HttpContext != null)
|
||||||
|
{
|
||||||
|
var value = _accessor.HttpContext.Request.Cookies[Constants.CookieConsentCookieName];
|
||||||
|
var index = type == "actioned" ? 1 : 0;
|
||||||
|
cookieValue = !string.IsNullOrEmpty(value) && value.Contains("|") ? value.Split('|')[index] : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cookieValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string CreateCookieString(bool saved, string savedValue)
|
||||||
|
{
|
||||||
|
var cookieString = string.Empty;
|
||||||
|
if (_accessor.HttpContext != null)
|
||||||
|
{
|
||||||
|
var savedCookie = saved ? savedValue : GetCookieValue("consent");
|
||||||
|
var actionedCookie = Constants.CookieConsentActionCookieValue;
|
||||||
|
var cookieValue = $"{savedCookie}|{actionedCookie}";
|
||||||
|
var options = _cookiePolicyOptions.ConsentCookie.Build(_accessor.HttpContext);
|
||||||
|
|
||||||
|
if (!_accessor.HttpContext.Response.HasStarted)
|
||||||
|
{
|
||||||
|
_accessor.HttpContext.Response.Cookies.Append(
|
||||||
|
Constants.CookieConsentCookieName,
|
||||||
|
cookieValue,
|
||||||
|
new CookieOptions()
|
||||||
|
{
|
||||||
|
Expires = options.Expires,
|
||||||
|
IsEssential = true,
|
||||||
|
SameSite = options.SameSite,
|
||||||
|
Secure = options.Secure
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the cookie string from response header
|
||||||
|
cookieString = options.CreateCookieHeader(Constants.CookieConsentCookieName, Uri.EscapeDataString(cookieValue)).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return cookieString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
Oqtane.Server/Services/OutputCacheService.cs
Normal file
41
Oqtane.Server/Services/OutputCacheService.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.OutputCaching;
|
||||||
|
|
||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Enums;
|
||||||
|
using Oqtane.Infrastructure;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Services
|
||||||
|
{
|
||||||
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
|
public class ServerOutputCacheService : IOutputCacheService
|
||||||
|
{
|
||||||
|
private readonly IOutputCacheStore _outputCacheStore;
|
||||||
|
private readonly ILogManager _logger;
|
||||||
|
private readonly IHttpContextAccessor _accessor;
|
||||||
|
|
||||||
|
public ServerOutputCacheService(IOutputCacheStore outputCacheStore, ILogManager logger, IHttpContextAccessor accessor)
|
||||||
|
{
|
||||||
|
_outputCacheStore = outputCacheStore;
|
||||||
|
_logger = logger;
|
||||||
|
_accessor = accessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task EvictByTag(string tag)
|
||||||
|
{
|
||||||
|
if (_accessor.HttpContext.User.IsInRole(RoleNames.Admin))
|
||||||
|
{
|
||||||
|
await _outputCacheStore.EvictByTagAsync(tag, default);
|
||||||
|
_logger.Log(LogLevel.Information, this, LogFunction.Other, "Evicted Output Cache for Tag {Tag}", tag);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Output Cache Eviction for {Tag}", tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -142,6 +142,8 @@ namespace Oqtane
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
services.AddOutputCache();
|
||||||
|
|
||||||
services.AddMvc(options =>
|
services.AddMvc(options =>
|
||||||
{
|
{
|
||||||
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
|
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
|
||||||
@ -222,6 +224,7 @@ namespace Oqtane
|
|||||||
app.UseJwtAuthorization();
|
app.UseJwtAuthorization();
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
app.UseCors();
|
app.UseCors();
|
||||||
|
app.UseOutputCache();
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
app.UseAntiforgery();
|
app.UseAntiforgery();
|
||||||
|
@ -13,11 +13,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.3" />
|
||||||
<PackageReference Include="System.Net.Http.Json" Version="9.0.1" />
|
<PackageReference Include="System.Net.Http.Json" Version="9.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
|
|
||||||
.breadcrumbs {
|
.breadcrumbs {
|
||||||
background-color: #e6e6e6;
|
background-color: #e6e6e6;
|
||||||
border-bottom: 1px solid #d6d5d5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-row {
|
.top-row {
|
||||||
@ -120,12 +119,7 @@
|
|||||||
.app-logo .navbar-brand {
|
.app-logo .navbar-brand {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
@media (max-width: 991.98px) {
|
||||||
@media (max-width: 767.98px) {
|
|
||||||
.main .top-row {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-search {
|
.app-search {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
@ -141,7 +135,7 @@
|
|||||||
|
|
||||||
.app-search:active, .app-search:hover {
|
.app-search:active, .app-search:hover {
|
||||||
display: block;
|
display: block;
|
||||||
position: fixed;
|
position: absolute;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
top: 0;
|
top: 0;
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
@ -149,6 +143,7 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
background-color: #e6e6e6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-search:active .app-form-inline, .app-search:hover .app-form-inline {
|
.app-search:active .app-form-inline, .app-search:hover .app-form-inline {
|
||||||
@ -169,6 +164,11 @@
|
|||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
.main .top-row {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
app {
|
app {
|
||||||
@ -257,6 +257,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
|
border-bottom: 1px solid #d6d5d5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
|
@ -99,7 +99,49 @@ div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
@media (max-width: 991.98px) {
|
||||||
|
.app-search {
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.app-search input{
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-search input + button {
|
||||||
|
position: initial;
|
||||||
|
padding-top: 7px;
|
||||||
|
padding-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-search:active, .app-search:hover {
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
min-height: 96px;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
z-index: 999;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-search:active .app-form-inline, .app-search:hover .app-form-inline {
|
||||||
|
margin: 10px auto;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-search:active .app-form-inline input, .app-search:hover .app-form-inline input {
|
||||||
|
width: 100%;
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
.app-search:active .app-form-inline input + button, .app-search:hover .app-form-inline input + button {
|
||||||
|
position: absolute;
|
||||||
|
color: rgb(42, 159, 214);
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@media (max-width: 767.98px) {
|
@media (max-width: 767.98px) {
|
||||||
|
|
||||||
.app-menu {
|
.app-menu {
|
||||||
@ -130,45 +172,7 @@ div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu
|
|||||||
position: relative;
|
position: relative;
|
||||||
top: 60px;
|
top: 60px;
|
||||||
}
|
}
|
||||||
.app-search {
|
.app-search:active, .app-search:hover{
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
.app-search input{
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-search input + button {
|
|
||||||
position: initial;
|
|
||||||
padding-top: 7px;
|
|
||||||
padding-bottom: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-search:active, .app-search:hover {
|
|
||||||
display: block;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
width: 100%;
|
|
||||||
left: 0;
|
|
||||||
z-index: 999;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-search:active .app-form-inline, .app-search:hover .app-form-inline {
|
|
||||||
margin: 10px auto;
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
max-width: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-search:active .app-form-inline input, .app-search:hover .app-form-inline input {
|
|
||||||
width: 100%;
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
.app-search:active .app-form-inline input + button, .app-search:hover .app-form-inline input + button {
|
|
||||||
position: absolute;
|
|
||||||
color: rgb(42, 159, 214);
|
|
||||||
padding-top: 6px;
|
|
||||||
padding-bottom: 6px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -271,4 +271,14 @@ app {
|
|||||||
|
|
||||||
.app-logo .navbar-brand {
|
.app-logo .navbar-brand {
|
||||||
padding: 5px 20px 5px 20px;
|
padding: 5px 20px 5px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cookie consent */
|
||||||
|
.gdpr-consent-bar .btn-show{
|
||||||
|
bottom: -3px;
|
||||||
|
left: 5px;
|
||||||
|
}
|
||||||
|
.gdpr-consent-bar .btn-hide{
|
||||||
|
top: 0;
|
||||||
|
right: 5px;
|
||||||
}
|
}
|
@ -14,6 +14,9 @@ Oqtane.Interop = {
|
|||||||
}
|
}
|
||||||
document.cookie = cookieString;
|
document.cookie = cookieString;
|
||||||
},
|
},
|
||||||
|
setCookieString: function (cookieString) {
|
||||||
|
document.cookie = cookieString;
|
||||||
|
},
|
||||||
getCookie: function (name) {
|
getCookie: function (name) {
|
||||||
name = name + "=";
|
name = name + "=";
|
||||||
var decodedCookie = decodeURIComponent(document.cookie);
|
var decodedCookie = decodeURIComponent(document.cookie);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
const scriptKeys = new Set();
|
const scriptKeys = new Set();
|
||||||
|
|
||||||
export function onUpdate() {
|
export function onUpdate() {
|
||||||
// determine if this is an enhanced navigation
|
// determine if this is an initial request
|
||||||
let enhancedNavigation = scriptKeys.size !== 0;
|
let initialRequest = scriptKeys.size === 0;
|
||||||
|
|
||||||
// iterate over all script elements in document
|
// iterate over all script elements in document
|
||||||
const scripts = document.getElementsByTagName('script');
|
const scripts = document.getElementsByTagName('script');
|
||||||
@ -11,7 +11,7 @@ export function onUpdate() {
|
|||||||
if (script.hasAttribute('data-reload')) {
|
if (script.hasAttribute('data-reload')) {
|
||||||
let key = getKey(script);
|
let key = getKey(script);
|
||||||
|
|
||||||
if (enhancedNavigation) {
|
if (!initialRequest) {
|
||||||
// reload the script if data-reload is "always" or "true"... or if the script has not been loaded previously and data-reload is "once"
|
// reload the script if data-reload is "always" or "true"... or if the script has not been loaded previously and data-reload is "once"
|
||||||
let dataReload = script.getAttribute('data-reload');
|
let dataReload = script.getAttribute('data-reload');
|
||||||
if ((dataReload === 'always' || dataReload === 'true') || (!scriptKeys.has(key) && dataReload == 'once')) {
|
if ((dataReload === 'always' || dataReload === 'true') || (!scriptKeys.has(key) && dataReload == 'once')) {
|
||||||
@ -40,7 +40,7 @@ function getKey(script) {
|
|||||||
function reloadScript(script) {
|
function reloadScript(script) {
|
||||||
try {
|
try {
|
||||||
if (isValid(script)) {
|
if (isValid(script)) {
|
||||||
replaceScript(script);
|
injectScript(script);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Blazor Script Reload failed to load script: ${getKey(script)}`, error);
|
console.error(`Blazor Script Reload failed to load script: ${getKey(script)}`, error);
|
||||||
@ -55,16 +55,18 @@ function isValid(script) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceScript(script) {
|
function injectScript(script) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var newScript = document.createElement('script');
|
var newScript = document.createElement('script');
|
||||||
|
|
||||||
// replicate attributes and content
|
// replicate attributes and content
|
||||||
for (let i = 0; i < script.attributes.length; i++) {
|
for (let i = 0; i < script.attributes.length; i++) {
|
||||||
newScript.setAttribute(script.attributes[i].name, script.attributes[i].value);
|
if (script.attributes[i].name !== 'data-reload') {
|
||||||
|
newScript.setAttribute(script.attributes[i].name, script.attributes[i].value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
newScript.nonce = script.nonce; // must be referenced explicitly
|
||||||
newScript.innerHTML = script.innerHTML;
|
newScript.innerHTML = script.innerHTML;
|
||||||
newScript.removeAttribute('data-reload');
|
|
||||||
|
|
||||||
// dynamically injected scripts cannot be async or deferred
|
// dynamically injected scripts cannot be async or deferred
|
||||||
newScript.async = false;
|
newScript.async = false;
|
||||||
@ -73,10 +75,10 @@ function replaceScript(script) {
|
|||||||
newScript.onload = () => resolve();
|
newScript.onload = () => resolve();
|
||||||
newScript.onerror = (error) => reject(error);
|
newScript.onerror = (error) => reject(error);
|
||||||
|
|
||||||
// remove existing script element
|
// inject script element in head to force execution in Blazor
|
||||||
script.remove();
|
|
||||||
|
|
||||||
// replace with new script element to force reload in Blazor
|
|
||||||
document.head.appendChild(newScript);
|
document.head.appendChild(newScript);
|
||||||
|
|
||||||
|
// remove data-reload attribute
|
||||||
|
script.removeAttribute('data-reload');
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -32,6 +32,15 @@ namespace Oqtane.Models
|
|||||||
this.CrossOrigin = CrossOrigin;
|
this.CrossOrigin = CrossOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Script(string Src, string Integrity, string CrossOrigin, ResourceLoadBehavior LoadBehavior)
|
||||||
|
{
|
||||||
|
SetDefaults();
|
||||||
|
this.Url = Src;
|
||||||
|
this.Integrity = Integrity;
|
||||||
|
this.CrossOrigin = CrossOrigin;
|
||||||
|
this.LoadBehavior = LoadBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
public Script(string Src, string Integrity, string CrossOrigin, ResourceLocation Location, ResourceLoadBehavior LoadBehavior, Dictionary<string, string> DataAttributes, string Type, string Bundle, string RenderMode)
|
public Script(string Src, string Integrity, string CrossOrigin, ResourceLocation Location, ResourceLoadBehavior LoadBehavior, Dictionary<string, string> DataAttributes, string Type, string Bundle, string RenderMode)
|
||||||
{
|
{
|
||||||
SetDefaults();
|
SetDefaults();
|
||||||
|
@ -35,6 +35,7 @@ namespace Oqtane.Models
|
|||||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||||
};
|
};
|
||||||
|
Settings = new List<Setting>();
|
||||||
PageTemplateModules = new List<PageTemplateModule>();
|
PageTemplateModules = new List<PageTemplateModule>();
|
||||||
|
|
||||||
// properties used by IModule
|
// properties used by IModule
|
||||||
@ -60,6 +61,7 @@ namespace Oqtane.Models
|
|||||||
public bool IsPersonalizable { get; set; }
|
public bool IsPersonalizable { get; set; }
|
||||||
public bool IsDeleted { get; set; }
|
public bool IsDeleted { get; set; }
|
||||||
public List<Permission> PermissionList { get; set; }
|
public List<Permission> PermissionList { get; set; }
|
||||||
|
public List<Setting> Settings { get; set; }
|
||||||
public List<PageTemplateModule> PageTemplateModules { get; set; }
|
public List<PageTemplateModule> PageTemplateModules { get; set; }
|
||||||
|
|
||||||
// properties used by IModule
|
// properties used by IModule
|
||||||
@ -99,6 +101,7 @@ namespace Oqtane.Models
|
|||||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||||
};
|
};
|
||||||
|
Settings = new List<Setting>();
|
||||||
Content = "";
|
Content = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +112,7 @@ namespace Oqtane.Models
|
|||||||
public string ContainerType { get; set; }
|
public string ContainerType { get; set; }
|
||||||
public bool IsDeleted { get; set; }
|
public bool IsDeleted { get; set; }
|
||||||
public List<Permission> PermissionList { get; set; }
|
public List<Permission> PermissionList { get; set; }
|
||||||
|
public List<Setting> Settings { get; set; }
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
|
|
||||||
[Obsolete("The ModulePermissions property is deprecated. Use PermissionList instead", false)]
|
[Obsolete("The ModulePermissions property is deprecated. Use PermissionList instead", false)]
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>6.1.0</Version>
|
<Version>6.1.1</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/v6.1.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</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,11 +19,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.3" />
|
||||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||||
<PackageReference Include="System.Text.Json" Version="9.0.1" />
|
<PackageReference Include="System.Text.Json" Version="9.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -4,8 +4,8 @@ namespace Oqtane.Shared
|
|||||||
{
|
{
|
||||||
public class Constants
|
public class Constants
|
||||||
{
|
{
|
||||||
public static readonly string Version = "6.1.0";
|
public static readonly string Version = "6.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,5.2.0,5.2.1,5.2.2,5.2.3,5.2.4,6.0.0,6.0.1,6.1.0";
|
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,5.2.0,5.2.1,5.2.2,5.2.3,5.2.4,6.0.0,6.0.1,6.1.0,6.1.1";
|
||||||
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";
|
||||||
@ -33,8 +33,8 @@ namespace Oqtane.Shared
|
|||||||
public const string PageManagementModule = "Oqtane.Modules.Admin.Pages, Oqtane.Client";
|
public const string PageManagementModule = "Oqtane.Modules.Admin.Pages, Oqtane.Client";
|
||||||
public const string ErrorModule = "Oqtane.Modules.Admin.Error.{Action}, Oqtane.Client";
|
public const string ErrorModule = "Oqtane.Modules.Admin.Error.{Action}, Oqtane.Client";
|
||||||
|
|
||||||
public const string AdminSiteTemplate = "Oqtane.SiteTemplates.AdminSiteTemplate, Oqtane.Server";
|
public const string AdminSiteTemplate = "Oqtane.Infrastructure.SiteTemplates.AdminSiteTemplate, Oqtane.Server";
|
||||||
public const string DefaultSiteTemplate = "Oqtane.SiteTemplates.DefaultSiteTemplate, Oqtane.Server";
|
public const string DefaultSiteTemplate = "Oqtane.Infrastructure.SiteTemplates.DefaultSiteTemplate, Oqtane.Server";
|
||||||
|
|
||||||
public static readonly string[] DefaultHostModuleTypes = new[] { "Upgrade", "Themes", "SystemInfo", "Sql", "Sites", "ModuleDefinitions", "Logs", "Jobs", "ModuleCreator" };
|
public static readonly string[] DefaultHostModuleTypes = new[] { "Upgrade", "Themes", "SystemInfo", "Sql", "Sites", "ModuleDefinitions", "Logs", "Jobs", "ModuleCreator" };
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ namespace Oqtane.Shared
|
|||||||
public const string DefaultSite = "Default Site";
|
public const string DefaultSite = "Default Site";
|
||||||
|
|
||||||
public const string ImageFiles = "jpg,jpeg,jpe,gif,bmp,png,ico,webp";
|
public const string ImageFiles = "jpg,jpeg,jpe,gif,bmp,png,ico,webp";
|
||||||
public const string UploadableFiles = ImageFiles + ",mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg,csv,json,xml,rss,css";
|
public const string UploadableFiles = ImageFiles + ",mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg,csv,json,rss,css";
|
||||||
public const string ReservedDevices = "CON,NUL,PRN,COM0,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8,COM9,LPT0,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,CONIN$,CONOUT$";
|
public const string ReservedDevices = "CON,NUL,PRN,COM0,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8,COM9,LPT0,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,CONIN$,CONOUT$";
|
||||||
|
|
||||||
public static readonly char[] InvalidFileNameChars =
|
public static readonly char[] InvalidFileNameChars =
|
||||||
@ -91,6 +91,11 @@ namespace Oqtane.Shared
|
|||||||
public const string BootstrapStylesheetUrl = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css";
|
public const string BootstrapStylesheetUrl = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css";
|
||||||
public const string BootstrapStylesheetIntegrity = "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==";
|
public const string BootstrapStylesheetIntegrity = "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==";
|
||||||
|
|
||||||
|
public const string CookieConsentCookieName = "Oqtane.CookieConsent";
|
||||||
|
public const string CookieConsentCookieValue = "yes";
|
||||||
|
public const string CookieConsentActionCookieValue = "yes";
|
||||||
|
|
||||||
|
public const string SitemapOutputCacheTag = "Sitemap";
|
||||||
// Obsolete constants
|
// Obsolete constants
|
||||||
|
|
||||||
const string RoleObsoleteMessage = "Use the corresponding member from Oqtane.Shared.RoleNames";
|
const string RoleObsoleteMessage = "Use the corresponding member from Oqtane.Shared.RoleNames";
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Version>6.1.0</Version>
|
<Version>6.1.1</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/v6.1.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</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>
|
||||||
|
20
README.md
20
README.md
@ -12,15 +12,21 @@ Oqtane is being developed based on some fundamental principles which are outline
|
|||||||
|
|
||||||
# Latest Release
|
# Latest Release
|
||||||
|
|
||||||
[6.0.1](https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1) was released on December 20, 2024 and is a maintenance release including 58 pull requests by 7 different contributors, pushing the total number of project commits all-time to over 6100. The Oqtane framework continues to evolve at a rapid pace to meet the needs of .NET developers.
|
[6.1.0](https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.0) was released on February 11, 2025 and is a minor release including 95 pull requests by 9 different contributors, pushing the total number of project commits all-time to over 6300. 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)
|
# Try It Now!
|
||||||
|
|
||||||
|
Microsoft's Public Cloud (requires an Azure account)
|
||||||
|
[](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Foqtane%2Foqtane.framework%2Fdev%2Fazuredeploy.json)
|
||||||
|
|
||||||
|
A free ASP.NET hosting account. No hidden fees. No credit card required.
|
||||||
|
[](https://www.monsterasp.net/)
|
||||||
|
|
||||||
# Getting Started (Version 6.x)
|
# Getting Started (Version 6.x)
|
||||||
|
|
||||||
**Installing using source code from the Dev/Master branch:**
|
**Installing using source code from the Dev/Master branch:**
|
||||||
|
|
||||||
- Install **[.NET 9.0.0 SDK](https://dotnet.microsoft.com/download/dotnet/9.0)**.
|
- Install **[.NET 9.0.2 SDK](https://dotnet.microsoft.com/download/dotnet/9.0)**.
|
||||||
|
|
||||||
- Install the latest edition (v17.12 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.12 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**.
|
||||||
|
|
||||||
@ -85,10 +91,14 @@ Connect with other developers, get support, and share ideas by joining the Oqtan
|
|||||||
|
|
||||||
# Roadmap
|
# Roadmap
|
||||||
This project is open source, and therefore is a work in progress...
|
This project is open source, and therefore is a work in progress...
|
||||||
|
|
||||||
|
[6.1.0](https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.0) (Feb 11, 2025)
|
||||||
|
- [x] Static Asset / Folder Asset Caching
|
||||||
|
- [x] JavaScript improvements in Blazor Static Server Rendering (SSR)
|
||||||
|
- [x] User Impersonation
|
||||||
|
|
||||||
[6.0.1](https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1) (Dec 20, 2024)
|
[6.0.1](https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1) (Dec 20, 2024)
|
||||||
- [x] Stabilization improvements
|
- [x] Stabilization improvements
|
||||||
- [x] JavaScript improvements in Blazor Static Server Rendering (SSR)
|
|
||||||
|
|
||||||
[6.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0) (Nov 14, 2024)
|
[6.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0) (Nov 14, 2024)
|
||||||
- [x] Migration to .NET 9
|
- [x] Migration to .NET 9
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
# Security Policy
|
# Security Policy
|
||||||
|
|
||||||
|
## Security Bulletins
|
||||||
|
|
||||||
|
All published security bulletins are available in the Oqtane [Security Center](https://www.oqtane.net/security). The Security Center allows you to select a specific version of the Oqtane Framework and view the associated security bulletins.
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
We make every effort to ensure rapid and thorough analysis of reported issues and, where appropriate, provide workarounds and updated application releases to fix them. If you identify a potential security vulnerability please report it via support@oqtane.org.
|
We make every effort to ensure rapid and thorough analysis of reported issues and, where appropriate, provide workarounds and updated application releases to fix them. If you identify a potential security vulnerability please report it via support@oqtane.org.
|
||||||
@ -7,7 +11,7 @@ We make every effort to ensure rapid and thorough analysis of reported issues an
|
|||||||
All submitted information is viewed only by members of the Oqtane Security Team, and will not be discussed outside the Team without the permission of the person/company who reported the issue. Each confirmed issue is assigned a severity level (critical, moderate, or low) corresponding to its potential impact on an Oqtane installation.
|
All submitted information is viewed only by members of the Oqtane Security Team, and will not be discussed outside the Team without the permission of the person/company who reported the issue. Each confirmed issue is assigned a severity level (critical, moderate, or low) corresponding to its potential impact on an Oqtane installation.
|
||||||
|
|
||||||
* **Critical** means the issue can be exploited by a remote attacker to gain access to data or functionality. All critical issue security bulletins include a recommended workaround or fix that should be applied as soon as possible.
|
* **Critical** means the issue can be exploited by a remote attacker to gain access to data or functionality. All critical issue security bulletins include a recommended workaround or fix that should be applied as soon as possible.
|
||||||
* **Moderate** means the issue can compromise data or functionality on a portal/website only if some other condition is met (e.g. a particular module or a user within a particular role is required). Moderate issue security bulletins typically include recommended actions to resolve the issue.
|
* **Moderate** means the issue can compromise data or functionality on a site only if some other condition is met (e.g. a particular module or a user within a particular role is required). Moderate issue security bulletins typically include recommended actions to resolve the issue.
|
||||||
* **Low** means the issue is very difficult to exploit or has a limited potential impact.
|
* **Low** means the issue is very difficult to exploit or has a limited potential impact.
|
||||||
|
|
||||||
Once an issue has been resolved via a public release of Oqtane, the release notes on GitHub are updated to reflect that security bulletins exist for the release. We strongly suggest using the "Watch" option on GitHub for "Releases" at a minimum to receive notifications of updated Oqtane releases.
|
Once an issue has been resolved via a public release of Oqtane, the release notes on GitHub are updated to reflect that security bulletins exist for the release. We strongly suggest using the "Watch" option on GitHub for "Releases" at a minimum to receive notifications of updated Oqtane releases.
|
||||||
|
109
azuredeploy.json
109
azuredeploy.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||||
"contentVersion": "1.0.0.1",
|
"contentVersion": "1.0.0.2",
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"sqlDatabaseEditionTierDtuCapacity": {
|
"sqlDatabaseEditionTierDtuCapacity": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -18,7 +18,7 @@
|
|||||||
"Standard-S12-3000-250",
|
"Standard-S12-3000-250",
|
||||||
"Premium-P1-125-500",
|
"Premium-P1-125-500",
|
||||||
"Premium-P2-250-500",
|
"Premium-P2-250-500",
|
||||||
"Premium-P4-500-500" ,
|
"Premium-P4-500-500",
|
||||||
"Premium-P6-1000-500",
|
"Premium-P6-1000-500",
|
||||||
"Premium-P11-1750-500-1024",
|
"Premium-P11-1750-500-1024",
|
||||||
"Premium-P15-4000-1024",
|
"Premium-P15-4000-1024",
|
||||||
@ -38,19 +38,19 @@
|
|||||||
"sqlDatabaseName": {
|
"sqlDatabaseName": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"description": "The name of the sql databaseName. It has to be unique."
|
"description": "The name of the sql database. It has to be unique."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sqlAdministratorLogin": {
|
"sqlAdministratorLogin": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"description": "The admin user of the SQL Server"
|
"description": "The admin user of the SQL Server."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sqlAdministratorLoginPassword": {
|
"sqlAdministratorLoginPassword": {
|
||||||
"type": "securestring",
|
"type": "securestring",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"description": "The password of the admin user of the SQL Server"
|
"description": "The password of the admin user of the SQL Server."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"BlazorWebsiteName": {
|
"BlazorWebsiteName": {
|
||||||
@ -75,7 +75,10 @@
|
|||||||
"P3",
|
"P3",
|
||||||
"P4"
|
"P4"
|
||||||
],
|
],
|
||||||
"defaultValue": "B1"
|
"defaultValue": "B1",
|
||||||
|
"metadata": {
|
||||||
|
"description": "The SKU for the App Service Plan"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"BlazorSKUCapacity": {
|
"BlazorSKUCapacity": {
|
||||||
"type": "int",
|
"type": "int",
|
||||||
@ -101,15 +104,18 @@
|
|||||||
"databaseEdition": "[variables('databaseEditionTierDtuCapacity')[0]]",
|
"databaseEdition": "[variables('databaseEditionTierDtuCapacity')[0]]",
|
||||||
"databaseTier": "[variables('databaseEditionTierDtuCapacity')[1]]",
|
"databaseTier": "[variables('databaseEditionTierDtuCapacity')[1]]",
|
||||||
"databaseDtu": "[if(greater(length(variables('databaseEditionTierDtuCapacity')), 2), variables('databaseEditionTierDtuCapacity')[2], '')]",
|
"databaseDtu": "[if(greater(length(variables('databaseEditionTierDtuCapacity')), 2), variables('databaseEditionTierDtuCapacity')[2], '')]",
|
||||||
"databaseMaxSizeGigaBytes":"[if(greater(length(variables('databaseEditionTierDtuCapacity')), 3), variables('databaseEditionTierDtuCapacity')[3], '')]",
|
"databaseMaxSizeGigaBytes": "[if(greater(length(variables('databaseEditionTierDtuCapacity')), 3), variables('databaseEditionTierDtuCapacity')[3], '')]",
|
||||||
"databaseServerlessTiers": [
|
"databaseServerlessTiers": [
|
||||||
"GP_S_Gen5_2"
|
"GP_S_Gen5_2"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"resources": [
|
"resources": [
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// SQL Server
|
||||||
|
// ------------------------------------------------------
|
||||||
{
|
{
|
||||||
"type": "Microsoft.Sql/servers",
|
"type": "Microsoft.Sql/servers",
|
||||||
"apiVersion": "2021-11-01",
|
"apiVersion": "2022-05-01-preview", // Updated API version
|
||||||
"name": "[parameters('sqlServerName')]",
|
"name": "[parameters('sqlServerName')]",
|
||||||
"location": "[parameters('location')]",
|
"location": "[parameters('location')]",
|
||||||
"tags": {
|
"tags": {
|
||||||
@ -121,9 +127,12 @@
|
|||||||
"version": "12.0"
|
"version": "12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// SQL Database (separate resource rather than subresource)
|
||||||
|
// ------------------------------------------------------
|
||||||
{
|
{
|
||||||
"type": "Microsoft.Sql/servers/databases",
|
"type": "Microsoft.Sql/servers/databases",
|
||||||
"apiVersion": "2021-11-01",
|
"apiVersion": "2022-05-01-preview", // Updated API version
|
||||||
"name": "[format('{0}/{1}', parameters('sqlServerName'), parameters('sqlDatabaseName'))]",
|
"name": "[format('{0}/{1}', parameters('sqlServerName'), parameters('sqlDatabaseName'))]",
|
||||||
"location": "[parameters('location')]",
|
"location": "[parameters('location')]",
|
||||||
"tags": {
|
"tags": {
|
||||||
@ -132,24 +141,40 @@
|
|||||||
"sku": {
|
"sku": {
|
||||||
"name": "[if(equals(variables('databaseEdition'), 'GeneralPurpose'), variables('databaseTier'), variables('databaseEdition'))]",
|
"name": "[if(equals(variables('databaseEdition'), 'GeneralPurpose'), variables('databaseTier'), variables('databaseEdition'))]",
|
||||||
"tier": "[variables('databaseEdition')]",
|
"tier": "[variables('databaseEdition')]",
|
||||||
"capacity": "[if(equals(variables('databaseDtu'), ''), json('null'), int(variables('databaseDtu')))]"
|
"capacity": "[if(equals(variables('databaseDtu'), ''), json('null'), int(variables('databaseDtu')))]"
|
||||||
},
|
},
|
||||||
"kind": "[concat('v12.0,user,vcore',if(contains(variables('databaseServerlessTiers'),variables('databaseTier')),',serverless',''))]",
|
"kind": "[concat('v12.0,user,vcore', if(contains(variables('databaseServerlessTiers'), variables('databaseTier')), ',serverless', ''))]",
|
||||||
"properties": {
|
"properties": {
|
||||||
"edition": "[variables('databaseEdition')]",
|
"edition": "[variables('databaseEdition')]",
|
||||||
"collation": "[variables('databaseCollation')]",
|
"collation": "[variables('databaseCollation')]",
|
||||||
"maxSizeBytes": "[if(equals(variables('databaseMaxSizeGigaBytes'), ''), json('null'), mul(mul(mul(int(variables('databaseMaxSizeGigaBytes')),1024),1024),1024))]",
|
"maxSizeBytes": "[if(equals(variables('databaseMaxSizeGigaBytes'), ''), json('null'), mul(mul(mul(int(variables('databaseMaxSizeGigaBytes')),1024),1024),1024))]",
|
||||||
"requestedServiceObjectiveName": "[variables('databaseTier')]"
|
"requestedServiceObjectiveName": "[variables('databaseTier')]"
|
||||||
|
|
||||||
},
|
},
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"[resourceId('Microsoft.Sql/servers', parameters('sqlserverName'))]"
|
"[resourceId('Microsoft.Sql/servers', parameters('sqlServerName'))]"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// Transparent Data Encryption child resource
|
||||||
|
// ------------------------------------------------------
|
||||||
|
{
|
||||||
|
"type": "Microsoft.Sql/servers/databases/transparentDataEncryption",
|
||||||
|
"apiVersion": "2021-02-01-preview",
|
||||||
|
"name": "[format('{0}/{1}/current', parameters('sqlServerName'), parameters('sqlDatabaseName'))]",
|
||||||
|
"properties": {
|
||||||
|
"state": "Enabled"
|
||||||
|
},
|
||||||
|
"dependsOn": [
|
||||||
|
"[resourceId('Microsoft.Sql/servers/databases', parameters('sqlServerName'), parameters('sqlDatabaseName'))]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// Firewall Rule (renamed to 'AllowAllMicrosoftAzureIps')
|
||||||
|
// ------------------------------------------------------
|
||||||
{
|
{
|
||||||
"type": "Microsoft.Sql/servers/firewallRules",
|
"type": "Microsoft.Sql/servers/firewallRules",
|
||||||
"apiVersion": "2021-11-01",
|
"apiVersion": "2022-05-01-preview", // Updated API version
|
||||||
"name": "[format('{0}/{1}', parameters('sqlServerName'), 'AllowAllWindowsAzureIps')]",
|
"name": "[format('{0}/{1}', parameters('sqlServerName'), 'AllowAllMicrosoftAzureIps')]",
|
||||||
"properties": {
|
"properties": {
|
||||||
"endIpAddress": "0.0.0.0",
|
"endIpAddress": "0.0.0.0",
|
||||||
"startIpAddress": "0.0.0.0"
|
"startIpAddress": "0.0.0.0"
|
||||||
@ -158,26 +183,33 @@
|
|||||||
"[resourceId('Microsoft.Sql/servers', parameters('sqlServerName'))]"
|
"[resourceId('Microsoft.Sql/servers', parameters('sqlServerName'))]"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// App Service Plan
|
||||||
|
// ------------------------------------------------------
|
||||||
{
|
{
|
||||||
"name": "[variables('hostingPlanName')]",
|
|
||||||
"type": "Microsoft.Web/serverfarms",
|
"type": "Microsoft.Web/serverfarms",
|
||||||
"location": "[resourceGroup().location]",
|
"apiVersion": "2022-03-01", // Updated API version
|
||||||
"apiVersion": "2022-09-01",
|
"name": "[variables('hostingPlanName')]",
|
||||||
"dependsOn": [],
|
"location": "[parameters('location')]",
|
||||||
"tags": {
|
"tags": {
|
||||||
"displayName": "Blazor"
|
"displayName": "Blazor"
|
||||||
},
|
},
|
||||||
"sku": {
|
"sku": {
|
||||||
"name": "[parameters('BlazorSKU')]",
|
"name": "[parameters('BlazorSKU')]",
|
||||||
|
// If you want to auto-map to certain "tier" strings, you can do so. Here we just set the capacity:
|
||||||
"capacity": "[parameters('BlazorSKUCapacity')]"
|
"capacity": "[parameters('BlazorSKUCapacity')]"
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": "[variables('hostingPlanName')]",
|
"name": "[variables('hostingPlanName')]",
|
||||||
"numberOfWorkers": 1
|
"numberOfWorkers": 1
|
||||||
}
|
},
|
||||||
|
"dependsOn": []
|
||||||
},
|
},
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// Web App
|
||||||
|
// ------------------------------------------------------
|
||||||
{
|
{
|
||||||
"apiVersion": "2018-02-01",
|
"apiVersion": "2022-03-01", // Updated API version
|
||||||
"name": "[parameters('BlazorWebsiteName')]",
|
"name": "[parameters('BlazorWebsiteName')]",
|
||||||
"type": "Microsoft.Web/sites",
|
"type": "Microsoft.Web/sites",
|
||||||
"location": "[parameters('location')]",
|
"location": "[parameters('location')]",
|
||||||
@ -189,27 +221,46 @@
|
|||||||
"displayName": "Website"
|
"displayName": "Website"
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": "[parameters('BlazorWebsiteName')]",
|
|
||||||
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
|
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
|
||||||
"siteConfig": {
|
"siteConfig": {
|
||||||
"webSocketsEnabled": true,
|
"webSocketsEnabled": true,
|
||||||
"netFrameworkVersion": "v5.0"
|
// Updated .NET version "v9.0" from second snippet
|
||||||
|
"netFrameworkVersion": "v9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resources": [
|
"resources": [
|
||||||
|
// --------------------------------------------------
|
||||||
|
// Source Control for your Web App
|
||||||
|
// --------------------------------------------------
|
||||||
{
|
{
|
||||||
"type": "sourcecontrols",
|
"type": "sourcecontrols",
|
||||||
"apiVersion": "2018-02-01",
|
"apiVersion": "2022-03-01",
|
||||||
"name": "web",
|
"name": "web",
|
||||||
"location": "[parameters('location')]",
|
"location": "[parameters('location')]",
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"[resourceId('Microsoft.Web/sites', parameters('BlazorWebsiteName'))]"
|
"[resourceId('Microsoft.Web/sites', parameters('BlazorWebsiteName'))]"
|
||||||
//"[resourceId('Microsoft.Web/Sites/config', parameters('BlazorWebsiteName'), 'connectionstrings')]"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"RepoUrl": "https://github.com/oqtane/oqtane.framework.git",
|
"repoUrl": "https://github.com/oqtane/oqtane.framework.git",
|
||||||
"branch": "master",
|
"branch": "master",
|
||||||
"IsManualIntegration": true
|
"isManualIntegration": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// --------------------------------------------------
|
||||||
|
// Connection Strings (to use FQDN)
|
||||||
|
// --------------------------------------------------
|
||||||
|
{
|
||||||
|
"type": "config",
|
||||||
|
"apiVersion": "2022-03-01",
|
||||||
|
"name": "connectionstrings",
|
||||||
|
"dependsOn": [
|
||||||
|
"[resourceId('Microsoft.Web/sites', parameters('BlazorWebsiteName'))]"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"DefaultConnection": {
|
||||||
|
"value": "[concat('Data Source=tcp:', reference(resourceId('Microsoft.Sql/servers', parameters('sqlServerName'))).fullyQualifiedDomainName, ',1433;Initial Catalog=', parameters('sqlDatabaseName'), ';User Id=', parameters('sqlAdministratorLogin'), '@', reference(resourceId('Microsoft.Sql/servers', parameters('sqlServerName'))).fullyQualifiedDomainName, ';Password=', parameters('sqlAdministratorLoginPassword'), ';')]",
|
||||||
|
"type": "SQLAzure"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
Reference in New Issue
Block a user